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 }