svn commit: r1022406 [2/3] - in /logging/chainsaw/trunk/src/main: java/org/apache/log4j/chainsaw/ java/org/apache/log4j/chainsaw/messages/ java/org/apache/log4j/chainsaw/vfs/ resources/org/apache/log4j/chainsaw/help/ resources/org/apache/log4j/chainsaw...

[email protected]
Newsgroups gmane.comp.apache.logging
Message-ID <[email protected]>
Modified: logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java?rev=1022406&r1=1022405&r2=1022406&view=diff
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java (original)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java Thu Oct 14 08:01:36 2010
@@ -230,11 +230,13 @@ public class LogPanel extends DockablePa
   private final LogPanelPreferencePanel logPanelPreferencesPanel = new LogPanelPreferencePanel(preferenceModel);
   private final FilterModel filterModel = new FilterModel();
   private final RuleColorizer colorizer = new RuleColorizer();
-  private final RuleMediator ruleMediator = new RuleMediator();
+  private final RuleMediator tableRuleMediator = new RuleMediator(false);
+  private final RuleMediator searchRuleMediator = new RuleMediator(true);
   private final EventDetailLayout detailLayout = new EventDetailLayout();
   private double lastDetailPanelSplitLocation = DEFAULT_DETAIL_SPLIT_LOCATION;
   private double lastLogTreePanelSplitLocation = DEFAULT_LOG_TREE_SPLIT_LOCATION;
   private Point currentPoint;
+  private JTable currentTable;
   private boolean paused = false;
   private Rule findRule;
   private String currentFindRuleText;
@@ -249,15 +251,25 @@ public class LogPanel extends DockablePa
   private int previousLastIndex = -1;
   private final DateFormat timestampExpressionFormat = new SimpleDateFormat(Constants.TIMESTAMP_RULE_FORMAT);
   private final Logger logger = LogManager.getLogger(LogPanel.class);
-  private TableCellEditor markerCellEditor;
   private AutoFilterComboBox filterCombo;
   private JScrollPane eventsPane;
   private int currentSearchMatchCount;
   private ApplicationPreferenceModel applicationPreferenceModel;
   private Rule clearTableExpressionRule;
   private int lowerPanelDividerLocation;
+  private EventContainer searchModel;
+  private JSortTable searchTable;
+  private TableColorizingRenderer searchRenderer;
+  private ToggleToolTips mainToggleToolTips;
+  private ToggleToolTips searchToggleToolTips;
+  private JScrollPane detailPane;
+  private JScrollPane searchPane;
+  //only one tableCellEditor, shared by both tables
+  private TableCellEditor markerCellEditor;
+  private JToolBar detailToolbar;
+  private boolean searchResultsDisplayed;
 
-    /**
+  /**
    * Creates a new LogPanel object.  If a LogPanel with this identifier has
    * been loaded previously, reload settings saved on last exit.
    *
@@ -378,16 +390,6 @@ public class LogPanel extends DockablePa
     dateFormatChangePopup.add(isoButton);
     dateFormatChangePopup.add(simpleTimeButton);
 
-    final JCheckBoxMenuItem menuItemToggleToolTips =
-      new JCheckBoxMenuItem("Show ToolTips");
-    menuItemToggleToolTips.addActionListener(
-      new ActionListener() {
-        public void actionPerformed(ActionEvent evt) {
-          preferenceModel.setToolTips(menuItemToggleToolTips.isSelected());
-        }
-      });
-    menuItemToggleToolTips.setIcon(new ImageIcon(ChainsawIcons.TOOL_TIP));
-
     final JCheckBoxMenuItem menuItemLoggerTree =
       new JCheckBoxMenuItem("Show Logger Tree");
     menuItemLoggerTree.addActionListener(
@@ -435,36 +437,51 @@ public class LogPanel extends DockablePa
     /*
      * add preferencemodel listeners
      */
-    preferenceModel.addPropertyChangeListener(
-      "levelIcons",
+    preferenceModel.addPropertyChangeListener("levelIcons",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
-          renderer.setLevelUseIcons(
-            ((Boolean) evt.getNewValue()).booleanValue());
+          boolean useIcons = ((Boolean) evt.getNewValue()).booleanValue();
+          renderer.setLevelUseIcons(useIcons);
           table.tableChanged(new TableModelEvent(tableModel));
+          searchRenderer.setLevelUseIcons(useIcons);
+          searchTable.tableChanged(new TableModelEvent(searchModel));
         }
       });
 
     /*
      * add preferencemodel listeners
      */
-    preferenceModel.addPropertyChangeListener(
-      "wrapMessage",
+    preferenceModel.addPropertyChangeListener("wrapMessage",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
-          renderer.setWrapMessage(
-            ((Boolean) evt.getNewValue()).booleanValue());
+          boolean wrap = ((Boolean) evt.getNewValue()).booleanValue();
+          renderer.setWrapMessage(wrap);
           table.tableChanged(new TableModelEvent(tableModel));
+          searchRenderer.setWrapMessage(wrap);
+          searchTable.tableChanged(new TableModelEvent(searchModel));
         }
       });
 
-      preferenceModel.addPropertyChangeListener(
-        "highlightSearchMatchText",
+    preferenceModel.addPropertyChangeListener("searchResultsVisible",
+      new PropertyChangeListener() {
+        public void propertyChange(PropertyChangeEvent evt) {
+          boolean displaySearchResultsInDetailsIfAvailable = ((Boolean) evt.getNewValue()).booleanValue();
+          if (displaySearchResultsInDetailsIfAvailable) {
+            showSearchResults();
+          } else {
+            hideSearchResults();
+          }
+        }
+      });
+
+      preferenceModel.addPropertyChangeListener("highlightSearchMatchText",
         new PropertyChangeListener() {
           public void propertyChange(PropertyChangeEvent evt) {
-            renderer.setHighlightSearchMatchText(
-              ((Boolean) evt.getNewValue()).booleanValue());
+            boolean highlightText = ((Boolean) evt.getNewValue()).booleanValue();
+            renderer.setHighlightSearchMatchText(highlightText);
             table.tableChanged(new TableModelEvent(tableModel));
+            searchRenderer.setHighlightSearchMatchText(highlightText);
+            searchTable.tableChanged(new TableModelEvent(searchModel));
           }
         });
 
@@ -472,12 +489,15 @@ public class LogPanel extends DockablePa
       "detailPaneVisible",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
-          boolean newValue = ((Boolean) evt.getNewValue()).booleanValue();
+          boolean detailPaneVisible = ((Boolean) evt.getNewValue()).booleanValue();
 
-          if (newValue) {
+          if (detailPaneVisible) {
             showDetailPane();
           } else {
-            hideDetailPane();
+            //don't hide the detail pane if search results are being displayed
+            if (!searchResultsDisplayed) {
+              hideDetailPane();
+            }
           }
         }
       });
@@ -496,63 +516,79 @@ public class LogPanel extends DockablePa
         }
       });
     
-    preferenceModel.addPropertyChangeListener(
-      "toolTips",
+    preferenceModel.addPropertyChangeListener("toolTips",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
-          renderer.setToolTipsVisible(
-            ((Boolean) evt.getNewValue()).booleanValue());
+          boolean toolTips = ((Boolean) evt.getNewValue()).booleanValue();
+          renderer.setToolTipsVisible(toolTips);
+          searchRenderer.setToolTipsVisible(toolTips);
         }
       });
 
-    preferenceModel.addPropertyChangeListener(
-      "visibleColumns",
+    preferenceModel.addPropertyChangeListener("visibleColumns",
       new PropertyChangeListener() {
     	public void propertyChange(PropertyChangeEvent evt) {
     		//remove all columns and re-add visible
             TableColumnModel columnModel = table.getColumnModel();
             while (columnModel.getColumnCount() > 0) {
-                columnModel.removeColumn(columnModel.getColumn(0)); 
-    		}
+                columnModel.removeColumn(columnModel.getColumn(0));
+        		}
             for (Iterator iter = preferenceModel.getVisibleColumnOrder().iterator();iter.hasNext();) {
-    			TableColumn c = (TableColumn)iter.next();
-    			columnModel.addColumn(c);
-    		}
-    	}
+              TableColumn c = (TableColumn)iter.next();
+              if (c.getHeaderValue().toString().toLowerCase().equals(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE))
+              {
+                c.setCellEditor(markerCellEditor);
+              }
+              columnModel.addColumn(c);
+        		}
+          TableColumnModel searchColumnModel = searchTable.getColumnModel();
+          while (searchColumnModel.getColumnCount() > 0) {
+              searchColumnModel.removeColumn(searchColumnModel.getColumn(0));
+          }
+          for (Iterator iter = preferenceModel.getVisibleColumnOrder().iterator();iter.hasNext();) {
+            TableColumn c = (TableColumn)iter.next();
+            searchColumnModel.addColumn(c);
+          }
+      	}
       });
 
     PropertyChangeListener datePrefsChangeListener =
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
-          LogPanelPreferenceModel model =
-            (LogPanelPreferenceModel) evt.getSource();
+          LogPanelPreferenceModel model = (LogPanelPreferenceModel) evt.getSource();
 
           isoButton.setSelected(model.isUseISO8601Format());
-          simpleTimeButton.setSelected(
-            !model.isUseISO8601Format() && !model.isCustomDateFormat());
+          simpleTimeButton.setSelected(!model.isUseISO8601Format() && !model.isCustomDateFormat());
 
           if (model.getTimeZone() != null) {
             renderer.setTimeZone(model.getTimeZone());
+            searchRenderer.setTimeZone(model.getTimeZone());
           }
           
           if (model.isUseISO8601Format()) {
             renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
+            searchRenderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
           } else {
       		try {
-            renderer.setDateFormatter(
-              new SimpleDateFormat(model.getDateFormatPattern()));
-            		} catch (IllegalArgumentException iae) {
-            			model.setDefaultDatePatternFormat();
-                        renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
-            		}
+              renderer.setDateFormatter(new SimpleDateFormat(model.getDateFormatPattern()));
+          } catch (IllegalArgumentException iae) {
+            model.setDefaultDatePatternFormat();
+            renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
+          }
+  		    try {
+              searchRenderer.setDateFormatter(new SimpleDateFormat(model.getDateFormatPattern()));
+          } catch (IllegalArgumentException iae) {
+            model.setDefaultDatePatternFormat();
+            searchRenderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
           }
