You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2017/05/30 23:48:58 UTC

[21/50] [abbrv] logging-chainsaw git commit: Added multi-select capability to event table and 'copy selection' context menu item (multi-select by holding down alt key while selecting rows) New 'add to find field' context menu option Simplified context me

Added multi-select capability to event table and 'copy selection' context menu item (multi-select by holding down alt key while selecting rows)
New 'add to find field' context menu option
Simplified context menu (removed scroll to bottom/top, moved dock option to bottom)
Updated logic determining value for a specific cell


Project: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/commit/9f01847a
Tree: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/tree/9f01847a
Diff: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/diff/9f01847a

Branch: refs/heads/master
Commit: 9f01847ae19f6ae850b6c7f4baf5a71c9dcd8361
Parents: cf258ed
Author: Scott Deboy <sd...@apache.org>
Authored: Sat Nov 6 23:42:31 2010 +0000
Committer: Scott Deboy <sd...@apache.org>
Committed: Sat Nov 6 23:42:31 2010 +0000

----------------------------------------------------------------------
 .../org/apache/log4j/chainsaw/LogPanel.java     | 399 ++++++++++---------
 .../log4j/chainsaw/LoggerNameTreePanel.java     |  55 ++-
 .../log4j/chainsaw/TableColorizingRenderer.java |   5 +-
 .../apache/log4j/chainsaw/color/ColorPanel.java |  16 +-
 .../log4j/chainsaw/help/release-notes.html      |   1 +
 5 files changed, 267 insertions(+), 209 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/9f01847a/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/chainsaw/LogPanel.java b/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
index edeec44..70d54da 100644
--- a/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
+++ b/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
@@ -40,6 +40,7 @@ import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionAdapter;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
@@ -57,9 +58,9 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.text.DateFormat;
 import java.text.NumberFormat;
-import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.Enumeration;
 import java.util.EventObject;
 import java.util.HashMap;
@@ -79,7 +80,6 @@ import javax.swing.Box;
 import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
 import javax.swing.ComboBoxEditor;
-import javax.swing.ComboBoxModel;
 import javax.swing.ImageIcon;
 import javax.swing.JButton;
 import javax.swing.JCheckBoxMenuItem;
@@ -206,6 +206,7 @@ import com.thoughtworks.xstream.io.xml.DomDriver;
  *
  */
 public class LogPanel extends DockablePanel implements EventBatchListener, Profileable {
+  private static final DateFormat TIMESTAMP_DATE_FORMAT = new SimpleDateFormat(Constants.TIMESTAMP_RULE_FORMAT);
   private static final double DEFAULT_DETAIL_SPLIT_LOCATION = 0.71d;
   private static final double DEFAULT_LOG_TREE_SPLIT_LOCATION = 0.2d;
   private final String identifier;
@@ -247,7 +248,6 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
   static final String COLORS_EXTENSION = ".colors";
   private static final int LOG_PANEL_SERIALIZATION_VERSION_NUMBER = 2; //increment when format changes
   private int previousLastIndex = -1;
-  private final DateFormat timestampExpressionFormat = new SimpleDateFormat(Constants.TIMESTAMP_RULE_FORMAT);
   private final Logger logger = LogManager.getLogger(LogPanel.class);
   private AutoFilterComboBox filterCombo;
   private AutoFilterComboBox findCombo;
@@ -408,27 +408,6 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
       });
     menuItemLoggerTree.setIcon(new ImageIcon(ChainsawIcons.WINDOW_ICON));
 
