View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  /*
    18   */
    19  package org.apache.log4j.chainsaw;
    20
    21  import org.apache.log4j.LogManager;
    22  import org.apache.log4j.Logger;
    23  import org.apache.log4j.chainsaw.color.RuleColorizer;
    24  import org.apache.log4j.chainsaw.filter.FilterModel;
    25  import org.apache.log4j.chainsaw.icons.ChainsawIcons;
    26  import org.apache.log4j.chainsaw.icons.LineIconFactory;
    27  import org.apache.log4j.rule.AbstractRule;
    28  import org.apache.log4j.rule.ColorRule;
    29  import org.apache.log4j.rule.ExpressionRule;
    30  import org.apache.log4j.rule.Rule;
    31  import org.apache.log4j.spi.LoggingEvent;
    32
    33  import javax.swing.*;
    34  import javax.swing.event.*;
    35  import javax.swing.tree.*;
    36  import java.awt.*;
    37  import java.awt.event.*;
    38  import java.util.*;
    39  import java.util.List;
    40
    41
    42  /**
    43   * A panel that encapsulates the Logger Name tree, with associated actions
    44   * and implements the Rule interface so that it can filter in/out events
    45   * that do not match the users request for refining the view based on Loggers.
    46   *
    47   * @author Paul Smith &lt;psmith@apache.org&gt;
    48   */
    49  final class LoggerNameTreePanel extends JPanel implements LoggerNameListener {
    50      //~ Static fields/initializers ==============================================
    51
    52      private static final int WARN_DEPTH = 4;
    53
    54      //~ Instance fields =========================================================
    55
    56      private LoggerNameTreeCellRenderer cellRenderer =
    57          new LoggerNameTreeCellRenderer();
    58      private final Action clearIgnoreListAction;
    59      private final Action closeAction;
    60      private final JButton closeButton = new SmallButton();
    61      private final Action collapseAction;
    62      private final JButton collapseButton = new SmallButton();
    63      private final Action editLoggerAction;
    64      private final JButton editLoggerButton = new SmallButton();
    65      private final Action expandAction;
    66      private final Action findAction;
    67      private final Action clearFindNextAction;
    68      private final Action defineColorRuleForLoggerAction;
    69      private final Action setRefineFocusAction;
    70      private final Action updateRefineFocusAction;
    71      private final Action updateFindAction;
    72      private final JButton expandButton = new SmallButton();
    73      private final Action focusOnAction;
    74      private final Action clearRefineFocusAction;
    75      private final SmallToggleButton focusOnLoggerButton =
    76          new SmallToggleButton();
    77      private final Set hiddenSet = new HashSet();
    78      private final Action hideAction;
    79      private final Action hideSubLoggersAction;
    80      private final LogPanelPreferenceModel preferenceModel;
    81
    82      private final JList ignoreList = new JList();
    83      private final JEditorPane ignoreExpressionEntryField = new JEditorPane();
    84      private final JEditorPane alwaysDisplayExpressionEntryField = new JEditorPane();
    85      private final JScrollPane ignoreListScroll = new JScrollPane(ignoreList);
    86      private final JDialog ignoreDialog = new JDialog();
    87      private final JDialog ignoreExpressionDialog = new JDialog();
    88      private final JDialog alwaysDisplayExpressionDialog = new JDialog();
    89      private final JLabel ignoreSummary = new JLabel("0 hidden loggers");
    90      private final JLabel ignoreExpressionSummary = new JLabel("Ignore expression");
    91      private final JLabel alwaysDisplayExpressionSummary = new JLabel("Always displayed expression");
    92      private final SmallToggleButton ignoreLoggerButton = new SmallToggleButton();
    93      private final EventListenerList listenerList = new EventListenerList();
    94      private final JTree logTree;
    95      private final Logger logger = LogManager.getLogger(LoggerNameTreePanel.class);
    96
    97      //  private final EventListenerList focusOnActionListeners =
    98      //    new EventListenerList();
    99      private final LogPanelLoggerTreeModel logTreeModel;
    100     private final PopupListener popupListener;
    101     private final LoggerTreePopupMenu popupMenu;
    102     private final VisibilityRuleDelegate visibilityRuleDelegate;
    103     private Rule colorRuleDelegate;
    104     private final JScrollPane scrollTree;
    105     private final JToolBar toolbar = new JToolBar();
    106     private final LogPanel logPanel;
    107     private final RuleColorizer colorizer;
    108     private Rule ignoreExpressionRule;
    109     private Rule alwaysDisplayExpressionRule;
    110     private boolean expandRootLatch = false;
    111     private String currentlySelectedLoggerName;
    112
    113     //~ Constructors ============================================================
    114
    115     /**
    116      * Creates a new LoggerNameTreePanel object.
    117      *
    118      * @param logTreeModel
    119      */
    120     LoggerNameTreePanel(LogPanelLoggerTreeModel logTreeModel, LogPanelPreferenceModel preferenceModel, LogPanel logPanel, RuleColorizer colorizer, FilterModel filterModel) {
    121         super();
    122         this.logTreeModel = logTreeModel;
    123         this.preferenceModel = preferenceModel;
    124         this.logPanel = logPanel;
    125         this.colorizer = colorizer;
    126
    127         setLayout(new BorderLayout());
    128         ignoreExpressionEntryField.setPreferredSize(new Dimension(300, 150));
    129         alwaysDisplayExpressionEntryField.setPreferredSize(new Dimension(300, 150));
    130         alwaysDisplayExpressionSummary.setMinimumSize(new Dimension(10, alwaysDisplayExpressionSummary.getHeight()));
    131         ignoreExpressionSummary.setMinimumSize(new Dimension(10, ignoreExpressionSummary.getHeight()));
    132         ignoreSummary.setMinimumSize(new Dimension(10, ignoreSummary.getHeight()));
    133
    134         JTextComponentFormatter.applySystemFontAndSize(ignoreExpressionEntryField);
    135         JTextComponentFormatter.applySystemFontAndSize(alwaysDisplayExpressionEntryField);
    136
    137         visibilityRuleDelegate = new VisibilityRuleDelegate();
    138         colorRuleDelegate =
    139             new AbstractRule() {
    140                 public boolean evaluate(LoggingEvent e, Map matches) {
    141                     boolean hiddenLogger = e.getLoggerName() != null && isHiddenLogger(e.getLoggerName());
    142                     boolean hiddenExpression = (ignoreExpressionRule != null && ignoreExpressionRule.evaluate(e, null));
    143                     boolean alwaysDisplayExpression = (alwaysDisplayExpressionRule != null && alwaysDisplayExpressionRule.evaluate(e, null));
    144                     boolean hidden = (!alwaysDisplayExpression) && (hiddenLogger || hiddenExpression);
    145                     String currentlySelectedLoggerName = getCurrentlySelectedLoggerName();
    146
    147                     return !isFocusOnSelected() && !hidden && currentlySelectedLoggerName != null && !"".equals(currentlySelectedLoggerName) && (e.getLoggerName().startsWith(currentlySelectedLoggerName + ".") || e.getLoggerName().endsWith(currentlySelectedLoggerName));
    148                 }
    149             };
    150
    151         logTree =
    152             new JTree(logTreeModel) {
    153                 public String getToolTipText(MouseEvent ev) {
    154                     if (ev == null) {
    155                         return null;
    156                     }
    157
    158                     TreePath path = logTree.getPathForLocation(ev.getX(), ev.getY());
    159
    160                     String loggerName = getLoggerName(path);
    161
    162                     if (hiddenSet.contains(loggerName)) {
    163                         loggerName += " (you are ignoring this logger)";
    164                     }
    165
    166                     return loggerName;
    167                 }
    168             };
    169
    170         ToolTipManager.sharedInstance().registerComponent(logTree);
    171         logTree.setCellRenderer(cellRenderer);
    172
    173         //	============================================
    174         logTreeModel.addTreeModelListener(new TreeModelListener() {
    175             public void treeNodesChanged(TreeModelEvent e) {
    176             }
    177
    178             public void treeNodesInserted(TreeModelEvent e) {
    179                 if (!expandRootLatch) {
    180                     ensureRootExpanded();
    181                     expandRootLatch = true;
    182                 }
    183             }
    184
    185             public void treeNodesRemoved(TreeModelEvent e) {
    186             }
    187
    188             public void treeStructureChanged(TreeModelEvent e) {
    189             }
    190         });
    191
    192         logTree.setEditable(false);
    193
    194         //	TODO decide if Multi-selection is useful, and how it would work
    195         TreeSelectionModel selectionModel = new DefaultTreeSelectionModel();
    196         selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    197         logTree.setSelectionModel(selectionModel);
    198
    199         logTree.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
    200         scrollTree = new JScrollPane(logTree);
    201         toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
    202
    203         expandAction = createExpandAction();
    204         findAction = createFindNextAction();
    205         clearFindNextAction = createClearFindNextAction();
    206         defineColorRuleForLoggerAction = createDefineColorRuleForLoggerAction();
    207         clearRefineFocusAction = createClearRefineFocusAction();
    208         setRefineFocusAction = createSetRefineFocusAction();
    209         updateRefineFocusAction = createUpdateRefineFocusAction();
    210         updateFindAction = createUpdateFindAction();
    211         editLoggerAction = createEditLoggerAction();
    212         closeAction = createCloseAction();
    213         collapseAction = createCollapseAction();
    214         focusOnAction = createFocusOnAction();
    215         hideAction = createIgnoreAction();
    216         hideSubLoggersAction = createIgnoreAllAction();
    217         clearIgnoreListAction = createClearIgnoreListAction();
    218
    219         popupMenu = new LoggerTreePopupMenu();
    220         popupListener = new PopupListener(popupMenu);
    221
    222         setupListeners();
    223         configureToolbarPanel();
    224
    225         add(toolbar, BorderLayout.NORTH);
    226         add(scrollTree, BorderLayout.CENTER);
    227
    228         ignoreDialog.setTitle("Hidden/Ignored Loggers");
    229         ignoreDialog.setModal(true);
    230
    231         ignoreExpressionDialog.setTitle("Hidden/Ignored Expression");
    232         ignoreExpressionDialog.setModal(true);
    233
    234         alwaysDisplayExpressionDialog.setTitle("Always displayed Expression");
    235         alwaysDisplayExpressionDialog.setModal(true);
    236
    237         JPanel ignorePanel = new JPanel();
    238         ignorePanel.setLayout(new BoxLayout(ignorePanel, BoxLayout.Y_AXIS));
    239         JPanel ignoreSummaryPanel = new JPanel();
    240         ignoreSummaryPanel.setLayout(new BoxLayout(ignoreSummaryPanel, BoxLayout.X_AXIS));
    241         ignoreSummaryPanel.add(ignoreSummary);
    242
    243         Action showIgnoreDialogAction = new AbstractAction("...") {
    244             public void actionPerformed(ActionEvent e) {
    245                 LogPanel.centerAndSetVisible(ignoreDialog);
    246             }
    247         };
    248
    249         Action showIgnoreExpressionDialogAction = new AbstractAction("...") {
    250             public void actionPerformed(ActionEvent e) {
    251                 LogPanel.centerAndSetVisible(ignoreExpressionDialog);
    252             }
    253         };
    254
    255
    256         Action showAlwaysDisplayExpressionDialogAction = new AbstractAction("...") {
    257             public void actionPerformed(ActionEvent e) {
    258                 LogPanel.centerAndSetVisible(alwaysDisplayExpressionDialog);
    259             }
    260         };
    261
    262         showIgnoreDialogAction.putValue(Action.SHORT_DESCRIPTION, "Click to view and manage your hidden/ignored loggers");
    263         JButton btnShowIgnoreDialog = new SmallButton(showIgnoreDialogAction);
    264
    265         ignoreSummaryPanel.add(btnShowIgnoreDialog);
    266         ignorePanel.add(ignoreSummaryPanel);
    267
    268         JPanel ignoreExpressionPanel = new JPanel();
    269         ignoreExpressionPanel.setLayout(new BoxLayout(ignoreExpressionPanel, BoxLayout.X_AXIS));
    270         ignoreExpressionPanel.add(ignoreExpressionSummary);
    271         showIgnoreExpressionDialogAction.putValue(Action.SHORT_DESCRIPTION, "Click to view and manage your hidden/ignored expression");
    272         JButton btnShowIgnoreExpressionDialog = new SmallButton(showIgnoreExpressionDialogAction);
    273         ignoreExpressionPanel.add(btnShowIgnoreExpressionDialog);
    274
    275         ignorePanel.add(ignoreExpressionPanel);
    276
    277         JPanel alwaysDisplayExpressionPanel = new JPanel();
    278         alwaysDisplayExpressionPanel.setLayout(new BoxLayout(alwaysDisplayExpressionPanel, BoxLayout.X_AXIS));
    279         alwaysDisplayExpressionPanel.add(alwaysDisplayExpressionSummary);
    280         showAlwaysDisplayExpressionDialogAction.putValue(Action.SHORT_DESCRIPTION, "Click to view and manage your always-displayed expression");
    281         JButton btnShowAlwaysDisplayExpressionDialog = new SmallButton(showAlwaysDisplayExpressionDialogAction);
    282         alwaysDisplayExpressionPanel.add(btnShowAlwaysDisplayExpressionDialog);
    283
    284         ignorePanel.add(alwaysDisplayExpressionPanel);
    285
    286         add(ignorePanel, BorderLayout.SOUTH);
    287
    288         ignoreList.setModel(new DefaultListModel());
    289         ignoreList.addMouseListener(new MouseAdapter() {
    290             public void mouseClicked(MouseEvent e) {
    291                 if (
    292                     (e.getClickCount() > 1)
    293                         && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 0)) {
    294                     int index = ignoreList.locationToIndex(e.getPoint());
    295
    296                     if (index >= 0) {
    297                         String string =
    298                             ignoreList.getModel().getElementAt(index).toString();
    299                         toggleHiddenLogger(string);
    300                         fireChangeEvent();
    301
    302                         /**
    303                          * TODO this needs to get the node that has this logger and fire a visual update
    304                          */
    305                         LoggerNameTreePanel.this.logTreeModel.nodeStructureChanged(
    306                             (TreeNode) LoggerNameTreePanel.this.logTreeModel.getRoot());
    307                     }
    308                 }
    309             }
    310         });
    311
    312         JPanel ignoreListPanel = new JPanel(new BorderLayout());
    313         ignoreListScroll.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Double click an entry to unhide it"));
    314         ignoreListPanel.add(ignoreListScroll, BorderLayout.CENTER);
    315
    316         JPanel ignoreExpressionDialogPanel = new JPanel(new BorderLayout());
    317         ignoreExpressionEntryField.addKeyListener(new ExpressionRuleContext(filterModel, ignoreExpressionEntryField));
    318
    319         ignoreExpressionDialogPanel.add(new JScrollPane(ignoreExpressionEntryField), BorderLayout.CENTER);
    320         JButton ignoreExpressionCloseButton = new JButton(new AbstractAction(" Close ") {
    321             public void actionPerformed(ActionEvent e) {
    322                 String ignoreText = ignoreExpressionEntryField.getText();
    323
    324                 if (updateIgnoreExpression(ignoreText)) {
    325                     ignoreExpressionDialog.setVisible(false);
    326                 }
    327             }
    328         });
    329
    330
    331         JPanel alwaysDisplayExpressionDialogPanel = new JPanel(new BorderLayout());
    332         alwaysDisplayExpressionEntryField.addKeyListener(new ExpressionRuleContext(filterModel, alwaysDisplayExpressionEntryField));
    333
    334         alwaysDisplayExpressionDialogPanel.add(new JScrollPane(alwaysDisplayExpressionEntryField), BorderLayout.CENTER);
    335         JButton alwaysDisplayExpressionCloseButton = new JButton(new AbstractAction(" Close ") {
    336             public void actionPerformed(ActionEvent e) {
    337                 String alwaysDisplayText = alwaysDisplayExpressionEntryField.getText();
    338
    339                 if (updateAlwaysDisplayExpression(alwaysDisplayText)) {
    340                     alwaysDisplayExpressionDialog.setVisible(false);
    341                 }
    342             }
    343         });
    344
    345         JPanel closeAlwaysDisplayExpressionPanel = new JPanel();
    346         closeAlwaysDisplayExpressionPanel.add(alwaysDisplayExpressionCloseButton);
    347         alwaysDisplayExpressionDialogPanel.add(closeAlwaysDisplayExpressionPanel, BorderLayout.SOUTH);
    348
    349         JPanel closeIgnoreExpressionPanel = new JPanel();
    350         closeIgnoreExpressionPanel.add(ignoreExpressionCloseButton);
    351         ignoreExpressionDialogPanel.add(closeIgnoreExpressionPanel, BorderLayout.SOUTH);
    352
    353         Box ignoreListButtonPanel = Box.createHorizontalBox();
    354
    355         JButton unhideAll = new JButton(new AbstractAction(" Unhide All ") {
    356
    357             public void actionPerformed(final ActionEvent e) {
    358                 SwingUtilities.invokeLater(() -> clearIgnoreListAction.actionPerformed(e));
    359
    360             }
    361         });
    362         ignoreListButtonPanel.add(unhideAll);
    363
    364         ignoreListButtonPanel.add(Box.createHorizontalGlue());
    365         JButton ignoreCloseButton = new JButton(new AbstractAction(" Close ") {
    366
    367             public void actionPerformed(ActionEvent e) {
    368                 ignoreDialog.setVisible(false);
    369
    370             }
    371         });
    372         ignoreListButtonPanel.add(ignoreCloseButton);
    373
    374
    375         ignoreListPanel.add(ignoreListButtonPanel, BorderLayout.SOUTH);
    376
    377
    378         ignoreDialog.getContentPane().add(ignoreListPanel);
    379         ignoreDialog.pack();
    380
    381         ignoreExpressionDialog.getContentPane().add(ignoreExpressionDialogPanel);
    382         ignoreExpressionDialog.pack();
    383
    384         alwaysDisplayExpressionDialog.getContentPane().add(alwaysDisplayExpressionDialogPanel);
    385         alwaysDisplayExpressionDialog.pack();
    386     }
    387
    388     private boolean updateIgnoreExpression(String ignoreText) {
    389         try {
    390             if (ignoreText != null && !ignoreText.trim().equals("")) {
    391                 ignoreExpressionRule = ExpressionRule.getRule(ignoreText);
    392             } else {
    393                 ignoreExpressionRule = null;
    394             }
    395             visibilityRuleDelegate.firePropertyChange("hiddenSet", null, null);
    396
    397             updateDisplay();
    398             ignoreExpressionEntryField.setBackground(UIManager.getColor("TextField.background"));
    399             return true;
    400         } catch (IllegalArgumentException iae) {
    401             ignoreExpressionEntryField.setToolTipText(iae.getMessage());
    402             ignoreExpressionEntryField.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
    403             return false;
    404         }
    405     }
    406
    407     private boolean updateAlwaysDisplayExpression(String alwaysDisplayText) {
    408         try {
    409             if (alwaysDisplayText != null && !alwaysDisplayText.trim().equals("")) {
    410                 alwaysDisplayExpressionRule = ExpressionRule.getRule(alwaysDisplayText);
    411             } else {
    412                 alwaysDisplayExpressionRule = null;
    413             }
    414             visibilityRuleDelegate.firePropertyChange("alwaysDisplayedSet", null, null);
    415
    416             updateDisplay();
    417             alwaysDisplayExpressionEntryField.setBackground(UIManager.getColor("TextField.background"));
    418             return true;
    419         } catch (IllegalArgumentException iae) {
    420             alwaysDisplayExpressionEntryField.setToolTipText(iae.getMessage());
    421             alwaysDisplayExpressionEntryField.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
    422             return false;
    423         }
    424     }
    425
    426     //~ Methods =================================================================
    427
    428     /**
    429      * Adds a change Listener to this LoggerNameTreePanel to be notfied
    430      * when the State of the Focus or Hidden details have changed.
    431      *
    432      * @param l
    433      */
    434     public void addChangeListener(ChangeListener l) {
    435         listenerList.add(ChangeListener.class, l);
    436     }
    437
    438     public Rule getLoggerColorRule() {
    439         return colorRuleDelegate;
    440     }
    441
    442     public Rule getLoggerVisibilityRule() {
    443         return visibilityRuleDelegate;
    444     }
    445
    446     /**
    447      * DOCUMENT ME!
    448      *
    449      * @param l DOCUMENT ME!
    450      */
    451     public void removeChangeListener(ChangeListener l) {
    452         listenerList.remove(ChangeListener.class, l);
    453     }
    454
    455     /**
    456      * Ensures the Focus is set to a specific logger name
    457      *
    458      * @param
    459      */
    460     public void setFocusOn(String newLogger) {
    461         DefaultMutableTreeNode node = logTreeModel.lookupLogger(newLogger);
    462
    463         if (node != null) {
    464             TreeNode[] nodes = node.getPath();
    465             TreePath treePath = new TreePath(nodes);
    466             logTree.setSelectionPath(treePath);
    467
    468             if (!focusOnLoggerButton.isSelected()) {
    469                 focusOnLoggerButton.doClick();
    470             }
    471         } else {
    472             logger.error("failed to lookup logger " + newLogger);
    473         }
    474     }
    475
    476     private boolean isHiddenLogger(String loggerName) {
    477         for (Object aHiddenSet : hiddenSet) {
    478             String hiddenLoggerEntry = aHiddenSet.toString();
    479             if (loggerName.startsWith(hiddenLoggerEntry + ".") || loggerName.endsWith(hiddenLoggerEntry)) {
    480                 return true;
    481             }
    482         }
    483         return false;
    484     }
    485
    486
    487     /**
    488      * DOCUMENT ME!
    489      *
    490      * @param logger
    491      */
    492     protected void toggleHiddenLogger(String logger) {
    493         if (!hiddenSet.contains(logger)) {
    494             hiddenSet.add(logger);
    495         } else {
    496             hiddenSet.remove(logger);
    497         }
    498
    499         visibilityRuleDelegate.firePropertyChange("hiddenSet", null, null);
    500     }
    501
    502     /**
    503      * Returns the full name of the Logger that is represented by
    504      * the currently selected Logger node in the tree.
    505      * <p>
    506      * This is the dotted name, of the current node including all it's parents.
    507      * <p>
    508      * If multiple Nodes are selected, the first path is used
    509      *
    510      * @return Logger Name or null if nothing selected
    511      */
    512     String getCurrentlySelectedLoggerName() {
    513         TreePath[] paths = logTree.getSelectionPaths();
    514
    515         if ((paths == null) || (paths.length == 0)) {
    516             return null;
    517         }
    518
    519         TreePath firstPath = paths[0];
    520
    521         return getLoggerName(firstPath);
    522     }
    523
    524     /**
    525      * Returns the full
    526      *
    527      * @param path DOCUMENT ME!
    528      * @return
    529      */
    530     String getLoggerName(TreePath path) {
    531         if (path != null) {
    532             Object[] objects = path.getPath();
    533             StringBuilder buf = new StringBuilder();
    534
    535             for (int i = 1; i < objects.length; i++) {
    536                 buf.append(objects[i].toString());
    537
    538                 if (i < (objects.length - 1)) {
    539                     buf.append(".");
    540                 }
    541             }
    542
    543             return buf.toString();
    544         }
    545
    546         return null;
    547     }
    548
    549     /**
    550      * adds a Collection of Strings to the ignore List and notifise all listeners of
    551      * both the "hiddenSet" property and those expecting the Rule to change
    552      * via the ChangeListener interface
    553      *
    554      * @param fqnLoggersToIgnore
    555      */
    556     void ignore(Collection fqnLoggersToIgnore) {
    557         hiddenSet.addAll(fqnLoggersToIgnore);
    558         visibilityRuleDelegate.firePropertyChange("hiddenSet", null, null);
    559         fireChangeEvent();
    560     }
    561
    562     /**
    563      * Returns true if the FocusOn element has been selected
    564      *
    565      * @return true if the FocusOn action/lement has been selected
    566      */
    567     boolean isFocusOnSelected() {
    568         return focusOnAction.getValue("checked") != null;
    569     }
    570
    571     void setFocusOnSelected(boolean selected) {
    572         if (selected) {
    573             focusOnAction.putValue("checked", Boolean.TRUE);
    574         } else {
    575             focusOnAction.putValue("checked", null);
    576         }
    577     }
    578
    579     /**
    580      * Given the currently selected nodes
    581      * collapses all the children of those nodes.
    582      */
    583     private void collapseCurrentlySelectedNode() {
    584         TreePath[] paths = logTree.getSelectionPaths();
    585
    586         if (paths == null) {
    587             return;
    588         }
    589
    590         logger.debug("Collapsing all children of selected node");
    591
    592         for (TreePath path : paths) {
    593             DefaultMutableTreeNode node =
    594                 (DefaultMutableTreeNode) path.getLastPathComponent();
    595             Enumeration enumeration = node.depthFirstEnumeration();
    596
    597             while (enumeration.hasMoreElements()) {
    598                 DefaultMutableTreeNode child =
    599                     (DefaultMutableTreeNode) enumeration.nextElement();
    600
    601                 if ((child.getParent() != null) && (child != node)) {
    602                     TreeNode[] nodes =
    603                         ((DefaultMutableTreeNode) child.getParent()).getPath();
    604
    605                     TreePath treePath = new TreePath(nodes);
    606                     logTree.collapsePath(treePath);
    607                 }
    608             }
    609         }
    610
    611         ensureRootExpanded();
    612     }
    613
    614     /**
    615      * configures all the components that are used in the mini-toolbar of this
    616      * component
    617      */
    618     private void configureToolbarPanel() {
    619         toolbar.setFloatable(false);
    620
    621         expandButton.setAction(expandAction);
    622         expandButton.setText(null);
    623         collapseButton.setAction(collapseAction);
    624         collapseButton.setText(null);
    625         focusOnLoggerButton.setAction(focusOnAction);
    626         focusOnLoggerButton.setText(null);
    627         ignoreLoggerButton.setAction(hideAction);
    628         ignoreLoggerButton.setText(null);
    629
    630         expandButton.setFont(expandButton.getFont().deriveFont(Font.BOLD));
    631         collapseButton.setFont(collapseButton.getFont().deriveFont(Font.BOLD));
    632
    633         editLoggerButton.setAction(editLoggerAction);
    634         editLoggerButton.setText(null);
    635         closeButton.setAction(closeAction);
    636         closeButton.setText(null);
    637
    638         toolbar.add(expandButton);
    639         toolbar.add(collapseButton);
    640         toolbar.addSeparator();
    641         toolbar.add(focusOnLoggerButton);
    642         toolbar.add(ignoreLoggerButton);
    643
    644         //    toolbar.add(editLoggerButton);
    645         toolbar.addSeparator();
    646
    647         toolbar.add(Box.createHorizontalGlue());
    648         toolbar.add(closeButton);
    649         toolbar.add(Box.createHorizontalStrut(5));
    650     }
    651
    652     /**
    653      * DOCUMENT ME!
    654      *
    655      * @return
    656      */
    657     private Action createClearIgnoreListAction() {
    658         Action action = new AbstractAction("Clear Ignore list", null) {
    659             public void actionPerformed(ActionEvent e) {
    660                 ignoreLoggerButton.setSelected(false);
    661                 logTreeModel.reload();
    662                 hiddenSet.clear();
    663                 fireChangeEvent();
    664             }
    665         };
    666
    667         action.putValue(
    668             Action.SHORT_DESCRIPTION,
    669             "Removes all entries from the Ignore list so you can see their events in the view");
    670
    671         return action;
    672     }
    673
    674     /**
    675      * An action that closes (hides) this panel
    676      *
    677      * @return
    678      */
    679     private Action createCloseAction() {
    680         Action action = new AbstractAction() {
    681             public void actionPerformed(ActionEvent e) {
    682                 preferenceModel.setLogTreePanelVisible(false);
    683             }
    684         };
    685
    686         action.putValue(Action.NAME, "Close");
    687         action.putValue(Action.SHORT_DESCRIPTION, "Closes the Logger panel");
    688         action.putValue(Action.SMALL_ICON, LineIconFactory.createCloseIcon());
    689
    690         return action;
    691     }
    692
    693     /**
    694      * DOCUMENT ME!
    695      *
    696      * @return
    697      */
    698     private Action createCollapseAction() {
    699         Action action = new AbstractAction() {
    700             public void actionPerformed(ActionEvent e) {
    701                 collapseCurrentlySelectedNode();
    702             }
    703         };
    704
    705         action.putValue(Action.SMALL_ICON, LineIconFactory.createCollapseIcon());
    706         action.putValue(Action.NAME, "Collapse Branch");
    707         action.putValue(
    708             Action.SHORT_DESCRIPTION,
    709             "Collapses all the children of the currently selected node");
    710         action.setEnabled(false);
    711
    712         return action;
    713     }
    714
    715     private Action createEditLoggerAction() {
    716         Action action = new AbstractAction() {
    717             public void actionPerformed(ActionEvent e) {
    718                 // TODO Auto-generated method stub
    719             }
    720         };
    721
    722         //    TODO enable this when it's ready.
    723         action.putValue("enabled", Boolean.FALSE);
    724
    725         action.putValue(Action.NAME, "Edit filters/colors");
    726         action.putValue(
    727             Action.SHORT_DESCRIPTION,
    728             "Allows you to specify filters and coloring for this Logger");
    729         action.putValue(
    730             Action.SMALL_ICON, new ImageIcon(ChainsawIcons.ICON_EDIT_RECEIVER));
    731         action.setEnabled(false);
    732
    733         return action;
    734     }
    735
    736     /**
    737      * Creates an action that is used to expand the selected node
    738      * and all children
    739      *
    740      * @return an Action
    741      */
    742     private Action createExpandAction() {
    743         Action action = new AbstractAction() {
    744             public void actionPerformed(ActionEvent e) {
    745                 expandCurrentlySelectedNode();
    746             }
    747         };
    748
    749         action.putValue(Action.SMALL_ICON, LineIconFactory.createExpandIcon());
    750         action.putValue(Action.NAME, "Expand branch");
    751         action.putValue(
    752             Action.SHORT_DESCRIPTION,
    753             "Expands all the child nodes of the currently selected node, recursively");
    754         action.setEnabled(false);
    755
    756         return action;
    757     }
    758
    759     /**
    760      * Creates an action that is used to find the next match of the selected node (similar to default selection behavior
    761      * except the search field is populated and the next match is selected.
    762      *
    763      * @return an Action
    764      */
    765     private Action createFindNextAction() {
    766         Action action = new AbstractAction() {
    767             public void actionPerformed(ActionEvent e) {
    768                 findNextUsingCurrentlySelectedNode();
    769             }
    770         };
    771
    772         action.putValue(Action.NAME, "Find next");
    773         action.putValue(
    774             Action.SHORT_DESCRIPTION,
    775             "Find using the selected node");
    776         action.setEnabled(false);
    777
    778         return action;
    779     }
    780
    781     private Action createSetRefineFocusAction() {
    782         Action action = new AbstractAction() {
    783             public void actionPerformed(ActionEvent e) {
    784                 setRefineFocusUsingCurrentlySelectedNode();
    785             }
    786         };
    787
    788         action.putValue(Action.NAME, "Set 'refine focus' to selected logger");
    789         action.putValue(
    790             Action.SHORT_DESCRIPTION,
    791             "Refine focus on the selected node");
    792         action.setEnabled(false);
    793
    794         return action;
    795     }
    796
    797     private Action createUpdateRefineFocusAction() {
    798         Action action = new AbstractAction() {
    799             public void actionPerformed(ActionEvent e) {
    800                 updateRefineFocusUsingCurrentlySelectedNode();
    801             }
    802         };
    803
    804         action.putValue(Action.NAME, "Update 'refine focus' to include selected logger");
    805         action.putValue(
    806             Action.SHORT_DESCRIPTION,
    807             "Add selected node to 'refine focus' field");
    808         action.setEnabled(false);
    809
    810         return action;
    811     }
    812
    813     private Action createUpdateFindAction() {
    814         Action action = new AbstractAction() {
    815             public void actionPerformed(ActionEvent e) {
    816                 updateFindUsingCurrentlySelectedNode();
    817             }
    818         };
    819
    820         action.putValue(Action.NAME, "Update 'find' to include selected logger");
    821         action.putValue(
    822             Action.SHORT_DESCRIPTION,
    823             "Add selected node to 'find' field");
    824         action.setEnabled(false);
    825
    826         return action;
    827     }
    828
    829     private void updateFindUsingCurrentlySelectedNode() {
    830         String selectedLogger = getCurrentlySelectedLoggerName();
    831         TreePath[] paths = logTree.getSelectionPaths();
    832
    833         if (paths == null) {
    834             return;
    835         }
    836         String currentFindText = logPanel.getFindText();
    837         logPanel.setFindText(currentFindText + " || logger ~= " + selectedLogger);
    838     }
    839
    840     private void updateRefineFocusUsingCurrentlySelectedNode() {
    841         String selectedLogger = getCurrentlySelectedLoggerName();
    842         TreePath[] paths = logTree.getSelectionPaths();
    843
    844         if (paths == null) {
    845             return;
    846         }
    847         String currentFilterText = logPanel.getRefineFocusText();
    848         logPanel.setRefineFocusText(currentFilterText + " || logger ~= " + selectedLogger);
    849     }
    850
    851     private void setRefineFocusUsingCurrentlySelectedNode() {
    852         String selectedLogger = getCurrentlySelectedLoggerName();
    853         TreePath[] paths = logTree.getSelectionPaths();
    854
    855         if (paths == null) {
    856             return;
    857         }
    858         logPanel.setRefineFocusText("logger ~= " + selectedLogger);
    859     }
    860
    861     private Action createDefineColorRuleForLoggerAction() {
    862         Action action = new AbstractAction() {
    863             public void actionPerformed(ActionEvent e) {
    864                 String selectedLogger = getCurrentlySelectedLoggerName();
    865                 TreePath[] paths = logTree.getSelectionPaths();
    866
    867                 if (paths == null) {
    868                     return;
    869                 }
    870                 Color c = JColorChooser.showDialog(getRootPane(), "Choose a color", Color.red);
    871                 if (c != null) {
    872                     String expression = "logger like '^" + selectedLogger + ".*'";
    873                     colorizer.addRule(ChainsawConstants.DEFAULT_COLOR_RULE_NAME, new ColorRule(expression,
    874                         ExpressionRule.getRule(expression), c, ChainsawConstants.COLOR_DEFAULT_FOREGROUND));
    875                 }
    876             }
    877         };
    878
    879         action.putValue(Action.NAME, "Define color rule for selected logger");
    880         action.putValue(
    881             Action.SHORT_DESCRIPTION,
    882             "Define color rule for logger");
    883         action.setEnabled(false);
    884         return action;
    885     }
    886
    887     /**
    888      * Creates an action that is used to find the next match of the selected node (similar to default selection behavior
    889      * except the search field is populated and the next match is selected.
    890      *
    891      * @return an Action
    892      */
    893     private Action createClearFindNextAction() {
    894         Action action = new AbstractAction() {
    895             public void actionPerformed(ActionEvent e) {
    896                 clearFindNext();
    897             }
    898         };
    899
    900         action.putValue(Action.NAME, "Clear find field");
    901         action.putValue(
    902             Action.SHORT_DESCRIPTION,
    903             "Clear the find field");
    904         action.setEnabled(false);
    905
    906         return action;
    907     }
    908
    909     private Action createClearRefineFocusAction() {
    910         Action action = new AbstractAction() {
    911             public void actionPerformed(ActionEvent e) {
    912                 clearRefineFocus();
    913             }
    914         };
    915
    916         action.putValue(Action.NAME, "Clear 'refine focus' field");
    917         action.putValue(
    918             Action.SHORT_DESCRIPTION,
    919             "Clear the refine focus field");
    920         action.setEnabled(false);
    921
    922         return action;
    923     }
    924
    925     /**
    926      * DOCUMENT ME!
    927      *
    928      * @return
    929      */
    930     private Action createFocusOnAction() {
    931         final Action action = new AbstractAction() {
    932             public void actionPerformed(ActionEvent e) {
    933                 toggleFocusOnState();
    934             }
    935         };
    936
    937         action.putValue(Action.NAME, "Focus");
    938         action.putValue(
    939             Action.SHORT_DESCRIPTION,
    940             "Allows you to Focus on the selected logger by setting a filter that discards all but this Logger");
    941         action.putValue(
    942             Action.SMALL_ICON, new ImageIcon(ChainsawIcons.WINDOW_ICON));
    943
    944         action.setEnabled(false);
    945
    946         return action;
    947     }
    948
    949     /**
    950      * DOCUMENT ME!
    951      *
    952      * @return
    953      */
    954     private Action createIgnoreAllAction() {
    955         Action action =
    956             new AbstractAction(
    957                 "Ignore loggers below selection") {
    958                 public void actionPerformed(ActionEvent e) {
    959                     //add all top level loggers as hidden loggers
    960                     TreePath[] paths = logTree.getSelectionPaths();
    961
    962                     StringBuilder parentPathString = new StringBuilder();
    963                     DefaultMutableTreeNode root;
    964                     if ((paths == null) || (paths.length == 0)) {
    965                         root = (DefaultMutableTreeNode) logTreeModel.getRoot();
    966                     } else {
    967                         root = (DefaultMutableTreeNode) logTree.getSelectionPath().getLastPathComponent();
    968                         TreeNode[] path = root.getPath();
    969                         //don't add 'root logger' to path string
    970                         for (int i = 1; i < path.length; i++) {
    971                             if (i > 1) {
    972                                 parentPathString.append(".");
    973                             }
    974                             parentPathString.append(path[i].toString());
    975                         }
    976                         if (!(parentPathString.toString().equals(""))) {
    977                             parentPathString.append(".");
    978                         }
    979                     }
    980                     Enumeration topLevelLoggersEnumeration = root.children();
    981                     Set topLevelLoggersSet = new HashSet();
    982                     while (topLevelLoggersEnumeration.hasMoreElements()) {
    983                         String thisLogger = topLevelLoggersEnumeration.nextElement().toString();
    984                         topLevelLoggersSet.add(parentPathString + thisLogger);
    985                     }
    986                     if (topLevelLoggersSet.size() > 0) {
    987                         ignore(topLevelLoggersSet);
    988                     }
    989
    990                     logTreeModel.nodeChanged(root);
    991                     ignoreLoggerButton.setSelected(false);
    992                     focusOnAction.setEnabled(false);
    993                     popupMenu.hideCheck.setSelected(false);
    994                     fireChangeEvent();
    995                 }
    996             };
    997
    998         action.putValue(
    999             Action.SHORT_DESCRIPTION,
    1000             "Adds all loggers to your Ignore list (unhide loggers you want to see in the view)");
    1001
    1002         return action;
    1003     }
    1004
    1005     /**
    1006      * DOCUMENT ME!
    1007      *
    1008      * @return
    1009      */
    1010     private Action createIgnoreAction() {
    1011         Action action =
    1012             new AbstractAction(
    1013                 "Ignore this Logger", new ImageIcon(ChainsawIcons.ICON_COLLAPSE)) {
    1014                 public void actionPerformed(ActionEvent e) {
    1015                     String logger = getCurrentlySelectedLoggerName();
    1016
    1017                     if (logger != null) {
    1018                         toggleHiddenLogger(logger);
    1019                         logTreeModel.nodeChanged(
    1020                             (TreeNode) logTree.getSelectionPath().getLastPathComponent());
    1021                         ignoreLoggerButton.setSelected(hiddenSet.contains(logger));
    1022                         focusOnAction.setEnabled(!hiddenSet.contains(logger));
    1023                         popupMenu.hideCheck.setSelected(hiddenSet.contains(logger));
    1024                     }
    1025
    1026                     fireChangeEvent();
    1027                 }
    1028             };
    1029
    1030         action.putValue(
    1031             Action.SHORT_DESCRIPTION,
    1032             "Adds the selected Logger to your Ignore list, filtering those events from view");
    1033
    1034         return action;
    1035     }
    1036
    1037     private void ensureRootExpanded() {
    1038         logger.debug("Ensuring Root node is expanded.");
    1039
    1040         final DefaultMutableTreeNode root =
    1041             (DefaultMutableTreeNode) logTreeModel.getRoot();
    1042         SwingUtilities.invokeLater(() -> logTree.expandPath(new TreePath(root)));
    1043     }
    1044
    1045     private void findNextUsingCurrentlySelectedNode() {
    1046         String selectedLogger = getCurrentlySelectedLoggerName();
    1047         TreePath[] paths = logTree.getSelectionPaths();
    1048
    1049         if (paths == null) {
    1050             return;
    1051         }
    1052         logPanel.setFindText("logger like '^" + selectedLogger + ".*'");
    1053     }
    1054
    1055     private void clearFindNext() {
    1056         logPanel.setFindText("");
    1057     }
    1058
    1059     private void clearRefineFocus() {
    1060         logPanel.setRefineFocusText("");
    1061     }
    1062
    1063     /**
    1064      * Expands the currently selected node (if any)
    1065      * including all the children.
    1066      */
    1067     private void expandCurrentlySelectedNode() {
    1068         TreePath[] paths = logTree.getSelectionPaths();
    1069
    1070         if (paths == null) {
    1071             return;
    1072         }
    1073
    1074         logger.debug("Expanding all children of selected node");
    1075
    1076         for (TreePath path : paths) {
    1077             /**
    1078              * TODO this is commented out, right now it expands all nodes including the root, so if there is a large tree..... look out.
    1079              */
    1080
    1081             //      /**
    1082             //       * Handle an expansion of the Root node by only doing the first level.
    1083             //       * Safe...
    1084             //       */
    1085             //      if (path.getPathCount() == 1) {
    1086             //        logTree.expandPath(path);
    1087             //
    1088             //        return;
    1089             //      }
    1090
    1091             DefaultMutableTreeNode treeNode =
    1092                 (DefaultMutableTreeNode) path.getLastPathComponent();
    1093
    1094             Enumeration depthEnum = treeNode.depthFirstEnumeration();
    1095
    1096             if (!depthEnum.hasMoreElements()) {
    1097                 break;
    1098             }
    1099
    1100             List<Integer> depths = new ArrayList<>();
    1101
    1102             while (depthEnum.hasMoreElements()) {
    1103                 depths.add(
    1104                     ((DefaultMutableTreeNode) depthEnum.nextElement()).getDepth());
    1105             }
    1106
    1107             Collections.sort(depths);
    1108             Collections.reverse(depths);
    1109
    1110             int maxDepth = depths.get(0);
    1111
    1112             if (maxDepth > WARN_DEPTH) {
    1113                 logger.warn("Should warn user, depth=" + maxDepth);
    1114             }
    1115
    1116             depthEnum = treeNode.depthFirstEnumeration();
    1117
    1118             while (depthEnum.hasMoreElements()) {
    1119                 DefaultMutableTreeNode node =
    1120                     (DefaultMutableTreeNode) depthEnum.nextElement();
    1121
    1122                 if (node.isLeaf() && node.getParent() != null) {
    1123                     TreeNode[] nodes =
    1124                         ((DefaultMutableTreeNode) node.getParent()).getPath();
    1125                     TreePath treePath = new TreePath(nodes);
    1126
    1127                     logger.debug("Expanding path:" + treePath);
    1128
    1129                     logTree.expandPath(treePath);
    1130                 }
    1131             }
    1132         }
    1133     }
    1134
    1135     private void fireChangeEvent() {
    1136         ChangeListener[] listeners =
    1137             listenerList.getListeners(ChangeListener.class);
    1138         ChangeEvent e = null;
    1139
    1140         for (ChangeListener listener : listeners) {
    1141             if (e == null) {
    1142                 e = new ChangeEvent(this);
    1143             }
    1144
    1145             listener.stateChanged(e);
    1146         }
    1147     }
    1148
    1149     private void reconfigureMenuText() {
    1150         String logger = getCurrentlySelectedLoggerName();
    1151
    1152         if ((logger == null) || (logger.length() == 0)) {
    1153             focusOnAction.putValue(Action.NAME, "Focus On...");
    1154             hideAction.putValue(Action.NAME, "Ignore...");
    1155             findAction.putValue(Action.NAME, "Find...");
    1156             setRefineFocusAction.putValue(Action.NAME, "Set refine focus field");
    1157             updateRefineFocusAction.putValue(Action.NAME, "Add to refine focus field");
    1158             updateFindAction.putValue(Action.NAME, "Add to find field");
    1159             defineColorRuleForLoggerAction.putValue(Action.NAME, "Define color rule");
    1160         } else {
    1161             focusOnAction.putValue(Action.NAME, "Focus On '" + logger + "'");
    1162             hideAction.putValue(Action.NAME, "Ignore '" + logger + "'");
    1163             findAction.putValue(Action.NAME, "Find '" + logger + "'");
    1164             setRefineFocusAction.putValue(Action.NAME, "Set refine focus field to '" + logger + "'");
    1165             updateRefineFocusAction.putValue(Action.NAME, "Add '" + logger + "' to 'refine focus' field");
    1166             updateFindAction.putValue(Action.NAME, "Add '" + logger + "' to 'find' field");
    1167             defineColorRuleForLoggerAction.putValue(Action.NAME, "Define color rule for '" + logger + "'");
    1168         }
    1169
    1170         // need to ensure the button doens't update itself with the text, looks stupid otherwise
    1171         hideSubLoggersAction.putValue(Action.NAME, "Ignore loggers below selection");
    1172         focusOnLoggerButton.setText(null);
    1173         ignoreLoggerButton.setText(null);
    1174     }
    1175
    1176     /**
    1177      * Configures varoius listeners etc for the components within
    1178      * this Class.
    1179      */
    1180     private void setupListeners() {
    1181         logTree.addMouseMotionListener(new MouseKeyIconListener());
    1182
    1183         /**
    1184          * Enable the actions depending on state of the tree selection
    1185          */
    1186         logTree.addTreeSelectionListener(e -> {
    1187             TreePath path = e.getNewLeadSelectionPath();
    1188             TreeNode node = null;
    1189
    1190             if (path != null) {
    1191                 node = (TreeNode) path.getLastPathComponent();
    1192             }
    1193             boolean focusOnSelected = isFocusOnSelected();
    1194             //          editLoggerAction.setEnabled(path != null);
    1195             currentlySelectedLoggerName = getCurrentlySelectedLoggerName();
    1196             focusOnAction.setEnabled(
    1197                 (path != null) && (node != null) && (node.getParent() != null)
    1198                     && !hiddenSet.contains(currentlySelectedLoggerName));
    1199             hideAction.setEnabled(
    1200                 (path != null) && (node != null) && (node.getParent() != null));
    1201             if (!focusOnAction.isEnabled()) {
    1202                 setFocusOnSelected(false);
    1203             } else {
    1204             }
    1205
    1206             expandAction.setEnabled(path != null);
    1207             findAction.setEnabled(path != null);
    1208             clearFindNextAction.setEnabled(true);
    1209             defineColorRuleForLoggerAction.setEnabled(path != null);
    1210             setRefineFocusAction.setEnabled(path != null);
    1211             updateRefineFocusAction.setEnabled(path != null);
    1212             updateFindAction.setEnabled(path != null);
    1213             clearRefineFocusAction.setEnabled(true);
    1214
    1215             if (currentlySelectedLoggerName != null) {
    1216                 boolean isHidden = hiddenSet.contains(currentlySelectedLoggerName);
    1217                 popupMenu.hideCheck.setSelected(isHidden);
    1218                 ignoreLoggerButton.setSelected(isHidden);
    1219             }
    1220
    1221             collapseAction.setEnabled(path != null);
    1222
    1223             reconfigureMenuText();
    1224             if (isFocusOnSelected()) {
    1225                 fireChangeEvent();
    1226             }
    1227             //fire change event if we toggled focus off
    1228             if (focusOnSelected && !isFocusOnSelected()) {
    1229                 fireChangeEvent();
    1230             }
    1231             //trigger a table repaint
    1232             logPanel.repaint();
    1233         });
    1234
    1235         logTree.addMouseListener(popupListener);
    1236
    1237         /**
    1238          * This listener ensures the Tool bar toggle button and popup menu check box
    1239          * stay in sync, plus notifies all the ChangeListeners that
    1240          * an effective filter criteria has been modified
    1241          */
    1242         focusOnAction.addPropertyChangeListener(evt -> {
    1243             popupMenu.focusOnCheck.setSelected(isFocusOnSelected());
    1244             focusOnLoggerButton.setSelected(isFocusOnSelected());
    1245
    1246             if (logTree.getSelectionPath() != null) {
    1247                 logTreeModel.nodeChanged(
    1248                     (TreeNode) logTree.getSelectionPath().getLastPathComponent());
    1249             }
    1250         });
    1251
    1252         hideAction.addPropertyChangeListener(evt -> {
    1253             if (logTree.getSelectionPath() != null) {
    1254                 logTreeModel.nodeChanged(
    1255                     (TreeNode) logTree.getSelectionPath().getLastPathComponent());
    1256             }
    1257         });
    1258
    1259         //    /**
    1260         //     * Now add a MouseListener that fires the expansion
    1261         //     * action if CTRL + DBL CLICK is done.
    1262         //     */
    1263         //    logTree.addMouseListener(
    1264         //      new MouseAdapter() {
    1265         //        public void mouseClicked(MouseEvent e) {
    1266         //          if (
    1267         //            (e.getClickCount() > 1)
    1268         //              && ((e.getModifiers() & InputEvent.CTRL_MASK) > 0)
    1269         //              && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 0)) {
    1270         //            expandCurrentlySelectedNode();
    1271         //            e.consume();
    1272         //          } else if (e.getClickCount() > 1) {
    1273         //            super.mouseClicked(e);
    1274         //            logger.debug("Ignoring dbl click event " + e);
    1275         //          }
    1276         //        }
    1277         //      });
    1278
    1279         logTree.addMouseListener(new MouseFocusOnListener());
    1280
    1281         /**
    1282          * We listen for when the FocusOn action changes, and then  translate
    1283          * that to a RuleChange
    1284          */
    1285         addChangeListener(evt -> {
    1286             visibilityRuleDelegate.firePropertyChange("rule", null, null);
    1287             updateDisplay();
    1288         });
    1289
    1290         visibilityRuleDelegate.addPropertyChangeListener(event -> {
    1291             if (event.getPropertyName().equals("hiddenSet")) {
    1292                 updateDisplay();
    1293             }
    1294         });
    1295     }
    1296
    1297     private void updateDisplay() {
    1298         updateHiddenSetModels();
    1299         updateIgnoreSummary();
    1300         updateIgnoreExpressionSummary();
    1301         updateAlwaysDisplayExpressionSummary();
    1302     }
    1303
    1304     private void updateHiddenSetModels() {
    1305         DefaultListModel model = (DefaultListModel) ignoreList.getModel();
    1306         model.clear();
    1307         List sortedIgnoreList = new ArrayList(hiddenSet);
    1308         Collections.sort(sortedIgnoreList);
    1309
    1310         for (Object aSortedIgnoreList : sortedIgnoreList) {
    1311             String string = (String) aSortedIgnoreList;
    1312             model.addElement(string);
    1313         }
    1314
    1315 //      ignoreList.setModel(model);
    1316
    1317     }
    1318
    1319     private void updateIgnoreSummary() {
    1320         ignoreSummary.setText(ignoreList.getModel().getSize() + " hidden loggers");
    1321     }
    1322
    1323     private void updateIgnoreExpressionSummary() {
    1324         ignoreExpressionSummary.setText(ignoreExpressionRule != null ? "Ignore (set)" : "Ignore (unset)");
    1325     }
    1326
    1327     private void updateAlwaysDisplayExpressionSummary() {
    1328         alwaysDisplayExpressionSummary.setText(alwaysDisplayExpressionRule != null ? "Always displayed (set)" : "Always displayed (unset)");
    1329     }
    1330
    1331     private void toggleFocusOnState() {
    1332         setFocusOnSelected(!isFocusOnSelected());
    1333         fireChangeEvent();
    1334     }
    1335
    1336     public Collection getHiddenSet() {
    1337         return Collections.unmodifiableSet(hiddenSet);
    1338     }
    1339
    1340     public String getHiddenExpression() {
    1341         String text = ignoreExpressionEntryField.getText();
    1342         if (text == null || text.trim().equals("")) {
    1343             return null;
    1344         }
    1345         return text.trim();
    1346     }
    1347
    1348     public void setHiddenExpression(String hiddenExpression) {
    1349         ignoreExpressionEntryField.setText(hiddenExpression);
    1350         updateIgnoreExpression(hiddenExpression);
    1351     }
    1352
    1353     public String getAlwaysDisplayExpression() {
    1354         String text = alwaysDisplayExpressionEntryField.getText();
    1355         if (text == null || text.trim().equals("")) {
    1356             return null;
    1357         }
    1358         return text.trim();
    1359     }
    1360
    1361     public void setAlwaysDisplayExpression(String alwaysDisplayExpression) {
    1362         alwaysDisplayExpressionEntryField.setText(alwaysDisplayExpression);
    1363         updateAlwaysDisplayExpression(alwaysDisplayExpression);
    1364     }
    1365
    1366     public void loggerNameAdded(String loggerName) {
    1367         //no-op
    1368     }
    1369
    1370     public void reset() {
    1371         expandRootLatch = false;
    1372         //keep track if focuson was active when we were reset
    1373         final String logger = currentlySelectedLoggerName;
    1374         final boolean focusOnSelected = isFocusOnSelected();
    1375         if (logger == null || !focusOnSelected) {
    1376             return;
    1377         }
    1378
    1379         //loggernameAdded runs on EDT
    1380         logTreeModel.loggerNameAdded(logger);
    1381         EventQueue.invokeLater(() -> setFocusOn(logger));
    1382     }
    1383
    1384     //~ Inner Classes ===========================================================
    1385
    1386     /**
    1387      * DOCUMENT ME!
    1388      *
    1389      * @author $author$
    1390      * @author Paul Smith &lt;psmith@apache.org&gt;
    1391      * @version $Revision$, $Date$
    1392      */
    1393     private class LoggerNameTreeCellRenderer extends DefaultTreeCellRenderer {
    1394         //~ Constructors ==========================================================
    1395
    1396         //    private JPanel panel = new JPanel();
    1397         private LoggerNameTreeCellRenderer() {
    1398             super();
    1399
    1400             //      panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
    1401             //      panel.add(this);
    1402             setLeafIcon(null);
    1403             setOpaque(false);
    1404         }
    1405
    1406         //~ Methods ===============================================================
    1407
    1408         /* (non-Javadoc)
    1409          * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree, java.lang.Object, boolean, boolean, boolean, int, boolean)
    1410          */
    1411
    1412         /**
    1413          * DOCUMENT ME!
    1414          *
    1415          * @param tree     DOCUMENT ME!
    1416          * @param value    DOCUMENT ME!
    1417          * @param sel      DOCUMENT ME!
    1418          * @param expanded DOCUMENT ME!
    1419          * @param leaf     DOCUMENT ME!
    1420          * @param row      DOCUMENT ME!
    1421          * @param focus    DOCUMENT ME!
    1422          * @return DOCUMENT ME!
    1423          */
    1424         public Component getTreeCellRendererComponent(
    1425             JTree tree, Object value, boolean sel, boolean expanded, boolean leaf,
    1426             int row, boolean focus) {
    1427             JLabel component =
    1428                 (JLabel) super.getTreeCellRendererComponent(
    1429                     tree, value, sel, expanded, leaf, row, focus);
    1430
    1431             Font originalFont = new Font(component.getFont().getName(), component.getFont().getStyle(), component.getFont().getSize());
    1432
    1433             int style = Font.PLAIN;
    1434
    1435             if (sel && focusOnLoggerButton.isSelected()) {
    1436                 style = style | Font.BOLD;
    1437             }
    1438
    1439             String logger =
    1440                 getLoggerName(
    1441                     new TreePath(((DefaultMutableTreeNode) value).getPath()));
    1442
    1443             if (hiddenSet.contains(logger)) {
    1444                 //        component.setEnabled(false);
    1445                 //        component.setIcon(leaf?null:getDefaultOpenIcon());
    1446                 style = style | Font.ITALIC;
    1447
    1448                 //        logger.debug("TreeRenderer: '" + logger + "' is in hiddenSet, italicizing");
    1449             } else {
    1450                 //          logger.debug("TreeRenderer: '" + logger + "' is NOT in hiddenSet, leaving plain");
    1451                 //        component.setEnabled(true);
    1452             }
    1453
    1454             if (originalFont != null) {
    1455                 Font font2 = originalFont.deriveFont(style);
    1456
    1457                 if (font2 != null) {
    1458                     component.setFont(font2);
    1459                 }
    1460             }
    1461
    1462             return component;
    1463         }
    1464     }
    1465
    1466     private class LoggerTreePopupMenu extends JPopupMenu {
    1467         //~ Instance fields =======================================================
    1468
    1469         JCheckBoxMenuItem focusOnCheck = new JCheckBoxMenuItem();
    1470         JCheckBoxMenuItem hideCheck = new JCheckBoxMenuItem();
    1471
    1472         //~ Constructors ==========================================================
    1473
    1474         private LoggerTreePopupMenu() {
    1475             initMenu();
    1476         }
    1477
    1478         //~ Methods ===============================================================
    1479
    1480         /* (non-Javadoc)
    1481          * @see javax.swing.JPopupMenu#show(java.awt.Component, int, int)
    1482          */
    1483
    1484         /**
    1485          * DOCUMENT ME!
    1486          *
    1487          * @param invoker DOCUMENT ME!
    1488          * @param x       DOCUMENT ME!
    1489          * @param y       DOCUMENT ME!
    1490          */
    1491         public void show(Component invoker, int x, int y) {
    1492             DefaultMutableTreeNode node =
    1493                 (DefaultMutableTreeNode) logTree.getLastSelectedPathComponent();
    1494
    1495             if (node == null) {
    1496                 return;
    1497             }
    1498
    1499             super.show(invoker, x, y);
    1500         }
    1501
    1502         /**
    1503          * DOCUMENT ME!
    1504          */
    1505         private void initMenu() {
    1506             focusOnCheck.setAction(focusOnAction);
    1507             hideCheck.setAction(hideAction);
    1508             add(expandAction);
    1509             add(collapseAction);
    1510             addSeparator();
    1511             add(focusOnCheck);
    1512             add(hideCheck);
    1513             addSeparator();
    1514             add(setRefineFocusAction);
    1515             add(updateRefineFocusAction);
    1516             add(clearRefineFocusAction);
    1517             addSeparator();
    1518             add(findAction);
    1519             add(updateFindAction);
    1520             add(clearFindNextAction);
    1521
    1522             addSeparator();
    1523             add(defineColorRuleForLoggerAction);
    1524             addSeparator();
    1525
    1526             add(hideSubLoggersAction);
    1527             add(clearIgnoreListAction);
    1528         }
    1529     }
    1530
    1531     private final class MouseFocusOnListener extends MouseAdapter {
    1532         //~ Methods ===============================================================
    1533
    1534         /* (non-Javadoc)
    1535          * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
    1536          */
    1537
    1538         /**
    1539          * DOCUMENT ME!
    1540          *
    1541          * @param e DOCUMENT ME!
    1542          */
    1543         public void mouseClicked(MouseEvent e) {
    1544             if (
    1545                 (e.getClickCount() > 1)
    1546                     && ((e.getModifiers() & InputEvent.CTRL_MASK) > 0)
    1547                     && ((e.getModifiers() & InputEvent.SHIFT_MASK) > 0)) {
    1548                 ignoreLoggerAtPoint(e.getPoint());
    1549                 e.consume();
    1550                 fireChangeEvent();
    1551             } else if (
    1552                 (e.getClickCount() > 1)
    1553                     && ((e.getModifiers() & InputEvent.CTRL_MASK) > 0)) {
    1554                 focusAnLoggerAtPoint(e.getPoint());
    1555                 e.consume();
    1556                 fireChangeEvent();
    1557             }
    1558         }
    1559
    1560         /**
    1561          * DOCUMENT ME!
    1562          *
    1563          * @param point
    1564          */
    1565         private void focusAnLoggerAtPoint(Point point) {
    1566             String logger = getLoggerAtPoint(point);
    1567
    1568             if (logger != null) {
    1569                 toggleFocusOnState();
    1570             }
    1571         }
    1572
    1573         /**
    1574          * DOCUMENT ME!
    1575          *
    1576          * @param point
    1577          * @return
    1578          */
    1579         private String getLoggerAtPoint(Point point) {
    1580             TreePath path = logTree.getPathForLocation(point.x, point.y);
    1581
    1582             if (path != null) {
    1583                 return getLoggerName(path);
    1584             }
    1585
    1586             return null;
    1587         }
    1588
    1589         /**
    1590          * DOCUMENT ME!
    1591          *
    1592          * @param point
    1593          */
    1594         private void ignoreLoggerAtPoint(Point point) {
    1595             String logger = getLoggerAtPoint(point);
    1596
    1597             if (logger != null) {
    1598                 toggleHiddenLogger(logger);
    1599                 fireChangeEvent();
    1600             }
    1601         }
    1602     }
    1603
    1604     private final class MouseKeyIconListener extends MouseMotionAdapter
    1605         implements MouseMotionListener {
    1606         //~ Instance fields =======================================================
    1607
    1608         Cursor focusOnCursor =
    1609             Toolkit.getDefaultToolkit().createCustomCursor(
    1610                 ChainsawIcons.FOCUS_ON_ICON.getImage(), new Point(10, 10), "");
    1611         Cursor ignoreCursor =
    1612             Toolkit.getDefaultToolkit().createCustomCursor(
    1613                 ChainsawIcons.IGNORE_ICON.getImage(), new Point(10, 10), "");
    1614
    1615         //~ Methods ===============================================================
    1616
    1617         /* (non-Javadoc)
    1618          * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
    1619          */
    1620
    1621         /**
    1622          * DOCUMENT ME!
    1623          *
    1624          * @param e DOCUMENT ME!
    1625          */
    1626         public void mouseMoved(MouseEvent e) {
    1627             //      logger.debug(e.toString());
    1628             if (
    1629                 ((e.getModifiers() & InputEvent.CTRL_MASK) > 0)
    1630                     && ((e.getModifiers() & InputEvent.SHIFT_MASK) > 0)) {
    1631                 logTree.setCursor(ignoreCursor);
    1632             } else if ((e.getModifiers() & InputEvent.CTRL_MASK) > 0) {
    1633                 logTree.setCursor(focusOnCursor);
    1634             } else {
    1635                 logTree.setCursor(Cursor.getDefaultCursor());
    1636             }
    1637         }
    1638     }
    1639
    1640     class VisibilityRuleDelegate extends AbstractRule {
    1641         public boolean evaluate(LoggingEvent e, Map matches) {
    1642             String currentlySelectedLoggerName = getCurrentlySelectedLoggerName();
    1643             boolean hiddenLogger = e.getLoggerName() != null && isHiddenLogger(e.getLoggerName());
    1644             boolean hiddenExpression = (ignoreExpressionRule != null && ignoreExpressionRule.evaluate(e, null));
    1645             boolean alwaysDisplayExpression = (alwaysDisplayExpressionRule != null && alwaysDisplayExpressionRule.evaluate(e, null));
    1646             boolean hidden = (!alwaysDisplayExpression) && (hiddenLogger || hiddenExpression);
    1647             if (currentlySelectedLoggerName == null) {
    1648                 //if there is no selected logger, pass if not hidden
    1649                 return !hidden;
    1650             }
    1651             boolean result = (e.getLoggerName() != null) && !hidden;
    1652
    1653             if (result && isFocusOnSelected()) {
    1654                 result = (e.getLoggerName() != null && (e.getLoggerName().startsWith(currentlySelectedLoggerName + ".") || e.getLoggerName().endsWith(currentlySelectedLoggerName)));
    1655             }
    1656
    1657             return result;
    1658         }
    1659
    1660         public void firePropertyChange(String propertyName, Object oldVal, Object newVal) {
    1661             super.firePropertyChange(propertyName, oldVal, newVal);
    1662         }
    1663     }
    1664
    1665 }