+        }
 
-          table.tableChanged(new TableModelEvent(tableModel));
+        table.tableChanged(new TableModelEvent(tableModel));
+        searchTable.tableChanged(new TableModelEvent(searchModel));
         }
       };
 
-    preferenceModel.addPropertyChangeListener(
-      "dateFormatPattern", datePrefsChangeListener);
+    preferenceModel.addPropertyChangeListener("dateFormatPattern", datePrefsChangeListener);
     preferenceModel.addPropertyChangeListener("dateFormatTimeZone", datePrefsChangeListener);
 
     preferenceModel.addPropertyChangeListener("clearTableExpression", new PropertyChangeListener() {
@@ -569,25 +605,25 @@ public class LogPanel extends DockablePa
         }
     });
 
-    preferenceModel.addPropertyChangeListener(
-      "loggerPrecision",
+    preferenceModel.addPropertyChangeListener("loggerPrecision",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
-          LogPanelPreferenceModel model =
-            (LogPanelPreferenceModel) evt.getSource();
+          LogPanelPreferenceModel model = (LogPanelPreferenceModel) evt.getSource();
 
           renderer.setLoggerPrecision(model.getLoggerPrecision());
-
           table.tableChanged(new TableModelEvent(tableModel));
+
+          searchRenderer.setLoggerPrecision(model.getLoggerPrecision());
+          searchTable.tableChanged(new TableModelEvent(searchModel));
         }
       });
 
-    preferenceModel.addPropertyChangeListener(
-      "toolTips",
+    preferenceModel.addPropertyChangeListener("toolTips",
       new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
           boolean value = ((Boolean) evt.getNewValue()).booleanValue();
-          menuItemToggleToolTips.setSelected(value);
+          searchToggleToolTips.setSelected(value);
+          mainToggleToolTips.setSelected(value);
         }
       });
 
@@ -627,6 +663,9 @@ public class LogPanel extends DockablePa
             if (table != null) {
               table.repaint();
             }
+            if (searchTable != null) {
+              searchTable.repaint();
+            }
         }
     });
 
@@ -636,17 +675,30 @@ public class LogPanel extends DockablePa
             if (table != null) {
               table.repaint();
             }
+            if (searchTable != null) {
+              searchTable.repaint();
+           }
         }
     });
 
     /*
      *End of preferenceModel listeners
      */
-    tableModel = new ChainsawCyclicBufferTableModel(cyclicBufferSize, colorizer);
+    tableModel = new ChainsawCyclicBufferTableModel(cyclicBufferSize, colorizer, "main");
     table = new JSortTable(tableModel);
+
+    markerCellEditor = new MarkerCellEditor();
+    table.setName("main");
     table.setColumnSelectionAllowed(false);
     table.setRowSelectionAllowed(true);
 
+    searchModel = new ChainsawCyclicBufferTableModel(cyclicBufferSize, colorizer, "search");
+    searchTable = new JSortTable(searchModel);
+
+    searchTable.setName("search");
+    searchTable.setColumnSelectionAllowed(false);
+    searchTable.setRowSelectionAllowed(true);
+
     //we've mapped f2, shift f2 and ctrl-f2 to marker-related actions, unmap them from the table
     table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("F2"), "none");
     table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.SHIFT_MASK), "none");
@@ -656,10 +708,18 @@ public class LogPanel extends DockablePa
     //we're also mapping ctrl-a to scroll-to-top, unmap from the table
     table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK), "none");
         
+    searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("F2"), "none");
+    searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.SHIFT_MASK), "none");
+    searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.CTRL_MASK), "none");
+    searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK), "none");
+
+    //we're also mapping ctrl-a to scroll-to-top, unmap from the table
+    searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK), "none");
+
     //add a listener to update the 'refine focus'
     tableModel.addNewKeyListener(new NewKeyListener() {
 		public void newKeyAdded(NewKeyEvent e) {
-            columnNameKeywordMap.put(e.getKey(), "PROP." + e.getKey());
+          columnNameKeywordMap.put(e.getKey(), "PROP." + e.getKey());
 		}
     });
 
@@ -667,7 +727,8 @@ public class LogPanel extends DockablePa
      * Set the Display rule to use the mediator, the model will add itself as
      * a property change listener and update itself when the rule changes.
      */
-    tableModel.setDisplayRule(ruleMediator);
+    tableModel.setDisplayRule(tableRuleMediator);
+    searchModel.setDisplayRule(searchRuleMediator);
 
     tableModel.addEventCountListener(
       new EventCountListener() {
@@ -742,7 +803,9 @@ public class LogPanel extends DockablePa
      * is a rule itself, and the RuleMediator will automatically listen when
      * it's rule state changes.
      */
-    ruleMediator.setLoggerRule(logTreePanel.getLoggerVisibilityRule());
+    tableRuleMediator.setLoggerRule(logTreePanel.getLoggerVisibilityRule());
+    searchRuleMediator.setLoggerRule(logTreePanel.getLoggerVisibilityRule());
+
     colorizer.setLoggerRule(logTreePanel.getLoggerColorRule());
 
     /*
@@ -779,6 +842,9 @@ public class LogPanel extends DockablePa
           if (table != null) {
             table.repaint();
           }
+          if (searchTable != null) {
+            searchTable.repaint();
+          }
         }
       });
 
@@ -789,16 +855,22 @@ public class LogPanel extends DockablePa
     table.setRowMargin(0);
     table.getColumnModel().setColumnMargin(0);
     table.setShowGrid(false);
-
-    table.getColumnModel().addColumnModelListener(
-      new ChainsawTableColumnModelListener());
-
+    table.getColumnModel().addColumnModelListener(new ChainsawTableColumnModelListener(table));
     table.setAutoCreateColumnsFromModel(false);
+    table.addMouseMotionListener(new TableColumnDetailMouseListener(table, tableModel));
+    table.addMouseListener(new TableMarkerListener(table, tableModel, searchModel));
+    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 
-    table.addMouseMotionListener(new TableColumnDetailMouseListener());
-    table.addMouseListener(new TableMarkerListener());
+    searchTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
+    searchTable.setRowMargin(0);
+    searchTable.getColumnModel().setColumnMargin(0);
+    searchTable.setShowGrid(false);
+    searchTable.getColumnModel().addColumnModelListener(new ChainsawTableColumnModelListener(searchTable));
+    searchTable.setAutoCreateColumnsFromModel(false);
+    searchTable.addMouseMotionListener(new TableColumnDetailMouseListener(searchTable, searchModel));
+    searchTable.addMouseListener(new TableMarkerListener(searchTable, searchModel, tableModel));
+    searchTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 
-    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 
     //set valueisadjusting if holding down a key - don't process setdetail events
     table.addKeyListener(
@@ -822,6 +894,7 @@ public class LogPanel extends DockablePa
       });
 
     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+    searchTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 
     table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
         public void valueChanged(ListSelectionEvent evt) {
@@ -893,57 +966,32 @@ public class LogPanel extends DockablePa
         }
       });
 
-    renderer = new TableColorizingRenderer(colorizer, applicationPreferenceModel, tableModel, preferenceModel);
+    renderer = new TableColorizingRenderer(colorizer, applicationPreferenceModel, tableModel, preferenceModel, true);
     renderer.setToolTipsVisible(preferenceModel.isToolTips());
 
     table.setDefaultRenderer(Object.class, renderer);
 
+    searchRenderer = new TableColorizingRenderer(colorizer, applicationPreferenceModel, searchModel, preferenceModel, false);
+    searchRenderer.setToolTipsVisible(preferenceModel.isToolTips());
+
+    searchTable.setDefaultRenderer(Object.class, searchRenderer);
+
     /*
      * Throwable popup
      */
-    markerCellEditor = new MarkerCellEditor();
+    table.addMouseListener(new ThrowableDisplayMouseAdapter(table, tableModel));
+    searchTable.addMouseListener(new ThrowableDisplayMouseAdapter(searchTable, searchModel));
 
-    final JDialog detailDialog = new JDialog((JFrame) null, true);
-    Container container = detailDialog.getContentPane();
-    final JEditorPane detailArea = new JEditorPane();
-    JTextComponentFormatter.applySystemFontAndSize(detailArea);
-    detailArea.setEditable(false);
-    Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
-    detailArea.setPreferredSize(new Dimension(screenDimension.width / 2, screenDimension.height / 2));
-    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
-    container.add(new JScrollPane(detailArea));
-
-    detailDialog.pack();
-
-    table.addMouseListener(new MouseAdapter() {
-        public void mouseClicked(MouseEvent e)
-        {
-            TableColumn column = table.getColumnModel().getColumn(table.columnAtPoint(e.getPoint()));
-            if (!column.getHeaderValue().toString().toUpperCase().equals(ChainsawColumns.getColumnName(ChainsawColumns.INDEX_THROWABLE_COL_NAME))) {
-                return;
-            }
-
-            ExtendedLoggingEvent event = tableModel.getRow(table.getSelectedRow());
-
-            //throwable string representation may be a length-one empty array
-            String[] ti = event.getThrowableStrRep();
-            if (ti != null && ti.length > 0 && (!(ti.length == 1 && ti[0].equals("")))) {
-                 detailDialog.setTitle(table.getColumnName(table.getSelectedColumn()) + " detail...");
-                  StringBuffer buf = new StringBuffer();
-                  buf.append(event.getMessage());
-                  buf.append("\n");
-                  for (int i = 0; i < ti.length; i++) {
-                    buf.append(ti[i]).append("\n    ");
-                  }
-
-                  detailArea.setText(buf.toString());
-                  SwingHelper.invokeOnEDT(new Runnable() {
-                    public void run() {
-                      centerAndSetVisible(detailDialog);
-                    }
-                  });
-                }
+    //select a row in the main table when a row in the search table is selected
+    searchTable.addMouseListener(new MouseAdapter() {
+      public void mouseClicked(MouseEvent e) {
+        LoggingEventWrapper loggingEventWrapper = searchModel.getRow(searchTable.getSelectedRow());
+        if (loggingEventWrapper != null) {
+          int id = new Integer(loggingEventWrapper.getLoggingEvent().getProperty("log4jid")).intValue();
+          //preserve the table's viewble column
+          setSelectedEvent(id);
         }
+      }
     });
 
     /*
@@ -962,13 +1010,15 @@ public class LogPanel extends DockablePa
         	try {
         	if(table.getColumn(e.getKey())!=null){
                 return;
-            } 
+            }
+            //no need to check search table - we use the same columns
         	} catch (IllegalArgumentException iae) {}
           TableColumn col = new TableColumn(e.getNewModelIndex());
           col.setHeaderValue(e.getKey());
 
           if (preferenceModel.addColumn(col)) {
         	  table.addColumn(col);
+              searchTable.addColumn(col);
         	  preferenceModel.setColumnVisible(e.getKey().toString(), true);
           }
         		}
@@ -981,7 +1031,7 @@ public class LogPanel extends DockablePa
     //refilter with a newValue of FALSE means refiltering is complete
     //assuming notification is called on the EDT so we can in the current EDT call update the scroll & selection
     tableModel.addPropertyChangeListener("refilter", new PropertyChangeListener() {
-        private LoggingEvent currentEvent;
+        private LoggingEventWrapper currentEvent;
         public void propertyChange(PropertyChangeEvent evt) {
             //if new value is true, filtering is about to begin
             //if new value is false, filtering is complete
@@ -1143,11 +1193,14 @@ public class LogPanel extends DockablePa
             }
 		}
     });
-    addPropertyChangeListener(
-      "detailPaneConversionPattern", detailPaneUpdater);
+    addPropertyChangeListener("detailPaneConversionPattern", detailPaneUpdater);
 
-    final JScrollPane detailPane = new JScrollPane(detail);
+    searchPane = new JScrollPane(searchTable);
+    searchPane.getVerticalScrollBar().setUnitIncrement(ChainsawConstants.DEFAULT_ROW_HEIGHT * 2);
+    searchPane.setPreferredSize(new Dimension(900, 50));
 
+    //default detail panel to contain detail panel - if searchResultsVisible is true, when a search if triggered, update detail pane to contain search results
+    detailPane = new JScrollPane(detail);
     detailPane.setPreferredSize(new Dimension(900, 50));
 
     detailPanel.add(detailPane, BorderLayout.CENTER);
@@ -1187,21 +1240,10 @@ public class LogPanel extends DockablePa
     statusLabelPanel.add(upperPanel, BorderLayout.CENTER);
     eventsAndStatusPanel.add(statusLabelPanel, BorderLayout.NORTH);
 
-    lowerPanel =
-      new JSplitPane(
-        JSplitPane.VERTICAL_SPLIT, eventsAndStatusPanel, detailPanel);
-
-    dividerSize = lowerPanel.getDividerSize();
-    lowerPanel.setDividerLocation(-1);
-
-    lowerPanel.setResizeWeight(1.0);
-    lowerPanel.setBorder(null);
-    lowerPanel.setContinuousLayout(true);
-
     /*
      * Detail panel layout editor
      */
-    final JToolBar detailToolbar = new JToolBar(SwingConstants.HORIZONTAL);
+    detailToolbar = new JToolBar(SwingConstants.HORIZONTAL);
     detailToolbar.setFloatable(false);
 
     final LayoutEditorPane layoutEditorPane = new LayoutEditorPane();
@@ -1295,6 +1337,15 @@ public class LogPanel extends DockablePa
 
     detailPanel.add(detailToolbar, BorderLayout.NORTH);
 
+    lowerPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, eventsAndStatusPanel, detailPanel);
+
+    dividerSize = lowerPanel.getDividerSize();
+    lowerPanel.setDividerLocation(-1);
+
+    lowerPanel.setResizeWeight(1.0);
+    lowerPanel.setBorder(null);
+    lowerPanel.setContinuousLayout(true);
+
     JPopupMenu editDetailPopupMenu = new JPopupMenu();
 
     editDetailPopupMenu.add(copyToRefineFocusAction);
@@ -1363,52 +1414,64 @@ public class LogPanel extends DockablePa
     /*
      * Other menu items
      */
-    final JMenuItem menuItemBestFit = new JMenuItem("Best fit column");
-    menuItemBestFit.addActionListener(
+    class BestFit extends JMenuItem {
+      public BestFit() {
+        super("Best fit column");
+    addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           if (currentPoint != null) {
-            int column = table.columnAtPoint(currentPoint);
+            int column = currentTable.columnAtPoint(currentPoint);
             int maxWidth = getMaxColumnWidth(column);
-            table.getColumnModel().getColumn(column).setPreferredWidth(
+            currentTable.getColumnModel().getColumn(column).setPreferredWidth(
               maxWidth);
           }
         }
       });
+      }
+    }
 
-    JMenuItem menuItemColorPanel = new JMenuItem("Color settings...");
-    menuItemColorPanel.addActionListener(
+    class ColorPanel extends JMenuItem {
+      public ColorPanel() {
+        super("Color settings...");
+        setIcon(ChainsawIcons.ICON_PREFERENCES);
+  addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           showColorPreferences();
         }
       });
-    menuItemColorPanel.setIcon(ChainsawIcons.ICON_PREFERENCES);
+      }
+    }
 
-    JMenuItem menuItemLogPanelPreferences =
-      new JMenuItem("Tab Preferences...");
-    menuItemLogPanelPreferences.addActionListener(
+    class LogPanelPreferences extends JMenuItem {
+      public LogPanelPreferences() {
+        super("Tab Preferences...");
+        setIcon(ChainsawIcons.ICON_PREFERENCES);
+    addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           showPreferences();
         }
       });
-    menuItemLogPanelPreferences.setIcon(ChainsawIcons.ICON_PREFERENCES);
+    }
+  }
 
-    final JMenuItem menuItemFocusOn =
-      new JMenuItem("Set 'refine focus' field to value under pointer");
-    menuItemFocusOn.addActionListener(
+    class FocusOn extends JMenuItem {
+      public FocusOn() {
+        super("Set 'refine focus' field to value under pointer");
+    addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           if (currentPoint != null) {
             String operator = "==";
-            int column = table.columnAtPoint(currentPoint);
-            int row = table.rowAtPoint(currentPoint);
-            String colName = table.getColumnName(column).toUpperCase();
+            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)) {
-            	value = timestampExpressionFormat.format(new Date(table.getValueAt(row, column).toString()));
+            	value = timestampExpressionFormat.format(new Date(currentTable.getValueAt(row, column).toString()));
             } else {
               Object o = table.getValueAt(row, column);
 
@@ -1430,28 +1493,31 @@ public class LogPanel extends DockablePa
           }
         }
       });
+      }
+    }
 
-    final JMenuItem menuDefineAddCustomFilter =
-      new JMenuItem("Add value under pointer to 'refine focus' field");
-    menuDefineAddCustomFilter.addActionListener(
+    class DefineAddCustomFilter extends JMenuItem {
+      public DefineAddCustomFilter() {
+        super("Add value under pointer to 'refine focus' field");
+  addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           if (currentPoint != null) {
             String operator = "==";
-            int column = table.columnAtPoint(currentPoint);
-            int row = table.rowAtPoint(currentPoint);
-            String colName = table.getColumnName(column).toUpperCase();
+            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) table.getCellRenderer(row, column);
+                (JComponent) currentTable.getCellRenderer(row, column);
 
               if (comp instanceof JLabel) {
                 value = ((JLabel) comp).getText();
               }
             } else {
-              Object o = table.getValueAt(row, column);
+              Object o = currentTable.getValueAt(row, column);
 
               if (o instanceof String[] && ((String[])o).length > 0) {
                 value = ((String[]) o)[0];
@@ -1470,28 +1536,31 @@ public class LogPanel extends DockablePa
           }
         }
       });
+      }
+    }
 
-    final JMenuItem menuBuildColorRule =
-      new JMenuItem("Define color rule for value under pointer");
-    menuBuildColorRule.addActionListener(
+    class BuildColorRule extends JMenuItem {
+      public BuildColorRule() {
+        super("Define color rule for value under pointer");
+      addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           if (currentPoint != null) {
             String operator = "==";
-            int column = table.columnAtPoint(currentPoint);
-            int row = table.rowAtPoint(currentPoint);
-            String colName = table.getColumnName(column).toUpperCase();
+            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) table.getCellRenderer(row, column);
+                (JComponent) currentTable.getCellRenderer(row, column);
 
               if (comp instanceof JLabel) {
                 value = ((JLabel) comp).getText();
               }
             } else {
-              Object o = table.getValueAt(row, column);
+              Object o = currentTable.getValueAt(row, column);
 
               if (o instanceof String[] && ((String[])o).length > 0) {
                 value = ((String[]) o)[0];
@@ -1511,42 +1580,51 @@ public class LogPanel extends DockablePa
           }
         }
       });
+      }
+    }
 
-    final JPopupMenu p = new JPopupMenu();
+    final JPopupMenu mainPopup = new JPopupMenu();
+    final JPopupMenu searchPopup = new JPopupMenu();
 
-    final Action clearFocusAction =
-      new AbstractAction("Clear 'refine focus' field") {
+    class ClearFocus extends AbstractAction {
+      public ClearFocus() {
+        super("Clear 'refine focus' field");
+      }
         public void actionPerformed(ActionEvent e) {
           filterText.setText(null);
-          ruleMediator.setRefinementRule(null);
+          tableRuleMediator.setFilterRule(null);
+          searchRuleMediator.setFilterRule(null);
         }
-      };
+      }
+
+    class Copy extends AbstractAction {
+      public Copy() {
+        super("Copy value under pointer to clipboard");
+      }
 
-    final Action copyAction =
-      new AbstractAction("Copy value under pointer to clipboard") {
         public void actionPerformed(ActionEvent e) {
-                      if (currentPoint != null) {
-            int column = table.columnAtPoint(currentPoint);
-            int row = table.rowAtPoint(currentPoint);
-            String colName = table.getColumnName(column).toUpperCase();
+          if (currentPoint != 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) table.getCellRenderer(row, column);
+                (JComponent) currentTable.getCellRenderer(row, column);
 
               if (comp instanceof JLabel) {
                 value = ((JLabel) comp).getText();
               }
             } else {
-              Object o = table.getValueAt(row, column);
+              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("")))) {
-                        ExtendedLoggingEvent event = tableModel.getRow(row);
-                        value = event.getMessage().toString();
+                        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];
                         }
@@ -1561,8 +1639,7 @@ public class LogPanel extends DockablePa
             clipboard.setContents(selection, null);
         }
       }
-      };
-
+      }
     final JMenuItem menuItemToggleDock = new JMenuItem("Undock/dock");
 
     dockingAction =
@@ -1582,28 +1659,34 @@ public class LogPanel extends DockablePa
     /*
      * Popup definition
      */
-    p.add(menuItemFocusOn);
-    p.add(menuDefineAddCustomFilter);
-    p.add(clearFocusAction);
-
-    p.add(new JSeparator());
-
-        final JMenuItem menuItemSearch =
-      new JMenuItem("Search for value under pointer");
-    menuItemSearch.addActionListener(
+    mainPopup.add(new FocusOn());
+    searchPopup.add(new FocusOn());
+    mainPopup.add(new DefineAddCustomFilter());
+    searchPopup.add(new DefineAddCustomFilter());
+    mainPopup.add(new ClearFocus());
+    searchPopup.add(new ClearFocus());
+
+    mainPopup.add(new JSeparator());
+    searchPopup.add(new JSeparator());
+
+    class Search extends JMenuItem {
+      public Search() {
+        super("Search for value under pointer");
+
+    addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
           if (currentPoint != null) {
             String operator = "~=";
-            int column = table.columnAtPoint(currentPoint);
-            int row = table.rowAtPoint(currentPoint);
-            String colName = table.getColumnName(column).toUpperCase();
+            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)) {
-            	value = timestampExpressionFormat.format(new Date(table.getValueAt(row, column).toString()));
+            	value = timestampExpressionFormat.format(new Date(currentTable.getValueAt(row, column).toString()));
             } else {
-              Object o = table.getValueAt(row, column);
+              Object o = currentTable.getValueAt(row, column);
 
               if (o != null) {
                 if (o instanceof String[] && ((String[])o).length > 0) {
@@ -1623,101 +1706,135 @@ public class LogPanel extends DockablePa
           }
         }
       });
+      }
+    }
 
-      final Action clearSearchAction =
-        new AbstractAction("Clear search field") {
+     class ClearSearch extends AbstractAction {
+       public ClearSearch() {
+         super("Clear search field");
+       }
           public void actionPerformed(ActionEvent e) {
             findField.setText(null);
             updateFindRule(null);
           }
-        };
-
-    p.add(menuItemSearch);
-    p.add(clearSearchAction);
-      
-    p.add(new JSeparator());
-
-    p.add(menuItemBestFit);
-
-    p.add(new JSeparator());
+        }
 
-    final JMenuItem menuItemDisplayNormalTimes =
-      new JMenuItem("Hide relative times");
-    menuItemDisplayNormalTimes.addActionListener(
+    mainPopup.add(new Search());
+    searchPopup.add(new Search());
+    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");
+  addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent e) {
           if (currentPoint != null) {
-            renderer.setUseNormalTimes();
-            tableModel.reFilter();
-            menuItemDisplayNormalTimes.setEnabled(false);
+            ((TableColorizingRenderer)currentTable.getDefaultRenderer(Object.class)).setUseNormalTimes();
+            ((ChainsawCyclicBufferTableModel)currentTable.getModel()).reFilter();
+            setEnabled(false);
           }
         }
     });
+      }
+    }
 
-    final JMenuItem menuItemDisplayRelativeTimesToRowUnderCursor =
-      new JMenuItem("Show times relative to this event");
-    menuItemDisplayRelativeTimesToRowUnderCursor.addActionListener(
+    class DisplayRelativeTimesToRowUnderCursor extends JMenuItem {
+    public DisplayRelativeTimesToRowUnderCursor() {
+      super("Show times relative to this event");
+      addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent e) {
             if (currentPoint != null) {
-              int row = table.rowAtPoint(currentPoint);
-              LoggingEvent event = tableModel.getRow(row);
-              if (event != null)
+              int row = currentTable.rowAtPoint(currentPoint);
+              ChainsawCyclicBufferTableModel cyclicBufferTableModel = (ChainsawCyclicBufferTableModel) currentTable.getModel();
+              LoggingEventWrapper loggingEventWrapper = cyclicBufferTableModel.getRow(row);
+              if (loggingEventWrapper != null)
               {
-                  renderer.setUseRelativeTimes(event.getTimeStamp());
-                  tableModel.reFilter();
+                  ((TableColorizingRenderer)currentTable.getDefaultRenderer(Object.class)).setUseRelativeTimes(loggingEventWrapper.getLoggingEvent().getTimeStamp());
+                  cyclicBufferTableModel.reFilter();
               }
-              menuItemDisplayNormalTimes.setEnabled(true);
+              setEnabled(true);
             }
         }
       });
+    }
+    }
 
-      final JMenuItem menuItemDisplayRelativeTimesToPreviousRow =
-        new JMenuItem("Show times relative to previous rows");
-      menuItemDisplayRelativeTimesToPreviousRow.addActionListener(
+    class DisplayRelativeTimesToPreviousRow extends JMenuItem {
+      public DisplayRelativeTimesToPreviousRow() {
+        super("Show times relative to previous rows");
+      addActionListener(
         new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               if (currentPoint != null) {
-                renderer.setUseRelativeTimesToPreviousRow();
-                tableModel.reFilter();
-                 menuItemDisplayNormalTimes.setEnabled(true);
+                ((TableColorizingRenderer)currentTable.getDefaultRenderer(Object.class)).setUseRelativeTimesToPreviousRow();
+                ((ChainsawCyclicBufferTableModel)currentTable.getModel()).reFilter();
+                setEnabled(true);
               }
           }
         });
+      }
+    }
 
-    menuItemDisplayNormalTimes.setEnabled(false);
-    p.add(menuItemDisplayRelativeTimesToRowUnderCursor);
-    p.add(menuItemDisplayRelativeTimesToPreviousRow);
-    p.add(menuItemDisplayNormalTimes);
-    p.add(new JSeparator());
-
-    p.add(menuBuildColorRule);
-    p.add(copyAction);
-    p.add(new JSeparator());
-        
-    p.add(menuItemToggleDetails);
-    p.add(menuItemLoggerTree);
-    p.add(menuItemToggleToolTips);
-
-    p.add(new JSeparator());
-
-    p.add(menuItemScrollToTop);
-    p.add(menuItemScrollBottom);
-    p.add(new JSeparator());
-
-    p.add(menuItemToggleDock);
-
-    p.add(new JSeparator());
-
-    p.add(menuItemColorPanel);
-    p.add(menuItemLogPanelPreferences);
-
-    final PopupListener popupListener = new PopupListener(p);
+    mainPopup.add(new DisplayRelativeTimesToRowUnderCursor());
+    searchPopup.add(new DisplayRelativeTimesToRowUnderCursor());
+    mainPopup.add(new DisplayRelativeTimesToPreviousRow());
+    searchPopup.add(new DisplayRelativeTimesToPreviousRow());
+    mainPopup.add(new DisplayNormalTimes());
+    searchPopup.add(new DisplayNormalTimes());
+    mainPopup.add(new JSeparator());
+    searchPopup.add(new JSeparator());
+
+    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(menuItemToggleDetails);
+    mainPopup.add(menuItemLoggerTree);
+    mainToggleToolTips = new ToggleToolTips();
+    searchToggleToolTips = new ToggleToolTips();
+    mainPopup.add(mainToggleToolTips);
+    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 JSeparator());
+    searchPopup.add(new JSeparator());
+
+    mainPopup.add(new ColorPanel());
+    searchPopup.add(new ColorPanel());
+    mainPopup.add(new LogPanelPreferences());
+    searchPopup.add(new LogPanelPreferences());
+
+    final PopupListener mainTablePopupListener = new PopupListener(mainPopup);
+    eventsPane.addMouseListener(mainTablePopupListener);
+    table.addMouseListener(mainTablePopupListener);
 
-    eventsPane.addMouseListener(popupListener);
-    table.addMouseListener(popupListener);
+    final PopupListener searchTablePopupListener = new PopupListener(searchPopup);
+    detailPane.addMouseListener(searchTablePopupListener);
   }
