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