001: /*
002: * Copyright (c) 2007, Sun Microsystems, Inc.
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * * Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in
012: * the documentation and/or other materials provided with the distribution.
013: * * Neither the name of Sun Microsystems, Inc. nor the names of its
014: * contributors may be used to endorse or promote products derived
015: * from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
023: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: package marsroverviewer;
031:
032: import org.jdesktop.application.Action;
033: import org.jdesktop.application.ResourceMap;
034: import org.jdesktop.application.SingleFrameApplication;
035: import org.jdesktop.application.FrameView;
036: import org.jdesktop.application.TaskMonitor;
037: import java.awt.event.ActionEvent;
038: import java.awt.event.ActionListener;
039: import java.awt.image.BufferedImage;
040: import java.net.URL;
041: import java.util.List;
042: import javax.swing.Timer;
043: import javax.swing.Icon;
044: import javax.swing.ImageIcon;
045: import javax.swing.JDialog;
046: import javax.swing.JFrame;
047: import org.jdesktop.application.Task;
048:
049: /**
050: * This application is a simple image viewer. This class displays the main frame
051: * of the application and provides much of the logic. This class is called by
052: * the main application class, MarsRoverViewerApp. For an overview of the
053: * application see the comments for the MarsRoverViewerApp class.
054: */
055: public class MarsRoverViewerView extends FrameView {
056:
057: /* The following fields define the application's internal state.
058: * We track our current - imageIndex - position in the list of URLs.
059: * The value of imageTask is managed by ShowImagTask, it's initialized
060: * (on the EDT) when the task is constructed and cleared (on the EDT)
061: * by the task, when it's done. The boolean *enabled fields are updated
062: * by calling updateNextPreviousEnabledProperties().
063: */
064: private List<URL> imageLocations;
065: private int imageIndex = 0;
066: private ShowImageTask imageTask = null;
067: private boolean nextImageEnabled = true;
068: private boolean previousImageEnabled = false;
069:
070: public MarsRoverViewerView(SingleFrameApplication app,
071: List<URL> imageLocations) {
072: super (app);
073: this .imageLocations = imageLocations;
074:
075: initComponents();
076:
077: // status bar initialization - message timeout, idle icon and busy animation, etc
078: ResourceMap resourceMap = getResourceMap();
079: int messageTimeout = resourceMap
080: .getInteger("StatusBar.messageTimeout");
081: messageTimer = new Timer(messageTimeout, new ActionListener() {
082: public void actionPerformed(ActionEvent e) {
083: statusMessageLabel.setText("");
084: }
085: });
086: messageTimer.setRepeats(false);
087: int busyAnimationRate = resourceMap
088: .getInteger("StatusBar.busyAnimationRate");
089: for (int i = 0; i < busyIcons.length; i++) {
090: busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons["
091: + i + "]");
092: }
093: busyIconTimer = new Timer(busyAnimationRate,
094: new ActionListener() {
095: public void actionPerformed(ActionEvent e) {
096: busyIconIndex = (busyIconIndex + 1)
097: % busyIcons.length;
098: statusAnimationLabel
099: .setIcon(busyIcons[busyIconIndex]);
100: }
101: });
102: idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
103: statusAnimationLabel.setIcon(idleIcon);
104: progressBar.setVisible(false);
105:
106: // connect action tasks to status bar via TaskMonitor
107: TaskMonitor taskMonitor = new TaskMonitor(getApplication()
108: .getContext());
109: taskMonitor
110: .addPropertyChangeListener(new java.beans.PropertyChangeListener() {
111: public void propertyChange(
112: java.beans.PropertyChangeEvent evt) {
113: String propertyName = evt.getPropertyName();
114: if ("started".equals(propertyName)) {
115: if (!busyIconTimer.isRunning()) {
116: statusAnimationLabel
117: .setIcon(busyIcons[0]);
118: busyIconIndex = 0;
119: busyIconTimer.start();
120: }
121: progressBar.setVisible(true);
122: progressBar.setIndeterminate(true);
123: } else if ("done".equals(propertyName)) {
124: busyIconTimer.stop();
125: statusAnimationLabel.setIcon(idleIcon);
126: progressBar.setVisible(false);
127: progressBar.setValue(0);
128: } else if ("message".equals(propertyName)) {
129: String text = (String) (evt.getNewValue());
130: statusMessageLabel
131: .setText((text == null) ? "" : text);
132: messageTimer.restart();
133: } else if ("progress".equals(propertyName)) {
134: int value = (Integer) (evt.getNewValue());
135: progressBar.setVisible(true);
136: progressBar.setIndeterminate(false);
137: progressBar.setValue(value);
138: }
139: }
140: });
141: }
142:
143: @Action
144: public void showAboutBox() {
145: if (aboutBox == null) {
146: JFrame mainFrame = MarsRoverViewerApp.getApplication()
147: .getMainFrame();
148: aboutBox = new MarsRoverViewerAboutBox(mainFrame);
149: aboutBox.setLocationRelativeTo(mainFrame);
150: }
151: MarsRoverViewerApp.getApplication().show(aboutBox);
152: }
153:
154: /* The nextImage, previousImage and refreshImage actions clear the
155: * displayed image, by calling showImageLoading(), to free up heap space.
156: * Most of the images we're loading are so large that there's not
157: * enough heap space (by default) to accomodate both the old and
158: * new ones. We could adjust the heap size parameters to
159: * eliminate the problem, however it's more neighborly to just
160: * limit the heap's growth.
161: */
162:
163: @Action(enabledProperty="nextImageEnabled")
164: public Task nextImage() {
165: Task task = null;
166: if (imageIndex < (imageLocations.size() - 1)) {
167: imageIndex += 1;
168: updateNextPreviousEnabledProperties();
169: task = new ShowImageTask(imageLocations.get(imageIndex));
170: }
171: return task;
172: }
173:
174: @Action(enabledProperty="previousImageEnabled")
175: public Task previousImage() {
176: Task task = null;
177: if (imageIndex > 0) {
178: imageIndex -= 1;
179: updateNextPreviousEnabledProperties();
180: task = new ShowImageTask(imageLocations.get(imageIndex));
181: }
182: return task;
183: }
184:
185: @Action
186: public Task refreshImage() {
187: return new ShowImageTask(imageLocations.get(imageIndex));
188: }
189:
190: @Action
191: public void stopLoading() {
192: if ((imageTask != null) && !imageTask.isDone()) {
193: imageTask.cancel(true);
194: }
195: }
196:
197: /* The properties below define the enabled state for the
198: * corresponding @Actions.
199: */
200:
201: private void updateNextPreviousEnabledProperties() {
202: setNextImageEnabled(imageIndex < (imageLocations.size() - 1));
203: setPreviousImageEnabled(imageIndex > 0);
204: }
205:
206: public boolean isNextImageEnabled() {
207: return nextImageEnabled;
208: }
209:
210: public void setNextImageEnabled(boolean nextImageEnabled) {
211: boolean oldValue = this .nextImageEnabled;
212: this .nextImageEnabled = nextImageEnabled;
213: firePropertyChange("nextImageEnabled", oldValue,
214: this .nextImageEnabled);
215: }
216:
217: public boolean isPreviousImageEnabled() {
218: return previousImageEnabled;
219: }
220:
221: public void setPreviousImageEnabled(boolean previousImageEnabled) {
222: boolean oldValue = this .previousImageEnabled;
223: this .previousImageEnabled = previousImageEnabled;
224: firePropertyChange("previousImageEnabled", oldValue,
225: this .previousImageEnabled);
226: }
227:
228: /* A application specific subclass of LoadImageTask.
229: *
230: * This class is constructed on the EDT. The constructor
231: * stops the current ShowImageTask, if one is still
232: * running, clears the display (imageLabel) so that
233: * we'll only have one enormous image on the heap, and
234: * updates the enabled state of the next/previous @Actions.
235: * When the task completes, we update the GUI.
236: */
237: private class ShowImageTask extends LoadImageTask {
238: ShowImageTask(URL imageURL) {
239: super (MarsRoverViewerView.this .getApplication(), imageURL);
240: stopLoading();
241: imageTask = this ;
242: showImageMessage(imageURL, "loadingWait");
243: }
244:
245: @Override
246: protected void cancelled() {
247: if (imageTask == this ) {
248: showImageMessage(getImageURL(), "loadingCancelled");
249: }
250: }
251:
252: @Override
253: protected void succeeded(BufferedImage image) {
254: super .succeeded(image);
255: if (imageTask == this ) {
256: showImage(getImageURL(), image);
257: }
258: }
259:
260: @Override
261: protected void failed(Throwable e) {
262: super .failed(e);
263: if (imageTask == this ) {
264: showImageMessage(getImageURL(), "loadingFailed");
265: }
266: }
267:
268: @Override
269: protected void finished() {
270: super .finished();
271: imageTask = null;
272: }
273: }
274:
275: private void showImage(URL imageURL, BufferedImage image) {
276: int width = image.getWidth();
277: int height = image.getHeight();
278: String tip = getResourceMap().getString("imageTooltip",
279: imageURL, width, height);
280: imageLabel.setToolTipText(tip);
281: imageLabel.setText(null);
282: imageLabel.setIcon(new ImageIcon(image));
283: }
284:
285: private void showImageMessage(URL imageURL, String key) {
286: String msg = getResourceMap().getString(key, imageURL);
287: imageLabel.setToolTipText("");
288: imageLabel.setText(msg);
289: imageLabel.setIcon(null);
290: }
291:
292: /** This method is called from within the constructor to
293: * initialize the form.
294: * WARNING: Do NOT modify this code. The content of this method is
295: * always regenerated by the Form Editor.
296: */
297: // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
298: private void initComponents() {
299:
300: mainPanel = new javax.swing.JPanel();
301: scrollPane = new javax.swing.JScrollPane();
302: imageLabel = new javax.swing.JLabel();
303: menuBar = new javax.swing.JMenuBar();
304: javax.swing.JMenu fileMenu = new javax.swing.JMenu();
305: javax.swing.JMenuItem previousMenuItem = new javax.swing.JMenuItem();
306: javax.swing.JMenuItem nextMenuItem = new javax.swing.JMenuItem();
307: javax.swing.JMenuItem refreshMenuItem = new javax.swing.JMenuItem();
308: javax.swing.JMenuItem stopMenuItem = new javax.swing.JMenuItem();
309: javax.swing.JSeparator separator1 = new javax.swing.JSeparator();
310: javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
311: javax.swing.JMenu helpMenu = new javax.swing.JMenu();
312: javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
313: toolbar = new javax.swing.JToolBar();
314: previousButton = new javax.swing.JButton();
315: nextButton = new javax.swing.JButton();
316: refreshButton = new javax.swing.JButton();
317: stopButton = new javax.swing.JButton();
318: statusPanel = new javax.swing.JPanel();
319: javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
320: statusMessageLabel = new javax.swing.JLabel();
321: statusAnimationLabel = new javax.swing.JLabel();
322: progressBar = new javax.swing.JProgressBar();
323:
324: mainPanel.setName("mainPanel"); // NOI18N
325:
326: scrollPane.setName("scrollPane"); // NOI18N
327:
328: imageLabel
329: .setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
330: imageLabel.setName("imageLabel"); // NOI18N
331: imageLabel.setOpaque(true);
332: scrollPane.setViewportView(imageLabel);
333:
334: org.jdesktop.layout.GroupLayout mainPanelLayout = new org.jdesktop.layout.GroupLayout(
335: mainPanel);
336: mainPanel.setLayout(mainPanelLayout);
337: mainPanelLayout.setHorizontalGroup(mainPanelLayout
338: .createParallelGroup(
339: org.jdesktop.layout.GroupLayout.LEADING).add(
340: scrollPane,
341: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
342: 480, Short.MAX_VALUE));
343: mainPanelLayout.setVerticalGroup(mainPanelLayout
344: .createParallelGroup(
345: org.jdesktop.layout.GroupLayout.LEADING).add(
346: scrollPane,
347: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
348: 288, Short.MAX_VALUE));
349: org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application
350: .getInstance(marsroverviewer.MarsRoverViewerApp.class)
351: .getContext().getResourceMap(MarsRoverViewerView.class);
352: resourceMap.injectComponents(mainPanel);
353:
354: menuBar.setName("menuBar"); // NOI18N
355:
356: fileMenu.setName("fileMenu"); // NOI18N
357:
358: javax.swing.ActionMap actionMap = org.jdesktop.application.Application
359: .getInstance(marsroverviewer.MarsRoverViewerApp.class)
360: .getContext().getActionMap(MarsRoverViewerView.class,
361: this );
362: previousMenuItem.setAction(actionMap.get("previousImage")); // NOI18N
363: previousMenuItem.setName("previousMenuItem"); // NOI18N
364: fileMenu.add(previousMenuItem);
365:
366: nextMenuItem.setAction(actionMap.get("nextImage")); // NOI18N
367: nextMenuItem.setName("nextMenuItem"); // NOI18N
368: fileMenu.add(nextMenuItem);
369:
370: refreshMenuItem.setAction(actionMap.get("refreshImage")); // NOI18N
371: refreshMenuItem.setName("refreshMenuItem"); // NOI18N
372: fileMenu.add(refreshMenuItem);
373:
374: stopMenuItem.setAction(actionMap.get("stopLoading")); // NOI18N
375: stopMenuItem.setName("stopMenuItem"); // NOI18N
376: fileMenu.add(stopMenuItem);
377:
378: separator1.setName("separator1"); // NOI18N
379: fileMenu.add(separator1);
380:
381: exitMenuItem.setAction(actionMap.get("quit")); // NOI18N
382: exitMenuItem.setName("exitMenuItem"); // NOI18N
383: fileMenu.add(exitMenuItem);
384:
385: menuBar.add(fileMenu);
386:
387: helpMenu.setName("helpMenu"); // NOI18N
388:
389: aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
390: aboutMenuItem.setName("aboutMenuItem"); // NOI18N
391: helpMenu.add(aboutMenuItem);
392:
393: menuBar.add(helpMenu);
394: resourceMap.injectComponents(menuBar);
395:
396: toolbar.setFloatable(false);
397: toolbar.setRollover(true);
398: toolbar.setName("toolbar"); // NOI18N
399:
400: previousButton.setAction(actionMap.get("previousImage")); // NOI18N
401: previousButton.setFocusable(false);
402: previousButton
403: .setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
404: previousButton.setName("previousButton"); // NOI18N
405: previousButton
406: .setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
407: toolbar.add(previousButton);
408:
409: nextButton.setAction(actionMap.get("nextImage")); // NOI18N
410: nextButton.setFocusable(false);
411: nextButton
412: .setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
413: nextButton.setName("nextButton"); // NOI18N
414: nextButton
415: .setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
416: toolbar.add(nextButton);
417:
418: refreshButton.setAction(actionMap.get("refreshImage")); // NOI18N
419: refreshButton.setFocusable(false);
420: refreshButton
421: .setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
422: refreshButton.setName("refreshButton"); // NOI18N
423: refreshButton
424: .setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
425: toolbar.add(refreshButton);
426:
427: stopButton.setAction(actionMap.get("stopLoading")); // NOI18N
428: stopButton.setFocusable(false);
429: stopButton
430: .setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
431: stopButton.setName("stopButton"); // NOI18N
432: stopButton
433: .setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
434: toolbar.add(stopButton);
435: resourceMap.injectComponents(toolbar);
436:
437: statusPanel.setName("statusPanel"); // NOI18N
438:
439: statusMessageLabel.setName("statusMessageLabel"); // NOI18N
440:
441: statusAnimationLabel
442: .setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
443: statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N
444:
445: progressBar.setName("progressBar"); // NOI18N
446:
447: org.jdesktop.layout.GroupLayout statusPanelLayout = new org.jdesktop.layout.GroupLayout(
448: statusPanel);
449: statusPanel.setLayout(statusPanelLayout);
450: statusPanelLayout
451: .setHorizontalGroup(statusPanelLayout
452: .createParallelGroup(
453: org.jdesktop.layout.GroupLayout.LEADING)
454: .add(
455: statusPanelSeparator,
456: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
457: 480, Short.MAX_VALUE)
458: .add(
459: statusPanelLayout
460: .createSequentialGroup()
461: .addContainerGap()
462: .add(statusMessageLabel)
463: .addPreferredGap(
464: org.jdesktop.layout.LayoutStyle.RELATED,
465: 306, Short.MAX_VALUE)
466: .add(
467: progressBar,
468: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
469: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
470: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
471: .addPreferredGap(
472: org.jdesktop.layout.LayoutStyle.RELATED)
473: .add(statusAnimationLabel)
474: .addContainerGap()));
475: statusPanelLayout
476: .setVerticalGroup(statusPanelLayout
477: .createParallelGroup(
478: org.jdesktop.layout.GroupLayout.LEADING)
479: .add(
480: statusPanelLayout
481: .createSequentialGroup()
482: .add(
483: statusPanelSeparator,
484: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
485: 2,
486: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
487: .addPreferredGap(
488: org.jdesktop.layout.LayoutStyle.RELATED,
489: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
490: Short.MAX_VALUE)
491: .add(
492: statusPanelLayout
493: .createParallelGroup(
494: org.jdesktop.layout.GroupLayout.BASELINE)
495: .add(
496: statusMessageLabel)
497: .add(
498: statusAnimationLabel)
499: .add(
500: progressBar,
501: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
502: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
503: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
504: .add(3, 3, 3)));
505: resourceMap.injectComponents(statusPanel);
506:
507: setComponent(mainPanel);
508: setMenuBar(menuBar);
509: setStatusBar(statusPanel);
510: setToolBar(toolbar);
511: }// </editor-fold>//GEN-END:initComponents
512:
513: // Variables declaration - do not modify//GEN-BEGIN:variables
514: private javax.swing.JLabel imageLabel;
515: private javax.swing.JPanel mainPanel;
516: private javax.swing.JMenuBar menuBar;
517: private javax.swing.JButton nextButton;
518: private javax.swing.JButton previousButton;
519: private javax.swing.JProgressBar progressBar;
520: private javax.swing.JButton refreshButton;
521: private javax.swing.JScrollPane scrollPane;
522: private javax.swing.JLabel statusAnimationLabel;
523: private javax.swing.JLabel statusMessageLabel;
524: private javax.swing.JPanel statusPanel;
525: private javax.swing.JButton stopButton;
526: private javax.swing.JToolBar toolbar;
527: // End of variables declaration//GEN-END:variables
528:
529: private final Timer messageTimer;
530: private final Timer busyIconTimer;
531: private final Icon idleIcon;
532: private final Icon[] busyIcons = new Icon[15];
533: private int busyIconIndex = 0;
534:
535: private JDialog aboutBox;
536: }
|