| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.log4j.chainsaw.color; |
| |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.ItemEvent; |
| import java.awt.event.ItemListener; |
| import java.awt.event.MouseEvent; |
| import java.util.*; |
| import java.util.List; |
| import javax.swing.*; |
| import javax.swing.border.Border; |
| import javax.swing.table.AbstractTableModel; |
| import javax.swing.table.DefaultTableModel; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import org.apache.commons.configuration2.AbstractConfiguration; |
| import org.apache.log4j.chainsaw.ChainsawConstants; |
| import org.apache.log4j.chainsaw.ExpressionRuleContext; |
| import org.apache.log4j.chainsaw.components.logpanel.LogPanel; |
| import org.apache.log4j.chainsaw.filter.FilterModel; |
| import org.apache.log4j.chainsaw.helper.SwingHelper; |
| import org.apache.log4j.chainsaw.icons.ChainsawIcons; |
| import org.apache.log4j.chainsaw.prefs.SettingsManager; |
| import org.apache.log4j.rule.ColorRule; |
| import org.apache.log4j.rule.ExpressionRule; |
| import org.apache.log4j.rule.Rule; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| |
| /** |
| * Panel which updates a RuleColorizer, allowing the user to build expression-based |
| * color rules. |
| * <p> |
| * TODO: examine ColorPanel/RuleColorizer/LogPanel listeners and interactions |
| * |
| * @author Scott Deboy <sdeboy@apache.org> |
| */ |
| public class ColorPanel extends JPanel { |
| private static final String LABEL_DOUBLE_CLICK_TO_EDIT = "Double click to edit"; |
| private static final String LABEL_BROWSE = "Browse..."; |
| private static final String DEFAULT_STATUS = "<html>Double click a rule field to edit the rule</html>"; |
| |
| private static final Logger logger = LogManager.getLogger(); |
| |
| private final FilterModel filterModel; |
| private final RulesTableModel rulesTableModel; |
| private final JScrollPane tableScrollPane; |
| private final JTable rulesTable; |
| private ActionListener closeListener; |
| private final JLabel statusBar; |
| private static final String NO_TAB = "None"; |
| private final DefaultComboBoxModel<String> logPanelColorizersModel; |
| private final Map<String, RuleColorizer> allLogPanelColorizers; |
| private final JTable searchTable; |
| private final DefaultTableModel searchTableModel; |
| private final Vector<Color> searchDataVectorEntry; |
| private final RuleColorizer globalColorizer; |
| private final JTable alternatingColorTable; |
| private final Vector<Color> alternatingColorDataVectorEntry; |
| private JCheckBox bypassSearchColorsCheckBox; |
| private JCheckBox globalRulesCheckbox; |
| private final LogPanel parentLogPanel; |
| private JLabel rulesLabel; |
| private SettingsManager settingsManager; |
| |
| public ColorPanel( |
| SettingsManager settingsManager, |
| final RuleColorizer globalColorizer, |
| final FilterModel filterModel, |
| final Map<String, RuleColorizer> allLogPanelColorizers, |
| final LogPanel parent) { |
| super(new BorderLayout()); |
| this.settingsManager = settingsManager; |
| |
| AbstractConfiguration configuration = settingsManager.getGlobalConfiguration(); |
| |
| this.filterModel = filterModel; |
| this.allLogPanelColorizers = allLogPanelColorizers; |
| this.globalColorizer = globalColorizer; |
| this.parentLogPanel = parent; |
| |
| parentLogPanel.getCurrentRuleColorizer().addPropertyChangeListener("colorrule", evt -> updateColors()); |
| |
| rulesTableModel = new RulesTableModel(); |
| rulesTable = new JTable(rulesTableModel); |
| rulesTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT); |
| rulesTable.getColumnModel().getColumn(0).setCellEditor(new RulesTableCellEditor()); |
| |
| searchTableModel = new DefaultTableModel(); |
| searchTable = new JTable(searchTableModel); |
| searchTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT); |
| searchTable.setPreferredScrollableViewportSize(new Dimension(30, 30)); |
| |
| DefaultTableModel alternatingColorTableModel = new DefaultTableModel(); |
| alternatingColorTable = new JTable(alternatingColorTableModel); |
| alternatingColorTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT); |
| alternatingColorTable.setPreferredScrollableViewportSize(new Dimension(30, 30)); |
| |
| Vector<String> searchColumns = new Vector<>(); |
| searchColumns.add("Background"); |
| searchColumns.add("Foreground"); |
| |
| // searchtable contains only a single-entry vector containing a two-item vector (foreground, background) |
| Vector<Vector<Color>> searchDataVector = new Vector<>(); |
| searchDataVectorEntry = new Vector<>(); |
| searchDataVectorEntry.add( |
| configuration.get(Color.class, "searchBackgroundColor", ChainsawConstants.FIND_LOGGER_BACKGROUND)); |
| searchDataVectorEntry.add( |
| configuration.get(Color.class, "searchForegroundColor", ChainsawConstants.FIND_LOGGER_FOREGROUND)); |
| searchDataVector.add(searchDataVectorEntry); |
| searchTableModel.setDataVector(searchDataVector, searchColumns); |
| |
| Vector<Vector<Color>> alternatingColorDataVector = new Vector<>(); |
| alternatingColorDataVectorEntry = new Vector<>(); |
| alternatingColorDataVectorEntry.add(configuration.get( |
| Color.class, "alternatingColorBackground", ChainsawConstants.COLOR_ODD_ROW_BACKGROUND)); |
| alternatingColorDataVectorEntry.add(configuration.get( |
| Color.class, "alternatingColorForeground", ChainsawConstants.COLOR_ODD_ROW_FOREGROUND)); |
| alternatingColorDataVector.add(alternatingColorDataVectorEntry); |
| |
| Vector<String> alternatingColorColumns = new Vector<>(); |
| alternatingColorColumns.add("Background"); |
| alternatingColorColumns.add("Foreground"); |
| |
| alternatingColorTableModel.setDataVector(alternatingColorDataVector, alternatingColorColumns); |
| |
| rulesTable.setPreferredScrollableViewportSize(new Dimension(525, 200)); |
| tableScrollPane = new JScrollPane(rulesTable); |
| |
| rulesTable.sizeColumnsToFit(0); |
| rulesTable.getColumnModel().getColumn(1).setPreferredWidth(80); |
| rulesTable.getColumnModel().getColumn(2).setPreferredWidth(80); |
| rulesTable.getColumnModel().getColumn(1).setMaxWidth(80); |
| rulesTable.getColumnModel().getColumn(2).setMaxWidth(80); |
| |
| searchTable.sizeColumnsToFit(0); |
| searchTable.getColumnModel().getColumn(0).setPreferredWidth(80); |
| searchTable.getColumnModel().getColumn(1).setPreferredWidth(80); |
| searchTable.getColumnModel().getColumn(0).setMaxWidth(80); |
| searchTable.getColumnModel().getColumn(1).setMaxWidth(80); |
| // building color choosers needs to be done on the EDT |
| SwingHelper.invokeOnEDT(() -> configureSingleEntryColorTable(searchTable)); |
| |
| alternatingColorTable.sizeColumnsToFit(0); |
| alternatingColorTable.getColumnModel().getColumn(0).setPreferredWidth(80); |
| alternatingColorTable.getColumnModel().getColumn(1).setPreferredWidth(80); |
| alternatingColorTable.getColumnModel().getColumn(0).setMaxWidth(80); |
| alternatingColorTable.getColumnModel().getColumn(1).setMaxWidth(80); |
| // building color choosers needs to be done on the EDT |
| SwingHelper.invokeOnEDT(() -> configureSingleEntryColorTable(alternatingColorTable)); |
| |
| configureTable(); |
| |
| statusBar = new JLabel(DEFAULT_STATUS); |
| |
| JPanel rightPanel = new JPanel(new BorderLayout()); |
| |
| JPanel rightOuterPanel = new JPanel(); |
| rightOuterPanel.setLayout(new BoxLayout(rightOuterPanel, BoxLayout.X_AXIS)); |
| rightOuterPanel.add(Box.createHorizontalStrut(10)); |
| |
| JPanel southPanel = new JPanel(); |
| southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS)); |
| southPanel.add(Box.createVerticalStrut(5)); |
| southPanel.add(Box.createVerticalStrut(5)); |
| JPanel searchAndAlternatingColorPanel = buildSearchAndAlternatingColorPanel(); |
| JPanel bypassSearchColorsPanel = buildBypassSearchColorsPanel(); |
| bypassSearchColorsCheckBox.setSelected(configuration.getBoolean("bypassSearchColors", false)); |
| |
| JPanel globalLabelPanel = new JPanel(); |
| globalLabelPanel.setLayout(new BoxLayout(globalLabelPanel, BoxLayout.X_AXIS)); |
| JLabel globalLabel = new JLabel("Global colors:"); |
| globalLabelPanel.add(globalLabel); |
| globalLabelPanel.add(Box.createHorizontalGlue()); |
| southPanel.add(globalLabelPanel); |
| southPanel.add(searchAndAlternatingColorPanel); |
| southPanel.add(bypassSearchColorsPanel); |
| southPanel.add(Box.createVerticalStrut(5)); |
| JPanel closePanel = buildClosePanel(); |
| southPanel.add(closePanel); |
| |
| JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); |
| statusPanel.add(statusBar); |
| southPanel.add(statusPanel); |
| |
| JPanel rulesPanel = buildRulesPanel(); |
| rulesPanel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| |
| rightPanel.add(rulesPanel, BorderLayout.CENTER); |
| rightPanel.add(southPanel, BorderLayout.SOUTH); |
| rightOuterPanel.add(rightPanel); |
| |
| JPanel topPanel = new JPanel(); |
| topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS)); |
| |
| JLabel selectText = new JLabel("Apply a tab's colors"); |
| topPanel.add(selectText); |
| topPanel.add(Box.createHorizontalStrut(5)); |
| |
| logPanelColorizersModel = new DefaultComboBoxModel(); |
| final JComboBox loadPanelColorizersComboBox = new JComboBox(logPanelColorizersModel); |
| loadLogPanelColorizers(); |
| |
| topPanel.add(loadPanelColorizersComboBox); |
| |
| topPanel.add(Box.createHorizontalStrut(5)); |
| final Action copyRulesAction = new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| rulesTableModel.clear(); |
| Object selectedItem = loadPanelColorizersComboBox.getSelectedItem(); |
| if (selectedItem != null) { |
| RuleColorizer sourceColorizer = allLogPanelColorizers.get(selectedItem.toString()); |
| RuleColorizer newColorizer = new RuleColorizer(); |
| newColorizer.setRules(sourceColorizer.getRules()); |
| parent.setRuleColorizer(newColorizer); |
| updateColors(); |
| parentLogPanel |
| .getCurrentRuleColorizer() |
| .addPropertyChangeListener("colorrule", evt -> updateColors()); |
| } |
| } |
| }; |
| |
| loadPanelColorizersComboBox.addActionListener(e -> { |
| Object selectedItem = loadPanelColorizersComboBox.getSelectedItem(); |
| if (selectedItem != null) { |
| String selectedColorizerName = selectedItem.toString(); |
| copyRulesAction.setEnabled(!(NO_TAB.equals(selectedColorizerName))); |
| } |
| }); |
| |
| copyRulesAction.putValue(Action.NAME, "Copy color rules"); |
| copyRulesAction.setEnabled(!(NO_TAB.equals(loadPanelColorizersComboBox.getSelectedItem()))); |
| |
| JButton copyRulesButton = new JButton(copyRulesAction); |
| topPanel.add(copyRulesButton); |
| |
| add(topPanel, BorderLayout.NORTH); |
| add(rightOuterPanel, BorderLayout.CENTER); |
| if (rulesTable.getRowCount() > 0) { |
| rulesTable.getSelectionModel().setSelectionInterval(0, 0); |
| } |
| } |
| |
| public void loadLogPanelColorizers() { |
| if (logPanelColorizersModel.getIndexOf(NO_TAB) == -1) { |
| logPanelColorizersModel.addElement(NO_TAB); |
| } |
| RuleColorizer currentLogPanelColorizer = parentLogPanel.getCurrentRuleColorizer(); |
| for (Map.Entry<String, RuleColorizer> entry : allLogPanelColorizers.entrySet()) { |
| if (!entry.getValue().equals(currentLogPanelColorizer) |
| && (logPanelColorizersModel.getIndexOf(entry.getKey()) == -1)) { |
| logPanelColorizersModel.addElement(entry.getKey()); |
| } |
| } |
| |
| AbstractConfiguration configuration = settingsManager.getGlobalConfiguration(); |
| // update search and alternating colors, since they may have changed from another color panel |
| searchDataVectorEntry.set( |
| 0, configuration.get(Color.class, "searchBackgroundColor", ChainsawConstants.FIND_LOGGER_BACKGROUND)); |
| searchDataVectorEntry.set( |
| 1, configuration.get(Color.class, "searchForegroundColor", ChainsawConstants.FIND_LOGGER_FOREGROUND)); |
| alternatingColorDataVectorEntry.set( |
| 0, |
| configuration.get( |
| Color.class, "alternatingColorBackground", ChainsawConstants.COLOR_ODD_ROW_BACKGROUND)); |
| alternatingColorDataVectorEntry.set( |
| 1, |
| configuration.get( |
| Color.class, "alternatingColorForeground", ChainsawConstants.COLOR_ODD_ROW_FOREGROUND)); |
| } |
| |
| public void componentChanged() { |
| logger.debug( |
| "Color panel changed. Current colorizer: {} Global colorizer: {}", |
| parentLogPanel.getCurrentRuleColorizer(), |
| globalColorizer); |
| |
| if (globalColorizer == parentLogPanel.getCurrentRuleColorizer()) { |
| globalRulesCheckbox.setSelected(true); |
| rulesLabel.setText("Global rules:"); |
| } else { |
| globalRulesCheckbox.setSelected(false); |
| rulesLabel.setText("Rules:"); |
| } |
| |
| loadLogPanelColorizers(); |
| updateColors(); |
| } |
| |
| public JPanel buildBypassSearchColorsPanel() { |
| JPanel panel = new JPanel(); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); |
| |
| bypassSearchColorsCheckBox = new JCheckBox("Don't use a search color for matching rows"); |
| panel.add(bypassSearchColorsCheckBox); |
| return panel; |
| } |
| |
| public JPanel buildSearchAndAlternatingColorPanel() { |
| JPanel panel = new JPanel(); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); |
| |
| JLabel defineSearchColorsLabel = new JLabel("Find colors"); |
| |
| panel.add(defineSearchColorsLabel); |
| |
| panel.add(Box.createHorizontalStrut(10)); |
| JScrollPane searchPane = new JScrollPane(searchTable); |
| searchPane.setBorder(BorderFactory.createEmptyBorder()); |
| panel.add(searchPane); |
| |
| panel.add(Box.createHorizontalStrut(10)); |
| JLabel defineAlternatingColorLabel = new JLabel("Alternating colors"); |
| |
| panel.add(defineAlternatingColorLabel); |
| |
| panel.add(Box.createHorizontalStrut(10)); |
| JScrollPane alternatingColorPane = new JScrollPane(alternatingColorTable); |
| alternatingColorPane.setBorder(BorderFactory.createEmptyBorder()); |
| |
| panel.add(alternatingColorPane); |
| panel.setBorder(BorderFactory.createEtchedBorder()); |
| panel.add(Box.createHorizontalGlue()); |
| return panel; |
| } |
| |
| public void updateColors() { |
| rulesTableModel.clear(); |
| rulesTableModel.addColorRules(); |
| rulesTableModel.fireTableDataChanged(); |
| } |
| |
| private void configureTable() { |
| prepareTable(rulesTable, 1, 2); |
| |
| JTextField textField = new JTextField(); |
| textField.addKeyListener(new ExpressionRuleContext(filterModel, textField)); |
| |
| rulesTable.getColumnModel().getColumn(0).setCellRenderer(new ExpressionTableCellRenderer()); |
| rulesTable.getColumnModel().getColumn(1).setCellRenderer(new ColorTableCellRenderer()); |
| rulesTable.getColumnModel().getColumn(2).setCellRenderer(new ColorTableCellRenderer()); |
| } |
| |
| private void configureSingleEntryColorTable(JTable thisTable) { |
| prepareTable(thisTable, 0, 1); |
| |
| thisTable.getColumnModel().getColumn(0).setCellRenderer(new ColorTableCellRenderer()); |
| thisTable.getColumnModel().getColumn(1).setCellRenderer(new ColorTableCellRenderer()); |
| } |
| |
| private JTable prepareTable(JTable table, int backgroundEditorPosition, int foregroundEditorPosition) { |
| table.setToolTipText(LABEL_DOUBLE_CLICK_TO_EDIT); |
| table.setRowHeight(20); |
| table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| table.setColumnSelectionAllowed(false); |
| |
| JComboBox background = createColorJComboBox(); |
| JComboBox foreground = createColorJComboBox(); |
| |
| DefaultCellEditor backgroundEditor = new DefaultCellEditor(background); |
| DefaultCellEditor foregroundEditor = new DefaultCellEditor(foreground); |
| |
| table.getColumnModel().getColumn(backgroundEditorPosition).setCellEditor(backgroundEditor); |
| table.getColumnModel().getColumn(foregroundEditorPosition).setCellEditor(foregroundEditor); |
| |
| background.addItemListener(new ColorItemListener(background)); |
| foreground.addItemListener(new ColorItemListener(foreground)); |
| return table; |
| } |
| |
| private JComboBox createColorJComboBox() { |
| Vector colors = new Vector(RuleColorizer.getDefaultColors()); |
| colors.add(LABEL_BROWSE); |
| JComboBox comboBox = new JComboBox(colors); |
| comboBox.setMaximumRowCount(15); |
| comboBox.setRenderer(new ColorListCellRenderer()); |
| return comboBox; |
| } |
| |
| public void setCloseActionListener(ActionListener listener) { |
| closeListener = listener; |
| } |
| |
| public void hidePanel() { |
| if (closeListener != null) { |
| closeListener.actionPerformed(null); |
| } |
| } |
| |
| void applyRules(RuleColorizer applyingColorizer) { |
| rulesTable.getColumnModel().getColumn(0).getCellEditor().stopCellEditing(); |
| |
| ((ExpressionTableCellRenderer) rulesTable.getColumnModel().getColumn(0).getCellRenderer()) |
| .setToolTipText(LABEL_DOUBLE_CLICK_TO_EDIT); |
| statusBar.setText(DEFAULT_STATUS); |
| |
| applyingColorizer.setRules(rulesTableModel.rules()); |
| } |
| |
| JPanel buildClosePanel() { |
| JPanel panel = new JPanel(); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); |
| panel.add(Box.createHorizontalGlue()); |
| |
| JButton applyButton = new JButton(" Apply "); |
| |
| applyButton.addActionListener(new AbstractAction() { |
| public void actionPerformed(ActionEvent evt) { |
| applyRules(parentLogPanel.getCurrentRuleColorizer()); |
| saveSearchColors(); |
| } |
| }); |
| |
| panel.add(Box.createHorizontalStrut(10)); |
| panel.add(applyButton); |
| |
| JButton closeButton = new JButton(" Close "); |
| |
| closeButton.addActionListener(new AbstractAction() { |
| public void actionPerformed(ActionEvent evt) { |
| hidePanel(); |
| } |
| }); |
| panel.add(Box.createHorizontalStrut(10)); |
| panel.add(closeButton); |
| |
| return panel; |
| } |
| |
| private void saveSearchColors() { |
| Vector thisVector = searchTableModel.getDataVector().get(0); |
| AbstractConfiguration globalConfig = settingsManager.getGlobalConfiguration(); |
| globalConfig.setProperty("searchBackgroundColor", RuleColorizer.colorToRGBString((Color) thisVector.get(0))); |
| globalConfig.setProperty("searchForegroundColor", RuleColorizer.colorToRGBString((Color) thisVector.get(1))); |
| } |
| |
| JPanel buildUpDownPanel() { |
| JPanel panel = new JPanel(new BorderLayout()); |
| JPanel innerPanel = new JPanel(); |
| innerPanel.setLayout(new GridLayout(5, 1)); |
| |
| final JButton upButton = new JButton(ChainsawIcons.ICON_UP); |
| upButton.setToolTipText("Move selected rule up"); |
| |
| final JButton downButton = new JButton(ChainsawIcons.ICON_DOWN); |
| downButton.setToolTipText("Move selected rule down"); |
| upButton.setEnabled(false); |
| downButton.setEnabled(false); |
| |
| rulesTable.getSelectionModel().addListSelectionListener(e -> { |
| if (!e.getValueIsAdjusting()) { |
| int index = rulesTable.getSelectionModel().getMaxSelectionIndex(); |
| |
| if (index < 0) { |
| downButton.setEnabled(false); |
| upButton.setEnabled(false); |
| } else if ((index == 0) && (rulesTableModel.getRowCount() == 1)) { |
| downButton.setEnabled(false); |
| upButton.setEnabled(false); |
| } else if ((index == 0) && (rulesTableModel.getRowCount() > 1)) { |
| downButton.setEnabled(true); |
| upButton.setEnabled(false); |
| } else if (index == (rulesTableModel.getRowCount() - 1)) { |
| downButton.setEnabled(false); |
| upButton.setEnabled(true); |
| } else { |
| downButton.setEnabled(true); |
| upButton.setEnabled(true); |
| } |
| } |
| }); |
| |
| JPanel upPanel = new JPanel(); |
| |
| upPanel.add(upButton); |
| |
| JPanel downPanel = new JPanel(); |
| downPanel.add(downButton); |
| |
| innerPanel.add(new JLabel("")); |
| innerPanel.add(upPanel); |
| innerPanel.add(new JLabel("")); |
| innerPanel.add(downPanel); |
| panel.add(innerPanel, BorderLayout.CENTER); |
| |
| upButton.addActionListener(new AbstractAction() { |
| public void actionPerformed(ActionEvent evt) { |
| int index = rulesTable.getSelectionModel().getMaxSelectionIndex(); |
| rulesTableModel.moveRowAtIndexUp(index); |
| } |
| }); |
| |
| downButton.addActionListener(new AbstractAction() { |
| public void actionPerformed(ActionEvent evt) { |
| int index = rulesTable.getSelectionModel().getMaxSelectionIndex(); |
| rulesTableModel.moveRowAtIndexDown(index); |
| } |
| }); |
| |
| return panel; |
| } |
| |
| JPanel buildRulesPanel() { |
| JPanel listPanel = new JPanel(new BorderLayout()); |
| JPanel panel = new JPanel(); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); |
| |
| panel.add(Box.createVerticalStrut(10)); |
| |
| rulesLabel = new JLabel("Rules:"); |
| globalRulesCheckbox = new JCheckBox("Use global rules"); |
| |
| if (globalColorizer == parentLogPanel.getCurrentRuleColorizer()) { |
| globalRulesCheckbox.setSelected(true); |
| rulesLabel.setText("Global rules:"); |
| } |
| |
| globalRulesCheckbox.addActionListener(new AbstractAction() { |
| @Override |
| public void actionPerformed(ActionEvent ae) { |
| if (globalRulesCheckbox.isSelected()) { |
| parentLogPanel.setRuleColorizer(globalColorizer); |
| } else { |
| parentLogPanel.setRuleColorizer(new RuleColorizer()); |
| } |
| componentChanged(); |
| parentLogPanel.getCurrentRuleColorizer().addPropertyChangeListener("colorrule", evt -> updateColors()); |
| } |
| }); |
| |
| panel.add(globalRulesCheckbox); |
| panel.add(rulesLabel); |
| |
| JPanel buttonPanel = new JPanel(new GridLayout(0, 2)); |
| buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| |
| JPanel newPanel = new JPanel(); |
| JButton newButton = new JButton(" New "); |
| newButton.addActionListener(new AbstractAction() { |
| public void actionPerformed(ActionEvent evt) { |
| rulesTableModel.addNewRule(); |
| } |
| }); |
| |
| newPanel.add(newButton); |
| |
| JPanel deletePanel = new JPanel(); |
| final JButton deleteButton = new JButton(" Delete "); |
| deleteButton.setEnabled(false); |
| |
| deleteButton.addActionListener(new AbstractAction() { |
| public void actionPerformed(ActionEvent evt) { |
| int index = rulesTable.getSelectionModel().getMaxSelectionIndex(); |
| rulesTableModel.deleteRuleAtIndex(index); |
| } |
| }); |
| |
| rulesTable.getSelectionModel().addListSelectionListener(e -> { |
| if (!e.getValueIsAdjusting()) { |
| int index = rulesTable.getSelectionModel().getMaxSelectionIndex(); |
| deleteButton.setEnabled(index >= 0); |
| } |
| }); |
| |
| deletePanel.add(deleteButton); |
| |
| buttonPanel.add(newPanel); |
| buttonPanel.add(deletePanel); |
| |
| listPanel.add(panel, BorderLayout.NORTH); |
| |
| JPanel tablePanel = new JPanel(new BorderLayout()); |
| tableScrollPane.setBorder(BorderFactory.createEtchedBorder()); |
| tablePanel.add(tableScrollPane, BorderLayout.CENTER); |
| tablePanel.add(buildUpDownPanel(), BorderLayout.EAST); |
| listPanel.add(tablePanel, BorderLayout.CENTER); |
| listPanel.add(buttonPanel, BorderLayout.SOUTH); |
| |
| return listPanel; |
| } |
| |
| class ColorListCellRenderer extends JLabel implements ListCellRenderer { |
| ColorListCellRenderer() { |
| setOpaque(true); |
| } |
| |
| public Component getListCellRendererComponent( |
| JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
| setText(" "); |
| |
| if (isSelected && (index > -1)) { |
| setBorder(BorderFactory.createLineBorder(Color.black, 2)); |
| } else { |
| setBorder(BorderFactory.createEmptyBorder()); |
| } |
| |
| if (value instanceof Color) { |
| setBackground((Color) value); |
| } else { |
| setBackground(Color.white); |
| |
| if (value != null) { |
| setText(value.toString()); |
| } |
| } |
| |
| return this; |
| } |
| } |
| |
| class ColorItemListener implements ItemListener { |
| JComboBox box; |
| JDialog dialog; |
| JColorChooser colorChooser; |
| Color lastColor; |
| |
| ColorItemListener(final JComboBox box) { |
| this.box = box; |
| colorChooser = new JColorChooser(); |
| dialog = JColorChooser.createDialog( |
| box, |
| "Pick a Color", |
| true, // modal |
| colorChooser, |
| e -> { |
| box.insertItemAt(colorChooser.getColor(), 0); |
| box.setSelectedIndex(0); |
| }, // OK button handler |
| e -> box.setSelectedItem(lastColor)); // CANCEL button handler |
| dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
| } |
| |
| public void itemStateChanged(ItemEvent e) { |
| if (e.getStateChange() == ItemEvent.SELECTED) { |
| if (box.getSelectedItem() instanceof Color) { |
| box.setBackground((Color) box.getSelectedItem()); |
| repaint(); |
| } else { |
| box.setBackground(Color.white); |
| int selectedRow = rulesTable.getSelectedRow(); |
| int selectedColumn = rulesTable.getSelectedColumn(); |
| if (selectedRow != -1 && selectedColumn != -1) { |
| colorChooser.setColor((Color) rulesTable.getValueAt(selectedRow, selectedColumn)); |
| lastColor = (Color) rulesTable.getValueAt(selectedRow, selectedColumn); |
| } |
| dialog.setVisible(true); |
| } |
| } |
| } |
| } |
| |
| class ColorTableCellRenderer implements TableCellRenderer { |
| Border border; |
| JPanel panel; |
| |
| ColorTableCellRenderer() { |
| panel = new JPanel(); |
| panel.setOpaque(true); |
| } |
| |
| public Color getCurrentColor() { |
| return panel.getBackground(); |
| } |
| |
| public Component getTableCellRendererComponent( |
| JTable thisTable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| if (value instanceof Color) { |
| panel.setBackground((Color) value); |
| } |
| if (border == null) { |
| border = BorderFactory.createMatteBorder(2, 2, 2, 2, rulesTable.getBackground()); |
| } |
| |
| panel.setBorder(border); |
| |
| return panel; |
| } |
| } |
| |
| class ExpressionTableCellRenderer implements TableCellRenderer { |
| JPanel panel = new JPanel(); |
| JLabel expressionLabel = new JLabel(); |
| JLabel iconLabel = new JLabel(); |
| Icon selectedIcon = new SelectedIcon(true); |
| Icon unselectedIcon = new SelectedIcon(false); |
| |
| ExpressionTableCellRenderer() { |
| panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); |
| panel.setOpaque(true); |
| panel.add(iconLabel); |
| panel.add(Box.createHorizontalStrut(5)); |
| panel.add(expressionLabel); |
| } |
| |
| void setToolTipText(String text) { |
| panel.setToolTipText(text); |
| } |
| |
| public Component getTableCellRendererComponent( |
| JTable thisTable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| if (value == null) { |
| return panel; |
| } |
| |
| List<ColorRule> rules = rulesTableModel.rules(); |
| ColorRule rule = rules.get(row); |
| expressionLabel.setText(value.toString()); |
| |
| expressionLabel.setBackground(rule.getBackgroundColor()); |
| panel.setBackground(rule.getBackgroundColor()); |
| expressionLabel.setForeground(rule.getForegroundColor()); |
| panel.setForeground(rule.getForegroundColor()); |
| |
| if (isSelected) { |
| iconLabel.setIcon(selectedIcon); |
| } else { |
| iconLabel.setIcon(unselectedIcon); |
| } |
| |
| return panel; |
| } |
| } |
| |
| class SelectedIcon implements Icon { |
| private final boolean isSelected; |
| private final int width = 9; |
| private final int height = 18; |
| private final int[] xPoints = new int[4]; |
| private final int[] yPoints = new int[4]; |
| |
| public SelectedIcon(boolean isSelected) { |
| this.isSelected = isSelected; |
| xPoints[0] = 0; |
| yPoints[0] = -1; |
| xPoints[1] = 0; |
| yPoints[1] = height; |
| xPoints[2] = width; |
| yPoints[2] = height / 2; |
| xPoints[3] = width; |
| yPoints[3] = (height / 2) - 1; |
| } |
| |
| public int getIconHeight() { |
| return height; |
| } |
| |
| public int getIconWidth() { |
| return width; |
| } |
| |
| public void paintIcon(Component c, Graphics g, int x, int y) { |
| if (isSelected) { |
| int length = xPoints.length; |
| int[] newXPoints = new int[length]; |
| int[] newYPoints = new int[length]; |
| |
| for (int i = 0; i < length; i++) { |
| newXPoints[i] = xPoints[i] + x; |
| newYPoints[i] = yPoints[i] + y; |
| } |
| |
| g.setColor(Color.black); |
| |
| g.fillPolygon(newXPoints, newYPoints, length); |
| } |
| } |
| } |
| |
| private class RulesTableModel extends AbstractTableModel { |
| |
| private final List<ColorRule> colorRules; |
| |
| RulesTableModel() { |
| colorRules = new ArrayList<>(); |
| addColorRules(); |
| } |
| |
| List<ColorRule> rules() { |
| return colorRules; |
| } |
| |
| void clear() { |
| colorRules.clear(); |
| fireTableDataChanged(); |
| } |
| |
| void addNewRule() { |
| Rule expressionRule = ExpressionRule.getRule("msg=='sample'"); |
| ColorRule newRule = new ColorRule("msg=='sample'", expressionRule, Color.WHITE, Color.BLACK); |
| colorRules.add(newRule); |
| fireTableRowsInserted(colorRules.size() - 1, colorRules.size() - 1); |
| } |
| |
| void deleteRuleAtIndex(int i) { |
| colorRules.remove(i); |
| fireTableRowsDeleted(i, i); |
| } |
| |
| void moveRowAtIndexDown(int row) { |
| if (row <= colorRules.size() - 2 && row >= 0) { |
| Collections.swap(colorRules, row, row + 1); |
| fireTableRowsUpdated(row, row + 1); |
| } |
| } |
| |
| void moveRowAtIndexUp(int row) { |
| if (row <= colorRules.size() && row > 0) { |
| Collections.swap(colorRules, row, row - 1); |
| fireTableRowsUpdated(row - 1, row); |
| } |
| } |
| |
| void addColorRules() { |
| colorRules.addAll(parentLogPanel.getCurrentRuleColorizer().getRules()); |
| } |
| |
| @Override |
| public void setValueAt(Object aValue, int rowIndex, int columnIndex) { |
| if (rowIndex >= colorRules.size()) { |
| return; |
| } |
| String expression = colorRules.get(rowIndex).getExpression(); |
| Rule rule = colorRules.get(rowIndex).getRule(); |
| Color backgroundColor = colorRules.get(rowIndex).getBackgroundColor(); |
| Color foregroundColor = colorRules.get(rowIndex).getForegroundColor(); |
| |
| switch (columnIndex) { |
| case 0: |
| expression = aValue.toString(); |
| rule = ExpressionRule.getRule(expression); |
| break; |
| case 1: |
| backgroundColor = (Color) aValue; |
| break; |
| case 2: |
| foregroundColor = (Color) aValue; |
| break; |
| } |
| |
| colorRules.set(rowIndex, new ColorRule(expression, rule, backgroundColor, foregroundColor)); |
| // Since the value has been set, resize all of the rows(if required) |
| rulesTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT); |
| fireTableRowsUpdated(rowIndex, rowIndex); |
| } |
| |
| @Override |
| public int getRowCount() { |
| return colorRules.size(); |
| } |
| |
| @Override |
| public int getColumnCount() { |
| return 3; |
| } |
| |
| @Override |
| public Object getValueAt(int row, int col) { |
| ColorRule rule = colorRules.get(row); |
| switch (col) { |
| case 0: |
| return rule.getExpression(); |
| case 1: |
| return rule.getBackgroundColor(); |
| case 2: |
| return rule.getForegroundColor(); |
| default: |
| return "foo"; |
| } |
| } |
| |
| @Override |
| public String getColumnName(int col) { |
| switch (col) { |
| case 0: |
| return "Expression"; |
| case 1: |
| return "Background Color"; |
| case 2: |
| return "Foreground Color"; |
| default: |
| return "BAD COL NAME"; |
| } |
| } |
| |
| @Override |
| public boolean isCellEditable(int row, int col) { |
| return true; |
| } |
| } |
| |
| private class RulesTableCellEditor extends AbstractCellEditor implements TableCellEditor { |
| |
| private final DefaultCellEditor defaultEditor = new DefaultCellEditor(new JTextField()); |
| |
| @Override |
| public Object getCellEditorValue() { |
| return defaultEditor.getCellEditorValue(); |
| } |
| |
| @Override |
| public Component getTableCellEditorComponent( |
| JTable table, Object value, boolean isSelected, int row, int column) { |
| Component component = defaultEditor.getTableCellEditorComponent(table, value, isSelected, row, column); |
| table.setRowHeight(row, component.getPreferredSize().height); |
| return component; |
| } |
| |
| @Override |
| public boolean isCellEditable(EventObject event) { |
| if (event instanceof MouseEvent) { |
| MouseEvent mouseEvent = (MouseEvent) event; |
| return mouseEvent.getClickCount() >= 2; |
| } |
| return false; |
| } |
| } |
| } |