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 <psmith@apache.org>
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 }