001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.application.support;
017
018 import java.awt.BorderLayout;
019 import java.awt.Dimension;
020 import java.awt.Point;
021 import java.awt.Window;
022 import java.awt.event.ActionEvent;
023 import java.awt.event.ActionListener;
024 import java.io.BufferedReader;
025 import java.io.IOException;
026 import java.io.InputStreamReader;
027
028 import javax.swing.BorderFactory;
029 import javax.swing.JComponent;
030 import javax.swing.JPanel;
031 import javax.swing.JSeparator;
032 import javax.swing.JTextArea;
033 import javax.swing.JViewport;
034 import javax.swing.Timer;
035 import javax.swing.event.HyperlinkEvent;
036 import javax.swing.event.HyperlinkListener;
037
038 import org.springframework.core.io.Resource;
039 import org.springframework.richclient.application.Application;
040 import org.springframework.richclient.application.ApplicationDescriptor;
041 import org.springframework.richclient.command.AbstractCommand;
042 import org.springframework.richclient.dialog.ApplicationDialog;
043 import org.springframework.richclient.dialog.CloseAction;
044 import org.springframework.richclient.text.HtmlPane;
045 import org.springframework.util.FileCopyUtils;
046 import org.springframework.util.StringUtils;
047
048 /**
049 * An implementation of an about box in a dialog. The dialog contents contain
050 * a simple, fixed area at the top showing the information from the
051 * ApplicationDescriptor configured in the context. Below that is a scrolling
052 * (animated) panel that displays the contents of a specified file.
053 *
054 * @author Keith Donald
055 * @author Oliver Hutchison
056 *
057 * @see #setAboutTextPath(Resource)
058 * @see ApplicationDescriptor
059 * @see HtmlPane
060 */
061 public class AboutBox {
062 private Resource aboutTextPath;
063
064 public AboutBox() {
065 }
066
067 /**
068 * @param path
069 */
070 public void setAboutTextPath(Resource path) {
071 this.aboutTextPath = path;
072 }
073
074 /**
075 * @return the aboutTextPath
076 */
077 public Resource getAboutTextPath() {
078 return aboutTextPath;
079 }
080
081 public void display(Window parent) {
082 AboutDialog aboutMainDialog = new AboutDialog();
083 aboutMainDialog.setParentComponent(parent);
084 aboutMainDialog.showDialog();
085 }
086
087 protected class AboutDialog extends ApplicationDialog {
088
089 private HtmlScroller scroller;
090
091 public AboutDialog() {
092 setTitle("About " + getApplicationName());
093 setResizable(false);
094 setCloseAction(CloseAction.DISPOSE);
095 }
096
097 protected void addDialogComponents() {
098 JComponent dialogContentPane = createDialogContentPane();
099 getDialogContentPane().add(dialogContentPane);
100 getDialogContentPane().add(createButtonBar(), BorderLayout.SOUTH);
101 }
102
103 /**
104 * Create the control that shows the application descriptor data (title,
105 * caption, description, version, and build id).
106 *
107 * @return control
108 */
109 protected JComponent createApplicationDescriptorComponent() {
110 // Build the application descriptor data, if available
111 JTextArea txtDescriptor = getComponentFactory().createTextAreaAsLabel();
112 txtDescriptor.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
113 ApplicationDescriptor appDesc = Application.instance().getDescriptor();
114 if( appDesc != null ) {
115 String displayName = appDesc.getDisplayName();
116 String caption = appDesc.getCaption();
117 String description = appDesc.getDescription();
118 String version = appDesc.getVersion();
119 String buildId = appDesc.getBuildId();
120 StringBuffer sb = new StringBuffer();
121
122 if( StringUtils.hasText(displayName) ) {
123 sb.append(displayName).append("\n");
124 }
125 if( StringUtils.hasText(caption) ) {
126 sb.append(caption).append("\n\n");
127 }
128 if( StringUtils.hasText(description) ) {
129 sb.append(description).append("\n\n");
130 }
131 if( StringUtils.hasText(version) ) {
132 sb.append(getMessage("aboutBox.version.label")).append(": ").append(version).append("\n");
133 }
134 if( StringUtils.hasText(buildId) ) {
135 sb.append(getMessage("aboutBox.buildId.label")).append(": ").append(buildId).append("\n");
136 }
137 txtDescriptor.setText(sb.toString());
138 }
139 return txtDescriptor;
140 }
141
142 /**
143 * Construct the main dialog pane.
144 * @return Constructed component
145 */
146 protected JComponent createDialogContentPane() {
147 JPanel dialogPanel = new JPanel(new BorderLayout());
148
149 // Add in the application descriptor data, if available
150 dialogPanel.add(createApplicationDescriptorComponent(), BorderLayout.NORTH);
151
152 // If a text file resource has been specified, then construct the
153 // scroller to show it.
154 if( aboutTextPath != null ) {
155 try {
156 scroller = new HtmlScroller(false, 2000, 15, 10);
157 String text = FileCopyUtils.copyToString(new BufferedReader(new InputStreamReader(aboutTextPath
158 .getInputStream())));
159 scroller.setHtml(text);
160 } catch( IOException e ) {
161 final IllegalStateException exp = new IllegalStateException("About text not accessible: "
162 + e.getMessage());
163 exp.setStackTrace(e.getStackTrace());
164 throw exp;
165 }
166 dialogPanel.add(scroller);
167 dialogPanel.setPreferredSize(new Dimension(scroller.getPreferredSize().width, 300));
168 } else {
169 // Set the preferred size
170 dialogPanel.setPreferredSize(new Dimension( 300, 200));
171 }
172 dialogPanel.add(new JSeparator(), BorderLayout.SOUTH);
173 return dialogPanel;
174 }
175
176 protected void onAboutToShow() {
177 if( scroller != null ) {
178 try {
179 String text = FileCopyUtils.copyToString(new BufferedReader(new InputStreamReader(aboutTextPath
180 .getInputStream())));
181 scroller.setHtml(text);
182 }
183 catch (IOException e) {
184 final IllegalStateException exp =
185 new IllegalStateException("About text not accessible: "+e.getMessage());
186 exp.setStackTrace(e.getStackTrace());
187 throw exp;
188 }
189 scroller.reset();
190 scroller.startScrolling();
191 }
192 }
193
194 protected boolean onFinish() {
195 if( scroller != null ) {
196 scroller.pauseScrolling();
197 }
198 return true;
199 }
200
201 protected Object[] getCommandGroupMembers() {
202 return new AbstractCommand[] { getFinishCommand() };
203 }
204
205 /**
206 * Get the scrolling HTML panel.
207 * @return scroller
208 */
209 protected HtmlScroller getHtmlScroller() {
210 return scroller;
211 }
212 }
213
214 /**
215 * A panel that scrolls the content of a HTML document.
216 *
217 * @author Oliver Hutchison
218 */
219 protected static class HtmlScroller extends JViewport implements HyperlinkListener {
220
221 private HtmlPane htmlPane;
222
223 private Timer timer;
224
225 private int initalDelay;
226
227 private double incY = 0;
228
229 private double currentY = 0;
230
231 private double currentX = 0;
232
233 /**
234 * Created a new HtmlScroller.
235 *
236 * @param antiAlias
237 * antialias the rendered HTML
238 * @param initalDelay
239 * inital delay after which scrolling begins
240 * @param speedPixSec
241 * scoll speed in pixels per second
242 * @param fps
243 * number of updates per second
244 */
245 public HtmlScroller(boolean antiAlias, int initalDelay, int speedPixSec, int fps) {
246 this.initalDelay = initalDelay;
247
248 incY = (double)speedPixSec / (double)fps;
249
250 htmlPane = new HtmlPane();
251 htmlPane.setAntiAlias(antiAlias);
252 htmlPane.addHyperlinkListener(this);
253 setView(htmlPane);
254 timer = new Timer(1000 / fps, new ActionListener() {
255 public void actionPerformed(ActionEvent e) {
256 int maxY = htmlPane.getHeight() - getHeight();
257 currentY = Math.max(0, Math.min(currentY + incY, maxY));
258 if (currentY <= 0 || currentY == maxY) {
259 pauseScrolling();
260 }
261 setViewPositionInternal(new Point((int)currentX, (int)currentY));
262 }
263 });
264 reset();
265 }
266
267 /**
268 * Sets the HTML that will be rendered by this component.
269 */
270 public void setHtml(String html) {
271 htmlPane.setText(html);
272 setPreferredSize(htmlPane.getPreferredSize());
273 }
274
275 /**
276 * Resets this component to its inital state.
277 */
278 public final void reset() {
279 currentX = 0;
280 currentY = 0;
281 timer.setInitialDelay(initalDelay);
282 setViewPositionInternal(new Point((int)currentX, (int)currentY));
283 }
284
285 /**
286 * Starts the scoller
287 */
288 public void startScrolling() {
289 timer.start();
290 }
291
292 /**
293 * Pauses the scoller.
294 */
295 public void pauseScrolling() {
296 timer.stop();
297 timer.setInitialDelay(0);
298 }
299
300 public void setViewPosition(Point p) {
301 // ignore calls that are not internal
302 }
303
304 public void hyperlinkUpdate(HyperlinkEvent e) {
305 if (e.getEventType().equals(HyperlinkEvent.EventType.ENTERED)) {
306 enteredLink();
307 }
308 else if (e.getEventType().equals(HyperlinkEvent.EventType.EXITED)) {
309 exitedLink();
310 }
311 }
312
313 private void setViewPositionInternal(Point p) {
314 super.setViewPosition(p);
315 }
316
317 private void enteredLink() {
318 pauseScrolling();
319 }
320
321 private void exitedLink() {
322 startScrolling();
323 }
324 }
325 }