http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/96ebd9ad/src/main/java/org/apache/log4j/chainsaw/LogUI.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/chainsaw/LogUI.java 
b/src/main/java/org/apache/log4j/chainsaw/LogUI.java
index 8ace4fd..f723cb0 100644
--- a/src/main/java/org/apache/log4j/chainsaw/LogUI.java
+++ b/src/main/java/org/apache/log4j/chainsaw/LogUI.java
@@ -17,76 +17,7 @@
 
 package org.apache.log4j.chainsaw;
 
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.EventQueue;
-import java.awt.Frame;
-import java.awt.Point;
-import java.awt.Toolkit;
-import java.awt.event.ActionEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.beans.PropertyChangeListener;
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.security.AllPermission;
-import java.security.CodeSource;
-import java.security.PermissionCollection;
-import java.security.Permissions;
-import java.security.Policy;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.Icon;
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JDialog;
-import javax.swing.JEditorPane;
-import javax.swing.JFrame;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-import javax.swing.JScrollPane;
-import javax.swing.JSplitPane;
-import javax.swing.JToolBar;
-import javax.swing.JWindow;
-import javax.swing.KeyStroke;
-import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
-import javax.swing.ToolTipManager;
-import javax.swing.UIManager;
-import javax.swing.WindowConstants;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import javax.swing.event.EventListenerList;
-import javax.swing.event.HyperlinkEvent;
-
-import org.apache.log4j.Appender;
-import org.apache.log4j.AppenderSkeleton;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
-import org.apache.log4j.Logger;
-import org.apache.log4j.LoggerRepositoryExImpl;
+import org.apache.log4j.*;
 import org.apache.log4j.chainsaw.color.RuleColorizer;
 import org.apache.log4j.chainsaw.dnd.FileDnDTarget;
 import org.apache.log4j.chainsaw.help.HelpManager;
@@ -97,20 +28,12 @@ import org.apache.log4j.chainsaw.icons.LineIconFactory;
 import org.apache.log4j.chainsaw.messages.MessageCenter;
 import org.apache.log4j.chainsaw.osx.OSXIntegration;
 import org.apache.log4j.chainsaw.plugins.PluginClassLoaderFactory;
-import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent;
-import org.apache.log4j.chainsaw.prefs.MRUFileListPreferenceSaver;
-import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent;
-import org.apache.log4j.chainsaw.prefs.SettingsListener;
-import org.apache.log4j.chainsaw.prefs.SettingsManager;
+import org.apache.log4j.chainsaw.prefs.*;
 import org.apache.log4j.chainsaw.receivers.ReceiversHelper;
 import org.apache.log4j.chainsaw.receivers.ReceiversPanel;
 import org.apache.log4j.chainsaw.vfs.VFSLogFilePatternReceiver;
 import org.apache.log4j.net.SocketNodeEventListener;
-import org.apache.log4j.plugins.Plugin;
-import org.apache.log4j.plugins.PluginEvent;
-import org.apache.log4j.plugins.PluginListener;
-import org.apache.log4j.plugins.PluginRegistry;
-import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.plugins.*;
 import org.apache.log4j.rewrite.PropertyRewritePolicy;
 import org.apache.log4j.rewrite.RewriteAppender;
 import org.apache.log4j.rule.ExpressionRule;
