Print

Print


Commit in java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui on MAIN
ApplicationWindow.java+36added 1050
Commands.java+21-231049 -> 1050
ErrorHandler.java+3-41049 -> 1050
Main.java+11049 -> 1050
MonitoringApplication.java+314-2041049 -> 1050
PlotFrame.java-291049 removed
RunPanel.java+1-11049 -> 1050
SystemStatusFrame.java-1701049 removed
WindowConfiguration.java+45added 1050
model/AbstractModel.java+12-11049 -> 1050
     /Configuration.java+9-11049 -> 1050
     /ConfigurationModel.java+67-41049 -> 1050
     /RunModel.java+1-11049 -> 1050
+510-438
2 added + 2 removed + 9 modified, total 13 files
Add loading and saving of GUI configuration.  Other minor refactoring and cleanup.

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
ApplicationWindow.java added at 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/ApplicationWindow.java	                        (rev 0)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/ApplicationWindow.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -0,0 +1,36 @@
+package org.hps.monitoring.gui;
+
+import java.awt.Dimension;
+
+import javax.swing.JFrame;
+
+/**
+ * An abstract class for windows in the monitoring application.
+ */
+abstract class ApplicationWindow extends JFrame {
+    
+    WindowConfiguration defaultWindowConfiguration;
+    WindowConfiguration currentWindowConfiguration;
+    
+    ApplicationWindow(String title) {
+        this.setTitle(title);
+    }
+            
+    final void updateWindowConfiguration(WindowConfiguration windowConfiguration) {
+        currentWindowConfiguration = windowConfiguration;
+        setSize(new Dimension(windowConfiguration.width, windowConfiguration.height));
+        setLocation(windowConfiguration.x, windowConfiguration.y);
+    }
+    
+    final void setDefaultWindowConfiguration(WindowConfiguration windowConfiguration) {
+        defaultWindowConfiguration = windowConfiguration;
+        updateWindowConfiguration(windowConfiguration);
+    }
+    
+    final void resetWindowConfiguration() {
+        if (defaultWindowConfiguration != null) {
+            setDefaultWindowConfiguration(defaultWindowConfiguration);
+            updateWindowConfiguration(defaultWindowConfiguration);
+        }
+    }
+}

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
Commands.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/Commands.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/Commands.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -5,43 +5,41 @@
  * A few commands handled only by sub-components are not listed here.
  */
 final class Commands {
-           
-    static final String DISCONNECT_ON_ERROR_CHANGED = "disconnectOnErrorChanged";
-    static final String DISCONNECT_ON_END_RUN_CHANGED = "disconnectOnEndRunChanged";
-    static final String STEERING_TYPE_CHANGED = "steeringTypeChanged";
-    static final String STEERING_RESOURCE_CHANGED = "steeringResourceChanged";
-    static final String LOG_TO_FILE_CHANGED = "logToFileChanged";
+
+    static final String AIDA_AUTO_SAVE = "aidaAutoSave";
     static final String AIDA_AUTO_SAVE_CHANGED = "aidaAutoSaveChanged";
-    static final String LOG_LEVEL_CHANGED = "logLevelChanged";    
-    
     static final String BLOCKING_CHANGED = "blockingChanged";
-    static final String VERBOSE_CHANGED = "verboseChanged";
-    static final String WAIT_MODE_CHANGED = "waitModeChanged";
-    
-    static final String DATA_SOURCE_TYPE_CHANGED = "dataSourceTypeChanged";
-    static final String PROCESSING_STAGE_CHANGED = "processingStageChanged";
-       
-    static final String AIDA_AUTO_SAVE = "aidaAutoSave";
-    static final String CLEAR_LOG_TABLE = "clearLogTable";
     static final String CHOOSE_LOG_FILE = "chooseLogFile";
     static final String CHOOSE_STEERING_FILE = "chooseSteeringFile";
     static final String CONNECT = "connect";
+    static final String CLEAR_LOG_TABLE = "clearLogTable";
+    static final String DATA_SOURCE_TYPE_CHANGED = "dataSourceTypeChanged";
     static final String DISCONNECT = "disconnect";
+    static final String DISCONNECT_ON_ERROR_CHANGED = "disconnectOnErrorChanged";
+    static final String DISCONNECT_ON_END_RUN_CHANGED = "disconnectOnEndRunChanged";
     static final String EXIT = "exit";
-    static final String LOAD_DEFAULT_CONFIG_FILE = "loadDefaultConfigFile";    
+    static final String LOAD_DEFAULT_CONFIG_FILE = "loadDefaultConfigFile";
+    static final String LOG_LEVEL_CHANGED = "logLevelChanged";
     static final String LOG_TO_FILE = "logToFile";
-    static final String LOG_TO_TERMINAL = "logToTerminal";    
+    static final String LOG_TO_FILE_CHANGED = "logToFileChanged";
+    static final String LOG_TO_TERMINAL = "logToTerminal";
     static final String NEXT = "next";
-    static final String PAUSE = "pause";
+    static final String PAUSE = "pause";    
+    static final String PROCESSING_STAGE_CHANGED = "processingStageChanged";
+    static final String RESTORE_DEFAULT_GUI_LAYOUT = "restoreDefaultGuiLayout";
     static final String RESUME = "resume";
+    static final String SAVE_CONFIG_FILE = "saveConfigFile";
+    static final String SAVE_LAYOUT = "saveLayout";
     static final String SAVE_LOG_TABLE = "saveLogTable";
     static final String SAVE_PLOTS = "savePlots";
     static final String SCREENSHOT = "screenshot";
-    static final String SAVE_CONFIG_FILE = "saveConfigFile";
-    static final String SET_EVENT_BUILDER = "setEventBuilder";    
-
-    static final String SET_STEERING_RESOURCE = "setSteeringResource";            
     static final String SELECT_CONFIG_FILE = "selectConfigFile";
     static final String SELECT_LOG_FILE = "logToFile";
+    static final String SET_EVENT_BUILDER = "setEventBuilder";
+    static final String SET_STEERING_RESOURCE = "setSteeringResource";
     static final String SHOW_SETTINGS = "showSettings";
+    static final String STEERING_TYPE_CHANGED = "steeringTypeChanged";
+    static final String STEERING_RESOURCE_CHANGED = "steeringResourceChanged";                   
+    static final String VERBOSE_CHANGED = "verboseChanged";
+    static final String WAIT_MODE_CHANGED = "waitModeChanged";                         
 }
