All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kg.apc.jmeter.graphs.AbstractGraphPanelVisualizer Maven / Gradle / Ivy

There is a newer version: 0.7
Show newest version
// fixme: perhaps we should replace model via setModel and use ONE model...
// TODO: https://groups.google.com/forum/#!topic/jmeter-plugins/qflK3oCjv4c
package kg.apc.jmeter.graphs;

import kg.apc.charting.*;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.vizualizers.CompositeResultCollector;
import kg.apc.jmeter.vizualizers.CorrectedResultCollector;
import kg.apc.jmeter.vizualizers.JSettingsPanel;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.LongProperty;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.ImageVisualizer;
import org.apache.jmeter.visualizers.Sample;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListMap;

public abstract class AbstractGraphPanelVisualizer
        extends AbstractVisualizer
        implements Clearable,
        ImageVisualizer,
        SettingsInterface {

    private static final Logger log = LoggingManager.getLoggerForClass();
    /**
     *
     */
    protected ConcurrentSkipListMap model;
    protected ConcurrentSkipListMap modelAggregate;
    /**
     *
     */
    protected long lastRepaint = 0;
    /**
     *
     */
    private int interval = 500;
    protected boolean isAggregate = false;
    /**
     *
     */
    protected GraphPanel graphPanel;
    /**
     *
     */
    protected ColorsDispatcher colors;
    private static final long REPAINT_INTERVAL = 500;
    public static final String INTERVAL_PROPERTY = "interval_grouping";
    public static final String GRAPH_AGGREGATED = "graph_aggregated";
    private JSettingsPanel settingsPanel = null;
    //
    private List includes = new ArrayList<>(0);
    private List excludes = new ArrayList<>(0);
    private String incRegex;
    private String excRegex;
    private boolean includeRegexChkboxState;
    private boolean excludeRegexChkboxState;
    protected long startTimeRef = 0;
    protected long startTimeInf;
    protected long startTimeSup;
    private long startOffset;
    private long endOffset;

    private JPanel container;
    private boolean filePanelVisible = true;
    private boolean maximized = false;
    private JButton maximizeButton;
    private boolean ignoreCurrentTestStartTime;

    private LabelToColorMapping labelToColorMapping = null;

    private void reloadLabelToColorMapping() {

        String labelToColorMappingString = JMeterUtils.getProperty("jmeterPlugin.labelToColorMapping");
        labelToColorMapping = LabelToColorMapping.load(labelToColorMappingString);
    }

    /**
     *
     */
    public AbstractGraphPanelVisualizer() {
        super();
        model = new ConcurrentSkipListMap<>();
        modelAggregate = new ConcurrentSkipListMap<>();
        colors = ColorsDispatcherFactory.getColorsDispatcher();
        //RowsProviderResultCollector resCollector = new RowsProviderResultCollector();
        //setModel(resCollector);
        initGui();
    }

    protected abstract JSettingsPanel createSettingsPanel();

    @Override
    public abstract String getStaticLabel();

    private void initGui() {
        setBorder(makeBorder());
        setLayout(new BorderLayout());
        add(JMeterPluginsUtils.addHelpLinkToPanel(makeTitlePanel(), getWikiPage()), BorderLayout.NORTH);
        container = getGraphPanelContainer();
        container.add(createGraphPanel(), BorderLayout.CENTER);
        add(container, BorderLayout.CENTER);
        reloadLabelToColorMapping();
        addMouseClickColorChangeListener();
    }


    @Override
    protected Component createTitleLabel() {
        JPanel pan = new JPanel(new GridBagLayout());
        JLabel titleLabel = new JLabel(getStaticLabel());
        Font curFont = titleLabel.getFont();
        titleLabel.setFont(curFont.deriveFont((float) curFont.getSize() + 4));

        GridBagConstraints labelConstraints = new GridBagConstraints();
        labelConstraints.weightx = 1.0;
        labelConstraints.fill = GridBagConstraints.HORIZONTAL;
        labelConstraints.gridx = 0;
        labelConstraints.gridy = 0;

        pan.add(titleLabel, labelConstraints);

        GridBagConstraints buttonConstraints = new GridBagConstraints();
        buttonConstraints.gridx = 1;
        buttonConstraints.gridy = 0;
        buttonConstraints.insets = new java.awt.Insets(0, 0, 0, 1);

        maximizeButton = new JButton(new javax.swing.ImageIcon(getClass().getResource("/kg/apc/jmeter/img/maximize.png")));
        maximizeButton.setFocusable(false);
        maximizeButton.setPreferredSize(new java.awt.Dimension(20, 20));
        maximizeButton.setToolTipText("Maximize Panel");

        maximizeButton.addActionListener(new MaximizeAction());

        pan.add(maximizeButton, buttonConstraints);
        return pan;
    }

    protected void enableMaximize(boolean enable) {
        maximizeButton.setVisible(enable);
    }

    protected void hideFilePanel() {
        filePanelVisible = false;
        getFilePanel().setVisible(false);
    }

    private void toogleMaximize() {
        maximized = !maximized;
        Component[] components = container.getComponents();
        for (Component cmp : components) {
            if (!(cmp instanceof GraphPanel)) {
                cmp.setVisible(!maximized);
            }
        }

        getFilePanel().setVisible(!maximized && filePanelVisible);
    }

    /**
     * Provide a JPanel with BorderLayout, holder of the GraphPanelChart, which
     * will be placed in the BorderLayout.CENTER. It can be overridden to create
     * custom Visualizer layout. Note the JMeter file panel can be retrieved
     * with getFilePanel() and moved in.
     *
     * @return a JPanel with a BorderLayout
     */
    protected JPanel getGraphPanelContainer() {
        return new JPanel(new BorderLayout());
    }

    protected GraphPanel createGraphPanel() {
        graphPanel = new GraphPanel();
        graphPanel.getGraphObject().setRows(model);
        graphPanel.getGraphObject().setPrecisionLabel(interval);
        setOptionsFromProperties(graphPanel.getGraphObject());
        setExtraChartSettings();
        // should be placed after creating graph panel
        settingsPanel = createSettingsPanel();
        graphPanel.getSettingsTab().add(getSettingsPanel(), BorderLayout.CENTER);
        //graphPanel.setSettingsTabPanel(settingsPanel);
        return graphPanel;
    }

    /*
     * Extra chart settings must be placed here as they must be called before createSettingsPanel()
     * Basically, all what is calling getChartSettings()
     */
    protected void setExtraChartSettings() {
    }

    public void updateGui(Sample sample) {
        long time = System.currentTimeMillis();
        if ((time - lastRepaint) >= REPAINT_INTERVAL) {
            updateGui();
            repaint();
            lastRepaint = time;
        }
    }

    public void updateGui() {
        graphPanel.updateGui();
    }

    @Override
    public void clearData() {

        startTimeRef = 0;
        clearRowsFromCompositeModels(getModel().getName());
        model.clear();
        modelAggregate.clear();
        colors.reset();
        graphPanel.clearRowsTab();
        updateGui();
        repaint();
    }

    @Override
    public Image getImage() {
        return graphPanel.getGraphImage();
    }

    @Override
    public int getGranulation() {
        return interval;
    }

    @Override
    public void setGranulation(int granulation) {
        if (granulation < 1) {
            throw new IllegalArgumentException("Interval cannot be less than 1");
        }
        interval = granulation;
        getSettingsPanel().setGranulationValue(granulation);
        graphPanel.getGraphObject().setPrecisionLabel(granulation);
    }

    /**
     * Invoked when the target of the listener has changed its state. This
     * implementation assumes that the target is the FilePanel, and will update
     * the result collector for the new filename.
     *
     * @param e the event that has occurred
     */
    @Override
    public void stateChanged(ChangeEvent e) {
        log.debug("getting new collector");
        collector = (CorrectedResultCollector) createTestElement();
        if (collector instanceof CorrectedResultCollector) {
            setUpFiltering((CorrectedResultCollector) collector);
        }
        collector.loadExistingFile();
    }

    @Override
    public TestElement createTestElement() {
        if (collector == null || !(collector instanceof CorrectedResultCollector)) {
            collector = new CorrectedResultCollector();
        }
        return super.createTestElement();
    }

    @Override
    public void modifyTestElement(TestElement c) {
        super.modifyTestElement(c);
        c.setProperty(new LongProperty(INTERVAL_PROPERTY, interval));
        c.setProperty(new BooleanProperty(GRAPH_AGGREGATED, isAggregate));
        c.setProperty(new StringProperty(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS, graphPanel.getRowSelectorPanel().getIncludeSampleLabels()));
        c.setProperty(new StringProperty(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS, graphPanel.getRowSelectorPanel().getExcludeSampleLabels()));
        c.setProperty(new StringProperty(CorrectedResultCollector.START_OFFSET,
                graphPanel.getRowSelectorPanel().getStartOffset()));
        c.setProperty(new StringProperty(CorrectedResultCollector.END_OFFSET,
                graphPanel.getRowSelectorPanel().getEndOffset()));

        c.setProperty(new BooleanProperty(
                CorrectedResultCollector.INCLUDE_REGEX_CHECKBOX_STATE,
                graphPanel.getRowSelectorPanel().isSelectedRegExpInc()));
        c.setProperty(new BooleanProperty(
                CorrectedResultCollector.EXCLUDE_REGEX_CHECKBOX_STATE,
                graphPanel.getRowSelectorPanel().isSelectedRegExpExc()));
    }

    @Override
    public void configure(TestElement el) {
        super.configure(el);
        int intervalProp = el.getPropertyAsInt(INTERVAL_PROPERTY);
        boolean aggregatedProp = el.getPropertyAsBoolean(GRAPH_AGGREGATED, false);
        if (intervalProp > 0) {
            setGranulation(intervalProp);
        }
        graphPanel.getRowSelectorPanel().setIncludeSampleLabels(el.getPropertyAsString(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS));
        graphPanel.getRowSelectorPanel().setExcludeSampleLabels(el.getPropertyAsString(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS));

        if (!CorrectedResultCollector.EMPTY_FIELD.equals(el
                .getPropertyAsString(CorrectedResultCollector.START_OFFSET))) {
            graphPanel
                    .getRowSelectorPanel()
                    .setStartOffset(
                            (el.getPropertyAsLong(CorrectedResultCollector.START_OFFSET)));
        }
        if (!CorrectedResultCollector.EMPTY_FIELD.equals(el
                .getPropertyAsString(CorrectedResultCollector.END_OFFSET))) {
            graphPanel
                    .getRowSelectorPanel()
                    .setEndOffset(
                            (el.getPropertyAsLong(CorrectedResultCollector.END_OFFSET)));
        }

        graphPanel
                .getRowSelectorPanel()
                .setSelectedRegExpInc(
                        el.getPropertyAsBoolean(CorrectedResultCollector.INCLUDE_REGEX_CHECKBOX_STATE));
        graphPanel
                .getRowSelectorPanel()
                .setSelectedRegExpExc(
                        el.getPropertyAsBoolean(CorrectedResultCollector.EXCLUDE_REGEX_CHECKBOX_STATE));

        if (el instanceof CorrectedResultCollector) {
            setUpFiltering((CorrectedResultCollector) el);
        }

        switchModel(aggregatedProp);
    }

    @Override
    public GraphPanelChart getGraphPanelChart() {
        return graphPanel.getGraphObject();
    }

    @Override
    public void switchModel(boolean aggregate) {
        ConcurrentSkipListMap selectedModel;
        if (aggregate) {
            // issue 64: we must fail requests for aggregate in unsupported cases
            if (modelAggregate.isEmpty() && !model.isEmpty()) {
                throw new UnsupportedOperationException("Seems you've requested "
                        + "aggregate mode for graph that don't support it. We apologize...");
            }

            selectedModel = modelAggregate;
        } else {
            selectedModel = model;
        }

        graphPanel.getGraphObject().setRows(selectedModel);
        graphPanel.clearRowsTab();

        for (AbstractGraphRow abstractGraphRow : selectedModel.values()) {
            graphPanel.addRow(abstractGraphRow);
        }

        isAggregate = aggregate;
        getSettingsPanel().setAggregateMode(aggregate);
    }

    private void addRowToCompositeModels(String rowName, AbstractGraphRow row) {
        GuiPackage gui = GuiPackage.getInstance();
        if (gui == null) {
            log.debug("No GUI Package present, ignored adding to composite");
            return;
        }

        JMeterTreeModel testTree = gui.getTreeModel();

        for (JMeterTreeNode obj : testTree.getNodesOfType(CompositeResultCollector.class)) {
            CompositeResultCollector compositeResultCollector = (CompositeResultCollector) obj.getTestElement();
            compositeResultCollector.getCompositeModel().addRow(rowName, row);
        }
    }

    private void clearRowsFromCompositeModels(String vizualizerName) {
        GuiPackage gui = GuiPackage.getInstance();
        if (gui == null) {
            log.debug("No GUI Package present, ignored removing from composite");
            return;
        }
        JMeterTreeModel testTree = gui.getTreeModel();

        for (JMeterTreeNode obj : testTree.getNodesOfType(CompositeResultCollector.class)) {
            //System.out.println("obj");
            CompositeResultCollector compositeResultCollector = (CompositeResultCollector) obj.getTestElement();
            compositeResultCollector.getCompositeModel().clearRows(vizualizerName);
        }
    }

    protected synchronized AbstractGraphRow getNewRow(
            ConcurrentSkipListMap model,
            int rowType,
            String label,
            int markerSize,
            boolean isBarRow,
            boolean displayLabel,
            boolean thickLines,
            boolean showInLegend,
            Color color,
            boolean canCompose) {
        AbstractGraphRow row;
        if (log.isDebugEnabled()) {
            log.debug("This AbstractGraphPanelVisualizer is an instance of [" + this.getClass().getName() + "]");
        }
        if (!model.containsKey(label)) {
            row = AbstractGraphRow.instantiateNewRow(rowType);
            row.setLabel(label);
            row.setMarkerSize(markerSize);
            row.setDrawBar(isBarRow);
            row.setDrawLine(!isBarRow);
            row.setDrawValueLabel(displayLabel);
            row.setDrawThickLines(thickLines);
            row.setShowInLegend(showInLegend);

            Color overrideColor = null;
            if (this.labelToColorMapping != null)
                overrideColor = labelToColorMapping.getColorForLabel(row.getLabel());
            if (log.isDebugEnabled())
                if (row != null) {
                    log.debug("%#@ Found override color [" + (overrideColor == null ? "null" : overrideColor.toString()) + "]");
                    log.debug("%#@ for label [" + row.getLabel() + "] color in-parm [" + (color == null ? "null" : color.toString()) + "]");
                    log.debug("%#@ prev row.getColor() [" + (row.getColor() == null ? "null" : row.getColor().toString()) + "]");
                } else
                    log.debug("%#@ Found null row displayLabel[" + displayLabel + "] and label [" + label + "]");

            row.setColor(overrideColor != null ? overrideColor : colors.getNextColor());
            if (log.isDebugEnabled())
                log.debug("%#@ new row.getColor() [" + (row.getColor() == null ? "null" : row.getColor().toString()) + "]");

            model.put(label, row);
            graphPanel.addRow(row);
            if (canCompose) {
                addRowToCompositeModels(getModel().getName(), row);
            }
        } else {
            row = model.get(label);
        }

        return row;
    }

    protected synchronized AbstractGraphRow getNewRow(
            ConcurrentSkipListMap model,
            int rowType,
            String label,
            int markerSize,
            boolean isBarRow,
            boolean displayLabel,
            boolean thickLines,
            boolean showInLegend,
            boolean canCompose) {
        return getNewRow(model, rowType, label, markerSize, isBarRow, displayLabel, thickLines, showInLegend, null, canCompose);
    }

    protected boolean isFromTransactionControler(SampleResult res) {
        return res.getResponseMessage() != null && res.getResponseMessage().startsWith("Number of samples in transaction");
    }

    /**
     * @return the settingsPanel
     */
    public JSettingsPanel getSettingsPanel() {
        return settingsPanel;
    }

    private void setOptionsFromProperties(GraphPanelChart graph) {
        // moved this into jmeter part, because charting package knows nothing on JMEters
        //properties from user.properties
        String cfgDrawGradient = JMeterUtils.getProperty("jmeterPlugin.drawGradient");
        if (cfgDrawGradient != null) {
            graph.getChartSettings().setDrawGradient("true".equalsIgnoreCase(cfgDrawGradient.trim()));
        }
        String cfgNeverDrawFinalZeroingLines = JMeterUtils.getProperty("jmeterPlugin.neverDrawFinalZeroingLines");
        if (cfgNeverDrawFinalZeroingLines != null) {
            graph.getChartSettings().setConfigNeverDrawFinalZeroingLines("true".equalsIgnoreCase(cfgNeverDrawFinalZeroingLines.trim()));
        }

        String cfgOptimizeYAxis = JMeterUtils.getProperty("jmeterPlugin.optimizeYAxis");
        if (cfgOptimizeYAxis != null) {
            graph.getChartSettings().setConfigOptimizeYAxis("true".equalsIgnoreCase(cfgOptimizeYAxis.trim()));
        }

        String cfgNeverDrawFinalCurrentX = JMeterUtils.getProperty("jmeterPlugin.neverDrawCurrentX");
        if (cfgNeverDrawFinalCurrentX != null) {
            graph.getChartSettings().setConfigNeverDrawCurrentX("true".equalsIgnoreCase(cfgNeverDrawFinalCurrentX.trim()));
        }
        String cfgCsvSeparator = JMeterUtils.getProperty("jmeterPlugin.csvSeparator");
        if (cfgCsvSeparator != null) {
            graph.getChartSettings().setConfigCsvSeparator(cfgCsvSeparator);
        }
        String cfgUseRelativeTime = JMeterUtils.getProperty("jmeterPlugin.useRelativeTime");
        if (cfgUseRelativeTime != null) {
            graph.getChartSettings().setUseRelativeTime("true".equalsIgnoreCase(cfgUseRelativeTime.trim()));
        }
        String cfgGraphLineWitdh = JMeterUtils.getProperty("jmeterPlugin.graphLineWidth");
        if (cfgGraphLineWitdh != null) {
            graph.getChartSettings().setLineWidth(JMeterPluginsUtils.getFloatFromString(cfgGraphLineWitdh, 1.0f));
        }
        String cfgGraphLineMarker = JMeterUtils.getProperty("jmeterPlugin.drawLineMarker");
        if (cfgGraphLineMarker != null) {
            boolean removeMarkers = "false".equalsIgnoreCase(cfgGraphLineMarker.trim());
            if (removeMarkers) {
                graph.getChartSettings().setChartMarkers(ChartSettings.CHART_MARKERS_NO);
            }
        }
    }

    protected boolean isSampleIncluded(SampleResult res) {
        if (null == res) {
            return true;
        }

        if (startTimeRef == 0) {
            startTimeRef = res.getStartTime();
            startTimeInf = startTimeRef - startTimeRef % 1000;
            startTimeSup = startTimeRef + (1000 - startTimeRef % 1000) % 1000;
        }

        if (includeRegexChkboxState && !incRegex.isEmpty()
                && !res.getSampleLabel().matches(incRegex)) {
            return false;
        }

        if (excludeRegexChkboxState && !excRegex.isEmpty()
                && res.getSampleLabel().matches(excRegex)) {
            return false;
        }

        if (!includeRegexChkboxState && !includes.isEmpty()
                && !includes.contains(res.getSampleLabel())) {
            return false;
        }

        if (!excludeRegexChkboxState && !excludes.isEmpty()
                && excludes.contains(res.getSampleLabel())) {
            return false;
        }

        if (startOffset > res.getStartTime() - startTimeInf) {
            return false;
        }

        if (endOffset < res.getStartTime() - startTimeSup) {
            return false;
        }
        return true;
    }

    protected boolean isSampleIncluded(String sampleLabel) {
        if (includeRegexChkboxState && !incRegex.isEmpty()
                && !sampleLabel.matches(incRegex)) {
            return false;
        }

        if (excludeRegexChkboxState && !excRegex.isEmpty()
                && sampleLabel.matches(excRegex)) {
            return false;
        }

        if (!includeRegexChkboxState && !includes.isEmpty()
                && !includes.contains(sampleLabel)) {
            return false;
        }

        if (!excludeRegexChkboxState && !excludes.isEmpty()
                && excludes.contains(sampleLabel)) {
            return false;
        }
        return true;
    }

    public void setUpFiltering(CorrectedResultCollector rc) {
        startOffset = rc.getTimeDelimiter(
                CorrectedResultCollector.START_OFFSET, Long.MIN_VALUE);
        endOffset = rc.getTimeDelimiter(CorrectedResultCollector.END_OFFSET,
                Long.MAX_VALUE);
        includeRegexChkboxState = rc
                .getRegexChkboxState(CorrectedResultCollector.INCLUDE_REGEX_CHECKBOX_STATE);
        excludeRegexChkboxState = rc
                .getRegexChkboxState(CorrectedResultCollector.EXCLUDE_REGEX_CHECKBOX_STATE);
        if (includeRegexChkboxState)
            incRegex = rc
                    .getRegex(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS);
        else
            includes = rc
                    .getList(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS);
        if (excludeRegexChkboxState)
            excRegex = rc
                    .getRegex(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS);
        else
            excludes = rc
                    .getList(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS);
    }

    private class MaximizeAction implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            toogleMaximize();
            if (!maximized) {
                maximizeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/kg/apc/jmeter/img/maximize.png")));
                maximizeButton.setToolTipText("Maximize Panel");
            } else {
                maximizeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/kg/apc/jmeter/img/restore.png")));
                maximizeButton.setToolTipText("Restore Panel");
            }
        }

    }

    /**
     * Ignore current test start time(ie take it from reload test data)
     * this is needed for visualizer that don't use current tests data
     * but reload from file
     */
    public void setIgnoreCurrentTestStartTime() {
        this.ignoreCurrentTestStartTime = true;
    }

    /**
     * Ignore current test start time(ie take it from reload test data)
     * this is needed for visualizer that don't use current tests data
     * but reload from file
     *
     * @return the ignoreTestStartTime
     */
    public boolean isIgnoreCurrentTestStartTime() {
        return ignoreCurrentTestStartTime;
    }

    @Override
    public void clearGui() {
        super.clearGui();
        graphPanel.getRowSelectorPanel().clearGui();
    }

    private void addMouseClickColorChangeListener() {
        this.graphPanel.getGraphObject().addMouseListener(new MouseClickColorChangeListener());
    }

    class MouseClickColorChangeListener extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent e) {
            log.debug("mouse click [" + e.getX() + "," + e.getY() + "] ClickCount[" + e.getClickCount() + "]");
            if (e.getClickCount() == 2) {
                for (Entry row : model.entrySet()) {
                    AbstractGraphRow agr = row.getValue();
                    if (agr != null) {
                        Rectangle r = agr.getLegendColorBox();
                        if (r != null) {
                            Rectangle slightlyLargerTarget = r.getBounds();
                            slightlyLargerTarget.height += 4;
                            slightlyLargerTarget.width += 2;
                            boolean hit = slightlyLargerTarget.contains(e.getPoint());
                            log.debug("hit [" + hit + "] rectangle [" + slightlyLargerTarget.toString() + "] point [" + e.getPoint() + "]");
                            if (hit) {
                                row.getValue().setColor(colors.getNextColor());
                            }
                        }
                    }
                }
                updateGui(null);

            }
            //repaint();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy