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    }