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 <sde...@apache.org> * @author Paul Smith <psm...@apache.org> - * */ 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>