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
    18  package org.apache.log4j.chainsaw.color;
    19
    20  import org.apache.log4j.chainsaw.ApplicationPreferenceModel;
    21  import org.apache.log4j.chainsaw.ChainsawConstants;
    22  import org.apache.log4j.chainsaw.ExpressionRuleContext;
    23  import org.apache.log4j.chainsaw.filter.FilterModel;
    24  import org.apache.log4j.chainsaw.helper.SwingHelper;
    25  import org.apache.log4j.chainsaw.icons.ChainsawIcons;
    26  import org.apache.log4j.rule.ColorRule;
    27  import org.apache.log4j.rule.ExpressionRule;
    28  import org.apache.log4j.rule.Rule;
    29
    30  import javax.swing.*;
    31  import javax.swing.border.Border;
    32  import javax.swing.table.DefaultTableModel;
    33  import javax.swing.table.TableCellRenderer;
    34  import java.awt.*;
    35  import java.awt.event.ActionEvent;
    36  import java.awt.event.ActionListener;
    37  import java.awt.event.ItemEvent;
    38  import java.awt.event.ItemListener;
    39  import java.util.*;
    40  import java.util.List;
    41
    42
    43  /**
    44   * Panel which updates a RuleColorizer, allowing the user to build expression-based
    45   * color rules.
    46   * <p>
    47   * TODO: examine ColorPanel/RuleColorizer/LogPanel listeners and interactions
    48   *
    49   * @author Scott Deboy &lt;sdeboy@apache.org&gt;
    50   */
    51  public class ColorPanel extends JPanel {
    52      private static final String DEFAULT_STATUS = "<html>Double click a rule field to edit the rule</html>";
    53      private final String currentRuleSet = "Default";
    54
    55      private RuleColorizer colorizer;
    56      private JPanel rulesPanel;
    57      private FilterModel filterModel;
    58      private DefaultTableModel tableModel;
    59      private JScrollPane tableScrollPane;
    60      private JTable table;
    61      private ActionListener closeListener;
    62      private JLabel statusBar;
    63      private Vector<String> columns;
    64      private final String noTab = "None";
    65      private DefaultComboBoxModel logPanelColorizersModel;
    66      private Map<String, RuleColorizer> allLogPanelColorizers;
    67      private RuleColorizer currentLogPanelColorizer;
    68      private JTable searchTable;
    69      private DefaultTableModel searchTableModel;
    70      private Vector<String> searchColumns;
    71      private Vector<Vector<Color>> searchDataVector;
    72      private Vector<Color> searchDataVectorEntry;
    73
    74      private JTable alternatingColorTable;
    75      private DefaultTableModel alternatingColorTableModel;
    76      private Vector<String> alternatingColorColumns;
    77      private Vector<Vector<Color>> alternatingColorDataVector;
    78      private Vector<Color> alternatingColorDataVectorEntry;
    79      private ApplicationPreferenceModel applicationPreferenceModel;
    80      private JCheckBox bypassSearchColorsCheckBox;
    81
    82      public ColorPanel(final RuleColorizer currentLogPanelColorizer, final FilterModel filterModel,
    83                        final Map<String, RuleColorizer> allLogPanelColorizers, final ApplicationPreferenceModel applicationPreferenceModel) {
    84          super(new BorderLayout());
    85
    86          this.currentLogPanelColorizer = currentLogPanelColorizer;
    87          this.colorizer = currentLogPanelColorizer;
    88          this.filterModel = filterModel;
    89          this.allLogPanelColorizers = allLogPanelColorizers;
    90          this.applicationPreferenceModel = applicationPreferenceModel;
    91
    92          currentLogPanelColorizer.addPropertyChangeListener(
    93              "colorrule",
    94              evt -> updateColors());
    95
    96          tableModel = new DefaultTableModel();
    97          table = new JTable(tableModel);
    98          table.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
    99
    100         searchTableModel = new DefaultTableModel();
    101         searchTable = new JTable(searchTableModel);
    102         searchTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
    103         searchTable.setPreferredScrollableViewportSize(new Dimension(30, 30));
    104
    105         alternatingColorTableModel = new DefaultTableModel();
    106         alternatingColorTable = new JTable(alternatingColorTableModel);
    107         alternatingColorTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
    108         alternatingColorTable.setPreferredScrollableViewportSize(new Dimension(30, 30));
    109
    110         columns = new Vector<>();
    111         columns.add("Expression");
    112         columns.add("Background");
    113         columns.add("Foreground");
    114
    115         searchColumns = new Vector<>();
    116         searchColumns.add("Background");
    117         searchColumns.add("Foreground");
    118
    119         alternatingColorColumns = new Vector<>();
    120         alternatingColorColumns.add("Background");
    121         alternatingColorColumns.add("Foreground");
    122
    123         //searchtable contains only a single-entry vector containing a two-item vector (foreground, background)
    124         searchDataVector = new Vector<>();
    125         searchDataVectorEntry = new Vector<>();
    126         searchDataVectorEntry.add(applicationPreferenceModel.getSearchBackgroundColor());
    127         searchDataVectorEntry.add(applicationPreferenceModel.getSearchForegroundColor());
    128         searchDataVector.add(searchDataVectorEntry);
    129         searchTableModel.setDataVector(searchDataVector, searchColumns);
    130
    131         alternatingColorDataVector = new Vector<>();
    132         alternatingColorDataVectorEntry = new Vector<>();
    133         alternatingColorDataVectorEntry.add(applicationPreferenceModel.getAlternatingColorBackgroundColor());
    134         alternatingColorDataVectorEntry.add(applicationPreferenceModel.getAlternatingColorForegroundColor());
    135         alternatingColorDataVector.add(alternatingColorDataVectorEntry);
    136         alternatingColorTableModel.setDataVector(alternatingColorDataVector, alternatingColorColumns);
    137
    138         table.setPreferredScrollableViewportSize(new Dimension(525, 200));
    139         tableScrollPane = new JScrollPane(table);
    140
    141         Vector<Vector<java.io.Serializable>> data = getColorizerVector();
    142         tableModel.setDataVector(data, columns);
    143
    144         table.sizeColumnsToFit(0);
    145         table.getColumnModel().getColumn(1).setPreferredWidth(80);
    146         table.getColumnModel().getColumn(2).setPreferredWidth(80);
    147         table.getColumnModel().getColumn(1).setMaxWidth(80);
    148         table.getColumnModel().getColumn(2).setMaxWidth(80);
    149
    150         searchTable.sizeColumnsToFit(0);
    151         searchTable.getColumnModel().getColumn(0).setPreferredWidth(80);
    152         searchTable.getColumnModel().getColumn(1).setPreferredWidth(80);
    153         searchTable.getColumnModel().getColumn(0).setMaxWidth(80);
    154         searchTable.getColumnModel().getColumn(1).setMaxWidth(80);
    155         //building color choosers needs to be done on the EDT
    156         SwingHelper.invokeOnEDT(() -> configureSingleEntryColorTable(searchTable));
    157
    158         alternatingColorTable.sizeColumnsToFit(0);
    159         alternatingColorTable.getColumnModel().getColumn(0).setPreferredWidth(80);
    160         alternatingColorTable.getColumnModel().getColumn(1).setPreferredWidth(80);
    161         alternatingColorTable.getColumnModel().getColumn(0).setMaxWidth(80);
    162         alternatingColorTable.getColumnModel().getColumn(1).setMaxWidth(80);
    163         //building color choosers needs to be done on the EDT
    164         SwingHelper.invokeOnEDT(() -> configureSingleEntryColorTable(alternatingColorTable));
    165
    166         configureTable();
    167
    168         statusBar = new JLabel(DEFAULT_STATUS);
    169
    170         rulesPanel = buildRulesPanel();
    171         rulesPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    172
    173         JPanel rightPanel = new JPanel(new BorderLayout());
    174
    175         JPanel rightOuterPanel = new JPanel();
    176         rightOuterPanel.setLayout(
    177             new BoxLayout(rightOuterPanel, BoxLayout.X_AXIS));
    178         rightOuterPanel.add(Box.createHorizontalStrut(10));
    179
    180         JPanel southPanel = new JPanel();
    181         southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));
    182         southPanel.add(Box.createVerticalStrut(5));
    183         southPanel.add(Box.createVerticalStrut(5));
    184         JPanel searchAndAlternatingColorPanel = buildSearchAndAlternatingColorPanel();
    185         JPanel bypassSearchColorsPanel = buildBypassSearchColorsPanel();
    186         bypassSearchColorsCheckBox.setSelected(applicationPreferenceModel.isBypassSearchColors());
    187
    188         JPanel globalLabelPanel = new JPanel();
    189         globalLabelPanel.setLayout(new BoxLayout(globalLabelPanel, BoxLayout.X_AXIS));
    190         JLabel globalLabel = new JLabel("Global colors:");
    191         globalLabelPanel.add(globalLabel);
    192         globalLabelPanel.add(Box.createHorizontalGlue());
    193         southPanel.add(globalLabelPanel);
    194         southPanel.add(searchAndAlternatingColorPanel);
    195         southPanel.add(bypassSearchColorsPanel);
    196         southPanel.add(Box.createVerticalStrut(5));
    197         JPanel closePanel = buildClosePanel();
    198         southPanel.add(closePanel);
    199
    200         JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    201         statusPanel.add(statusBar);
    202         southPanel.add(statusPanel);
    203         rightPanel.add(rulesPanel, BorderLayout.CENTER);
    204         rightPanel.add(southPanel, BorderLayout.SOUTH);
    205         rightOuterPanel.add(rightPanel);
    206
    207         JPanel topPanel = new JPanel();
    208         topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
    209
    210         JLabel selectText = new JLabel("Apply a tab's colors");
    211         topPanel.add(selectText);
    212         topPanel.add(Box.createHorizontalStrut(5));
    213
    214         logPanelColorizersModel = new DefaultComboBoxModel();
    215         final JComboBox loadPanelColorizersComboBox = new JComboBox(logPanelColorizersModel);
    216         loadLogPanelColorizers();
    217
    218         topPanel.add(loadPanelColorizersComboBox);
    219
    220         topPanel.add(Box.createHorizontalStrut(5));
    221         final Action copyRulesAction = new AbstractAction() {
    222             public void actionPerformed(ActionEvent e) {
    223                 tableModel.getDataVector().clear();
    224                 Object selectedItem = loadPanelColorizersComboBox.getSelectedItem();
    225                 if (selectedItem != null) {
    226                     RuleColorizer sourceColorizer = allLogPanelColorizers.get(selectedItem.toString());
    227                     colorizer.setRules(sourceColorizer.getRules());
    228                     updateColors();
    229                 }
    230             }
    231         };
    232
    233         loadPanelColorizersComboBox.addActionListener(e -> {
    234             Object selectedItem = loadPanelColorizersComboBox.getSelectedItem();
    235             if (selectedItem != null) {
    236                 String selectedColorizerName = selectedItem.toString();
    237                 copyRulesAction.setEnabled(!(noTab.equals(selectedColorizerName)));
    238             }
    239         });
    240
    241         copyRulesAction.putValue(Action.NAME, "Copy color rules");
    242         copyRulesAction.setEnabled(!(noTab.equals(loadPanelColorizersComboBox.getSelectedItem())));
    243
    244         JButton copyRulesButton = new JButton(copyRulesAction);
    245         topPanel.add(copyRulesButton);
    246
    247         add(topPanel, BorderLayout.NORTH);
    248         add(rightOuterPanel, BorderLayout.CENTER);
    249         if (table.getRowCount() > 0) {
    250             table.getSelectionModel().setSelectionInterval(0, 0);
    251         }
    252     }
    253
    254     public void loadLogPanelColorizers() {
    255         if (logPanelColorizersModel.getIndexOf(noTab) == -1) {
    256             logPanelColorizersModel.addElement(noTab);
    257         }
    258         for (Object o : allLogPanelColorizers.entrySet()) {
    259             Map.Entry entry = (Map.Entry) o;
    260             if (!entry.getValue().equals(currentLogPanelColorizer) && (logPanelColorizersModel.getIndexOf(entry.getKey()) == -1)) {
    261                 logPanelColorizersModel.addElement(entry.getKey());
    262             }
    263         }
    264         //update search and alternating colors, since they may have changed from another color panel
    265         searchDataVectorEntry.set(0, applicationPreferenceModel.getSearchBackgroundColor());
    266         searchDataVectorEntry.set(1, applicationPreferenceModel.getSearchForegroundColor());
    267         alternatingColorDataVectorEntry.set(0, applicationPreferenceModel.getAlternatingColorBackgroundColor());
    268         alternatingColorDataVectorEntry.set(1, applicationPreferenceModel.getAlternatingColorForegroundColor());
    269     }
    270
    271     public JPanel buildBypassSearchColorsPanel() {
    272         JPanel panel = new JPanel();
    273         panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
    274
    275         bypassSearchColorsCheckBox = new JCheckBox("Don't use a search color for matching rows");
    276         panel.add(bypassSearchColorsCheckBox);
    277         return panel;
    278     }
    279
    280     public JPanel buildSearchAndAlternatingColorPanel() {
    281         JPanel panel = new JPanel();
    282         panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
    283
    284         JLabel defineSearchColorsLabel = new JLabel("Find colors");
    285
    286         panel.add(defineSearchColorsLabel);
    287
    288         panel.add(Box.createHorizontalStrut(10));
    289         JScrollPane searchPane = new JScrollPane(searchTable);
    290         searchPane.setBorder(BorderFactory.createEmptyBorder());
    291         panel.add(searchPane);
    292
    293         panel.add(Box.createHorizontalStrut(10));
    294         JLabel defineAlternatingColorLabel = new JLabel("Alternating colors");
    295
    296         panel.add(defineAlternatingColorLabel);
    297
    298         panel.add(Box.createHorizontalStrut(10));
    299         JScrollPane alternatingColorPane = new JScrollPane(alternatingColorTable);
    300         alternatingColorPane.setBorder(BorderFactory.createEmptyBorder());
    301
    302         panel.add(alternatingColorPane);
    303         panel.setBorder(BorderFactory.createEtchedBorder());
    304         panel.add(Box.createHorizontalGlue());
    305         return panel;
    306     }
    307
    308     public void updateColors() {
    309         tableModel.getDataVector().clear();
    310         tableModel.getDataVector().addAll(getColorizerVector());
    311         tableModel.fireTableDataChanged();
    312     }
    313
    314     private Vector<Vector<java.io.Serializable>> getColorizerVector() {
    315         Vector<Vector<java.io.Serializable>> data = new Vector<>();
    316         Map map = colorizer.getRules();
    317         for (Object o1 : map.entrySet()) {
    318             Map.Entry entry = (Map.Entry) o1;
    319             //update ruleset list
    320             if (entry.getKey().equals(currentRuleSet)) {
    321
    322                 for (Object o : ((List) entry.getValue())) {
    323                     ColorRule rule = (ColorRule) o;
    324                     Vector<java.io.Serializable> v = new Vector<>();
    325                     v.add(rule.getExpression());
    326                     v.add(rule.getBackgroundColor());
    327                     v.add(rule.getForegroundColor());
    328                     data.add(v);
    329                 }
    330             }
    331         }
    332         return data;
    333     }
    334
    335     private void configureTable() {
    336         table.setToolTipText("Double click to edit");
    337         table.setRowHeight(20);
    338         table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    339         table.setColumnSelectionAllowed(false);
    340
    341         Vector backgroundColors = colorizer.getDefaultColors();
    342         Vector foregroundColors = colorizer.getDefaultColors();
    343         backgroundColors.add("Browse...");
    344         foregroundColors.add("Browse...");
    345
    346         JComboBox background = new JComboBox(backgroundColors);
    347         background.setMaximumRowCount(15);
    348         background.setRenderer(new ColorListCellRenderer());
    349
    350         JComboBox foreground = new JComboBox(foregroundColors);
    351         foreground.setMaximumRowCount(15);
    352         foreground.setRenderer(new ColorListCellRenderer());
    353
    354         DefaultCellEditor backgroundEditor = new DefaultCellEditor(background);
    355         DefaultCellEditor foregroundEditor = new DefaultCellEditor(foreground);
    356         JTextField textField = new JTextField();
    357         textField.addKeyListener(
    358             new ExpressionRuleContext(filterModel, textField));
    359         table.getColumnModel().getColumn(0).setCellEditor(
    360             new DefaultCellEditor(textField));
    361         table.getColumnModel().getColumn(1).setCellEditor(backgroundEditor);
    362         table.getColumnModel().getColumn(2).setCellEditor(foregroundEditor);
    363
    364         background.addItemListener(new ColorItemListener(background));
    365         foreground.addItemListener(new ColorItemListener(foreground));
    366
    367         table.getColumnModel().getColumn(0).setCellRenderer(
    368             new ExpressionTableCellRenderer());
    369         table.getColumnModel().getColumn(1).setCellRenderer(
    370             new ColorTableCellRenderer());
    371         table.getColumnModel().getColumn(2).setCellRenderer(
    372             new ColorTableCellRenderer());
    373     }
    374
    375     private void configureSingleEntryColorTable(JTable thisTable) {
    376         thisTable.setToolTipText("Double click to edit");
    377         thisTable.setRowHeight(20);
    378         thisTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    379         thisTable.setColumnSelectionAllowed(false);
    380
    381         Vector backgroundColors = colorizer.getDefaultColors();
    382         Vector foregroundColors = colorizer.getDefaultColors();
    383         backgroundColors.add("Browse...");
    384         foregroundColors.add("Browse...");
    385
    386         JComboBox background = new JComboBox(backgroundColors);
    387         background.setMaximumRowCount(15);
    388         background.setRenderer(new ColorListCellRenderer());
    389
    390         JComboBox foreground = new JComboBox(foregroundColors);
    391         foreground.setMaximumRowCount(15);
    392         foreground.setRenderer(new ColorListCellRenderer());
    393
    394         DefaultCellEditor backgroundEditor = new DefaultCellEditor(background);
    395         DefaultCellEditor foregroundEditor = new DefaultCellEditor(foreground);
    396         thisTable.getColumnModel().getColumn(0).setCellEditor(backgroundEditor);
    397         thisTable.getColumnModel().getColumn(1).setCellEditor(foregroundEditor);
    398
    399         background.addItemListener(new ColorItemListener(background));
    400         foreground.addItemListener(new ColorItemListener(foreground));
    401
    402         thisTable.getColumnModel().getColumn(0).setCellRenderer(
    403             new ColorTableCellRenderer());
    404         thisTable.getColumnModel().getColumn(1).setCellRenderer(
    405             new ColorTableCellRenderer());
    406     }
    407
    408     public void setCloseActionListener(ActionListener listener) {
    409         closeListener = listener;
    410     }
    411
    412     public void hidePanel() {
    413         if (closeListener != null) {
    414             closeListener.actionPerformed(null);
    415         }
    416     }
    417
    418     void applyRules(String ruleSet, RuleColorizer applyingColorizer) {
    419         table.getColumnModel().getColumn(0).getCellEditor().stopCellEditing();
    420
    421         List list = new ArrayList();
    422         Vector vector = tableModel.getDataVector();
    423         StringBuilder result = new StringBuilder();
    424
    425         for (int i = 0; i < vector.size(); i++) {
    426             Vector v = (Vector) vector.elementAt(i);
    427
    428             try {
    429                 Rule expressionRule = ExpressionRule.getRule((String) v.elementAt(0));
    430                 Color background = getBackground();
    431                 Color foreground = getForeground();
    432
    433                 if (v.elementAt(1) instanceof Color) {
    434                     background = (Color) v.elementAt(1);
    435                 }
    436
    437                 if (v.elementAt(2) instanceof Color) {
    438                     foreground = (Color) v.elementAt(2);
    439                 }
    440
    441                 ColorRule r = new ColorRule((String) v.elementAt(0), expressionRule, background, foreground);
    442                 list.add(r);
    443             } catch (IllegalArgumentException iae) {
    444                 if (!result.toString().equals("")) {
    445                     result.append("<br>");
    446                 }
    447
    448                 result.append(iae.getMessage());
    449             }
    450         }
    451
    452         //all rules are valid, they can be applied
    453         if (result.toString().equals("")) {
    454             ((ExpressionTableCellRenderer) table.getColumnModel().getColumn(0).getCellRenderer())
    455                 .setToolTipText("Double click to edit");
    456             statusBar.setText(DEFAULT_STATUS);
    457
    458             //only update rules if there were no errors
    459             Map map = new HashMap();
    460             map.put(ruleSet, list);
    461             applyingColorizer.setRules(map);
    462
    463         } else {
    464             statusBar.setText("Errors - see expression tooltip (color filters won't be active until errors are resolved)");
    465             ((ExpressionTableCellRenderer) table.getColumnModel().getColumn(0).getCellRenderer())
    466                 .setToolTipText("<html>" + result.toString() + "</html>");
    467         }
    468     }
    469
    470     JPanel buildClosePanel() {
    471         JPanel panel = new JPanel();
    472         panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
    473         panel.add(Box.createHorizontalGlue());
    474
    475         JButton saveAsDefaultButton = new JButton(" Save as default ");
    476
    477         saveAsDefaultButton.addActionListener(
    478             new AbstractAction() {
    479                 public void actionPerformed(ActionEvent evt) {
    480                     RuleColorizer defaultColorizer = allLogPanelColorizers.get(ChainsawConstants.DEFAULT_COLOR_RULE_NAME);
    481                     applyRules(currentRuleSet, defaultColorizer);
    482                 }
    483             });
    484
    485         panel.add(saveAsDefaultButton);
    486
    487         JButton applyButton = new JButton(" Apply ");
    488
    489         applyButton.addActionListener(
    490             new AbstractAction() {
    491                 public void actionPerformed(ActionEvent evt) {
    492                     applyRules(currentRuleSet, colorizer);
    493                     saveSearchColors();
    494                     saveAlternatingColors();
    495                     saveBypassFlag();
    496                 }
    497             });
    498
    499         panel.add(Box.createHorizontalStrut(10));
    500         panel.add(applyButton);
    501
    502         JButton closeButton = new JButton(" Close ");
    503
    504         closeButton.addActionListener(
    505             new AbstractAction() {
    506                 public void actionPerformed(ActionEvent evt) {
    507                     hidePanel();
    508                 }
    509             });
    510         panel.add(Box.createHorizontalStrut(10));
    511         panel.add(closeButton);
    512
    513         return panel;
    514     }
    515
    516     private void saveSearchColors() {
    517         Vector thisVector = (Vector) searchTableModel.getDataVector().get(0);
    518         applicationPreferenceModel.setSearchBackgroundColor((Color) thisVector.get(0));
    519         applicationPreferenceModel.setSearchForegroundColor((Color) thisVector.get(1));
    520     }
    521
    522     private void saveAlternatingColors() {
    523         Vector thisVector = (Vector) alternatingColorTableModel.getDataVector().get(0);
    524         applicationPreferenceModel.setAlternatingBackgroundColor((Color) thisVector.get(0));
    525         Color alternatingColorForegroundColor = (Color) thisVector.get(1);
    526         applicationPreferenceModel.setAlternatingForegroundColor(alternatingColorForegroundColor);
    527     }
    528
    529     private void saveBypassFlag() {
    530         applicationPreferenceModel.setBypassSearchColors(bypassSearchColorsCheckBox.isSelected());
    531     }
    532
    533     JPanel buildUpDownPanel() {
    534         JPanel panel = new JPanel(new BorderLayout());
    535         JPanel innerPanel = new JPanel();
    536         innerPanel.setLayout(new GridLayout(5, 1));
    537
    538         final JButton upButton = new JButton(ChainsawIcons.ICON_UP);
    539         upButton.setToolTipText("Move selected rule up");
    540
    541         final JButton downButton = new JButton(ChainsawIcons.ICON_DOWN);
    542         downButton.setToolTipText("Move selected rule down");
    543         upButton.setEnabled(false);
    544         downButton.setEnabled(false);
    545
    546         table.getSelectionModel().addListSelectionListener(
    547             e -> {
    548                 if (!e.getValueIsAdjusting()) {
    549                     int index = table.getSelectionModel().getMaxSelectionIndex();
    550
    551                     if (index < 0) {
    552                         downButton.setEnabled(false);
    553                         upButton.setEnabled(false);
    554                     } else if ((index == 0) && (tableModel.getRowCount() == 1)) {
    555                         downButton.setEnabled(false);
    556                         upButton.setEnabled(false);
    557                     } else if ((index == 0) && (tableModel.getRowCount() > 1)) {
    558                         downButton.setEnabled(true);
    559                         upButton.setEnabled(false);
    560                     } else if (index == (tableModel.getRowCount() - 1)) {
    561                         downButton.setEnabled(false);
    562                         upButton.setEnabled(true);
    563                     } else {
    564                         downButton.setEnabled(true);
    565                         upButton.setEnabled(true);
    566                     }
    567                 }
    568             });
    569
    570         JPanel upPanel = new JPanel();
    571
    572         upPanel.add(upButton);
    573
    574         JPanel downPanel = new JPanel();
    575         downPanel.add(downButton);
    576
    577         innerPanel.add(new JLabel(""));
    578         innerPanel.add(upPanel);
    579         innerPanel.add(new JLabel(""));
    580         innerPanel.add(downPanel);
    581         panel.add(innerPanel, BorderLayout.CENTER);
    582
    583         upButton.addActionListener(
    584             new AbstractAction() {
    585                 public void actionPerformed(ActionEvent evt) {
    586                     int index = table.getSelectionModel().getMaxSelectionIndex();
    587
    588                     if (index > 0) {
    589                         Vector v = tableModel.getDataVector();
    590                         Vector row = (Vector) v.elementAt(index);
    591                         tableModel.removeRow(index);
    592                         index = index - 1;
    593                         tableModel.insertRow(index, row);
    594                         table.getSelectionModel().setSelectionInterval(index, index);
    595                     }
    596                 }
    597             });
    598
    599         downButton.addActionListener(
    600             new AbstractAction() {
    601                 public void actionPerformed(ActionEvent evt) {
    602                     int index = table.getSelectionModel().getMaxSelectionIndex();
    603
    604                     if ((index > -1) && (index < (tableModel.getRowCount() - 1))) {
    605                         Vector v = tableModel.getDataVector();
    606                         Vector row = (Vector) v.elementAt(index);
    607
    608                         tableModel.removeRow(index);
    609                         index = index + 1;
    610                         tableModel.insertRow(index, row);
    611                         table.getSelectionModel().setSelectionInterval(index, index);
    612                     }
    613                 }
    614             });
    615
    616         return panel;
    617     }
    618
    619     JPanel buildRulesPanel() {
    620         JPanel listPanel = new JPanel(new BorderLayout());
    621         JPanel panel = new JPanel();
    622         panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    623
    624         panel.add(Box.createVerticalStrut(10));
    625
    626         JLabel rulesLabel = new JLabel("Rules:");
    627
    628         panel.add(rulesLabel);
    629
    630         JPanel buttonPanel = new JPanel(new GridLayout(0, 2));
    631         buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    632
    633         JPanel newPanel = new JPanel();
    634         JButton newButton = new JButton(" New ");
    635         newButton.addActionListener(
    636             new AbstractAction() {
    637                 public void actionPerformed(ActionEvent evt) {
    638                     int currentRow = table.getSelectedRow();
    639                     Vector<java.io.Serializable> v = new Vector<>();
    640                     v.add("");
    641                     v.add(Color.white);
    642                     v.add(Color.black);
    643
    644                     if (currentRow < 0) {
    645                         tableModel.addRow(v);
    646                         currentRow = table.getRowCount() - 1;
    647                     } else {
    648                         tableModel.insertRow(currentRow, v);
    649                     }
    650
    651                     table.getSelectionModel().setSelectionInterval(
    652                         currentRow, currentRow);
    653                 }
    654             });
    655
    656         newPanel.add(newButton);
    657
    658         JPanel deletePanel = new JPanel();
    659         final JButton deleteButton = new JButton(" Delete ");
    660         deleteButton.setEnabled(false);
    661
    662         deleteButton.addActionListener(
    663             new AbstractAction() {
    664                 public void actionPerformed(ActionEvent evt) {
    665                     int index = table.getSelectionModel().getMaxSelectionIndex();
    666
    667                     if ((index > -1) && (index < table.getRowCount())) {
    668                         tableModel.removeRow(index);
    669
    670                         if (index > 0) {
    671                             index = index - 1;
    672                         }
    673
    674                         if (tableModel.getRowCount() > 0) {
    675                             table.getSelectionModel().setSelectionInterval(index, index);
    676                         }
    677                     }
    678                 }
    679             });
    680
    681         table.getSelectionModel().addListSelectionListener(
    682             e -> {
    683                 if (!e.getValueIsAdjusting()) {
    684                     int index = table.getSelectionModel().getMaxSelectionIndex();
    685
    686                     if (index < 0) {
    687                         deleteButton.setEnabled(false);
    688                     } else {
    689                         deleteButton.setEnabled(true);
    690                     }
    691                 }
    692             });
    693
    694         deletePanel.add(deleteButton);
    695
    696         buttonPanel.add(newPanel);
    697         buttonPanel.add(deletePanel);
    698
    699         listPanel.add(panel, BorderLayout.NORTH);
    700
    701         JPanel tablePanel = new JPanel(new BorderLayout());
    702         tableScrollPane.setBorder(BorderFactory.createEtchedBorder());
    703         tablePanel.add(tableScrollPane, BorderLayout.CENTER);
    704         tablePanel.add(buildUpDownPanel(), BorderLayout.EAST);
    705         listPanel.add(tablePanel, BorderLayout.CENTER);
    706         listPanel.add(buttonPanel, BorderLayout.SOUTH);
    707
    708         return listPanel;
    709     }
    710
    711     class ColorListCellRenderer extends JLabel implements ListCellRenderer {
    712         ColorListCellRenderer() {
    713             setOpaque(true);
    714         }
    715
    716         public Component getListCellRendererComponent(
    717             JList list, Object value, int index, boolean isSelected,
    718             boolean cellHasFocus) {
    719             setText(" ");
    720
    721             if (isSelected && (index > -1)) {
    722                 setBorder(BorderFactory.createLineBorder(Color.black, 2));
    723             } else {
    724                 setBorder(BorderFactory.createEmptyBorder());
    725             }
    726
    727             if (value instanceof Color) {
    728                 setBackground((Color) value);
    729             } else {
    730                 setBackground(Color.white);
    731
    732                 if (value != null) {
    733                     setText(value.toString());
    734                 }
    735             }
    736
    737             return this;
    738         }
    739     }
    740
    741     class ColorItemListener implements ItemListener {
    742         JComboBox box;
    743         JDialog dialog;
    744         JColorChooser colorChooser;
    745         Color lastColor;
    746
    747         ColorItemListener(final JComboBox box) {
    748             this.box = box;
    749             colorChooser = new JColorChooser();
    750             dialog =
    751                 JColorChooser.createDialog(
    752                     box, "Pick a Color", true, //modal
    753                     colorChooser,
    754                     e -> {
    755                         box.insertItemAt(colorChooser.getColor(), 0);
    756                         box.setSelectedIndex(0);
    757                     }, //OK button handler
    758                     e -> box.setSelectedItem(lastColor)); //CANCEL button handler
    759             dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
    760         }
    761
    762         public void itemStateChanged(ItemEvent e) {
    763             if (e.getStateChange() == ItemEvent.SELECTED) {
    764                 if (box.getSelectedItem() instanceof Color) {
    765                     box.setBackground((Color) box.getSelectedItem());
    766                     repaint();
    767                 } else {
    768                     box.setBackground(Color.white);
    769                     int selectedRow = table.getSelectedRow();
    770                     int selectedColumn = table.getSelectedColumn();
    771                     if (selectedRow != -1 && selectedColumn != -1) {
    772                         colorChooser.setColor((Color) table.getValueAt(selectedRow, selectedColumn));
    773                         lastColor = (Color) table.getValueAt(selectedRow, selectedColumn);
    774                     }
    775                     dialog.setVisible(true);
    776                 }
    777             }
    778         }
    779     }
    780
    781     class ColorTableCellRenderer implements TableCellRenderer {
    782         Border border;
    783         JPanel panel;
    784
    785         ColorTableCellRenderer() {
    786             panel = new JPanel();
    787             panel.setOpaque(true);
    788         }
    789
    790         public Color getCurrentColor() {
    791             return panel.getBackground();
    792         }
    793
    794         public Component getTableCellRendererComponent(
    795             JTable thisTable, Object value, boolean isSelected, boolean hasFocus, int row,
    796             int column) {
    797             if (value instanceof Color) {
    798                 panel.setBackground((Color) value);
    799             }
    800             if (border == null) {
    801                 border = BorderFactory.createMatteBorder(2, 2, 2, 2, table.getBackground());
    802             }
    803
    804             panel.setBorder(border);
    805
    806             return panel;
    807         }
    808     }
    809
    810     class ExpressionTableCellRenderer implements TableCellRenderer {
    811         JPanel panel = new JPanel();
    812         JLabel expressionLabel = new JLabel();
    813         JLabel iconLabel = new JLabel();
    814         Icon selectedIcon = new SelectedIcon(true);
    815         Icon unselectedIcon = new SelectedIcon(false);
    816
    817         ExpressionTableCellRenderer() {
    818             panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
    819             panel.setOpaque(true);
    820             panel.add(iconLabel);
    821             panel.add(Box.createHorizontalStrut(5));
    822             panel.add(expressionLabel);
    823         }
    824
    825         void setToolTipText(String text) {
    826             panel.setToolTipText(text);
    827         }
    828
    829         public Component getTableCellRendererComponent(
    830             JTable thisTable, Object value, boolean isSelected, boolean hasFocus, int row,
    831             int column) {
    832             if (value == null) {
    833                 return panel;
    834             }
    835
    836             Vector v = tableModel.getDataVector();
    837             Vector r = (Vector) v.elementAt(row);
    838             expressionLabel.setText(value.toString());
    839
    840             if (r.elementAt(1) instanceof Color) {
    841                 expressionLabel.setBackground((Color) r.elementAt(1));
    842                 panel.setBackground((Color) r.elementAt(1));
    843             }
    844
    845             if (r.elementAt(2) instanceof Color) {
    846                 expressionLabel.setForeground((Color) r.elementAt(2));
    847                 panel.setForeground((Color) r.elementAt(2));
    848             }
    849
    850             if (isSelected) {
    851                 iconLabel.setIcon(selectedIcon);
    852             } else {
    853                 iconLabel.setIcon(unselectedIcon);
    854             }
    855
    856             return panel;
    857         }
    858     }
    859
    860     class SelectedIcon implements Icon {
    861         private boolean isSelected;
    862         private int width = 9;
    863         private int height = 18;
    864         private int[] xPoints = new int[4];
    865         private int[] yPoints = new int[4];
    866
    867         public SelectedIcon(boolean isSelected) {
    868             this.isSelected = isSelected;
    869             xPoints[0] = 0;
    870             yPoints[0] = -1;
    871             xPoints[1] = 0;
    872             yPoints[1] = height;
    873             xPoints[2] = width;
    874             yPoints[2] = height / 2;
    875             xPoints[3] = width;
    876             yPoints[3] = (height / 2) - 1;
    877         }
    878
    879         public int getIconHeight() {
    880             return height;
    881         }
    882
    883         public int getIconWidth() {
    884             return width;
    885         }
    886
    887         public void paintIcon(Component c, Graphics g, int x, int y) {
    888             if (isSelected) {
    889                 int length = xPoints.length;
    890                 int[] newXPoints = new int[length];
    891                 int[] newYPoints = new int[length];
    892
    893                 for (int i = 0; i < length; i++) {
    894                     newXPoints[i] = xPoints[i] + x;
    895                     newYPoints[i] = yPoints[i] + y;
    896                 }
    897
    898                 g.setColor(Color.black);
    899
    900                 g.fillPolygon(newXPoints, newYPoints, length);
    901             }
    902         }
    903     }
    904 }