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.BorderFactory;
42  import javax.swing.Box;
43  import javax.swing.BoxLayout;
44  import javax.swing.DefaultCellEditor;
45  import javax.swing.DefaultListModel;
46  import javax.swing.Icon;
47  import javax.swing.JButton;
48  import javax.swing.JColorChooser;
49  import javax.swing.JComboBox;
50  import javax.swing.JDialog;
51  import javax.swing.JFrame;
52  import javax.swing.JLabel;
53  import javax.swing.JList;
54  import javax.swing.JPanel;
55  import javax.swing.JScrollPane;
56  import javax.swing.JSeparator;
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.ExpressionRuleContext;
69  import org.apache.log4j.chainsaw.filter.FilterModel;
70  import org.apache.log4j.chainsaw.icons.ChainsawIcons;
71  import org.apache.log4j.rule.ColorRule;
72  import org.apache.log4j.rule.ExpressionRule;
73  import org.apache.log4j.rule.Rule;
74  
75  
76  /***
77   * Panel which updates a RuleColorizer, allowing the user to build expression-based
78   * color rules.
79   * 
80   * TODO: examine ColorPanel/RuleColorizer/LogPanel listeners and interactions
81   *
82   * @author Scott Deboy <sdeboy@apache.org>
83   */
84  public class ColorPanel extends JPanel {
85    private RuleColorizer colorizer;
86    private JPanel ruleSetsPanel;
87    private JPanel rulesPanel;
88    private FilterModel filterModel;
89    private DefaultTableModel tableModel;
90    private JScrollPane tableScrollPane;
91    private JTable table;
92    private ActionListener closeListener;
93    private JLabel statusBar;
94    private Vector columns;
95    private String currentRuleSet = "Default";
96    private DefaultListModel ruleSetListModel;
97  
98    public ColorPanel(
99      final RuleColorizer colorizer, final FilterModel filterModel) {
100     super(new BorderLayout());
101 
102     this.colorizer = colorizer;
103     this.filterModel = filterModel;
104     
105     colorizer.addPropertyChangeListener(
106     	      "colorrule",
107     	      new PropertyChangeListener() {
108     	        public void propertyChange(PropertyChangeEvent evt) {
109     	        	updateColors();
110     	        }
111     	      });
112 
113     tableModel = new DefaultTableModel();
114     table = new JTable(tableModel);
115 
116     ruleSetListModel = new DefaultListModel();
117     
118     columns = new Vector();
119     columns.add("Expression");
120     columns.add("Background");
121     columns.add("Foreground");
122 
123     table.setPreferredScrollableViewportSize(new Dimension(400, 200));
124     tableScrollPane = new JScrollPane(table);
125 
126     Vector data = getColorizerVector();    
127     tableModel.setDataVector(data, columns);
128 
129     table.sizeColumnsToFit(0);
130     table.getColumnModel().getColumn(1).setPreferredWidth(80);
131     table.getColumnModel().getColumn(2).setPreferredWidth(80);
132     table.getColumnModel().getColumn(1).setMaxWidth(80);
133     table.getColumnModel().getColumn(2).setMaxWidth(80);
134 
135     configureTable();
136 
137     statusBar = new JLabel("Ruleset support not yet implemented");
138 
139     ruleSetsPanel = buildRuleSetsPanel();
140     ruleSetsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
141 
142     rulesPanel = buildRulesPanel();
143     rulesPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
144 
145     JPanel rightPanel = new JPanel(new BorderLayout());
146 
147     JPanel rightOuterPanel = new JPanel();
148     rightOuterPanel.setLayout(
149       new BoxLayout(rightOuterPanel, BoxLayout.X_AXIS));
150     rightOuterPanel.add(Box.createHorizontalStrut(10));
151 
152     JPanel southPanel = new JPanel();
153     southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));
154     southPanel.add(Box.createVerticalStrut(5));
155     southPanel.add(new JSeparator());
156     southPanel.add(Box.createVerticalStrut(5));
157     JPanel closePanel = buildClosePanel();
158     southPanel.add(closePanel);
159 
160     JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
161     statusPanel.add(statusBar);
162     southPanel.add(statusPanel);
163     rightPanel.add(rulesPanel, BorderLayout.CENTER);
164     rightPanel.add(southPanel, BorderLayout.SOUTH);
165     rightOuterPanel.add(rightPanel);
166 
167     add(ruleSetsPanel, BorderLayout.WEST);
168     add(rightOuterPanel, BorderLayout.CENTER);
169     if (table.getRowCount() > 0) {
170         table.getSelectionModel().setSelectionInterval(0, 0);
171     }
172   }
173 
174   public static void main(String[] args) {
175     FilterModel filterModel = new FilterModel();
176     RuleColorizer colorizer = new RuleColorizer();
177 
178     ColorPanel p = new ColorPanel(colorizer, filterModel);
179     final JFrame f = new JFrame();
180 
181     p.setCloseActionListener(
182       new ActionListener() {
183         public void actionPerformed(ActionEvent e) {
184           System.exit(0);
185         }
186       });
187 
188 //    Following does not compile on JDK 1.3.1
189 //    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
190     f.getContentPane().add(p);
191     f.pack();
192     f.setVisible(true);
193   }
194   
195   public void updateColors() {
196     tableModel.getDataVector().clear();
197     tableModel.getDataVector().addAll(getColorizerVector());
198   }
199   
200   private Vector getColorizerVector() {
201       Vector data = new Vector();
202       Map map = colorizer.getRules();
203       Iterator iter = map.entrySet().iterator();
204       ruleSetListModel.removeAllElements();
205       while (iter.hasNext()) {
206         Map.Entry entry = (Map.Entry)iter.next();
207         //update ruleset list
208         ruleSetListModel.addElement(entry.getKey());
209         if (entry.getKey().equals(currentRuleSet)) {
210             Iterator iter2 = ((List)entry.getValue()).iterator();
211       
212             while (iter2.hasNext()) {
213                 ColorRule rule = (ColorRule)iter2.next();
214                 Vector v = new Vector();
215                 v.add(rule.getExpression());
216                 v.add(rule.getBackgroundColor());
217                 v.add(rule.getForegroundColor());
218                 data.add(v);
219             }
220          }
221       }
222       return data;
223   }
224 
225   private Vector getDefaultColors() {
226     Vector vec = new Vector();
227 
228     vec.add(Color.white);
229     vec.add(Color.black);
230 
231     vec.add(new Color(0, 153, 0));
232     vec.add(new Color(0, 204, 51));
233     vec.add(new Color(153, 255, 153));
234     vec.add(new Color(51, 255, 0));
235     vec.add(new Color(204, 255, 204));
236     vec.add(new Color(0, 153, 153));
237     vec.add(new Color(0, 204, 204));
238     vec.add(new Color(153, 102, 0));
239     vec.add(new Color(102, 102, 0));
240     vec.add(new Color(153, 153, 0));
241     vec.add(new Color(204, 204, 0));
242     vec.add(new Color(255, 255, 0));
243     vec.add(new Color(255, 255, 204));
244     vec.add(new Color(255, 153, 51));
245     vec.add(new Color(204, 0, 51));
246     vec.add(new Color(255, 51, 51));
247     vec.add(new Color(255, 153, 153));
248     vec.add(new Color(255, 204, 204));
249     vec.add(new Color(204, 0, 204));
250     vec.add(new Color(255, 51, 255));
251     vec.add(new Color(153, 51, 255));
252     vec.add(new Color(0, 0, 153));
253     vec.add(new Color(0, 0, 255));
254     vec.add(new Color(51, 153, 255));
255     vec.add(new Color(153, 153, 255));
256     vec.add(new Color(204, 204, 255));
257     vec.add(new Color(102, 255, 255));
258     vec.add(new Color(102, 102, 102));
259     vec.add(new Color(153, 153, 153));
260     vec.add(new Color(204, 204, 204));
261 
262     return vec;
263   }
264 
265   private void configureTable() {
266     table.setToolTipText("Double click to edit");
267     table.setRowHeight(20);
268     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
269     table.setColumnSelectionAllowed(false);
270 
271     Vector backgroundColors = getDefaultColors();
272     Vector foregroundColors = getDefaultColors();
273     backgroundColors.add("Browse...");
274     foregroundColors.add("Browse...");
275 
276     JComboBox background = new JComboBox(backgroundColors);
277     background.setMaximumRowCount(15);
278     background.setRenderer(new ColorListCellRenderer());
279 
280     JComboBox foreground = new JComboBox(foregroundColors);
281     foreground.setMaximumRowCount(15);
282     foreground.setRenderer(new ColorListCellRenderer());
283 
284     DefaultCellEditor backgroundEditor = new DefaultCellEditor(background);
285     DefaultCellEditor foregroundEditor = new DefaultCellEditor(foreground);
286     JTextField textField = new JTextField();
287     textField.addKeyListener(
288       new ExpressionRuleContext(filterModel, textField));
289     table.getColumnModel().getColumn(0).setCellEditor(
290       new DefaultCellEditor(textField));
291     table.getColumnModel().getColumn(1).setCellEditor(backgroundEditor);
292     table.getColumnModel().getColumn(2).setCellEditor(foregroundEditor);
293 
294     background.addItemListener(new ColorItemListener(background));
295     foreground.addItemListener(new ColorItemListener(foreground));
296 
297     table.getColumnModel().getColumn(0).setCellRenderer(
298       new ExpressionTableCellRenderer());
299     table.getColumnModel().getColumn(1).setCellRenderer(
300       new ColorTableCellRenderer());
301     table.getColumnModel().getColumn(2).setCellRenderer(
302       new ColorTableCellRenderer());
303   }
304 
305   public void setCloseActionListener(ActionListener listener) {
306     closeListener = listener;
307   }
308 
309   public void hidePanel() {
310     if (closeListener != null) {
311       closeListener.actionPerformed(null);
312     }
313   }
314 
315   void applyRules(String ruleSet) {
316     table.getColumnModel().getColumn(0).getCellEditor().stopCellEditing();
317 
318     List list = new ArrayList();
319     Vector vector = tableModel.getDataVector();
320     StringBuffer result = new StringBuffer();
321 
322     for (int i = 0; i < vector.size(); i++) {
323       Vector v = (Vector) vector.elementAt(i);
324 
325       try {
326         Rule expressionRule = ExpressionRule.getRule((String) v.elementAt(0));
327         Color background = getBackground();
328         Color foreground = getForeground();
329 
330         if (v.elementAt(1) instanceof Color) {
331           background = (Color) v.elementAt(1);
332         }
333 
334         if (v.elementAt(2) instanceof Color) {
335           foreground = (Color) v.elementAt(2);
336         }
337 
338         ColorRule r = new ColorRule((String)v.elementAt(0), expressionRule, background, foreground);
339         list.add(r);
340       } catch (IllegalArgumentException iae) {
341         if (!result.toString().equals("")) {
342           result.append("<br>");
343         }
344 
345         result.append(iae.getMessage());
346       }
347     }
348 
349     if (result.toString().equals("")) {
350       ((ExpressionTableCellRenderer) table.getColumnModel().getColumn(0).getCellRenderer())
351       .setToolTipText("Double click to edit");
352       statusBar.setText("");
353     } else {
354       statusBar.setText("Errors - see expression tooltip");
355       ((ExpressionTableCellRenderer) table.getColumnModel().getColumn(0).getCellRenderer())
356       .setToolTipText("<html>" + result.toString() + "</html>");
357     }
358     Map map = new HashMap();
359     map.put(ruleSet, list);
360 
361     colorizer.setRules(map);
362   }
363 
364   JPanel buildClosePanel() {
365     JPanel panel = new JPanel();
366     panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
367     panel.add(Box.createHorizontalGlue());
368 
369     JButton applyButton = new JButton("Apply");
370 
371     applyButton.addActionListener(
372       new AbstractAction() {
373         public void actionPerformed(ActionEvent evt) {
374           applyRules(currentRuleSet);
375         }
376       });
377 
378     panel.add(applyButton);
379 
380     JButton closeButton = new JButton("Close");
381 
382     closeButton.addActionListener(
383       new AbstractAction() {
384         public void actionPerformed(ActionEvent evt) {
385           hidePanel();
386         }
387       });
388     panel.add(Box.createHorizontalStrut(10));
389     panel.add(closeButton);
390 
391     return panel;
392   }
393 
394   JPanel buildUpDownPanel() {
395     JPanel panel = new JPanel(new BorderLayout());
396     JPanel innerPanel = new JPanel();
397     innerPanel.setLayout(new GridLayout(5, 1));
398 
399     final JButton upButton = new JButton(ChainsawIcons.ICON_UP);
400     upButton.setToolTipText("Move selected rule up");
401 
402     final JButton downButton = new JButton(ChainsawIcons.ICON_DOWN);
403     downButton.setToolTipText("Move selected rule down");
404     upButton.setEnabled(false);
405     downButton.setEnabled(false);
406 
407     table.getSelectionModel().addListSelectionListener(
408       new ListSelectionListener() {
409         public void valueChanged(ListSelectionEvent e) {
410           if (!e.getValueIsAdjusting()) {
411             int index = table.getSelectionModel().getMaxSelectionIndex();
412 
413             if (index < 0) {
414               downButton.setEnabled(false);
415               upButton.setEnabled(false);
416             } else if ((index == 0) && (tableModel.getRowCount() == 1)) {
417               downButton.setEnabled(false);
418               upButton.setEnabled(false);
419             } else if ((index == 0) && (tableModel.getRowCount() > 1)) {
420               downButton.setEnabled(true);
421               upButton.setEnabled(false);
422             } else if (index == (tableModel.getRowCount() - 1)) {
423               downButton.setEnabled(false);
424               upButton.setEnabled(true);
425             } else {
426               downButton.setEnabled(true);
427               upButton.setEnabled(true);
428             }
429           }
430         }
431       });
432 
433     JPanel upPanel = new JPanel();
434 
435     upPanel.add(upButton);
436 
437     JPanel downPanel = new JPanel();
438     downPanel.add(downButton);
439 
440     innerPanel.add(new JLabel(""));
441     innerPanel.add(upPanel);
442     innerPanel.add(new JLabel(""));
443     innerPanel.add(downPanel);
444     panel.add(innerPanel, BorderLayout.CENTER);
445 
446     upButton.addActionListener(
447       new AbstractAction() {
448         public void actionPerformed(ActionEvent evt) {
449           int index = table.getSelectionModel().getMaxSelectionIndex();
450 
451           if (index > 0) {
452             Vector v = tableModel.getDataVector();
453             Vector row = (Vector) v.elementAt(index);
454             tableModel.removeRow(index);
455             index = index - 1;
456             tableModel.insertRow(index, row);
457             table.getSelectionModel().setSelectionInterval(index, index);
458           }
459         }
460       });
461 
462     downButton.addActionListener(
463       new AbstractAction() {
464         public void actionPerformed(ActionEvent evt) {
465           int index = table.getSelectionModel().getMaxSelectionIndex();
466 
467           if ((index > -1) && (index < (tableModel.getRowCount() - 1))) {
468             Vector v = tableModel.getDataVector();
469             Vector row = (Vector) v.elementAt(index);
470 
471             tableModel.removeRow(index);
472             index = index + 1;
473             tableModel.insertRow(index, row);
474             table.getSelectionModel().setSelectionInterval(index, index);
475           }
476         }
477       });
478 
479     return panel;
480   }
481 
482   JPanel buildRuleSetsPanel() {
483     JPanel panel = new JPanel(new BorderLayout());
484 
485     JLabel ruleSetLabel = new JLabel("RuleSets:");
486     panel.add(ruleSetLabel, BorderLayout.NORTH);
487 
488     final JList list = new JList(ruleSetListModel);
489     JScrollPane scrollPane = new JScrollPane(list);
490     list.setEnabled(false);
491 
492     panel.add(scrollPane, BorderLayout.CENTER);
493 
494     JPanel buttonPanel = new JPanel(new GridLayout(0, 2));
495 
496     JPanel newPanel = new JPanel();
497     JButton newButton = new JButton("New");
498     newButton.setEnabled(false);
499     newPanel.add(newButton);
500 
501     JPanel deletePanel = new JPanel();
502     JButton deleteButton = new JButton("Delete");
503     deleteButton.setEnabled(false);
504     deletePanel.add(deleteButton);
505 
506     buttonPanel.add(newPanel);
507     buttonPanel.add(deletePanel);
508 
509     panel.add(buttonPanel, BorderLayout.SOUTH);
510 
511     return panel;
512   }
513 
514   JPanel buildRulesPanel() {
515     JPanel listPanel = new JPanel(new BorderLayout());
516     JPanel panel = new JPanel();
517     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
518 
519     JLabel ruleSetLabel = new JLabel("RuleSet Name:");
520     panel.add(ruleSetLabel);
521 
522     JTextField ruleSetTextField = new JTextField(20);
523     ruleSetTextField.setText(currentRuleSet);
524     ruleSetTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
525     ruleSetTextField.setEnabled(false);
526 
527     panel.add(ruleSetTextField);
528 
529     panel.add(Box.createVerticalStrut(10));
530 
531     JLabel rulesLabel = new JLabel("Rules:");
532 
533     panel.add(rulesLabel);
534 
535     JPanel buttonPanel = new JPanel(new GridLayout(0, 2));
536     buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
537 
538     JPanel newPanel = new JPanel();
539     JButton newButton = new JButton("New");
540     newButton.addActionListener(
541       new AbstractAction() {
542         public void actionPerformed(ActionEvent evt) {
543           int currentRow = table.getSelectedRow();
544           Vector v = new Vector();
545           v.add("");
546           v.add(Color.white);
547           v.add(Color.black);
548 
549           if (currentRow < 0) {
550             tableModel.addRow(v);
551             currentRow = table.getRowCount() - 1;
552           } else {
553             tableModel.insertRow(currentRow, v);
554           }
555 
556           table.getSelectionModel().setSelectionInterval(
557             currentRow, currentRow);
558         }
559       });
560 
561     newPanel.add(newButton);
562 
563     JPanel deletePanel = new JPanel();
564     final JButton deleteButton = new JButton("Delete");
565     deleteButton.setEnabled(false);
566 
567     deleteButton.addActionListener(
568       new AbstractAction() {
569         public void actionPerformed(ActionEvent evt) {
570           int index = table.getSelectionModel().getMaxSelectionIndex();
571 
572           if ((index > -1) && (index < table.getRowCount())) {
573             tableModel.removeRow(index);
574 
575             if (index > 0) {
576               index = index - 1;
577             }
578 
579             if (tableModel.getRowCount() > 0) {
580               table.getSelectionModel().setSelectionInterval(index, index);
581             }
582           }
583         }
584       });
585 
586     table.getSelectionModel().addListSelectionListener(
587       new ListSelectionListener() {
588         public void valueChanged(ListSelectionEvent e) {
589           if (!e.getValueIsAdjusting()) {
590             int index = table.getSelectionModel().getMaxSelectionIndex();
591 
592             if (index < 0) {
593               deleteButton.setEnabled(false);
594             } else {
595               deleteButton.setEnabled(true);
596             }
597           }
598         }
599       });
600 
601     deletePanel.add(deleteButton);
602 
603     buttonPanel.add(newPanel);
604     buttonPanel.add(deletePanel);
605 
606     listPanel.add(panel, BorderLayout.NORTH);
607 
608     JPanel tablePanel = new JPanel(new BorderLayout());
609     tablePanel.add(tableScrollPane, BorderLayout.CENTER);
610     tablePanel.add(buildUpDownPanel(), BorderLayout.EAST);
611     listPanel.add(tablePanel, BorderLayout.CENTER);
612     listPanel.add(buttonPanel, BorderLayout.SOUTH);
613 
614     return listPanel;
615   }
616 
617   class ColorListCellRenderer extends JLabel implements ListCellRenderer {
618     ColorListCellRenderer() {
619       setOpaque(true);
620     }
621 
622     public Component getListCellRendererComponent(
623       JList list, Object value, int index, boolean isSelected,
624       boolean cellHasFocus) {
625       setText(" ");
626 
627       if (isSelected && (index > -1)) {
628         setBorder(BorderFactory.createLineBorder(Color.black, 2));
629       } else {
630         setBorder(BorderFactory.createEmptyBorder());
631       }
632 
633       if (value instanceof Color) {
634         setBackground((Color) value);
635       } else {
636         setBackground(Color.white);
637 
638         if (value != null) {
639           setText(value.toString());
640         }
641       }
642 
643       return this;
644     }
645   }
646 
647   class ColorItemListener implements ItemListener {
648     JComboBox box;
649     JDialog dialog;
650     JColorChooser colorChooser;
651     Color lastColor;
652 
653     ColorItemListener(final JComboBox box) {
654       this.box = box;
655       colorChooser = new JColorChooser();
656       dialog =
657         JColorChooser.createDialog(
658           box, "Pick a Color", true, //modal
659           colorChooser,
660           new ActionListener() {
661             public void actionPerformed(ActionEvent e) {
662               box.insertItemAt(colorChooser.getColor(), 0);
663               box.setSelectedIndex(0);
664             }
665           }, //OK button handler
666           new ActionListener() {
667             public void actionPerformed(ActionEvent e) {
668                 box.setSelectedItem(lastColor);
669             }
670           }); //CANCEL button handler
671       dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
672     }
673 
674     public void itemStateChanged(ItemEvent e) {
675       if (e.getStateChange() == ItemEvent.SELECTED) {
676         if (box.getSelectedItem() instanceof Color) {
677           box.setBackground((Color) box.getSelectedItem());
678           repaint();
679         } else {
680           box.setBackground(Color.white);
681           colorChooser.setColor((Color)table.getValueAt(table.getSelectedRow(), table.getSelectedColumn()));
682           lastColor = (Color)table.getValueAt(table.getSelectedRow(), table.getSelectedColumn());
683           dialog.setVisible(true);
684         }
685       }
686     }
687   }
688 
689   class ColorTableCellRenderer implements TableCellRenderer {
690     Border border;
691     JPanel panel;
692     
693     ColorTableCellRenderer() {
694         panel = new JPanel();
695         panel.setOpaque(true);
696     }
697 
698     public Color getCurrentColor() {
699         return panel.getBackground();
700     }
701 
702     public Component getTableCellRendererComponent(
703       JTable thisTable, Object value, boolean isSelected, boolean hasFocus, int row,
704       int column) {
705       if (value instanceof Color) {
706         panel.setBackground((Color) value);
707       }
708       if (border == null) {
709         border = BorderFactory.createMatteBorder(2, 2, 2, 2, table.getBackground());
710       }
711 
712       panel.setBorder(border);
713 
714       return panel;
715     }
716   }
717 
718   class ExpressionTableCellRenderer implements TableCellRenderer {
719     JPanel panel = new JPanel();
720     JLabel expressionLabel = new JLabel();
721     JLabel iconLabel = new JLabel();
722     Icon selectedIcon = new SelectedIcon(true);
723     Icon unselectedIcon = new SelectedIcon(false);
724 
725     ExpressionTableCellRenderer() {
726       panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
727       panel.setOpaque(true);
728       panel.add(iconLabel);
729       panel.add(Box.createHorizontalStrut(5));
730       panel.add(expressionLabel);
731     }
732     
733     void setToolTipText(String text) {
734         panel.setToolTipText(text);
735     }
736 
737     public Component getTableCellRendererComponent(
738       JTable thisTable, Object value, boolean isSelected, boolean hasFocus, int row,
739       int column) {
740 
741       Vector v = tableModel.getDataVector();
742       Vector r = (Vector) v.elementAt(row);
743       expressionLabel.setText(value.toString());
744 
745       if (r.elementAt(1) instanceof Color) {
746         expressionLabel.setBackground((Color) r.elementAt(1));
747         panel.setBackground((Color) r.elementAt(1));
748       }
749 
750       if (r.elementAt(2) instanceof Color) {
751         expressionLabel.setForeground((Color) r.elementAt(2));
752         panel.setForeground((Color) r.elementAt(2));
753       }
754 
755       if (isSelected) {
756           iconLabel.setIcon(selectedIcon);
757       } else {
758           iconLabel.setIcon(unselectedIcon);
759       }
760 
761       return panel;
762     }
763   }
764 
765   class SelectedIcon implements Icon {
766       private boolean isSelected;
767       private int width = 9;
768       private int height = 18;
769       private int[] xPoints = new int[4];
770       private int[] yPoints = new int[4];
771 
772       public SelectedIcon(boolean isSelected) {
773         this.isSelected = isSelected;
774         xPoints[0] = 0;
775         yPoints[0] = -1;
776         xPoints[1] = 0;
777         yPoints[1] = height;
778         xPoints[2] = width;
779         yPoints[2] = height / 2;
780         xPoints[3] = width;
781         yPoints[3] = (height / 2) - 1;
782       }
783 
784       public int getIconHeight() {
785         return height;
786       }
787 
788       public int getIconWidth() {
789         return width;
790       }
791 
792       public void paintIcon(Component c, Graphics g, int x, int y) {
793         if (isSelected) {
794           int length = xPoints.length;
795           int[] newXPoints = new int[length];
796           int[] newYPoints = new int[length];
797 
798           for (int i = 0; i < length; i++) {
799             newXPoints[i] = xPoints[i] + x;
800             newYPoints[i] = yPoints[i] + y;
801           }
802 
803           g.setColor(Color.black);
804 
805           g.fillPolygon(newXPoints, newYPoints, length);
806         }
807       }
808   }
809 }