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