/* * Copyright (c) 2005, Graph Builder * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of Graph Builder nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import javax.swing.*; import java.awt.*; import java.awt.font.*; import java.awt.event.*; import java.awt.geom.*; import java.util.Vector; import com.graphbuilder.curve.*; public class UnsortedValueVectorPanel extends JPanel { public static final int VALUES_ABOVE_BAR = 0; public static final int VALUES_BELOW_BAR = 1; private ValueVector valueVector = null; private Vector vvlVec = new Vector(); private boolean showValues = true; private int numDigits = 3; private double min = 0; private double max = 1.0; private Shape knobShape = new Rectangle(0, 0, 7, 7); private int knobWidth = 7; private int knobHeight = 7; private Rectangle2D rect = new Rectangle2D.Double(0, 0, 0, 0); private boolean find = true; private int mousex = 0; private int mousey = 0; private int current = 0; private int offsety = 0; private boolean disabled = true; private Color barColor = Color.black; private Color knobDrawColor = Color.black; private Color knobFillColor = Color.blue; private Color barColorDisabled = Color.gray; private Color knobDrawColorDisabled = Color.gray; private Color knobFillColorDisabled = Color.gray; private Color valueTextColorDisabled = Color.gray; private int barValueGap = 5; private int valuePosition = VALUES_ABOVE_BAR; private Color valueTextColor = Color.black; private int top = 0; private int bottom = 5; private int left = 15; private int right = 15; private boolean doubleClick = false; private int currentValueIndex = -1; private boolean canInsertAndRemove = false; public UnsortedValueVectorPanel(boolean canInsertAndRemove) { Control ctrl = new Control(); addMouseListener(ctrl); addMouseMotionListener(ctrl); this.canInsertAndRemove = canInsertAndRemove; } private class Control implements MouseListener, MouseMotionListener{ public Control() { } public void mousePressed(MouseEvent evt) { if (disabled) return; mousex = evt.getX(); mousey = evt.getY(); doubleClick = false; if (canInsertAndRemove && evt.getClickCount() >= 2) doubleClick = true; repaint(); } public void mouseReleased(MouseEvent evt) { if (disabled) return; mousex = Integer.MIN_VALUE; mousey = Integer.MIN_VALUE; find = true; repaint(); } public void mouseDragged(MouseEvent evt) { if (disabled) return; mousex = evt.getX(); mousey = evt.getY(); repaint(); } public void mouseClicked(MouseEvent evt) {} public void mouseEntered(MouseEvent evt) {} public void mouseExited(MouseEvent evt) {} public void mouseMoved(MouseEvent evt) {} } public Shape getKnobShape() { return knobShape; } public void setKnobShape(Shape s) { if (s == null) throw new IllegalArgumentException(); Rectangle2D r = s.getBounds2D(); knobWidth = (int) Math.round(r.getWidth()); knobHeight = (int) Math.round(r.getHeight()); } public void setInsets(int top, int left, int bottom, int right) { this.top = top; this.left = left; this.bottom = bottom; this.right = right; } public double getMin() { return min; } public double getMax() { return max; } public void setRange(double min, double max) { if (min >= max) throw new IllegalArgumentException("Required: min < max"); this.min = min; this.max = max; } public void setShowValues(boolean b) { showValues = b; } public boolean getShowValues() { return showValues; } public void setValuePosition(int p) { if (p != VALUES_ABOVE_BAR && p != VALUES_BELOW_BAR) throw new IllegalArgumentException("Unknown value position."); valuePosition = p; } private void setValue(int i, double val) { if (val < min) val = min; if (val > max) val = max; double oldValue = valueVector.get(i); if (oldValue != val) { valueVector.set(val, i); fireValueChanged(valueVector, i, oldValue); } } public int getValuePosition() { return valuePosition; } public void setNumDigits(int n) { numDigits = n; } public int getNumDigits() { return numDigits; } public ValueVector getValueVector() { return valueVector; } public void setValueVector(ValueVector kv) { valueVector = kv; } public Color getKnobFillColor() { return knobFillColor; } public void setKnobFillColor(Color c) { if (c == null) throw new IllegalArgumentException("Knob fill color cannot be null."); knobFillColor = c; } public void setDisabled(boolean b) { disabled = b; } public boolean getDisabled() { return disabled; } public void paint(Graphics _g) { super.paint(_g); Graphics2D g = (Graphics2D) _g; int w = getWidth(); int h = getHeight(); if (valueVector == null) { String msg = "<No Value-Vector Data Available>"; TextLayout t = new TextLayout(msg, g.getFont(), g.getFontRenderContext()); int th = (int) Math.round(t.getAscent() + t.getDescent()); int tw = (int) Math.round(t.getAdvance()); int x = (w - tw) / 2; int y = (h - th) / 2 + th; g.drawString(msg, x, y); return; } int nw = w - (left + right); int nh = h - (bottom + top); int y = top; int ty = 0; int n = valueVector.size(); if (showValues) { String zero = "0"; TextLayout t = new TextLayout(zero, g.getFont(), g.getFontRenderContext()); int ascent = (int) (int) Math.round(t.getAscent()); int th = ascent + barValueGap; if (valuePosition == VALUES_ABOVE_BAR) { ty = top + ascent; y += th; nh = nh - th; } else if (valuePosition == VALUES_BELOW_BAR) { ty = top + nh - 1; nh = nh - th; } } if (disabled) g.setColor(barColorDisabled); else g.setColor(barColor); g.drawRect(left, y, nw - 1, nh); double xgap = 0; if (n != 1) xgap = 1.0 * nw / (n - 1); for (int i = 0; i < n; i++) { int x = (int) Math.round(xgap * i + left); String s = String.valueOf(valueVector.get(i)); int j = numDigits + 1; // assumes decimal place if (s.charAt(0) == '-') j++; if (j < s.length()) s = s.substring(0, j); TextLayout t = new TextLayout(s, g.getFont(), g.getFontRenderContext()); int tw = (int) Math.round(t.getAdvance()); if (disabled) g.setColor(valueTextColorDisabled); else g.setColor(valueTextColor); g.drawString(s, x - tw / 2, ty); if (i != n - 1) g.drawLine(x,y,x,y+nh); int ky = y + (int) Math.round((1.0 - ((valueVector.get(i) - min) / (max - min))) * nh) - knobHeight / 2; int kx = x - knobWidth / 2; if (ky < y - knobHeight / 2) ky = y - knobHeight / 2; if (ky > y + nh - knobHeight / 2) ky = y + nh - knobHeight / 2; if (i == n - 1) kx--; g.translate(kx, ky); if (find) { rect.setRect(kx, ky, knobWidth, knobHeight); if (rect.contains(mousex, mousey)) { find = false; current = i; offsety = (ky + knobHeight / 2) - mousey; if (doubleClick) { doubleClick = false; double value = valueVector.get(i); valueVector.remove(i); fireValueRemoved(valueVector, i, value); current = -1; n--; } } } else if (current != -1) { setValue(current, (1.0 - 1.0 * (mousey - y + offsety) / nh) * (max - min) + min); } if (disabled) g.setColor(knobFillColorDisabled); else g.setColor(knobFillColor); g.fill(knobShape); if (disabled) g.setColor(knobDrawColorDisabled); else g.setColor(knobDrawColor); g.draw(knobShape); g.translate(-kx, -ky); } if (doubleClick) { doubleClick = false; int k = 0; double val = min; if (mousex < left) {} else if (mousex > left + nw) { k = n; val = max; } else { k = (int) ((1.0 * mousex - left) / xgap) + 1; if (k > 0 && k < valueVector.size()) val = (valueVector.get(k) + valueVector.get(k-1)) / 2.0; else { if (k < 0) k = 0; else if (k > valueVector.size()) k = valueVector.size(); } } valueVector.insert(val, k); fireValueInserted(valueVector, k, val); } } private void fireValueInserted(ValueVector v, int index, double value) { for (int i = 0; i < getNumValueVectorListeners(); i++) getValueVectorListener(i).valueInserted(v, index, value); } private void fireValueRemoved(ValueVector v, int index, double value) { for (int i = 0; i < getNumValueVectorListeners(); i++) getValueVectorListener(i).valueRemoved(v, index, value); } private void fireValueChanged(ValueVector v, int index, double oldValue) { for (int i = 0; i < getNumValueVectorListeners(); i++) getValueVectorListener(i).valueChanged(v, index, oldValue); } public void addValueVectorListener(ValueVectorListener vvl) { if (vvl == null) throw new IllegalArgumentException("cannot add null listener."); vvlVec.add(vvl); } public void removeValueVectorListener(ValueVectorListener vvl) { vvlVec.remove(vvl); } public ValueVectorListener getValueVectorListener(int i) { return (ValueVectorListener) vvlVec.get(i); } public int getNumValueVectorListeners() { return vvlVec.size(); } public static void main(String[] args) { JPanel panel = new JPanel(new BorderLayout()); UnsortedValueVectorPanel ukvp = new UnsortedValueVectorPanel(true); ukvp.setValueVector(new ValueVector()); panel.add(ukvp, BorderLayout.CENTER); JFrame f = new JFrame(); f.setSize(400, 400); f.setContentPane(panel); f.show(); } }