MarsViewer Application
One powerful feature of the Scaffold framework is extending existing applications facilitated by layered properties. The MarsViewer extends the ImageViewer example application to leverage existing infrastructure (image loading).
The MarsViewer is basically a clone of the Task Demo (SingleFrameExample5) featured in the JSR 296 - Swing Application Framework web site.
Step 1 - Extend the ImageViewer
Recall the ImageViewer application which opens an image via a file chooser asynchronously, updating a progress bar and blocking input with a busy cursor while loading.
The MarsViewer is basically an ImageViewer, but differs in a few key ways:
- Uses a hard coded list of locations, instead of letting the user select a file
- Images are fetched from a remote server (via HTTP), instead of a local file
- Image loading can be interrupted, either to just stop, or to subsequently download another image instead (e.g. the next or previous image in the list)
First, lets just make the GUI changes - we'll hook up the new functionality later. All that's needed is a new properties file, and a few icons for the new actions. The Scaffold layered properties feature allows us to reuse the ImageViewer application completely in tact, and just override the parts (i.e. properties) that differ.
- Application Files
-
com/meldcraft/marsviewer/MarsViewer.properties com/meldcraft/marsviewer/aiNext.png com/meldcraft/marsviewer/aiPrev.png com/meldcraft/marsviewer/aiReload.png com/meldcraft/marsviewer/aiStop.png
- com/meldcraft/marsviewer/MarsViewer.properties
-
baseResourceLayers = com.meldcraft.imageviewer.ImageViewer Application.name = Huge Mars Rover Images From JPL Application.mainmenu = File View History Help Application.maintoolbar = Prev Next | Reload | Stop File.items = Exit View.items = Stop Reload History.items = Prev Next Nav.{1}.name = {2} Nav.{1}.accelerator = {3} Nav.replicateElement = Prev;_Back;control LEFT | control NP_LEFT,\ Next;_Forward;control RIGHT | control NP_RIGHT,\ Reload;_Reload;control R Stop.name = _Stop Stop.accelerator = control S Nav2.{1}.elementProperty.hideActionText = false Nav2.replicateElement = Prev, Next, Reload, Stop
Compile and run the application:
java -cp Scaffold.jar;ScaffoldGUIS.jar;ImageViewer.jar;MarsViewer/classes \ com.meldcraft.application.AppManager \ com.meldcraft.marsviewer.MarsViewerApplication
So, the menu and toolbar items have been updated, and the image area and status bar remain in tact - just what we wanted.
Note that the ImageViewer.jar from the ImageViewer application is used (unmodified) in the classpath. The baseResourceLayers property in MarsViewer.properties tells Scaffold to load the ImageViewer properties first, and then add to and override those properties with values in the MarsViewer.properties file (just one of several ways to layer properties, by the way).
Step 2 - Hook Up Image List
Now, we can add the list of images, and hook up the navigation buttons. We'll create a new POJO to manage the list - basically a circular queue that can give us the next, previous and current image location.
- Application Files
-
com/meldcraft/marsviewer/List.java com/meldcraft/marsviewer/MarsViewer.properties com/meldcraft/marsviewer/aiNext.png com/meldcraft/marsviewer/aiPrev.png com/meldcraft/marsviewer/aiReload.png com/meldcraft/marsviewer/aiStop.png
- com/meldcraft/marsviewer/List.java
-
package com.meldcraft.marsviewer; public class List { private String[] mItems; private int mIndex = 0; public void setItems(String[] items) { mItems = items; } public synchronized String next() { String ret = null; String[] items = mItems; if ((items != null) && (items.length > 0)) { mIndex = (mIndex + 1) % items.length; ret = items[mIndex]; } return ret; } public synchronized String previous() { String ret = null; String[] items = mItems; if ((items != null) && (items.length > 0)) { if (mIndex > items.length) { mIndex = items.length; } if (mIndex == 0) { mIndex = items.length - 1; } else { mIndex--; } ret = items[mIndex]; } return ret; } public synchronized String current() { String ret = null; String[] items = mItems; if ((items != null) && (items.length > 0)) { int idx = (mIndex >= items.length) ? items.length-1 : mIndex; ret = items[idx]; } return ret; } }
- com/meldcraft/marsviewer/MarsViewer.properties
-
baseResourceLayers = com.meldcraft.imageviewer.ImageViewer size = 600,500 Application.name = Huge Mars Rover Images From JPL Application.mainmenu = File View History Help Application.maintoolbar = Prev Next | Reload | Stop File.items = Exit View.items = Stop Reload History.items = Prev Next Nav.{1}.name = {2} Nav.{1}.accelerator = {3} Nav.{1}.target = List Nav.{1}.method = {4} Nav.replicateElement = Prev;_Back;control LEFT | control NP_LEFT;previous(),\ Next;_Forward;control RIGHT | control NP_RIGHT;next(),\ Reload;_Reload;control R;current() Stop.name = _Stop Stop.accelerator = control S Stop.ContextCheckingAction.key = LoadingImage Nav2.{1}.elementProperty.hideActionText = false Nav2.replicateElement = Prev, Next, Reload, Stop List.className = com.meldcraft.marsviewer.List Items.replicateElement = PIA03171, PIA02652, PIA05108, PIA02696,\ PIA05049, PIA05460, PIA07327, PIA05117,\ PIA05199, PIA05990, PIA03623 Items.replicateList.List.elementProperty.items = \ http://photojournal.jpl.nasa.gov/jpeg/{1}.jpg ImageLoader.elementProperty.addIIOReadProgressListener = \ <proxy@:onThreadInterrupt=<target>[0].abort@> Application.contextListeners+= ,Next.invokeReturn=NavInvoker,\ Prev.invokeReturn=NavInvoker,\ Reload.invokeReturn=NavInvoker,\ Stop.action=StopInvoker,\ Application.initialized=InitInvoker,\ FileOpenInvoker.invokeStop=StoppedInvoker,\ FileOpenInvoker.invokeStart+=,LoadingInvoker,\ FileOpenInvoker.invokeEnd+=,NotLoadingInvoker InitInvoker.forward = Reload.action=<null> NavInvoker.forward = FileOpenInvoker.jobAction=stop,\ Open.invokeReturn=<target> FileOpenInvoker.busyCursor = false FileOpenInvoker.invokeTargetThread = pool FileOpenInvoker.invokeTargetThread.poolCoalesce = true FileOpenInvoker.jobActionable = true TitleInvoker.invokeCondition = <target>.completed ClearIconInvoker.invokeCondition = <null> LoadingInvoker.forward = LoadingImage=true NotLoadingInvoker.forward = LoadingImage=false StopInvoker.forward = FileOpenInvoker.jobAction=stop StoppedInvoker.target = StatusBar.Label StoppedInvoker.method = setText StoppedInvoker.method.arg = Stopped.
Compile and run the application:
java -cp Scaffold.jar;ScaffoldGUIS.jar;ImageViewer.jar;MarsViewer/classes \ com.meldcraft.application.AppManager \ com.meldcraft.marsviewer.MarsViewerApplication
The first image is loaded when the application starts - displays loading progress just like the ImageViewer.
Notes:
- Unlike the ImageViewer, there is no busy cursor - the user can press Next, Previous, Reload or Stop at any time to interrupt the current download
- The Stop button is enabled only while an image is loading. This is configured by the ContextCheckingAction.key property, in coordinatation with the LoadingInvoker and NotLoadingInvoker - the default Scaffold action offers this means of setting the action enabled state via the ApplicationContext messaging.
- Pressing the Stop button will stop the current download as soon as a method on the IIOReadProgressListener proxy is invoked.
- Pressing Next, Previous or Reload will stop the current download (as soon as a method on the IIOReadProgressListener proxy is invoked), and then immediately start the new download.
- The += operator is used to append to the preexisting ImageViewer properties for Application.contextListeners, FileOpenInvoker.invokeStart, and FileOpenInvoker.invokeEnd.
Below is a summary of the console log during application startup that shows the ApplicationContext messaging:
... (application initialization) 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=Application.initialized value=null 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=Reload.action value=null 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=List value=com.meldcraft.marsviewer.List@1174b07 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=Reload.invokeStart value=0,false,null 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=Reload.invokeReturn value=http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=FileOpenInvoker.jobAction value=stop 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=Open.invokeReturn value=http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=ImageLoader value=com.meldcraft.imageviewer.ImageLoader@6b7920 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[main]: key=Reload.invokeEnd value=0,true,null 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=FileOpenInvoker.invokeStart value=1,false,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:08 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: OpeningInvoker using EDT thread override for javax.swing.JLabel[,2,5,438x16,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=,verticalAlignment=CENTER,verticalTextPosition=CENTER] 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=OpeningInvoker.invokeStart value=2,false,1,false,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=OpeningInvoker.invokeReturn value=null 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=OpeningInvoker.invokeEnd value=2,true,1,false,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:08 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=LoadingImage value=true 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=ImageLoader.addIIOReadProgressListenerProxy.imageStarted value=[Ljava.lang.Object;@1edc073 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=ImageLoader.addIIOReadProgressListenerProxy.imageProgress value=[Ljava.lang.Object;@121f1d 2007/08/31 13:44:09 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: ProgressInvoker using EDT thread override for javax.swing.JProgressBar[,440,5,150x16,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@116471f,flags=8,maximumSize=,minimumSize=,preferredSize=,orientation=HORIZONTAL,paintBorder=true,paintString=false,progressString=,indeterminateString=false] 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressInvoker.invokeStart value=3,false,[Ljava.lang.Object;@121f1d 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressInvoker.invokeReturn value=null 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressInvoker.invokeEnd value=3,true,[Ljava.lang.Object;@121f1d 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=ImageLoader.addIIOReadProgressListenerProxy.imageProgress value=[Ljava.lang.Object;@64f6cd ... (more progress) 2007/08/31 13:44:09 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: ProgressInvoker using EDT thread override for javax.swing.JProgressBar[,440,5,150x16,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@116471f,flags=8,maximumSize=,minimumSize=,preferredSize=,orientation=HORIZONTAL,paintBorder=true,paintString=false,progressString=,indeterminateString=false] 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressInvoker.invokeStart value=4,false,[Ljava.lang.Object;@c832d2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressInvoker.invokeReturn value=null 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressInvoker.invokeEnd value=4,true,[Ljava.lang.Object;@c832d2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=ImageLoader.addIIOReadProgressListenerProxy.imageProgress value=[Ljava.lang.Object;@166a22b ... (more progress) 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=FileOpenInvoker.invokeReturn value=BufferedImage@15b0afd: type = 5 ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@2e7820 transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 6144 height = 1536 #numDataElements 3 dataOff[0] = 2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: OpenIconInvoker using EDT thread override for javax.swing.JLabel[,0,0,588x367,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,horizontalAlignment=CENTER,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=,verticalAlignment=CENTER,verticalTextPosition=CENTER] 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=OpenIconInvoker.invokeStart value=5,false,BufferedImage@15b0afd: type = 5 ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@2e7820 transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 6144 height = 1536 #numDataElements 3 dataOff[0] = 2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=OpenIconInvoker.invokeReturn value=null 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=OpenIconInvoker.invokeEnd value=5,true,BufferedImage@15b0afd: type = 5 ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@2e7820 transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 6144 height = 1536 #numDataElements 3 dataOff[0] = 2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: ClearErrorInvoker using EDT thread override for javax.swing.JLabel[,2,5,438x16,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=Opening http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg,verticalAlignment=CENTER,verticalTextPosition=CENTER] 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ClearErrorInvoker.invokeStart value=6,false,BufferedImage@15b0afd: type = 5 ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@2e7820 transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 6144 height = 1536 #numDataElements 3 dataOff[0] = 2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ClearErrorInvoker.invokeReturn value=null 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ClearErrorInvoker.invokeEnd value=6,true,BufferedImage@15b0afd: type = 5 ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@2e7820 transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 6144 height = 1536 #numDataElements 3 dataOff[0] = 2 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=FileOpenInvoker.invokeEnd value=1,true,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:09 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: TitleInvoker using EDT thread override for javax.swing.JFrame[frame0,500,350,600x500,invalid,layout=java.awt.BorderLayout,title=Huge Mars Rover Images From JPL,resizable,normal,defaultCloseOperation=DO_NOTHING_ON_CLOSE,rootPane=javax.swing.JRootPane[,4,23,592x473,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true] 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=TitleInvoker.invokeStart value=7,false,1,true,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=TitleInvoker.invokeReturn value=null 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=TitleInvoker.invokeEnd value=7,true,1,true,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:09 PDT INFO: SwingApplicationContextChangedInvoker [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: ProgressCompleteInvoker using EDT thread override for javax.swing.JProgressBar[,440,5,150x16,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@116471f,flags=8,maximumSize=,minimumSize=,preferredSize=,orientation=HORIZONTAL,paintBorder=true,paintString=false,progressString=,indeterminateString=false] 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressCompleteInvoker.invokeStart value=8,false,1,true,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressCompleteInvoker.invokeReturn value=null 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[AWT-EventQueue-0]: key=ProgressCompleteInvoker.invokeEnd value=8,true,1,true,http://photojournal.jpl.nasa.gov/jpeg/PIA03171.jpg 2007/08/31 13:44:09 PDT INFO: SwingApplicationContext [Huge Mars Rover Images From JPL] contextChanged[ReflectInvoker-FileOpenInvoker-Thread-0]: key=LoadingImage value=false