1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.chainsaw;
19
20 import com.thoughtworks.xstream.XStream;
21 import com.thoughtworks.xstream.io.xml.DomDriver;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.LogManager;
24 import org.apache.log4j.Logger;
25 import org.apache.log4j.PatternLayout;
26 import org.apache.log4j.chainsaw.color.ColorPanel;
27 import org.apache.log4j.chainsaw.color.RuleColorizer;
28 import org.apache.log4j.chainsaw.filter.FilterModel;
29 import org.apache.log4j.chainsaw.helper.SwingHelper;
30 import org.apache.log4j.chainsaw.icons.ChainsawIcons;
31 import org.apache.log4j.chainsaw.icons.LineIconFactory;
32 import org.apache.log4j.chainsaw.layout.DefaultLayoutFactory;
33 import org.apache.log4j.chainsaw.layout.EventDetailLayout;
34 import org.apache.log4j.chainsaw.layout.LayoutEditorPane;
35 import org.apache.log4j.chainsaw.messages.MessageCenter;
36 import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent;
37 import org.apache.log4j.chainsaw.prefs.Profileable;
38 import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent;
39 import org.apache.log4j.chainsaw.prefs.SettingsManager;
40 import org.apache.log4j.chainsaw.xstream.TableColumnConverter;
41 import org.apache.log4j.helpers.Constants;
42 import org.apache.log4j.rule.ColorRule;
43 import org.apache.log4j.rule.ExpressionRule;
44 import org.apache.log4j.rule.Rule;
45 import org.apache.log4j.spi.LoggingEvent;
46 import org.apache.log4j.spi.LoggingEventFieldResolver;
47
48 import javax.swing.*;
49 import javax.swing.event.*;
50 import javax.swing.table.TableCellEditor;
51 import javax.swing.table.TableColumn;
52 import javax.swing.table.TableColumnModel;
53 import javax.swing.text.Document;
54 import java.awt.*;
55 import java.awt.datatransfer.Clipboard;
56 import java.awt.datatransfer.StringSelection;
57 import java.awt.event.*;
58 import java.beans.PropertyChangeEvent;
59 import java.beans.PropertyChangeListener;
60 import java.io.*;
61 import java.net.URLEncoder;
62 import java.text.DateFormat;
63 import java.text.NumberFormat;
64 import java.text.SimpleDateFormat;
65 import java.util.*;
66 import java.util.List;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public class LogPanel extends DockablePanel implements EventBatchListener, Profileable {
119 private static final DateFormat TIMESTAMP_DATE_FORMAT = new SimpleDateFormat(Constants.TIMESTAMP_RULE_FORMAT);
120 private static final double DEFAULT_DETAIL_SPLIT_LOCATION = 0.71d;
121 private static final double DEFAULT_LOG_TREE_SPLIT_LOCATION = 0.2d;
122 private final String identifier;
123 private final ChainsawStatusBar statusBar;
124 private final JFrame logPanelPreferencesFrame = new JFrame();
125 private ColorPanel colorPanel;
126 private final JFrame colorFrame = new JFrame();
127 private final JFrame undockedFrame;
128 private final DockablePanel externalPanel;
129 private final Action dockingAction;
130 private final JToolBar undockedToolbar;
131 private final JSortTable table;
132 private final TableColorizingRenderer renderer;
133 private final EventContainer tableModel;
134 private final JEditorPane detail;
135 private final JSplitPane lowerPanel;
136 private final DetailPaneUpdater detailPaneUpdater;
137 private final JPanel detailPanel = new JPanel(new BorderLayout());
138 private final JSplitPane nameTreeAndMainPanelSplit;
139 private final LoggerNameTreePanel logTreePanel;
140 private final LogPanelPreferenceModel preferenceModel = new LogPanelPreferenceModel();
141 private ApplicationPreferenceModel applicationPreferenceModel;
142 private final LogPanelPreferencePanel logPanelPreferencesPanel;
143 private final FilterModel filterModel = new FilterModel();
144 private final RuleColorizer colorizer = new RuleColorizer();
145 private final RuleMediator tableRuleMediator = new RuleMediator(false);
146 private final RuleMediator searchRuleMediator = new RuleMediator(true);
147 private final EventDetailLayout detailLayout = new EventDetailLayout();
148 private double lastLogTreePanelSplitLocation = DEFAULT_LOG_TREE_SPLIT_LOCATION;
149 private Point currentPoint;
150 private JTable currentTable;
151 private boolean paused = false;
152 private Rule findRule;
153 private String currentFindRuleText;
154 private Rule findMarkerRule;
155 private final int dividerSize;
156 static final String TABLE_COLUMN_ORDER = "table.columns.order";
157 static final String TABLE_COLUMN_WIDTHS = "table.columns.widths";
158 static final String COLORS_EXTENSION = ".colors";
159 private static final int LOG_PANEL_SERIALIZATION_VERSION_NUMBER = 2;
160 private int previousLastIndex = -1;
161 private final Logger logger = LogManager.getLogger(LogPanel.class);
162 private AutoFilterComboBox filterCombo;
163 private AutoFilterComboBox findCombo;
164 private JScrollPane eventsPane;
165 private int currentSearchMatchCount;
166 private Rule clearTableExpressionRule;
167 private int lowerPanelDividerLocation;
168 private EventContainer searchModel;
169 private final JSortTable searchTable;
170 private TableColorizingRenderer searchRenderer;
171 private ToggleToolTips mainToggleToolTips;
172 private ToggleToolTips searchToggleToolTips;
173 private JScrollPane detailPane;
174 private JScrollPane searchPane;
175
176 private TableCellEditor markerCellEditor;
177 private JToolBar detailToolbar;
178 private boolean searchResultsDisplayed;
179 private ColorizedEventAndSearchMatchThumbnail colorizedEventAndSearchMatchThumbnail;
180 private EventTimeDeltaMatchThumbnail eventTimeDeltaMatchThumbnail;
181 private boolean isDetailPanelVisible;
182
183
184
185
186
187
188
189
190 public LogPanel(final ChainsawStatusBar statusBar, final String identifier, int cyclicBufferSize,
191 Map<String, RuleColorizer> allColorizers, final ApplicationPreferenceModel applicationPreferenceModel) {
192 this.identifier = identifier;
193 this.statusBar = statusBar;
194 this.applicationPreferenceModel = applicationPreferenceModel;
195 this.logPanelPreferencesPanel = new LogPanelPreferencePanel(preferenceModel, applicationPreferenceModel);
196 logger.debug("creating logpanel for " + identifier);
197
198 setLayout(new BorderLayout());
199
200 String prototypeValue = "1231231231231231231231";
201
202 filterCombo = new AutoFilterComboBox();
203 findCombo = new AutoFilterComboBox();
204
205 filterCombo.setPrototypeDisplayValue(prototypeValue);
206 buildCombo(filterCombo, true, findCombo.model);
207
208 findCombo.setPrototypeDisplayValue(prototypeValue);
209 buildCombo(findCombo, false, filterCombo.model);
210
211 final Map<Object, String> columnNameKeywordMap = new HashMap<>();
212 columnNameKeywordMap.put(ChainsawConstants.CLASS_COL_NAME, LoggingEventFieldResolver.CLASS_FIELD);
213 columnNameKeywordMap.put(ChainsawConstants.FILE_COL_NAME, LoggingEventFieldResolver.FILE_FIELD);
214 columnNameKeywordMap.put(ChainsawConstants.LEVEL_COL_NAME, LoggingEventFieldResolver.LEVEL_FIELD);
215 columnNameKeywordMap.put(ChainsawConstants.LINE_COL_NAME, LoggingEventFieldResolver.LINE_FIELD);
216 columnNameKeywordMap.put(ChainsawConstants.LOGGER_COL_NAME, LoggingEventFieldResolver.LOGGER_FIELD);
217 columnNameKeywordMap.put(ChainsawConstants.NDC_COL_NAME, LoggingEventFieldResolver.NDC_FIELD);
218 columnNameKeywordMap.put(ChainsawConstants.MESSAGE_COL_NAME, LoggingEventFieldResolver.MSG_FIELD);
219 columnNameKeywordMap.put(ChainsawConstants.THREAD_COL_NAME, LoggingEventFieldResolver.THREAD_FIELD);
220 columnNameKeywordMap.put(ChainsawConstants.THROWABLE_COL_NAME, LoggingEventFieldResolver.EXCEPTION_FIELD);
221 columnNameKeywordMap.put(ChainsawConstants.TIMESTAMP_COL_NAME, LoggingEventFieldResolver.TIMESTAMP_FIELD);
222 columnNameKeywordMap.put(ChainsawConstants.ID_COL_NAME.toUpperCase(), LoggingEventFieldResolver.PROP_FIELD + Constants.LOG4J_ID_KEY);
223 columnNameKeywordMap.put(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE.toUpperCase(), LoggingEventFieldResolver.PROP_FIELD + ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
224 columnNameKeywordMap.put(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE.toUpperCase(), LoggingEventFieldResolver.PROP_FIELD + ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
225
226 logPanelPreferencesFrame.setTitle("'" + identifier + "' Log Panel Preferences");
227 logPanelPreferencesFrame.setIconImage(
228 ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
229 logPanelPreferencesFrame.getContentPane().add(new JScrollPane(logPanelPreferencesPanel));
230
231 logPanelPreferencesFrame.setSize(740, 520);
232
233 logPanelPreferencesPanel.setOkCancelActionListener(
234 e -> logPanelPreferencesFrame.setVisible(false));
235
236 KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
237 Action closeLogPanelPreferencesFrameAction = new AbstractAction() {
238 public void actionPerformed(ActionEvent e) {
239 logPanelPreferencesFrame.setVisible(false);
240 }
241 };
242 logPanelPreferencesFrame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE");
243 logPanelPreferencesFrame.getRootPane().
244 getActionMap().put("ESCAPE", closeLogPanelPreferencesFrameAction);
245
246
247 setDetailPaneConversionPattern(
248 DefaultLayoutFactory.getDefaultPatternLayout());
249 detailLayout.setConversionPattern(
250 DefaultLayoutFactory.getDefaultPatternLayout());
251
252 undockedFrame = new JFrame(identifier);
253 undockedFrame.setDefaultCloseOperation(
254 WindowConstants.DO_NOTHING_ON_CLOSE);
255
256 if (ChainsawIcons.UNDOCKED_ICON != null) {
257 undockedFrame.setIconImage(
258 new ImageIcon(ChainsawIcons.UNDOCKED_ICON).getImage());
259 }
260
261 externalPanel = new DockablePanel();
262 externalPanel.setLayout(new BorderLayout());
263
264 undockedFrame.addWindowListener(
265 new WindowAdapter() {
266 public void windowClosing(WindowEvent e) {
267 dock();
268 }
269 });
270
271 undockedToolbar = createDockwindowToolbar();
272 externalPanel.add(undockedToolbar, BorderLayout.NORTH);
273 undockedFrame.getContentPane().add(externalPanel);
274 undockedFrame.setSize(new Dimension(1024, 768));
275 undockedFrame.pack();
276
277 preferenceModel.addPropertyChangeListener(
278 "scrollToBottom",
279 evt -> {
280 boolean value = (Boolean) evt.getNewValue();
281 if (value) {
282 scrollToBottom();
283 }
284 });
285
286
287
288
289
290
291
292
293 final JPopupMenu dateFormatChangePopup = new JPopupMenu();
294 final JRadioButtonMenuItem isoButton =
295 new JRadioButtonMenuItem(
296 new AbstractAction("Use ISO8601Format") {
297 public void actionPerformed(ActionEvent e) {
298 preferenceModel.setDateFormatPattern("ISO8601");
299 }
300 });
301 final JRadioButtonMenuItem simpleTimeButton =
302 new JRadioButtonMenuItem(
303 new AbstractAction("Use simple time") {
304 public void actionPerformed(ActionEvent e) {
305 preferenceModel.setDateFormatPattern("HH:mm:ss");
306 }
307 });
308
309 ButtonGroup dfBG = new ButtonGroup();
310 dfBG.add(isoButton);
311 dfBG.add(simpleTimeButton);
312 simpleTimeButton.setSelected(true);
313 dateFormatChangePopup.add(isoButton);
314 dateFormatChangePopup.add(simpleTimeButton);
315
316 final JCheckBoxMenuItem menuItemLoggerTree =
317 new JCheckBoxMenuItem("Show Logger Tree");
318 menuItemLoggerTree.addActionListener(
319 e -> preferenceModel.setLogTreePanelVisible(
320 menuItemLoggerTree.isSelected()));
321 menuItemLoggerTree.setIcon(new ImageIcon(ChainsawIcons.WINDOW_ICON));
322
323 final JCheckBoxMenuItem menuItemToggleDetails =
324 new JCheckBoxMenuItem("Show Detail Pane");
325 menuItemToggleDetails.addActionListener(
326 e -> preferenceModel.setDetailPaneVisible(
327 menuItemToggleDetails.isSelected()));
328
329 menuItemToggleDetails.setIcon(new ImageIcon(ChainsawIcons.INFO));
330
331
332
333
334 preferenceModel.addPropertyChangeListener("levelIcons",
335 new PropertyChangeListener() {
336 public void propertyChange(PropertyChangeEvent evt) {
337 boolean useIcons = (Boolean) evt.getNewValue();
338 renderer.setLevelUseIcons(useIcons);
339 table.tableChanged(new TableModelEvent(tableModel));
340 searchRenderer.setLevelUseIcons(useIcons);
341 searchTable.tableChanged(new TableModelEvent(searchModel));
342 }
343 });
344
345
346
347
348 preferenceModel.addPropertyChangeListener("wrapMessage",
349 new PropertyChangeListener() {
350 public void propertyChange(PropertyChangeEvent evt) {
351 boolean wrap = (Boolean) evt.getNewValue();
352 renderer.setWrapMessage(wrap);
353 table.tableChanged(new TableModelEvent(tableModel));
354 searchRenderer.setWrapMessage(wrap);
355 searchTable.tableChanged(new TableModelEvent(searchModel));
356 }
357 });
358
359 preferenceModel.addPropertyChangeListener("searchResultsVisible",
360 evt -> {
361 boolean displaySearchResultsInDetailsIfAvailable = (Boolean) evt.getNewValue();
362 if (displaySearchResultsInDetailsIfAvailable) {
363 showSearchResults();
364 } else {
365 hideSearchResults();
366 }
367 });
368
369 preferenceModel.addPropertyChangeListener("highlightSearchMatchText",
370 new PropertyChangeListener() {
371 public void propertyChange(PropertyChangeEvent evt) {
372 boolean highlightText = (Boolean) evt.getNewValue();
373 renderer.setHighlightSearchMatchText(highlightText);
374 table.tableChanged(new TableModelEvent(tableModel));
375 searchRenderer.setHighlightSearchMatchText(highlightText);
376 searchTable.tableChanged(new TableModelEvent(searchModel));
377 }
378 });
379
380 preferenceModel.addPropertyChangeListener(
381 "detailPaneVisible",
382 evt -> {
383 boolean detailPaneVisible = (Boolean) evt.getNewValue();
384
385 if (detailPaneVisible) {
386 showDetailPane();
387 } else {
388
389 if (!searchResultsDisplayed) {
390 hideDetailPane();
391 }
392 }
393 });
394
395 preferenceModel.addPropertyChangeListener(
396 "logTreePanelVisible",
397 evt -> {
398 boolean newValue = (Boolean) evt.getNewValue();
399
400 if (newValue) {
401 showLogTreePanel();
402 } else {
403 hideLogTreePanel();
404 }
405 });
406
407 preferenceModel.addPropertyChangeListener("toolTips",
408 new PropertyChangeListener() {
409 public void propertyChange(PropertyChangeEvent evt) {
410 boolean toolTips = (Boolean) evt.getNewValue();
411 renderer.setToolTipsVisible(toolTips);
412 searchRenderer.setToolTipsVisible(toolTips);
413 }
414 });
415
416 preferenceModel.addPropertyChangeListener("visibleColumns",
417 new PropertyChangeListener() {
418 public void propertyChange(PropertyChangeEvent evt) {
419
420 TableColumnModel columnModel = table.getColumnModel();
421 while (columnModel.getColumnCount() > 0) {
422 columnModel.removeColumn(columnModel.getColumn(0));
423 }
424 for (Object o1 : preferenceModel.getVisibleColumnOrder()) {
425 TableColumn c = (TableColumn) o1;
426 if (c.getHeaderValue().toString().equalsIgnoreCase(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE)) {
427 c.setCellEditor(markerCellEditor);
428 }
429 columnModel.addColumn(c);
430 }
431 TableColumnModel searchColumnModel = searchTable.getColumnModel();
432 while (searchColumnModel.getColumnCount() > 0) {
433 searchColumnModel.removeColumn(searchColumnModel.getColumn(0));
434 }
435 for (Object o : preferenceModel.getVisibleColumnOrder()) {
436 TableColumn c = (TableColumn) o;
437 searchColumnModel.addColumn(c);
438 }
439 }
440 });
441
442 PropertyChangeListener datePrefsChangeListener =
443 new PropertyChangeListener() {
444 public void propertyChange(PropertyChangeEvent evt) {
445 LogPanelPreferenceModel model = (LogPanelPreferenceModel) evt.getSource();
446
447 isoButton.setSelected(model.isUseISO8601Format());
448 simpleTimeButton.setSelected(!model.isUseISO8601Format() && !model.isCustomDateFormat());
449
450 if (model.getTimeZone() != null) {
451 renderer.setTimeZone(model.getTimeZone());
452 searchRenderer.setTimeZone(model.getTimeZone());
453 }
454
455 if (model.isUseISO8601Format()) {
456 renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
457 searchRenderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
458 } else {
459 try {
460 renderer.setDateFormatter(new SimpleDateFormat(model.getDateFormatPattern()));
461 } catch (IllegalArgumentException iae) {
462 model.setDefaultDatePatternFormat();
463 renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
464 }
465 try {
466 searchRenderer.setDateFormatter(new SimpleDateFormat(model.getDateFormatPattern()));
467 } catch (IllegalArgumentException iae) {
468 model.setDefaultDatePatternFormat();
469 searchRenderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
470 }
471 }
472
473 table.tableChanged(new TableModelEvent(tableModel));
474 searchTable.tableChanged(new TableModelEvent(searchModel));
475 }
476 };
477
478 preferenceModel.addPropertyChangeListener("dateFormatPattern", datePrefsChangeListener);
479 preferenceModel.addPropertyChangeListener("dateFormatTimeZone", datePrefsChangeListener);
480
481 preferenceModel.addPropertyChangeListener("clearTableExpression", evt -> {
482 LogPanelPreferenceModel model = (LogPanelPreferenceModel) evt.getSource();
483 String expression = model.getClearTableExpression();
484 try {
485 clearTableExpressionRule = ExpressionRule.getRule(expression);
486 logger.info("clearTableExpressionRule set to: " + expression);
487 } catch (Exception e) {
488 logger.info("clearTableExpressionRule invalid - ignoring: " + expression);
489 clearTableExpressionRule = null;
490 }
491 });
492
493 preferenceModel.addPropertyChangeListener("loggerPrecision",
494 new PropertyChangeListener() {
495 public void propertyChange(PropertyChangeEvent evt) {
496 LogPanelPreferenceModel model = (LogPanelPreferenceModel) evt.getSource();
497
498 renderer.setLoggerPrecision(model.getLoggerPrecision());
499 table.tableChanged(new TableModelEvent(tableModel));
500
501 searchRenderer.setLoggerPrecision(model.getLoggerPrecision());
502 searchTable.tableChanged(new TableModelEvent(searchModel));
503 }
504 });
505
506 preferenceModel.addPropertyChangeListener("toolTips",
507 evt -> {
508 boolean value = (Boolean) evt.getNewValue();
509 searchToggleToolTips.setSelected(value);
510 mainToggleToolTips.setSelected(value);
511 });
512
513 preferenceModel.addPropertyChangeListener(
514 "logTreePanelVisible",
515 evt -> {
516 boolean value = (Boolean) evt.getNewValue();
517 menuItemLoggerTree.setSelected(value);
518 });
519
520 preferenceModel.addPropertyChangeListener(
521 "detailPaneVisible",
522 evt -> {
523 boolean value = (Boolean) evt.getNewValue();
524 menuItemToggleDetails.setSelected(value);
525 });
526
527 applicationPreferenceModel.addPropertyChangeListener("searchColor", new PropertyChangeListener() {
528 public void propertyChange(PropertyChangeEvent evt) {
529 if (table != null) {
530 table.repaint();
531 }
532 if (searchTable != null) {
533 searchTable.repaint();
534 }
535 }
536 });
537
538 applicationPreferenceModel.addPropertyChangeListener("alternatingColor", new PropertyChangeListener() {
539 public void propertyChange(PropertyChangeEvent evt) {
540 if (table != null) {
541 table.repaint();
542 }
543 if (searchTable != null) {
544 searchTable.repaint();
545 }
546 }
547 });
548
549
550
551
552 tableModel = new ChainsawCyclicBufferTableModel(cyclicBufferSize, colorizer, "main");
553 table = new JSortTable(tableModel);
554
555 markerCellEditor = new MarkerCellEditor();
556 table.setName("main");
557 table.setColumnSelectionAllowed(false);
558 table.setRowSelectionAllowed(true);
559
560 searchModel = new ChainsawCyclicBufferTableModel(cyclicBufferSize, colorizer, "search");
561 searchTable = new JSortTable(searchModel);
562
563 searchTable.setName("search");
564 searchTable.setColumnSelectionAllowed(false);
565 searchTable.setRowSelectionAllowed(true);
566
567
568 table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("F2"), "none");
569 table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.SHIFT_MASK), "none");
570 table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "none");
571 table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | InputEvent.SHIFT_MASK), "none");
572
573
574 table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "none");
575
576 searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("F2"), "none");
577 searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.SHIFT_MASK), "none");
578 searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "none");
579 searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | InputEvent.SHIFT_MASK), "none");
580
581
582 searchTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "none");
583
584
585 tableModel.addNewKeyListener(e -> columnNameKeywordMap.put(e.getKey(), "PROP." + e.getKey()));
586
587
588
589
590
591 tableModel.setRuleMediator(tableRuleMediator);
592 searchModel.setRuleMediator(searchRuleMediator);
593
594 tableModel.addEventCountListener(
595 (currentCount, totalCount) -> {
596 if (LogPanel.this.isVisible()) {
597 statusBar.setSelectedLine(
598 table.getSelectedRow() + 1, currentCount, totalCount, getIdentifier());
599 }
600 });
601
602 tableModel.addEventCountListener(
603 new EventCountListener() {
604 final NumberFormat formatter = NumberFormat.getPercentInstance();
605 boolean warning75 = false;
606 boolean warning100 = false;
607
608 public void eventCountChanged(int currentCount, int totalCount) {
609 if (preferenceModel.isCyclic()) {
610 double percent =
611 ((double) totalCount) / tableModel.getMaxSize();
612 String msg;
613 boolean wasWarning = warning75 || warning100;
614 if ((percent > 0.75) && (percent < 1.0) && !warning75) {
615 msg =
616 "Warning :: " + formatter.format(percent) + " of the '"
617 + getIdentifier() + "' buffer has been used";
618 warning75 = true;
619 } else if ((percent >= 1.0) && !warning100) {
620 msg =
621 "Warning :: " + formatter.format(percent) + " of the '"
622 + getIdentifier()
623 + "' buffer has been used. Older events are being discarded.";
624 warning100 = true;
625 } else {
626
627 msg = "";
628 warning75 = false;
629 warning100 = false;
630 }
631
632 if (msg != null && wasWarning) {
633 MessageCenter.getInstance().getLogger().info(msg);
634 }
635 }
636 }
637 });
638
639
640
641
642
643 LogPanelLoggerTreeModel logTreeModel = new LogPanelLoggerTreeModel();
644 logTreePanel = new LoggerNameTreePanel(logTreeModel, preferenceModel, this, colorizer, filterModel);
645 logTreePanel.getLoggerVisibilityRule().addPropertyChangeListener(evt -> {
646 if (evt.getPropertyName().equals("searchExpression")) {
647 findCombo.setSelectedItem(evt.getNewValue().toString());
648 findNext();
649 }
650 });
651
652 tableModel.addLoggerNameListener(logTreeModel);
653 tableModel.addLoggerNameListener(logTreePanel);
654
655
656
657
658
659
660 tableRuleMediator.setLoggerRule(logTreePanel.getLoggerVisibilityRule());
661 searchRuleMediator.setLoggerRule(logTreePanel.getLoggerVisibilityRule());
662
663 colorizer.setLoggerRule(logTreePanel.getLoggerColorRule());
664
665
666
667
668 colorFrame.setTitle("'" + identifier + "' color settings");
669 colorFrame.setIconImage(
670 ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
671
672 allColorizers.put(identifier, colorizer);
673 colorPanel = new ColorPanel(colorizer, filterModel, allColorizers, applicationPreferenceModel);
674
675 colorFrame.getContentPane().add(colorPanel);
676
677 Action closeColorPanelAction = new AbstractAction() {
678 public void actionPerformed(ActionEvent e) {
679 colorPanel.hidePanel();
680 }
681 };
682 colorFrame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE");
683 colorFrame.getRootPane().
684 getActionMap().put("ESCAPE", closeColorPanelAction);
685
686 colorPanel.setCloseActionListener(
687 e -> colorFrame.setVisible(false));
688
689 colorizer.addPropertyChangeListener(
690 "colorrule",
691 new PropertyChangeListener() {
692 public void propertyChange(PropertyChangeEvent evt) {
693 for (Object o : tableModel.getAllEvents()) {
694 LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) o;
695 loggingEventWrapper.updateColorRuleColors(colorizer.getBackgroundColor(loggingEventWrapper.getLoggingEvent()), colorizer.getForegroundColor(loggingEventWrapper.getLoggingEvent()));
696 }
697
698
699
700
701
702
703
704 colorizedEventAndSearchMatchThumbnail.configureColors();
705 lowerPanel.revalidate();
706 lowerPanel.repaint();
707
708 searchTable.revalidate();
709 searchTable.repaint();
710 }
711 });
712
713
714
715
716 table.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
717 table.setRowMargin(0);
718 table.getColumnModel().setColumnMargin(0);
719 table.setShowGrid(false);
720 table.getColumnModel().addColumnModelListener(new ChainsawTableColumnModelListener(table));
721 table.setAutoCreateColumnsFromModel(false);
722 table.addMouseMotionListener(new TableColumnDetailMouseListener(table, tableModel));
723 table.addMouseListener(new TableMarkerListener(table, tableModel, searchModel));
724 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
725
726 searchTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
727 searchTable.setRowMargin(0);
728 searchTable.getColumnModel().setColumnMargin(0);
729 searchTable.setShowGrid(false);
730 searchTable.getColumnModel().addColumnModelListener(new ChainsawTableColumnModelListener(searchTable));
731 searchTable.setAutoCreateColumnsFromModel(false);
732 searchTable.addMouseMotionListener(new TableColumnDetailMouseListener(searchTable, searchModel));
733 searchTable.addMouseListener(new TableMarkerListener(searchTable, searchModel, tableModel));
734 searchTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
735
736
737
738 table.addKeyListener(
739 new KeyListener() {
740 public void keyTyped(KeyEvent e) {
741 }
742
743 public void keyPressed(KeyEvent e) {
744 synchronized (detail) {
745 table.getSelectionModel().setValueIsAdjusting(true);
746 detail.notify();
747 }
748 }
749
750 public void keyReleased(KeyEvent e) {
751 synchronized (detail) {
752 table.getSelectionModel().setValueIsAdjusting(false);
753 detail.notify();
754 }
755 }
756 });
757
758 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
759 searchTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
760
761 table.getSelectionModel().addListSelectionListener(evt -> {
762 if (((evt.getFirstIndex() == evt.getLastIndex())
763 && (evt.getFirstIndex() > 0) && previousLastIndex != -1) || (evt.getValueIsAdjusting())) {
764 return;
765 }
766 boolean lastIndexOnLastRow = (evt.getLastIndex() == (table.getRowCount() - 1));
767 boolean lastIndexSame = (previousLastIndex == evt.getLastIndex());
768
769
770
771
772
773
774
775
776
777
778
779
780 boolean disableScrollToBottom = (lastIndexOnLastRow && lastIndexSame && previousLastIndex != evt.getFirstIndex());
781 if (disableScrollToBottom && isScrollToBottom() && table.getRowCount() > 0) {
782 preferenceModel.setScrollToBottom(false);
783 }
784 previousLastIndex = evt.getLastIndex();
785 }
786 );
787
788 table.getSelectionModel().addListSelectionListener(
789 new ListSelectionListener() {
790 public void valueChanged(ListSelectionEvent evt) {
791 if (((evt.getFirstIndex() == evt.getLastIndex())
792 && (evt.getFirstIndex() > 0) && previousLastIndex != -1) || (evt.getValueIsAdjusting())) {
793 return;
794 }
795
796 final ListSelectionModel lsm = (ListSelectionModel) evt.getSource();
797
798 if (lsm.isSelectionEmpty()) {
799 if (isVisible()) {
800 statusBar.setNothingSelected();
801 }
802
803 if (detail.getDocument().getDefaultRootElement() != null) {
804 detailPaneUpdater.setSelectedRow(-1);
805 }
806 } else {
807 if (table.getSelectedRow() > -1) {
808 int selectedRow = table.getSelectedRow();
809
810 if (isVisible()) {
811 updateStatusBar();
812 }
813
814 try {
815 if (tableModel.getRowCount() >= selectedRow) {
816 detailPaneUpdater.setSelectedRow(table.getSelectedRow());
817 } else {
818 detailPaneUpdater.setSelectedRow(-1);
819 }
820 } catch (Exception e) {
821 e.printStackTrace();
822 detailPaneUpdater.setSelectedRow(-1);
823 }
824 }
825 }
826 }
827 });
828
829 renderer = new TableColorizingRenderer(colorizer, applicationPreferenceModel, tableModel, preferenceModel, true);
830 renderer.setToolTipsVisible(preferenceModel.isToolTips());
831
832 table.setDefaultRenderer(Object.class, renderer);
833
834 searchRenderer = new TableColorizingRenderer(colorizer, applicationPreferenceModel, searchModel, preferenceModel, false);
835 searchRenderer.setToolTipsVisible(preferenceModel.isToolTips());
836
837 searchTable.setDefaultRenderer(Object.class, searchRenderer);
838
839
840
841
842 table.addMouseListener(new ThrowableDisplayMouseAdapter(table, tableModel));
843 searchTable.addMouseListener(new ThrowableDisplayMouseAdapter(searchTable, searchModel));
844
845
846 searchTable.addMouseListener(new MouseAdapter() {
847 public void mouseClicked(MouseEvent e) {
848 LoggingEventWrapper loggingEventWrapper = searchModel.getRow(searchTable.getSelectedRow());
849 if (loggingEventWrapper != null) {
850 int id = new Integer(loggingEventWrapper.getLoggingEvent().getProperty("log4jid"));
851
852 setSelectedEvent(id);
853 }
854 }
855 });
856
857
858
859
860
861 tableModel.addNewKeyListener(
862 e -> SwingHelper.invokeOnEDT(() -> {
863
864
865
866
867 try {
868 if (table.getColumn(e.getKey()) != null) {
869 return;
870 }
871
872 } catch (IllegalArgumentException iae) {
873 }
874 TableColumn col = new TableColumn(e.getNewModelIndex());
875 col.setHeaderValue(e.getKey());
876
877 if (preferenceModel.addColumn(col)) {
878 if (preferenceModel.isColumnVisible(col) || !applicationPreferenceModel.isDefaultColumnsSet() || applicationPreferenceModel.isDefaultColumnsSet() &&
879 applicationPreferenceModel.getDefaultColumnNames().contains(col.getHeaderValue())) {
880 table.addColumn(col);
881 searchTable.addColumn(col);
882 preferenceModel.setColumnVisible(e.getKey().toString(), true);
883 }
884 }
885 }));
886
887
888
889
890
891 tableModel.addPropertyChangeListener("refilter", new PropertyChangeListener() {
892 private LoggingEventWrapper currentEvent;
893
894 public void propertyChange(PropertyChangeEvent evt) {
895
896
897 if (evt.getNewValue().equals(Boolean.TRUE)) {
898 int currentRow = table.getSelectedRow();
899 if (currentRow > -1) {
900 currentEvent = tableModel.getRow(currentRow);
901 }
902 } else {
903 if (currentEvent != null) {
904 table.scrollToRow(tableModel.getRowIndex(currentEvent));
905 }
906 }
907 }
908 });
909
910 table.getTableHeader().addMouseListener(
911 new MouseAdapter() {
912 public void mouseClicked(MouseEvent e) {
913 checkEvent(e);
914 }
915
916 public void mousePressed(MouseEvent e) {
917 checkEvent(e);
918 }
919
920 public void mouseReleased(MouseEvent e) {
921 checkEvent(e);
922 }
923
924 private void checkEvent(MouseEvent e) {
925 if (e.isPopupTrigger()) {
926 TableColumnModel colModel = table.getColumnModel();
927 int index = colModel.getColumnIndexAtX(e.getX());
928 int modelIndex = colModel.getColumn(index).getModelIndex();
929
930 if ((modelIndex + 1) == ChainsawColumns.INDEX_TIMESTAMP_COL_NAME) {
931 dateFormatChangePopup.show(e.getComponent(), e.getX(), e.getY());
932 }
933 }
934 }
935 });
936
937
938
939
940 JPanel upperPanel = new JPanel();
941 upperPanel.setLayout(new BoxLayout(upperPanel, BoxLayout.X_AXIS));
942 upperPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 0));
943
944 final JLabel filterLabel = new JLabel("Refine focus on: ");
945 filterLabel.setFont(filterLabel.getFont().deriveFont(Font.BOLD));
946
947 upperPanel.add(filterLabel);
948 upperPanel.add(Box.createHorizontalStrut(3));
949 upperPanel.add(filterCombo);
950 upperPanel.add(Box.createHorizontalStrut(3));
951
952 final JTextField filterText = (JTextField) filterCombo.getEditor().getEditorComponent();
953 final JTextField findText = (JTextField) findCombo.getEditor().getEditorComponent();
954
955
956
957 final JButton removeFilterButton = new JButton(" Remove ");
958
959 removeFilterButton.setToolTipText("Click here to remove the selected expression from the list");
960 removeFilterButton.addActionListener(
961 new AbstractAction() {
962 public void actionPerformed(ActionEvent e) {
963 Object selectedItem = filterCombo.getSelectedItem();
964 if (e.getSource() == removeFilterButton && selectedItem != null && !selectedItem.toString().trim().equals("")) {
965
966 int index = filterCombo.getSelectedIndex();
967 filterText.setText(null);
968 filterCombo.setSelectedIndex(-1);
969 filterCombo.removeItemAt(index);
970 if (!(findCombo.getSelectedItem() != null && findCombo.getSelectedItem().equals(selectedItem))) {
971
972 ((AutoFilterComboBox.AutoFilterComboBoxModel) findCombo.getModel()).removeElement(selectedItem);
973 }
974 }
975 }
976 }
977 );
978 upperPanel.add(removeFilterButton);
979
980 upperPanel.add(Box.createHorizontalStrut(25));
981
982 final JLabel findLabel = new JLabel("Find: ");
983 findLabel.setFont(filterLabel.getFont().deriveFont(Font.BOLD));
984
985 upperPanel.add(findLabel);
986 upperPanel.add(Box.createHorizontalStrut(3));
987
988 upperPanel.add(findCombo);
989 upperPanel.add(Box.createHorizontalStrut(3));
990
991 Action findNextAction = getFindNextAction();
992 Action findPreviousAction = getFindPreviousAction();
993
994 JButton findNextButton = new SmallButton(findNextAction);
995 findNextButton.setText("");
996 findNextButton.getActionMap().put(
997 findNextAction.getValue(Action.NAME), findNextAction);
998 findNextButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
999 (KeyStroke) findNextAction.getValue(Action.ACCELERATOR_KEY),
1000 findNextAction.getValue(Action.NAME));
1001
1002 JButton findPreviousButton = new SmallButton(findPreviousAction);
1003 findPreviousButton.setText("");
1004 findPreviousButton.getActionMap().put(
1005 findPreviousAction.getValue(Action.NAME), findPreviousAction);
1006 findPreviousButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
1007 (KeyStroke) findPreviousAction.getValue(Action.ACCELERATOR_KEY),
1008 findPreviousAction.getValue(Action.NAME));
1009
1010 upperPanel.add(findNextButton);
1011
1012 upperPanel.add(findPreviousButton);
1013 upperPanel.add(Box.createHorizontalStrut(3));
1014
1015
1016 final JButton removeFindButton = new JButton(" Remove ");
1017 removeFindButton.setToolTipText("Click here to remove the selected expression from the list");
1018 removeFindButton.addActionListener(
1019 new AbstractAction() {
1020 public void actionPerformed(ActionEvent e) {
1021 Object selectedItem = findCombo.getSelectedItem();
1022 if (e.getSource() == removeFindButton && selectedItem != null && !selectedItem.toString().trim().equals("")) {
1023
1024 int index = findCombo.getSelectedIndex();
1025 findText.setText(null);
1026 findCombo.setSelectedIndex(-1);
1027 findCombo.removeItemAt(index);
1028 if (!(filterCombo.getSelectedItem() != null && filterCombo.getSelectedItem().equals(selectedItem))) {
1029
1030 ((AutoFilterComboBox.AutoFilterComboBoxModel) filterCombo.getModel()).removeElement(selectedItem);
1031 }
1032 }
1033 }
1034 }
1035 );
1036 upperPanel.add(removeFindButton);
1037
1038
1039 Action findFocusAction = new AbstractAction() {
1040 public void actionPerformed(ActionEvent actionEvent) {
1041 findCombo.requestFocus();
1042 }
1043 };
1044
1045 Action filterFocusAction = new AbstractAction() {
1046 public void actionPerformed(ActionEvent actionEvent) {
1047 filterCombo.requestFocus();
1048 }
1049 };
1050
1051 Action findClearAction = new AbstractAction() {
1052 public void actionPerformed(ActionEvent actionEvent) {
1053 findCombo.setSelectedIndex(-1);
1054 findNext();
1055 }
1056 };
1057
1058 Action filterClearAction = new AbstractAction() {
1059 public void actionPerformed(ActionEvent actionEvent) {
1060 setRefineFocusText("");
1061 filterCombo.refilter();
1062 }
1063 };
1064
1065
1066 KeyStroke ksFindFocus =
1067 KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1068 KeyStroke ksFilterFocus =
1069 KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1070 KeyStroke ksFindClear =
1071 KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1072 KeyStroke ksFilterClear =
1073 KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1074
1075 getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksFindFocus, "FindFocus");
1076 getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksFilterFocus, "FilterFocus");
1077 getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksFindClear, "FindClear");
1078 getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksFilterClear, "FilterClear");
1079
1080 getActionMap().put("FindFocus", findFocusAction);
1081 getActionMap().put("FilterFocus", filterFocusAction);
1082 getActionMap().put("FindClear", findClearAction);
1083 getActionMap().put("FilterClear", filterClearAction);
1084
1085
1086
1087
1088 detail = new JEditorPane(ChainsawConstants.DETAIL_CONTENT_TYPE, "");
1089 detail.setEditable(false);
1090
1091 detailPaneUpdater = new DetailPaneUpdater();
1092
1093
1094 addFocusListener(new FocusListener() {
1095
1096 public void focusGained(FocusEvent e) {
1097 detailPaneUpdater.updateDetailPane();
1098 }
1099
1100 public void focusLost(FocusEvent e) {
1101
1102 }
1103 });
1104 findMarkerRule = ExpressionRule.getRule("prop." + ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE + " exists");
1105
1106 tableModel.addTableModelListener(e -> {
1107 int currentRow = table.getSelectedRow();
1108 if (e.getFirstRow() <= currentRow && e.getLastRow() >= currentRow) {
1109
1110 detailPaneUpdater.setAndUpdateSelectedRow(table.getSelectedRow());
1111 }
1112 });
1113 addPropertyChangeListener("detailPaneConversionPattern", detailPaneUpdater);
1114
1115 searchPane = new JScrollPane(searchTable);
1116 searchPane.getVerticalScrollBar().setUnitIncrement(ChainsawConstants.DEFAULT_ROW_HEIGHT * 2);
1117 searchPane.setPreferredSize(new Dimension(900, 50));
1118
1119
1120 detailPane = new JScrollPane(detail);
1121 detailPane.setPreferredSize(new Dimension(900, 50));
1122
1123 detailPanel.add(detailPane, BorderLayout.CENTER);
1124
1125 JPanel eventsAndStatusPanel = new JPanel(new BorderLayout());
1126
1127 eventsPane = new JScrollPane(table);
1128 eventsPane.getVerticalScrollBar().setUnitIncrement(ChainsawConstants.DEFAULT_ROW_HEIGHT * 2);
1129
1130 eventsAndStatusPanel.add(eventsPane, BorderLayout.CENTER);
1131
1132 Integer scrollBarWidth = (Integer) UIManager.get("ScrollBar.width");
1133
1134 JPanel rightPanel = new JPanel();
1135 rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
1136
1137 JPanel rightThumbNailPanel = new JPanel();
1138 rightThumbNailPanel.setLayout(new BoxLayout(rightThumbNailPanel, BoxLayout.Y_AXIS));
1139 rightThumbNailPanel.add(Box.createVerticalStrut(scrollBarWidth));
1140 colorizedEventAndSearchMatchThumbnail = new ColorizedEventAndSearchMatchThumbnail();
1141 rightThumbNailPanel.add(colorizedEventAndSearchMatchThumbnail);
1142 rightThumbNailPanel.add(Box.createVerticalStrut(scrollBarWidth));
1143 rightPanel.add(rightThumbNailPanel);
1144
1145 if (scrollBarWidth != null) {
1146 rightThumbNailPanel.setPreferredSize(new Dimension(scrollBarWidth - 4, -1));
1147 }
1148 eventsAndStatusPanel.add(rightPanel, BorderLayout.EAST);
1149
1150 JPanel leftPanel = new JPanel();
1151 leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS));
1152
1153 JPanel leftThumbNailPanel = new JPanel();
1154 leftThumbNailPanel.setLayout(new BoxLayout(leftThumbNailPanel, BoxLayout.Y_AXIS));
1155 leftThumbNailPanel.add(Box.createVerticalStrut(scrollBarWidth));
1156 eventTimeDeltaMatchThumbnail = new EventTimeDeltaMatchThumbnail();
1157 leftThumbNailPanel.add(eventTimeDeltaMatchThumbnail);
1158 leftThumbNailPanel.add(Box.createVerticalStrut(scrollBarWidth));
1159 leftPanel.add(leftThumbNailPanel);
1160
1161
1162 if (scrollBarWidth != null) {
1163 leftThumbNailPanel.setPreferredSize(new Dimension(scrollBarWidth - 4, -1));
1164 }
1165 eventsAndStatusPanel.add(leftPanel, BorderLayout.WEST);
1166
1167 final JPanel statusLabelPanel = new JPanel();
1168 statusLabelPanel.setLayout(new BorderLayout());
1169
1170 statusLabelPanel.add(upperPanel, BorderLayout.CENTER);
1171 eventsAndStatusPanel.add(statusLabelPanel, BorderLayout.NORTH);
1172
1173
1174
1175
1176 detailToolbar = new JToolBar(SwingConstants.HORIZONTAL);
1177 detailToolbar.setFloatable(false);
1178
1179 final LayoutEditorPane layoutEditorPane = new LayoutEditorPane();
1180 final JDialog layoutEditorDialog =
1181 new JDialog((JFrame) null, "Pattern Editor");
1182 layoutEditorDialog.getContentPane().add(layoutEditorPane);
1183 layoutEditorDialog.setSize(640, 480);
1184
1185 layoutEditorPane.addCancelActionListener(
1186 e -> layoutEditorDialog.setVisible(false));
1187
1188 layoutEditorPane.addOkActionListener(
1189 e -> {
1190 setDetailPaneConversionPattern(
1191 layoutEditorPane.getConversionPattern());
1192 layoutEditorDialog.setVisible(false);
1193 });
1194
1195 Action copyToRefineFocusAction = new AbstractAction("Set 'refine focus' field") {
1196 public void actionPerformed(ActionEvent e) {
1197 String selectedText = detail.getSelectedText();
1198 if (selectedText == null || selectedText.equals("")) {
1199
1200 return;
1201 }
1202 filterText.setText("msg ~= '" + selectedText + "'");
1203 }
1204 };
1205
1206 Action copyToSearchAction = new AbstractAction("Find next") {
1207 public void actionPerformed(ActionEvent e) {
1208 String selectedText = detail.getSelectedText();
1209 if (selectedText == null || selectedText.equals("")) {
1210
1211 return;
1212 }
1213 findCombo.setSelectedItem("msg ~= '" + selectedText + "'");
1214 findNext();
1215 }
1216 };
1217
1218 Action editDetailAction =
1219 new AbstractAction(
1220 "Edit...", new ImageIcon(ChainsawIcons.ICON_EDIT_RECEIVER)) {
1221 public void actionPerformed(ActionEvent e) {
1222 layoutEditorPane.setConversionPattern(
1223 getDetailPaneConversionPattern());
1224
1225 Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
1226 Point p =
1227 new Point(
1228 ((int) ((size.getWidth() / 2)
1229 - (layoutEditorDialog.getSize().getWidth() / 2))),
1230 ((int) ((size.getHeight() / 2)
1231 - (layoutEditorDialog.getSize().getHeight() / 2))));
1232 layoutEditorDialog.setLocation(p);
1233
1234 layoutEditorDialog.setVisible(true);
1235 }
1236 };
1237
1238 editDetailAction.putValue(
1239 Action.SHORT_DESCRIPTION,
1240 "opens a Dialog window to Edit the Pattern Layout text");
1241
1242 final SmallButton editDetailButton = new SmallButton(editDetailAction);
1243 editDetailButton.setText(null);
1244 detailToolbar.add(Box.createHorizontalGlue());
1245 detailToolbar.add(editDetailButton);
1246 detailToolbar.addSeparator();
1247 detailToolbar.add(Box.createHorizontalStrut(5));
1248
1249 Action closeDetailAction =
1250 new AbstractAction(null, LineIconFactory.createCloseIcon()) {
1251 public void actionPerformed(ActionEvent arg0) {
1252 preferenceModel.setDetailPaneVisible(false);
1253 }
1254 };
1255
1256 closeDetailAction.putValue(
1257 Action.SHORT_DESCRIPTION, "Hides the Detail Panel");
1258
1259 SmallButton closeDetailButton = new SmallButton(closeDetailAction);
1260 detailToolbar.add(closeDetailButton);
1261
1262 detailPanel.add(detailToolbar, BorderLayout.NORTH);
1263
1264 lowerPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, eventsAndStatusPanel, detailPanel);
1265
1266 dividerSize = lowerPanel.getDividerSize();
1267 lowerPanel.setDividerLocation(-1);
1268
1269 lowerPanel.setResizeWeight(1.0);
1270 lowerPanel.setBorder(null);
1271 lowerPanel.setContinuousLayout(true);
1272
1273 JPopupMenu editDetailPopupMenu = new JPopupMenu();
1274
1275 editDetailPopupMenu.add(copyToRefineFocusAction);
1276 editDetailPopupMenu.add(copyToSearchAction);
1277 editDetailPopupMenu.addSeparator();
1278
1279 editDetailPopupMenu.add(editDetailAction);
1280 editDetailPopupMenu.addSeparator();
1281
1282 final ButtonGroup layoutGroup = new ButtonGroup();
1283
1284 JRadioButtonMenuItem defaultLayoutRadio =
1285 new JRadioButtonMenuItem(
1286 new AbstractAction("Set to Default Layout") {
1287 public void actionPerformed(ActionEvent e) {
1288 setDetailPaneConversionPattern(
1289 DefaultLayoutFactory.getDefaultPatternLayout());
1290 }
1291 });
1292
1293 JRadioButtonMenuItem fullLayoutRadio =
1294 new JRadioButtonMenuItem(
1295 new AbstractAction("Set to Full Layout") {
1296 public void actionPerformed(ActionEvent e) {
1297 setDetailPaneConversionPattern(
1298 DefaultLayoutFactory.getFullPatternLayout());
1299 }
1300 });
1301
1302 editDetailPopupMenu.add(defaultLayoutRadio);
1303 editDetailPopupMenu.add(fullLayoutRadio);
1304
1305 layoutGroup.add(defaultLayoutRadio);
1306 layoutGroup.add(fullLayoutRadio);
1307 defaultLayoutRadio.setSelected(true);
1308
1309 JRadioButtonMenuItem tccLayoutRadio =
1310 new JRadioButtonMenuItem(
1311 new AbstractAction("Set to TCCLayout") {
1312 public void actionPerformed(ActionEvent e) {
1313 setDetailPaneConversionPattern(
1314 PatternLayout.TTCC_CONVERSION_PATTERN);
1315 }
1316 });
1317 editDetailPopupMenu.add(tccLayoutRadio);
1318 layoutGroup.add(tccLayoutRadio);
1319
1320 PopupListener editDetailPopupListener =
1321 new PopupListener(editDetailPopupMenu);
1322 detail.addMouseListener(editDetailPopupListener);
1323
1324
1325
1326
1327 nameTreeAndMainPanelSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, logTreePanel, lowerPanel);
1328 nameTreeAndMainPanelSplit.setDividerLocation(-1);
1329
1330 add(nameTreeAndMainPanelSplit, BorderLayout.CENTER);
1331
1332 if (isLogTreeVisible()) {
1333 showLogTreePanel();
1334 } else {
1335 hideLogTreePanel();
1336 }
1337
1338
1339
1340
1341 class BestFit extends JMenuItem {
1342 public BestFit() {
1343 super("Best fit column");
1344 addActionListener(
1345 evt -> {
1346 if (currentPoint != null) {
1347 int column = currentTable.columnAtPoint(currentPoint);
1348 int maxWidth = getMaxColumnWidth(column);
1349 currentTable.getColumnModel().getColumn(column).setPreferredWidth(
1350 maxWidth);
1351 }
1352 });
1353 }
1354 }
1355
1356 class ColorPanel extends JMenuItem {
1357 public ColorPanel() {
1358 super("Color settings...");
1359 setIcon(ChainsawIcons.ICON_PREFERENCES);
1360 addActionListener(
1361 evt -> showColorPreferences());
1362 }
1363 }
1364
1365 class LogPanelPreferences extends JMenuItem {
1366 public LogPanelPreferences() {
1367 super("Tab Preferences...");
1368 setIcon(ChainsawIcons.ICON_PREFERENCES);
1369 addActionListener(
1370 evt -> showPreferences());
1371 }
1372 }
1373
1374 class FocusOn extends JMenuItem {
1375 public FocusOn() {
1376 super("Set 'refine focus' field to value under pointer");
1377 addActionListener(
1378 evt -> {
1379 if (currentPoint != null) {
1380 String operator = "==";
1381 int column = currentTable.columnAtPoint(currentPoint);
1382 int row = currentTable.rowAtPoint(currentPoint);
1383 String colName = currentTable.getColumnName(column).toUpperCase();
1384 String value = getValueOf(row, column);
1385
1386 if (columnNameKeywordMap.containsKey(colName)) {
1387 filterText.setText(
1388 columnNameKeywordMap.get(colName).toString() + " " + operator
1389 + " '" + value + "'");
1390 }
1391 }
1392 });
1393 }
1394 }
1395
1396 class DefineAddCustomFilter extends JMenuItem {
1397 public DefineAddCustomFilter() {
1398 super("Add value under pointer to 'refine focus' field");
1399 addActionListener(
1400 evt -> {
1401 if (currentPoint != null) {
1402 String operator = "==";
1403 int column = currentTable.columnAtPoint(currentPoint);
1404 int row = currentTable.rowAtPoint(currentPoint);
1405 String value = getValueOf(row, column);
1406 String colName = currentTable.getColumnName(column).toUpperCase();
1407
1408 if (columnNameKeywordMap.containsKey(colName)) {
1409 filterText.setText(
1410 filterText.getText() + " && "
1411 + columnNameKeywordMap.get(colName).toString() + " "
1412 + operator + " '" + value + "'");
1413 }
1414
1415 }
1416 });
1417 }
1418 }
1419
1420 class DefineAddCustomFind extends JMenuItem {
1421 public DefineAddCustomFind() {
1422 super("Add value under pointer to 'find' field");
1423 addActionListener(
1424 evt -> {
1425 if (currentPoint != null) {
1426 String operator = "==";
1427 int column = currentTable.columnAtPoint(currentPoint);
1428 int row = currentTable.rowAtPoint(currentPoint);
1429 String value = getValueOf(row, column);
1430 String colName = currentTable.getColumnName(column).toUpperCase();
1431
1432 if (columnNameKeywordMap.containsKey(colName)) {
1433 findCombo.setSelectedItem(
1434 findText.getText() + " && "
1435 + columnNameKeywordMap.get(colName).toString() + " "
1436 + operator + " '" + value + "'");
1437 findNext();
1438 }
1439 }
1440 });
1441 }
1442 }
1443
1444 class BuildColorRule extends JMenuItem {
1445 public BuildColorRule() {
1446 super("Define color rule for value under pointer");
1447 addActionListener(
1448 evt -> {
1449 if (currentPoint != null) {
1450 String operator = "==";
1451 int column = currentTable.columnAtPoint(currentPoint);
1452 int row = currentTable.rowAtPoint(currentPoint);
1453 String colName = currentTable.getColumnName(column).toUpperCase();
1454 String value = getValueOf(row, column);
1455
1456 if (columnNameKeywordMap.containsKey(colName)) {
1457 Color c = JColorChooser.showDialog(getRootPane(), "Choose a color", Color.red);
1458 if (c != null) {
1459 String expression = columnNameKeywordMap.get(colName).toString() + " " + operator + " '" + value + "'";
1460 colorizer.addRule(ChainsawConstants.DEFAULT_COLOR_RULE_NAME, new ColorRule(expression,
1461 ExpressionRule.getRule(expression), c, ChainsawConstants.COLOR_DEFAULT_FOREGROUND));
1462 }
1463 }
1464 }
1465 });
1466 }
1467 }
1468
1469 final JPopupMenu mainPopup = new JPopupMenu();
1470 final JPopupMenu searchPopup = new JPopupMenu();
1471
1472 class ClearFocus extends AbstractAction {
1473 public ClearFocus() {
1474 super("Clear 'refine focus' field");
1475 }
1476
1477 public void actionPerformed(ActionEvent e) {
1478 filterText.setText(null);
1479 tableRuleMediator.setFilterRule(null);
1480 searchRuleMediator.setFilterRule(null);
1481 }
1482 }
1483
1484 class CopySelection extends AbstractAction {
1485 public CopySelection() {
1486 super("Copy selection to clipboard");
1487 }
1488
1489 public void actionPerformed(ActionEvent e) {
1490 if (currentTable == null) {
1491 return;
1492 }
1493 int start = currentTable.getSelectionModel().getMinSelectionIndex();
1494 int end = currentTable.getSelectionModel().getMaxSelectionIndex();
1495 StringBuilder result = new StringBuilder();
1496 for (int row = start; row < end + 1; row++) {
1497 for (int column = 0; column < currentTable.getColumnCount(); column++) {
1498 result.append(getValueOf(row, column));
1499 if (column != (currentTable.getColumnCount() - 1)) {
1500 result.append(" - ");
1501 }
1502 }
1503 result.append(System.getProperty("line.separator"));
1504 }
1505 StringSelection selection = new StringSelection(result.toString());
1506 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
1507 clipboard.setContents(selection, null);
1508 }
1509 }
1510
1511 class CopyField extends AbstractAction {
1512 public CopyField() {
1513 super("Copy value under pointer to clipboard");
1514 }
1515
1516 public void actionPerformed(ActionEvent e) {
1517 if (currentPoint != null && currentTable != null) {
1518 int column = currentTable.columnAtPoint(currentPoint);
1519 int row = currentTable.rowAtPoint(currentPoint);
1520 String value = getValueOf(row, column);
1521 StringSelection selection = new StringSelection(value);
1522 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
1523 clipboard.setContents(selection, null);
1524 }
1525 }
1526 }
1527 final JMenuItem menuItemToggleDock = new JMenuItem("Undock/dock");
1528
1529 dockingAction =
1530 new AbstractAction("Undock") {
1531 public void actionPerformed(ActionEvent evt) {
1532 if (isDocked()) {
1533 undock();
1534 } else {
1535 dock();
1536 }
1537 }
1538 };
1539 dockingAction.putValue(
1540 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.UNDOCK));
1541 menuItemToggleDock.setAction(dockingAction);
1542
1543
1544
1545
1546 mainPopup.add(new FocusOn());
1547 searchPopup.add(new FocusOn());
1548 mainPopup.add(new DefineAddCustomFilter());
1549 searchPopup.add(new DefineAddCustomFilter());
1550 mainPopup.add(new ClearFocus());
1551 searchPopup.add(new ClearFocus());
1552
1553 mainPopup.add(new JSeparator());
1554 searchPopup.add(new JSeparator());
1555
1556 class Search extends JMenuItem {
1557 public Search() {
1558 super("Find value under pointer");
1559
1560 addActionListener(
1561 evt -> {
1562 if (currentPoint != null) {
1563 String operator = "==";
1564 int column = currentTable.columnAtPoint(currentPoint);
1565 int row = currentTable.rowAtPoint(currentPoint);
1566 String colName = currentTable.getColumnName(column).toUpperCase();
1567 String value = getValueOf(row, column);
1568 if (columnNameKeywordMap.containsKey(colName)) {
1569 findCombo.setSelectedItem(
1570 columnNameKeywordMap.get(colName).toString() + " " + operator
1571 + " '" + value + "'");
1572 findNext();
1573 }
1574 }
1575 });
1576 }
1577 }
1578
1579 class ClearSearch extends AbstractAction {
1580 public ClearSearch() {
1581 super("Clear find field");
1582 }
1583
1584 public void actionPerformed(ActionEvent e) {
1585 findCombo.setSelectedItem(null);
1586 updateFindRule(null);
1587 }
1588 }
1589
1590 mainPopup.add(new Search());
1591 searchPopup.add(new Search());
1592 mainPopup.add(new DefineAddCustomFind());
1593 searchPopup.add(new DefineAddCustomFind());
1594 mainPopup.add(new ClearSearch());
1595 searchPopup.add(new ClearSearch());
1596
1597 mainPopup.add(new JSeparator());
1598 searchPopup.add(new JSeparator());
1599
1600 class DisplayNormalTimes extends JMenuItem {
1601 public DisplayNormalTimes() {
1602 super("Hide relative times");
1603 addActionListener(
1604 e -> {
1605 if (currentPoint != null) {
1606 ((TableColorizingRenderer) currentTable.getDefaultRenderer(Object.class)).setUseNormalTimes();
1607 ((ChainsawCyclicBufferTableModel) currentTable.getModel()).reFilter();
1608 setEnabled(true);
1609 }
1610 });
1611 }
1612 }
1613
1614 class DisplayRelativeTimesToRowUnderCursor extends JMenuItem {
1615 public DisplayRelativeTimesToRowUnderCursor() {
1616 super("Show times relative to this event");
1617 addActionListener(
1618 e -> {
1619 if (currentPoint != null) {
1620 int row = currentTable.rowAtPoint(currentPoint);
1621 ChainsawCyclicBufferTableModel cyclicBufferTableModel = (ChainsawCyclicBufferTableModel) currentTable.getModel();
1622 LoggingEventWrapper loggingEventWrapper = cyclicBufferTableModel.getRow(row);
1623 if (loggingEventWrapper != null) {
1624 ((TableColorizingRenderer) currentTable.getDefaultRenderer(Object.class)).setUseRelativeTimes(loggingEventWrapper.getLoggingEvent().getTimeStamp());
1625 cyclicBufferTableModel.reFilter();
1626 }
1627 setEnabled(true);
1628 }
1629 });
1630 }
1631 }
1632
1633 class DisplayRelativeTimesToPreviousRow extends JMenuItem {
1634 public DisplayRelativeTimesToPreviousRow() {
1635 super("Show times relative to previous rows");
1636 addActionListener(
1637 e -> {
1638 if (currentPoint != null) {
1639 ((TableColorizingRenderer) currentTable.getDefaultRenderer(Object.class)).setUseRelativeTimesToPreviousRow();
1640 ((ChainsawCyclicBufferTableModel) currentTable.getModel()).reFilter();
1641 setEnabled(true);
1642 }
1643 });
1644 }
1645 }
1646
1647 mainPopup.add(new DisplayRelativeTimesToRowUnderCursor());
1648 searchPopup.add(new DisplayRelativeTimesToRowUnderCursor());
1649 mainPopup.add(new DisplayRelativeTimesToPreviousRow());
1650 searchPopup.add(new DisplayRelativeTimesToPreviousRow());
1651 mainPopup.add(new DisplayNormalTimes());
1652 searchPopup.add(new DisplayNormalTimes());
1653 mainPopup.add(new JSeparator());
1654 searchPopup.add(new JSeparator());
1655
1656 mainPopup.add(new BuildColorRule());
1657 searchPopup.add(new BuildColorRule());
1658 mainPopup.add(new JSeparator());
1659 searchPopup.add(new JSeparator());
1660 mainPopup.add(new CopyField());
1661 mainPopup.add(new CopySelection());
1662 searchPopup.add(new CopyField());
1663 searchPopup.add(new CopySelection());
1664 mainPopup.add(new JSeparator());
1665 searchPopup.add(new JSeparator());
1666
1667 mainPopup.add(menuItemToggleDetails);
1668 mainPopup.add(menuItemLoggerTree);
1669 mainToggleToolTips = new ToggleToolTips();
1670 searchToggleToolTips = new ToggleToolTips();
1671 mainPopup.add(mainToggleToolTips);
1672 searchPopup.add(searchToggleToolTips);
1673
1674 mainPopup.add(new JSeparator());
1675
1676 mainPopup.add(menuItemToggleDock);
1677
1678 mainPopup.add(new BestFit());
1679 searchPopup.add(new BestFit());
1680
1681 mainPopup.add(new JSeparator());
1682
1683 mainPopup.add(new ColorPanel());
1684 searchPopup.add(new ColorPanel());
1685 mainPopup.add(new LogPanelPreferences());
1686 searchPopup.add(new LogPanelPreferences());
1687
1688 final PopupListener mainTablePopupListener = new PopupListener(mainPopup);
1689 eventsPane.addMouseListener(mainTablePopupListener);
1690 table.addMouseListener(mainTablePopupListener);
1691
1692 table.addMouseListener(new MouseListener() {
1693 public void mouseClicked(MouseEvent mouseEvent) {
1694 checkMultiSelect(mouseEvent);
1695 }
1696
1697 public void mousePressed(MouseEvent mouseEvent) {
1698 checkMultiSelect(mouseEvent);
1699 }
1700
1701 public void mouseReleased(MouseEvent mouseEvent) {
1702 checkMultiSelect(mouseEvent);
1703 }
1704
1705 public void mouseEntered(MouseEvent mouseEvent) {
1706 checkMultiSelect(mouseEvent);
1707 }
1708
1709 public void mouseExited(MouseEvent mouseEvent) {
1710 checkMultiSelect(mouseEvent);
1711 }
1712
1713 private void checkMultiSelect(MouseEvent mouseEvent) {
1714 if (mouseEvent.isAltDown()) {
1715 table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
1716 } else {
1717 table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1718 }
1719 }
1720 });
1721
1722
1723 searchTable.addMouseListener(new MouseListener() {
1724 public void mouseClicked(MouseEvent mouseEvent) {
1725 checkMultiSelect(mouseEvent);
1726 }
1727
1728 public void mousePressed(MouseEvent mouseEvent) {
1729 checkMultiSelect(mouseEvent);
1730 }
1731
1732 public void mouseReleased(MouseEvent mouseEvent) {
1733 checkMultiSelect(mouseEvent);
1734 }
1735
1736 public void mouseEntered(MouseEvent mouseEvent) {
1737 checkMultiSelect(mouseEvent);
1738 }
1739
1740 public void mouseExited(MouseEvent mouseEvent) {
1741 checkMultiSelect(mouseEvent);
1742 }
1743
1744 private void checkMultiSelect(MouseEvent mouseEvent) {
1745 if (mouseEvent.isAltDown()) {
1746 searchTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
1747 } else {
1748 searchTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1749 }
1750 }
1751 });
1752
1753
1754 final PopupListener searchTablePopupListener = new PopupListener(searchPopup);
1755 searchPane.addMouseListener(searchTablePopupListener);
1756 searchTable.addMouseListener(searchTablePopupListener);
1757 }
1758
1759 private String getValueOf(int row, int column) {
1760 if (currentTable == null) {
1761 return "";
1762 }
1763
1764 Object o = currentTable.getValueAt(row, column);
1765
1766 if (o instanceof Date) {
1767 return TIMESTAMP_DATE_FORMAT.format((Date) o);
1768 }
1769
1770 if (o instanceof String) {
1771 return (String) o;
1772 }
1773
1774 if (o instanceof Level) {
1775 return o.toString();
1776 }
1777
1778 if (o instanceof String[]) {
1779 StringBuilder value = new StringBuilder();
1780
1781 String[] ti = (String[]) o;
1782 if (ti.length > 0 && (!(ti.length == 1 && ti[0].equals("")))) {
1783 LoggingEventWrapper loggingEventWrapper = ((ChainsawCyclicBufferTableModel) (currentTable.getModel())).getRow(row);
1784 value = new StringBuilder(loggingEventWrapper.getLoggingEvent().getMessage().toString());
1785 for (int i = 0; i < ((String[]) o).length; i++) {
1786 value.append('\n').append(((String[]) o)[i]);
1787 }
1788 }
1789 return value.toString();
1790 }
1791 return "";
1792 }
1793
1794 private Action getFindNextAction() {
1795 final Action action =
1796 new AbstractAction("Find next") {
1797 public void actionPerformed(ActionEvent e) {
1798 findNext();
1799 }
1800 };
1801
1802
1803 action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F3"));
1804 action.putValue(
1805 Action.SHORT_DESCRIPTION,
1806 "Find the next occurrence of the rule from the current row");
1807 action.putValue(Action.SMALL_ICON, new ImageIcon(ChainsawIcons.DOWN));
1808
1809 return action;
1810 }
1811
1812 private Action getFindPreviousAction() {
1813 final Action action =
1814 new AbstractAction("Find previous") {
1815 public void actionPerformed(ActionEvent e) {
1816 findPrevious();
1817 }
1818 };
1819
1820
1821 action.putValue(
1822 Action.ACCELERATOR_KEY,
1823 KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_MASK));
1824 action.putValue(
1825 Action.SHORT_DESCRIPTION,
1826 "Find the previous occurrence of the rule from the current row");
1827 action.putValue(Action.SMALL_ICON, new ImageIcon(ChainsawIcons.UP));
1828
1829 return action;
1830 }
1831
1832 private void buildCombo(final AutoFilterComboBox combo, boolean isFiltering, final AutoFilterComboBox.AutoFilterComboBoxModel otherModel) {
1833
1834 combo.addItem("LEVEL == TRACE");
1835 combo.addItem("LEVEL >= DEBUG");
1836 combo.addItem("LEVEL >= INFO");
1837 combo.addItem("LEVEL >= WARN");
1838 combo.addItem("LEVEL >= ERROR");
1839 combo.addItem("LEVEL == FATAL");
1840
1841 final JTextField filterText = (JTextField) combo.getEditor().getEditorComponent();
1842 if (isFiltering) {
1843 filterText.getDocument().addDocumentListener(new DelayedTextDocumentListener(filterText));
1844 }
1845 filterText.setToolTipText("Enter an expression - right click or ctrl-space for menu - press enter to add to list");
1846 filterText.addKeyListener(new ExpressionRuleContext(filterModel, filterText));
1847
1848 if (combo.getEditor().getEditorComponent() instanceof JTextField) {
1849 combo.addActionListener(
1850 new AbstractAction() {
1851 public void actionPerformed(ActionEvent e) {
1852 if (e.getActionCommand().equals("comboBoxEdited")) {
1853 try {
1854
1855 Object item = combo.getSelectedItem();
1856 if (item != null && !item.toString().trim().equals("")) {
1857 ExpressionRule.getRule(item.toString());
1858
1859 combo.insertItemAt(item, 0);
1860 otherModel.insertElementAt(item, 0);
1861 }
1862
1863 filterText.setBackground(UIManager.getColor("TextField.background"));
1864 } catch (IllegalArgumentException iae) {
1865
1866
1867 filterText.setToolTipText(iae.getMessage());
1868 filterText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
1869 }
1870 }
1871 }
1872 });
1873 }
1874 }
1875
1876
1877
1878
1879
1880
1881 public boolean isScrollToBottom() {
1882 return preferenceModel.isScrollToBottom();
1883 }
1884
1885 public void setRefineFocusText(String refineFocusText) {
1886 final JTextField filterText = (JTextField) filterCombo.getEditor().getEditorComponent();
1887 filterText.setText(refineFocusText);
1888 }
1889
1890 public String getRefineFocusText() {
1891 final JTextField filterText = (JTextField) filterCombo.getEditor().getEditorComponent();
1892 return filterText.getText();
1893 }
1894
1895
1896
1897
1898 public void toggleScrollToBottom() {
1899 preferenceModel.setScrollToBottom(!preferenceModel.isScrollToBottom());
1900 }
1901
1902 private void scrollToBottom() {
1903
1904 EventQueue.invokeLater(() -> {
1905 int scrollRow = tableModel.getRowCount() - 1;
1906 table.scrollToRow(scrollRow);
1907 });
1908 }
1909
1910 public void scrollToTop() {
1911 EventQueue.invokeLater(() -> {
1912 if (tableModel.getRowCount() > 1) {
1913 table.scrollToRow(0);
1914 }
1915 });
1916 }
1917
1918
1919
1920
1921
1922
1923
1924 public String getNamespace() {
1925 return getIdentifier();
1926 }
1927
1928
1929
1930
1931
1932
1933
1934 public String getInterestedIdentifier() {
1935 return getIdentifier();
1936 }
1937
1938
1939
1940
1941
1942
1943
1944
1945 public void receiveEventBatch(String ident, final List<LoggingEvent> events) {
1946
1947 SwingHelper.invokeOnEDT(() -> {
1948
1949
1950
1951 if (isPaused()) {
1952 return;
1953 }
1954 final int selectedRow = table.getSelectedRow();
1955 final int startingRow = table.getRowCount();
1956 final LoggingEventWrapper selectedEvent;
1957 if (selectedRow >= 0) {
1958 selectedEvent = tableModel.getRow(selectedRow);
1959 } else {
1960 selectedEvent = null;
1961 }
1962
1963 final int startingSearchRow = searchTable.getRowCount();
1964
1965 boolean rowAdded = false;
1966 boolean searchRowAdded = false;
1967
1968 int addedRowCount = 0;
1969 int searchAddedRowCount = 0;
1970
1971 for (Object event1 : events) {
1972
1973 LoggingEvent event = (LoggingEvent) event1;
1974
1975 LoggingEventWrapper loggingEventWrapper1 = new LoggingEventWrapper(event);
1976
1977 if (clearTableExpressionRule != null && clearTableExpressionRule.evaluate(event, null)) {
1978 logger.info("clear table expression matched - clearing table - matching event msg - " + event.getMessage());
1979 clearEvents();
1980 }
1981
1982 updateOtherModels(event);
1983 boolean isCurrentRowAdded = tableModel.isAddRow(loggingEventWrapper1);
1984 if (isCurrentRowAdded) {
1985 addedRowCount++;
1986 }
1987 rowAdded = rowAdded || isCurrentRowAdded;
1988
1989
1990 LoggingEventWrapper loggingEventWrapper2 = new LoggingEventWrapper(loggingEventWrapper1);
1991 boolean isSearchCurrentRowAdded = searchModel.isAddRow(loggingEventWrapper2);
1992 if (isSearchCurrentRowAdded) {
1993 searchAddedRowCount++;
1994 }
1995 searchRowAdded = searchRowAdded || isSearchCurrentRowAdded;
1996 }
1997
1998 if (rowAdded) {
1999 tableModel.fireTableEvent(startingRow, startingRow + addedRowCount, addedRowCount);
2000 }
2001 if (searchRowAdded) {
2002 searchModel.fireTableEvent(startingSearchRow, startingSearchRow + searchAddedRowCount, searchAddedRowCount);
2003 }
2004
2005
2006 tableModel.notifyCountListeners();
2007
2008 if (rowAdded) {
2009 if (tableModel.isSortEnabled()) {
2010 tableModel.sort();
2011 }
2012
2013
2014 detailPaneUpdater.setSelectedRow(table.getSelectedRow());
2015 }
2016
2017 if (searchRowAdded) {
2018 if (searchModel.isSortEnabled()) {
2019 searchModel.sort();
2020 }
2021 }
2022
2023 if (!isScrollToBottom() && selectedEvent != null) {
2024 final int newIndex = tableModel.getRowIndex(selectedEvent);
2025 if (newIndex >= 0) {
2026
2027 table.setRowSelectionInterval(newIndex, newIndex);
2028 }
2029 }
2030 });
2031 }
2032
2033
2034
2035
2036
2037
2038
2039 public void loadSettings(LoadSettingsEvent event) {
2040
2041 File xmlFile = null;
2042 try {
2043 xmlFile = new File(SettingsManager.getInstance().getSettingsDirectory(), URLEncoder.encode(identifier, "UTF-8") + ".xml");
2044 } catch (UnsupportedEncodingException e) {
2045 e.printStackTrace();
2046 }
2047
2048 if (xmlFile.exists()) {
2049 XStream stream = buildXStreamForLogPanelPreference();
2050 ObjectInputStream in = null;
2051 try {
2052 FileReader r = new FileReader(xmlFile);
2053 in = stream.createObjectInputStream(r);
2054 LogPanelPreferenceModel storedPrefs = (LogPanelPreferenceModel) in.readObject();
2055 lowerPanelDividerLocation = in.readInt();
2056 int treeDividerLocation = in.readInt();
2057 String conversionPattern = in.readObject().toString();
2058 Point p = (Point) in.readObject();
2059 Dimension d = (Dimension) in.readObject();
2060
2061 int versionNumber = 0;
2062 try {
2063 versionNumber = in.readInt();
2064 } catch (EOFException eof) {
2065 }
2066
2067 Vector savedVector;
2068
2069
2070 if (versionNumber > 0) {
2071 savedVector = (Vector) in.readObject();
2072 for (Object item : savedVector) {
2073
2074 filterCombo.insertItemAt(item, 0);
2075 findCombo.insertItemAt(item, 0);
2076 }
2077 if (versionNumber > 1) {
2078
2079 int index = 0;
2080 String columnOrder = event.getSetting(TABLE_COLUMN_ORDER);
2081 StringTokenizer tok = new StringTokenizer(columnOrder, ",");
2082 while (tok.hasMoreElements()) {
2083 String element = tok.nextElement().toString().trim().toUpperCase();
2084 TableColumn column = new TableColumn(index++);
2085 column.setHeaderValue(element);
2086 preferenceModel.addColumn(column);
2087 }
2088
2089 TableColumnModel columnModel = table.getColumnModel();
2090
2091 while (columnModel.getColumnCount() > 0) {
2092 columnModel.removeColumn(columnModel.getColumn(0));
2093 }
2094
2095 for (Object o1 : preferenceModel.getVisibleColumnOrder()) {
2096 TableColumn col = (TableColumn) o1;
2097 columnModel.addColumn(col);
2098 }
2099
2100 TableColumnModel searchColumnModel = searchTable.getColumnModel();
2101
2102 while (searchColumnModel.getColumnCount() > 0) {
2103 searchColumnModel.removeColumn(searchColumnModel.getColumn(0));
2104 }
2105
2106 for (Object o : preferenceModel.getVisibleColumnOrder()) {
2107 TableColumn col = (TableColumn) o;
2108 searchColumnModel.addColumn(col);
2109 }
2110
2111 preferenceModel.apply(storedPrefs);
2112 } else {
2113 loadDefaultColumnSettings(event);
2114 }
2115
2116
2117 tableModel.setCyclic(preferenceModel.isCyclic());
2118 searchModel.setCyclic(preferenceModel.isCyclic());
2119 lowerPanel.setDividerLocation(lowerPanelDividerLocation);
2120 nameTreeAndMainPanelSplit.setDividerLocation(treeDividerLocation);
2121 detailLayout.setConversionPattern(conversionPattern);
2122 if (p.x != 0 && p.y != 0) {
2123 undockedFrame.setLocation(p.x, p.y);
2124 undockedFrame.setSize(d);
2125 } else {
2126 undockedFrame.setLocation(0, 0);
2127 undockedFrame.setSize(new Dimension(1024, 768));
2128 }
2129 } else {
2130 loadDefaultColumnSettings(event);
2131 }
2132 } catch (Exception e) {
2133 e.printStackTrace();
2134 loadDefaultColumnSettings(event);
2135
2136 } finally {
2137 if (in != null) {
2138 try {
2139 in.close();
2140 } catch (IOException ioe) {
2141 }
2142 }
2143 }
2144 } else {
2145
2146 loadDefaultColumnSettings(event);
2147 }
2148
2149 tableModel.setCyclic(preferenceModel.isCyclic());
2150 searchModel.setCyclic(preferenceModel.isCyclic());
2151 logTreePanel.ignore(preferenceModel.getHiddenLoggers());
2152 logTreePanel.setHiddenExpression(preferenceModel.getHiddenExpression());
2153 logTreePanel.setAlwaysDisplayExpression(preferenceModel.getAlwaysDisplayExpression());
2154 if (preferenceModel.getClearTableExpression() != null) {
2155 try {
2156 clearTableExpressionRule = ExpressionRule.getRule(preferenceModel.getClearTableExpression());
2157 } catch (Exception e) {
2158 clearTableExpressionRule = null;
2159 }
2160 }
2161
2162
2163 colorizer.loadColorSettings(identifier);
2164 }
2165
2166
2167
2168
2169
2170
2171
2172 public void saveSettings(SaveSettingsEvent event) {
2173 File xmlFile;
2174 try {
2175 xmlFile = new File(SettingsManager.getInstance().getSettingsDirectory(), URLEncoder.encode(identifier, "UTF-8") + ".xml");
2176 } catch (UnsupportedEncodingException e) {
2177 e.printStackTrace();
2178
2179 return;
2180 }
2181
2182 preferenceModel.setHiddenLoggers(new HashSet(logTreePanel.getHiddenSet()));
2183 preferenceModel.setHiddenExpression(logTreePanel.getHiddenExpression());
2184 preferenceModel.setAlwaysDisplayExpression(logTreePanel.getAlwaysDisplayExpression());
2185 List visibleOrder = new ArrayList();
2186 Enumeration<TableColumn> cols = table.getColumnModel().getColumns();
2187 while (cols.hasMoreElements()) {
2188 TableColumn c = cols.nextElement();
2189 visibleOrder.add(c);
2190 }
2191 preferenceModel.setVisibleColumnOrder(visibleOrder);
2192
2193
2194 XStream stream = buildXStreamForLogPanelPreference();
2195 ObjectOutputStream s = null;
2196 try {
2197 FileWriter w = new FileWriter(xmlFile);
2198 s = stream.createObjectOutputStream(w);
2199 s.writeObject(preferenceModel);
2200 if (isDetailPanelVisible) {
2201
2202 s.writeInt(lowerPanel.getDividerLocation());
2203 } else {
2204
2205 s.writeInt(lowerPanelDividerLocation);
2206 }
2207 s.writeInt(nameTreeAndMainPanelSplit.getDividerLocation());
2208 s.writeObject(detailLayout.getConversionPattern());
2209 s.writeObject(undockedFrame.getLocation());
2210 s.writeObject(undockedFrame.getSize());
2211
2212 s.writeInt(LOG_PANEL_SERIALIZATION_VERSION_NUMBER);
2213
2214 Vector combinedVector = new Vector();
2215 combinedVector.addAll(filterCombo.getModelData());
2216 combinedVector.addAll(findCombo.getModelData());
2217
2218 s.writeObject(combinedVector);
2219 } catch (Exception ex) {
2220 ex.printStackTrace();
2221
2222 } finally {
2223 if (s != null) {
2224 try {
2225 s.close();
2226 } catch (IOException ioe) {
2227 }
2228 }
2229 }
2230
2231
2232 colorizer.saveColorSettings(identifier);
2233 }
2234
2235 private XStream buildXStreamForLogPanelPreference() {
2236 XStream stream = new XStream(new DomDriver());
2237 stream.registerConverter(new TableColumnConverter());
2238 return stream;
2239 }
2240
2241
2242
2243
2244 void showPreferences() {
2245
2246 centerAndSetVisible(logPanelPreferencesFrame);
2247 }
2248
2249 public static void centerAndSetVisible(Window window) {
2250 Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
2251 window.setLocation(new Point((screenDimension.width / 2) - (window.getSize().width / 2),
2252 (screenDimension.height / 2) - (window.getSize().height / 2)));
2253 window.setVisible(true);
2254 }
2255
2256
2257
2258
2259 void showColorPreferences() {
2260 colorPanel.loadLogPanelColorizers();
2261 colorFrame.pack();
2262 centerAndSetVisible(colorFrame);
2263 }
2264
2265
2266
2267
2268 void toggleDetailVisible() {
2269 preferenceModel.setDetailPaneVisible(
2270 !preferenceModel.isDetailPaneVisible());
2271 }
2272
2273
2274
2275
2276
2277
2278 boolean isDetailVisible() {
2279 return preferenceModel.isDetailPaneVisible();
2280 }
2281
2282 boolean isSearchResultsVisible() {
2283 return preferenceModel.isSearchResultsVisible();
2284 }
2285
2286
2287
2288
2289 void toggleLogTreeVisible() {
2290 preferenceModel.setLogTreePanelVisible(
2291 !preferenceModel.isLogTreePanelVisible());
2292 }
2293
2294
2295
2296
2297
2298
2299 boolean isLogTreeVisible() {
2300 return preferenceModel.isLogTreePanelVisible();
2301 }
2302
2303
2304
2305
2306
2307
2308 List getEvents() {
2309 return tableModel.getAllEvents();
2310 }
2311
2312
2313
2314
2315
2316
2317 List getFilteredEvents() {
2318 return tableModel.getFilteredEvents();
2319 }
2320
2321 List<LoggingEventWrapper> getMatchingEvents(Rule rule) {
2322 return tableModel.getMatchingEvents(rule);
2323 }
2324
2325
2326
2327
2328 void clearEvents() {
2329 clearModel();
2330 }
2331
2332
2333
2334
2335
2336
2337 String getIdentifier() {
2338 return identifier;
2339 }
2340
2341
2342
2343
2344
2345 void undock() {
2346 final int row = table.getSelectedRow();
2347 setDocked(false);
2348 externalPanel.removeAll();
2349
2350 externalPanel.add(undockedToolbar, BorderLayout.NORTH);
2351 externalPanel.add(nameTreeAndMainPanelSplit, BorderLayout.CENTER);
2352 externalPanel.setDocked(false);
2353 undockedFrame.pack();
2354
2355 undockedFrame.setVisible(true);
2356 dockingAction.putValue(Action.NAME, "Dock");
2357 dockingAction.putValue(Action.SMALL_ICON, ChainsawIcons.ICON_DOCK);
2358 if (row > -1) {
2359 EventQueue.invokeLater(() -> table.scrollToRow(row));
2360 }
2361 }
2362
2363
2364
2365
2366
2367
2368 void addEventCountListener(EventCountListener l) {
2369 tableModel.addEventCountListener(l);
2370 }
2371
2372
2373
2374
2375
2376
2377 boolean isPaused() {
2378 return paused;
2379 }
2380
2381
2382
2383
2384
2385
2386 void setPaused(boolean paused) {
2387 boolean oldValue = this.paused;
2388 this.paused = paused;
2389 firePropertyChange("paused", oldValue, paused);
2390 }
2391
2392
2393
2394
2395
2396
2397
2398 int setSelectedEvent(int eventNumber) {
2399 int row = tableModel.locate(ExpressionRule.getRule("prop.log4jid == " + eventNumber), 0, true);
2400 if (row > -1) {
2401 preferenceModel.setScrollToBottom(false);
2402
2403 table.scrollToRow(row);
2404 }
2405 return row;
2406 }
2407
2408
2409
2410
2411
2412
2413 void addPreferencePropertyChangeListener(PropertyChangeListener listener) {
2414 preferenceModel.addPropertyChangeListener(listener);
2415 }
2416
2417
2418
2419
2420
2421 void toggleCyclic() {
2422 boolean toggledCyclic = !preferenceModel.isCyclic();
2423
2424 preferenceModel.setCyclic(toggledCyclic);
2425 tableModel.setCyclic(toggledCyclic);
2426 searchModel.setCyclic(toggledCyclic);
2427 }
2428
2429
2430
2431
2432
2433
2434 boolean isCyclic() {
2435 return preferenceModel.isCyclic();
2436 }
2437
2438 public void updateFindRule(String ruleText) {
2439 if ((ruleText == null) || (ruleText.trim().equals(""))) {
2440 findRule = null;
2441 tableModel.updateEventsWithFindRule(null);
2442 colorizer.setFindRule(null);
2443 tableRuleMediator.setFindRule(null);
2444 searchRuleMediator.setFindRule(null);
2445
2446 findCombo.setBackground(UIManager.getColor("TextField.background"));
2447 findCombo.setToolTipText(
2448 "Enter an expression - right click or ctrl-space for menu - press enter to add to list");
2449 currentSearchMatchCount = 0;
2450 currentFindRuleText = null;
2451 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
2452
2453 if (isSearchResultsVisible()) {
2454 hideSearchResults();
2455 }
2456 } else {
2457
2458 preferenceModel.setScrollToBottom(false);
2459 if (ruleText.equals(currentFindRuleText)) {
2460
2461 return;
2462 }
2463 currentFindRuleText = ruleText;
2464 try {
2465 final JTextField findText = (JTextField) findCombo.getEditor().getEditorComponent();
2466 findText.setToolTipText(
2467 "Enter an expression - right click or ctrl-space for menu - press enter to add to list");
2468 findRule = ExpressionRule.getRule(ruleText);
2469 currentSearchMatchCount = tableModel.updateEventsWithFindRule(findRule);
2470 searchModel.updateEventsWithFindRule(findRule);
2471 colorizer.setFindRule(findRule);
2472 tableRuleMediator.setFindRule(findRule);
2473 searchRuleMediator.setFindRule(findRule);
2474
2475 findText.setBackground(UIManager.getColor("TextField.background"));
2476 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
2477 if (isSearchResultsVisible()) {
2478 showSearchResults();
2479 }
2480 } catch (IllegalArgumentException re) {
2481 findRule = null;
2482 final JTextField findText = (JTextField) findCombo.getEditor().getEditorComponent();
2483 findText.setToolTipText(re.getMessage());
2484 findText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
2485 colorizer.setFindRule(null);
2486 tableRuleMediator.setFindRule(null);
2487 searchRuleMediator.setFindRule(null);
2488 tableModel.updateEventsWithFindRule(null);
2489 searchModel.updateEventsWithFindRule(null);
2490 currentSearchMatchCount = 0;
2491 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
2492
2493 if (isSearchResultsVisible()) {
2494 hideSearchResults();
2495 }
2496 }
2497 }
2498 }
2499
2500 private void hideSearchResults() {
2501 if (searchResultsDisplayed) {
2502 detailPanel.removeAll();
2503 JPanel leftSpacePanel = new JPanel();
2504 Integer scrollBarWidth = (Integer) UIManager.get("ScrollBar.width");
2505 leftSpacePanel.setPreferredSize(new Dimension(scrollBarWidth - 4, -1));
2506
2507 JPanel rightSpacePanel = new JPanel();
2508 rightSpacePanel.setPreferredSize(new Dimension(scrollBarWidth - 4, -1));
2509
2510 detailPanel.add(detailToolbar, BorderLayout.NORTH);
2511 detailPanel.add(detailPane, BorderLayout.CENTER);
2512
2513 detailPanel.add(leftSpacePanel, BorderLayout.WEST);
2514 detailPanel.add(rightSpacePanel, BorderLayout.EAST);
2515
2516 detailPanel.revalidate();
2517 detailPanel.repaint();
2518
2519 searchResultsDisplayed = false;
2520
2521 if (!isDetailVisible()) {
2522 hideDetailPane();
2523 }
2524 }
2525 }
2526
2527 private void showSearchResults() {
2528 if (isSearchResultsVisible() && !searchResultsDisplayed && findRule != null) {
2529
2530 detailPanel.removeAll();
2531 detailPanel.add(searchPane, BorderLayout.CENTER);
2532 Integer scrollBarWidth = (Integer) UIManager.get("ScrollBar.width");
2533 JPanel leftSpacePanel = new JPanel();
2534 leftSpacePanel.setPreferredSize(new Dimension(scrollBarWidth - 4, -1));
2535 JPanel rightSpacePanel = new JPanel();
2536 rightSpacePanel.setPreferredSize(new Dimension(scrollBarWidth - 4, -1));
2537 detailPanel.add(leftSpacePanel, BorderLayout.WEST);
2538 detailPanel.add(rightSpacePanel, BorderLayout.EAST);
2539 detailPanel.revalidate();
2540 detailPanel.repaint();
2541
2542 searchResultsDisplayed = true;
2543
2544 if (!isDetailVisible()) {
2545 showDetailPane();
2546 }
2547 }
2548 }
2549
2550
2551
2552
2553 private void showDetailPane() {
2554 if (!isDetailPanelVisible) {
2555 lowerPanel.setDividerSize(dividerSize);
2556 if (lowerPanelDividerLocation == 0) {
2557 lowerPanel.setDividerLocation(DEFAULT_DETAIL_SPLIT_LOCATION);
2558 lowerPanelDividerLocation = lowerPanel.getDividerLocation();
2559 } else {
2560 lowerPanel.setDividerLocation(lowerPanelDividerLocation);
2561 }
2562 detailPanel.setVisible(true);
2563 detailPanel.repaint();
2564 lowerPanel.repaint();
2565 isDetailPanelVisible = true;
2566 }
2567 }
2568
2569
2570
2571
2572 private void hideDetailPane() {
2573
2574 if (isDetailPanelVisible) {
2575 lowerPanelDividerLocation = lowerPanel.getDividerLocation();
2576 }
2577 lowerPanel.setDividerSize(0);
2578 detailPanel.setVisible(false);
2579 lowerPanel.repaint();
2580 isDetailPanelVisible = false;
2581 }
2582
2583
2584
2585
2586 private void showLogTreePanel() {
2587 nameTreeAndMainPanelSplit.setDividerSize(dividerSize);
2588 nameTreeAndMainPanelSplit.setDividerLocation(
2589 lastLogTreePanelSplitLocation);
2590 logTreePanel.setVisible(true);
2591 nameTreeAndMainPanelSplit.repaint();
2592 }
2593
2594
2595
2596
2597 private void hideLogTreePanel() {
2598
2599 int currentSize = nameTreeAndMainPanelSplit.getWidth() - nameTreeAndMainPanelSplit.getDividerSize() - 1;
2600
2601 if (currentSize > 0) {
2602 lastLogTreePanelSplitLocation =
2603 (double) nameTreeAndMainPanelSplit.getDividerLocation() / currentSize;
2604 }
2605 nameTreeAndMainPanelSplit.setDividerSize(0);
2606 logTreePanel.setVisible(false);
2607 nameTreeAndMainPanelSplit.repaint();
2608 }
2609
2610
2611
2612
2613
2614
2615 private JToolBar createDockwindowToolbar() {
2616 final JToolBar toolbar = new JToolBar();
2617 toolbar.setFloatable(false);
2618
2619 final Action dockPauseAction =
2620 new AbstractAction("Pause") {
2621 public void actionPerformed(ActionEvent evt) {
2622 setPaused(!isPaused());
2623 }
2624 };
2625
2626 dockPauseAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_P);
2627 dockPauseAction.putValue(
2628 Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F12"));
2629 dockPauseAction.putValue(
2630 Action.SHORT_DESCRIPTION,
2631 "Halts the display, while still allowing events to stream in the background");
2632 dockPauseAction.putValue(
2633 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.PAUSE));
2634
2635 final SmallToggleButton dockPauseButton =
2636 new SmallToggleButton(dockPauseAction);
2637 dockPauseButton.setText("");
2638
2639 dockPauseButton.getModel().setSelected(isPaused());
2640
2641 addPropertyChangeListener(
2642 "paused",
2643 evt -> dockPauseButton.getModel().setSelected(isPaused()));
2644 toolbar.add(dockPauseButton);
2645
2646 Action dockShowPrefsAction =
2647 new AbstractAction("") {
2648 public void actionPerformed(ActionEvent arg0) {
2649 showPreferences();
2650 }
2651 };
2652
2653 dockShowPrefsAction.putValue(
2654 Action.SHORT_DESCRIPTION, "Define preferences...");
2655 dockShowPrefsAction.putValue(
2656 Action.SMALL_ICON, ChainsawIcons.ICON_PREFERENCES);
2657
2658 toolbar.add(new SmallButton(dockShowPrefsAction));
2659
2660 Action dockToggleLogTreeAction =
2661 new AbstractAction() {
2662 public void actionPerformed(ActionEvent e) {
2663 toggleLogTreeVisible();
2664 }
2665 };
2666
2667 dockToggleLogTreeAction.putValue(Action.SHORT_DESCRIPTION, "Toggles the Logger Tree Pane");
2668 dockToggleLogTreeAction.putValue("enabled", Boolean.TRUE);
2669 dockToggleLogTreeAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_T);
2670 dockToggleLogTreeAction.putValue(
2671 Action.ACCELERATOR_KEY,
2672 KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
2673 dockToggleLogTreeAction.putValue(
2674 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.WINDOW_ICON));
2675
2676 final SmallToggleButton toggleLogTreeButton =
2677 new SmallToggleButton(dockToggleLogTreeAction);
2678 preferenceModel.addPropertyChangeListener("logTreePanelVisible", evt -> toggleLogTreeButton.setSelected(preferenceModel.isLogTreePanelVisible()));
2679
2680 toggleLogTreeButton.setSelected(isLogTreeVisible());
2681 toolbar.add(toggleLogTreeButton);
2682 toolbar.addSeparator();
2683
2684 final Action undockedClearAction =
2685 new AbstractAction("Clear") {
2686 public void actionPerformed(ActionEvent arg0) {
2687 clearModel();
2688 }
2689 };
2690
2691 undockedClearAction.putValue(
2692 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.DELETE));
2693 undockedClearAction.putValue(
2694 Action.SHORT_DESCRIPTION, "Removes all the events from the current view");
2695
2696 final SmallButton dockClearButton = new SmallButton(undockedClearAction);
2697 dockClearButton.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
2698 KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2699 undockedClearAction.getValue(Action.NAME));
2700 dockClearButton.getActionMap().put(
2701 undockedClearAction.getValue(Action.NAME), undockedClearAction);
2702
2703 dockClearButton.setText("");
2704 toolbar.add(dockClearButton);
2705 toolbar.addSeparator();
2706
2707 Action dockToggleScrollToBottomAction =
2708 new AbstractAction("Toggles Scroll to Bottom") {
2709 public void actionPerformed(ActionEvent e) {
2710 toggleScrollToBottom();
2711 }
2712 };
2713
2714 dockToggleScrollToBottomAction.putValue(Action.SHORT_DESCRIPTION, "Toggles Scroll to Bottom");
2715 dockToggleScrollToBottomAction.putValue("enabled", Boolean.TRUE);
2716 dockToggleScrollToBottomAction.putValue(
2717 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.SCROLL_TO_BOTTOM));
2718
2719 final SmallToggleButton toggleScrollToBottomButton =
2720 new SmallToggleButton(dockToggleScrollToBottomAction);
2721 preferenceModel.addPropertyChangeListener("scrollToBottom", evt -> toggleScrollToBottomButton.setSelected(isScrollToBottom()));
2722
2723 toggleScrollToBottomButton.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
2724 KeyStroke.getKeyStroke(KeyEvent.VK_B, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2725 dockToggleScrollToBottomAction.getValue(Action.NAME));
2726 toggleScrollToBottomButton.getActionMap().put(
2727 dockToggleScrollToBottomAction.getValue(Action.NAME), dockToggleScrollToBottomAction);
2728
2729 toggleScrollToBottomButton.setSelected(isScrollToBottom());
2730 toggleScrollToBottomButton.setText("");
2731 toolbar.add(toggleScrollToBottomButton);
2732 toolbar.addSeparator();
2733
2734 findCombo.addActionListener(e -> {
2735
2736 if (e.getActionCommand().equalsIgnoreCase("comboBoxEdited")) {
2737 findNext();
2738 }
2739 });
2740 Action redockAction =
2741 new AbstractAction("", ChainsawIcons.ICON_DOCK) {
2742 public void actionPerformed(ActionEvent arg0) {
2743 dock();
2744 }
2745 };
2746
2747 redockAction.putValue(
2748 Action.SHORT_DESCRIPTION,
2749 "Docks this window back with the main Chainsaw window");
2750
2751 SmallButton redockButton = new SmallButton(redockAction);
2752 toolbar.add(redockButton);
2753
2754 return toolbar;
2755 }
2756
2757
2758
2759
2760 protected void updateStatusBar() {
2761 SwingHelper.invokeOnEDT(
2762 () -> {
2763 statusBar.setSelectedLine(
2764 table.getSelectedRow() + 1, tableModel.getRowCount(),
2765 tableModel.size(), getIdentifier());
2766 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
2767 });
2768 }
2769
2770
2771
2772
2773
2774
2775 private void setDetailPaneConversionPattern(String conversionPattern) {
2776 String oldPattern = getDetailPaneConversionPattern();
2777 (detailLayout).setConversionPattern(conversionPattern);
2778 firePropertyChange(
2779 "detailPaneConversionPattern", oldPattern,
2780 getDetailPaneConversionPattern());
2781 }
2782
2783
2784
2785
2786
2787
2788 private String getDetailPaneConversionPattern() {
2789 return (detailLayout).getConversionPattern();
2790 }
2791
2792
2793
2794
2795 private void clearModel() {
2796 previousLastIndex = -1;
2797 tableModel.clearModel();
2798 searchModel.clearModel();
2799
2800 synchronized (detail) {
2801 detailPaneUpdater.setSelectedRow(-1);
2802 detail.notify();
2803 }
2804
2805 statusBar.setNothingSelected();
2806 }
2807
2808 public void findNextColorizedEvent() {
2809 EventQueue.invokeLater(() -> {
2810 final int nextRow = tableModel.findColoredRow(table.getSelectedRow() + 1, true);
2811 if (nextRow > -1) {
2812 table.scrollToRow(nextRow);
2813 }
2814 });
2815 }
2816
2817 public void findPreviousColorizedEvent() {
2818 EventQueue.invokeLater(() -> {
2819 final int previousRow = tableModel.findColoredRow(table.getSelectedRow() - 1, false);
2820 if (previousRow > -1) {
2821 table.scrollToRow(previousRow);
2822 }
2823 });
2824 }
2825
2826
2827
2828
2829
2830 public void findNext() {
2831 Object item = findCombo.getSelectedItem();
2832 updateFindRule(item == null ? null : item.toString());
2833
2834 if (findRule != null) {
2835 EventQueue.invokeLater(() -> {
2836 final JTextField findText = (JTextField) findCombo.getEditor().getEditorComponent();
2837 try {
2838 int filteredEventsSize = getFilteredEvents().size();
2839 int startRow = table.getSelectedRow() + 1;
2840 if (startRow > filteredEventsSize - 1) {
2841 startRow = 0;
2842 }
2843
2844 final int nextRow = tableModel.locate(findRule, startRow, true);
2845
2846 if (nextRow > -1) {
2847 table.scrollToRow(nextRow);
2848 findText.setToolTipText("Enter an expression - right click or ctrl-space for menu - press enter to add to list");
2849 }
2850 findText.setBackground(UIManager.getColor("TextField.background"));
2851 } catch (IllegalArgumentException iae) {
2852 findText.setToolTipText(iae.getMessage());
2853 findText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
2854 colorizer.setFindRule(null);
2855 tableRuleMediator.setFindRule(null);
2856 searchRuleMediator.setFindRule(null);
2857 }
2858 });
2859 }
2860 }
2861
2862
2863
2864
2865
2866 public void findPrevious() {
2867 Object item = findCombo.getSelectedItem();
2868 updateFindRule(item == null ? null : item.toString());
2869
2870 if (findRule != null) {
2871 EventQueue.invokeLater(() -> {
2872 final JTextField findText = (JTextField) findCombo.getEditor().getEditorComponent();
2873 try {
2874 int startRow = table.getSelectedRow() - 1;
2875 int filteredEventsSize = getFilteredEvents().size();
2876 if (startRow < 0) {
2877 startRow = filteredEventsSize - 1;
2878 }
2879 final int previousRow = tableModel.locate(findRule, startRow, false);
2880
2881 if (previousRow > -1) {
2882 table.scrollToRow(previousRow);
2883 findCombo.setToolTipText("Enter an expression - right click or ctrl-space for menu - press enter to add to list");
2884 }
2885 findText.setBackground(UIManager.getColor("TextField.background"));
2886 } catch (IllegalArgumentException iae) {
2887 findText.setToolTipText(iae.getMessage());
2888 findText.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
2889 }
2890 });
2891 }
2892 }
2893
2894
2895
2896
2897
2898 private void dock() {
2899
2900 final int row = table.getSelectedRow();
2901 setDocked(true);
2902 undockedFrame.setVisible(false);
2903 removeAll();
2904
2905 add(nameTreeAndMainPanelSplit, BorderLayout.CENTER);
2906 externalPanel.setDocked(true);
2907 dockingAction.putValue(Action.NAME, "Undock");
2908 dockingAction.putValue(Action.SMALL_ICON, ChainsawIcons.ICON_UNDOCK);
2909 if (row > -1) {
2910 EventQueue.invokeLater(() -> table.scrollToRow(row));
2911 }
2912 }
2913
2914
2915
2916
2917
2918
2919 private void loadDefaultColumnSettings(LoadSettingsEvent event) {
2920 String columnOrder = event.getSetting(TABLE_COLUMN_ORDER);
2921
2922 TableColumnModel columnModel = table.getColumnModel();
2923 TableColumnModel searchColumnModel = searchTable.getColumnModel();
2924
2925 Map<String, TableColumn> columnNameMap = new HashMap<>();
2926 Map<String, TableColumn> searchColumnNameMap = new HashMap<>();
2927
2928 for (int i = 0; i < columnModel.getColumnCount(); i++) {
2929 columnNameMap.put(table.getColumnName(i).toUpperCase(), columnModel.getColumn(i));
2930 }
2931
2932 for (int i = 0; i < searchColumnModel.getColumnCount(); i++) {
2933 searchColumnNameMap.put(searchTable.getColumnName(i).toUpperCase(), searchColumnModel.getColumn(i));
2934 }
2935
2936 int index;
2937 StringTokenizer tok = new StringTokenizer(columnOrder, ",");
2938 List<TableColumn> sortedColumnList = new ArrayList<>();
2939
2940
2941
2942
2943
2944
2945 while (tok.hasMoreElements()) {
2946 String element = tok.nextElement().toString().trim().toUpperCase();
2947 TableColumn column = columnNameMap.get(element);
2948
2949 if (column != null) {
2950 sortedColumnList.add(column);
2951 table.removeColumn(column);
2952 searchTable.removeColumn(column);
2953 }
2954 }
2955 preferenceModel.setDetailPaneVisible(event.asBoolean("detailPaneVisible"));
2956 preferenceModel.setLogTreePanelVisible(event.asBoolean("logTreePanelVisible"));
2957 preferenceModel.setHighlightSearchMatchText(event.asBoolean("highlightSearchMatchText"));
2958 preferenceModel.setWrapMessage(event.asBoolean("wrapMessage"));
2959 preferenceModel.setSearchResultsVisible(event.asBoolean("searchResultsVisible"));
2960
2961 for (Object aSortedColumnList : sortedColumnList) {
2962 TableColumn element = (TableColumn) aSortedColumnList;
2963 if (preferenceModel.addColumn(element)) {
2964 if (!applicationPreferenceModel.isDefaultColumnsSet() || applicationPreferenceModel.isDefaultColumnsSet() &&
2965 applicationPreferenceModel.getDefaultColumnNames().contains(element.getHeaderValue())) {
2966 table.addColumn(element);
2967 searchTable.addColumn(element);
2968 preferenceModel.setColumnVisible(element.getHeaderValue().toString(), true);
2969 }
2970 }
2971 }
2972
2973 String columnWidths = event.getSetting(TABLE_COLUMN_WIDTHS);
2974
2975 tok = new StringTokenizer(columnWidths, ",");
2976 index = 0;
2977
2978 while (tok.hasMoreElements()) {
2979 String element = (String) tok.nextElement();
2980
2981 try {
2982 int width = Integer.parseInt(element);
2983
2984 if (index > (columnModel.getColumnCount() - 1)) {
2985 logger.warn(
2986 "loadsettings - failed attempt to set width for index " + index
2987 + ", width " + element);
2988 } else {
2989 columnModel.getColumn(index).setPreferredWidth(width);
2990 searchColumnModel.getColumn(index).setPreferredWidth(width);
2991 }
2992
2993 index++;
2994 } catch (NumberFormatException e) {
2995 logger.error("Error decoding a Table width", e);
2996 }
2997 }
2998 undockedFrame.setSize(getSize());
2999 undockedFrame.setLocation(getBounds().x, getBounds().y);
3000
3001 repaint();
3002 }
3003
3004
3005
3006
3007
3008
3009
3010 private int getMaxColumnWidth(int index) {
3011 FontMetrics metrics = getGraphics().getFontMetrics();
3012 int longestWidth =
3013 metrics.stringWidth(" " + table.getColumnName(index) + " ")
3014 + (2 * table.getColumnModel().getColumnMargin());
3015
3016 for (int i = 0, j = tableModel.getRowCount(); i < j; i++) {
3017 Component c =
3018 renderer.getTableCellRendererComponent(
3019 table, table.getValueAt(i, index), false, false, i, index);
3020
3021 if (c instanceof JLabel) {
3022 longestWidth =
3023 Math.max(longestWidth, metrics.stringWidth(((JLabel) c).getText()));
3024 }
3025 }
3026
3027 return longestWidth + 5;
3028 }
3029
3030 private String getToolTipTextForEvent(LoggingEventWrapper loggingEventWrapper) {
3031 return detailLayout.getHeader() + detailLayout.format(loggingEventWrapper.getLoggingEvent()) + detailLayout.getFooter();
3032 }
3033
3034
3035
3036
3037
3038
3039
3040 private void updateOtherModels(LoggingEvent event) {
3041
3042
3043
3044
3045 tableModel.addLoggerName(event.getLoggerName());
3046
3047 filterModel.processNewLoggingEvent(event);
3048 }
3049
3050 public void findNextMarker() {
3051 EventQueue.invokeLater(() -> {
3052 int startRow = table.getSelectedRow() + 1;
3053 int filteredEventsSize = getFilteredEvents().size();
3054 if (startRow > filteredEventsSize - 1) {
3055 startRow = 0;
3056 }
3057 final int nextRow = tableModel.locate(findMarkerRule, startRow, true);
3058
3059 if (nextRow > -1) {
3060 table.scrollToRow(nextRow);
3061 }
3062 });
3063 }
3064
3065 public void findPreviousMarker() {
3066 EventQueue.invokeLater(() -> {
3067 int startRow = table.getSelectedRow() - 1;
3068 int filteredEventsSize = getFilteredEvents().size();
3069 if (startRow < 0) {
3070 startRow = filteredEventsSize - 1;
3071 }
3072 final int previousRow = tableModel.locate(findMarkerRule, startRow, false);
3073
3074 if (previousRow > -1) {
3075 table.scrollToRow(previousRow);
3076 }
3077 });
3078 }
3079
3080 public void clearAllMarkers() {
3081
3082 tableModel.removePropertyFromEvents(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
3083 }
3084
3085 public void toggleMarker() {
3086 int row = table.getSelectedRow();
3087 if (row != -1) {
3088 LoggingEventWrapper loggingEventWrapper = tableModel.getRow(row);
3089 if (loggingEventWrapper != null) {
3090 Object marker = loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
3091 if (marker == null) {
3092 loggingEventWrapper.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, "set");
3093 } else {
3094 loggingEventWrapper.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
3095 }
3096
3097 tableModel.fireRowUpdated(row, (marker == null));
3098 }
3099 }
3100 }
3101
3102 public void layoutComponents() {
3103 if (preferenceModel.isDetailPaneVisible()) {
3104 showDetailPane();
3105 } else {
3106 hideDetailPane();
3107 }
3108 }
3109
3110 public void setFindText(String findText) {
3111 findCombo.setSelectedItem(findText);
3112 findNext();
3113 }
3114
3115 public String getFindText() {
3116 Object selectedItem = findCombo.getSelectedItem();
3117 if (selectedItem == null) {
3118 return "";
3119 }
3120 return selectedItem.toString();
3121 }
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131 private final class DelayedTextDocumentListener
3132 implements DocumentListener {
3133 private static final long CHECK_PERIOD = 1000;
3134 private final JTextField textField;
3135 private long lastTimeStamp = System.currentTimeMillis();
3136 private final Thread delayThread;
3137 private final String defaultToolTip;
3138 private String lastText = "";
3139
3140 private DelayedTextDocumentListener(final JTextField textFeld) {
3141 super();
3142 this.textField = textFeld;
3143 this.defaultToolTip = textFeld.getToolTipText();
3144
3145 this.delayThread =
3146 new Thread(
3147 () -> {
3148 while (true) {
3149 try {
3150 Thread.sleep(CHECK_PERIOD);
3151 } catch (InterruptedException e) {
3152 }
3153
3154 if (
3155 (System.currentTimeMillis() - lastTimeStamp) < CHECK_PERIOD) {
3156
3157
3158
3159 } else if (
3160 (System.currentTimeMillis() - lastTimeStamp) < (2 * CHECK_PERIOD)) {
3161
3162
3163
3164 if (!(textFeld.getText().trim().equals(lastText.trim()))) {
3165 lastText = textFeld.getText();
3166 EventQueue.invokeLater(DelayedTextDocumentListener.this::setFilter);
3167 }
3168 } else {
3169
3170
3171
3172 }
3173 }
3174 });
3175
3176 delayThread.setPriority(Thread.MIN_PRIORITY);
3177 delayThread.start();
3178 }
3179
3180
3181
3182
3183
3184
3185 public void insertUpdate(DocumentEvent e) {
3186 notifyChange();
3187 }
3188
3189
3190
3191
3192
3193
3194 public void removeUpdate(DocumentEvent e) {
3195 notifyChange();
3196 }
3197
3198
3199
3200
3201
3202
3203 public void changedUpdate(DocumentEvent e) {
3204 notifyChange();
3205 }
3206
3207
3208
3209
3210 private void notifyChange() {
3211 this.lastTimeStamp = System.currentTimeMillis();
3212 }
3213
3214
3215
3216
3217 private void setFilter() {
3218 if (textField.getText().trim().equals("")) {
3219
3220 textField.setBackground(UIManager.getColor("TextField.background"));
3221 tableRuleMediator.setFilterRule(null);
3222 searchRuleMediator.setFilterRule(null);
3223 textField.setToolTipText(defaultToolTip);
3224 if (findRule != null) {
3225 currentSearchMatchCount = tableModel.getSearchMatchCount();
3226 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
3227 }
3228 } else {
3229 try {
3230 tableRuleMediator.setFilterRule(ExpressionRule.getRule(textField.getText()));
3231 searchRuleMediator.setFilterRule(ExpressionRule.getRule(textField.getText()));
3232 textField.setToolTipText(defaultToolTip);
3233 if (findRule != null) {
3234 currentSearchMatchCount = tableModel.getSearchMatchCount();
3235 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
3236 }
3237
3238 textField.setBackground(UIManager.getColor("TextField.background"));
3239 } catch (IllegalArgumentException iae) {
3240
3241 textField.setToolTipText(iae.getMessage());
3242 textField.setBackground(ChainsawConstants.INVALID_EXPRESSION_BACKGROUND);
3243 if (findRule != null) {
3244 currentSearchMatchCount = tableModel.getSearchMatchCount();
3245 statusBar.setSearchMatchCount(currentSearchMatchCount, getIdentifier());
3246 }
3247 }
3248 }
3249 }
3250 }
3251
3252 private final class TableMarkerListener extends MouseAdapter {
3253 private JTable markerTable;
3254 private EventContainer markerEventContainer;
3255 private EventContainer otherMarkerEventContainer;
3256
3257 private TableMarkerListener(JTable markerTable, EventContainer markerEventContainer, EventContainer otherMarkerEventContainer) {
3258 this.markerTable = markerTable;
3259 this.markerEventContainer = markerEventContainer;
3260 this.otherMarkerEventContainer = otherMarkerEventContainer;
3261 }
3262
3263 public void mouseClicked(MouseEvent evt) {
3264 if (evt.getClickCount() == 2) {
3265 int row = markerTable.rowAtPoint(evt.getPoint());
3266 if (row != -1) {
3267 LoggingEventWrapper loggingEventWrapper = markerEventContainer.getRow(row);
3268 if (loggingEventWrapper != null) {
3269 Object marker = loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
3270 if (marker == null) {
3271 loggingEventWrapper.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, "set");
3272 } else {
3273 loggingEventWrapper.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
3274 }
3275
3276 markerEventContainer.fireRowUpdated(row, (marker == null));
3277 otherMarkerEventContainer.fireRowUpdated(otherMarkerEventContainer.getRowIndex(loggingEventWrapper), (marker == null));
3278 }
3279 }
3280 }
3281 }
3282 }
3283
3284
3285
3286
3287 private final class TableColumnDetailMouseListener extends MouseMotionAdapter {
3288 private int currentRow = -1;
3289 private JTable detailTable;
3290 private EventContainer detailEventContainer;
3291
3292 private TableColumnDetailMouseListener(JTable detailTable, EventContainer detailEventContainer) {
3293 this.detailTable = detailTable;
3294 this.detailEventContainer = detailEventContainer;
3295 }
3296
3297
3298
3299
3300
3301
3302 public void mouseMoved(MouseEvent evt) {
3303 currentPoint = evt.getPoint();
3304 currentTable = detailTable;
3305
3306 if (preferenceModel.isToolTips()) {
3307 int row = detailTable.rowAtPoint(evt.getPoint());
3308
3309 if ((row == currentRow) || (row == -1)) {
3310 return;
3311 }
3312
3313 currentRow = row;
3314
3315 LoggingEventWrapper event = detailEventContainer.getRow(currentRow);
3316
3317 if (event != null) {
3318 String toolTipText = getToolTipTextForEvent(event);
3319 detailTable.setToolTipText(toolTipText);
3320 }
3321 } else {
3322 detailTable.setToolTipText(null);
3323 }
3324 }
3325 }
3326
3327
3328
3329 private class ChainsawTableColumnModelListener implements TableColumnModelListener {
3330 private JSortTable modelListenerTable;
3331
3332 private ChainsawTableColumnModelListener(JSortTable modelListenerTable) {
3333 this.modelListenerTable = modelListenerTable;
3334 }
3335
3336 public void columnAdded(TableColumnModelEvent e) {
3337
3338 }
3339
3340
3341
3342
3343
3344
3345 public void columnRemoved(TableColumnModelEvent e) {
3346 modelListenerTable.updateSortedColumn();
3347 }
3348
3349
3350
3351
3352
3353
3354 public void columnMoved(TableColumnModelEvent e) {
3355 modelListenerTable.updateSortedColumn();
3356 }
3357
3358
3359
3360
3361
3362
3363 public void columnMarginChanged(ChangeEvent e) {
3364 }
3365
3366
3367
3368
3369
3370
3371 public void columnSelectionChanged(ListSelectionEvent e) {
3372 }
3373 }
3374
3375
3376
3377
3378
3379 private class DetailPaneUpdater implements PropertyChangeListener {
3380 private int selectedRow = -1;
3381 int lastRow = -1;
3382
3383 private DetailPaneUpdater() {
3384 }
3385
3386
3387
3388
3389
3390
3391 private void setSelectedRow(int row) {
3392 selectedRow = row;
3393 updateDetailPane();
3394 }
3395
3396 private void setAndUpdateSelectedRow(int row) {
3397 selectedRow = row;
3398 updateDetailPane(true);
3399 }
3400
3401 private void updateDetailPane() {
3402 updateDetailPane(false);
3403 }
3404
3405
3406
3407
3408 private void updateDetailPane(boolean force) {
3409
3410
3411
3412
3413
3414 if (!preferenceModel.isDetailPaneVisible()) {
3415 return;
3416 }
3417
3418 LoggingEventWrapper loggingEventWrapper = null;
3419 if (force || (selectedRow != -1 && (lastRow != selectedRow))) {
3420 loggingEventWrapper = tableModel.getRow(selectedRow);
3421
3422 if (loggingEventWrapper != null) {
3423 final StringBuilder buf = new StringBuilder();
3424 buf.append(detailLayout.getHeader())
3425 .append(detailLayout.format(loggingEventWrapper.getLoggingEvent())).append(
3426 detailLayout.getFooter());
3427 if (buf.length() > 0) {
3428 try {
3429 final Document doc = detail.getEditorKit().createDefaultDocument();
3430 detail.getEditorKit().read(new StringReader(buf.toString()), doc, 0);
3431
3432 SwingHelper.invokeOnEDT(() -> {
3433 detail.setDocument(doc);
3434 JTextComponentFormatter.applySystemFontAndSize(detail);
3435 detail.setCaretPosition(0);
3436 lastRow = selectedRow;
3437 });
3438 } catch (Exception e) {
3439 }
3440 }
3441 }
3442 }
3443
3444 if (loggingEventWrapper == null && (lastRow != selectedRow)) {
3445 try {
3446 final Document doc = detail.getEditorKit().createDefaultDocument();
3447 detail.getEditorKit().read(new StringReader("<html>Nothing selected</html>"), doc, 0);
3448 SwingHelper.invokeOnEDT(() -> {
3449 detail.setDocument(doc);
3450 JTextComponentFormatter.applySystemFontAndSize(detail);
3451 detail.setCaretPosition(0);
3452 lastRow = selectedRow;
3453 });
3454 } catch (Exception e) {
3455 }
3456 }
3457 }
3458
3459
3460
3461
3462
3463
3464 public void propertyChange(PropertyChangeEvent arg0) {
3465 SwingUtilities.invokeLater(
3466 () -> updateDetailPane(true));
3467 }
3468 }
3469
3470 private class ThrowableDisplayMouseAdapter extends MouseAdapter {
3471 private JTable throwableTable;
3472 private EventContainer throwableEventContainer;
3473 final JDialog detailDialog;
3474 final JEditorPane detailArea;
3475
3476 public ThrowableDisplayMouseAdapter(JTable throwableTable, EventContainer throwableEventContainer) {
3477 this.throwableTable = throwableTable;
3478 this.throwableEventContainer = throwableEventContainer;
3479
3480 detailDialog = new JDialog((JFrame) null, true);
3481 Container container = detailDialog.getContentPane();
3482 detailArea = new JEditorPane();
3483 JTextComponentFormatter.applySystemFontAndSize(detailArea);
3484 detailArea.setEditable(false);
3485 Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
3486 detailArea.setPreferredSize(new Dimension(screenDimension.width / 2, screenDimension.height / 2));
3487 container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
3488 container.add(new JScrollPane(detailArea));
3489
3490 detailDialog.pack();
3491 }
3492
3493 public void mouseClicked(MouseEvent e) {
3494 TableColumn column = throwableTable.getColumnModel().getColumn(throwableTable.columnAtPoint(e.getPoint()));
3495 if (!column.getHeaderValue().toString().toUpperCase().equals(ChainsawColumns.getColumnName(ChainsawColumns.INDEX_THROWABLE_COL_NAME))) {
3496 return;
3497 }
3498
3499 LoggingEventWrapper loggingEventWrapper = throwableEventContainer.getRow(throwableTable.getSelectedRow());
3500
3501
3502 String[] ti = loggingEventWrapper.getLoggingEvent().getThrowableStrRep();
3503 if (ti != null && ti.length > 0 && (!(ti.length == 1 && ti[0].equals("")))) {
3504 detailDialog.setTitle(throwableTable.getColumnName(throwableTable.getSelectedColumn()) + " detail...");
3505 StringBuilder buf = new StringBuilder();
3506 buf.append(loggingEventWrapper.getLoggingEvent().getMessage());
3507 buf.append("\n");
3508 for (String aTi : ti) {
3509 buf.append(aTi).append("\n ");
3510 }
3511
3512 detailArea.setText(buf.toString());
3513 SwingHelper.invokeOnEDT(() -> centerAndSetVisible(detailDialog));
3514 }
3515 }
3516 }
3517
3518 private class MarkerCellEditor implements TableCellEditor {
3519 JTable currentTable;
3520 JTextField textField = new JTextField();
3521 Set<CellEditorListener> cellEditorListeners = new HashSet<>();
3522 private LoggingEventWrapper currentLoggingEventWrapper;
3523 private final Object mutex = new Object();
3524
3525 public Object getCellEditorValue() {
3526 return textField.getText();
3527 }
3528
3529 public boolean isCellEditable(EventObject anEvent) {
3530 return true;
3531 }
3532
3533 public boolean shouldSelectCell(EventObject anEvent) {
3534 textField.selectAll();
3535 return true;
3536 }
3537
3538 public boolean stopCellEditing() {
3539 if (textField.getText().trim().equals("")) {
3540 currentLoggingEventWrapper.removeProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE);
3541 } else {
3542 currentLoggingEventWrapper.setProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE, textField.getText());
3543 }
3544
3545 tableModel.fireRowUpdated(tableModel.getRowIndex(currentLoggingEventWrapper), true);
3546 int index = searchModel.getRowIndex(currentLoggingEventWrapper);
3547 if (index > -1) {
3548 searchModel.fireRowUpdated(index, true);
3549 }
3550
3551 ChangeEvent event = new ChangeEvent(currentTable);
3552 Set<CellEditorListener> cellEditorListenersCopy;
3553 synchronized (mutex) {
3554 cellEditorListenersCopy = new HashSet<>(cellEditorListeners);
3555 }
3556
3557 for (Object aCellEditorListenersCopy : cellEditorListenersCopy) {
3558 ((CellEditorListener) aCellEditorListenersCopy).editingStopped(event);
3559 }
3560 currentLoggingEventWrapper = null;
3561 currentTable = null;
3562
3563 return true;
3564 }
3565
3566 public void cancelCellEditing() {
3567 Set<CellEditorListener> cellEditorListenersCopy;
3568 synchronized (mutex) {
3569 cellEditorListenersCopy = new HashSet<>(cellEditorListeners);
3570 }
3571
3572 ChangeEvent event = new ChangeEvent(currentTable);
3573 for (Object aCellEditorListenersCopy : cellEditorListenersCopy) {
3574 ((CellEditorListener) aCellEditorListenersCopy).editingCanceled(event);
3575 }
3576 currentLoggingEventWrapper = null;
3577 currentTable = null;
3578 }
3579
3580 public void addCellEditorListener(CellEditorListener l) {
3581 synchronized (mutex) {
3582 cellEditorListeners.add(l);
3583 }
3584 }
3585
3586 public void removeCellEditorListener(CellEditorListener l) {
3587 synchronized (mutex) {
3588 cellEditorListeners.remove(l);
3589 }
3590 }
3591
3592 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
3593 currentTable = table;
3594 currentLoggingEventWrapper = ((EventContainer) table.getModel()).getRow(row);
3595 if (currentLoggingEventWrapper != null) {
3596 textField.setText(currentLoggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE));
3597 textField.selectAll();
3598 } else {
3599 textField.setText("");
3600 }
3601 return textField;
3602 }
3603 }
3604
3605 private class EventTimeDeltaMatchThumbnail extends AbstractEventMatchThumbnail {
3606 public EventTimeDeltaMatchThumbnail() {
3607 super();
3608 initializeLists();
3609 }
3610
3611 boolean primaryMatches(ThumbnailLoggingEventWrapper wrapper) {
3612 String millisDelta = wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
3613 if (millisDelta != null && !millisDelta.trim().equals("")) {
3614 long millisDeltaLong = Long.parseLong(millisDelta);
3615
3616 return millisDeltaLong >= 1000;
3617 }
3618 return false;
3619 }
3620
3621 boolean secondaryMatches(ThumbnailLoggingEventWrapper wrapper) {
3622
3623 return false;
3624 }
3625
3626 private void initializeLists() {
3627 secondaryList.clear();
3628 primaryList.clear();
3629
3630 int i = 0;
3631 for (Object o : tableModel.getFilteredEvents()) {
3632 LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) o;
3633 ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, loggingEventWrapper);
3634 i++;
3635
3636 if (primaryMatches(wrapper)) {
3637 primaryList.add(wrapper);
3638 }
3639 }
3640 revalidate();
3641 repaint();
3642 }
3643
3644 public void paintComponent(Graphics g) {
3645 super.paintComponent(g);
3646
3647 int rowCount = table.getRowCount();
3648 if (rowCount == 0) {
3649 return;
3650 }
3651
3652
3653 int height = eventsPane.getHeight();
3654 int maxHeight = Math.min(maxEventHeight, (height / rowCount));
3655 int minHeight = Math.max(1, maxHeight);
3656 int componentHeight = height - minHeight;
3657 int eventHeight = minHeight;
3658
3659
3660 for (Object aPrimaryList : primaryList) {
3661 ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper) aPrimaryList;
3662 if (primaryMatches(wrapper)) {
3663 float ratio = (wrapper.rowNum / (float) rowCount);
3664
3665 int verticalLocation = (int) (componentHeight * ratio);
3666
3667 int startX = 1;
3668 int width = getWidth() - (startX * 2);
3669
3670 String millisDelta = wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.MILLIS_DELTA_COL_NAME_LOWERCASE);
3671 long millisDeltaLong = Long.parseLong(millisDelta);
3672 long delta = Math.min(ChainsawConstants.MILLIS_DELTA_RENDERING_HEIGHT_MAX, Math.max(0, (long) (millisDeltaLong * ChainsawConstants.MILLIS_DELTA_RENDERING_FACTOR)));
3673 float widthMaxMillisDeltaRenderRatio = ((float) width / ChainsawConstants.MILLIS_DELTA_RENDERING_HEIGHT_MAX);
3674 int widthToUse = Math.max(2, (int) (delta * widthMaxMillisDeltaRenderRatio));
3675 eventHeight = Math.min(maxEventHeight, eventHeight + 3);
3676
3677 drawEvent(applicationPreferenceModel.getDeltaColor(), (verticalLocation - eventHeight + 1), eventHeight, g, startX, widthToUse);
3678
3679 }
3680 }
3681 }
3682 }
3683
3684
3685 private class ColorizedEventAndSearchMatchThumbnail extends AbstractEventMatchThumbnail {
3686 public ColorizedEventAndSearchMatchThumbnail() {
3687 super();
3688 configureColors();
3689 }
3690
3691 boolean primaryMatches(ThumbnailLoggingEventWrapper wrapper) {
3692 return !wrapper.loggingEventWrapper.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND);
3693 }
3694
3695 boolean secondaryMatches(ThumbnailLoggingEventWrapper wrapper) {
3696 return wrapper.loggingEventWrapper.isSearchMatch();
3697 }
3698
3699 private void configureColors() {
3700 secondaryList.clear();
3701 primaryList.clear();
3702
3703 int i = 0;
3704 for (Object o : tableModel.getFilteredEvents()) {
3705 LoggingEventWrapper loggingEventWrapper = (LoggingEventWrapper) o;
3706 ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, loggingEventWrapper);
3707 if (secondaryMatches(wrapper)) {
3708 secondaryList.add(wrapper);
3709 }
3710 i++;
3711
3712 if (primaryMatches(wrapper)) {
3713 primaryList.add(wrapper);
3714 }
3715 }
3716 revalidate();
3717 repaint();
3718 }
3719
3720 public void paintComponent(Graphics g) {
3721 super.paintComponent(g);
3722
3723 int rowCount = table.getRowCount();
3724 if (rowCount == 0) {
3725 return;
3726 }
3727
3728
3729 int height = eventsPane.getHeight();
3730 int maxHeight = Math.min(maxEventHeight, (height / rowCount));
3731 int minHeight = Math.max(1, maxHeight);
3732 int componentHeight = height - minHeight;
3733 int eventHeight = minHeight;
3734
3735
3736 for (Object aPrimaryList1 : primaryList) {
3737 ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper) aPrimaryList1;
3738 if (!wrapper.loggingEventWrapper.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND)) {
3739 if (wrapper.loggingEventWrapper.getLoggingEvent().getLevel().toInt() < Level.WARN.toInt() && wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE) == null) {
3740 float ratio = (wrapper.rowNum / (float) rowCount);
3741
3742 int verticalLocation = (int) (componentHeight * ratio);
3743
3744 int startX = 1;
3745 int width = getWidth() - (startX * 2);
3746
3747 drawEvent(wrapper.loggingEventWrapper.getColorRuleBackground(), verticalLocation, eventHeight, g, startX, width);
3748
3749 }
3750 }
3751 }
3752
3753
3754 for (Object aPrimaryList : primaryList) {
3755 ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper) aPrimaryList;
3756 if (!wrapper.loggingEventWrapper.getColorRuleBackground().equals(ChainsawConstants.COLOR_DEFAULT_BACKGROUND)) {
3757 if (wrapper.loggingEventWrapper.getLoggingEvent().getLevel().toInt() >= Level.WARN.toInt() || wrapper.loggingEventWrapper.getLoggingEvent().getProperty(ChainsawConstants.LOG4J_MARKER_COL_NAME_LOWERCASE) != null) {
3758 float ratio = (wrapper.rowNum / (float) rowCount);
3759
3760 int verticalLocation = (int) (componentHeight * ratio);
3761
3762 int startX = 1;
3763 int width = getWidth() - (startX * 2);
3764
3765
3766
3767 eventHeight = Math.min(maxEventHeight, eventHeight + 3);
3768
3769
3770 drawEvent(wrapper.loggingEventWrapper.getColorRuleBackground(), (verticalLocation - eventHeight + 1), eventHeight, g, startX, width);
3771
3772 }
3773 }
3774 }
3775
3776 for (Object aSecondaryList : secondaryList) {
3777 ThumbnailLoggingEventWrapper wrapper = (ThumbnailLoggingEventWrapper) aSecondaryList;
3778 float ratio = (wrapper.rowNum / (float) rowCount);
3779
3780 int verticalLocation = (int) (componentHeight * ratio);
3781
3782 int startX = 1;
3783 int width = getWidth() - (startX * 2);
3784 width = (width / 2);
3785
3786
3787 drawEvent(Color.BLACK, verticalLocation, eventHeight, g, startX, width);
3788
3789 }
3790 }
3791 }
3792
3793 abstract class AbstractEventMatchThumbnail extends JPanel {
3794 protected List<ThumbnailLoggingEventWrapper> primaryList = new ArrayList<>();
3795 protected List<ThumbnailLoggingEventWrapper> secondaryList = new ArrayList<>();
3796 protected final int maxEventHeight = 6;
3797
3798 AbstractEventMatchThumbnail() {
3799 super();
3800 addMouseMotionListener(new MouseMotionAdapter() {
3801 public void mouseMoved(MouseEvent e) {
3802 if (preferenceModel.isThumbnailBarToolTips()) {
3803 int yPosition = e.getPoint().y;
3804 ThumbnailLoggingEventWrapper event = getEventWrapperAtPosition(yPosition);
3805 if (event != null) {
3806 setToolTipText(getToolTipTextForEvent(event.loggingEventWrapper));
3807 }
3808 } else {
3809 setToolTipText(null);
3810 }
3811 }
3812 });
3813
3814 addMouseListener(new MouseAdapter() {
3815 public void mouseClicked(MouseEvent e) {
3816 int yPosition = e.getPoint().y;
3817 ThumbnailLoggingEventWrapper event = getEventWrapperAtPosition(yPosition);
3818
3819 if (event != null) {
3820 int id = Integer.parseInt(event.loggingEventWrapper.getLoggingEvent().getProperty("log4jid"));
3821 setSelectedEvent(id);
3822 }
3823 }
3824 });
3825
3826 tableModel.addTableModelListener(e -> {
3827 int firstRow = e.getFirstRow();
3828
3829 int lastRow = Math.min(e.getLastRow(), table.getRowCount() - 1);
3830
3831 if (firstRow < 0 || lastRow < 0) {
3832 primaryList.clear();
3833 secondaryList.clear();
3834 }
3835
3836
3837
3838 List displayedEvents = tableModel.getFilteredEvents();
3839 if (e.getType() == TableModelEvent.INSERT) {
3840
3841 for (int i = firstRow; i < lastRow; i++) {
3842 LoggingEventWrapper event = (LoggingEventWrapper) displayedEvents.get(i);
3843 ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, event);
3844 if (secondaryMatches(wrapper)) {
3845 secondaryList.add(wrapper);
3846
3847 }
3848 if (primaryMatches(wrapper)) {
3849
3850 primaryList.add(wrapper);
3851 }
3852
3853 }
3854
3855
3856
3857 } else if (e.getType() == TableModelEvent.DELETE) {
3858
3859
3860 for (Iterator<ThumbnailLoggingEventWrapper> iter = secondaryList.iterator(); iter.hasNext(); ) {
3861 ThumbnailLoggingEventWrapper wrapper = iter.next();
3862 if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
3863
3864 iter.remove();
3865 }
3866 }
3867 for (Iterator<ThumbnailLoggingEventWrapper> iter = primaryList.iterator(); iter.hasNext(); ) {
3868 ThumbnailLoggingEventWrapper wrapper = iter.next();
3869 if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
3870
3871 iter.remove();
3872 }
3873 }
3874
3875
3876
3877 } else if (e.getType() == TableModelEvent.UPDATE) {
3878
3879
3880 for (Iterator<ThumbnailLoggingEventWrapper> iter = secondaryList.iterator(); iter.hasNext(); ) {
3881 ThumbnailLoggingEventWrapper wrapper = iter.next();
3882 if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
3883
3884 iter.remove();
3885 }
3886 }
3887 for (Iterator<ThumbnailLoggingEventWrapper> iter = primaryList.iterator(); iter.hasNext(); ) {
3888 ThumbnailLoggingEventWrapper wrapper = iter.next();
3889 if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
3890
3891 iter.remove();
3892 }
3893 }
3894
3895
3896 for (int i = firstRow; i <= lastRow; i++) {
3897 LoggingEventWrapper event = (LoggingEventWrapper) displayedEvents.get(i);
3898 ThumbnailLoggingEventWrapper wrapper = new ThumbnailLoggingEventWrapper(i, event);
3899
3900
3901 if (primaryMatches(wrapper)) {
3902
3903 primaryList.add(wrapper);
3904 } else {
3905 primaryList.remove(wrapper);
3906 }
3907
3908 if (secondaryMatches(wrapper)) {
3909
3910
3911 secondaryList.add(wrapper);
3912 } else {
3913 secondaryList.remove(wrapper);
3914 }
3915 }
3916
3917 }
3918 revalidate();
3919 repaint();
3920
3921 EventQueue.invokeLater(() -> {
3922 if (isScrollToBottom()) {
3923 scrollToBottom();
3924 }
3925 });
3926 });
3927 }
3928
3929 abstract boolean primaryMatches(ThumbnailLoggingEventWrapper wrapper);
3930
3931 abstract boolean secondaryMatches(ThumbnailLoggingEventWrapper wrapper);
3932
3933
3934
3935
3936
3937
3938
3939 protected ThumbnailLoggingEventWrapper getEventWrapperAtPosition(int yPosition) {
3940 int rowCount = table.getRowCount();
3941
3942
3943 int height = eventsPane.getHeight();
3944
3945 yPosition = Math.max(yPosition, 0);
3946
3947
3948 if (yPosition >= height) {
3949 yPosition = height;
3950 }
3951
3952
3953 float ratio = (float) yPosition / height;
3954 int rowToSelect = Math.round(rowCount * ratio);
3955
3956 ThumbnailLoggingEventWrapper event = getClosestRow(rowToSelect);
3957 return event;
3958 }
3959
3960 private ThumbnailLoggingEventWrapper getClosestRow(int rowToSelect) {
3961 ThumbnailLoggingEventWrapper closestRow = null;
3962 int rowDelta = Integer.MAX_VALUE;
3963 for (Object aSecondaryList : secondaryList) {
3964 ThumbnailLoggingEventWrapper event = (ThumbnailLoggingEventWrapper) aSecondaryList;
3965 int newRowDelta = Math.abs(rowToSelect - event.rowNum);
3966 if (newRowDelta < rowDelta) {
3967 closestRow = event;
3968 rowDelta = newRowDelta;
3969 }
3970 }
3971 for (Object aPrimaryList : primaryList) {
3972 ThumbnailLoggingEventWrapper event = (ThumbnailLoggingEventWrapper) aPrimaryList;
3973 int newRowDelta = Math.abs(rowToSelect - event.rowNum);
3974 if (newRowDelta < rowDelta) {
3975 closestRow = event;
3976 rowDelta = newRowDelta;
3977 }
3978 }
3979 return closestRow;
3980 }
3981
3982 public Point getToolTipLocation(MouseEvent event) {
3983
3984 return new Point(event.getX(), event.getY() + 30);
3985 }
3986
3987 protected void drawEvent(Color newColor, int verticalLocation, int eventHeight, Graphics g, int x, int width) {
3988
3989
3990 int y = verticalLocation + (eventHeight / 2);
3991 Color oldColor = g.getColor();
3992 g.setColor(newColor);
3993 g.fillRect(x, y, width, eventHeight);
3994 if (eventHeight >= 3) {
3995 g.setColor(newColor.darker());
3996 g.drawRect(x, y, width, eventHeight);
3997 }
3998 g.setColor(oldColor);
3999 }
4000 }
4001
4002 class ThumbnailLoggingEventWrapper {
4003 int rowNum;
4004 LoggingEventWrapper loggingEventWrapper;
4005
4006 public ThumbnailLoggingEventWrapper(int rowNum, LoggingEventWrapper loggingEventWrapper) {
4007 this.rowNum = rowNum;
4008 this.loggingEventWrapper = loggingEventWrapper;
4009 }
4010
4011 public String toString() {
4012 return "event - rownum: " + rowNum + ", level: " + loggingEventWrapper.getLoggingEvent().getLevel();
4013 }
4014
4015 public boolean equals(Object o) {
4016 if (this == o) {
4017 return true;
4018 }
4019 if (o == null || getClass() != o.getClass()) {
4020 return false;
4021 }
4022
4023 ThumbnailLoggingEventWrapper that = (ThumbnailLoggingEventWrapper) o;
4024
4025 return loggingEventWrapper != null ? loggingEventWrapper.equals(that.loggingEventWrapper) : that.loggingEventWrapper == null;
4026 }
4027
4028 public int hashCode() {
4029 return loggingEventWrapper != null ? loggingEventWrapper.hashCode() : 0;
4030 }
4031 }
4032
4033 class AutoFilterComboBox extends JComboBox {
4034 private boolean bypassFiltering;
4035 private List allEntries = new ArrayList();
4036 private List displayedEntries = new ArrayList();
4037 private AutoFilterComboBoxModel model = new AutoFilterComboBoxModel();
4038
4039 private final JTextField textField = new JTextField();
4040 private String lastTextToMatch;
4041
4042 public AutoFilterComboBox() {
4043 textField.setPreferredSize(getPreferredSize());
4044 setModel(model);
4045 setEditor(new AutoFilterEditor());
4046 ((JTextField) getEditor().getEditorComponent()).getDocument().addDocumentListener(new AutoFilterDocumentListener());
4047 setEditable(true);
4048 addPopupMenuListener(new PopupMenuListenerImpl());
4049 }
4050
4051 public Vector getModelData() {
4052
4053 Vector vector = new Vector();
4054 for (Object allEntry : allEntries) {
4055 vector.insertElementAt(allEntry, 0);
4056 }
4057 return vector;
4058 }
4059
4060 private void refilter() {
4061
4062 String textToMatch = getEditor().getItem().toString();
4063 if (bypassFiltering || (lastTextToMatch != null && lastTextToMatch.equals(textToMatch))) {
4064 return;
4065 }
4066 lastTextToMatch = textToMatch;
4067 bypassFiltering = true;
4068 model.removeAllElements();
4069 List entriesCopy = new ArrayList(allEntries);
4070 for (Object anEntriesCopy : entriesCopy) {
4071 String thisEntry = anEntriesCopy.toString();
4072 if (thisEntry.toLowerCase(Locale.ENGLISH).contains(textToMatch.toLowerCase())) {
4073 model.addElement(thisEntry);
4074 }
4075 }
4076 bypassFiltering = false;
4077
4078 if (displayedEntries.size() > 0 && !textToMatch.equals("")) {
4079 showPopup();
4080 } else {
4081 hidePopup();
4082 }
4083 }
4084
4085 class AutoFilterEditor implements ComboBoxEditor {
4086 public Component getEditorComponent() {
4087 return textField;
4088 }
4089
4090 public void setItem(Object item) {
4091 if (bypassFiltering) {
4092 return;
4093 }
4094 bypassFiltering = true;
4095 if (item == null) {
4096 textField.setText("");
4097 } else {
4098 textField.setText(item.toString());
4099 }
4100 bypassFiltering = false;
4101 }
4102
4103 public Object getItem() {
4104 return textField.getText();
4105 }
4106
4107 public void selectAll() {
4108 textField.selectAll();
4109 }
4110
4111 public void addActionListener(ActionListener listener) {
4112 textField.addActionListener(listener);
4113 }
4114
4115 public void removeActionListener(ActionListener listener) {
4116 textField.removeActionListener(listener);
4117 }
4118 }
4119
4120 class AutoFilterDocumentListener implements DocumentListener {
4121 public void insertUpdate(DocumentEvent e) {
4122 refilter();
4123 }
4124
4125 public void removeUpdate(DocumentEvent e) {
4126 refilter();
4127 }
4128
4129 public void changedUpdate(DocumentEvent e) {
4130 refilter();
4131 }
4132 }
4133
4134 class AutoFilterComboBoxModel extends AbstractListModel implements MutableComboBoxModel {
4135 private Object selectedItem;
4136
4137 public void addElement(Object obj) {
4138
4139 bypassFiltering = true;
4140
4141 boolean entryExists = !allEntries.contains(obj);
4142 if (entryExists) {
4143 allEntries.add(obj);
4144 }
4145 displayedEntries.add(obj);
4146 if (!entryExists) {
4147 fireIntervalAdded(this, displayedEntries.size() - 1, displayedEntries.size());
4148 }
4149 bypassFiltering = false;
4150 }
4151
4152 public void removeElement(Object obj) {
4153 int index = displayedEntries.indexOf(obj);
4154 if (index != -1) {
4155 removeElementAt(index);
4156 }
4157 }
4158
4159 public void insertElementAt(Object obj, int index) {
4160
4161 if (allEntries.contains(obj)) {
4162 return;
4163 }
4164 bypassFiltering = true;
4165 displayedEntries.add(index, obj);
4166 allEntries.add(index, obj);
4167 fireIntervalAdded(this, index, index);
4168 bypassFiltering = false;
4169 refilter();
4170 }
4171
4172 public void removeElementAt(int index) {
4173 bypassFiltering = true;
4174
4175 Object obj = displayedEntries.get(index);
4176 allEntries.remove(obj);
4177 displayedEntries.remove(obj);
4178 fireIntervalRemoved(this, index, index);
4179 bypassFiltering = false;
4180 refilter();
4181 }
4182
4183 public void setSelectedItem(Object item) {
4184 if ((selectedItem != null && !selectedItem.equals(item)) || selectedItem == null && item != null) {
4185 selectedItem = item;
4186 fireContentsChanged(this, -1, -1);
4187 }
4188 }
4189
4190 public Object getSelectedItem() {
4191 return selectedItem;
4192 }
4193
4194 public int getSize() {
4195 return displayedEntries.size();
4196 }
4197
4198 public Object getElementAt(int index) {
4199 if (index >= 0 && index < displayedEntries.size()) {
4200 return displayedEntries.get(index);
4201 }
4202 return null;
4203 }
4204
4205 public void removeAllElements() {
4206 bypassFiltering = true;
4207 int displayedEntrySize = displayedEntries.size();
4208 if (displayedEntrySize > 0) {
4209 displayedEntries.clear();
4210
4211 fireIntervalRemoved(this, 0, displayedEntrySize - 1);
4212 }
4213 bypassFiltering = false;
4214 }
4215
4216 public void showAllElements() {
4217
4218 removeAllElements();
4219 bypassFiltering = true;
4220 displayedEntries.addAll(allEntries);
4221 if (displayedEntries.size() > 0) {
4222 fireIntervalAdded(this, 0, displayedEntries.size() - 1);
4223 }
4224 bypassFiltering = false;
4225 }
4226 }
4227
4228 private class PopupMenuListenerImpl implements PopupMenuListener {
4229 private boolean willBecomeVisible = false;
4230
4231 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
4232 bypassFiltering = true;
4233 ((JComboBox) e.getSource()).setSelectedIndex(-1);
4234 bypassFiltering = false;
4235 if (!willBecomeVisible) {
4236
4237 if (displayedEntries.contains(textField.getText())) {
4238 model.showAllElements();
4239 }
4240
4241
4242
4243 JComboBox list = (JComboBox) e.getSource();
4244 willBecomeVisible = true;
4245 try {
4246 list.getUI().setPopupVisible(list, true);
4247 } finally {
4248 willBecomeVisible = false;
4249 }
4250 }
4251 }
4252
4253 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
4254
4255 }
4256
4257 public void popupMenuCanceled(PopupMenuEvent e) {
4258
4259 }
4260 }
4261 }
4262
4263 class ToggleToolTips extends JCheckBoxMenuItem {
4264 public ToggleToolTips() {
4265 super("Show ToolTips", new ImageIcon(ChainsawIcons.TOOL_TIP));
4266 addActionListener(
4267 evt -> preferenceModel.setToolTips(isSelected()));
4268 }
4269 }
4270 }