/* * DKnob2.java * (c) 2005 by Joakim Eriksson * * DKnob is a component similar to JSlider but with * round "user interface", a knob. */ package com.dreamfabric.gui; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; import javax.swing.event.*; public class DKnob2 extends JComponent { private static final long serialVersionUID = -3944978568068155143L; private final static float START = 225; private final static float LENGTH = 270; private final static float PI = (float) 3.1415; private final static float START_ANG = (START/360)*PI*2; private final static float LENGTH_ANG = (LENGTH/360)*PI*2; private final static float MULTIP = 180 / PI; private final static Color DEFAULT_FOCUS_COLOR = new Color(0x8080ff); public static final Color DARK = new Color(0x40, 0x40, 0x40, 0xe0); public static final Color DARK_T = new Color(0x40, 0x40, 0x40, 0x80); public static final Color DARK_L = new Color(0x80, 0x80, 0x80, 0xa0); public static final Color LIGHT_D = new Color(0xa0, 0xa0, 0xa0, 0xa0); public static final Color LIGHT = new Color(0xc0, 0xc0, 0xc0, 0xa0); public static final Color LIGHT_T = new Color(0xc0, 0xc0, 0xc0, 0x80); private float DRAG_SPEED; private float CLICK_SPEED; private int size; private int middle; private String label = ""; private String type = ""; public final static int SIMPLE = 1; public final static int ROUND = 2; private int dragType = ROUND; private Polygon knob = new Polygon(); private final static Dimension MIN_SIZE = new Dimension(30, 40); private final static Dimension PREF_SIZE = new Dimension(40, 60); // Set the antialiasing to get the right look! private final static RenderingHints AALIAS = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); private ChangeEvent changeEvent = null; private EventListenerList listenerList = new EventListenerList(); private Arc2D hitArc = new Arc2D.Float(Arc2D.PIE); private float ang = (float) START_ANG; private float val; private int lowInt = 0; private int highInt = 100; private int medInt = 0; private int divisor = 0; private int dragpos = -1; private float startVal; private Color focusColor; private double lastAng; public DKnob2(String label) { this(label, ""); } public DKnob2(String label, String type) { DRAG_SPEED = 0.01F; CLICK_SPEED = 0.01F; this.label = label; this.type = type; for (int i = 0, n = 120; i < n; i++) { int y = 20 + (int) (0.5 + (Math.sin(i * 3.141592 / 4) * 1.5 + 20) * Math.sin(i * 3.141592 * 2.0 / n)); int x = 20 + (int) (0.5 + (Math.sin(i * 3.141592 / 4) * 1.5 + 20) * Math.cos(i * 3.141592 * 2.0 / n)); knob.addPoint(x, y); } focusColor = DEFAULT_FOCUS_COLOR; setPreferredSize(PREF_SIZE); setFocusable(true); hitArc.setAngleStart(235); // Degrees ??? Radians??? addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me) { dragpos = me.getX() + me.getY(); startVal = val; // Fix last angle int xpos = middle - me.getX(); int ypos = middle - me.getY(); lastAng = Math.atan2(xpos, ypos); requestFocus(); } public void mouseClicked(MouseEvent me) { hitArc.setAngleExtent(-(LENGTH + 20)); if (hitArc.contains(me.getX(), me.getY())) { hitArc.setAngleExtent(MULTIP * (ang-START_ANG)-10); if (hitArc.contains(me.getX(), me.getY())) { decValue(); } else incValue(); } } }); // Let the user control the knob with the mouse addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent me) { if ( dragType == SIMPLE) { float f = DRAG_SPEED * (float) ((me.getX() + me.getY()) - dragpos); setValue(startVal + f); } else if ( dragType == ROUND) { // Measure relative the middle of the button! int xpos = middle - me.getX(); int ypos = middle - me.getY(); double ang = Math.atan2(xpos, ypos); double diff = lastAng - ang; setValue((float) (getValue() + (diff / LENGTH_ANG))); lastAng = ang; } } public void mouseMoved(MouseEvent me) { } }); // Let the user control the knob with the keyboard addKeyListener(new KeyListener() { public void keyTyped(KeyEvent e) {} public void keyReleased(KeyEvent e) {} public void keyPressed(KeyEvent e) { int k = e.getKeyCode(); if (k == KeyEvent.VK_RIGHT) { incValue(); } else if (k == KeyEvent.VK_LEFT) { decValue(); } } }); // Handle focus so that the knob gets the correct focus highlighting. addFocusListener(new FocusListener() { public void focusGained(FocusEvent e) { repaint(); } public void focusLost(FocusEvent e) { repaint(); } }); } public void setDragType(int type) { dragType = type; } public int getDragType() { return dragType; } private void incValue() { setValue(val + CLICK_SPEED); } private void decValue() { setValue(val - CLICK_SPEED); } public float getValue() { return val; } public int getIntValue() { if (medInt > 0) { if (val > 0.5) { return medInt + (int) (2 * (val - 0.5) * (highInt - medInt)); } return lowInt + (int) (2 * val * (medInt - lowInt)); } return lowInt + (int) (val * (highInt - lowInt)); } public void setIntValue(int val) { if (medInt > 0) { if (val > medInt) { setValue(0.5f + 0.5f * (val - medInt) / (highInt - medInt)); } else { setValue(0.5f * (val - lowInt) / (medInt - lowInt)); } } else { setValue(1.0f * (val - lowInt) / (highInt - lowInt)); } } public void setValue(float val) { if (val < 0) val = 0; if (val > 1) val = 1; this.val = val; ang = START_ANG - (float) LENGTH_ANG * val; repaint(); fireChangeEvent(); } public void addChangeListener(ChangeListener cl) { listenerList.add(ChangeListener.class, cl); } public void removeChangeListener(ChangeListener cl) { listenerList.remove(ChangeListener.class, cl); } public Dimension getMinimumSize() { return MIN_SIZE; } protected void fireChangeEvent() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i] == ChangeListener.class) { // Lazily create the event: if (changeEvent == null) changeEvent = new ChangeEvent(this); ((ChangeListener)listeners[i+1]).stateChanged(changeEvent); } } } // Paint the DKnob public void paint(Graphics g) { if (g instanceof Graphics2D) { Graphics2D g2d = (Graphics2D) g; int width = getWidth(); int height = getHeight() - 10; size = Math.min(width, height) - 5; middle = size/2; g2d.setBackground(getParent().getBackground()); g2d.addRenderingHints(AALIAS); // For the size of the "mouse click" area hitArc.setFrame(1, 1, size + 12, size + 12); g2d.scale(size / 30.0, size / 30.0); drawKnob(g2d); } } private void drawKnob(Graphics2D g2d) { g2d.scale(0.5,0.5); g2d.drawString(label, 2, 10); g2d.scale(2,2); g2d.translate(0,6); if (hasFocus()) { g2d.setColor(focusColor); g2d.fill(new Arc2D.Double(0.0, 0.0, 27.0, 27.0, 90.0, 360.0, Arc2D.OPEN)); } g2d.setColor(DARK); g2d.fill(new Arc2D.Double(1.0, 1.0, 25.0, 25.0, 90.0, 360.0, Arc2D.OPEN)); g2d.setColor(DARK_L); g2d.fill(new Arc2D.Double(2.0, 2.0, 22.0, 22.0, 90.0, 360.0, Arc2D.OPEN)); g2d.setColor(LIGHT_D); g2d.draw(new Arc2D.Double(3.0, 3.0, 21.0, 21.0, 75.0, 120.0, Arc2D.OPEN)); g2d.setColor(LIGHT); g2d.draw(new Arc2D.Double(4.0, 4.0, 20.0, 20.0, 122.5, 25.0, Arc2D.OPEN)); g2d.setColor(DARK); g2d.fill(new Arc2D.Double(5.0, 5.0, 18.0, 18.0, 90.0, 360.0, Arc2D.OPEN)); g2d.setColor(DARK_T); g2d.fill(new Arc2D.Double(9.0, 9.0, 18.0, 18.0, 90.0, 360.0, Arc2D.OPEN)); g2d.setColor(DARK_L); g2d.fill(new Arc2D.Double(6.0, 6.0, 16.0, 16.0, 90.0, 360.0, Arc2D.OPEN)); g2d.setColor(DARK_T); g2d.draw(new Arc2D.Double(7.0, 7.0, 14.0, 14.0, 270.0, 90.0, Arc2D.OPEN)); g2d.setColor(LIGHT_D); g2d.draw(new Arc2D.Double(7.0, 7.0, 15.0, 15.0, 90.0, 90.0, Arc2D.OPEN)); g2d.setColor(LIGHT_D); g2d.fill(new Arc2D.Double(7.0, 7.0, 14.0, 14.0, 180 * ang / 3.141592 - 60, 120.0, Arc2D.OPEN)); g2d.setColor(DARK); g2d.draw(new Arc2D.Double(6.0, 6.0, 16.0, 16.0, 180 * ang / 3.141592 - 1, 2.0, Arc2D.PIE)); g2d.setColor(DARK); g2d.draw(new Arc2D.Double(5.0, 5.0, 18.0, 18.0, 180 * ang / 3.141592 - 8, 16.0, Arc2D.OPEN)); g2d.draw(new Arc2D.Double(4.0, 4.0, 20.0, 20.0, 180 * ang / 3.141592 - 4, 8.0, Arc2D.OPEN)); g2d.scale(0.5,0.5); if (divisor != 0) { g2d.drawString("" + (getIntValue() * 1.0 / divisor) + " " + type, 10, 64); } else { g2d.drawString("" + getIntValue() + " " + type, 10, 64); } } public void setInterval(int low, int high) { this.lowInt = low; this.highInt = high; } public void setInterval(int low, int med, int high) { this.lowInt = low; this.highInt = high; this.medInt = med; } // Just used for printing the label... public void setDivisor(int divisor) { this.divisor = divisor; } public static void main(String[] args) { JFrame win = new JFrame("DTest!"); win.getContentPane().setLayout(new BorderLayout()); win.setSize(120,140); JPanel volumePanel = new JPanel(new BorderLayout()); volumePanel.setBackground(new Color(180,180,195)); win.getContentPane().add(volumePanel, BorderLayout.CENTER); DKnob2 ts; volumePanel.add(ts = new DKnob2("Volume", ""), BorderLayout.CENTER); ts.setInterval(0,15); win.setVisible(true); } }