-  
+
   /**
    * Accessor
    *
@@ -1808,18 +1925,26 @@ public class LogPanel extends DockablePa
         }
         final int selectedRow = table.getSelectedRow();
         final int startingRow = table.getRowCount();
-        final ExtendedLoggingEvent selectedEvent;
+        final LoggingEventWrapper selectedEvent;
         if (selectedRow >= 0) {
           selectedEvent = tableModel.getRow(selectedRow);
         } else {
           selectedEvent = null;
         }
 
+        final int startingSearchRow = searchTable.getRowCount();
+
         boolean rowAdded = false;
+        boolean searchRowAdded = false;
 
         int addedRowCount = 0;
+        int searchAddedRowCount = 0;
+
         for (Iterator iter = events.iterator(); iter.hasNext();) {
-          ExtendedLoggingEvent event = new ExtendedLoggingEvent((LoggingEvent) iter.next());
+          //these are actual LoggingEvent instances
+          LoggingEvent event = (LoggingEvent)iter.next();
+          //create two separate loggingEventWrappers (main table and search table), as they have different info on display state
+          LoggingEventWrapper loggingEventWrapper1 = new LoggingEventWrapper(event, tableModel);
             //if the clearTableExpressionRule is not null, evaluate & clear the table if it matches
             if (clearTableExpressionRule != null && clearTableExpressionRule.evaluate(event, null)) {
                 logger.info("clear table expression matched - clearing table - matching event msg - " + event.getMessage());
@@ -1827,17 +1952,27 @@ public class LogPanel extends DockablePa
             }
 
           updateOtherModels(event);
-
-          boolean isCurrentRowAdded = tableModel.isAddRow(event);
+          boolean isCurrentRowAdded = tableModel.isAddRow(loggingEventWrapper1);
           if (isCurrentRowAdded) {
               addedRowCount++;
           }
           rowAdded = rowAdded || isCurrentRowAdded;
+
+          //create a new loggingEventWrapper via copy constructor to ensure same IDs
+          LoggingEventWrapper loggingEventWrapper2 = new LoggingEventWrapper(loggingEventWrapper1, searchModel);
+          boolean isSearchCurrentRowAdded = searchModel.isAddRow(loggingEventWrapper2);
+          if (isSearchCurrentRowAdded) {
+              searchAddedRowCount++;
+          }
+          searchRowAdded = searchRowAdded || isSearchCurrentRowAdded;
         }
         //fire after adding all events
         if (rowAdded) {
           tableModel.fireTableEvent(startingRow, startingRow + addedRowCount, addedRowCount);
         }
+        if (searchRowAdded) {
+          searchModel.fireTableEvent(startingSearchRow, startingSearchRow + searchAddedRowCount, searchAddedRowCount);
+        }
 
         //tell the model to notify the count listeners
         tableModel.notifyCountListeners();
@@ -1846,11 +1981,17 @@ public class LogPanel extends DockablePa
           if (tableModel.isSortEnabled()) {
             tableModel.sort();
           }
-          
+
           //always update detail pane (since we may be using a cyclic buffer which is full)
           detailPaneUpdater.setSelectedRow(table.getSelectedRow());
         }
 
+        if (searchRowAdded) {
+          if (searchModel.isSortEnabled()) {
+            searchModel.sort();
+          }
+        }
+
         if (!isScrollToBottom() && selectedEvent != null) {
           final int newIndex = tableModel.getRowIndex(selectedEvent);
           if (newIndex >= 0) {
@@ -1925,12 +2066,25 @@ public class LogPanel extends DockablePa
                         TableColumn col = (TableColumn)iter.next();
                         columnModel.addColumn(col);
                     }
+
+                  TableColumnModel searchColumnModel = searchTable.getColumnModel();
+                  //remove previous columns
+                  while (searchColumnModel.getColumnCount() > 0) {
+                      searchColumnModel.removeColumn(searchColumnModel.getColumn(0));
+                  }
+                  //add visible column order columns
+                  for (Iterator iter = preferenceModel.getVisibleColumnOrder().iterator();iter.hasNext();) {
+                      TableColumn col = (TableColumn)iter.next();
+                      searchColumnModel.addColumn(col);
+                  }
+
                     preferenceModel.apply(storedPrefs);
                 } else {
                     loadDefaultColumnSettings(event);
                 }
                 //ensure tablemodel cyclic flag is updated
                 tableModel.setCyclic(preferenceModel.isCyclic());
+                searchModel.setCyclic(preferenceModel.isCyclic());
                 //may be panel configs that don't have these values
                 lowerPanel.setDividerLocation(lowerPanelDividerLocation);
                 nameTreeAndMainPanelSplit.setDividerLocation(treeDividerLocation);
@@ -1994,6 +2148,7 @@ public class LogPanel extends DockablePa
     	visibleOrder.add(c);
     }
     preferenceModel.setVisibleColumnOrder(visibleOrder);
+    //search table will use same columns as main table
     
     XStream stream = buildXStreamForLogPanelPreference();
     ObjectOutputStream s = null;
@@ -2077,6 +2232,10 @@ public class LogPanel extends DockablePa
     return preferenceModel.isDetailPaneVisible();
   }
 
+  boolean isSearchResultsVisible() {
+    return preferenceModel.isSearchResultsVisible();
+  }
+
   /**
    * Toggle panel preference for logger tree visibility on or off
    */
@@ -2109,7 +2268,7 @@ public class LogPanel extends DockablePa
    * @return list of LoggingEvents
    */
   List getFilteredEvents() {
-  	return tableModel.getFilteredEvents();  
+  	return tableModel.getFilteredEvents();
   }
   
   List getMatchingEvents(Rule rule) {
@@ -2210,7 +2369,7 @@ public class LogPanel extends DockablePa
       if (row > -1) {
         preferenceModel.setScrollToBottom(false);
 
-        table.scrollTo(row, 0);
+        table.scrollToRow(row);
       }
       return row;
   }
@@ -2233,6 +2392,7 @@ public class LogPanel extends DockablePa
 
     preferenceModel.setCyclic(toggledCyclic);
     tableModel.setCyclic(toggledCyclic);
