/* * VectorPanel.java * (FScape) * * Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de * * * Changelog: * 05-Mar-05 created */ package de.sciss.fscape.gui; import de.sciss.gui.Axis; import de.sciss.gui.VectorSpace; import javax.swing.*; import javax.swing.event.MouseInputAdapter; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; /** * GUI container with a VectorDisplay, * axis, and mouse cross-hair information features. */ public class VectorPanel extends JPanel implements TopPainter { public static final int FLAG_HLOG_GADGET = 0x01; public static final int FLAG_VLOG_GADGET = 0x02; public static final int FLAG_UPDATE_GADGET = 0x04; private static final int GADGET_MASK = 0x07; private final VectorPanel.Client client; private VectorSpace space = null; // GUI components private final VectorDisplay ggVectorDisplay; private final Axis hAxis, vAxis; private JCheckBox ggHLog = null; private JCheckBox ggVLog = null; // mouse listening private FontMetrics fntMetrics; private static final Color colrCross = new Color(0x00, 0x00, 0x00, 0x7F); private static final Color colrTextBg = new Color(0xFF, 0xFF, 0xFF, 0xA0); private final Cursor csrCrossHair = new Cursor(Cursor.CROSSHAIR_CURSOR); private String lastTxt; private boolean paintCrossHair = false; private Point lastPt = null; // X-Hair public VectorPanel(VectorPanel.Client client, int flags) { super(new BorderLayout()); final Color c1 = getForeground(); final Color c2 = getBackground(); final boolean isDark = UIManager.getBoolean("dark-skin"); JPanel buttonPane; JButton ggUpdate; Box box; ActionListener actionListener; MouseInputAdapter mouseListener; this.client = client; // ---- create vector display ---- ggVectorDisplay = new VectorDisplay(isDark); ggVectorDisplay.addTopPainter(this); ggVectorDisplay.setCursor(csrCrossHair); mouseListener = new MouseInputAdapter() { public void mousePressed(MouseEvent e) { ggVectorDisplay.requestFocus(); redrawCrossHair(e); } public void mouseDragged(MouseEvent e) { redrawCrossHair(e); } }; ggVectorDisplay.addMouseListener(mouseListener); ggVectorDisplay.addMouseMotionListener(mouseListener); this.add(ggVectorDisplay, BorderLayout.CENTER); // ---- create south button panel gadgets ---- if( (flags & GADGET_MASK) != 0 ) { buttonPane = new JPanel( new FlowLayout( FlowLayout.LEADING, 4, 1 )); actionListener = new ActionListener() { public void actionPerformed( ActionEvent e ) { requestUpdate(); } }; if( (flags & FLAG_UPDATE_GADGET) != 0 ) { ggUpdate = new JButton("Update"); ggUpdate.addActionListener( actionListener ); buttonPane.add( ggUpdate ); } if( (flags & FLAG_HLOG_GADGET) != 0 ) { ggHLog = new JCheckBox("Horiz.log."); ggHLog.addActionListener( actionListener ); buttonPane.add( ggHLog ); } if( (flags & FLAG_VLOG_GADGET) != 0 ) { ggVLog = new JCheckBox("Vert.log."); ggVLog.addActionListener( actionListener ); buttonPane.add( ggVLog ); } this.add( buttonPane, BorderLayout.SOUTH ); } // ---- create axis gadgets ---- hAxis = new Axis(Axis.HORIZONTAL); vAxis = new Axis(Axis.VERTICAL ); box = Box.createHorizontalBox(); box.add(Box.createHorizontalStrut(vAxis.getPreferredSize().width)); box.add(hAxis); this.add(box , BorderLayout.NORTH); this.add(vAxis, BorderLayout.WEST ); fntMetrics = getFontMetrics(getFont()); this.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { if (e.getPropertyName().equals("font")) { fntMetrics = getFontMetrics((Font) e.getNewValue()); } } }); } protected void redrawCrossHair(MouseEvent e) { Dimension dim = ggVectorDisplay.getSize(); int x = e.getX(); int y = e.getY(); String xTxt, yTxt; int dataLen; double dx, dy; float[] v; boolean hLog = ggHLog != null && ggHLog.isSelected(); boolean vLog = ggVLog != null && ggVLog.isSelected(); paintCrossHair = false; if ((space != null) && !e.isAltDown()) { v = ggVectorDisplay.getVector(); dataLen = v.length; if( dataLen < 1 ) return; if ((x >= 0) && (y >= 0) && (x < dim.width) && (y < dim.height)) { if (e.isShiftDown()) { dy = space.vUnityToSpace(1.0 - (double) y / (dim.height - 1)); dx = space.hUnityToSpace((double) x / (dim.width - 1)); } else { x = (int) ((double) x / (dim.width - 1) * (dataLen - 1) + 0.5); dy = v[x]; dx = space.hUnityToSpace((double) x / (dataLen - 1)); y = (int) ((1.0 - space.vSpaceToUnity(dy)) * (dim.height - 1) + 0.5); x = e.getX(); // (int) (rec.space.hSpaceToUnity( dx ) * (dim.width - 1) + 0.5); } lastPt = new Point(x, y); yTxt = client.formatVText(dy, vLog); xTxt = client.formatHText(dx, hLog); lastTxt = yTxt + " @ " + xTxt; paintCrossHair = true; } } ggVectorDisplay.repaint(); } protected void requestUpdate() { boolean hLog = ggHLog != null && ggHLog.isSelected(); boolean vLog = ggVLog != null && ggVLog.isSelected(); client.requestUpdate(hLog, vLog); } public void setVector(float[] data) { ggVectorDisplay.setVector(this, data); } public void setSpace(VectorSpace space) { this.space = space; ggVectorDisplay.setMinMax((float) space.vmin, (float) space.vmax); hAxis.setSpace(space); vAxis.setSpace(space); } // -------- TopPainter methods -------- public void paintOnTop(Graphics2D g) { Dimension dim = ggVectorDisplay.getSize(); if (paintCrossHair) { g.setColor(colrCross); g.drawLine(0, lastPt.y, dim.width - 1, lastPt.y); g.drawLine(lastPt.x, 0, lastPt.x, dim.height - 1); g.setColor(colrTextBg); g.fillRect(1, 1, fntMetrics.stringWidth(lastTxt) + 6, fntMetrics.getHeight() + 4); g.setColor(Color.blue); g.drawString(lastTxt, 4, fntMetrics.getHeight() + 1); } } // -------- internal client class -------- public interface Client { public void requestUpdate(boolean hLog, boolean vLog); public String formatVText(double vValue, boolean vLog); public String formatHText(double hValue, boolean hLog); } }