Added persistent "always display" expression support (button below the logger tree).  The 'always display' expression overrides hidden loggers and the hidden expression but not the refine focus filtering mechanism.  Often used with expressions like 'exception exists || level > warn' to ensure errors and exceptions are not filtered out due to the hidden expression or hidden logger mechanism.

git-svn-id: https://svn.apache.org/repos/asf/logging/chainsaw/trunk@1178301 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/log4j/chainsaw/LogPanel.java b/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
index aef99d7..6fe2a8b 100644
--- a/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
+++ b/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
@@ -2335,6 +2335,7 @@
     searchModel.setCyclic(preferenceModel.isCyclic());
     logTreePanel.ignore(preferenceModel.getHiddenLoggers());
     logTreePanel.setHiddenExpression(preferenceModel.getHiddenExpression());
+    logTreePanel.setAlwaysDisplayExpression(preferenceModel.getAlwaysDisplayExpression());
     if (preferenceModel.getClearTableExpression() != null) {
         try {
             clearTableExpressionRule = ExpressionRule.getRule(preferenceModel.getClearTableExpression());
@@ -2366,6 +2367,7 @@
 
     preferenceModel.setHiddenLoggers(new HashSet(logTreePanel.getHiddenSet()));
     preferenceModel.setHiddenExpression(logTreePanel.getHiddenExpression());
+    preferenceModel.setAlwaysDisplayExpression(logTreePanel.getAlwaysDisplayExpression());
     List visibleOrder = new ArrayList();
     Enumeration cols = table.getColumnModel().getColumns();
     while (cols.hasMoreElements()) {
diff --git a/src/main/java/org/apache/log4j/chainsaw/LogPanelPreferenceModel.java b/src/main/java/org/apache/log4j/chainsaw/LogPanelPreferenceModel.java
index 8634055..a1fdec6 100644
--- a/src/main/java/org/apache/log4j/chainsaw/LogPanelPreferenceModel.java
+++ b/src/main/java/org/apache/log4j/chainsaw/LogPanelPreferenceModel.java
@@ -84,6 +84,7 @@
   private boolean wrapMsg = true;
   private boolean highlightSearchMatchText;
   private String hiddenExpression;
+  private String alwaysDisplayExpression;
   private String clearTableExpression;
   //default to cyclic mode off
   private boolean cyclic = false;
@@ -254,6 +255,7 @@
     setVisibleColumns(model.getVisibleColumns());
     setHiddenLoggers(model.getHiddenLoggers());
     setHiddenExpression(model.getHiddenExpression());
+    setAlwaysDisplayExpression(model.getAlwaysDisplayExpression());
     setShowMillisDeltaAsGap(model.isShowMillisDeltaAsGap());
     setClearTableExpression(model.getClearTableExpression());
   }
@@ -506,6 +508,16 @@
     return hiddenExpression;
   }
 
+  public void setAlwaysDisplayExpression(String alwaysDisplayExpression) {
+    Object oldValue = this.hiddenExpression;
+    this.alwaysDisplayExpression = alwaysDisplayExpression;
+    propertySupport.firePropertyChange("alwaysDisplayExpression", oldValue, this.alwaysDisplayExpression);
+  }
+
+  public String getAlwaysDisplayExpression() {
+    return alwaysDisplayExpression;
+  }
+
   public void setClearTableExpression(String clearTableExpression) {
     Object oldValue = this.clearTableExpression;
     this.clearTableExpression = clearTableExpression;
diff --git a/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java b/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
index b451709..57f4ed9 100644
--- a/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
+++ b/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
@@ -137,11 +137,14 @@
 
   private final JList ignoreList = new JList();
   private final JEditorPane ignoreExpressionEntryField = new JEditorPane();
+  private final JEditorPane alwaysDisplayExpressionEntryField = new JEditorPane();
   private final JScrollPane ignoreListScroll = new JScrollPane(ignoreList);
   private final JDialog ignoreDialog = new JDialog();
   private final JDialog ignoreExpressionDialog = new JDialog();
+  private final JDialog alwaysDisplayExpressionDialog = new JDialog();
   private final JLabel ignoreSummary = new JLabel("0 hidden loggers");
   private final JLabel ignoreExpressionSummary = new JLabel("Ignore expression");
+  private final JLabel alwaysDisplayExpressionSummary = new JLabel("Always displayed expression");
   private final SmallToggleButton ignoreLoggerButton = new SmallToggleButton();
   private final EventListenerList listenerList = new EventListenerList();
   private final JTree logTree;
@@ -159,6 +162,7 @@
   private final LogPanel logPanel;
   private final RuleColorizer colorizer;
   private Rule ignoreExpressionRule;
+  private Rule alwaysDisplayExpressionRule;
   private boolean expandRootLatch = false;
   private String currentlySelectedLoggerName;
 
@@ -179,12 +183,13 @@
 
     setLayout(new BorderLayout());
     ignoreExpressionEntryField.setPreferredSize(new Dimension(300, 150));
-
+    alwaysDisplayExpressionEntryField.setPreferredSize(new Dimension(300, 150));
+    alwaysDisplayExpressionSummary.setMinimumSize(new Dimension(10, alwaysDisplayExpressionSummary.getHeight()));
     ignoreExpressionSummary.setMinimumSize(new Dimension(10, ignoreExpressionSummary.getHeight()));
     ignoreSummary.setMinimumSize(new Dimension(10, ignoreSummary.getHeight()));
 
     JTextComponentFormatter.applySystemFontAndSize(ignoreExpressionEntryField);
-
+    JTextComponentFormatter.applySystemFontAndSize(alwaysDisplayExpressionEntryField);
 
     visibilityRuleDelegate = new VisibilityRuleDelegate();
     colorRuleDelegate = 
@@ -194,7 +199,8 @@
           {
             boolean hiddenLogger = e.getLoggerName() != null && isHiddenLogger(e.getLoggerName());
             boolean hiddenExpression = (ignoreExpressionRule != null && ignoreExpressionRule.evaluate(e, null));
-            boolean hidden = hiddenLogger || hiddenExpression;
+            boolean alwaysDisplayExpression = (alwaysDisplayExpressionRule != null && alwaysDisplayExpressionRule.evaluate(e, null));
+            boolean hidden = (!alwaysDisplayExpression) && (hiddenLogger || hiddenExpression);
             String currentlySelectedLoggerName = getCurrentlySelectedLoggerName();
 
             if (!isFocusOnSelected() && !hidden && currentlySelectedLoggerName != null && !"".equals(currentlySelectedLoggerName))
@@ -298,6 +304,9 @@
     ignoreExpressionDialog.setTitle("Hidden/Ignored Expression");
     ignoreExpressionDialog.setModal(true);
 
+    alwaysDisplayExpressionDialog.setTitle("Always displayed Expression");
+    alwaysDisplayExpressionDialog.setModal(true);
+
     JPanel ignorePanel = new JPanel();
     ignorePanel.setLayout(new BoxLayout(ignorePanel, BoxLayout.Y_AXIS));
     JPanel ignoreSummaryPanel = new JPanel();
@@ -315,6 +324,14 @@
           LogPanel.centerAndSetVisible(ignoreExpressionDialog);
       }
     };
+
+
+    Action showAlwaysDisplayExpressionDialogAction = new AbstractAction("...") {
+      public void actionPerformed(ActionEvent e) {
+          LogPanel.centerAndSetVisible(alwaysDisplayExpressionDialog);
+      }
+    };
+
     showIgnoreDialogAction.putValue(Action.SHORT_DESCRIPTION, "Click to view and manage your hidden/ignored loggers");
     JButton btnShowIgnoreDialog = new SmallButton(showIgnoreDialogAction);
     
@@ -329,6 +346,16 @@
     ignoreExpressionPanel.add(btnShowIgnoreExpressionDialog);
 
     ignorePanel.add(ignoreExpressionPanel);
+
+    JPanel alwaysDisplayExpressionPanel = new JPanel();
+    alwaysDisplayExpressionPanel.setLayout(new BoxLayout(alwaysDisplayExpressionPanel, BoxLayout.X_AXIS));
+    alwaysDisplayExpressionPanel.add(alwaysDisplayExpressionSummary);
+    showAlwaysDisplayExpressionDialogAction.putValue(Action.SHORT_DESCRIPTION, "Click to view and manage your always-displayed expression");
+    JButton btnShowAlwaysDisplayExpressionDialog = new SmallButton(showAlwaysDisplayExpressionDialogAction);
+    alwaysDisplayExpressionPanel.add(btnShowAlwaysDisplayExpressionDialog);
+
+    ignorePanel.add(alwaysDisplayExpressionPanel);
+
     add(ignorePanel, BorderLayout.SOUTH);
 
     ignoreList.setModel(new DefaultListModel());
@@ -376,9 +403,29 @@
                 ignoreExpressionDialog.setVisible(false);
               }
           }});
-    JPanel closePanel = new JPanel();
-    closePanel.add(ignoreExpressionCloseButton);
-    ignoreExpressionDialogPanel.add(closePanel, BorderLayout.SOUTH);
+
+
+    JPanel alwaysDisplayExpressionDialogPanel = new JPanel(new BorderLayout());
+    alwaysDisplayExpressionEntryField.addKeyListener(new ExpressionRuleContext(filterModel, alwaysDisplayExpressionEntryField));
+
+    alwaysDisplayExpressionDialogPanel.add(new JScrollPane(alwaysDisplayExpressionEntryField), BorderLayout.CENTER);
+    JButton alwaysDisplayExpressionCloseButton = new JButton(new AbstractAction(" Close ") {
+          public void actionPerformed(ActionEvent e)
+          {
+              String alwaysDisplayText = alwaysDisplayExpressionEntryField.getText();
+
+              if (updateAlwaysDisplayExpression(alwaysDisplayText)) {
+                alwaysDisplayExpressionDialog.setVisible(false);
+              }
+          }});
+
+    JPanel closeAlwaysDisplayExpressionPanel = new JPanel();
+    closeAlwaysDisplayExpressionPanel.add(alwaysDisplayExpressionCloseButton);
+    alwaysDisplayExpressionDialogPanel.add(closeAlwaysDisplayExpressionPanel, BorderLayout.SOUTH);
+
+    JPanel closeIgnoreExpressionPanel = new JPanel();
+    closeIgnoreExpressionPanel.add(ignoreExpressionCloseButton);
+    ignoreExpressionDialogPanel.add(closeIgnoreExpressionPanel, BorderLayout.SOUTH);
 
     Box ignoreListButtonPanel = Box.createHorizontalBox();
     