+    searchModel.setCyclic(toggledCyclic);
   }
 
   /**
@@ -2249,12 +2409,19 @@ public class LogPanel extends DockablePa
       findRule = null;
       tableModel.updateEventsWithFindRule(null);
       colorizer.setFindRule(null);
+      tableRuleMediator.setFindRule(null);
+      searchRuleMediator.setFindRule(null);
       //reset background color in case we were previously an invalid expression
       findField.setBackground(UIManager.getColor("TextField.background"));
       findField.setToolTipText(
         "Enter expression - right click or ctrl-space for menu");
       currentSearchMatchCount = 0;
+      currentFindRuleText = null;
       statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
+      //if the preference to show search results is enabled, the find rule is now null - hide search results
+      if (isSearchResultsVisible()) {
+        hideSearchResults();
+      }
     } else {
       //only turn off scrolltobottom when finding something (find not empty)
       preferenceModel.setScrollToBottom(false);
@@ -2268,17 +2435,65 @@ public class LogPanel extends DockablePa
           "Enter expression - right click or ctrl-space for menu");
         findRule = ExpressionRule.getRule(ruleText);
         currentSearchMatchCount = tableModel.updateEventsWithFindRule(findRule);
+        searchModel.updateEventsWithFindRule(findRule);
         colorizer.setFindRule(findRule);
+        tableRuleMediator.setFindRule(findRule);
+        searchRuleMediator.setFindRule(findRule);
         //valid expression, reset background color in case we were previously an invalid expression
         findField.setBackground(UIManager.getColor("TextField.background"));
         statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
+        if (isSearchResultsVisible()) {
+          showSearchResults();
+        }
       } catch (IllegalArgumentException re) {
+        findRule = null;
         findField.setToolTipText(re.getMessage());
         findField.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
         colorizer.setFindRule(null);
+        tableRuleMediator.setFindRule(null);
+        searchRuleMediator.setFindRule(null);
         tableModel.updateEventsWithFindRule(null);
+        searchModel.updateEventsWithFindRule(null);
         currentSearchMatchCount = 0;
         statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
+        //if the preference to show search results is enabled, the find rule is now null - hide search results
+        if (isSearchResultsVisible()) {
+          hideSearchResults();
+        }
+      }
+    }
+  }
+
+  private void hideSearchResults() {
+    if (searchResultsDisplayed) {
+      detailPanel.removeAll();
+      detailPanel.add(detailToolbar, BorderLayout.NORTH);
+      detailPanel.add(detailPane, BorderLayout.CENTER);
+      detailPanel.invalidate();
+      detailPanel.revalidate();
+      detailPanel.repaint();
+      //if the detail visible pref is not enabled, hide the detail pane
+      searchResultsDisplayed = false;
+      //hide if pref is not enabled
+      if (!isDetailVisible()) {
+        hideDetailPane();
+      }
+    }
+  }
+
+  private void showSearchResults() {
+    if (isSearchResultsVisible() && !searchResultsDisplayed && findRule != null) {
+      //if pref is set, always update detail panel to contain search results
+      detailPanel.removeAll();
+      detailPanel.add(searchPane, BorderLayout.CENTER);
+      detailPanel.invalidate();
+      detailPanel.revalidate();
+      detailPanel.repaint();
+      //if the detail visible pref is not enabled, show the detail pane
+      searchResultsDisplayed = true;
+      //show if pref is not enabled
+      if (!isDetailVisible()) {
+        showDetailPane();
       }
     }
   }
@@ -2294,7 +2509,8 @@ public class LogPanel extends DockablePa
           lowerPanel.setDividerLocation(lastDetailPanelSplitLocation);
       }
       detailPanel.setVisible(true);
-    lowerPanel.repaint();
+      detailPanel.repaint();
+      lowerPanel.repaint();
   }
 
   /**
@@ -2623,6 +2839,7 @@ public class LogPanel extends DockablePa
   private void clearModel() {
     previousLastIndex = -1;
     tableModel.clearModel();
+    searchModel.clearModel();
 
     synchronized (detail) {
       detailPaneUpdater.setSelectedRow(-1);
@@ -2681,6 +2898,8 @@ public class LogPanel extends DockablePa
                 } catch (IllegalArgumentException iae) {
                   findField.setToolTipText(iae.getMessage());
                   colorizer.setFindRule(null);
+                  tableRuleMediator.setFindRule(null);
+                  searchRuleMediator.setFindRule(null);
                 }
             }
         });
@@ -2751,13 +2970,19 @@ public class LogPanel extends DockablePa
     String columnOrder = event.getSetting(TABLE_COLUMN_ORDER);
 
     TableColumnModel columnModel = table.getColumnModel();
+    TableColumnModel searchColumnModel = searchTable.getColumnModel();
 
     Map columnNameMap = new HashMap();
+    Map searchColumnNameMap = new HashMap();
 
     for (int i = 0; i < columnModel.getColumnCount(); i++) {
       columnNameMap.put(table.getColumnName(i).toUpperCase(), columnModel.getColumn(i));
     }
 
+    for (int i = 0; i < searchColumnModel.getColumnCount(); i++) {
+      searchColumnNameMap.put(searchTable.getColumnName(i).toUpperCase(), searchColumnModel.getColumn(i));
+    }
+
     int index = 0;
     StringTokenizer tok = new StringTokenizer(columnOrder, ",");
     List sortedColumnList = new ArrayList();
@@ -2774,17 +2999,20 @@ public class LogPanel extends DockablePa
       if (column != null) {
         sortedColumnList.add(column);
         table.removeColumn(column);
+        searchTable.removeColumn(column);
       }
     }
     preferenceModel.setDetailPaneVisible(event.asBoolean("detailPaneVisible"));
     preferenceModel.setLogTreePanelVisible(event.asBoolean("logTreePanelVisible"));
     preferenceModel.setHighlightSearchMatchText(event.asBoolean("highlightSearchMatchText"));
     preferenceModel.setWrapMessage(event.asBoolean("wrapMessage"));
+    preferenceModel.setSearchResultsVisible(event.asBoolean("searchResultsVisible"));
     //re-add columns to the table in the order provided from the list
     for (Iterator iter = sortedColumnList.iterator(); iter.hasNext();) {
       TableColumn element = (TableColumn) iter.next();
       if (preferenceModel.addColumn(element)) {
           table.addColumn(element);
+          searchTable.addColumn(element);
     	  preferenceModel.setColumnVisible(element.getHeaderValue().toString(), true);
       }
     }
@@ -2806,6 +3034,7 @@ public class LogPanel extends DockablePa
             + ", width " + element);
         } else {
           columnModel.getColumn(index).setPreferredWidth(width);
+          searchColumnModel.getColumn(index).setPreferredWidth(width);
         }
 
         index++;
@@ -2850,9 +3079,9 @@ public class LogPanel extends DockablePa
     return longestWidth + 5;
   }
 
-  private String getToolTipTextForEvent(ExtendedLoggingEvent event) {
+  private String getToolTipTextForEvent(LoggingEventWrapper loggingEventWrapper) {
     StringBuffer buf = new StringBuffer();
-    buf.append(detailLayout.getHeader()).append(detailLayout.format(event)).append(detailLayout.getFooter());
+    buf.append(detailLayout.getHeader()).append(detailLayout.format(loggingEventWrapper.getLoggingEvent())).append(detailLayout.getFooter());
     return buf.toString();
   }
 
@@ -2907,19 +3136,20 @@ public class LogPanel extends DockablePa
     }
 
     public void clearAllMarkers() {
-        tableModel.removePropertyFromEvents(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
+      //this will get the properties to be removed from both tables..but
+      tableModel.removePropertyFromEvents(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
     }
 
     public void toggleMarker() {
         int row = table.getSelectedRow();
         if (row != -1) {
-          LoggingEvent event = tableModel.getRow(row);
-          if (event != null) {
-              Object marker = event.getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
+          LoggingEventWrapper loggingEventWrapper = tableModel.getRow(row);
+          if (loggingEventWrapper != null) {
+              Object marker = loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
               if (marker == null) {
-                  event.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, "set");
+                  loggingEventWrapper.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, "set");
               } else {
-                  event.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
+                  loggingEventWrapper.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
               }
               //if marker -was- null, it no longer is (may need to add the column)
               tableModel.fireRowUpdated(row, (marker == null));
@@ -2936,7 +3166,7 @@ public class LogPanel extends DockablePa
         }
     }
 
-    /**
+  /**
    * This class receives notification when the Refine focus text field is
    * updated, where a backgrounh thread periodically wakes up and checks if
    * they have stopped typing yet. This ensures that the filtering of the
@@ -2951,7 +3181,7 @@ public class LogPanel extends DockablePa
     private long lastTimeStamp = System.currentTimeMillis();
     private final Thread delayThread;
     private final String defaultToolTip;
-    private String lastFilterText = null;
+    private String lastFilterText = "";
 
     private DelayedFilterTextDocumentListener(final JTextField filterText) {
       super();
@@ -2978,7 +3208,7 @@ public class LogPanel extends DockablePa
                   // they stopped typing recently, but have stopped for at least
                   // 1 sample period. lets apply the filter
                   //                logger.debug("Typed something recently applying filter");
-                  if (!(filterText.getText().equals(lastFilterText))) {
+                  if (!(filterText.getText().trim().equals(lastFilterText.trim()))) {
                     lastFilterText = filterText.getText();
                     EventQueue.invokeLater(new Runnable()
                     {
@@ -3042,12 +3272,13 @@ public class LogPanel extends DockablePa
       if (filterText.getText().trim().equals("")) {
         //reset background color in case we were previously an invalid expression
         filterText.setBackground(UIManager.getColor("TextField.background"));
-        ruleMediator.setRefinementRule(null);
+        tableRuleMediator.setFilterRule(null);
+        searchRuleMediator.setFilterRule(null);
         filterText.setToolTipText(defaultToolTip);
       } else {
         try {
-          ruleMediator.setRefinementRule(
-            ExpressionRule.getRule(filterText.getText()));
+          tableRuleMediator.setFilterRule(ExpressionRule.getRule(filterText.getText()));
+          searchRuleMediator.setFilterRule(ExpressionRule.getRule(filterText.getText()));
           filterText.setToolTipText(defaultToolTip);
           //valid expression, reset background color in case we were previously an invalid expression
           filterText.setBackground(UIManager.getColor("TextField.background"));
@@ -3061,20 +3292,31 @@ public class LogPanel extends DockablePa
   }
 
   private final class TableMarkerListener extends MouseAdapter {
+    private JTable markerTable;
+    private EventContainer markerEventContainer;
+    private EventContainer otherMarkerEventContainer;
+
+    private TableMarkerListener(JTable markerTable, EventContainer markerEventContainer, EventContainer otherMarkerEventContainer) {
+      this.markerTable = markerTable;
+      this.markerEventContainer = markerEventContainer;
+      this.otherMarkerEventContainer = otherMarkerEventContainer;
+    }
+    
       public void mouseClicked(MouseEvent evt) {
           if (evt.getClickCount() == 2) {
-              int row = table.rowAtPoint(evt.getPoint());
+              int row = markerTable.rowAtPoint(evt.getPoint());
               if (row != -1) {
-                ExtendedLoggingEvent event = tableModel.getRow(row);
-                if (event != null) {
-                    Object marker = event.getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
+                LoggingEventWrapper loggingEventWrapper = markerEventContainer.getRow(row);
+                if (loggingEventWrapper != null) {
+                    Object marker = loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
                     if (marker == null) {
-                        event.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, "set");
+                        loggingEventWrapper.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, "set");
                     } else {
-                        event.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
+                        loggingEventWrapper.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
                     }
                     //if marker -was- null, it no longer is (may need to add the column)
-                    tableModel.fireRowUpdated(row, (marker == null));
+                    markerEventContainer.fireRowUpdated(row, (marker == null));
+                    otherMarkerEventContainer.fireRowUpdated(otherMarkerEventContainer.getRowIndex(loggingEventWrapper), (marker == null));
                 }
               }
           }
@@ -3086,8 +3328,12 @@ public class LogPanel extends DockablePa
    */
   private final class TableColumnDetailMouseListener extends MouseMotionAdapter {
     private int currentRow = -1;
+    private JTable detailTable;
+    private EventContainer detailEventContainer;
 
-    private TableColumnDetailMouseListener() {
+    private TableColumnDetailMouseListener(JTable detailTable, EventContainer detailEventContainer) {
+      this.detailTable = detailTable;
+      this.detailEventContainer = detailEventContainer;
     }
 
     /**
@@ -3097,9 +3343,10 @@ public class LogPanel extends DockablePa
      */
     public void mouseMoved(MouseEvent evt) {
       currentPoint = evt.getPoint();
+      currentTable = detailTable;
 
       if (preferenceModel.isToolTips()) {
-        int row = table.rowAtPoint(evt.getPoint());
+        int row = detailTable.rowAtPoint(evt.getPoint());
 
         if ((row == currentRow) || (row == -1)) {
           return;
@@ -3107,41 +3354,29 @@ public class LogPanel extends DockablePa
 
         currentRow = row;
 
-        ExtendedLoggingEvent event = tableModel.getRow(currentRow);
+        LoggingEventWrapper event = detailEventContainer.getRow(currentRow);
 
         if (event != null) {
           String toolTipText = getToolTipTextForEvent(event);
-          table.setToolTipText(toolTipText);
+          detailTable.setToolTipText(toolTipText);
         }
       } else {
-        table.setToolTipText(null);
+        detailTable.setToolTipText(null);
       }
     }
   }
 
   //if columnmoved or columnremoved callback received, re-apply table's sort index based
   //sort column name
-  private class ChainsawTableColumnModelListener
-    implements TableColumnModelListener {
-    private ChainsawTableColumnModelListener() {
+  private class ChainsawTableColumnModelListener implements TableColumnModelListener {
+    private JSortTable modelListenerTable;
+
+    private ChainsawTableColumnModelListener(JSortTable modelListenerTable) {
+      this.modelListenerTable = modelListenerTable;
     }
 
-    /**
-     * If a new column was added to the display and that column was the marker column,
-     * set the cell editor to the markerCellEditor
-     *
-     * @param e
-     */
     public void columnAdded(TableColumnModelEvent e) {
-      Enumeration enumeration = table.getColumnModel().getColumns();
-
-      while (enumeration.hasMoreElements()) {
-        TableColumn column = (TableColumn) enumeration.nextElement();
-
-        if (column.getHeaderValue().toString().toLowerCase().equals(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE)) {
-          column.setCellEditor(markerCellEditor);
-        }
-      }
+      //no-op
     }
 
     /**
@@ -3150,7 +3385,7 @@ public class LogPanel extends DockablePa
      * @param e
      */
     public void columnRemoved(TableColumnModelEvent e) {
-      table.updateSortedColumn();
+      modelListenerTable.updateSortedColumn();
     }
 
     /**
@@ -3159,7 +3394,7 @@ public class LogPanel extends DockablePa
      * @param e
      */
     public void columnMoved(TableColumnModelEvent e) {
-      table.updateSortedColumn();
+      modelListenerTable.updateSortedColumn();
     }
 
     /**
@@ -3220,14 +3455,14 @@ public class LogPanel extends DockablePa
         return;
       }
 
-	      ExtendedLoggingEvent event = null;
+	      LoggingEventWrapper loggingEventWrapper = null;
 	      if (force || (selectedRow != -1 && (lastRow != selectedRow))) {
-	        event = tableModel.getRow(selectedRow);
+	        loggingEventWrapper = tableModel.getRow(selectedRow);
 	
-	        if (event != null) {
+	        if (loggingEventWrapper != null) {
 	          final StringBuffer buf = new StringBuffer();
 	          buf.append(detailLayout.getHeader())
-	             .append(detailLayout.format(event)).append(
+	             .append(detailLayout.format(loggingEventWrapper.getLoggingEvent())).append(
 	            detailLayout.getFooter());
 	          if (buf.length() > 0) {
 		          	try {
@@ -3247,7 +3482,7 @@ public class LogPanel extends DockablePa
 	        }
 	      }
 	
-	      if (event == null && (lastRow != selectedRow)) {
+	      if (loggingEventWrapper == null && (lastRow != selectedRow)) {
           	try {
           		final Document doc = detail.getEditorKit().createDefaultDocument();
           		detail.getEditorKit().read(new StringReader("<html>Nothing selected</html>"), doc, 0);
@@ -3277,12 +3512,63 @@ public class LogPanel extends DockablePa
         });
     }
   }
+    private class ThrowableDisplayMouseAdapter extends MouseAdapter {
+      private JTable throwableTable;
+      private EventContainer throwableEventContainer;
+      final JDialog detailDialog;
+      final JEditorPane detailArea;
+      public ThrowableDisplayMouseAdapter(JTable throwableTable, EventContainer throwableEventContainer) {
+        this.throwableTable = throwableTable;
+        this.throwableEventContainer = throwableEventContainer;
+
+        detailDialog = new JDialog((JFrame) null, true);
+        Container container = detailDialog.getContentPane();
+        detailArea = new JEditorPane();
+        JTextComponentFormatter.applySystemFontAndSize(detailArea);
+        detailArea.setEditable(false);
+        Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
+        detailArea.setPreferredSize(new Dimension(screenDimension.width / 2, screenDimension.height / 2));
+        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
+        container.add(new JScrollPane(detailArea));
+
+        detailDialog.pack();
+      }
+        public void mouseClicked(MouseEvent e)
+        {
+            TableColumn column = throwableTable.getColumnModel().getColumn(throwableTable.columnAtPoint(e.getPoint()));
+            if (!column.getHeaderValue().toString().toUpperCase().equals(ChainsawColumns.getColumnName(ChainsawColumns.INDEX_THROWABLE_COL_NAME))) {
+                return;
+            }
+
+            LoggingEventWrapper loggingEventWrapper = throwableEventContainer.getRow(throwableTable.getSelectedRow());
+
+            //throwable string representation may be a length-one empty array
+            String[] ti = loggingEventWrapper.getLoggingEvent().getThrowableStrRep();
+            if (ti != null && ti.length > 0 && (!(ti.length == 1 && ti[0].equals("")))) {
+                 detailDialog.setTitle(throwableTable.getColumnName(throwableTable.getSelectedColumn()) + " detail...");
+                  StringBuffer buf = new StringBuffer();
+                  buf.append(loggingEventWrapper.getLoggingEvent().getMessage());
+                  buf.append("\n");
+                  for (int i = 0; i < ti.length; i++) {
+                    buf.append(ti[i]).append("\n    ");
+                  }
+
+                  detailArea.setText(buf.toString());
+                  SwingHelper.invokeOnEDT(new Runnable() {
+                    public void run() {
+                      centerAndSetVisible(detailDialog);
+                    }
+                  });
+                }
+        }
+    }
 
     private class MarkerCellEditor implements TableCellEditor {
-        JTextField textField = new JTextField();
-        Set cellEditorListeners = new HashSet();
-        private ExtendedLoggingEvent currentEvent;
-        private final Object mutex = new Object();
+      JTable currentTable;
+      JTextField textField = new JTextField();
+      Set cellEditorListeners = new HashSet();
+      private LoggingEventWrapper currentLoggingEventWrapper;
+      private final Object mutex = new Object();
 
         public Object getCellEditorValue()
         {
@@ -3303,12 +3589,18 @@ public class LogPanel extends DockablePa
         public boolean stopCellEditing()
         {
             if (textField.getText().trim().equals("")) {
-                currentEvent.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
+                currentLoggingEventWrapper.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
             } else {
-                currentEvent.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, textField.getText());
+                currentLoggingEventWrapper.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, textField.getText());
+            }
+            //row should always exist in the main table if it is being edited
+            tableModel.fireRowUpdated(tableModel.getRowIndex(currentLoggingEventWrapper), true);
+            int index = searchModel.getRowIndex(currentLoggingEventWrapper);
+            if (index > -1) {
+              searchModel.fireRowUpdated(index, true);
             }
-            tableModel.fireRowUpdated(table.getSelectedRow(), true);
-            ChangeEvent event = new ChangeEvent(table);
+
+            ChangeEvent event = new ChangeEvent(currentTable);
             Set cellEditorListenersCopy;
             synchronized(mutex) {
                 cellEditorListenersCopy = new HashSet(cellEditorListeners);
@@ -3317,6 +3609,9 @@ public class LogPanel extends DockablePa
             for (Iterator iter = cellEditorListenersCopy.iterator();iter.hasNext();) {
                 ((CellEditorListener)iter.next()).editingStopped(event);
             }
+            currentLoggingEventWrapper = null;
+            currentTable = null;
+
             return true;
         }
 
@@ -3327,10 +3622,12 @@ public class LogPanel extends DockablePa
                 cellEditorListenersCopy = new HashSet(cellEditorListeners);
             }
 
-           ChangeEvent event = new ChangeEvent(table);
+           ChangeEvent event = new ChangeEvent(currentTable);
            for (Iterator iter = cellEditorListenersCopy.iterator();iter.hasNext();) {
                ((CellEditorListener)iter.next()).editingCanceled(event);
            }
+          currentLoggingEventWrapper = null;
+          currentTable = null;
         }
 
         public void addCellEditorListener(CellEditorListener l)
@@ -3349,11 +3646,15 @@ public class LogPanel extends DockablePa
 
         public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
         {
-            currentEvent = tableModel.getRow(row);
-            if (currentEvent != null) {
-                textField.setText(currentEvent.getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE));
+          currentTable = table;
+          currentLoggingEventWrapper =((EventContainer) table.getModel()).getRow(row);
+            if (currentLoggingEventWrapper != null) {
+                textField.setText(currentLoggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE));
                 textField.selectAll();
             }
+            else {
+              textField.setText("");
+            }
             return textField;
         }
     }
@@ -3364,8 +3665,8 @@ public class LogPanel extends DockablePa
             initializeLists();
         }
 
-        boolean primaryMatches(EventWrapper wrapper) {
-            String millisDelta = wrapper.loggingEvent.getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
+        boolean primaryMatches(ThumbnailLoggingEventWrapper wrapper) {
+            String millisDelta = wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
             if (millisDelta != null && !millisDelta.trim().equals("")) {
                 long millisDeltaLong = Long.parseLong(millisDelta);
                 //arbitrary
@@ -3374,7 +3675,7 @@ public class LogPanel extends DockablePa
             return false;
         }
 
-        boolean secondaryMatches(EventWrapper wrapper) {
+        boolean secondaryMatches(ThumbnailLoggingEventWrapper wrapper) {
             //secondary is not used
             return false;
         }
@@ -3385,8 +3686,8 @@ public class LogPanel extends DockablePa
 
             int i=0;
             for (Iterator iter = tableModel.getFilteredEvents().iterator();iter.hasNext();) {
-                ExtendedLoggingEvent extendedLoggingEvent = (ExtendedLoggingEvent) iter.next();
-                EventWrapper wrapper = new EventWrapper(i, extendedLoggingEvent);
+                LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) iter.next();
+                ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, loggingEventWrapper);
                 i++;
                 //only add if there is a color defined
                 if (primaryMatches(wrapper)) {
@@ -3418,7 +3719,7 @@ public class LogPanel extends DockablePa
 
             //draw all events
             for (Iterator iter = primaryList.iterator();iter.hasNext();) {
-                EventWrapper wrapper = (EventWrapper)iter.next();
+                ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper)iter.next();
                     if (primaryMatches(wrapper)) {
                         float ratio = (wrapper.rowNum / (float)rowCount);
         //                System.out.println("error - ratio: " + ratio + ", component height: " + componentHeight);
@@ -3427,7 +3728,7 @@ public class LogPanel extends DockablePa
                         int startX = 1;
                         int width = getWidth() - (startX * 2);
                         //max out at 50, min 2...
-                        String millisDelta = wrapper.loggingEvent.getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
+                        String millisDelta = wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
                         long millisDeltaLong = Long.parseLong(millisDelta);
                         long delta = Math.min(ChainsawConstants.MILLIS_DELTA_RENDERING_HEIGHT_MAX, Math.max(0, (long) (millisDeltaLong * ChainsawConstants.MILLIS_DELTA_RENDERING_FACTOR)));
                         float widthMaxMillisDeltaRenderRatio = ((float)width / ChainsawConstants.MILLIS_DELTA_RENDERING_HEIGHT_MAX);
@@ -3452,12 +3753,12 @@ public class LogPanel extends DockablePa
             });
         }
 
-        boolean primaryMatches(EventWrapper wrapper) {
-            return !wrapper.loggingEvent.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND);
+        boolean primaryMatches(ThumbnailLoggingEventWrapper wrapper) {
+            return !wrapper.loggingEventWrapper.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND);
         }
 
-        boolean secondaryMatches(EventWrapper wrapper) {
-            return wrapper.loggingEvent.isSearchMatch();
+        boolean secondaryMatches(ThumbnailLoggingEventWrapper wrapper) {
+            return wrapper.loggingEventWrapper.isSearchMatch();
         }
 
         private void configureColors() {
@@ -3466,9 +3767,9 @@ public class LogPanel extends DockablePa
 
             int i=0;
             for (Iterator iter = tableModel.getFilteredEvents().iterator();iter.hasNext();) {
-                ExtendedLoggingEvent extendedLoggingEvent = (ExtendedLoggingEvent) iter.next();
-                extendedLoggingEvent.updateColorRuleColors(colorizer.getBackgroundColor(extendedLoggingEvent), colorizer.getForegroundColor(extendedLoggingEvent));
-                EventWrapper wrapper = new EventWrapper(i, extendedLoggingEvent);
+                LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) iter.next();
+                loggingEventWrapper.updateColorRuleColors(colorizer.getBackgroundColor(loggingEventWrapper.getLoggingEvent()), colorizer.getForegroundColor(loggingEventWrapper.getLoggingEvent()));
+                ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, loggingEventWrapper);
                 if (secondaryMatches(wrapper)) {
                     secondaryList.add(wrapper);
                 }
@@ -3503,9 +3804,9 @@ public class LogPanel extends DockablePa
 
             //draw all non error/warning/marker events
             for (Iterator iter = primaryList.iterator();iter.hasNext();) {
-                EventWrapper wrapper = (EventWrapper)iter.next();
-                if (!wrapper.loggingEvent.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND)) {
-                    if (wrapper.loggingEvent.getLevel().toInt() < Level.WARN.toInt() && wrapper.loggingEvent.getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE) == null) {
+                ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper)iter.next();
+                if (!wrapper.loggingEventWrapper.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND)) {
+                    if (wrapper.loggingEventWrapper.getLoggingEvent().getLevel().toInt() < Level.WARN.toInt() && wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE) == null) {
                         float ratio = (wrapper.rowNum / (float)rowCount);
         //                System.out.println("error - ratio: " + ratio + ", component height: " + componentHeight);
                         int verticalLocation = (int) (componentHeight * ratio) + topOffset;
@@ -3513,7 +3814,7 @@ public class LogPanel extends DockablePa
                         int startX = 1;
                         int width = getWidth() - (startX * 2);
 
-                        drawEvent(wrapper.loggingEvent.getColorRuleBackground(), verticalLocation, eventHeight, g, startX, width);
+                        drawEvent(wrapper.loggingEventWrapper.getColorRuleBackground(), verticalLocation, eventHeight, g, startX, width);
         //                System.out.println("painting error - rownum: " + wrapper.rowNum + ", location: " + verticalLocation + ", height: " + eventHeight + ", component height: " + componentHeight + ", row count: " + rowCount);
                     }
                 }
@@ -3521,9 +3822,9 @@ public class LogPanel extends DockablePa
 
             //draw warnings, error, fatal & markers last (full width)
             for (Iterator iter = primaryList.iterator();iter.hasNext();) {
-                EventWrapper wrapper = (EventWrapper)iter.next();
-                if (!wrapper.loggingEvent.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND)) {
-                    if (wrapper.loggingEvent.getLevel().toInt() >= Level.WARN.toInt() || wrapper.loggingEvent.getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE) != null) {
+                ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper)iter.next();
+                if (!wrapper.loggingEventWrapper.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND)) {
+                    if (wrapper.loggingEventWrapper.getLoggingEvent().getLevel().toInt() >= Level.WARN.toInt() || wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE) != null) {
                         float ratio = (wrapper.rowNum / (float)rowCount);
         //                System.out.println("error - ratio: " + ratio + ", component height: " + componentHeight);
                         int verticalLocation = (int) (componentHeight * ratio) + topOffset;
@@ -3536,14 +3837,14 @@ public class LogPanel extends DockablePa
                         eventHeight = Math.min(maxEventHeight, eventHeight + 3);
 //                            eventHeight = maxEventHeight;
 
-                        drawEvent(wrapper.loggingEvent.getColorRuleBackground(), (verticalLocation - eventHeight + 1), eventHeight, g, startX, width);
+                        drawEvent(wrapper.loggingEventWrapper.getColorRuleBackground(), (verticalLocation - eventHeight + 1), eventHeight, g, startX, width);
     //                System.out.println("painting error - rownum: " + wrapper.rowNum + ", location: " + verticalLocation + ", height: " + eventHeight + ", component height: " + componentHeight + ", row count: " + rowCount);
                     }
                 }
             }
 
             for (Iterator iter = secondaryList.iterator();iter.hasNext();) {
-                EventWrapper wrapper = (EventWrapper)iter.next();
+                ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper)iter.next();
                 float ratio = (wrapper.rowNum / (float)rowCount);
 //                System.out.println("warning - ratio: " + ratio + ", component height: " + componentHeight);
                 int verticalLocation = (int) (componentHeight * ratio) + topOffset;
@@ -3570,9 +3871,9 @@ public class LogPanel extends DockablePa
               public void mouseMoved(MouseEvent e) {
                 if (preferenceModel.isThumbnailBarToolTips()) {
                     int yPosition = e.getPoint().y;
-                    EventWrapper event = getEventWrapperAtPosition(yPosition);
+                    ThumbnailLoggingEventWrapper event = getEventWrapperAtPosition(yPosition);
                     if (event != null) {
-                        setToolTipText(getToolTipTextForEvent(event.loggingEvent));
+                        setToolTipText(getToolTipTextForEvent(event.loggingEventWrapper));
                     }
                 } else {
                     setToolTipText(null);
@@ -3584,10 +3885,10 @@ public class LogPanel extends DockablePa
                 public void mouseClicked(MouseEvent e)
                 {
                     int yPosition = e.getPoint().y;
-                    EventWrapper event = getEventWrapperAtPosition(yPosition);
+                    ThumbnailLoggingEventWrapper event = getEventWrapperAtPosition(yPosition);
 //                    System.out.println("rowToSelect: " + rowToSelect + ", closestRow: " + event.loggingEvent.getProperty("log4jid"));
                     if (event != null) {
-                        int id = new Integer(event.loggingEvent.getProperty("log4jid")).intValue();
+                        int id = Integer.parseInt(event.loggingEventWrapper.getLoggingEvent().getProperty("log4jid"));
                         setSelectedEvent(id);
                     }
                 }
@@ -3610,8 +3911,8 @@ public class LogPanel extends DockablePa
                     if (e.getType() == TableModelEvent.INSERT) {
 //                        System.out.println("insert - current warnings: " + warnings.size() + ", errors: " + errors.size() + ", first row: " + firstRow + ", last row: " + lastRow);
                         for (int i=firstRow;i<lastRow;i++) {
-                            ExtendedLoggingEvent event = (ExtendedLoggingEvent)displayedEvents.get(i);
-                            EventWrapper wrapper = new EventWrapper(i, event);
+                            LoggingEventWrapper event = (LoggingEventWrapper)displayedEvents.get(i);
+                            ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, event);
                             if (secondaryMatches(wrapper)) {
                                 secondaryList.add(wrapper);
 //                                System.out.println("added warning: " + i + " - " + event.getLevel());
@@ -3629,14 +3930,14 @@ public class LogPanel extends DockablePa
                         //find each eventwrapper with an id in the deleted range and remove it...
 //                        System.out.println("delete- current warnings: " + warnings.size() + ", errors: " + errors.size() + ", first row: " + firstRow + ", last row: " + lastRow + ", displayed event count: " + displayedEvents.size() );
                         for (Iterator iter = secondaryList.iterator();iter.hasNext();) {
-                            EventWrapper wrapper = (EventWrapper)iter.next();
+                            ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper)iter.next();
                             if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
 //                                System.out.println("deleting find: " + wrapper);
                                 iter.remove();
                             }
                         }
                         for (Iterator iter = primaryList.iterator();iter.hasNext();) {
-                            EventWrapper wrapper = (EventWrapper)iter.next();
+                            ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper)iter.next();
                             if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
 //                                System.out.println("deleting error: " + wrapper);
                                 iter.remove();
@@ -3649,14 +3950,14 @@ public class LogPanel extends DockablePa
 //                        System.out.println("update - about to delete old warnings in range: " + firstRow + " to " + lastRow + ", current warnings: " + warnings.size() + ", errors: " + errors.size());
                         //find each eventwrapper with an id in the deleted range and remove it...
                         for (Iterator iter = secondaryList.iterator();iter.hasNext();) {
-                            EventWrapper wrapper = (EventWrapper)iter.next();

[... 132 lines stripped ...]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.