-    final JMenuItem menuItemScrollToTop = new JMenuItem("Scroll to top");
-    menuItemScrollToTop.addActionListener(
-      new ActionListener() {
-          public void actionPerformed(ActionEvent evt)
-          {
-              scrollToTop();
-          }
-      });
-    final JCheckBoxMenuItem menuItemScrollBottom =
-      new JCheckBoxMenuItem("Scroll to bottom");
-    menuItemScrollBottom.addActionListener(
-      new ActionListener() {
-        public void actionPerformed(ActionEvent evt) {
-          preferenceModel.setScrollToBottom(menuItemScrollBottom.isSelected());
-        }
-      });
-    menuItemScrollBottom.setSelected(isScrollToBottom());
-
-    menuItemScrollBottom.setIcon(
-      new ImageIcon(ChainsawIcons.SCROLL_TO_BOTTOM));
-
     final JCheckBoxMenuItem menuItemToggleDetails =
       new JCheckBoxMenuItem("Show Detail Pane");
     menuItemToggleDetails.addActionListener(
@@ -644,18 +623,6 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
       });
 
     preferenceModel.addPropertyChangeListener(
-      "scrollToBottom",
-      new PropertyChangeListener() {
-        public void propertyChange(PropertyChangeEvent evt) {
-          boolean value = ((Boolean) evt.getNewValue()).booleanValue();
-          menuItemScrollBottom.setSelected(value);
-          if (value) {
-            scrollToBottom();
-          }
-        }
-      });
-
-    preferenceModel.addPropertyChangeListener(
       "detailPaneVisible",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
@@ -1562,26 +1529,7 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
             int column = currentTable.columnAtPoint(currentPoint);
             int row = currentTable.rowAtPoint(currentPoint);
             String colName = currentTable.getColumnName(column).toUpperCase();
-            String value = "";
-
-            if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
-              try {
-                value = timestampExpressionFormat.parse(currentTable.getValueAt(row, column).toString()).toString();
-              } catch (ParseException e) {
-                e.printStackTrace();
-              }
-            } else {
-              Object o = table.getValueAt(row, column);
-
-              if (o != null) {
-                if (o instanceof String[] && ((String[])o).length > 0) {
-                  value = ((String[]) o)[0];
-                  operator = "~=";
-                } else {
-                  value = o.toString();
-                }
-              }
-            }
+            String value = getValueOf(row, column);
 
             if (columnNameKeywordMap.containsKey(colName)) {
               filterText.setText(
@@ -1604,32 +1552,41 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
             String operator = "==";
             int column = currentTable.columnAtPoint(currentPoint);
             int row = currentTable.rowAtPoint(currentPoint);
+            String value = getValueOf(row, column);
             String colName = currentTable.getColumnName(column).toUpperCase();
-            String value = "";
 
-            if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
-              JComponent comp =
-                (JComponent) currentTable.getCellRenderer(row, column);
+            if (columnNameKeywordMap.containsKey(colName)) {
+              filterText.setText(
+                filterText.getText() + " && "
+                + columnNameKeywordMap.get(colName).toString() + " "
+                + operator + " '" + value + "'");
+            }
 
-              if (comp instanceof JLabel) {
-                value = ((JLabel) comp).getText();
-              }
-            } else {
-              Object o = currentTable.getValueAt(row, column);
+          }
+        }
+      });
+      }
+    }
 