\ No newline at end of file

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
ErrorHandler.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/ErrorHandler.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/ErrorHandler.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -10,12 +10,12 @@
 /**
  * <p>
  * An error handling class which is able to do any of the following,
- * depending on what the caller wants to do with the error.
+ * depending on how the users wants to handle the error.
  * </p>
  * <ul>
  * <li>Print a message</li>
  * <li>Print the stack trace</li>
- * <li>Log to a Logger</li>
+ * <li>Log message to a Logger</li>
  * <li>Show an error dialog</li>
  * <li>Raise an exception</li>
  * <li>Exit the application</li>
@@ -23,8 +23,7 @@
  * </p>
  * It mostly uses the "builder" pattern so that the various handling methods
  * can be easily chained, where appropriate.  Some methods are not available
- * for chaining when it doesn't make sense, e.g. popping up a dialog should
- * happen last so this is not chainable.
+ * for chaining when it doesn't make sense.
  * </p>
  */
 class ErrorHandler {

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
Main.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/Main.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/Main.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -17,6 +17,7 @@
  * This is the front-end for running the monitoring app via a 
  * {@link #main(String[])} method.
  */
+// FIXME: Move to org.hps.monitoring instead of gui package.
 public class Main {
 
     /**

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
MonitoringApplication.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/MonitoringApplication.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/MonitoringApplication.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -11,13 +11,17 @@
 import static org.hps.monitoring.gui.Commands.LOG_TO_TERMINAL;
 import static org.hps.monitoring.gui.Commands.NEXT;
 import static org.hps.monitoring.gui.Commands.PAUSE;
+import static org.hps.monitoring.gui.Commands.RESTORE_DEFAULT_GUI_LAYOUT;
 import static org.hps.monitoring.gui.Commands.RESUME;
 import static org.hps.monitoring.gui.Commands.SAVE_CONFIG_FILE;
+import static org.hps.monitoring.gui.Commands.SAVE_LAYOUT;
 import static org.hps.monitoring.gui.Commands.SAVE_LOG_TABLE;
 import static org.hps.monitoring.gui.Commands.SAVE_PLOTS;
 import static org.hps.monitoring.gui.Commands.SCREENSHOT;
 import static org.hps.monitoring.gui.Commands.SELECT_CONFIG_FILE;
 import static org.hps.monitoring.gui.Commands.SHOW_SETTINGS;
+import static org.hps.monitoring.gui.model.ConfigurationModel.MONITORING_APPLICATION_LAYOUT_PROPERTY;
+import static org.hps.monitoring.gui.model.ConfigurationModel.SAVE_LAYOUT_PROPERTY;
 
 import java.awt.Dimension;
 import java.awt.GridBagConstraints;
@@ -32,6 +36,8 @@
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -50,6 +56,7 @@
 import java.util.logging.Logger;
 
 import javax.imageio.ImageIO;
+import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JDialog;
 import javax.swing.JFileChooser;
 import javax.swing.JFrame;
@@ -83,12 +90,6 @@
 import org.hps.record.composite.EventProcessingThread;
 import org.hps.record.enums.DataSourceType;
 import org.hps.record.et.EtConnection;
-import org.jlab.coda.et.EtAttachment;
-import org.jlab.coda.et.EtConstants;
-import org.jlab.coda.et.EtStation;
-import org.jlab.coda.et.EtStationConfig;
-import org.jlab.coda.et.EtSystem;
-import org.jlab.coda.et.EtSystemOpenConfig;
 import org.lcsim.job.JobControlManager;
 import org.lcsim.util.Driver;
 import org.lcsim.util.aida.AIDA;
@@ -96,22 +97,24 @@
 /**
  * This class is the implementation of the GUI for the Monitoring Application.
  */
-public final class MonitoringApplication extends JFrame implements ActionListener, SystemStatusListener {
+// TODO: Move GUI/window functionality to a new class.  (This one is too big!)
+public final class MonitoringApplication extends ApplicationWindow implements ActionListener, SystemStatusListener, PropertyChangeListener {
 
     // Top-level Swing components.
     private JPanel mainPanel;
     private EventButtonsPanel buttonsPanel;
     private ConnectionStatusPanel connectionStatusPanel;
     private RunPanel runPanel;
+    private SettingsDialog settingsDialog;
+    private PlotWindow plotWindow;
+    private SystemStatusWindow systemStatusWindow;
     private JMenuBar menuBar;
-    private SettingsDialog settingsDialog;
-    private PlotFrame plotFrame;
-    private SystemStatusFrame systemStatusFrame;
 
     // References to menu items that will be toggled depending on application state.
     private JMenuItem savePlotsItem;
     private JMenuItem logItem;
     private JMenuItem terminalItem;
+    private JMenuItem saveLayoutItem;
 
     // Saved references to System.out and System.err in case need to reset.
     private final PrintStream sysOut = System.out;
@@ -122,7 +125,6 @@
 
     // ET connection parameters and state.
     private EtConnection connection;
-    //private ConnectionStatus connectionStatus = ConnectionStatus.DISCONNECTED;
 
     // Event processing objects.
     private JobControlManager jobManager;
@@ -139,17 +141,16 @@
     private JTable logTable;
     private static Level DEFAULT_LOG_LEVEL = Level.INFO;
 
-    // Format for screenshots.  
-    // FIXME: This is hard-coded to PNG format.
-    private static final String screenshotFormat = "png";
+    // Graogucs format for screenshots.  
+    private static final String SCREENSHOT_FORMAT = "png";
 
     // Format of date field for log.
     private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM-dd-yyyy HH:mm:ss.SSS");
 
-    // GUI size settings.
+    // Some useful GUI size settings.
     private static final int SCREEN_WIDTH = ScreenUtil.getScreenWidth();
     private static final int SCREEN_HEIGHT = ScreenUtil.getScreenHeight();
-    private final static int LOG_TABLE_WIDTH = 700; // FIXME: Should be set from main panel width.
+    private final static int LOG_TABLE_WIDTH = 700; /* FIXME: Should be set from main panel width. */
     private final static int LOG_TABLE_HEIGHT = 270;
     private static final int MAIN_FRAME_HEIGHT = ScreenUtil.getScreenHeight() / 2;
     private static final int MAIN_FRAME_WIDTH = 650;
@@ -170,60 +171,203 @@
      * Constructor for the monitoring application.
      */
     public MonitoringApplication() {
+        
+        super(getApplicationTitle());
+        
+        // Add the application as a property change listener on the configuration model.
+        configurationModel.addPropertyChangeListener(this);
     }
         
     /**
-     * Perform all intialization on start up.
+     * Initialize GUI components and all other necessary objects
+     * to put the application in a usable state.
      */
     public void initialize() {
-        
+                
         // Create and configure the logger.
         setupLogger();
-        
+                
         // Setup the error handling class.
         setupErrorHandler();
         
         // Setup an uncaught exception handler.
         setupUncaughtExceptionHandler();
 
-        // Setup the application menus.
-        createApplicationMenu();
-
         // Create the main GUI panel.
         createMainPanel();
 
         // Create the log table GUI component.
         createLogTable();
-
+        
+        // Create settings dialog window.
+        createSettingsDialog();
+                
+        // Setup the application menus.
+        createMenuBar();
+                        
+        // Create the system status window.
+        createSystemStatusWindow();
+        
         // Configuration of window for showing plots.
-        createPlotFrame();
+        createPlotWindow();
         
-        // Create the system status window.
-        createSystemStatusFrame();
-
         // Setup AIDA.
         setupAida();
-
+       
         // Configure the application's primary JFrame.
         configApplicationFrame();
-
-        // Create settings dialog window.
-        createSettingsDialog();
         
-        // Register the ConfigurationModel with sub-components.
-        setupConfigurationModel();
-                
-        // Load the current configuration, either the default or from command line arg.
+        // Load the current configuration, which will push values into the GUI.
         loadConfiguration();
-
+        
         // Log that the application started successfully.
         log(Level.CONFIG, "Application initialized successfully.");
     }
-         
+    
+    /**
+     * The action handler method for the application.
+     * @param e The event to handle.
+     */
+    public void actionPerformed(ActionEvent e) {
+        String cmd = e.getActionCommand();
+        if (CONNECT.equals(cmd)) {
+            // Run the start session method on a seperate thread.
+            new Thread() {
+                public void run() {
+                    startSession();
+                }                
+            }.start();
+        } else if (DISCONNECT.equals(cmd)) {
+            // Run the stop session method on a seperate thread.
+            new Thread() {
+                public void run() {
+                    stopSession();
+                }
+            }.start();            
+        } else if (SAVE_PLOTS.equals(cmd)) {
+            savePlots();
+        } else if (CHOOSE_LOG_FILE.equals(cmd)) {
+            chooseLogFile();
+        } else if (LOG_TO_TERMINAL.equals(cmd)) {
+            logToTerminal();
+        } else if (SCREENSHOT.equals(cmd)) {
+            chooseScreenshot();
+        } else if (EXIT.equals(cmd)) {
+            exit();
+        } else if (SAVE_LOG_TABLE.equals(cmd)) {
+            saveLogTableToFile();
+        } else if (CLEAR_LOG_TABLE.equals(cmd)) {
+            clearLogTable();
+        } else if (PAUSE.equals(cmd)) {
+            pauseEventProcessing();
+        } else if (NEXT.equals(cmd)) {
+            nextEvent();
+        } else if (RESUME.equals(cmd)) {
+            resumeEventProcessing();
+        } else if (LOG_LEVEL_CHANGED.equals(cmd)) {
+            setLogLevel();
+        } else if (AIDA_AUTO_SAVE.equals(cmd)) {
+            getJobSettingsPanel().chooseAidaAutoSaveFile();
+        } else if (SHOW_SETTINGS.equals(cmd)) {
+            showSettingsDialog();
+        } else if (SELECT_CONFIG_FILE.equals(cmd)) {
+            chooseConfigurationFile();
+        } else if (SAVE_CONFIG_FILE.equals(cmd)) {
+            updateLayoutConfiguration(); /* Save current GUI layout settings first, if needed. */
+            saveConfigurationFile();
+        } else if (LOAD_DEFAULT_CONFIG_FILE.equals(cmd)) {
+            loadDefaultConfigFile();
+        } else if (SAVE_LAYOUT.equals(cmd)) {
+            setSaveLayout();
+        } else if (RESTORE_DEFAULT_GUI_LAYOUT.equals(cmd)) {
+            restoreDefaultLayout();
+        }
+    }
+        
+    /**
+     * Set the GUI to visible.
+     */
+    public void setVisible(boolean visible) {
+        
+        super.setVisible(true);
+        
+        this.systemStatusWindow.setVisible(true);
+        
+        // FIXME: If this is done earlier before app is visible, then the GUI will fail to show!
+        this.connectionStatusPanel.setConnectionStatus(ConnectionStatus.DISCONNECTED);
+    }
+    
+    /**
+     * Set the Configuration but don't update the ConfigurationModel.
+     * @param configuration
+     */
+    public void setConfiguration(Configuration configuration) {
+        this.configuration = configuration;
+    }    
+    
+    /**
+     * Handle a property change event.
+     * @evt The property change event.
+     */
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+                       
+        if (evt.getPropertyName().equals("ancestor"))
+            return;                                          
+        Object value = evt.getNewValue();
+        if (evt.getPropertyName().equals(SAVE_LAYOUT_PROPERTY)) {
+            saveLayoutItem.setSelected((Boolean) value);
+        } else if (evt.getPropertyName().equals(MONITORING_APPLICATION_LAYOUT_PROPERTY)){
+            updateWindowConfiguration(new WindowConfiguration((String) value));
+        } else if (evt.getPropertyName().equals(ConfigurationModel.SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY)) {
+            if (systemStatusWindow != null) {
+                systemStatusWindow.updateWindowConfiguration(new WindowConfiguration((String) value));
+            } else {
+                System.err.println("ERROR: The systemStatusFrame is null!");
+            }
+        } else if (evt.getPropertyName().equals(ConfigurationModel.PLOT_FRAME_LAYOUT_PROPERTY)) {
+            if (plotWindow != null) {
+                plotWindow.updateWindowConfiguration(new WindowConfiguration((String) value));
+            } else {
+                System.err.println("ERROR: The plotWindow is null!");
+            }
+        }
+    }
+    
+    /**
+     * Hook for logging all status changes from the system status monitor.
+     */
+    @Override
+    public void statusChanged(SystemStatus status) {
+        
+        // Choose the appropriate log level.
+        Level level = Level.INFO;
+        if (status.getStatusCode().equals(Level.WARNING)) {
+            level = Level.WARNING;
+        } else if (status.getStatusCode().ordinal() >= StatusCode.ERROR.ordinal()) {
+            level = Level.SEVERE;
+        }
+       
+        // Log all status changes.
+        log(level, "STATUS, "
+                + "subsys: " + status.getSubsystem() + ", "
+                + "code: " + status.getStatusCode().name() + ", "                 
+                + "descr: " + status.getDescription() + ", "                 
+                + "mesg: " + status.getMessage());
+    }
+    
+    /* -------------------------- private methods ----------------------------- */
+               
+    /**
+     * Setup the error handler.
+     */
     private void setupErrorHandler() {
         errorHandler = new ErrorHandler(this, logger);
     }
     
+    /**
+     * Setup the uncaught exception handler which will trap unhandled errors.
+     */
     private void setupUncaughtExceptionHandler() {
         Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {            
             public void uncaughtException(Thread thread, Throwable exception) {
@@ -234,45 +378,59 @@
             }
         });
     }
-            
+     
+    /**
+     * Create the settings dialog GUI component.
+     */
     private void createSettingsDialog() {
+        
+        // Create and configure the settings dialog which has sub-panels for application configuration.
         settingsDialog = new SettingsDialog();
         settingsDialog.getSettingsPanel().addActionListener(this);
         getJobSettingsPanel().addActionListener(this);
+        
+        // Push the ConfigurationModel to the job settings dialog.
+        getJobSettingsPanel().setConfigurationModel(configurationModel);
+        getConnectionSettingsPanel().setConfigurationModel(configurationModel);
+        settingsDialog.getSettingsPanel().getDataSourcePanel().setConfigurationModel(configurationModel);        
     }
 
-    private void createPlotFrame() {
-        plotFrame = new PlotFrame();                
-        plotFrame.setSize(SCREEN_WIDTH - MAIN_FRAME_WIDTH, SCREEN_HEIGHT);
-        plotFrame.setLocation(
-                (int)(ScreenUtil.getBoundsX(0)) + MAIN_FRAME_WIDTH,
-                plotFrame.getY());
+    /**
+     * Create the plot window.
+     */
+    private void createPlotWindow() {
+        
+        // Create the JFrame.
+        plotWindow = new PlotWindow();
+        
+        // Set initial size and position which might be overridden later.
+        plotWindow.setDefaultWindowConfiguration(
+                new WindowConfiguration(
+                        SCREEN_WIDTH - MAIN_FRAME_WIDTH,
+                        SCREEN_HEIGHT,
+                        (int)(ScreenUtil.getBoundsX(0)) + MAIN_FRAME_WIDTH,
+                        plotWindow.getY()
+                ));               
     }
     
-    private void createSystemStatusFrame() {
-        systemStatusFrame = new SystemStatusFrame();
-        systemStatusFrame.setLocation(
+    private void createSystemStatusWindow() {
+        systemStatusWindow = new SystemStatusWindow();
+        WindowConfiguration wc = new WindowConfiguration(
+                650, /* FIXME: Hard-coded width setting. */
+                ScreenUtil.getScreenHeight() / 2,
                 (int)ScreenUtil.getBoundsX(0),
                 MAIN_FRAME_HEIGHT);
+        systemStatusWindow.setMinimumSize(new Dimension(wc.width, wc.height));
+        systemStatusWindow.setDefaultWindowConfiguration(wc);
     }
     
-    public void setVisible(boolean visible) {
-        
-        super.setVisible(true);
-        
-        this.systemStatusFrame.setVisible(true);
-        
-        // FIXME: If this is done earlier before app is visible, the GUI will fail to show!
-        this.connectionStatusPanel.setConnectionStatus(ConnectionStatus.DISCONNECTED);
-    }
-
     /**
      * Configure the AIDA plotting backend.
      */
     private void setupAida() {
         MonitoringAnalysisFactory.register();
         MonitoringAnalysisFactory.configure();
-        MonitoringPlotFactory.setRootPane(this.plotFrame.getPlotPane());
+        MonitoringPlotFactory.setRootPane(this.plotWindow.getPlotPane());
     }
 
     /**
@@ -320,7 +478,7 @@
     /**
      * Create the application menu bar and menu items.
      */
-    private void createApplicationMenu() {
+    private void createMenuBar() {
 
         menuBar = new JMenuBar();
 
@@ -332,28 +490,46 @@
         loadConfigItem.addActionListener(this);
         loadConfigItem.setMnemonic(KeyEvent.VK_C);
         loadConfigItem.setActionCommand(SELECT_CONFIG_FILE);
-        loadConfigItem.setToolTipText("Load application settings from a properties file.");
+        loadConfigItem.setToolTipText("Load application settings from a properties file");
         applicationMenu.add(loadConfigItem);
         
         JMenuItem saveConfigItem = new JMenuItem("Save Settings ...");
         saveConfigItem.addActionListener(this);
         saveConfigItem.setMnemonic(KeyEvent.VK_S);
         saveConfigItem.setActionCommand(SAVE_CONFIG_FILE);        
-        saveConfigItem.setToolTipText("Save settings to a properties file.");
+        saveConfigItem.setToolTipText("Save settings to a properties file");
         applicationMenu.add(saveConfigItem);
         
         JMenuItem settingsItem = new JMenuItem("Show Settings ...");
         settingsItem.setMnemonic(KeyEvent.VK_P);
         settingsItem.setActionCommand(SHOW_SETTINGS);
         settingsItem.addActionListener(this);
-        settingsItem.setToolTipText("Show application settings menu.");
+        settingsItem.setToolTipText("Show application settings menu");
         applicationMenu.add(settingsItem);
+       
+        applicationMenu.addSeparator();
+        
+        saveLayoutItem = new JCheckBoxMenuItem("Save GUI Layout");
+        saveLayoutItem.setActionCommand(SAVE_LAYOUT);
+        saveLayoutItem.addActionListener(this);
+        saveLayoutItem.setToolTipText("Include current GUI layout when saving settings.");
+        saveLayoutItem.setSelected(configurationModel.getSaveLayout()); /* Initial setting from config. */
+        saveLayoutItem.addPropertyChangeListener(this); /* Any subsequent changes to model will activate this. */
+        applicationMenu.add(saveLayoutItem);
+        
+        JMenuItem restoreLayoutItem = new JMenuItem("Restore Default GUI Layout");
+        restoreLayoutItem.setActionCommand(RESTORE_DEFAULT_GUI_LAYOUT);
+        restoreLayoutItem.addActionListener(this);
+        restoreLayoutItem.setToolTipText("Restore the GUI windows to their default positions and sizes");
+        applicationMenu.add(restoreLayoutItem);
+        
+        applicationMenu.addSeparator();
                 
         JMenuItem exitItem = new JMenuItem("Exit");
         exitItem.setMnemonic(KeyEvent.VK_X);
         exitItem.setActionCommand(EXIT);
         exitItem.addActionListener(this);
-        exitItem.setToolTipText("Exit from the application.");
+        exitItem.setToolTipText("Exit from the application");
         applicationMenu.add(exitItem);
         
         JMenu plotsMenu = new JMenu("Plots");
@@ -417,7 +593,7 @@
         screenshotItem.setMnemonic(KeyEvent.VK_N);
         screenshotItem.setActionCommand(SCREENSHOT);
         screenshotItem.addActionListener(this);
-        screenshotItem.setToolTipText("Save a full screenshot to a " + screenshotFormat + " file.");
+        screenshotItem.setToolTipText("Save a full screenshot to a " + SCREENSHOT_FORMAT + " file.");
         utilMenu.add(screenshotItem);
     }
    
@@ -484,64 +660,9 @@
     }
    
     /**
-     * The action handler method for the entire application.
-     * @param e The event to handle.
-     */
-    public void actionPerformed(ActionEvent e) {
-        String cmd = e.getActionCommand();
-        if (CONNECT.equals(cmd)) {
-            // Run the start session method on a seperate thread.
-            new Thread() {
-                public void run() {
-                    startSession();
-                }                
-            }.start();
-        } else if (DISCONNECT.equals(cmd)) {
-            // Run the stop session method on a seperate thread.
-            new Thread() {
-                public void run() {
-                    stopSession();
-                }
-            }.start();            
-        } else if (SAVE_PLOTS.equals(cmd)) {
-            savePlots();
-        } else if (CHOOSE_LOG_FILE.equals(cmd)) {
-            chooseLogFile();
-        } else if (LOG_TO_TERMINAL.equals(cmd)) {
-            logToTerminal();
-        } else if (SCREENSHOT.equals(cmd)) {
-            chooseScreenshot();
-        } else if (EXIT.equals(cmd)) {
-            exit();
-        } else if (SAVE_LOG_TABLE.equals(cmd)) {
-            saveLogTableToFile();
-        } else if (CLEAR_LOG_TABLE.equals(cmd)) {
-            clearLogTable();
-        } else if (PAUSE.equals(cmd)) {
-            pauseEventProcessing();
-        } else if (NEXT.equals(cmd)) {
-            nextEvent();
-        } else if (RESUME.equals(cmd)) {
-            resumeEventProcessing();
-        } else if (LOG_LEVEL_CHANGED.equals(cmd)) {
-            setLogLevel();
-        } else if (AIDA_AUTO_SAVE.equals(cmd)) {
-            getJobSettingsPanel().chooseAidaAutoSaveFile();
-        } else if (SHOW_SETTINGS.equals(cmd)) {
-            showSettingsWindow();
-        } else if (SELECT_CONFIG_FILE.equals(cmd)) {
-            chooseConfigurationFile();
-        } else if (SAVE_CONFIG_FILE.equals(cmd)) {
-            saveConfigurationFile();
-        } else if (LOAD_DEFAULT_CONFIG_FILE.equals(cmd)) {
-            loadDefaultConfigFile();
-        } 
-    }
-
-    /**
      * Show the settings window.
      */
-    private void showSettingsWindow() {
+    private void showSettingsDialog() {
         settingsDialog.setVisible(true);
     }
        
@@ -562,7 +683,6 @@
      * @param status The connection status.
      */
     private void setConnectionStatus(ConnectionStatus status) {
-        //connectionStatus = status;
         connectionStatusPanel.setConnectionStatus(status);
         log(Level.FINE, "Connection status changed to <" + status.name() + ">");
         logHandler.flush();
@@ -572,18 +692,27 @@
      * Setup the primary <code>JFrame</code> for the application.
      */
     private void configApplicationFrame() {
-        mainPanel.setOpaque(true);
-        setTitle(getApplicationTitle());
+        
+        mainPanel.setOpaque(true);        
+
+        // Configure window size and position.
+        WindowConfiguration wc = new WindowConfiguration(
+                MAIN_FRAME_WIDTH, 
+                MAIN_FRAME_HEIGHT, 
+                (int)ScreenUtil.getBoundsX(0), 
+                getY());
+        setMinimumSize(new Dimension(wc.width, wc.height));
+        setPreferredSize(new Dimension(wc.width, wc.height));
+        setDefaultWindowConfiguration(wc);
+        
+        setResizable(true);
         setContentPane(mainPanel);
         setJMenuBar(menuBar);
         setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
-        setPreferredSize(new Dimension(MAIN_FRAME_WIDTH, MAIN_FRAME_HEIGHT));
-        setMinimumSize(new Dimension(MAIN_FRAME_WIDTH, MAIN_FRAME_HEIGHT));        
-        setResizable(true);        
-        setLocation((int)ScreenUtil.getBoundsX(0), getY());
-        pack();              
+                                
+        pack();                                     
     }
-        
+               
     /**
      * Save all the plots to a file using a <code>JFileChooser</code>.
      */
@@ -749,10 +878,6 @@
         if (connection != null) {
             cleanupEtConnection();
         }
-        if (plotFrame.isVisible())
-            plotFrame.setVisible(false);
-        if (systemStatusFrame.isVisible())
-            systemStatusFrame.setVisible(false);
         setVisible(false);
         System.exit(0);
     }
@@ -768,8 +893,8 @@
         if (r == JFileChooser.APPROVE_OPTION) {
             String fileName = fc.getSelectedFile().getPath();
             int extIndex = fileName.lastIndexOf(".");
-            if ((extIndex == -1) || !(fileName.substring(extIndex + 1, fileName.length())).toLowerCase().equals(screenshotFormat)) {
-                fileName = fileName + "." + screenshotFormat;
+            if ((extIndex == -1) || !(fileName.substring(extIndex + 1, fileName.length())).toLowerCase().equals(SCREENSHOT_FORMAT)) {
+                fileName = fileName + "." + SCREENSHOT_FORMAT;
             }
             takeScreenshot(fileName);
             log(Level.INFO, "Screenshot saved to file <" + fileName + ">");
@@ -786,7 +911,7 @@
         try {
             Robot robot = new Robot();
             BufferedImage image = robot.createScreenCapture(screenRectangle);
-            ImageIO.write(image, screenshotFormat, new File(fileName));
+            ImageIO.write(image, SCREENSHOT_FORMAT, new File(fileName));
         } catch (Exception e) {
             errorHandler.setError(e)
                 .setMessage("Failed to take screenshot.")
@@ -1210,7 +1335,7 @@
      */
     private void setupSystemStatusMonitor() {
         // Clear the system status monitor table.        
-        systemStatusFrame.getTableModel().clear();
+        systemStatusWindow.getTableModel().clear();
         
         // Get the global registry of SystemStatus objects.
         SystemStatusRegistry registry = SystemStatusRegistry.getSystemStatusRegistery();
@@ -1218,7 +1343,7 @@
         // Process the SystemStatus objects.
         for (SystemStatus systemStatus : registry.getSystemStatuses()) {
             // Add a row to the table for every SystemStatus.
-            systemStatusFrame.getTableModel().addSystemStatus(systemStatus);
+            systemStatusWindow.getTableModel().addSystemStatus(systemStatus);
             
             // Add this class as a listener so all status changes can be logged.
             systemStatus.addListener(this);
@@ -1234,12 +1359,12 @@
         resetAidaTree();
 
         // Plot frame visible?
-        if (!plotFrame.isVisible())
+        if (!plotWindow.isVisible())
             // Turn on plot frame if it is off.
-            plotFrame.setVisible(true);
+            plotWindow.setVisible(true);
             
         // Reset plots.
-        plotFrame.reset(); 
+        plotWindow.reset(); 
     }
 
     /**
@@ -1325,11 +1450,11 @@
                 dialog.setVisible(false);
                 dialog.dispose();
                 MonitoringApplication.this.setEnabled(true);
-                plotFrame.setEnabled(true);
+                plotWindow.setEnabled(true);
             }
         });
         MonitoringApplication.this.setEnabled(false);
-        plotFrame.setEnabled(false);
+        plotWindow.setEnabled(false);
         dialog.setVisible(visible);
         return dialog;
     }    
@@ -1341,25 +1466,18 @@
      * In this case, event processing will exit later when the ET system goes down.
      */
     private void stopEventProcessing() {
-        //System.out.println("MonitoringApplication.stopEventProcessing");
-                       
+            
+        // Is the event processing thread not null? 
         if (processingThread != null) {
-            //System.out.println("processingThread not null");
+            
             // Is the event processing thread actually still alive?
             if (processingThread.isAlive()) {
-                
-                //System.out.println("processing thread is alive...");
-                //System.out.println("killing session watchdog");
 
                 // Interrupt and kill the event processing watchdog thread if necessary.
                 killSessionWatchdogThread();
-                
-                //System.out.println("stopping event processing chain...");
-
-                // Request the event processing to stop.          
-                loop.execute(Command.STOP);
-                
-                //System.out.println("requested stop of event processing");
+               
+                // Request the event processing loop to execute stop.
+                loop.execute(Command.STOP);                
             }
 
             // Wait for the event processing thread to finish.  This should just return
@@ -1368,26 +1486,20 @@
                 // In the case where ET is configured for sleep or timed wait, an untimed join could 
                 // block forever, so only wait for ~1 second before continuing.  The EventProcessingChain
                 // should still cleanup automatically when its thread completes after the ET system goes down.
-                //System.out.println("joining event processing thread...");
                 processingThread.join(1000);
-                //System.out.println("joined event processing thread!");
             } catch (InterruptedException e) {
                 // Don't know when this would ever happen.
-                //System.out.println("join was interrupted!");
             }
        
-            // Handle last error that occurred in event processing.
+            // Notify of last error that occurred in event processing.
             if (loop.getLastError() != null) {
-                //System.out.println("last error: " + processingChain.getLastError().getMessage());
                 errorHandler.setError(loop.getLastError()).log().printStackTrace();
             }
        
-            // Reset event processing objects.
-            //System.out.println("setting objects to null...");
+            // Reset event processing objects for next session.
             loop.dispose();
             loop = null;
             processingThread = null;
-            //System.out.println("stopEventProcessing - done!");
         }
     }
 
@@ -1395,7 +1507,9 @@
      * Kill the current session watchdog thread.
      */
     private void killSessionWatchdogThread() {
+        // Is the session watchdog thread not null?
         if (sessionWatchdogThread != null) {
+            // Is the thread still alive?
             if (sessionWatchdogThread.isAlive()) {
                 // Interrupt the thread which should cause it to stop.
                 sessionWatchdogThread.interrupt();
@@ -1403,8 +1517,10 @@
                     // This should always work once the thread is interupted.
                     sessionWatchdogThread.join();
                 } catch (InterruptedException e) {
+                    // Should never happen.
                 }
             }
+            // Set the thread object to null.
             sessionWatchdogThread = null;
         }
     }
@@ -1485,28 +1601,44 @@
         int r = fc.showSaveDialog(mainPanel);
         if (r == JFileChooser.APPROVE_OPTION) {
             File f = fc.getSelectedFile();
-            log(Level.CONFIG, "Saving configuration to file <" + f.getPath() + ">");
-            configuration.writeToFile(f);            
+            log(Level.CONFIG, "Saving configuration to file <" + f.getPath() + ">");            
+            configuration.writeToFile(f);
         }
     }
-       
-    /**
-     * Setup the <code>ConfigurationModel</code> by registering it with sub-components.
-     */
-    private void setupConfigurationModel() {        
-        getJobSettingsPanel().setConfigurationModel(configurationModel);
-        getConnectionSettingsPanel().setConfigurationModel(configurationModel);
-        settingsDialog.getSettingsPanel().getDataSourcePanel().setConfigurationModel(configurationModel);
+    
+    private void updateLayoutConfiguration() {
+        // Should the GUI config be saved?
+        if (configurationModel.getSaveLayout()) {
+            // Push the current GUI settings into the configuration.
+            saveLayoutConfiguration();
+        } else {
+            // Remove any GUI settings from the configuration.
+            clearLayoutConfiguration();
+        }
     }
-
-    /**
-     * Set the Configuration but don't update the ConfigurationModel.
-     * @param configuration
-     */
-    public void setConfiguration(Configuration configuration) {
-        this.configuration = configuration;
-    }    
     
+    private void saveLayoutConfiguration() {
+        configurationModel.setMonitoringApplicationLayout(new WindowConfiguration(this).toString());
+        configurationModel.setSystemStatusFrameLayout(new WindowConfiguration(systemStatusWindow).toString());
+        configurationModel.setPlotFrameLayout(new WindowConfiguration(plotWindow).toString());
+    }
+    
+    private void clearLayoutConfiguration() {
+        configurationModel.remove(ConfigurationModel.MONITORING_APPLICATION_LAYOUT_PROPERTY);
+        configurationModel.remove(ConfigurationModel.SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY);
+        configurationModel.remove(ConfigurationModel.PLOT_FRAME_LAYOUT_PROPERTY);
+    }
+    
+    private void setSaveLayout() {
+        configurationModel.setSaveLayout(saveLayoutItem.isSelected());
+    }      
+    
+    private void restoreDefaultLayout() {
+        resetWindowConfiguration();
+        plotWindow.resetWindowConfiguration();
+        systemStatusWindow.resetWindowConfiguration();
+    }
+           
     /**
      * Load the current Configuration by updating the ConfigurationModel.
      */
@@ -1530,29 +1662,7 @@
         setConfiguration(new Configuration(DEFAULT_CONFIG_RESOURCE));
         loadConfiguration();
     }
-
-    /**
-     * Hook for logging all status changes from the system status monitor.
-     */
-    @Override
-    public void statusChanged(SystemStatus status) {
         
-        // Choose the appropriate log level.
-        Level level = Level.INFO;
-        if (status.getStatusCode().equals(Level.WARNING)) {
-            level = Level.WARNING;
-        } else if (status.getStatusCode().ordinal() >= StatusCode.ERROR.ordinal()) {
-            level = Level.SEVERE;
-        }
-       
-        // Log all status changes.
-        log(level, "STATUS, "
-                + "subsys: " + status.getSubsystem() + ", "
-                + "code: " + status.getStatusCode().name() + ", "                 
-                + "descr: " + status.getDescription() + ", "                 
-                + "mesg: " + status.getMessage());
-    }
-    
     /**
      * Create an ET server connection from a <code>ConfigurationModel</code>.
      * @param config The ConfigurationModel with the connection parameters.

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
PlotFrame.java removed after 1049
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/PlotFrame.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/PlotFrame.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -1,29 +0,0 @@
-package org.hps.monitoring.gui;
-
-import javax.swing.JFrame;
-import javax.swing.JTabbedPane;
-
-/**
- * A <code>JFrame</code> where monitoring plots will show in tabs.
- */
-class PlotFrame extends JFrame {
-    
-    private JTabbedPane plotPane;
-    
-    PlotFrame() {
-        plotPane = new JTabbedPane();
-        setContentPane(plotPane);
-        setTitle("Monitoring Plots");
-        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
-        setResizable(true);
-        pack();           
-    }
-    
-    void reset() {
-        plotPane.removeAll();
-    }       
-    
-    JTabbedPane getPlotPane() {
-        return plotPane;
-    }
-}
\ No newline at end of file

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
RunPanel.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/RunPanel.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/RunPanel.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -38,7 +38,7 @@
 // TODO: Add event sequence number from CompositeRecord.
 // TODO: Add average data rate field (over entire session).
 // TODO: Add average proc time per event field (over entire session).
-public class RunPanel extends JPanel implements PropertyChangeListener {
+class RunPanel extends JPanel implements PropertyChangeListener {
 
     FieldPanel runNumberField = new FieldPanel("Run Number", "", 10, false);
     DatePanel startDateField = new DatePanel("Run Start", "", 16, false); 

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
SystemStatusFrame.java removed after 1049
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/SystemStatusFrame.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/SystemStatusFrame.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -1,170 +0,0 @@
-package org.hps.monitoring.gui;
-
-import static org.hps.monitoring.gui.model.SystemStatusTableModel.ACTIVE_COL;
-import static org.hps.monitoring.gui.model.SystemStatusTableModel.CLEARABLE_COL;
-import static org.hps.monitoring.gui.model.SystemStatusTableModel.LAST_CHANGED_COL;
-import static org.hps.monitoring.gui.model.SystemStatusTableModel.RESET_COL;
-import static org.hps.monitoring.gui.model.SystemStatusTableModel.STATUS_COL;
-import static org.hps.monitoring.gui.model.SystemStatusTableModel.SYSTEM_COL;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import javax.swing.JButton;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.table.DefaultTableCellRenderer;
-import javax.swing.table.TableCellRenderer;
-
-import org.hps.monitoring.gui.model.SystemStatusTableModel;
-import org.hps.monitoring.subsys.StatusCode;
-
-/**
- * A GUI window for showing changes to {@link org.hps.monitoring.subsys.SystemStatus} objects
- * using a <code>JTable</code>.
- */
-// TODO: It might be a good idea if there was a second table which logged all status changes as separate
-//       rows so they could be seen in order.
-class SystemStatusFrame extends JFrame {
-
-    JTable table;
-
-    int WIDTH = 650;
-    int HEIGHT = ScreenUtil.getScreenHeight() / 2;
-        
-    SystemStatusFrame() {
-        table = new JTable(new SystemStatusTableModel());
-        
-        // Rendering of system status cells using different background colors.
-        table.getColumnModel().getColumn(SystemStatusTableModel.STATUS_COL).setCellRenderer(new DefaultTableCellRenderer() {
-
-            @Override
-            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
-
-                // Cells are by default rendered as a JLabel.
-                JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
-
-                // Color code the cell by its status.
-                StatusCode statusCode = StatusCode.valueOf((String) value);
-                if (statusCode.ordinal() >= StatusCode.ERROR.ordinal()) {
-                    // Any type of error is red.
-                    label.setBackground(Color.RED);
-                } else if (statusCode.equals(StatusCode.WARNING)) {
-                    // Warnings are yellow.
-                    label.setBackground(Color.YELLOW);
-                } else if (statusCode.equals(StatusCode.OKAY)) {
-                    // Okay is green.
-                    label.setBackground(Color.GREEN);
-                } else if (statusCode.equals(StatusCode.OFFLINE)) {
-                    // Offline is orange.
-                    label.setBackground(Color.ORANGE);
-                } else if (statusCode.equals(StatusCode.UNKNOWN)) {
-                    // Unknown is gray.
-                    label.setBackground(Color.GRAY);
-                } else if (statusCode.equals(StatusCode.CLEARED)) {
-                    // Cleared is light gray.
-                    label.setBackground(Color.LIGHT_GRAY);
-                } else {
-                    // Default is white, though this shouldn't happen!
-                    label.setBackground(Color.WHITE);
-                }
-                return label;
-            }
-        });
-        
-        // Date formatting for last changed.
-        table.getColumnModel().getColumn(LAST_CHANGED_COL).setCellRenderer(new DefaultTableCellRenderer() {
-
-            final SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM-dd-yyyy HH:mm:ss.SSS");
-
-            @Override
-            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
-                if (value instanceof Date) {
-                    value = dateFormat.format(value);
-                }
-                return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-            }
-        });
-        
-        // Button for clearing system statuses.
-        table.getColumnModel().getColumn(RESET_COL).setCellRenderer(new ButtonRenderer("Clear"));
-        table.addMouseListener(new JTableButtonMouseListener(table));
-        table.getColumn("Clearable").setWidth(0);
-        table.getColumn("Clearable").setMinWidth(0);
-        table.getColumn("Clearable").setMaxWidth(0);
-        
-        // Column widths.
-        table.getColumnModel().getColumn(ACTIVE_COL).setPreferredWidth(8);
-        table.getColumnModel().getColumn(STATUS_COL).setPreferredWidth(10);
-        table.getColumnModel().getColumn(SYSTEM_COL).setPreferredWidth(10);
-        // TODO: Add widths for every column.
-        
-        table.setAutoCreateRowSorter(true);
-                
-        // Scroll pane.
-        JScrollPane scrollPane = new JScrollPane(table);
-        scrollPane.setOpaque(true);
-
-        // Additional config.
-        setMinimumSize(new Dimension(WIDTH, HEIGHT));
-        setTitle("System Status Monitor");
-        setContentPane(scrollPane);
-        setResizable(true);
-        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
-        pack();
-    }
-
-    public SystemStatusTableModel getTableModel() {
-        return (SystemStatusTableModel) table.getModel();
-    }
-    
-    /**
-     * Renders a button if the status is clearable.
-     */
-    private class ButtonRenderer extends JButton implements TableCellRenderer {
-                
-        public ButtonRenderer(String label) {
-            this.setText(label);
-        }
-
-        public Component getTableCellRendererComponent(
-                JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {            
-            boolean clearable = (Boolean)table.getModel().getValueAt(row, CLEARABLE_COL);
-            if (clearable)
-                return this;
-            else 
-                return null;
-        }
-    }
-    
-    /**
-     * Fires a mouse click event when the clear button is pressed, which in turn
-     * will activate the action event for the button.  The <code>ActionListener</code> 
-     * then sets the <code>StatusCode</code> to <code>CLEARED</code>.
-     */
-    private static class JTableButtonMouseListener extends MouseAdapter {
-        private final JTable table;
-        
-        public JTableButtonMouseListener(JTable table) {
-            this.table = table;
-        }
-
-        public void mouseClicked(MouseEvent e) {
-            int column = table.getColumnModel().getColumnIndexAtX(e.getX());
-            int row = e.getY() / table.getRowHeight(); 
-            if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() && column >= 0) {
-                Object value = table.getValueAt(row, column);
-                if (value instanceof JButton) {
-                    ((JButton)value).doClick();
-                }
-            }
-        }
-    }          
-}
\ No newline at end of file

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui
WindowConfiguration.java added at 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/WindowConfiguration.java	                        (rev 0)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/WindowConfiguration.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -0,0 +1,45 @@
+package org.hps.monitoring.gui;
+
+import javax.swing.JFrame;
+
+
+/**
+ * Simple class for encapsulating the width, height, x position, and
+ * y position of a GUI component in the app.
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+class WindowConfiguration {
+    
+    int width;
+    int height;
+    int x;
+    int y;
+    
+    WindowConfiguration(int width, int height, int x, int y) {
+        this.width = width;
+        this.height = height;
+        this.x = x;
+        this.y = y;
+    }
+    
+    WindowConfiguration(String configuration) {
+        String[] splited = configuration.split(" ");
+        if (splited.length != 4)
+            throw new IllegalArgumentException("Bad configuration string format: " + configuration);
+        width = Integer.parseInt(splited[0]);
+        height = Integer.parseInt(splited[1]);
+        x = Integer.parseInt(splited[2]);
+        y = Integer.parseInt(splited[3]);
+    }
+    
+    WindowConfiguration(JFrame frame) {
+        width = frame.getWidth();
+        height = frame.getHeight();
+        x = frame.getLocation().x;
+        y = frame.getLocation().y;
+    }
+    
+    public String toString() {
+        return width + " " + height + " " + x + " " + y;
+    }
+}

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model
AbstractModel.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/AbstractModel.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/AbstractModel.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -34,6 +34,10 @@
     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
         if (listenersEnabled)
             propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
+        //System.out.println("firePropertyChange");
+        //System.out.println("  name: " + propertyName);
+        //System.out.println("  old value: " + oldValue);
+        //System.out.println("  new value: " + newValue);
     }
     
     protected void firePropertyChange(PropertyChangeEvent evt) {
@@ -56,7 +60,14 @@
                 }
             }
             try {
-                Object value = getMethod.invoke(this, (Object[])null);
+                Object value = null;
+                try {
+                    value = getMethod.invoke(this, (Object[])null);
+                } catch (NullPointerException e) {
+                    throw new RuntimeException("No get method exists for property: " + property, e);
+                }
+                // Is the value non-null?  
+                // (Null values are actually okay.  It just means the property is not set.)
                 if (value != null) {
                     firePropertyChange(property, value, value);                    
                     for (PropertyChangeListener listener : propertyChangeSupport.getPropertyChangeListeners()) {

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model
Configuration.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/Configuration.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/Configuration.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -12,7 +12,7 @@
  * The accessor methods to get these values are not public, because the {@link ConfigurationModel}
  * should be used instead.
  */
-public class Configuration {
+public final class Configuration {
     
     Properties properties;
     File file;
@@ -138,6 +138,14 @@
     }
     
     /**
+     * Remove a configuration value.
+     * @param key The key of the value.
+     */
+    void remove(String key) {
+        properties.remove(key);
+    }
+    
+    /**
      * Convert this object to a string by printing out its properties list.
      */
     public String toString() {

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model
ConfigurationModel.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/ConfigurationModel.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/ConfigurationModel.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -1,6 +1,7 @@
 package org.hps.monitoring.gui.model;
 
 import java.io.File;
+import java.util.List;
 import java.util.logging.Level;
 
 import org.hps.monitoring.enums.SteeringType;
@@ -30,6 +31,10 @@
     public static final String LOG_FILE_NAME_PROPERTY = "LogFileName";
     public static final String LOG_LEVEL_PROPERTY = "LogLevel";
     public static final String LOG_TO_FILE_PROPERTY = "LogToFile";    
+    public static final String MONITORING_APPLICATION_LAYOUT_PROPERTY = "MonitoringApplicationLayout";
+    public static final String PLOT_FRAME_LAYOUT_PROPERTY = "PlotFrameLayout";
+    public static final String SAVE_LAYOUT_PROPERTY = "SaveLayout";
+    public static final String SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY = "SystemStatusFrameLayout";
     public static final String STEERING_TYPE_PROPERTY = "SteeringType";
     public static final String STEERING_FILE_PROPERTY = "SteeringFile";
     public static final String STEERING_RESOURCE_PROPERTY = "SteeringResource";
@@ -64,7 +69,7 @@
             EVENT_BUILDER_PROPERTY,
             LOG_FILE_NAME_PROPERTY,
             LOG_LEVEL_PROPERTY,
-            LOG_TO_FILE_PROPERTY,
+            LOG_TO_FILE_PROPERTY,            
             STEERING_FILE_PROPERTY,
             STEERING_RESOURCE_PROPERTY,
             STEERING_TYPE_PROPERTY,
@@ -86,7 +91,13 @@
             STATION_POSITION_PROPERTY,
             WAIT_MODE_PROPERTY,
             WAIT_TIME_PROPERTY,
-            PRESCALE_PROPERTY            
+            PRESCALE_PROPERTY,
+            
+            // GUI layout
+            SAVE_LAYOUT_PROPERTY,
+            MONITORING_APPLICATION_LAYOUT_PROPERTY,
+            PLOT_FRAME_LAYOUT_PROPERTY,
+            SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY
     };        
     
     String detectorName;
@@ -103,6 +114,10 @@
         this.config = config;
         fireAllChanged();
     }
+    
+    public Configuration getConfiguration() {
+        return this.config;
+    }
            
     public Level getLogLevel() {
         return Level.parse(config.get(LOG_LEVEL_PROPERTY));        
@@ -378,9 +393,57 @@
         config.set(PRESCALE_PROPERTY, prescale);
         firePropertyChange(PRESCALE_PROPERTY, oldValue, getPrescale());
     }
-                
+    
+    public boolean getSaveLayout() {
+        return config.getBoolean(SAVE_LAYOUT_PROPERTY);
+    }
+    
+    public void setSaveLayout(boolean saveLayout) {
+        boolean oldValue = getSaveLayout();
+        config.set(SAVE_LAYOUT_PROPERTY, saveLayout);
+        firePropertyChange(SAVE_LAYOUT_PROPERTY, oldValue, getSaveLayout());
+    }
+       
+    public String getMonitoringApplicationLayout() {
+        return config.get(MONITORING_APPLICATION_LAYOUT_PROPERTY);
+    }
+    
+    public void setMonitoringApplicationLayout(String layout) {
+        String oldValue = getMonitoringApplicationLayout();
+        config.set(MONITORING_APPLICATION_LAYOUT_PROPERTY, layout);
+        firePropertyChange(MONITORING_APPLICATION_LAYOUT_PROPERTY, oldValue, getMonitoringApplicationLayout());
+    }
+    
+    public String getSystemStatusFrameLayout() {
+        return config.get(SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY);
+    }
+    
+    public void setSystemStatusFrameLayout(String layout) {
+        String oldValue = getSystemStatusFrameLayout();
+        config.set(SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY, layout);
+        firePropertyChange(SYSTEM_STATUS_FRAME_LAYOUT_PROPERTY, oldValue, getSystemStatusFrameLayout());
+    }
+    
+    public String getPlotFrameLayout() {
+        return config.get(PLOT_FRAME_LAYOUT_PROPERTY);
+    }
+    
+    public void setPlotFrameLayout(String layout) {
+        String oldValue = getPlotFrameLayout();
+        config.set(PLOT_FRAME_LAYOUT_PROPERTY, layout);
+        firePropertyChange(PLOT_FRAME_LAYOUT_PROPERTY, oldValue, getPlotFrameLayout());        
+    }
+    
+    public void remove(String property) {
+        Object oldValue = config.get(property);
+        if (oldValue != null) {
+            config.remove(property);
+            firePropertyChange(property, oldValue, null);
+        }
+    }
+                                          
     @Override
     public String[] getPropertyNames() {
         return CONFIG_PROPERTIES;
-    }
+    }    
 }
\ No newline at end of file

java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model
RunModel.java 1049 -> 1050
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/RunModel.java	2014-09-18 22:48:45 UTC (rev 1049)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/gui/model/RunModel.java	2014-09-18 22:50:35 UTC (rev 1050)
@@ -5,7 +5,7 @@
 /**
  * Backing model for run information that shows in the {@link org.hps.monitoring.gui.RunPanel}.
  */
-public class RunModel extends AbstractModel {
+public final class RunModel extends AbstractModel {
     
     public final static String RUN_NUMBER_PROPERTY = "RunNumber"; 
     public final static String START_DATE_PROPERTY = "StartDate";
SVNspam 0.1