@@ -122,1896 +45,1901 @@ import org.apache.log4j.spi.LoggingEvent;
 import org.apache.log4j.xml.DOMConfigurator;
 import org.apache.log4j.xml.XMLDecoder;
 
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.HyperlinkEvent;
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.*;
+import java.util.*;
+import java.util.List;
+
 
 /**
  * The main entry point for Chainsaw, this class represents the first frame
  * that is used to display a Welcome panel, and any other panels that are
  * generated because Logging Events are streamed via a Receiver, or other
  * mechanism.
- * 
- * NOTE: Some of Chainsaw's application initialization should be performed 
prior 
- * to activating receivers and the logging framework used to perform 
self-logging.  
- * 
+ * <p>
+ * NOTE: Some of Chainsaw's application initialization should be performed 
prior
+ * to activating receivers and the logging framework used to perform 
self-logging.
+ * <p>
  * DELAY as much as possible the logging framework initialization process,
  * currently initialized by the creation of a ChainsawAppenderHandler.
- * 
+ *
  * @author Scott Deboy &lt;sde...@apache.org&gt;
  * @author Paul Smith  &lt;psm...@apache.org&gt;
- *
  */
 public class LogUI extends JFrame implements ChainsawViewer, SettingsListener {
-  private static final String MAIN_WINDOW_HEIGHT = "main.window.height";
-  private static final String MAIN_WINDOW_WIDTH = "main.window.width";
-  private static final String MAIN_WINDOW_Y = "main.window.y";
-  private static final String MAIN_WINDOW_X = "main.window.x";
-  private static ChainsawSplash splash;
-  private static final double DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION = 0.85d;
-  private final JFrame preferencesFrame = new JFrame();
-  private boolean noReceiversDefined;
-  private ReceiversPanel receiversPanel;
-  private ChainsawTabbedPane tabbedPane;
-  private JToolBar toolbar;
-  private ChainsawStatusBar statusBar;
-  private  ApplicationPreferenceModel applicationPreferenceModel;
-  private ApplicationPreferenceModelPanel applicationPreferenceModelPanel;
-  private final Map tableModelMap = new HashMap();
-  private final Map tableMap = new HashMap();
-  private final List<String> filterableColumns = new ArrayList<>();
-  private final Map<String, Component> panelMap = new HashMap<>();
-  ChainsawAppenderHandler handler;
-  private ChainsawToolBarAndMenus tbms;
-  private ChainsawAbout aboutBox;
-  private final SettingsManager sm = SettingsManager.getInstance();
-  private final JFrame tutorialFrame = new JFrame("Chainsaw Tutorial");
-  private JSplitPane mainReceiverSplitPane;
-  private double lastMainReceiverSplitLocation = 
DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION;
-  private final List<LogPanel> identifierPanels = new ArrayList<>();
-  private int dividerSize;
-  private int cyclicBufferSize;
-  private static Logger logger;
-  private static String configurationURLAppArg;
-
-  /**
-   * Set to true, if and only if the GUI has completed it's full
-   * initialization. Any logging events that come in must wait until this is
-   * true, and if it is false, should wait on the initializationLock object
-   * until notified.
-   */
-  private boolean isGUIFullyInitialized = false;
-  private final Object initializationLock = new Object();
-
-  /**
-   * The shutdownAction is called when the user requests to exit Chainsaw, and
-   * by default this exits the VM, but a developer may replace this action with
-   * something that better suits their needs
-   */
-  private Action shutdownAction = null;
-
-  /**
-   * Clients can register a ShutdownListener to be notified when the user has
-   * requested Chainsaw to exit.
-   */
-  private EventListenerList shutdownListenerList = new EventListenerList();
-  private WelcomePanel welcomePanel;
-
-  private static final Object repositorySelectorGuard = new Object();
-  private static final LoggerRepositoryExImpl repositoryExImpl = new 
LoggerRepositoryExImpl(LogManager.getLoggerRepository());
-  
-  private PluginRegistry pluginRegistry;
-  //map of tab names to rulecolorizers
-  private Map<String, RuleColorizer> allColorizers = new HashMap<>();
-  private ReceiverConfigurationPanel receiverConfigurationPanel = new 
ReceiverConfigurationPanel();
-
-  /**
-   * Constructor which builds up all the visual elements of the frame including
-   * the Menu bar
-   */
-  public LogUI() {
-    super("Chainsaw");
-    setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
-
-    if (ChainsawIcons.WINDOW_ICON != null) {
-      setIconImage(new ImageIcon(ChainsawIcons.WINDOW_ICON).getImage());
-    }
-  }
-
-  private static final void showSplash(Frame owner) {
-    splash = new ChainsawSplash(owner);
-    SwingHelper.centerOnScreen(splash);
-    splash.setVisible(true);
-  }
-
-  private static final void removeSplash() {
-    if (splash != null) {
-      splash.setVisible(false);
-      splash.dispose();
-    }
-  }
-
-  /**
-   * Registers a ShutdownListener with this calss so that it can be notified
-   * when the user has requested that Chainsaw exit.
-   *
-   * @param l
-   */
-  public void addShutdownListener(ShutdownListener l) {
-    shutdownListenerList.add(ShutdownListener.class, l);
-  }
-
-  /**
-   * Removes the registered ShutdownListener so that the listener will not be
-   * notified on a shutdown.
-   *
-   * @param l
-   */
-  public void removeShutdownListener(ShutdownListener l) {
-    shutdownListenerList.remove(ShutdownListener.class, l);
-  }
-
-  /**
-   * Starts Chainsaw by attaching a new instance to the Log4J main root Logger
-   * via a ChainsawAppender, and activates itself
-   *
-   * @param args
-   */
-  public static void main(String[] args) {
-      if (args.length > 0) {
-          configurationURLAppArg = args[0];
-      }
-
-      if(OSXIntegration.IS_OSX) {
-          System.setProperty("apple.laf.useScreenMenuBar", "true");
-      }
-      
-    
-    LogManager.setRepositorySelector(() -> repositoryExImpl, 
repositorySelectorGuard);
-    
-    final ApplicationPreferenceModel model = new ApplicationPreferenceModel();
-
-    SettingsManager.getInstance().configure(new 
ApplicationPreferenceModelSaver(model));
-    //if a configuration URL param was provided, set the configuration URL 
field to null
-    if (configurationURLAppArg != null) {
-        model.setBypassConfigurationURL(configurationURLAppArg);
-    }
+    private static final String MAIN_WINDOW_HEIGHT = "main.window.height";
+    private static final String MAIN_WINDOW_WIDTH = "main.window.width";
+    private static final String MAIN_WINDOW_Y = "main.window.y";
+    private static final String MAIN_WINDOW_X = "main.window.x";
+    private static ChainsawSplash splash;
+    private static final double DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION = 0.85d;
+    private final JFrame preferencesFrame = new JFrame();
+    private boolean noReceiversDefined;
+    private ReceiversPanel receiversPanel;
+    private ChainsawTabbedPane tabbedPane;
+    private JToolBar toolbar;
+    private ChainsawStatusBar statusBar;
+    private ApplicationPreferenceModel applicationPreferenceModel;
+    private ApplicationPreferenceModelPanel applicationPreferenceModelPanel;
+    private final Map tableModelMap = new HashMap();
+    private final Map tableMap = new HashMap();
+    private final List<String> filterableColumns = new ArrayList<>();
+    private final Map<String, Component> panelMap = new HashMap<>();
+    ChainsawAppenderHandler handler;
+    private ChainsawToolBarAndMenus tbms;
+    private ChainsawAbout aboutBox;
+    private final SettingsManager sm = SettingsManager.getInstance();
+    private final JFrame tutorialFrame = new JFrame("Chainsaw Tutorial");
+    private JSplitPane mainReceiverSplitPane;
+    private double lastMainReceiverSplitLocation = 
DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION;
+    private final List<LogPanel> identifierPanels = new ArrayList<>();
+    private int dividerSize;
+    private int cyclicBufferSize;
+    private static Logger logger;
+    private static String configurationURLAppArg;
 
-    EventQueue.invokeLater(() -> {
-      String lookAndFeelClassName = model.getLookAndFeelClassName();
-      if (lookAndFeelClassName == null || 
lookAndFeelClassName.trim().equals("")) {
-          String osName = System.getProperty("os.name");
-          if (osName.toLowerCase(Locale.ENGLISH).startsWith("mac")) {
-              //no need to assign look and feel
-          } else if (osName.toLowerCase(Locale.ENGLISH).startsWith("windows")) 
{
-              lookAndFeelClassName = 
"com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
-              model.setLookAndFeelClassName(lookAndFeelClassName);
-          } else if (osName.toLowerCase(Locale.ENGLISH).startsWith("linux")) {
-              lookAndFeelClassName = 
"com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
-              model.setLookAndFeelClassName(lookAndFeelClassName);
-          }
-      }
-
-      if (lookAndFeelClassName != null && 
!(lookAndFeelClassName.trim().equals(""))) {
-        loadLookAndFeelUsingPluginClassLoader(lookAndFeelClassName);
-      }
-      createChainsawGUI(model, null);
-    });
-  }
-
-  /**
-   * Creates, activates, and then shows the Chainsaw GUI, optionally showing
-   * the splash screen, and using the passed shutdown action when the user
-   * requests to exit the application (if null, then Chainsaw will exit the vm)
-   *
-   * @param model
-   * @param newShutdownAction
-   *                    DOCUMENT ME!
-   */
-  public static void createChainsawGUI(
-    ApplicationPreferenceModel model, Action newShutdownAction) {
-    
-    if (model.isOkToRemoveSecurityManager()) {
-                       MessageCenter
-                                       .getInstance()
-                                       .addMessage(
-                                                       "User has authorised 
removal of Java Security Manager via preferences");
-                       System.setSecurityManager(null);
-            // this SHOULD set the Policy/Permission stuff for any
-            // code loaded from our custom classloader.  
-            // crossing fingers...
-                       Policy.setPolicy(new Policy() {
+    /**
+     * Set to true, if and only if the GUI has completed it's full
+     * initialization. Any logging events that come in must wait until this is
+     * true, and if it is false, should wait on the initializationLock object
+     * until notified.
+     */
+    private boolean isGUIFullyInitialized = false;
+    private final Object initializationLock = new Object();
 
-                               public void refresh() {
-                               }
+    /**
+     * The shutdownAction is called when the user requests to exit Chainsaw, 
and
+     * by default this exits the VM, but a developer may replace this action 
with
+     * something that better suits their needs
+     */
+    private Action shutdownAction = null;
 
-                               public PermissionCollection 
getPermissions(CodeSource codesource) {
-                                       Permissions perms = new Permissions();
-                                       perms.add(new AllPermission());
-                                       return (perms);
-                               }
-                       });
-    }
-    
-    final LogUI logUI = new LogUI();
-    logUI.applicationPreferenceModel = model;
+    /**
+     * Clients can register a ShutdownListener to be notified when the user has
+     * requested Chainsaw to exit.
+     */
+    private EventListenerList shutdownListenerList = new EventListenerList();
+    private WelcomePanel welcomePanel;
 
-    if (model.isShowSplash()) {
-      showSplash(logUI);
-    }
-    logUI.cyclicBufferSize = model.getCyclicBufferSize();
-    logUI.pluginRegistry = repositoryExImpl.getPluginRegistry();
+    private static final Object repositorySelectorGuard = new Object();
+    private static final LoggerRepositoryExImpl repositoryExImpl = new 
LoggerRepositoryExImpl(LogManager.getLoggerRepository());
+
+    private PluginRegistry pluginRegistry;
+    //map of tab names to rulecolorizers
+    private Map<String, RuleColorizer> allColorizers = new HashMap<>();
+    private ReceiverConfigurationPanel receiverConfigurationPanel = new 
ReceiverConfigurationPanel();
 
-    logUI.handler = new ChainsawAppenderHandler();
-    logUI.handler.addEventBatchListener(logUI.new NewTabEventBatchReceiver());
-    
     /**
-     * TODO until we work out how JoranConfigurator might be able to have
-     * configurable class loader, if at all.  For now we temporarily replace 
the
-     * TCCL so that Plugins that need access to resources in 
-     * the Plugins directory can find them (this is particularly
-     * important for the Web start version of Chainsaw
-     */ 
-    //configuration initialized here
-    logUI.ensureChainsawAppenderHandlerAdded();
-    logger = LogManager.getLogger(LogUI.class);
-
-    //set hostname, application and group properties which will cause Chainsaw 
and other apache-generated
-    //logging events to route (by default) to a tab named 'chainsaw-log'
-    PropertyRewritePolicy policy = new PropertyRewritePolicy();
-    policy.setProperties("hostname=chainsaw,application=log,group=chainsaw");
-    
-    RewriteAppender rewriteAppender = new RewriteAppender();
-    rewriteAppender.setRewritePolicy(policy);
-
-    Enumeration appenders = Logger.getLogger("org.apache").getAllAppenders();
-    if (!appenders.hasMoreElements()) {
-       appenders = Logger.getRootLogger().getAllAppenders();
-    }
-    while (appenders.hasMoreElements()) {
-       Appender nextAppender = (Appender)appenders.nextElement();
-       rewriteAppender.addAppender(nextAppender);
+     * Constructor which builds up all the visual elements of the frame 
including
+     * the Menu bar
+     */
+    public LogUI() {
+        super("Chainsaw");
+        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+
+        if (ChainsawIcons.WINDOW_ICON != null) {
+            setIconImage(new ImageIcon(ChainsawIcons.WINDOW_ICON).getImage());
+        }
     }
-    Logger.getLogger("org.apache").removeAllAppenders();
-    Logger.getLogger("org.apache").addAppender(rewriteAppender);
-    Logger.getLogger("org.apache").setAdditivity(false);
-
-    //commons-vfs uses httpclient for http filesystem support, route this to 
the chainsaw-log tab as well
-    appenders = Logger.getLogger("httpclient").getAllAppenders();
-    if (!appenders.hasMoreElements()) {
-        appenders = Logger.getRootLogger().getAllAppenders();
+
+    private static final void showSplash(Frame owner) {
+        splash = new ChainsawSplash(owner);
+        SwingHelper.centerOnScreen(splash);
+        splash.setVisible(true);
     }
-    while (appenders.hasMoreElements()) {
-        Appender nextAppender = (Appender)appenders.nextElement();
-        rewriteAppender.addAppender(nextAppender);
+
+    private static final void removeSplash() {
+        if (splash != null) {
+            splash.setVisible(false);
+            splash.dispose();
+        }
     }
-    Logger.getLogger("httpclient").removeAllAppenders();
-    Logger.getLogger("httpclient").addAppender(rewriteAppender);
-    Logger.getLogger("httpclient").setAdditivity(false);
-
-    //set the commons.vfs.cache logger to info, since it can contain password 
information
-    Logger.getLogger("org.apache.commons.vfs.cache").setLevel(Level.INFO);
-    
-    Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
-e.printStackTrace();
-        logger.error("Uncaught exception in thread " + t, e);
-    });
-
-    String config = configurationURLAppArg;
-    if (config != null) {
-        logger.info("Command-line configuration arg provided (overriding 
auto-configuration URL) - using: " + config);
-    } else {
-        config = model.getConfigurationURL();
+
+    /**
+     * Registers a ShutdownListener with this calss so that it can be notified
+     * when the user has requested that Chainsaw exit.
+     *
+     * @param l
+     */
+    public void addShutdownListener(ShutdownListener l) {
+        shutdownListenerList.add(ShutdownListener.class, l);
     }
 
-    if (config != null && (!config.trim().equals(""))) {
-      config = config.trim();
-      try {
-        URL configURL = new URL(config);
-        logger.info("Using '" + config + "' for auto-configuration");
-        logUI.loadConfigurationUsingPluginClassLoader(configURL);
-      } catch(MalformedURLException e) {
-        logger.error("Initial configuration - failed to convert config string 
to url", e);
-      } catch (IOException e) {
-          logger.error("Unable to access auto-configuration URL: " + config);
-      }
+    /**
+     * Removes the registered ShutdownListener so that the listener will not be
+     * notified on a shutdown.
+     *
+     * @param l
+     */
+    public void removeShutdownListener(ShutdownListener l) {
+        shutdownListenerList.remove(ShutdownListener.class, l);
     }
 
-      //register a listener to load the configuration when it changes (avoid 
having to restart Chainsaw when applying a new configuration)
-    //this doesn't remove receivers from receivers panel, it just triggers 
DOMConfigurator.configure.
-       model.addPropertyChangeListener("configurationURL", evt -> {
-        String newConfiguration = evt.getNewValue().toString();
-        if (newConfiguration != null && !(newConfiguration.trim().equals(""))) 
{
-            newConfiguration = newConfiguration.trim();
-            try {
-                logger.info("loading updated configuration: " + 
newConfiguration);
-                URL newConfigurationURL = new URL(newConfiguration);
-                File file = new File(newConfigurationURL.toURI());
-                if (file.exists()) {
-                    
logUI.loadConfigurationUsingPluginClassLoader(newConfigurationURL);
-                } else {
-                    logger.info("Updated configuration but file does not 
exist");
+    /**
+     * Starts Chainsaw by attaching a new instance to the Log4J main root 
Logger
+     * via a ChainsawAppender, and activates itself
+     *
+     * @param args
+     */
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            configurationURLAppArg = args[0];
+        }
+
+        if (OSXIntegration.IS_OSX) {
+            System.setProperty("apple.laf.useScreenMenuBar", "true");
+        }
+
+
+        LogManager.setRepositorySelector(() -> repositoryExImpl, 
repositorySelectorGuard);
+
+        final ApplicationPreferenceModel model = new 
ApplicationPreferenceModel();
+
+        SettingsManager.getInstance().configure(new 
ApplicationPreferenceModelSaver(model));
+        //if a configuration URL param was provided, set the configuration URL 
field to null
+        if (configurationURLAppArg != null) {
+            model.setBypassConfigurationURL(configurationURLAppArg);
+        }
+
+        EventQueue.invokeLater(() -> {
+            String lookAndFeelClassName = model.getLookAndFeelClassName();
+            if (lookAndFeelClassName == null || 
lookAndFeelClassName.trim().equals("")) {
+                String osName = System.getProperty("os.name");
+                if (osName.toLowerCase(Locale.ENGLISH).startsWith("mac")) {
+                    //no need to assign look and feel
+                } else if 
(osName.toLowerCase(Locale.ENGLISH).startsWith("windows")) {
+                    lookAndFeelClassName = 
"com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+                    model.setLookAndFeelClassName(lookAndFeelClassName);
+                } else if 
(osName.toLowerCase(Locale.ENGLISH).startsWith("linux")) {
+                    lookAndFeelClassName = 
"com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
+                    model.setLookAndFeelClassName(lookAndFeelClassName);
                 }
-            } catch (MalformedURLException | URISyntaxException e) {
-                logger.error("Updated configuration - failed to convert config 
string to URL", e);
             }
+
+            if (lookAndFeelClassName != null && 
!(lookAndFeelClassName.trim().equals(""))) {
+                loadLookAndFeelUsingPluginClassLoader(lookAndFeelClassName);
+            }
+            createChainsawGUI(model, null);
+        });
+    }
+
+    /**
+     * Creates, activates, and then shows the Chainsaw GUI, optionally showing
+     * the splash screen, and using the passed shutdown action when the user
+     * requests to exit the application (if null, then Chainsaw will exit the 
vm)
+     *
+     * @param model
+     * @param newShutdownAction DOCUMENT ME!
+     */
+    public static void createChainsawGUI(
+        ApplicationPreferenceModel model, Action newShutdownAction) {
+
+        if (model.isOkToRemoveSecurityManager()) {
+            MessageCenter
+                .getInstance()
+                .addMessage(
+                    "User has authorised removal of Java Security Manager via 
preferences");
+            System.setSecurityManager(null);
+            // this SHOULD set the Policy/Permission stuff for any
+            // code loaded from our custom classloader.  
+            // crossing fingers...
+            Policy.setPolicy(new Policy() {
+
+                public void refresh() {
+                }
+
+                public PermissionCollection getPermissions(CodeSource 
codesource) {
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    return (perms);
+                }
+            });
         }
-    });
 
-    LogManager.getRootLogger().setLevel(Level.TRACE);
-    EventQueue.invokeLater(logUI::activateViewer);
+        final LogUI logUI = new LogUI();
+        logUI.applicationPreferenceModel = model;
+
+        if (model.isShowSplash()) {
+            showSplash(logUI);
+        }
+        logUI.cyclicBufferSize = model.getCyclicBufferSize();
+        logUI.pluginRegistry = repositoryExImpl.getPluginRegistry();
+
+        logUI.handler = new ChainsawAppenderHandler();
+        logUI.handler.addEventBatchListener(logUI.new 
NewTabEventBatchReceiver());
+
+        /**
+         * TODO until we work out how JoranConfigurator might be able to have
+         * configurable class loader, if at all.  For now we temporarily 
replace the
+         * TCCL so that Plugins that need access to resources in
+         * the Plugins directory can find them (this is particularly
+         * important for the Web start version of Chainsaw
+         */
+        //configuration initialized here
+        logUI.ensureChainsawAppenderHandlerAdded();
+        logger = LogManager.getLogger(LogUI.class);
+
+        //set hostname, application and group properties which will cause 
Chainsaw and other apache-generated
+        //logging events to route (by default) to a tab named 'chainsaw-log'
+        PropertyRewritePolicy policy = new PropertyRewritePolicy();
+        
policy.setProperties("hostname=chainsaw,application=log,group=chainsaw");
+
+        RewriteAppender rewriteAppender = new RewriteAppender();
+        rewriteAppender.setRewritePolicy(policy);
+
+        Enumeration appenders = 
Logger.getLogger("org.apache").getAllAppenders();
+        if (!appenders.hasMoreElements()) {
+            appenders = Logger.getRootLogger().getAllAppenders();
+        }
+        while (appenders.hasMoreElements()) {
+            Appender nextAppender = (Appender) appenders.nextElement();
+            rewriteAppender.addAppender(nextAppender);
+        }
+        Logger.getLogger("org.apache").removeAllAppenders();
+        Logger.getLogger("org.apache").addAppender(rewriteAppender);
+        Logger.getLogger("org.apache").setAdditivity(false);
+
+        //commons-vfs uses httpclient for http filesystem support, route this 
to the chainsaw-log tab as well
+        appenders = Logger.getLogger("httpclient").getAllAppenders();
+        if (!appenders.hasMoreElements()) {
+            appenders = Logger.getRootLogger().getAllAppenders();
+        }
+        while (appenders.hasMoreElements()) {
+            Appender nextAppender = (Appender) appenders.nextElement();
+            rewriteAppender.addAppender(nextAppender);
+        }
+        Logger.getLogger("httpclient").removeAllAppenders();
+        Logger.getLogger("httpclient").addAppender(rewriteAppender);
+        Logger.getLogger("httpclient").setAdditivity(false);
 
-    logger.info("SecurityManager is now: " + System.getSecurityManager());
+        //set the commons.vfs.cache logger to info, since it can contain 
password information
+        Logger.getLogger("org.apache.commons.vfs.cache").setLevel(Level.INFO);
 
-    if (newShutdownAction != null) {
-      logUI.setShutdownAction(newShutdownAction);
-    } else {
-      logUI.setShutdownAction(
-        new AbstractAction() {
-          public void actionPerformed(ActionEvent e) {
-            System.exit(0);
-          }
+        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
+            e.printStackTrace();
+            logger.error("Uncaught exception in thread " + t, e);
         });
+
+        String config = configurationURLAppArg;
+        if (config != null) {
+            logger.info("Command-line configuration arg provided (overriding 
auto-configuration URL) - using: " + config);
+        } else {
+            config = model.getConfigurationURL();
+        }
+
+        if (config != null && (!config.trim().equals(""))) {
+            config = config.trim();
+            try {
+                URL configURL = new URL(config);
+                logger.info("Using '" + config + "' for auto-configuration");
+                logUI.loadConfigurationUsingPluginClassLoader(configURL);
+            } catch (MalformedURLException e) {
+                logger.error("Initial configuration - failed to convert config 
string to url", e);
+            } catch (IOException e) {
+                logger.error("Unable to access auto-configuration URL: " + 
config);
+            }
+        }
+
+        //register a listener to load the configuration when it changes (avoid 
having to restart Chainsaw when applying a new configuration)
+        //this doesn't remove receivers from receivers panel, it just triggers 
DOMConfigurator.configure.
+        model.addPropertyChangeListener("configurationURL", evt -> {
+            String newConfiguration = evt.getNewValue().toString();
+            if (newConfiguration != null && 
!(newConfiguration.trim().equals(""))) {
+                newConfiguration = newConfiguration.trim();
+                try {
+                    logger.info("loading updated configuration: " + 
newConfiguration);
+                    URL newConfigurationURL = new URL(newConfiguration);
+                    File file = new File(newConfigurationURL.toURI());
+                    if (file.exists()) {
+                        
logUI.loadConfigurationUsingPluginClassLoader(newConfigurationURL);
+                    } else {
+                        logger.info("Updated configuration but file does not 
exist");
+                    }
+                } catch (MalformedURLException | URISyntaxException e) {
+                    logger.error("Updated configuration - failed to convert 
config string to URL", e);
+                }
+            }
+        });
+
+        LogManager.getRootLogger().setLevel(Level.TRACE);
+        EventQueue.invokeLater(logUI::activateViewer);
+
+        logger.info("SecurityManager is now: " + System.getSecurityManager());
+
+        if (newShutdownAction != null) {
+            logUI.setShutdownAction(newShutdownAction);
+        } else {
+            logUI.setShutdownAction(
+                new AbstractAction() {
+                    public void actionPerformed(ActionEvent e) {
+                        System.exit(0);
+                    }
+                });
+        }
     }
-  }
-
-  /**
-   * Allow Chainsaw v2 to be ran in-process (configured as a ChainsawAppender)
-   * NOTE: Closing Chainsaw will NOT stop the application generating the 
events.
-   * @param appender
-   *
-   */
-  public void activateViewer(ChainsawAppender appender) {
-
-    if(OSXIntegration.IS_OSX) {
-        System.setProperty("apple.laf.useScreenMenuBar", "true");
-    }
-    
-    LogManager.setRepositorySelector(() -> repositoryExImpl, 
repositorySelectorGuard);
 
-    //if Chainsaw is launched as an appender, ensure the root logger level is 
TRACE
-    LogManager.getRootLogger().setLevel(Level.TRACE);
+    /**
+     * Allow Chainsaw v2 to be ran in-process (configured as a 
ChainsawAppender)
+     * NOTE: Closing Chainsaw will NOT stop the application generating the 
events.
+     *
+     * @param appender
+     */
+    public void activateViewer(ChainsawAppender appender) {
 
-    final ApplicationPreferenceModel model = new ApplicationPreferenceModel();
-    SettingsManager.getInstance().configure(new 
ApplicationPreferenceModelSaver(model));
+        if (OSXIntegration.IS_OSX) {
+            System.setProperty("apple.laf.useScreenMenuBar", "true");
+        }
+
+        LogManager.setRepositorySelector(() -> repositoryExImpl, 
repositorySelectorGuard);
+
+        //if Chainsaw is launched as an appender, ensure the root logger level 
is TRACE
+        LogManager.getRootLogger().setLevel(Level.TRACE);
+
+        final ApplicationPreferenceModel model = new 
ApplicationPreferenceModel();
+        SettingsManager.getInstance().configure(new 
ApplicationPreferenceModelSaver(model));
+
+        cyclicBufferSize = model.getCyclicBufferSize();
+
+        handler = new ChainsawAppenderHandler(appender);
+        handler.addEventBatchListener(new NewTabEventBatchReceiver());
+
+        logger = LogManager.getLogger(LogUI.class);
+
+        setShutdownAction(
+            new AbstractAction() {
+                public void actionPerformed(ActionEvent e) {
+                }
+            });
 
-    cyclicBufferSize = model.getCyclicBufferSize();
+        applicationPreferenceModel = new ApplicationPreferenceModel();
 
-    handler = new ChainsawAppenderHandler(appender);
-    handler.addEventBatchListener(new NewTabEventBatchReceiver());
-    
-    logger = LogManager.getLogger(LogUI.class);
+        SettingsManager.getInstance().configure(new 
ApplicationPreferenceModelSaver(model));
 
-    setShutdownAction(
-        new AbstractAction() {
-          public void actionPerformed(ActionEvent e) {
-          }
+        EventQueue.invokeLater(() -> {
+            
loadLookAndFeelUsingPluginClassLoader(model.getLookAndFeelClassName());
+            createChainsawGUI(model, null);
+            getApplicationPreferenceModel().apply(model);
+            activateViewer();
         });
-    
-    applicationPreferenceModel = new ApplicationPreferenceModel();
-
-    SettingsManager.getInstance().configure(new 
ApplicationPreferenceModelSaver(model));
-
-    EventQueue.invokeLater(() -> {
-        loadLookAndFeelUsingPluginClassLoader(model.getLookAndFeelClassName());
-        createChainsawGUI(model, null);
-        getApplicationPreferenceModel().apply(model);
-        activateViewer();
-    });
-  }
-
-  /**
-   * Initialises the menu's and toolbars, but does not actually create any of
-   * the main panel components.
-   *
-   */
-  private void initGUI() {
-
-    setupHelpSystem();
-    statusBar = new ChainsawStatusBar(this);
-    setupReceiverPanel();
-
-    setToolBarAndMenus(new ChainsawToolBarAndMenus(this));
-    toolbar = getToolBarAndMenus().getToolbar();
-    setJMenuBar(getToolBarAndMenus().getMenubar());
-    
-    setTabbedPane(new ChainsawTabbedPane());
-    getSettingsManager().addSettingsListener(getTabbedPane());
-    getSettingsManager().configure(getTabbedPane());
-    
+    }
+
     /**
-     * This adds Drag & Drop capability to Chainsaw
+     * Initialises the menu's and toolbars, but does not actually create any of
+     * the main panel components.
      */
-    FileDnDTarget dnDTarget = new FileDnDTarget(tabbedPane);
-    dnDTarget.addPropertyChangeListener("fileList", evt -> {
-        final List fileList = (List) evt.getNewValue();
-
-        Thread thread = new Thread(() -> {
-            logger.debug("Loading files: " + fileList);
-            for (Object aFileList : fileList) {
-                File file = (File) aFileList;
-                final Decoder decoder = new XMLDecoder();
-                try {
-                    getStatusBar().setMessage("Loading " + 
file.getAbsolutePath() + "...");
-                    FileLoadAction.importURL(handler, decoder, file
+    private void initGUI() {
+
+        setupHelpSystem();
+        statusBar = new ChainsawStatusBar(this);
+        setupReceiverPanel();
+
+        setToolBarAndMenus(new ChainsawToolBarAndMenus(this));
+        toolbar = getToolBarAndMenus().getToolbar();
+        setJMenuBar(getToolBarAndMenus().getMenubar());
+
+        setTabbedPane(new ChainsawTabbedPane());
+        getSettingsManager().addSettingsListener(getTabbedPane());
+        getSettingsManager().configure(getTabbedPane());
+
+        /**
+         * This adds Drag & Drop capability to Chainsaw
+         */
+        FileDnDTarget dnDTarget = new FileDnDTarget(tabbedPane);
+        dnDTarget.addPropertyChangeListener("fileList", evt -> {
+            final List fileList = (List) evt.getNewValue();
+
+            Thread thread = new Thread(() -> {
+                logger.debug("Loading files: " + fileList);
+                for (Object aFileList : fileList) {
+                    File file = (File) aFileList;
+                    final Decoder decoder = new XMLDecoder();
+                    try {
+                        getStatusBar().setMessage("Loading " + 
file.getAbsolutePath() + "...");
+                        FileLoadAction.importURL(handler, decoder, file
                             .getName(), file.toURI().toURL());
-                } catch (Exception e) {
-                    String errorMsg = "Failed to import a file";
-                    logger.error(errorMsg, e);
-                    getStatusBar().setMessage(errorMsg);
+                    } catch (Exception e) {
+                        String errorMsg = "Failed to import a file";
+                        logger.error(errorMsg, e);
+                        getStatusBar().setMessage(errorMsg);
+                    }
                 }
-            }
 
-        });
+            });
 
-        thread.setPriority(Thread.MIN_PRIORITY);
-        thread.start();
+            thread.setPriority(Thread.MIN_PRIORITY);
+            thread.start();
 
-    });
+        });
 
-    applicationPreferenceModelPanel = new 
ApplicationPreferenceModelPanel(applicationPreferenceModel);
+        applicationPreferenceModelPanel = new 
ApplicationPreferenceModelPanel(applicationPreferenceModel);
 
-    applicationPreferenceModelPanel.setOkCancelActionListener(
+        applicationPreferenceModelPanel.setOkCancelActionListener(
             e -> preferencesFrame.setVisible(false));
-      KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
-          Action closeAction = new AbstractAction() {
-              public void actionPerformed(ActionEvent e) {
+        KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, 
false);
+        Action closeAction = new AbstractAction() {
+            public void actionPerformed(ActionEvent e) {
                 preferencesFrame.setVisible(false);
-              }
-          };
-          
preferencesFrame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape,
 "ESCAPE"); preferencesFrame.getRootPane().
-                  getActionMap().put("ESCAPE", closeAction);
-
-    OSXIntegration.init(this);
-  
-  }
-
-  private void initPlugins(PluginRegistry pluginRegistry) {
-    pluginRegistry.addPluginListener(
-      new PluginListener() {
-        public void pluginStarted(PluginEvent e) {
-          if (e.getPlugin() instanceof JComponent) {
-            JComponent plugin = (JComponent) e.getPlugin();
-            getTabbedPane().addANewTab(plugin.getName(), plugin, null, null);
-            getPanelMap().put(plugin.getName(), plugin);
-          }
-        }
+            }
+        };
+        
preferencesFrame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape,
 "ESCAPE");
+        preferencesFrame.getRootPane().
+            getActionMap().put("ESCAPE", closeAction);
 
-        public void pluginStopped(PluginEvent e) {
-          //TODO remove the plugin from the gui
-        }
-      });
+        OSXIntegration.init(this);
 
-    // TODO this should all be in a config file
+    }
+
+    private void initPlugins(PluginRegistry pluginRegistry) {
+        pluginRegistry.addPluginListener(
+            new PluginListener() {
+                public void pluginStarted(PluginEvent e) {
+                    if (e.getPlugin() instanceof JComponent) {
+                        JComponent plugin = (JComponent) e.getPlugin();
+                        getTabbedPane().addANewTab(plugin.getName(), plugin, 
null, null);
+                        getPanelMap().put(plugin.getName(), plugin);
+                    }
+                }
+
+                public void pluginStopped(PluginEvent e) {
+                    //TODO remove the plugin from the gui
+                }
+            });
+
+        // TODO this should all be in a config file
 //    ChainsawCentral cc = new ChainsawCentral();
 //    pluginRegistry.addPlugin(cc);
 //    cc.activateOptions();
-    
-    try {
-        Class<? extends Plugin> pluginClass = 
Class.forName("org.apache.log4j.chainsaw.zeroconf.ZeroConfPlugin").asSubclass(Plugin.class);
-        Plugin plugin = pluginClass.newInstance();
-        pluginRegistry.addPlugin(plugin);
-        plugin.activateOptions();
-        MessageCenter.getInstance().getLogger().info("Looks like ZeroConf is 
available... WooHoo!");
-    } catch (Throwable e) {
-        MessageCenter.getInstance().getLogger().error("Doesn't look like 
ZeroConf is available", e);
+
+        try {
+            Class<? extends Plugin> pluginClass = 
Class.forName("org.apache.log4j.chainsaw.zeroconf.ZeroConfPlugin").asSubclass(Plugin.class);
+            Plugin plugin = pluginClass.newInstance();
+            pluginRegistry.addPlugin(plugin);
+            plugin.activateOptions();
+            MessageCenter.getInstance().getLogger().info("Looks like ZeroConf 
is available... WooHoo!");
+        } catch (Throwable e) {
+            MessageCenter.getInstance().getLogger().error("Doesn't look like 
ZeroConf is available", e);
+        }
     }
-  }
 
-  private void setupReceiverPanel() {
-    receiversPanel = new ReceiversPanel();
-    receiversPanel.addPropertyChangeListener(
-      "visible",
+    private void setupReceiverPanel() {
+        receiversPanel = new ReceiversPanel();
+        receiversPanel.addPropertyChangeListener(
+            "visible",
             evt -> getApplicationPreferenceModel().setReceivers(
-                    (Boolean) evt.getNewValue()));
-  }
-
-  /**
-   * Initialises the Help system and the WelcomePanel
-   *
-   */
-  private void setupHelpSystem() {
-    welcomePanel = new WelcomePanel();
-
-    JToolBar tb = welcomePanel.getToolbar();
-    
-    
-    tb.add(
-      new SmallButton(
-        new AbstractAction("Tutorial", new ImageIcon(ChainsawIcons.HELP)) {
-        public void actionPerformed(ActionEvent e) {
-          setupTutorial();
-        }
-      }));
-    tb.addSeparator();
-
-    final Action exampleConfigAction =
-      new AbstractAction("View example Receiver configuration") {
-        public void actionPerformed(ActionEvent e) {
-          HelpManager.getInstance().setHelpURL(
-            ChainsawConstants.EXAMPLE_CONFIG_URL);
-        }
-      };
+                (Boolean) evt.getNewValue()));
+    }
 
-    exampleConfigAction.putValue(
-      Action.SHORT_DESCRIPTION,
-      "Displays an example Log4j configuration file with several Receivers 
defined.");
+    /**
+     * Initialises the Help system and the WelcomePanel
+     */
+    private void setupHelpSystem() {
+        welcomePanel = new WelcomePanel();
 
-    JButton exampleButton = new SmallButton(exampleConfigAction);
-    tb.add(exampleButton);
+        JToolBar tb = welcomePanel.getToolbar();
 
-    tb.add(Box.createHorizontalGlue());
 
-    /**
-     * Setup a listener on the HelpURL property and automatically change the 
WelcomePages URL
-     * to it.
-     */
-    HelpManager.getInstance().addPropertyChangeListener(
-      "helpURL",
+        tb.add(
+            new SmallButton(
+                new AbstractAction("Tutorial", new 
ImageIcon(ChainsawIcons.HELP)) {
+                    public void actionPerformed(ActionEvent e) {
+                        setupTutorial();
+                    }
+                }));
+        tb.addSeparator();
+
+        final Action exampleConfigAction =
+            new AbstractAction("View example Receiver configuration") {
+                public void actionPerformed(ActionEvent e) {
+                    HelpManager.getInstance().setHelpURL(
+                        ChainsawConstants.EXAMPLE_CONFIG_URL);
+                }
+            };
+
+        exampleConfigAction.putValue(
+            Action.SHORT_DESCRIPTION,
+            "Displays an example Log4j configuration file with several 
Receivers defined.");
+
+        JButton exampleButton = new SmallButton(exampleConfigAction);
+        tb.add(exampleButton);
+
+        tb.add(Box.createHorizontalGlue());
+
+        /**
+         * Setup a listener on the HelpURL property and automatically change 
the WelcomePages URL
+         * to it.
+         */
+        HelpManager.getInstance().addPropertyChangeListener(
+            "helpURL",
             evt -> {
-              URL newURL = (URL) evt.getNewValue();
+                URL newURL = (URL) evt.getNewValue();
 
-              if (newURL != null) {
-                welcomePanel.setURL(newURL);
-                ensureWelcomePanelVisible();
-              }
+                if (newURL != null) {
+                    welcomePanel.setURL(newURL);
+                    ensureWelcomePanelVisible();
+                }
             });
-  }
-
-  private void ensureWelcomePanelVisible() {
-      // ensure that the Welcome Panel is made visible
-      if(!getTabbedPane().containsWelcomePanel()) {
-          addWelcomePanel();
-      }
-      getTabbedPane().setSelectedComponent(welcomePanel);
-  }
-
-  /**
-   * Given the load event, configures the size/location of the main window etc
-   * etc.
-   *
-   * @param event
-   *                    DOCUMENT ME!
-   */
-  public void loadSettings(LoadSettingsEvent event) {
-    setLocation(
-      event.asInt(LogUI.MAIN_WINDOW_X), event.asInt(LogUI.MAIN_WINDOW_Y));
-      int width = event.asInt(LogUI.MAIN_WINDOW_WIDTH);
-      int height = event.asInt(LogUI.MAIN_WINDOW_HEIGHT);
-      if (width == -1 && height == -1) {
-          width = Toolkit.getDefaultToolkit().getScreenSize().width;
-          height = Toolkit.getDefaultToolkit().getScreenSize().height;
-          setSize(width, height);
-          setExtendedState(getExtendedState() | MAXIMIZED_BOTH);
-      } else {
-          setSize(width, height);
-      }
-
-    getToolBarAndMenus().stateChange();
-    RuleColorizer colorizer = new RuleColorizer();
-    colorizer.loadColorSettings(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
-    allColorizers.put(ChainsawConstants.DEFAULT_COLOR_RULE_NAME, colorizer);
-    if (event.getSetting("SavedConfig.logFormat") != null) {
-      
receiverConfigurationPanel.getModel().setLogFormat(event.getSetting("SavedConfig.logFormat"));
     }
-  }
-
-  /**
-   * Ensures the location/size of the main window is stored with the settings
-   *
-   * @param event
-   *                    DOCUMENT ME!
-   */
-  public void saveSettings(SaveSettingsEvent event) {
-    event.saveSetting(LogUI.MAIN_WINDOW_X, (int) getLocation().getX());
-    event.saveSetting(LogUI.MAIN_WINDOW_Y, (int) getLocation().getY());
-
-    event.saveSetting(LogUI.MAIN_WINDOW_WIDTH, getWidth());
-    event.saveSetting(LogUI.MAIN_WINDOW_HEIGHT, getHeight());
-    RuleColorizer colorizer = 
allColorizers.get(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
-    colorizer.saveColorSettings(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
-    if (receiverConfigurationPanel.getModel().getLogFormat() != null ) {
-      event.saveSetting("SavedConfig.logFormat", 
receiverConfigurationPanel.getModel().getLogFormat());
+
+    private void ensureWelcomePanelVisible() {
+        // ensure that the Welcome Panel is made visible
+        if (!getTabbedPane().containsWelcomePanel()) {
+            addWelcomePanel();
+        }
+        getTabbedPane().setSelectedComponent(welcomePanel);
     }
-  }
-
-  /**
-   * Activates itself as a viewer by configuring Size, and location of itself,
-   * and configures the default Tabbed Pane elements with the correct layout,
-   * table columns, and sets itself viewable.
-   */
-  public void activateViewer() {
-    LoggerRepository repo = LogManager.getLoggerRepository();
-    if (repo instanceof LoggerRepositoryEx) {
-        this.pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry();
-    }  
-    initGUI();
-
-    initPrefModelListeners();
 
     /**
-     * We add a simple appender to the MessageCenter logger
-     * so that each message is displayed in the Status bar
+     * Given the load event, configures the size/location of the main window 
etc
+     * etc.
+     *
+     * @param event DOCUMENT ME!
      */
-    MessageCenter.getInstance().getLogger().addAppender(
-      new AppenderSkeleton() {
-        protected void append(LoggingEvent event) {
-          getStatusBar().setMessage(event.getMessage().toString());
+    public void loadSettings(LoadSettingsEvent event) {
+        setLocation(
+            event.asInt(LogUI.MAIN_WINDOW_X), 
event.asInt(LogUI.MAIN_WINDOW_Y));
+        int width = event.asInt(LogUI.MAIN_WINDOW_WIDTH);
+        int height = event.asInt(LogUI.MAIN_WINDOW_HEIGHT);
+        if (width == -1 && height == -1) {
+            width = Toolkit.getDefaultToolkit().getScreenSize().width;
+            height = Toolkit.getDefaultToolkit().getScreenSize().height;
+            setSize(width, height);
+            setExtendedState(getExtendedState() | MAXIMIZED_BOTH);
+        } else {
+            setSize(width, height);
         }
 
-        public void close() {
+        getToolBarAndMenus().stateChange();
+        RuleColorizer colorizer = new RuleColorizer();
+        colorizer.loadColorSettings(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
+        allColorizers.put(ChainsawConstants.DEFAULT_COLOR_RULE_NAME, 
colorizer);
+        if (event.getSetting("SavedConfig.logFormat") != null) {
+            
receiverConfigurationPanel.getModel().setLogFormat(event.getSetting("SavedConfig.logFormat"));
         }
+    }
 
-        public boolean requiresLayout() {
-          return false;
+    /**
+     * Ensures the location/size of the main window is stored with the settings
+     *
+     * @param event DOCUMENT ME!
+     */
+    public void saveSettings(SaveSettingsEvent event) {
+        event.saveSetting(LogUI.MAIN_WINDOW_X, (int) getLocation().getX());
+        event.saveSetting(LogUI.MAIN_WINDOW_Y, (int) getLocation().getY());
+
+        event.saveSetting(LogUI.MAIN_WINDOW_WIDTH, getWidth());
+        event.saveSetting(LogUI.MAIN_WINDOW_HEIGHT, getHeight());
+        RuleColorizer colorizer = 
allColorizers.get(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
+        colorizer.saveColorSettings(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
+        if (receiverConfigurationPanel.getModel().getLogFormat() != null) {
+            event.saveSetting("SavedConfig.logFormat", 
receiverConfigurationPanel.getModel().getLogFormat());
         }
-      });
+    }
 
+    /**
+     * Activates itself as a viewer by configuring Size, and location of 
itself,
+     * and configures the default Tabbed Pane elements with the correct layout,
+     * table columns, and sets itself viewable.
+     */
+    public void activateViewer() {
+        LoggerRepository repo = LogManager.getLoggerRepository();
+        if (repo instanceof LoggerRepositoryEx) {
+            this.pluginRegistry = ((LoggerRepositoryEx) 
repo).getPluginRegistry();
+        }
+        initGUI();
 
+        initPrefModelListeners();
 
-    initSocketConnectionListener();
+        /**
+         * We add a simple appender to the MessageCenter logger
+         * so that each message is displayed in the Status bar
+         */
+        MessageCenter.getInstance().getLogger().addAppender(
+            new AppenderSkeleton() {
+                protected void append(LoggingEvent event) {
+                    getStatusBar().setMessage(event.getMessage().toString());
+                }
 
-    if (pluginRegistry.getPlugins(Receiver.class).size() == 0) {
-      noReceiversDefined = true;
-    }
+                public void close() {
+                }
 
-    getFilterableColumns().add(ChainsawConstants.LEVEL_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.LOGGER_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.THREAD_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.NDC_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.PROPERTIES_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.CLASS_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.METHOD_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.FILE_COL_NAME);
-    getFilterableColumns().add(ChainsawConstants.NONE_COL_NAME);
-
-    JPanel panePanel = new JPanel();
-    panePanel.setLayout(new BorderLayout(2, 2));
-
-    getContentPane().setLayout(new BorderLayout());
-
-    getTabbedPane().addChangeListener(getToolBarAndMenus());
-    getTabbedPane().addChangeListener(e -> {
-        LogPanel thisLogPanel = getCurrentLogPanel();
-        if (thisLogPanel != null)
-        {
-            thisLogPanel.updateStatusBar();
-        }
-    });
-
-    KeyStroke ksRight =
-      KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
-    KeyStroke ksLeft =
-      KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
-    KeyStroke ksGotoLine =
-      KeyStroke.getKeyStroke(KeyEvent.VK_G,  
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
-
-    getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-      ksRight, "MoveRight");
-    getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-      ksLeft, "MoveLeft");
-    getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-      ksGotoLine, "GotoLine");
-
-    Action moveRight =
-      new AbstractAction() {
-        public void actionPerformed(ActionEvent e) {
-          int temp = getTabbedPane().getSelectedIndex();
-          ++temp;
-
-          if (temp != getTabbedPane().getTabCount()) {
-            getTabbedPane().setSelectedTab(temp);
-          }
-        }
-      };
+                public boolean requiresLayout() {
+                    return false;
+                }
+            });
 
-    Action moveLeft =
-      new AbstractAction() {
-        public void actionPerformed(ActionEvent e) {
-          int temp = getTabbedPane().getSelectedIndex();
-          --temp;
 
-          if (temp > -1) {
-            getTabbedPane().setSelectedTab(temp);
-          }
-        }
-      };
-
-    Action gotoLine =
-      new AbstractAction() {
-        public void actionPerformed(ActionEvent e) {
-          String inputLine = JOptionPane.showInputDialog(LogUI.this, "Enter 
the line number to go:", "Goto Line", JOptionPane.PLAIN_MESSAGE);
-          try {
-                 int lineNumber = Integer.parseInt(inputLine);
-              int row = getCurrentLogPanel().setSelectedEvent(lineNumber);
-              if (row == -1) {
-                  JOptionPane.showMessageDialog(LogUI.this, "You have entered 
an invalid line number", "Error", JOptionPane.ERROR_MESSAGE);
-              }
-          } catch (NumberFormatException nfe) {
-              JOptionPane.showMessageDialog(LogUI.this, "You have entered an 
invalid line number", "Error", JOptionPane.ERROR_MESSAGE);
-          }
+        initSocketConnectionListener();
+
+        if (pluginRegistry.getPlugins(Receiver.class).size() == 0) {
+            noReceiversDefined = true;
         }
-      };
 
+        getFilterableColumns().add(ChainsawConstants.LEVEL_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.LOGGER_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.THREAD_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.NDC_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.PROPERTIES_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.CLASS_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.METHOD_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.FILE_COL_NAME);
+        getFilterableColumns().add(ChainsawConstants.NONE_COL_NAME);
+
+        JPanel panePanel = new JPanel();
+        panePanel.setLayout(new BorderLayout(2, 2));
+
+        getContentPane().setLayout(new BorderLayout());
+
+        getTabbedPane().addChangeListener(getToolBarAndMenus());
+        getTabbedPane().addChangeListener(e -> {
+            LogPanel thisLogPanel = getCurrentLogPanel();
+            if (thisLogPanel != null) {
+                thisLogPanel.updateStatusBar();
+            }
+        });
+
+        KeyStroke ksRight =
+            KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+        KeyStroke ksLeft =
+            KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+        KeyStroke ksGotoLine =
+            KeyStroke.getKeyStroke(KeyEvent.VK_G, 
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+
+        getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+            ksRight, "MoveRight");
+        getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+            ksLeft, "MoveLeft");
+        getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+            ksGotoLine, "GotoLine");
+
+        Action moveRight =
+            new AbstractAction() {
+                public void actionPerformed(ActionEvent e) {
+                    int temp = getTabbedPane().getSelectedIndex();
+                    ++temp;
+
+                    if (temp != getTabbedPane().getTabCount()) {
+                        getTabbedPane().setSelectedTab(temp);
+                    }
+                }
+            };
+
+        Action moveLeft =
+            new AbstractAction() {
+                public void actionPerformed(ActionEvent e) {
+                    int temp = getTabbedPane().getSelectedIndex();
+                    --temp;
 
-    getTabbedPane().getActionMap().put("MoveRight", moveRight);
-    getTabbedPane().getActionMap().put("MoveLeft", moveLeft);
-    getTabbedPane().getActionMap().put("GotoLine", gotoLine);
+                    if (temp > -1) {
+                        getTabbedPane().setSelectedTab(temp);
+                    }
+                }
+            };
 
-    /**
+        Action gotoLine =
+            new AbstractAction() {
+                public void actionPerformed(ActionEvent e) {
+                    String inputLine = JOptionPane.showInputDialog(LogUI.this, 
"Enter the line number to go:", "Goto Line", JOptionPane.PLAIN_MESSAGE);
+                    try {
+                        int lineNumber = Integer.parseInt(inputLine);
+                        int row = 
getCurrentLogPanel().setSelectedEvent(lineNumber);
+                        if (row == -1) {
+                            JOptionPane.showMessageDialog(LogUI.this, "You 
have entered an invalid line number", "Error", JOptionPane.ERROR_MESSAGE);
+                        }
+                    } catch (NumberFormatException nfe) {
+                        JOptionPane.showMessageDialog(LogUI.this, "You have 
entered an invalid line number", "Error", JOptionPane.ERROR_MESSAGE);
+                    }
+                }
+            };
+
+
+        getTabbedPane().getActionMap().put("MoveRight", moveRight);
+        getTabbedPane().getActionMap().put("MoveLeft", moveLeft);
+        getTabbedPane().getActionMap().put("GotoLine", gotoLine);
+
+        /**
          * We listen for double clicks, and auto-undock currently selected Tab 
if
          * the mouse event location matches the currently selected tab
          */
-    getTabbedPane().addMouseListener(
-      new MouseAdapter() {
-        public void mouseClicked(MouseEvent e) {
-          super.mouseClicked(e);
+        getTabbedPane().addMouseListener(
+            new MouseAdapter() {
+                public void mouseClicked(MouseEvent e) {
+                    super.mouseClicked(e);
+
+                    if (
+                        (e.getClickCount() > 1)
+                            && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 
0)) {
+                        int tabIndex = getTabbedPane().getSelectedIndex();
+
+                        if (
+                            (tabIndex != -1)
+                                && (tabIndex == 
getTabbedPane().getSelectedIndex())) {
+                            LogPanel logPanel = getCurrentLogPanel();
+
+                            if (logPanel != null) {
+                                logPanel.undock();
+                            }
+                        }
+                    }
+                }
+            });
 
-          if (
-            (e.getClickCount() > 1)
-              && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 0)) {
-            int tabIndex = getTabbedPane().getSelectedIndex();
+        panePanel.add(getTabbedPane());
+        addWelcomePanel();
 
-            if (
-              (tabIndex != -1)
-                && (tabIndex == getTabbedPane().getSelectedIndex())) {
-              LogPanel logPanel = getCurrentLogPanel();
+        getContentPane().add(toolbar, BorderLayout.NORTH);
+        getContentPane().add(statusBar, BorderLayout.SOUTH);
 
-              if (logPanel != null) {
-                logPanel.undock();
-              }
-            }
-          }
+        mainReceiverSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 
panePanel, receiversPanel);
+        dividerSize = mainReceiverSplitPane.getDividerSize();
+        mainReceiverSplitPane.setDividerLocation(-1);
+
+        getContentPane().add(mainReceiverSplitPane, BorderLayout.CENTER);
+
+        /**
+         * We need to make sure that all the internal GUI components have been 
added to the
+         * JFrame so that any plugns that get activated during 
initPlugins(...) method
+         * have access to inject menus
+         */
+        initPlugins(pluginRegistry);
+
+        mainReceiverSplitPane.setResizeWeight(1.0);
+        addWindowListener(
+            new WindowAdapter() {
+                public void windowClosing(WindowEvent event) {
+                    exit();
+                }
+            });
+        preferencesFrame.setTitle("'Application-wide Preferences");
+        preferencesFrame.setIconImage(
+            ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
+        preferencesFrame.getContentPane().add(applicationPreferenceModelPanel);
+
+        preferencesFrame.setSize(750, 520);
+
+        Dimension screenDimension = 
Toolkit.getDefaultToolkit().getScreenSize();
+        preferencesFrame.setLocation(
+            new Point(
+                (screenDimension.width / 2) - 
(preferencesFrame.getSize().width / 2),
+                (screenDimension.height / 2) - 
(preferencesFrame.getSize().height / 2)));
+
+        pack();
+
+        final JPopupMenu tabPopup = new JPopupMenu();
+        final Action hideCurrentTabAction =
+            new AbstractAction("Hide") {
+                public void actionPerformed(ActionEvent e) {
+                    Component selectedComp = 
getTabbedPane().getSelectedComponent();
+                    if (selectedComp instanceof LogPanel) {
+                        displayPanel(getCurrentLogPanel().getIdentifier(), 
false);
+                        tbms.stateChange();
+                    } else {
+                        getTabbedPane().remove(selectedComp);
+                    }
+                }
+            };
+
+        final Action hideOtherTabsAction =
+            new AbstractAction("Hide Others") {
+                public void actionPerformed(ActionEvent e) {
+                    Component selectedComp = 
getTabbedPane().getSelectedComponent();
+                    String currentName;
+                    if (selectedComp instanceof LogPanel) {
+                        currentName = getCurrentLogPanel().getIdentifier();
+                    } else if (selectedComp instanceof WelcomePanel) {
+                        currentName = ChainsawTabbedPane.WELCOME_TAB;
+                    } else {
+                        currentName = ChainsawTabbedPane.ZEROCONF;
+                    }
+
+                    int count = getTabbedPane().getTabCount();
+                    int index = 0;
+
+                    for (int i = 0; i < count; i++) {
+                        String name = getTabbedPane().getTitleAt(index);
+
+                        if (
+                            getPanelMap().keySet().contains(name)
+                                && !name.equals(currentName)) {
+                            displayPanel(name, false);
+                            tbms.stateChange();
+                        } else {
+                            index++;
+                        }
+                    }
+                }
+            };
+
+        Action showHiddenTabsAction =
+            new AbstractAction("Show All Hidden") {
+                public void actionPerformed(ActionEvent e) {
+                    for (Object o : getPanels().entrySet()) {
+                        Map.Entry entry = (Map.Entry) o;
+                        Boolean docked = (Boolean) entry.getValue();
+                        if (docked) {
+                            String identifier = (String) entry.getKey();
+                            int count = getTabbedPane().getTabCount();
+                            boolean found = false;
+
+                            for (int i = 0; i < count; i++) {
+                                String name = getTabbedPane().getTitleAt(i);
+
+                                if (name.equals(identifier)) {
+                                    found = true;
+
+                                    break;
+                                }
+                            }
+
+                            if (!found) {
+                                displayPanel(identifier, true);
+                                tbms.stateChange();
+                            }
+                        }
+                    }
+                }
+            };
+
+        tabPopup.add(hideCurrentTabAction);
+        tabPopup.add(hideOtherTabsAction);
+        tabPopup.addSeparator();
+        tabPopup.add(showHiddenTabsAction);
+
+        final PopupListener tabPopupListener = new PopupListener(tabPopup);
+        getTabbedPane().addMouseListener(tabPopupListener);
+
+        this.handler.addPropertyChangeListener(
+            "dataRate",
+            evt -> {
+                double dataRate = (Double) evt.getNewValue();
+                statusBar.setDataRate(dataRate);
+            });
+
+        getSettingsManager().addSettingsListener(this);
+        
getSettingsManager().addSettingsListener(MRUFileListPreferenceSaver.getInstance());
+        getSettingsManager().addSettingsListener(receiversPanel);
+        try {
+            //if an uncaught exception is thrown, allow the UI to continue to 
load
+            getSettingsManager().loadSettings();
+        } catch (Exception e) {
+            e.printStackTrace();
         }
-      });
+        //app preferences have already been loaded (and configuration url 
possibly set to blank if being overridden)
+        //but we need a listener so the settings will be saved on exit (added 
after loadsettings was called)
+        getSettingsManager().addSettingsListener(new 
ApplicationPreferenceModelSaver(applicationPreferenceModel));
 
-    panePanel.add(getTabbedPane());
-    addWelcomePanel();
+        setVisible(true);
 
-    getContentPane().add(toolbar, BorderLayout.NORTH);
-    getContentPane().add(statusBar, BorderLayout.SOUTH);
+        if (applicationPreferenceModel.isReceivers()) {
+            showReceiverPanel();
+        } else {
+            hideReceiverPanel();
+        }
 
-    mainReceiverSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 
panePanel, receiversPanel);
-    dividerSize = mainReceiverSplitPane.getDividerSize();
-    mainReceiverSplitPane.setDividerLocation(-1);
+        removeSplash();
 
-    getContentPane().add(mainReceiverSplitPane, BorderLayout.CENTER);
+        synchronized (initializationLock) {
+            isGUIFullyInitialized = true;
+            initializationLock.notifyAll();
+        }
 
-    /**
-     * We need to make sure that all the internal GUI components have been 
added to the
-     * JFrame so that any plugns that get activated during initPlugins(...) 
method
-     * have access to inject menus  
-     */
-    initPlugins(pluginRegistry);
+        if (
+            noReceiversDefined
+                && applicationPreferenceModel.isShowNoReceiverWarning()) {
+            SwingHelper.invokeOnEDT(this::showReceiverConfigurationPanel);
+        }
 
-    mainReceiverSplitPane.setResizeWeight(1.0);
-    addWindowListener(
-      new WindowAdapter() {
-        public void windowClosing(WindowEvent event) {
-          exit();
+        Container container = tutorialFrame.getContentPane();
+        final JEditorPane tutorialArea = new JEditorPane();
+        tutorialArea.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
+        tutorialArea.setEditable(false);
+        container.setLayout(new BorderLayout());
+
+        try {
+            tutorialArea.setPage(ChainsawConstants.TUTORIAL_URL);
+            JTextComponentFormatter.applySystemFontAndSize(tutorialArea);
+
+            container.add(new JScrollPane(tutorialArea), BorderLayout.CENTER);
+        } catch (Exception e) {
+            MessageCenter.getInstance().getLogger().error(
+                "Error occurred loading the Tutorial", e);
         }
-      });
-    preferencesFrame.setTitle("'Application-wide Preferences");
-    preferencesFrame.setIconImage(
-      ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
-    preferencesFrame.getContentPane().add(applicationPreferenceModelPanel);
-
-    preferencesFrame.setSize(750, 520);
-
-    Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
-    preferencesFrame.setLocation(
-      new Point(
-        (screenDimension.width / 2) - (preferencesFrame.getSize().width / 2),
-        (screenDimension.height / 2) - (preferencesFrame.getSize().height / 
2)));
-
-    pack();
-
-    final JPopupMenu tabPopup = new JPopupMenu();
-    final Action hideCurrentTabAction =
-      new AbstractAction("Hide") {
-        public void actionPerformed(ActionEvent e) {
-          Component selectedComp = getTabbedPane().getSelectedComponent();
-          if (selectedComp instanceof LogPanel) {
-            displayPanel(getCurrentLogPanel().getIdentifier(), false);
-            tbms.stateChange();
-          } else {
-            getTabbedPane().remove(selectedComp);
-          }
+
+        tutorialFrame.setIconImage(new 
ImageIcon(ChainsawIcons.HELP).getImage());
+        tutorialFrame.setSize(new Dimension(640, 480));
+
+        final Action startTutorial =
+            new AbstractAction(
+                "Start Tutorial", new 
ImageIcon(ChainsawIcons.ICON_RESUME_RECEIVER)) {
+                public void actionPerformed(ActionEvent e) {
+                    if (
+                        JOptionPane.showConfirmDialog(
+                            null,
+                            "This will start 3 \"Generator\" receivers for use 
in the Tutorial.  Is that ok?",
+                            "Confirm", JOptionPane.YES_NO_OPTION) == 
JOptionPane.YES_OPTION) {
+                        new Thread(new Tutorial()).start();
+                        putValue("TutorialStarted", Boolean.TRUE);
+                    } else {
+                        putValue("TutorialStarted", Boolean.FALSE);
+                    }
+                }
+            };
+
+        final Action stopTutorial =
+            new AbstractAction(
+                "Stop Tutorial", new 
ImageIcon(ChainsawIcons.ICON_STOP_RECEIVER)) {
+                public void actionPerformed(ActionEvent e) {
+                    if (
+                        JOptionPane.showConfirmDialog(
+                            null,
+                            "This will stop all of the \"Generator\" receivers 
used in the Tutorial, but leave any other Receiver untouched.  Is that ok?",
+                            "Confirm", JOptionPane.YES_NO_OPTION) == 
JOptionPane.YES_OPTION) {
+                        new Thread(
+                            () -> {
+                                LoggerRepository repo1 = 
LogManager.getLoggerRepository();
+                                if (repo1 instanceof LoggerRepositoryEx) {
+                                    PluginRegistry pluginRegistry = 
((LoggerRepositoryEx) repo1).getPluginRegistry();
+                                    List list = 
pluginRegistry.getPlugins(Generator.class);
+
+                                    for (Object aList : list) {
+                                        Plugin plugin = (Plugin) aList;
+                                        
pluginRegistry.stopPlugin(plugin.getName());
+                                    }
+                                }
+                            }).start();
+                        setEnabled(false);
+                        startTutorial.putValue("TutorialStarted", 
Boolean.FALSE);
+                    }
+                }
+            };
+
+        stopTutorial.putValue(
+            Action.SHORT_DESCRIPTION,
+            "Removes all of the Tutorials Generator Receivers, leaving all 
other Receivers untouched");
+        startTutorial.putValue(
+            Action.SHORT_DESCRIPTION,
+            "Begins the Tutorial, starting up some Generator Receivers so you 
can see Chainsaw in action");
+        stopTutorial.setEnabled(false);
+
+        final SmallToggleButton startButton = new 
SmallToggleButton(startTutorial);
+        PropertyChangeListener pcl =
+            evt -> {
+                stopTutorial.setEnabled(
+                    
startTutorial.getValue("TutorialStarted").equals(Boolean.TRUE));
+                startButton.setSelected(stopTutorial.isEnabled());
+            };
+
+        startTutorial.addPropertyChangeListener(pcl);
+        stopTutorial.addPropertyChangeListener(pcl);
+
+        pluginRegistry.addPluginListener(
+            new PluginListener() {
+                public void pluginStarted(PluginEvent e) {
+                }
+
+                public void pluginStopped(PluginEvent e) {
+                    List list = pluginRegistry.getPlugins(Generator.class);
+
+                    if (list.size() == 0) {
+                        startTutorial.putValue("TutorialStarted", 
Boolean.FALSE);
+                    }
+                }
+            });
+
+        final SmallButton stopButton = new SmallButton(stopTutorial);
+
+        final JToolBar tutorialToolbar = new JToolBar();
+        tutorialToolbar.setFloatable(false);
+        tutorialToolbar.add(startButton);
+        tutorialToolbar.add(stopButton);
+        container.add(tutorialToolbar, BorderLayout.NORTH);
+        tutorialArea.addHyperlinkListener(
+            e -> {
+                if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+                    if (e.getDescription().equals("StartTutorial")) {
+                        startTutorial.actionPerformed(null);
+                    } else if (e.getDescription().equals("StopTutorial")) {
+                        stopTutorial.actionPerformed(null);
+                    } else {
+                        try {
+                            tutorialArea.setPage(e.getURL());
+                        } catch (IOException e1) {
+                            MessageCenter.getInstance().getLogger().error(
+                                "Failed to change the URL for the Tutorial", 
e1);
+                        }
+                    }
+                }
+            });
+
+        /**
+         * loads the saved tab settings and if there are hidden tabs,
+         * hide those tabs out of currently loaded tabs..
+         */
+
+        if (!getTabbedPane().tabSetting.isWelcome()) {
+            displayPanel(ChainsawTabbedPane.WELCOME_TAB, false);
+        }
+        if (!getTabbedPane().tabSetting.isZeroconf()) {
+            displayPanel(ChainsawTabbedPane.ZEROCONF, false);
         }
-      };
-
-    final Action hideOtherTabsAction =
-      new AbstractAction("Hide Others") {
-        public void actionPerformed(ActionEvent e) {
-          Component selectedComp = getTabbedPane().getSelectedComponent();
-          String currentName;
-          if (selectedComp instanceof LogPanel) {
-            currentName = getCurrentLogPanel().getIdentifier();
-          } else if (selectedComp instanceof WelcomePanel) {
-            currentName = ChainsawTabbedPane.WELCOME_TAB;
-          } else {
-            currentName = ChainsawTabbedPane.ZEROCONF;
-          }
-
-          int count = getTabbedPane().getTabCount();
-          int index = 0;
-
-          for (int i = 0; i < count; i++) {
-            String name = getTabbedPane().getTitleAt(index);
+        tbms.stateChange();
 
-            if (
-              getPanelMap().keySet().contains(name)
-                && !name.equals(currentName)) {
-              displayPanel(name, false);
-              tbms.stateChange();
-            } else {
-              index++;
+    }
+
+    /**
+     * Display the log tree pane, using the last known divider location
+     */
+    private void showReceiverPanel() {
+        mainReceiverSplitPane.setDividerSize(dividerSize);
+        
mainReceiverSplitPane.setDividerLocation(lastMainReceiverSplitLocation);
+        receiversPanel.setVisible(true);
+        mainReceiverSplitPane.repaint();
+    }
+
+    /**
+     * Hide the log tree pane, holding the current divider location for later 
use
+     */
+    private void hideReceiverPanel() {
+        //subtract one to make sizes match
+        int currentSize = mainReceiverSplitPane.getWidth() - 
mainReceiverSplitPane.getDividerSize();
+        if (mainReceiverSplitPane.getDividerLocation() > -1) {
+            if (!(((mainReceiverSplitPane.getDividerLocation() + 1) == 
currentSize)
+                || ((mainReceiverSplitPane.getDividerLocation() - 1) == 0))) {
+                lastMainReceiverSplitLocation = ((double) mainReceiverSplitPane
+                    .getDividerLocation() / currentSize);
             }
-          }
         }
-      };
-
-    Action showHiddenTabsAction =
-      new AbstractAction("Show All Hidden") {
-        public void actionPerformed(ActionEvent e) {
-            for (Object o : getPanels().entrySet()) {
-                Map.Entry entry = (Map.Entry) o;
-                Boolean docked = (Boolean) entry.getValue();
-                if (docked) {
-                    String identifier = (String) entry.getKey();
-                    int count = getTabbedPane().getTabCount();
-                    boolean found = false;
+        mainReceiverSplitPane.setDividerSize(0);
+        receiversPanel.setVisible(false);
+        mainReceiverSplitPane.repaint();
+    }
+
+    private void initSocketConnectionListener() {
+        final SocketNodeEventListener socketListener =
+            new SocketNodeEventListener() {
+                public void socketOpened(String remoteInfo) {
+                    statusBar.remoteConnectionReceived(remoteInfo);
+                }
+
+                public void socketClosedEvent(Exception e) {
+                    MessageCenter.getInstance().getLogger().info(
+                        "Connection lost! :: " + e.getMessage());
+                }
+            };
 
-                    for (int i = 0; i < count; i++) {
-                        String name = getTabbedPane().getTitleAt(i);
+        PluginListener pluginListener =
+            new PluginListener() {
+                public void pluginStarted(PluginEvent e) {
+                    MessageCenter.getInstance().getLogger().info(
+                        e.getPlugin().getName() + " started!");
 
-                        if (name.equals(identifier)) {
-                            found = true;
+                    Method method = getAddListenerMethod(e.getPlugin());
 
-                            break;
+                    if (method != null) {
+                        try {
+                            method.invoke(e.getPlugin(), socketListener);
+                        } catch (Exception ex) {
+                            MessageCenter.getInstance().getLogger().error(
+                                "Failed to add a SocketNodeEventListener", ex);
                         }
                     }
+                }
 
-                    if (!found) {
-                        displayPanel(identifier, true);
-                        tbms.stateChange();
+                Method getRemoveListenerMethod(Plugin p) {
+                    try {
+                        return p.getClass().getMethod(
+                            "removeSocketNodeEventListener",
+                            SocketNodeEventListener.class);
+                    } catch (Exception e) {
+                        return null;
                     }
                 }
-            }
-        }
-      };
 
-    tabPopup.add(hideCurrentTabAction);
-    tabPopup.add(hideOtherTabsAction);
-    tabPopup.addSeparator();
-    tabPopup.add(showHiddenTabsAction);
+                Method getAddListenerMethod(Plugin p) {
+                    try {
+                        return p.getClass().getMethod(
+                            "addSocketNodeEventListener",
+                            SocketNodeEventListener.class);
+                    } catch (Exception e) {
+                        return null;
+                    }
+                }
+
+                public void pluginStopped(PluginEvent e) {
+                    Method method = getRemoveListenerMethod(e.getPlugin());
+
+                    if (method != null) {
+                        try {
+                            method.invoke(e.getPlugin(), socketListener);
+                        } catch (Exception ex) {
+                            MessageCenter.getInstance().getLogger().error(
+                                "Failed to remove SocketNodeEventListener", 
ex);
+                        }
+                    }
+
+                    MessageCenter.getInstance().getLogger().info(
+                        e.getPlugin().getName() + " stopped!");
+                }
+            };
+
+        pluginRegistry.addPluginListener(pluginListener);
+    }
+
+    private void initPrefModelListeners() {
+        applicationPreferenceModel.addPropertyChangeListener(
+            "identifierExpression",
+            evt -> 
handler.setIdentifierExpression(evt.getNewValue().toString()));
+        
handler.setIdentifierExpression(applicationPreferenceModel.getIdentifierExpression());
+
 
-    final PopupListener tabPopupListener = new PopupListener(tabPopup);
-    getTabbedPane().addMouseListener(tabPopupListener);
+        applicationPreferenceModel.addPropertyChangeListener(
+            "toolTipDisplayMillis",
+            evt -> ToolTipManager.sharedInstance().setDismissDelay(
+                (Integer) evt.getNewValue()));
+        ToolTipManager.sharedInstance().setDismissDelay(
+            applicationPreferenceModel.getToolTipDisplayMillis());
 
-    this.handler.addPropertyChangeListener(
-      "dataRate",
+        applicationPreferenceModel.addPropertyChangeListener(
+            "responsiveness",
             evt -> {
-              double dataRate = (Double) evt.getNewValue();
-              statusBar.setDataRate(dataRate);
+                int value = (Integer) evt.getNewValue();
+                handler.setQueueInterval((value * 1000) - 750);
             });
+        
handler.setQueueInterval((applicationPreferenceModel.getResponsiveness() * 
1000) - 750);
 
-    getSettingsManager().addSettingsListener(this);
-    
getSettingsManager().addSettingsListener(MRUFileListPreferenceSaver.getInstance());
-    getSettingsManager().addSettingsListener(receiversPanel);
-    try {
-      //if an uncaught exception is thrown, allow the UI to continue to load
-      getSettingsManager().loadSettings();
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-    //app preferences have already been loaded (and configuration url possibly 
set to blank if being overridden)
-    //but we need a listener so the settings will be saved on exit (added 
after loadsettings was called)
-    getSettingsManager().addSettingsListener(new 
ApplicationPreferenceModelSaver(applicationPreferenceModel));
+        applicationPreferenceModel.addPropertyChangeListener(
+            "tabPlacement",
+            evt -> SwingUtilities.invokeLater(
+                () -> {
+                    int placement = (Integer) evt.getNewValue();
 
-    setVisible(true);
+                    switch (placement) {
+                        case SwingConstants.TOP:
+                        case SwingConstants.BOTTOM:
+                            tabbedPane.setTabPlacement(placement);
 
-    if (applicationPreferenceModel.isReceivers()) {
-      showReceiverPanel();
-    } else {
-      hideReceiverPanel();
-    }
+                            break;
 
-    removeSplash();
+                        default:
+                            break;
+                    }
+                }));
 
-    synchronized (initializationLock) {
-      isGUIFullyInitialized = true;
-      initializationLock.notifyAll();
-    }
+        applicationPreferenceModel.addPropertyChangeListener(
+            "statusBar",
+            evt -> {
+                boolean value = (Boolean) evt.getNewValue();
+                setStatusBarVisible(value);
+            });
+        setStatusBarVisible(applicationPreferenceModel.isStatusBar());
 
-    if (
-      noReceiversDefined
-        && applicationPreferenceModel.isShowNoReceiverWarning()) {
-      SwingHelper.invokeOnEDT(this::showReceiverConfigurationPanel);
-    }
+        applicationPreferenceModel.addPropertyChangeListener(
+            "receivers",
+            evt -> {
+                boolean value = (Boolean) evt.getNewValue();
 
-    Container container = tutorialFrame.getContentPane();
-    final JEditorPane tutorialArea = new JEditorPane();
-    tutorialArea.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
-    tutorialArea.setEditable(false);
-    container.setLayout(new BorderLayout());
+                if (value) {
+                    showReceiverPanel();
+                } else {
+                    hideReceiverPanel();
+                }
+            });
+//    if (applicationPreferenceModel.isReceivers()) {
+//      showReceiverPanel();
+//    } else {
+//      hideReceiverPanel();
+//    }
 
-    try {
-      tutorialArea.setPage(ChainsawConstants.TUTORIAL_URL);
-      JTextComponentFormatter.applySystemFontAndSize(tutorialArea);
 
-      container.add(new JScrollPane(tutorialArea), BorderLayout.CENTER);
-    } catch (Exception e) {
-      MessageCenter.getInstance().getLogger().error(
-        "Error occurred loading the Tutorial", e);
+        applicationPreferenceModel.addPropertyChangeListener(
+            "toolbar",
+            evt -> {
+                boolean value = (Boolean) evt.getNewValue();
+                toolbar.setVisible(value);
+            });
+        toolbar.setVisible(applicationPreferenceModel.isToolbar());
+
     }
 
-    tutorialFrame.setIconImage(new ImageIcon(ChainsawIcons.HELP).getImage());
-    tutorialFrame.setSize(new Dimension(640, 480));
-
-    final Action startTutorial =
-      new AbstractAction(
-        "Start Tutorial", new ImageIcon(ChainsawIcons.ICON_RESUME_RECEIVER)) {
-        public void actionPerformed(ActionEvent e) {
-          if (
-            JOptionPane.showConfirmDialog(
-                null,
-                "This will start 3 \"Generator\" receivers for use in the 
Tutorial.  Is that ok?",
-                "Confirm", JOptionPane.YES_NO_OPTION) == 
JOptionPane.YES_OPTION) {
-            new Thread(new Tutorial()).start();
-            putValue("TutorialStarted", Boolean.TRUE);
-          } else {
-            putValue("TutorialStarted", Boolean.FALSE);
-          }
-        }
-      };
-
-    final Action stopTutorial =
-      new AbstractAction(
-        "Stop Tutorial", new ImageIcon(ChainsawIcons.ICON_STOP_RECEIVER)) {
-        public void actionPerformed(ActionEvent e) {
-          if (
-   

<TRUNCATED>

Reply via email to