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

cdc.perfs.ui.swing.ChartPanel Maven / Gradle / Ivy

The newest version!
package cdc.perfs.ui.swing;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.function.Function;

import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToolTip;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import cdc.perfs.api.Source;
import cdc.perfs.core.Context;
import cdc.perfs.core.Environment;
import cdc.perfs.core.EnvironmentKind;
import cdc.perfs.core.Measure;
import cdc.perfs.core.MeasureStatus;
import cdc.perfs.core.Position;
import cdc.perfs.core.SpanPosition;
import cdc.perfs.ui.PerfsChartHelper;
import cdc.perfs.ui.PerfsChartHelper.ChangeListener;
import cdc.perfs.ui.PerfsChartHelper.PickedData;
import cdc.perfs.ui.RefreshRate;
import cdc.perfs.ui.Rendering;
import cdc.perfs.ui.SourceDisplay;
import cdc.util.time.RefTime;
import cdc.util.time.TimeMeasureMode;
import cdc.util.time.TimeUnit;

/**
 * Panel dedicated to graphical display of measures.
 *
 * @author Damien Carbonne
 *
 */
final class ChartPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = LogManager.getLogger(ChartPanel.class);
    final transient DragParams dragParams = new DragParams();

    final Timer timer;
    private RefreshRate refreshRate = null;
    private Rendering rendering = null;
    private boolean displayStatsEnabled = false;
    boolean drawBordersEnabled = false;

    boolean mouseOver = false;
    private JToolTip wToolTip = null;
    /**
     * Action that is triggered on timer events for refresh.
     */
    private final transient ActionListener timerHandler;
    final ControlledChartPanel wControl;
    final ContextsTableModel contextsModel;
    final SourcesTableModel sourcesModel;
    final transient Helper helper;

    public ChartPanel(ControlledChartPanel control,
                      ContextsTableModel contextsModel,
                      SourcesTableModel sourcesModel) {
        super();
        this.helper = new Helper(contextsModel.getEnvironment().getKind(),
                                 sourcesModel::getDisplay);
        this.timerHandler = e -> {
            helper.computeParameters();
            repaint();
            if (mouseOver) {
                refreshToolTip(ChartPanel.this, e.getWhen());
            }
        };
        this.wControl = control;
        this.contextsModel = contextsModel;
        this.sourcesModel = sourcesModel;

        // Do this to receive key events
        setFocusable(true);

        if (contextsModel.getEnvironment().getKind() == EnvironmentKind.RUNTIME) {
            this.timer = new Timer(20, this.timerHandler);
        } else {
            this.timer = null;
            this.contextsModel.addTableModelListener(event -> repaintIfSnapshot());
            this.sourcesModel.addTableModelListener(event -> repaintIfSnapshot());
        }

        addMouseWheelListener(e -> {
            if (e.getWheelRotation() < 0) {
                wControl.zoomIn();
            } else {
                wControl.zoomOut();
            }
        });

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                dragParams.enabled = helper.isFocusLocked() && (e.getButton() == MouseEvent.BUTTON1);
                dragParams.x0 = e.getX();
                dragParams.px = dragParams.x0;
                if (e.isPopupTrigger()) {
                    showPopup(e.getComponent(),
                              e.getX(),
                              e.getY());
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    showPopup(e.getComponent(),
                              e.getX(),
                              e.getY());
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                mouseOver = true;
                // Do this to receive key events
                requestFocusInWindow();
            }

            @Override
            public void mouseExited(MouseEvent e) {
                mouseOver = false;
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                if (dragParams.enabled) {
                    final int dx = e.getX() - dragParams.px;
                    dragParams.px = e.getX();
                    setFocusNanos(helper.getFocusNanos() + helper.deltaXToDeltaTime(-dx));
                }
            }
        });

        addHierarchyListener(e -> {
            if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
                if (isShowing()) {
                    repaintIfSnapshot();
                    if (timer != null) {
                        timer.restart();
                    }
                } else {
                    if (timer != null) {
                        timer.stop();
                    }
                }
            }
        });

        addAncestorListener(new AncestorListener() {
            @Override
            public void ancestorRemoved(AncestorEvent event) {
                // Ignore
            }

            @Override
            public void ancestorMoved(AncestorEvent event) {
                // Called when chart is scrolled
                repaintIfSnapshot();
            }

            @Override
            public void ancestorAdded(AncestorEvent event) {
                // Ignore
            }
        });

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                if (e.getKeyChar() == ' ' && contextsModel.getEnvironment().getKind() == EnvironmentKind.RUNTIME) {
                    wControl.setFocusLocked(!isFocusLocked());
                }
            }

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                    wControl.less();
                } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                    wControl.more();
                } else if (e.getKeyCode() == KeyEvent.VK_PLUS || e.getKeyCode() == KeyEvent.VK_ADD || e.getKeyChar() == '+') {
                    wControl.zoomIn();
                } else if (e.getKeyCode() == KeyEvent.VK_LESS || e.getKeyCode() == KeyEvent.VK_SUBTRACT || e.getKeyChar() == '-') {
                    wControl.zoomOut();
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
                wControl.stop();
            }
        });

        setRefreshRate(RefreshRate.LOW);
        setRendering(Rendering.FASTEST);

        setPreferredSize(new Dimension(100, 1000));

        if (timer != null) {
            timer.start();
        } else {
            repaintIfSnapshot();
        }

        setToolTipText("TODO");
    }

    @Override
    public JToolTip createToolTip() {
        wToolTip = super.createToolTip();
        return wToolTip;
    }

    @Override
    public String getToolTipText(MouseEvent event) {
        final PickedData data = helper.pick(event.getX(), event.getY());
        return PerfsChartHelper.getToolTipTextHtml(data);
    }

    /**
     * Blends a color with a fixed color (black).
     * 

* For a Runtime environment, result equals input color.
* For Snapshot environment, result is different. * * @param kind The environment kind. * @param c The color to blend. * @return The blended color. */ static Color blend(EnvironmentKind kind, Color c) { if (kind == EnvironmentKind.RUNTIME) { return c; } else { final double w0 = 0.9; final double w1 = 1.0 - w0; final double r = w0 * c.getRed() + w1 * 0.0; final double g = w0 * c.getGreen() + w1 * 0.0; final double b = w0 * c.getBlue() + w1 * 0.0; return new Color((int) r, (int) g, (int) b, 255); } } private class Helper extends PerfsChartHelper { private final Function sourceDisplay; protected final Rectangle2D.Double rect = new Rectangle2D.Double(); protected final Line2D line = new Line2D.Double(); private final Color bgStatsColor; private final Color bgScaleColor; private final Color bgColor; private final Color aliveEvenBackgroundColor; private final Color deadEvenBackgroundColor; private final Color aliveOddBackgroundColor; private final Color deadOddBackgroundColor; private final Color fgColor; private final Color highlightedRunningColor; private final Color highlightedFrozenColor; private final Color highlightedErrorColor; private final Color normalRunningColor; private final Color normalFrozenColor; private final Color normalErrorColor; protected final Color borderColor; protected final Color synthesisColor; protected final Color infoColor; private final Color centerTimeColor; private final Color maxTimeColor; final Rectangle visibleRectangle = new Rectangle(); final Filter rootFilter = new Filter(1); Filter currentFilter = null; Helper(EnvironmentKind kind, Function sourceDisplay) { this.sourceDisplay = sourceDisplay; bgStatsColor = new Color(0.0f, 0.0f, 0.0f, 0.5f); bgScaleColor = blend(kind, Color.WHITE); bgColor = blend(kind, new Color(0.95f, 0.95f, 0.95f)); aliveEvenBackgroundColor = blend(kind, new Color(0.95f, 0.95f, 0.95f)); deadEvenBackgroundColor = blend(kind, new Color(0.95f, 0.85f, 0.85f)); aliveOddBackgroundColor = blend(kind, new Color(0.85f, 0.85f, 0.85f)); deadOddBackgroundColor = blend(kind, new Color(0.85f, 0.75f, 0.75f)); fgColor = blend(kind, new Color(0.6f, 0.6f, 0.6f)); highlightedRunningColor = blend(kind, new Color(0.6f, 1.0f, 0.2f)); highlightedFrozenColor = blend(kind, new Color(1.0f, 1.0f, 0.4f)); highlightedErrorColor = blend(kind, new Color(1.0f, 0.6f, 0.55f)); normalRunningColor = blend(kind, new Color(0.8f, 1.0f, 0.4f)); normalFrozenColor = blend(kind, new Color(1.0f, 1.0f, 0.8f)); normalErrorColor = blend(kind, new Color(1.0f, 0.8f, 0.75f)); borderColor = blend(kind, new Color(0.6f, 0.6f, 0.6f)); synthesisColor = blend(kind, Color.GRAY); infoColor = Color.BLACK; centerTimeColor = Color.GREEN; maxTimeColor = Color.RED; } @Override protected void retrieveWindowSize() { final Dimension d = ChartPanel.this.getSize(); width = d.width; height = d.height; if (width < 0.01) { width = 600.0; } } @Override protected void setPreferredHeight(int height) { if (getPreferredSize().getHeight() != height) { setPreferredSize(new Dimension(100, height)); revalidate(); } } @Override public Environment getEnvironment() { return contextsModel.getEnvironment(); } @Override protected void setToolTipTextHtml(String text) { ChartPanel.this.setToolTipText(text); } @Override protected List getContexts() { return contextsModel.getContexts(); } @Override protected boolean isVisible(Context context) { return contextsModel.isVisible(context); } @Override protected void redraw() { ChartPanel.this.repaint(); } /** * Draw time scale on top of visible part of the window. * * @param g2 The Graphic context to use. */ protected void drawScale(Graphics2D g2) { final int y0 = (int) visibleRectangle.getMinY(); // Background g2.setPaint(bgScaleColor); rect.setRect(0, y0, width, DY0); g2.fill(rect); // Line under the area line.setLine(0.0, y0 + DY0, timeRelNanosToX(timeSupRelNanos), y0 + DY0); g2.setPaint(fgColor); g2.draw(line); // Vertical time graduations g2.setPaint(fgColor); double x = Math.floor(timeInfRelNanos / timeStepNanos) * timeStepNanos; assert (x <= timeInfRelNanos); final Line2D l = new Line2D.Double(); for (; x <= timeSupRelNanos; x += timeStepNanos) { final double xp = timeRelNanosToX(x); l.setLine(xp, y0 + DY0 - 5.0, xp, y0 + DY0); g2.draw(l); g2.drawString(RefTime.nanosToString((long) x, timeStepUnit), (int) xp - shift(timeStepUnit), y0 + 10); } } /** * Draw background elements. * * @param g2 The Graphic context to use. */ protected void drawBackground(Graphics2D g2) { // Contexts background double y0 = DY0; int count = 0; for (final Context context : contextsModel.getContexts()) { if (contextsModel.isVisible(context)) { final double dy = context.getHeight() * (DY1 + DY2) + DY2; if (count % 2 == 0) { if (context.isAlive()) { g2.setPaint(aliveEvenBackgroundColor); } else { g2.setPaint(deadEvenBackgroundColor); } } else { if (context.isAlive()) { g2.setPaint(aliveOddBackgroundColor); } else { g2.setPaint(deadOddBackgroundColor); } } rect.setRect(0.0, y0, width, dy); g2.fill(rect); y0 += dy; count++; } } // Remaining space g2.setPaint(bgColor); rect.setRect(0, y0, width, height - DY0); g2.fill(rect); // Vertical time lines g2.setPaint(fgColor); double x = Math.floor(timeInfRelNanos / timeStepNanos) * timeStepNanos; assert (x <= timeInfRelNanos); for (; x <= timeSupRelNanos; x += timeStepNanos) { final double xp = timeRelNanosToX(x); line.setLine(xp, DY0, xp, y0); g2.draw(line); } // Elapsed time vertical line if (timeInfRelNanos <= elapsedNanos && elapsedNanos <= timeSupRelNanos) { g2.setPaint(maxTimeColor); final double xp = timeRelNanosToX(elapsedNanos); line.setLine(xp, DY0 - 5.0, xp, y0); g2.draw(line); } // Focus time vertical line if (timeInfRelNanos <= focusRelNanos && focusRelNanos <= timeSupRelNanos) { g2.setPaint(centerTimeColor); final double xp = timeRelNanosToX(focusRelNanos); line.setLine(xp, DY0 - 5.0, xp, y0); g2.draw(line); } } /** * Draw all visible contexts. * * @param g2 the graphical 2D context. */ protected void drawContexts(Graphics2D g2) { double y0 = DY0 + DY2; for (final Context context : contextsModel.getContexts()) { if (contextsModel.isVisible(context)) { drawContext(g2, context, y0); y0 += context.getHeight() * (DY1 + DY2); // Line under the context area line.setLine(0.0, y0, timeRelNanosToX(timeSupRelNanos), y0); g2.setPaint(fgColor); g2.draw(line); y0 += DY2; } } } class Filter extends AbstractFilter { private Graphics2D g2; public Filter(int depth) { super(depth); } @Override protected AbstractFilter create(int depth) { return new Filter(depth); } @Override protected void drawMeasure(Measure measure, SpanPosition position, double y) { Helper.this.drawMeasure(g2, measure, position, y); } @Override public Filter next() { return (Filter) super.next(); } @Override protected void drawSynthesis() { addDrawing(); setRectSafe(rect, x0, y, x1 - x0, DY1); // Fill rectangle g2.setPaint(synthesisColor); g2.fill(rect); // Draw border if (drawBordersEnabled) { g2.setPaint(borderColor); g2.draw(rect); } // Draw infos if (x1 - x0 > 100.0 && x0 <= getWidth() && x0 > -1000.0) { g2.setPaint(infoColor); g2.drawString(count + " measures", (int) x0 + 2, (int) (y + DY1 - 2.0)); } } public void init(Graphics2D g2, double y) { super.init(y); this.g2 = g2; } } /** * Draw one context. * * @param g2 the graphical 2D context. * @param context the context to draw. * @param y0 vertical position of the first measure to draw. */ private void drawContext(Graphics2D g2, Context context, double y0) { // Index of the first root measure to draw int index = context.getRootMeasureIndex((long) timeInfRelNanos, TimeMeasureMode.RELATIVE, Position.GREATER_OR_EQUAL); final int count = context.getRootMeasuresCount(); rootFilter.init(g2, y0); currentFilter = rootFilter; while (index >= 0 && index < count) { final Measure measure = context.getRootMeasure(index); final double t0 = measure.getRelativeBeginNanos(); final double t1 = measure.getRelativeEndOrCurrentNanos(); if (t0 <= timeSupRelNanos && t1 >= timeInfRelNanos) { final SpanPosition position = rootFilter.getPosition(measure); rootFilter.addMeasure(measure, position); index++; } else { // Measures are ordered: next measures are not visible index = -1; } } rootFilter.flush(); } protected void setRectSafe(Rectangle2D.Double r, double x, double y, double w, double h) { final double eps = 5.0; final double dx0 = (x <= -eps ? -eps - x : 0.0); final double dx1 = (x + w > width + eps ? x + w - width - eps : 0.0); r.setRect(x + dx0, y, w - dx0 - dx1, h); } /** * Recursively draw one measure and its children. * * @param g2 The graphical 2D context. * @param measure The measure to draw. * @param position The measure span position. * @param y Vertical position of the measure to draw. */ protected void drawMeasure(Graphics2D g2, Measure measure, SpanPosition position, double y) { addDrawing(); final SourceDisplay display = sourceDisplay.apply(measure.getSource()); if (display != SourceDisplay.HIDDEN) { final MeasureStatus status = measure.getStatus(); final double x0 = getX0(measure); final double x1 = getX1(measure); switch (status) { case FROZEN: if (display == SourceDisplay.NORMAL) { g2.setPaint(normalFrozenColor); } else { g2.setPaint(highlightedFrozenColor); } break; case FROZEN_ON_ERROR: if (display == SourceDisplay.NORMAL) { g2.setPaint(normalErrorColor); } else { g2.setPaint(highlightedErrorColor); } break; case RUNNING: if (display == SourceDisplay.NORMAL) { g2.setPaint(normalRunningColor); } else { g2.setPaint(highlightedRunningColor); } break; default: break; } final double dx = x1 - x0; setRectSafe(rect, x0, y, dx, DY1); // Fill rectangle g2.fill(rect); // Draw border if (drawBordersEnabled) { g2.setPaint(borderColor); g2.draw(rect); } // Draw information if (dx > 100.0 && x0 <= width && x1 >= 50.0) { g2.setPaint(infoColor); g2.drawString(measure.getLabel(), Math.max(0, (int) x0 + 2), (int) (y + DY1 - 2.0)); } } // Draw children if (measure.getFirstChild() != null) { final Filter mem = currentFilter; final Filter filter = currentFilter.next(); filter.init(g2, y + DY1 + DY2); currentFilter = filter; for (Measure child = measure.getFirstChild(); child != null; child = child.getNextSibling()) { final SpanPosition childPosition = position == SpanPosition.INSIDE ? SpanPosition.INSIDE : filter.getPosition(child); filter.addMeasure(child, childPosition); } filter.flush(); currentFilter = mem; } } protected void drawStats(Graphics2D g2) { if (getDisplayStatsEnabled()) { final boolean runtime = getEnvironment().getKind() == EnvironmentKind.RUNTIME; final int dx = 170; final int dy = 70 + (runtime ? 20 : 0); final int xmargin = 10; final int ymargin = 20 + (int) visibleRectangle.getMinY(); g2.setPaint(bgStatsColor); rect.setRect(width - dx - xmargin, ymargin, dx, dy); g2.fill(rect); g2.setPaint(Color.WHITE); g2.drawString(RefTime.nanosToString(getDrawingNanos()), (int) width - dx, ymargin + 20); g2.drawString(String.format("%,d items(s)", getDrawingCount()), (int) width - dx, ymargin + 40); g2.drawString(String.format("%,d measures(s)", getEnvironment().getMeasuresCount()), (int) width - dx, ymargin + 60); if (runtime) { g2.drawString(String.format("%03d ms", (int) RefTime.nanosToDouble(getTickNanos(), TimeUnit.MILLI)), (int) width - dx, ymargin + 80); } } } } @Override public void paint(Graphics g) { helper.startDrawing(); helper.visibleRectangle.setBounds(getVisibleRect()); super.paint(g); final Graphics2D g2 = (Graphics2D) g; switch (getRendering()) { case BEST: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); break; case FASTEST: default: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); break; } helper.computeParameters(); helper.drawBackground(g2); helper.drawContexts(g2); helper.drawScale(g2); helper.endDrawing(); helper.drawStats(g2); } protected static class DragParams { boolean enabled; /** Current x0. */ int x0; /** Previous x0. */ int px; } private void refreshToolTip(JComponent component, long time) { if (wToolTip != null && wToolTip.isShowing()) { final PointerInfo info = MouseInfo.getPointerInfo(); if (info == null) { LOGGER.warn("Can not retrieve pointer info"); } else { final Point absLocation = info.getLocation(); final Point relLocation = new Point(absLocation); SwingUtilities.convertPointFromScreen(relLocation, component); if (component.contains(relLocation)) { final MouseEvent event = new MouseEvent(component, MouseEvent.MOUSE_MOVED, time, 0, relLocation.x, relLocation.y, absLocation.x, absLocation.y, 0, false, 0); java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event); } } } } void showPopup(Component component, int x, int y) { final PointerInfo info = MouseInfo.getPointerInfo(); if (info == null) { LOGGER.warn("Can not retrieve pointer info"); } else { if (isFocusLocked()) { final Point absLocation = info.getLocation(); final Point relLocation = new Point(absLocation); SwingUtilities.convertPointFromScreen(relLocation, component); final PerfsChartHelper.PickedData data = helper.pick(relLocation.x, relLocation.y); if (data.isMeasure()) { final JPopupMenu wMenu = new JPopupMenu(); final JMenu wDisplay = new JMenu("Set " + data.measure.getSource() + " display to"); wMenu.add(wDisplay); final SourceDisplay currentDisplay = sourcesModel.getDisplay(data.measure.getSource()); for (final SourceDisplay display : SourceDisplay.values()) { final JMenuItem wItem = new JMenuItem(display.getLabel()); wItem.addActionListener(e -> sourcesModel.setDisplay(data.measure.getSource(), display)); wDisplay.add(wItem); if (currentDisplay == display) { wItem.setEnabled(false); } } wMenu.show(component, x, y); } else if (data.isContext()) { final JPopupMenu wMenu = new JPopupMenu(); final JMenuItem wUnlock = new JMenuItem("Unlock"); wUnlock.addActionListener(e -> wControl.setFocusLocked(false)); if (contextsModel.getEnvironment().getKind() == EnvironmentKind.SNAPSHOT) { wUnlock.setEnabled(false); } wMenu.add(wUnlock); final JMenuItem wAllNormal = new JMenuItem("Display all sources as Normal"); wAllNormal.addActionListener(e -> sourcesModel.setDisplay(SourceDisplay.NORMAL)); wMenu.add(wAllNormal); final JMenuItem wShowAllContexts = new JMenuItem("Show all contexts"); wShowAllContexts.addActionListener(e -> contextsModel.setVisible(true)); wMenu.add(wShowAllContexts); final JMenuItem wHideAllContexts = new JMenuItem("Hide all contexts"); wHideAllContexts.addActionListener(e -> contextsModel.setVisible(false)); wMenu.add(wHideAllContexts); final JMenuItem wHideContext = new JMenuItem("Hide " + data.context.getName()); wHideContext.addActionListener(e -> contextsModel.setVisible(data.context, false)); wMenu.add(wHideContext); wMenu.show(component, x, y); } else { final JPopupMenu wMenu = new JPopupMenu(); final JMenuItem wAllNormal = new JMenuItem("Display all sources as Normal"); wAllNormal.addActionListener(e -> sourcesModel.setDisplay(SourceDisplay.NORMAL)); wMenu.add(wAllNormal); final JMenuItem wShowAllContexts = new JMenuItem("Show all contexts"); wShowAllContexts.addActionListener(e -> contextsModel.setVisible(true)); wMenu.add(wShowAllContexts); final JMenuItem wHideAllContexts = new JMenuItem("Hide all contexts"); wHideAllContexts.addActionListener(e -> contextsModel.setVisible(false)); wMenu.add(wHideAllContexts); wMenu.show(component, x, y); } } else { final JPopupMenu wMenu = new JPopupMenu(); final JMenuItem wLock = new JMenuItem("Lock"); wLock.addActionListener(e -> wControl.setFocusLocked(true)); if (contextsModel.getEnvironment().getKind() == EnvironmentKind.SNAPSHOT) { wLock.setEnabled(false); } wMenu.add(wLock); wMenu.show(component, x, y); } } } public Environment getEnvironment() { return contextsModel.getEnvironment(); } List getContexts() { return contextsModel.getContexts(); } boolean isVisible(Context context) { return contextsModel.isVisible(context); } public final void addChangeListener(ChangeListener listener) { helper.addChangeListener(listener); } public final void removeChangeListener(ChangeListener listener) { helper.removeChangeListener(listener); } public void setFocusLocked(boolean locked) { helper.setFocusLocked(locked); repaint(); } public boolean isFocusLocked() { return helper.isFocusLocked(); } public void setScale(double scale) { helper.setScale(scale); } public double getScale() { return helper.getScale(); } public void setFocusNanos(double time) { helper.setFocusNanos(time); } public double getFocusNanos() { return helper.getFocusNanos(); } public void incrementFocus(long nanosSinceFirstChange) { helper.incrementFocus(nanosSinceFirstChange); } public void decrementFocus(long nanosSinceFirstChange) { helper.decrementFocus(nanosSinceFirstChange); } public void setRefreshRate(RefreshRate rate) { this.refreshRate = rate; if (this.timer != null) { this.timer.setDelay(rate.getDelay()); } helper.fireChange(); repaintIfSnapshot(); } public RefreshRate getRefreshRate() { return refreshRate; } public void setRendering(Rendering rendering) { this.rendering = rendering; helper.fireChange(); repaintIfSnapshot(); } public Rendering getRendering() { return rendering; } public void setDisplayStatsEnabled(boolean enabled) { this.displayStatsEnabled = enabled; helper.fireChange(); repaintIfSnapshot(); } public boolean getDisplayStatsEnabled() { return displayStatsEnabled; } public void setDrawBordersEnabled(boolean enabled) { this.drawBordersEnabled = enabled; helper.fireChange(); repaintIfSnapshot(); } public boolean getDrawBordersEnabled() { return drawBordersEnabled; } void repaintIfSnapshot() { if (getEnvironment().getKind() == EnvironmentKind.SNAPSHOT) { repaintIfShowing(); } } void repaintIfShowing() { if (isShowing()) { helper.computeParameters(); helper.redraw(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy