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 }