@@ -415,6 +462,9 @@
 
     ignoreExpressionDialog.getContentPane().add(ignoreExpressionDialogPanel);
     ignoreExpressionDialog.pack();
+
+    alwaysDisplayExpressionDialog.getContentPane().add(alwaysDisplayExpressionDialogPanel);
+    alwaysDisplayExpressionDialog.pack();
   }
 
     private boolean updateIgnoreExpression(String ignoreText)
@@ -427,7 +477,7 @@
             }
             visibilityRuleDelegate.firePropertyChange("hiddenSet", null, null);
 
-            updateAllIgnoreStuff();
+            updateDisplay();
             ignoreExpressionEntryField.setBackground(UIManager.getColor("TextField.background"));
             return true;
         } catch (IllegalArgumentException iae) {
@@ -437,6 +487,26 @@
         }
     }
 
+    private boolean updateAlwaysDisplayExpression(String alwaysDisplayText)
+    {
+        try {
+            if (alwaysDisplayText != null && !alwaysDisplayText.trim().equals("")) {
+                alwaysDisplayExpressionRule = ExpressionRule.getRule(alwaysDisplayText);
+            } else {
+                alwaysDisplayExpressionRule = null;
+            }
+            visibilityRuleDelegate.firePropertyChange("alwaysDisplayedSet", null, null);
+
+            updateDisplay();
+            alwaysDisplayExpressionEntryField.setBackground(UIManager.getColor("TextField.background"));
+            return true;
+        } catch (IllegalArgumentException iae) {
+            alwaysDisplayExpressionEntryField.setToolTipText(iae.getMessage());
+            alwaysDisplayExpressionEntryField.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
+            return false;
+        }
+    }
+
     //~ Methods =================================================================
 
   /**
@@ -1426,7 +1496,7 @@
         public void stateChanged(ChangeEvent evt)
         {
           visibilityRuleDelegate.firePropertyChange("rule", null, null);
-          updateAllIgnoreStuff();
+          updateDisplay();
         }
       });
 
@@ -1435,16 +1505,17 @@
         public void propertyChange(PropertyChangeEvent event)
         {
           if (event.getPropertyName().equals("hiddenSet")) {
-            updateAllIgnoreStuff();
+            updateDisplay();
           }
         }
       });
   }
 
-  private void updateAllIgnoreStuff() {
+  private void updateDisplay() {
       updateHiddenSetModels();
       updateIgnoreSummary();
       updateIgnoreExpressionSummary();
+      updateAlwaysDisplayExpressionSummary();
   }
   
   private void updateHiddenSetModels() {
@@ -1470,6 +1541,10 @@
     ignoreExpressionSummary.setText(ignoreExpressionRule != null?"Ignore (set)":"Ignore (unset)");
   }
   
+  private void updateAlwaysDisplayExpressionSummary() {
+    alwaysDisplayExpressionSummary.setText(alwaysDisplayExpressionRule != null?"Always displayed (set)":"Always displayed (unset)");
+  }
+
   private void toggleFocusOnState()
   {
     setFocusOnSelected(!isFocusOnSelected());
@@ -1493,6 +1568,19 @@
         updateIgnoreExpression(hiddenExpression);
     }
 
+    public String getAlwaysDisplayExpression() {
+        String text = alwaysDisplayExpressionEntryField.getText();
+        if (text == null || text.trim().equals("")) {
+            return null;
+        }
+        return text.trim();
+    }
+
+    public void setAlwaysDisplayExpression(String alwaysDisplayExpression) {
+        alwaysDisplayExpressionEntryField.setText(alwaysDisplayExpression);
+        updateAlwaysDisplayExpression(alwaysDisplayExpression);
+    }
+
     public void loggerNameAdded(String loggerName)
     {
         //no-op
@@ -1810,7 +1898,8 @@
           String currentlySelectedLoggerName = getCurrentlySelectedLoggerName();
           boolean hiddenLogger = e.getLoggerName() != null && isHiddenLogger(e.getLoggerName());
           boolean hiddenExpression = (ignoreExpressionRule != null && ignoreExpressionRule.evaluate(e, null));
-          boolean hidden = hiddenLogger || hiddenExpression;
+          boolean alwaysDisplayExpression = (alwaysDisplayExpressionRule != null && alwaysDisplayExpressionRule.evaluate(e, null));
+          boolean hidden = (!alwaysDisplayExpression) && (hiddenLogger || hiddenExpression);
           if (currentlySelectedLoggerName == null) {
           	//if there is no selected logger, pass if not hidden
           	return !hidden;
diff --git a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
index cf5e053..98e26df 100644
--- a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
+++ b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
@@ -10,6 +10,10 @@
 <b>NOTE:</b> The mechanism and format used to persist settings in Chainsaw is subject to change.  If you are experiencing problems displaying events in Chainsaw, please delete everything in the $user.dir/.chainsaw directory and restart Chainsaw.
 <br>
 <h1>2.1</h1>
+<h2>2 Oct 2011</h2>
+<ul>
+<li>Added persistent "always display" expression support (button below the logger tree).  The 'always display' expression overrides hidden loggers and the hidden expression but not the refine focus filtering mechanism.  Often used with expressions like 'exception exists || level > warn' to ensure errors and exceptions are not filtered out due to the hidden expression or hidden logger mechanism. </li>
+</ul>
 <h2>11 Nov 2010</h2>
 <ul>
 <li>Added per-tab preference to not use a search match color in the primary table</li>