View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j.chainsaw;
18  
19  import org.apache.log4j.LogManager;
20  import org.apache.log4j.Logger;
21  
22  import javax.swing.*;
23  import javax.swing.table.TableColumn;
24  import javax.swing.tree.DefaultMutableTreeNode;
25  import javax.swing.tree.DefaultTreeModel;
26  import javax.swing.tree.TreeModel;
27  import java.awt.*;
28  import java.awt.event.ActionListener;
29  import java.awt.event.MouseAdapter;
30  import java.awt.event.MouseEvent;
31  import java.util.ArrayList;
32  import java.util.Enumeration;
33  import java.util.List;
34  
35  
36  /**
37   * GUI panel used to manipulate the PreferenceModel for a Log Panel
38   *
39   * @author Paul Smith
40   */
41  public class LogPanelPreferencePanel extends AbstractPreferencePanel {
42      //~ Instance fields =========================================================
43  
44      private final LogPanelPreferenceModel preferenceModel;
45      private final ModifiableListModel columnListModel = new ModifiableListModel();
46      private static final Logger logger = LogManager.getLogger(LogPanelPreferencePanel.class);
47      private ApplicationPreferenceModel appPreferenceModel;
48  
49      //~ Constructors ============================================================
50  
51      public LogPanelPreferencePanel(LogPanelPreferenceModel model, ApplicationPreferenceModel appModel) {
52          preferenceModel = model;
53          appPreferenceModel = appModel;
54          initComponents();
55  
56          getOkButton().addActionListener(e -> hidePanel());
57  
58          getCancelButton().addActionListener(e -> hidePanel());
59      }
60  
61      //~ Methods =================================================================
62  
63      /**
64       * DOCUMENT ME!
65       *
66       * @param args DOCUMENT ME!
67       */
68      public static void main(String[] args) {
69          JFrame f = new JFrame("Preferences Panel Test Bed");
70          LogPanelPreferenceModel model = new LogPanelPreferenceModel();
71          ApplicationPreferenceModel appModel = new ApplicationPreferenceModel();
72          LogPanelPreferencePanel panel = new LogPanelPreferencePanel(model, appModel);
73          f.getContentPane().add(panel);
74  
75          model.addPropertyChangeListener(evt -> logger.warn(evt.toString()));
76          panel.setOkCancelActionListener(e -> System.exit(1));
77  
78          f.setSize(640, 480);
79          f.setVisible(true);
80      }
81  
82      protected TreeModel createTreeModel() {
83          final DefaultMutableTreeNode rootNode =
84              new DefaultMutableTreeNode("Preferences");
85          DefaultTreeModel model = new DefaultTreeModel(rootNode);
86  
87          DefaultMutableTreeNode visuals =
88              new DefaultMutableTreeNode(new VisualsPrefPanel());
89          DefaultMutableTreeNode formatting =
90              new DefaultMutableTreeNode(new FormattingPanel());
91          DefaultMutableTreeNode columns =
92              new DefaultMutableTreeNode(new ColumnSelectorPanel());
93  
94          rootNode.add(visuals);
95          rootNode.add(formatting);
96          rootNode.add(columns);
97  
98          return model;
99      }
100 
101     //~ Inner Classes ===========================================================
102 
103     /**
104      * Allows the user to choose which columns to display.
105      *
106      * @author Paul Smith
107      */
108     public class ColumnSelectorPanel extends BasicPrefPanel {
109         //~ Constructors ==========================================================
110 
111         ColumnSelectorPanel() {
112             super("Columns");
113             initComponents();
114         }
115 
116         //~ Methods ===============================================================
117 
118         private void initComponents() {
119             setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
120 
121             Box columnBox = new Box(BoxLayout.Y_AXIS);
122 
123             //		columnBox.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Displayed Columns"));
124             final JList columnList = new JList();
125             columnList.setVisibleRowCount(17);
126 
127             for (Object o : preferenceModel.getColumns()) {
128                 TableColumn col = (TableColumn) o;
129                 Enumeration enumeration = columnListModel.elements();
130                 boolean found = false;
131                 while (enumeration.hasMoreElements()) {
132                     TableColumn thisCol = (TableColumn) enumeration.nextElement();
133                     if (thisCol.getHeaderValue().equals(col.getHeaderValue())) {
134                         found = true;
135                     }
136                 }
137                 if (!found) {
138                     columnListModel.addElement(col);
139                 }
140             }
141 
142             columnList.setModel(columnListModel);
143 
144             CheckListCellRenderer cellRenderer = new CheckListCellRenderer() {
145                 protected boolean isSelected(Object value) {
146                     return LogPanelPreferencePanel.this.preferenceModel.isColumnVisible((TableColumn)
147                         value);
148                 }
149             };
150 
151             columnList.addMouseListener(new MouseAdapter() {
152                 public void mouseClicked(MouseEvent e) {
153                     int i = columnList.locationToIndex(e.getPoint());
154 
155                     if (i >= 0) {
156                         Object column = columnListModel.get(i);
157                         preferenceModel.toggleColumn(((TableColumn) column));
158                     }
159                 }
160             });
161             JButton setAsDefaultsButton = new JButton("Use selected columns as default visible columns");
162             setAsDefaultsButton.addActionListener(actionEvent -> {
163                 List selectedColumns = new ArrayList();
164                 for (int i = 0; i < columnListModel.getSize(); i++) {
165                     if (preferenceModel.isColumnVisible((TableColumn) columnListModel.get(i))) {
166                         selectedColumns.add(((TableColumn) columnListModel.get(i)).getHeaderValue());
167                     }
168                 }
169                 appPreferenceModel.setDefaultColumnNames(selectedColumns);
170             });
171             columnList.setCellRenderer(cellRenderer);
172             columnBox.add(new JScrollPane(columnList));
173             columnBox.add(Box.createVerticalStrut(5));
174             columnBox.add(setAsDefaultsButton);
175             add(columnBox);
176             add(Box.createVerticalGlue());
177         }
178     }
179 
180     /**
181      * Provides preference gui's for all the Formatting options
182      * available for the columns etc.
183      */
184     private class FormattingPanel extends BasicPrefPanel {
185         //~ Instance fields =======================================================
186 
187         private JTextField customFormatText = new JTextField("", 10);
188         private JTextField loggerPrecision = new JTextField(10);
189         private JRadioButton rdCustom = new JRadioButton("Custom Format ");
190         private final JRadioButton rdISO =
191             new JRadioButton(
192                 "<html><b>Fast</b> ISO 8601 format (yyyy-MM-dd HH:mm:ss) </html>");
193         private final JTextField timeZone = new JTextField(10);
194         private final JRadioButton rdLevelIcons = new JRadioButton("Icons ");
195         private final JRadioButton rdLevelText = new JRadioButton("Text ");
196         private JRadioButton rdLast;
197 
198         //~ Constructors ==========================================================
199 
200         private FormattingPanel() {
201             super("Formatting");
202             this.initComponents();
203             setupListeners();
204         }
205 
206         //~ Methods ===============================================================
207 
208         private void initComponents() {
209             setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
210 
211             JPanel dateFormatPanel = new JPanel();
212             dateFormatPanel.setLayout(new BoxLayout(dateFormatPanel, BoxLayout.Y_AXIS));
213             dateFormatPanel.setBorder(
214                 BorderFactory.createTitledBorder(
215                     BorderFactory.createEtchedBorder(), "Timestamp"));
216 
217             ButtonGroup bgDateFormat = new ButtonGroup();
218 
219             rdISO.setSelected(preferenceModel.isUseISO8601Format());
220 
221             rdISO.setHorizontalTextPosition(SwingConstants.RIGHT);
222             rdISO.setAlignmentX(Component.LEFT_ALIGNMENT);
223 
224             bgDateFormat.add(rdISO);
225             dateFormatPanel.add(rdISO);
226 
227             for (Object DATE_FORMAT : LogPanelPreferenceModel.DATE_FORMATS) {
228                 final String format = (String) DATE_FORMAT;
229                 final JRadioButton rdFormat = new JRadioButton(format);
230                 rdFormat.setHorizontalTextPosition(SwingConstants.RIGHT);
231                 rdFormat.setAlignmentX(Component.LEFT_ALIGNMENT);
232 
233                 rdFormat.addActionListener(e -> {
234                     preferenceModel.setDateFormatPattern(format);
235                     customFormatText.setEnabled(rdCustom.isSelected());
236                     rdLast = rdFormat;
237                 });
238                 //update based on external changes to dateformatpattern (column context
239                 //menu)
240                 preferenceModel.addPropertyChangeListener(
241                     "dateFormatPattern", evt -> {
242                         rdFormat.setSelected(
243                             preferenceModel.getDateFormatPattern().equals(format));
244                         rdLast = rdFormat;
245                     });
246 
247                 dateFormatPanel.add(rdFormat);
248                 bgDateFormat.add(rdFormat);
249             }
250 
251             customFormatText.setPreferredSize(new Dimension(100, 20));
252             customFormatText.setMaximumSize(customFormatText.getPreferredSize());
253             customFormatText.setMinimumSize(customFormatText.getPreferredSize());
254             customFormatText.setEnabled(false);
255 
256             bgDateFormat.add(rdCustom);
257             rdCustom.setSelected(preferenceModel.isCustomDateFormat());
258 
259             // add a custom date format
260             if (preferenceModel.isCustomDateFormat()) {
261                 customFormatText.setText(preferenceModel.getDateFormatPattern());
262                 customFormatText.setEnabled(true);
263             }
264 
265             JPanel customPanel = new JPanel();
266             customPanel.setLayout(new BoxLayout(customPanel, BoxLayout.X_AXIS));
267             customPanel.add(rdCustom);
268             customPanel.add(customFormatText);
269             customPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
270 
271             dateFormatPanel.add(customPanel);
272             dateFormatPanel.add(Box.createVerticalStrut(5));
273 
274             JLabel dateFormatLabel = new JLabel("Time zone of events (or blank for local time zone");
275             dateFormatPanel.add(dateFormatLabel);
276 
277             timeZone.setMaximumSize(timeZone.getPreferredSize());
278             dateFormatPanel.add(Box.createVerticalStrut(5));
279             dateFormatPanel.add(timeZone);
280 
281             add(dateFormatPanel);
282 
283             JPanel levelFormatPanel = new JPanel();
284             levelFormatPanel.setLayout(
285                 new BoxLayout(levelFormatPanel, BoxLayout.Y_AXIS));
286             levelFormatPanel.setBorder(
287                 BorderFactory.createTitledBorder(
288                     BorderFactory.createEtchedBorder(), "Level"));
289             levelFormatPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
290 
291             ButtonGroup bgLevel = new ButtonGroup();
292             bgLevel.add(rdLevelIcons);
293             bgLevel.add(rdLevelText);
294 
295             rdLevelIcons.setSelected(preferenceModel.isLevelIcons());
296 
297             levelFormatPanel.add(rdLevelIcons);
298             levelFormatPanel.add(rdLevelText);
299 
300             add(levelFormatPanel);
301 
302             JPanel loggerFormatPanel = new JPanel();
303             loggerFormatPanel.setLayout(
304                 new BoxLayout(loggerFormatPanel, BoxLayout.Y_AXIS));
305             loggerFormatPanel.setBorder(
306                 BorderFactory.createTitledBorder(
307                     BorderFactory.createEtchedBorder(), "Logger"));
308             loggerFormatPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
309 
310             loggerFormatPanel.add(Box.createVerticalStrut(3));
311 
312             final JLabel precisionLabel =
313                 new JLabel("Number of package levels to hide (or blank to display full logger)");
314 
315             loggerFormatPanel.add(precisionLabel);
316             loggerFormatPanel.add(Box.createVerticalStrut(5));
317 
318             loggerPrecision.setMaximumSize(loggerPrecision.getPreferredSize());
319             loggerFormatPanel.add(loggerPrecision);
320 
321             add(loggerFormatPanel);
322         }
323 
324         /*
325          * Restore text fields to current model values
326          */
327         private void reset() {
328 
329             if (preferenceModel.isCustomDateFormat()) {
330                 customFormatText.setText(preferenceModel.getDateFormatPattern());
331             } else {
332                 if (rdLast != null) {
333                     rdLast.setSelected(true);
334                 }
335                 customFormatText.setEnabled(false);
336             }
337 
338             loggerPrecision.setText(preferenceModel.getLoggerPrecision());
339             timeZone.setText(preferenceModel.getTimeZone());
340         }
341 
342         /*
343          * Commit text fields to model
344          */
345         private void commit() {
346             if (rdCustom.isSelected()) {
347                 preferenceModel.setDateFormatPattern(customFormatText.getText());
348             }
349             preferenceModel.setLoggerPrecision(loggerPrecision.getText());
350             preferenceModel.setTimeZone(timeZone.getText());
351         }
352 
353         /**
354          * DOCUMENT ME!
355          */
356         private void setupListeners() {
357             getOkButton().addActionListener(evt -> commit());
358 
359             getCancelButton().addActionListener(evt -> reset());
360 
361             rdCustom.addActionListener(e -> {
362                 customFormatText.setEnabled(rdCustom.isSelected());
363                 customFormatText.grabFocus();
364             });
365 
366             //a second?? listener for dateformatpattern
367             preferenceModel.addPropertyChangeListener(
368                 "dateFormatPattern", evt -> {
369                     /**
370                      * we need to make sure we are not reacting to the user typing, so only do this
371                      * if the text box is not the same as the model
372                      */
373                     if (
374                         preferenceModel.isCustomDateFormat()
375                             && !customFormatText.getText().equals(
376                             evt.getNewValue().toString())) {
377                         customFormatText.setText(preferenceModel.getDateFormatPattern());
378                         rdCustom.setSelected(true);
379                         customFormatText.setEnabled(true);
380                     } else {
381                         rdCustom.setSelected(false);
382                     }
383                 });
384 
385             rdISO.addActionListener(e -> {
386                 preferenceModel.setDateFormatPattern("ISO8601");
387                 customFormatText.setEnabled(rdCustom.isSelected());
388                 rdLast = rdISO;
389             });
390             preferenceModel.addPropertyChangeListener(
391                 "dateFormatPattern", evt -> {
392                     rdISO.setSelected(preferenceModel.isUseISO8601Format());
393                     rdLast = rdISO;
394                 });
395             preferenceModel.addPropertyChangeListener(
396                 "dateFormatTimeZone", evt -> timeZone.setText(preferenceModel.getTimeZone())
397             );
398 
399             ActionListener levelIconListener = e -> preferenceModel.setLevelIcons(rdLevelIcons.isSelected());
400 
401             rdLevelIcons.addActionListener(levelIconListener);
402             rdLevelText.addActionListener(levelIconListener);
403 
404             preferenceModel.addPropertyChangeListener(
405                 "levelIcons", evt -> {
406                     boolean value = (Boolean) evt.getNewValue();
407                     rdLevelIcons.setSelected(value);
408                     rdLevelText.setSelected(!value);
409                 });
410         }
411     }
412 
413     /**
414      * DOCUMENT ME!
415      *
416      * @author $author$
417      * @author psmith
418      * @version $Revision$, $Date$
419      */
420     private class VisualsPrefPanel extends BasicPrefPanel {
421         //~ Instance fields =======================================================
422 
423         private final JCheckBox detailPanelVisible =
424             new JCheckBox("Show Event Detail panel");
425 
426         private final JCheckBox loggerTreePanel =
427             new JCheckBox("Show Logger Tree");
428         private final JCheckBox wrapMessage = new JCheckBox("Wrap message field (display multi-line rows) ");
429         private final JCheckBox searchResultsVisible = new JCheckBox("Display find results in details panel ");
430         private final JCheckBox highlightSearchMatchText = new JCheckBox("Highlight find match text ");
431         private final JCheckBox scrollToBottom =
432             new JCheckBox("Scroll to bottom (view tracks with new events)");
433         private final JCheckBox showMillisDeltaAsGap =
434             new JCheckBox("Display timestamp delta between events as row gap");
435         private final JCheckBox toolTips =
436             new JCheckBox("Show Event Detail Tooltips");
437         private final JCheckBox thumbnailBarToolTips =
438             new JCheckBox("Show Thumbnail Bar Tooltips");
439         private final JEditorPane clearTableExpression = new JEditorPane();
440 
441         //~ Constructors ==========================================================
442 
443         /**
444          * Creates a new VisualsPrefPanel object.
445          */
446         private VisualsPrefPanel() {
447             super("Visuals");
448             initPanelComponents();
449             setupListeners();
450         }
451 
452         //~ Methods ===============================================================
453 
454         /**
455          * DOCUMENT ME!
456          */
457         private void initPanelComponents() {
458             JTextComponentFormatter.applySystemFontAndSize(clearTableExpression);
459             setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
460             toolTips.setAlignmentX(Component.LEFT_ALIGNMENT);
461             thumbnailBarToolTips.setAlignmentX(Component.LEFT_ALIGNMENT);
462             detailPanelVisible.setAlignmentX(Component.LEFT_ALIGNMENT);
463             loggerTreePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
464             scrollToBottom.setAlignmentX(Component.LEFT_ALIGNMENT);
465             showMillisDeltaAsGap.setAlignmentX(Component.LEFT_ALIGNMENT);
466             add(toolTips);
467             add(thumbnailBarToolTips);
468             add(detailPanelVisible);
469             add(loggerTreePanel);
470             add(scrollToBottom);
471             add(wrapMessage);
472             add(highlightSearchMatchText);
473             add(searchResultsVisible);
474             add(showMillisDeltaAsGap);
475             JPanel clearPanel = new JPanel(new BorderLayout());
476             clearPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
477             clearPanel.add(new JLabel("Clear all events if expression matches"), BorderLayout.NORTH);
478             clearTableExpression.setText(preferenceModel.getClearTableExpression());
479             clearTableExpression.setPreferredSize(new Dimension(300, 50));
480             JPanel clearTableScrollPanel = new JPanel(new BorderLayout());
481             clearTableScrollPanel.add(new JScrollPane(clearTableExpression), BorderLayout.NORTH);
482             clearPanel.add(clearTableScrollPanel, BorderLayout.CENTER);
483             add(clearPanel);
484 
485             toolTips.setSelected(preferenceModel.isToolTips());
486             thumbnailBarToolTips.setSelected(preferenceModel.isThumbnailBarToolTips());
487             detailPanelVisible.setSelected(preferenceModel.isDetailPaneVisible());
488             searchResultsVisible.setSelected(preferenceModel.isSearchResultsVisible());
489             loggerTreePanel.setSelected(preferenceModel.isLogTreePanelVisible());
490         }
491 
492         /**
493          * DOCUMENT ME!
494          */
495         private void setupListeners() {
496             ActionListener wrapMessageListener = e -> preferenceModel.setWrapMessage(wrapMessage.isSelected());
497 
498             wrapMessage.addActionListener(wrapMessageListener);
499 
500             ActionListener searchResultsVisibleListener = e -> preferenceModel.setSearchResultsVisible(searchResultsVisible.isSelected());
501 
502             searchResultsVisible.addActionListener(searchResultsVisibleListener);
503 
504             ActionListener highlightSearchMatchTextListener = e -> preferenceModel.setHighlightSearchMatchText(highlightSearchMatchText.isSelected());
505 
506             highlightSearchMatchText.addActionListener(highlightSearchMatchTextListener);
507 
508             preferenceModel.addPropertyChangeListener(
509                 "wrapMessage", evt -> {
510                     boolean value = (Boolean) evt.getNewValue();
511                     wrapMessage.setSelected(value);
512                 });
513 
514             preferenceModel.addPropertyChangeListener(
515                 "searchResultsVisible", evt -> {
516                     boolean value = (Boolean) evt.getNewValue();
517                     searchResultsVisible.setSelected(value);
518                 });
519 
520             preferenceModel.addPropertyChangeListener(
521                 "highlightSearchMatchText", evt -> {
522                     boolean value = (Boolean) evt.getNewValue();
523                     highlightSearchMatchText.setSelected(value);
524                 });
525 
526             toolTips.addActionListener(e -> preferenceModel.setToolTips(toolTips.isSelected()));
527 
528             thumbnailBarToolTips.addActionListener(e -> preferenceModel.setThumbnailBarToolTips(thumbnailBarToolTips.isSelected()));
529 
530             getOkButton().addActionListener(e -> preferenceModel.setClearTableExpression(clearTableExpression.getText().trim()));
531 
532             preferenceModel.addPropertyChangeListener(
533                 "toolTips", evt -> {
534                     boolean value = (Boolean) evt.getNewValue();
535                     toolTips.setSelected(value);
536                 });
537 
538             preferenceModel.addPropertyChangeListener(
539                 "thumbnailBarToolTips", evt -> {
540                     boolean value = (Boolean) evt.getNewValue();
541                     thumbnailBarToolTips.setSelected(value);
542                 });
543 
544             detailPanelVisible.addActionListener(e -> preferenceModel.setDetailPaneVisible(detailPanelVisible.isSelected()));
545 
546             preferenceModel.addPropertyChangeListener(
547                 "detailPaneVisible", evt -> {
548                     boolean value = (Boolean) evt.getNewValue();
549                     detailPanelVisible.setSelected(value);
550                 });
551 
552             scrollToBottom.addActionListener(e -> preferenceModel.setScrollToBottom(scrollToBottom.isSelected()));
553 
554             showMillisDeltaAsGap.addActionListener(e -> preferenceModel.setShowMillisDeltaAsGap(showMillisDeltaAsGap.isSelected()));
555 
556             preferenceModel.addPropertyChangeListener("showMillisDeltaAsGap", evt -> {
557                 boolean value = (Boolean) evt.getNewValue();
558                 showMillisDeltaAsGap.setSelected(value);
559             });
560             preferenceModel.addPropertyChangeListener(
561                 "scrollToBottom", evt -> {
562                     boolean value = (Boolean) evt.getNewValue();
563                     scrollToBottom.setSelected(value);
564                 });
565 
566             loggerTreePanel.addActionListener(e -> preferenceModel.setLogTreePanelVisible(loggerTreePanel.isSelected()));
567 
568             preferenceModel.addPropertyChangeListener(
569                 "logTreePanelVisible", evt -> {
570                     boolean value = (Boolean) evt.getNewValue();
571                     loggerTreePanel.setSelected(value);
572                 });
573 
574             preferenceModel.addPropertyChangeListener("columns", evt -> {
575                 List cols = (List) evt.getNewValue();
576                 for (Object col1 : cols) {
577                     TableColumn col = (TableColumn) col1;
578                     Enumeration enumeration = columnListModel.elements();
579                     boolean found = false;
580                     while (enumeration.hasMoreElements()) {
581                         TableColumn thisCol = (TableColumn) enumeration.nextElement();
582                         if (thisCol.getHeaderValue().equals(col.getHeaderValue())) {
583                             found = true;
584                         }
585                     }
586                     if (!found) {
587                         columnListModel.addElement(col);
588                         columnListModel.fireContentsChanged();
589                     }
590                 }
591             });
592 
593             preferenceModel.addPropertyChangeListener(
594                 "visibleColumns", evt -> columnListModel.fireContentsChanged());
595 
596             preferenceModel.addPropertyChangeListener("clearTableExpression", evt -> clearTableExpression.setText(((LogPanelPreferenceModel) evt.getSource()).getClearTableExpression()));
597         }
598     }
599 }