-              if (o instanceof String[] && ((String[])o).length > 0) {
-                value = ((String[]) o)[0];
-                operator = "~=";
-              } else {
-                value = o.toString();
-              }
-            }
+        class DefineAddCustomFind extends JMenuItem {
+      public DefineAddCustomFind() {
+        super("Add value under pointer to 'find' field");
+  addActionListener(
+      new ActionListener() {
+        public void actionPerformed(ActionEvent evt) {
+          if (currentPoint != null) {
+            String operator = "==";
+            int column = currentTable.columnAtPoint(currentPoint);
+            int row = currentTable.rowAtPoint(currentPoint);
+            String value = getValueOf(row, column);
+            String colName = currentTable.getColumnName(column).toUpperCase();
 
             if (columnNameKeywordMap.containsKey(colName)) {
-              filterText.setText(
-                filterText.getText() + " && "
+              findCombo.setSelectedItem(
+                findText.getText() + " && "
                 + columnNameKeywordMap.get(colName).toString() + " "
                 + operator + " '" + value + "'");
+              findNext();
             }
           }
         }
@@ -1648,24 +1605,7 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
             int column = currentTable.columnAtPoint(currentPoint);
             int row = currentTable.rowAtPoint(currentPoint);
             String colName = currentTable.getColumnName(column).toUpperCase();
-            String value = "";
-
-            if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
-              JComponent comp =
-                (JComponent) currentTable.getCellRenderer(row, column);
-
-              if (comp instanceof JLabel) {
-                value = ((JLabel) comp).getText();
-              }
-            } else {
-              Object o = currentTable.getValueAt(row, column);
-
-              if (o instanceof String[] && ((String[])o).length > 0) {
-                value = ((String[]) o)[0];
-              } else {
-                value = o.toString();
-              }
-            }
+            String value = getValueOf(row, column);
 
             if (columnNameKeywordMap.containsKey(colName)) {
                 Color c = JColorChooser.showDialog(getRootPane(), "Choose a color", Color.red);
@@ -1695,49 +1635,49 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
         }
       }
 
-    class Copy extends AbstractAction {
-      public Copy() {
+    class CopySelection extends AbstractAction {
+      public CopySelection() {
+        super("Copy selection to clipboard");
+      }
+
+        public void actionPerformed(ActionEvent e) {
+          if (currentTable == null) {
+            return;
+          }
+          int start = currentTable.getSelectionModel().getMinSelectionIndex();
+          int end = currentTable.getSelectionModel().getMaxSelectionIndex();
+          StringBuffer result = new StringBuffer();
+          for (int row=start;row<end+1;row++) {
+            for (int column=0;column<currentTable.getColumnCount();column++) {
+              result.append(getValueOf(row, column));
+              if (column != (currentTable.getColumnCount() - 1)) {
+                result.append(" - ");
+              }
+            }
+            result.append(System.getProperty("line.separator"));
+          }
+          StringSelection selection = new StringSelection(result.toString());
+          Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+          clipboard.setContents(selection, null);
+      }
+    }
+
+    class CopyField extends AbstractAction {
+      public CopyField() {
         super("Copy value under pointer to clipboard");
       }
 
         public void actionPerformed(ActionEvent e) {
-          if (currentPoint != null) {
+          if (currentPoint != null && currentTable != null) {
             int column = currentTable.columnAtPoint(currentPoint);
             int row = currentTable.rowAtPoint(currentPoint);
-            String colName = currentTable.getColumnName(column).toUpperCase();
-            String value = "";
-
-            if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
-              JComponent comp =
-                (JComponent) currentTable.getCellRenderer(row, column);
-
-              if (comp instanceof JLabel) {
-                value = ((JLabel) comp).getText();
-              }
-            } else {
-              Object o = currentTable.getValueAt(row, column);
-              //exception - build message + throwable
-              if (o != null) {
-                  if (o instanceof String[]) {
-                      String[] ti = (String[])o;
-                      if (ti.length > 0 && (!(ti.length == 1 && ti[0].equals("")))) {
-                        LoggingEventWrapper loggingEventWrapper = ((ChainsawCyclicBufferTableModel)(currentTable.getModel())).getRow(row);
-                        value = loggingEventWrapper.getLoggingEvent().getMessage().toString();
-                        for (int i=0;i<((String[])o).length;i++) {
-                            value = value + "\n" + ((String[]) o)[i];
-                        }
-                      }
-                  } else {
-                    value = o.toString();
-                  }
-              }
-            }
+            String value = getValueOf(row, column);
             StringSelection selection = new StringSelection(value);
             Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
             clipboard.setContents(selection, null);
         }
       }
-      }
+    }
     final JMenuItem menuItemToggleDock = new JMenuItem("Undock/dock");
 
     dockingAction =
@@ -1769,36 +1709,17 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
 
     class Search extends JMenuItem {
       public Search() {
-        super("Search for value under pointer");
+        super("Find value under pointer");
 
     addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           if (currentPoint != null) {
-            String operator = "~=";
+            String operator = "==";
             int column = currentTable.columnAtPoint(currentPoint);
             int row = currentTable.rowAtPoint(currentPoint);
             String colName = currentTable.getColumnName(column).toUpperCase();
-            String value = "";
-
-            if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
-              try {
-                value = timestampExpressionFormat.parse(currentTable.getValueAt(row, column).toString()).toString();
-              } catch (ParseException e) {
-                e.printStackTrace();
-              }
-            } else {
-              Object o = currentTable.getValueAt(row, column);
-
-              if (o != null) {
-                if (o instanceof String[] && ((String[])o).length > 0) {
-                  value = ((String[]) o)[0];
-                } else {
-                  value = o.toString();
-                }
-              }
-            }
-
+            String value = getValueOf(row, column);
             if (columnNameKeywordMap.containsKey(colName)) {
               findCombo.setSelectedItem(
                 columnNameKeywordMap.get(colName).toString() + " " + operator
@@ -1823,18 +1744,14 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
 
     mainPopup.add(new Search());
     searchPopup.add(new Search());
+    mainPopup.add(new DefineAddCustomFind());
+    searchPopup.add(new DefineAddCustomFind());
     mainPopup.add(new ClearSearch());
     searchPopup.add(new ClearSearch());
 
     mainPopup.add(new JSeparator());
     searchPopup.add(new JSeparator());
 
-    mainPopup.add(new BestFit());
-    searchPopup.add(new BestFit());
-
-    mainPopup.add(new JSeparator());
-    searchPopup.add(new JSeparator());
-
     class DisplayNormalTimes extends JMenuItem {
       public DisplayNormalTimes() {
         super("Hide relative times");
@@ -1900,8 +1817,12 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
 
     mainPopup.add(new BuildColorRule());
     searchPopup.add(new BuildColorRule());
-    mainPopup.add(new Copy());
-    searchPopup.add(new Copy());
+    mainPopup.add(new JSeparator());
+    searchPopup.add(new JSeparator());
+    mainPopup.add(new CopyField());
+    mainPopup.add(new CopySelection());
+    searchPopup.add(new CopyField());
+    searchPopup.add(new CopySelection());
     mainPopup.add(new JSeparator());
     searchPopup.add(new JSeparator());
 
@@ -1913,14 +1834,12 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
     searchPopup.add(searchToggleToolTips);
 
     mainPopup.add(new JSeparator());
-    searchPopup.add(new JSeparator());
-
-    mainPopup.add(menuItemScrollToTop);
-    mainPopup.add(menuItemScrollBottom);
-    mainPopup.add(new JSeparator());
 
     mainPopup.add(menuItemToggleDock);
 
+    mainPopup.add(new BestFit());
+    searchPopup.add(new BestFit());
+
     mainPopup.add(new JSeparator());
 
     mainPopup.add(new ColorPanel());
@@ -1932,11 +1851,108 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
     eventsPane.addMouseListener(mainTablePopupListener);
     table.addMouseListener(mainTablePopupListener);
 
+    table.addMouseListener(new MouseListener(){
+      public void mouseClicked(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mousePressed(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mouseReleased(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mouseEntered(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mouseExited(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      private void checkMultiSelect(MouseEvent mouseEvent) {
+        if (mouseEvent.isAltDown()) {
+          table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+        } else {
+          table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        }
+      }
+    });
+
+
+    searchTable.addMouseListener(new MouseListener(){
+      public void mouseClicked(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mousePressed(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mouseReleased(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mouseEntered(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      public void mouseExited(MouseEvent mouseEvent) {
+        checkMultiSelect(mouseEvent);
+      }
+
+      private void checkMultiSelect(MouseEvent mouseEvent) {
+        if (mouseEvent.isAltDown()) {
+          searchTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+        } else {
+          searchTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        }
+      }
+    });
+
+
     final PopupListener searchTablePopupListener = new PopupListener(searchPopup);
     searchPane.addMouseListener(searchTablePopupListener);
     searchTable.addMouseListener(searchTablePopupListener);
   }
 
+       private String getValueOf(int row, int column) {
+         if (currentTable == null) {
+           return "";
+         }
+
+         Object o = currentTable.getValueAt(row, column);
+
+         if (o instanceof Date) {
+           return TIMESTAMP_DATE_FORMAT.format((Date)o);
+         }
+
+         if (o instanceof String) {
+           return (String)o;
+         }
+
+         if (o instanceof Level) {
+           return o.toString();
+         }
+
+         if (o instanceof String[]) {
+           String value = "";
+          //exception - build message + throwable
+          String[] ti = (String[])o;
+            if (ti.length > 0 && (!(ti.length == 1 && ti[0].equals("")))) {
+              LoggingEventWrapper loggingEventWrapper = ((ChainsawCyclicBufferTableModel)(currentTable.getModel())).getRow(row);
+              value = loggingEventWrapper.getLoggingEvent().getMessage().toString();
+              for (int i=0;i<((String[])o).length;i++) {
+                  value = value + "\n" + ((String[]) o)[i];
+              }
+            }
+           return value;
+         }
+         return "";
+      }
+
     private Action getFindNextAction() {
     final Action action =
       new AbstractAction("Find next") {
@@ -1988,7 +2004,7 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
     if (isFiltering) {
       filterText.getDocument().addDocumentListener(new DelayedTextDocumentListener(filterText));
     }
-    filterText.setToolTipText("Enter an expression, press enter to add to list");
+    filterText.setToolTipText("Enter an expression - right click or ctrl-space for menu - press enter to add to list");
     filterText.addKeyListener(new ExpressionRuleContext(filterModel, filterText));
 
     if (combo.getEditor().getEditorComponent() instanceof JTextField) {
@@ -2604,7 +2620,7 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
       //reset background color in case we were previously an invalid expression
       findCombo.setBackground(UIManager.getColor("TextField.background"));
       findCombo.setToolTipText(
-        "Enter expression - right click or ctrl-space for menu");
+        "Enter an expression - right click or ctrl-space for menu - press enter to add to list");
       currentSearchMatchCount = 0;
       currentFindRuleText = null;
       statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
@@ -2621,8 +2637,9 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
       }
       currentFindRuleText = ruleText;
       try {
-        findCombo.setToolTipText(
-          "Enter expression - right click or ctrl-space for menu");
+        final JTextField findText =(JTextField) findCombo.getEditor().getEditorComponent();
+        findText.setToolTipText(
+          "Enter an expression - right click or ctrl-space for menu - press enter to add to list");
         findRule = ExpressionRule.getRule(ruleText);
         currentSearchMatchCount = tableModel.updateEventsWithFindRule(findRule);
         searchModel.updateEventsWithFindRule(findRule);
@@ -2630,15 +2647,16 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
         tableRuleMediator.setFindRule(findRule);
         searchRuleMediator.setFindRule(findRule);
         //valid expression, reset background color in case we were previously an invalid expression
-        findCombo.setBackground(UIManager.getColor("TextField.background"));
+        findText.setBackground(UIManager.getColor("TextField.background"));
         statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
         if (isSearchResultsVisible()) {
           showSearchResults();
         }
       } catch (IllegalArgumentException re) {
         findRule = null;
-        findCombo.setToolTipText(re.getMessage());
-        findCombo.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
+        final JTextField findText =(JTextField) findCombo.getEditor().getEditorComponent();
+        findText.setToolTipText(re.getMessage());
+        findText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
         colorizer.setFindRule(null);
         tableRuleMediator.setFindRule(null);
         searchRuleMediator.setFindRule(null);
@@ -3012,6 +3030,7 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
     if (findRule != null) {
         EventQueue.invokeLater(new Runnable() {
             public void run() {
+              final JTextField findText =(JTextField) findCombo.getEditor().getEditorComponent();
                 try {
                   int filteredEventsSize = getFilteredEvents().size();
                   int startRow = table.getSelectedRow() + 1;
@@ -3023,10 +3042,12 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
 
                   if (nextRow > -1) {
                     table.scrollToRow(nextRow);
-                    findCombo.setToolTipText("Enter an expression");
+                    findText.setToolTipText("Enter an expression - right click or ctrl-space for menu - press enter to add to list");
                   }
+                  findText.setBackground(UIManager.getColor("TextField.background"));
                 } catch (IllegalArgumentException iae) {
-                  findCombo.setToolTipText(iae.getMessage());
+                  findText.setToolTipText(iae.getMessage());
+                  findText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
                   colorizer.setFindRule(null);
                   tableRuleMediator.setFindRule(null);
                   searchRuleMediator.setFindRule(null);
@@ -3048,6 +3069,7 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
     if (findRule != null) {
         EventQueue.invokeLater(new Runnable() {
             public void run() {
+              final JTextField findText =(JTextField) findCombo.getEditor().getEditorComponent();
                 try {
                     int startRow = table.getSelectedRow() - 1;
                     int filteredEventsSize = getFilteredEvents().size();
@@ -3058,10 +3080,12 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
 
                     if (previousRow > -1) {
                         table.scrollToRow(previousRow);
-                        findCombo.setToolTipText("Enter an expression");
+                        findCombo.setToolTipText("Enter an expression - right click or ctrl-space for menu - press enter to add to list");
                     }
+                  findText.setBackground(UIManager.getColor("TextField.background"));
                 } catch (IllegalArgumentException iae) {
-                  findCombo.setToolTipText(iae.getMessage());
+                  findText.setToolTipText(iae.getMessage());
+                  findText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
                 }
             }
         });
@@ -3296,6 +3320,19 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
         }
     }
 
+  public void setFindText(String findText) {
+    findCombo.setSelectedItem(findText);
+    findNext();
+  }
+
+  public String getFindText() {
+    Object selectedItem = findCombo.getSelectedItem();
+    if (selectedItem == null) {
+      return "";
+    }
+    return selectedItem.toString();
+  }
+
   /**
    * This class receives notification when the Refine focus or find field is
    * updated, where a background thread periodically wakes up and checks if
@@ -3419,28 +3456,6 @@ public class LogPanel extends DockablePanel implements EventBatchListener, Profi
         }
       }
     }
-
-    private void setFind() {
-      if (textField.getText().trim().equals("")) {
-        //reset background color in case we were previously an invalid expression
-        textField.setBackground(UIManager.getColor("TextField.background"));
-        tableRuleMediator.setFindRule(null);
-        searchRuleMediator.setFindRule(null);
-        textField.setToolTipText(defaultToolTip);
-      } else {
-        try {
-          tableRuleMediator.setFindRule(ExpressionRule.getRule(textField.getText()));
-          searchRuleMediator.setFindRule(ExpressionRule.getRule(textField.getText()));
-          textField.setToolTipText(defaultToolTip);
-          //valid expression, reset background color in case we were previously an invalid expression
-          textField.setBackground(UIManager.getColor("TextField.background"));
-        } catch (IllegalArgumentException iae) {
-          //invalid expression, change background of the field
-          textField.setToolTipText(iae.getMessage());
-          textField.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
-        }
-      }
-    }
   }
 
   private final class TableMarkerListener extends MouseAdapter {

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/9f01847a/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java b/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
index f5ac429..ff01eed 100644
--- a/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
+++ b/src/main/java/org/apache/log4j/chainsaw/LoggerNameTreePanel.java
@@ -24,7 +24,6 @@ import java.awt.Component;
 import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.EventQueue;
-import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.Point;
 import java.awt.Toolkit;
@@ -120,11 +119,12 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
   private final Action editLoggerAction;
   private final JButton editLoggerButton = new SmallButton();
   private final Action expandAction;
-  private final Action findNextAction;
+  private final Action findAction;
   private final Action clearFindNextAction;
   private final Action defineColorRuleForLoggerAction;
   private final Action setRefineFocusAction;
   private final Action updateRefineFocusAction;
+  private final Action updateFindAction;
   private final JButton expandButton = new SmallButton();
   private final Action focusOnAction;
   private final Action clearRefineFocusAction;
@@ -270,12 +270,13 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
     toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
 
     expandAction = createExpandAction();
-    findNextAction = createFindNextAction();
+    findAction = createFindNextAction();
     clearFindNextAction = createClearFindNextAction();
     defineColorRuleForLoggerAction = createDefineColorRuleForLoggerAction();
     clearRefineFocusAction = createClearRefineFocusAction();
     setRefineFocusAction = createSetRefineFocusAction();
     updateRefineFocusAction = createUpdateRefineFocusAction();
+    updateFindAction = createUpdateFindAction();
     editLoggerAction = createEditLoggerAction();
     closeAction = createCloseAction();
     collapseAction = createCollapseAction();
@@ -873,6 +874,38 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
       return action;
     }
 
+  private Action createUpdateFindAction()
+  {
+    Action action = new AbstractAction()
+      {
+        public void actionPerformed(ActionEvent e)
+        {
+          updateFindUsingCurrentlySelectedNode();
+        }
+      };
+
+    action.putValue(Action.NAME, "Update 'find' to include selected logger");
+    action.putValue(
+      Action.SHORT_DESCRIPTION,
+      "Add selected node to 'find' field");
+    action.setEnabled(false);
+
+    return action;
+  }
+
+  private void updateFindUsingCurrentlySelectedNode()
+  {
+      String selectedLogger = getCurrentlySelectedLoggerName();
+      TreePath[] paths = logTree.getSelectionPaths();
+
+      if (paths == null)
+      {
+        return;
+      }
+      String currentFindText = logPanel.getFindText();
+      logPanel.setFindText(currentFindText + " || logger ~= " + selectedLogger);
+  }
+
     private void updateRefineFocusUsingCurrentlySelectedNode()
     {
         String selectedLogger = getCurrentlySelectedLoggerName();
@@ -1116,12 +1149,12 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
       {
         return;
       }
-      visibilityRuleDelegate.firePropertyChange("searchExpression", null, "logger like '^" + selectedLogger + ".*'");
+      logPanel.setFindText("logger like '^" + selectedLogger + ".*'");
   }
 
   private void clearFindNext()
   {
-      visibilityRuleDelegate.firePropertyChange("searchExpression", null, "");
+      logPanel.setFindText("");
   }
 
   private void clearRefineFocus()
@@ -1238,18 +1271,20 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
     {
       focusOnAction.putValue(Action.NAME, "Focus On...");
       hideAction.putValue(Action.NAME, "Ignore...");
-      findNextAction.putValue(Action.NAME, "Find...");
+      findAction.putValue(Action.NAME, "Find...");
       setRefineFocusAction.putValue(Action.NAME, "Set refine focus field");
       updateRefineFocusAction.putValue(Action.NAME, "Add to refine focus field");
+      updateFindAction.putValue(Action.NAME, "Add to find field");
       defineColorRuleForLoggerAction.putValue(Action.NAME, "Define color rule");
     }
     else
     {
       focusOnAction.putValue(Action.NAME, "Focus On '" + logger + "'");
       hideAction.putValue(Action.NAME, "Ignore '" + logger + "'");
-      findNextAction.putValue(Action.NAME, "Find '" + logger + "'");
+      findAction.putValue(Action.NAME, "Find '" + logger + "'");
       setRefineFocusAction.putValue(Action.NAME, "Set refine focus field to '" + logger + "'");
       updateRefineFocusAction.putValue(Action.NAME, "Add '" + logger + "' to 'refine focus' field");
+      updateFindAction.putValue(Action.NAME, "Add '" + logger + "' to 'find' field");
       defineColorRuleForLoggerAction.putValue(Action.NAME, "Define color rule for '" + logger + "'");
     }
 
@@ -1298,11 +1333,12 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
           }
 
           expandAction.setEnabled(path != null);
-          findNextAction.setEnabled(path != null);
+          findAction.setEnabled(path != null);
           clearFindNextAction.setEnabled(true);
           defineColorRuleForLoggerAction.setEnabled(path != null);
           setRefineFocusAction.setEnabled(path != null);
           updateRefineFocusAction.setEnabled(path != null);
+          updateFindAction.setEnabled(path != null);
           clearRefineFocusAction.setEnabled(true);
 
           if (currentlySelectedLoggerName != null)
@@ -1632,7 +1668,8 @@ final class LoggerNameTreePanel extends JPanel implements LoggerNameListener
       add(updateRefineFocusAction);
       add(clearRefineFocusAction);
       addSeparator();
-      add(findNextAction);
+      add(findAction);
+      add(updateFindAction);
       add(clearFindNextAction);
 
       addSeparator();

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/9f01847a/src/main/java/org/apache/log4j/chainsaw/TableColorizingRenderer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/chainsaw/TableColorizingRenderer.java b/src/main/java/org/apache/log4j/chainsaw/TableColorizingRenderer.java
index 209204d..983db9a 100644
--- a/src/main/java/org/apache/log4j/chainsaw/TableColorizingRenderer.java
+++ b/src/main/java/org/apache/log4j/chainsaw/TableColorizingRenderer.java
@@ -179,7 +179,7 @@ public class TableColorizingRenderer extends DefaultTableCellRenderer {
     int row, int col) {
     EventContainer container = (EventContainer) table.getModel();
     LoggingEventWrapper loggingEventWrapper = container.getRow(row);
-    value = formatField(value, row, loggingEventWrapper);
+    value = formatField(value, loggingEventWrapper);
     TableColumn tableColumn = table.getColumnModel().getColumn(col);
     int width = tableColumn.getWidth();
     JLabel label = (JLabel)super.getTableCellRendererComponent(table, value,
@@ -569,10 +569,9 @@ public class TableColorizingRenderer extends DefaultTableCellRenderer {
    *
    * @param field object
    *
-   * @param renderingRow
    * @return formatted object
    */
-  private Object formatField(Object field, int renderingRow, LoggingEventWrapper loggingEventWrapper) {
+  private Object formatField(Object field, LoggingEventWrapper loggingEventWrapper) {
     if (!(field instanceof Date)) {
       return (field == null ? "" : field);
     }

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/9f01847a/src/main/java/org/apache/log4j/chainsaw/color/ColorPanel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/chainsaw/color/ColorPanel.java b/src/main/java/org/apache/log4j/chainsaw/color/ColorPanel.java
index 6234680..dcc4621 100644
--- a/src/main/java/org/apache/log4j/chainsaw/color/ColorPanel.java
+++ b/src/main/java/org/apache/log4j/chainsaw/color/ColorPanel.java
@@ -265,16 +265,22 @@ public class ColorPanel extends JPanel
         public void actionPerformed(ActionEvent e)
           {
               tableModel.getDataVector().clear();
-              RuleColorizer sourceColorizer = (RuleColorizer) allLogPanelColorizers.get(loadPanelColorizersComboBox.getSelectedItem().toString());
-              colorizer.setRules(sourceColorizer.getRules());
-              updateColors();
+              Object selectedItem = loadPanelColorizersComboBox.getSelectedItem();
+              if (selectedItem != null) {
+                RuleColorizer sourceColorizer = (RuleColorizer) allLogPanelColorizers.get(selectedItem.toString());
+                colorizer.setRules(sourceColorizer.getRules());
+                updateColors();
+              }
           }
       };
         
       loadPanelColorizersComboBox.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
-              String selectedColorizerName = loadPanelColorizersComboBox.getSelectedItem().toString();
-              copyRulesAction.setEnabled(!(noTab.equals(selectedColorizerName)));
+              Object selectedItem = loadPanelColorizersComboBox.getSelectedItem();
+              if (selectedItem != null) {
+                String selectedColorizerName = selectedItem.toString();
+                copyRulesAction.setEnabled(!(noTab.equals(selectedColorizerName)));
+              }
           }
       });
 

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/9f01847a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
----------------------------------------------------------------------
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 59a0fb5..c9799bc 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
@@ -13,6 +13,7 @@
 <h2>6 Nov 2010</h2>
 <ul>
 <li>Log file receiver configurations can now be loaded from both log4j.xml and log4j.properties configuration fileappender entries</li>
+<li>Added multi-select capability to event table and 'copy selection' context menu item (multi-select by holding down alt key while selecting rows)</li>
 </ul>
 <h2>5 Nov 2010</h2>
 <ul>