Swing Components Java

// Example from http://www.crionics.com/products/opensource/faq/swing_ex/SwingExamples.html
/* (swing1.1.1) */
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BoundedRangeModel;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSliderUI;
import javax.swing.plaf.basic.BasicSliderUI.TrackListener;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.MetalSliderUI;
/**
 * @version 1.0 9/3/99
 */
public class MThumbSliderExample extends JFrame {
  public MThumbSliderExample() {
    super("MThumbSlider Example");
    JSlider slider = new JSlider();
    slider.putClientProperty("JSlider.isFilled", Boolean.TRUE);
    int n = 2;
    MThumbSlider mSlider = new MThumbSlider(n);
    mSlider.setValueAt(25, 0);
    mSlider.setValueAt(75, 1);
    mSlider.setFillColorAt(Color.green, 0);
    mSlider.setFillColorAt(Color.yellow, 1);
    mSlider.setTrackFillColor(Color.red);
    mSlider.putClientProperty("JSlider.isFilled", Boolean.TRUE);
    getContentPane().setLayout(new FlowLayout());
    getContentPane().add(slider);
    getContentPane().add(mSlider);
  }
  public static void main(String args[]) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    MThumbSliderExample f = new MThumbSliderExample();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    f.setSize(300, 80);
    f.show();
  }
}
class MThumbSlider extends JSlider {
  protected int thumbNum;
  protected BoundedRangeModel[] sliderModels;
  protected Icon[] thumbRenderers;
  protected Color[] fillColors;
  protected Color trackFillColor;
  private static final String uiClassID = "MThumbSliderUI";
  public MThumbSlider(int n) {
    createThumbs(n);
    updateUI();
  }
  protected void createThumbs(int n) {
    thumbNum = n;
    sliderModels = new BoundedRangeModel[n];
    thumbRenderers = new Icon[n];
    fillColors = new Color[n];
    for (int i = 0; i < n; i++) {
      sliderModels[i] = new DefaultBoundedRangeModel(50, 0, 0, 100);
      thumbRenderers[i] = null;
      fillColors[i] = null;
    }
  }
  public void updateUI() {
    AssistantUIManager.setUIName(this);
    super.updateUI();
    /*
     * // another way // updateLabelUIs();
     * setUI(AssistantUIManager.createUI(this)); //setUI(new
     * BasicMThumbSliderUI(this)); //setUI(new MetalMThumbSliderUI(this));
     * //setUI(new MotifMThumbSliderUI(this));
     */
  }
  public String getUIClassID() {
    return uiClassID;
  }
  public int getThumbNum() {
    return thumbNum;
  }
  public int getValueAt(int index) {
    return getModelAt(index).getValue();
  }
  public void setValueAt(int n, int index) {
    getModelAt(index).setValue(n);
    // should I fire?
  }
  public int getMinimum() {
    return getModelAt(0).getMinimum();
  }
  public int getMaximum() {
    return getModelAt(0).getMaximum();
  }
  public BoundedRangeModel getModelAt(int index) {
    return sliderModels[index];
  }
  public Icon getThumbRendererAt(int index) {
    return thumbRenderers[index];
  }
  public void setThumbRendererAt(Icon icon, int index) {
    thumbRenderers[index] = icon;
  }
  public Color getFillColorAt(int index) {
    return fillColors[index];
  }
  public void setFillColorAt(Color color, int index) {
    fillColors[index] = color;
  }
  public Color getTrackFillColor() {
    return trackFillColor;
  }
  public void setTrackFillColor(Color color) {
    trackFillColor = color;
  }
}
class MetalMThumbSliderUI extends MetalSliderUI implements
    MThumbSliderAdditional {
  MThumbSliderAdditionalUI additonalUi;
  MouseInputAdapter mThumbTrackListener;
  public static ComponentUI createUI(JComponent c) {
    return new MetalMThumbSliderUI((JSlider) c);
  }
  public MetalMThumbSliderUI() {
    //super(null);
  }
  public MetalMThumbSliderUI(JSlider b) {
    //super(null);
  }
  public void installUI(JComponent c) {
    additonalUi = new MThumbSliderAdditionalUI(this);
    additonalUi.installUI(c);
    mThumbTrackListener = createMThumbTrackListener((JSlider) c);
    super.installUI(c);
  }
  public void uninstallUI(JComponent c) {
    super.uninstallUI(c);
    additonalUi.uninstallUI(c);
    additonalUi = null;
    mThumbTrackListener = null;
  }
  protected MouseInputAdapter createMThumbTrackListener(JSlider slider) {
    return additonalUi.trackListener;
  }
  protected TrackListener createTrackListener(JSlider slider) {
    return null;
  }
  protected ChangeListener createChangeListener(JSlider slider) {
    return additonalUi.changeHandler;
  }
  protected void installListeners(JSlider slider) {
    slider.addMouseListener(mThumbTrackListener);
    slider.addMouseMotionListener(mThumbTrackListener);
    slider.addFocusListener(focusListener);
    slider.addComponentListener(componentListener);
    slider.addPropertyChangeListener(propertyChangeListener);
    slider.getModel().addChangeListener(changeListener);
  }
  protected void uninstallListeners(JSlider slider) {
    slider.removeMouseListener(mThumbTrackListener);
    slider.removeMouseMotionListener(mThumbTrackListener);
    slider.removeFocusListener(focusListener);
    slider.removeComponentListener(componentListener);
    slider.removePropertyChangeListener(propertyChangeListener);
    slider.getModel().removeChangeListener(changeListener);
  }
  protected void calculateGeometry() {
    super.calculateGeometry();
    additonalUi.calculateThumbsSize();
    additonalUi.calculateThumbsLocation();
  }
  protected void calculateThumbLocation() {
  }
  Icon thumbRenderer;
  public void paint(Graphics g, JComponent c) {
    Rectangle clip = g.getClipBounds();
    Rectangle[] thumbRects = additonalUi.getThumbRects();
    thumbRect = thumbRects[0];
    int thumbNum = additonalUi.getThumbNum();
    if (slider.getPaintTrack() && clip.intersects(trackRect)) {
      boolean filledSlider_tmp = filledSlider;
      filledSlider = false;
      paintTrack(g);
      filledSlider = filledSlider_tmp;
      if (filledSlider) {
        g.translate(trackRect.x, trackRect.y);
        Point t1 = new Point(0, 0);
        Point t2 = new Point(0, 0);
        Rectangle maxThumbRect = new Rectangle(thumbRect);
        thumbRect = maxThumbRect;
        if (slider.getOrientation() == JSlider.HORIZONTAL) {
          t2.y = (trackRect.height - 1) - getThumbOverhang();
          t1.y = t2.y - (getTrackWidth() - 1);
          t2.x = trackRect.width - 1;
          int maxPosition = xPositionForValue(slider.getMaximum());
          thumbRect.x = maxPosition - (thumbRect.width / 2) - 2;
          thumbRect.y = trackRect.y;
        } else {
          t1.x = (trackRect.width - getThumbOverhang())
              - getTrackWidth();
          t2.x = (trackRect.width - getThumbOverhang()) - 1;
          t2.y = trackRect.height - 1;
          int maxPosition = yPositionForValue(slider.getMaximum());
          thumbRect.x = trackRect.x;
          thumbRect.y = maxPosition - (thumbRect.height / 2) - 2;
        }
        Color fillColor = ((MThumbSlider) slider).getTrackFillColor();
        if (fillColor == null) {
          fillColor = MetalLookAndFeel.getControlShadow();
        }
        fillTrack(g, t1, t2, fillColor);
        for (int i = thumbNum - 1; 0 <= i; i--) {
          thumbRect = thumbRects[i];
          fillColor = ((MThumbSlider) slider).getFillColorAt(i);
          if (fillColor == null) {
            fillColor = MetalLookAndFeel.getControlShadow();
          }
          fillTrack(g, t1, t2, fillColor);
        }
        g.translate(-trackRect.x, -trackRect.y);
      }
    }
    if (slider.getPaintTicks() && clip.intersects(tickRect)) {
      paintTicks(g);
    }
    if (slider.getPaintLabels() && clip.intersects(labelRect)) {
      paintLabels(g);
    }
    for (int i = thumbNum - 1; 0 <= i; i--) {
      if (clip.intersects(thumbRects[i])) {
        thumbRect = thumbRects[i];
        thumbRenderer = ((MThumbSlider) slider).getThumbRendererAt(i);
        if (thumbRenderer == null) {
          if (slider.getOrientation() == JSlider.HORIZONTAL) {
            thumbRenderer = horizThumbIcon;
          } else {
            thumbRenderer = vertThumbIcon;
          }
        }
        paintThumb(g);
      }
    }
  }
  public void paintThumb(Graphics g) {
    thumbRenderer.paintIcon(slider, g, thumbRect.x, thumbRect.y);
  }
  public void fillTrack(Graphics g, Point t1, Point t2, Color fillColor) {
    //                               t1-------------------
    //                               | |
    //                               --------------------t2
    int middleOfThumb = 0;
    if (slider.getOrientation() == JSlider.HORIZONTAL) {
      middleOfThumb = thumbRect.x + (thumbRect.width / 2) - trackRect.x;
      if (slider.isEnabled()) {
        g.setColor(fillColor);
        g.fillRect(t1.x + 2, t1.y + 2, middleOfThumb - t1.x - 1, t2.y
            - t1.y - 3);
        g.setColor(fillColor.brighter());
        g.drawLine(t1.x + 1, t1.y + 1, middleOfThumb, t1.y + 1);
        g.drawLine(t1.x + 1, t1.y + 1, t1.x + 1, t2.y - 2);
      } else {
        g.setColor(fillColor);
        g.fillRect(t1.x, t1.y, middleOfThumb - t1.x + 2, t2.y - t1.y);
      }
    } else {
      middleOfThumb = thumbRect.y + (thumbRect.height / 2) - trackRect.y;
      if (slider.isEnabled()) {
        g.setColor(slider.getBackground());
        g.drawLine(t1.x + 1, middleOfThumb, t2.x - 2, middleOfThumb);
        g.drawLine(t1.x + 1, middleOfThumb, t1.x + 1, t2.y - 2);
        g.setColor(fillColor);
        g.fillRect(t1.x + 2, middleOfThumb + 1, t2.x - t1.x - 3, t2.y
            - 2 - middleOfThumb);
      } else {
        g.setColor(fillColor);
        g.fillRect(t1.x, middleOfThumb + 2, t2.x - 1 - t1.x, t2.y
            - t1.y);
      }
    }
  }
  public void scrollByBlock(int direction) {
  }
  public void scrollByUnit(int direction) {
  }
  //  
  //  MThumbSliderAdditional
  //
  public Rectangle getTrackRect() {
    return trackRect;
  }
  public Dimension getThumbSize() {
    return super.getThumbSize();
  }
  public int xPositionForValue(int value) {
    return super.xPositionForValue(value);
  }
  public int yPositionForValue(int value) {
    return super.yPositionForValue(value);
  }
}
interface MThumbSliderAdditional {
  public Rectangle getTrackRect();
  public Dimension getThumbSize();
  public int xPositionForValue(int value);
  public int yPositionForValue(int value);
}
class MThumbSliderAdditionalUI {
  MThumbSlider mSlider;
  BasicSliderUI ui;
  Rectangle[] thumbRects;
  int thumbNum;
  private transient boolean isDragging;
  Icon thumbRenderer;
  Rectangle trackRect;
  ChangeHandler changeHandler;
  TrackListener trackListener;
  public MThumbSliderAdditionalUI(BasicSliderUI ui) {
    this.ui = ui;
  }
  public void installUI(JComponent c) {
    mSlider = (MThumbSlider) c;
    thumbNum = mSlider.getThumbNum();
    thumbRects = new Rectangle[thumbNum];
    for (int i = 0; i < thumbNum; i++) {
      thumbRects[i] = new Rectangle();
    }
    isDragging = false;
    trackListener = new MThumbSliderAdditionalUI.TrackListener(mSlider);
    changeHandler = new ChangeHandler();
  }
  public void uninstallUI(JComponent c) {
    thumbRects = null;
    trackListener = null;
    changeHandler = null;
  }
  protected void calculateThumbsSize() {
    Dimension size = ((MThumbSliderAdditional) ui).getThumbSize();
    for (int i = 0; i < thumbNum; i++) {
      thumbRects[i].setSize(size.width, size.height);
    }
  }
  protected void calculateThumbsLocation() {
    for (int i = 0; i < thumbNum; i++) {
      if (mSlider.getSnapToTicks()) {
        int tickSpacing = mSlider.getMinorTickSpacing();
        if (tickSpacing == 0) {
          tickSpacing = mSlider.getMajorTickSpacing();
        }
        if (tickSpacing != 0) {
          int sliderValue = mSlider.getValueAt(i);
          int snappedValue = sliderValue;
          //int min = mSlider.getMinimumAt(i);
          int min = mSlider.getMinimum();
          if ((sliderValue - min) % tickSpacing != 0) {
            float temp = (float) (sliderValue - min)
                / (float) tickSpacing;
            int whichTick = Math.round(temp);
            snappedValue = min + (whichTick * tickSpacing);
            mSlider.setValueAt(snappedValue, i);
          }
        }
      }
      trackRect = getTrackRect();
      if (mSlider.getOrientation() == JSlider.HORIZONTAL) {
        int value = mSlider.getValueAt(i);
        int valuePosition = ((MThumbSliderAdditional) ui)
            .xPositionForValue(value);
        thumbRects[i].x = valuePosition - (thumbRects[i].width / 2);
        thumbRects[i].y = trackRect.y;
      } else {
        int valuePosition = ((MThumbSliderAdditional) ui)
            .yPositionForValue(mSlider.getValueAt(i)); // need
        thumbRects[i].x = trackRect.x;
        thumbRects[i].y = valuePosition - (thumbRects[i].height / 2);
      }
    }
  }
  public int getThumbNum() {
    return thumbNum;
  }
  public Rectangle[] getThumbRects() {
    return thumbRects;
  }
  private static Rectangle unionRect = new Rectangle();
  public void setThumbLocationAt(int x, int y, int index) {
    Rectangle rect = thumbRects[index];
    unionRect.setBounds(rect);
    rect.setLocation(x, y);
    SwingUtilities.computeUnion(rect.x, rect.y, rect.width, rect.height,
        unionRect);
    mSlider.repaint(unionRect.x, unionRect.y, unionRect.width,
        unionRect.height);
  }
  public Rectangle getTrackRect() {
    return ((MThumbSliderAdditional) ui).getTrackRect();
  }
  public class ChangeHandler implements ChangeListener {
    public void stateChanged(ChangeEvent e) {
      if (!isDragging) {
        calculateThumbsLocation();
        mSlider.repaint();
      }
    }
  }
  public class TrackListener extends MouseInputAdapter {
    protected transient int offset;
    protected transient int currentMouseX, currentMouseY;
    protected Rectangle adjustingThumbRect = null;
    protected int adjustingThumbIndex;
    protected MThumbSlider slider;
    protected Rectangle trackRect;
    TrackListener(MThumbSlider slider) {
      this.slider = slider;
    }
    public void mousePressed(MouseEvent e) {
      if (!slider.isEnabled()) {
        return;
      }
      currentMouseX = e.getX();
      currentMouseY = e.getY();
      slider.requestFocus();
      for (int i = 0; i < thumbNum; i++) {
        Rectangle rect = thumbRects[i];
        if (rect.contains(currentMouseX, currentMouseY)) {
          switch (slider.getOrientation()) {
          case JSlider.VERTICAL:
            offset = currentMouseY - rect.y;
            break;
          case JSlider.HORIZONTAL:
            offset = currentMouseX - rect.x;
            break;
          }
          isDragging = true;
          slider.setValueIsAdjusting(true);
          adjustingThumbRect = rect;
          adjustingThumbIndex = i;
          return;
        }
      }
    }
    public void mouseDragged(MouseEvent e) {
      if (!slider.isEnabled() || !isDragging
          || !slider.getValueIsAdjusting()
          || adjustingThumbRect == null) {
        return;
      }
      int thumbMiddle = 0;
      currentMouseX = e.getX();
      currentMouseY = e.getY();
      Rectangle rect = thumbRects[adjustingThumbIndex];
      trackRect = getTrackRect();
      switch (slider.getOrientation()) {
      case JSlider.VERTICAL:
        int halfThumbHeight = rect.height / 2;
        int thumbTop = e.getY() - offset;
        int trackTop = trackRect.y;
        int trackBottom = trackRect.y + (trackRect.height - 1);
        thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
        thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
        setThumbLocationAt(rect.x, thumbTop, adjustingThumbIndex);
        thumbMiddle = thumbTop + halfThumbHeight;
        mSlider.setValueAt(ui.valueForYPosition(thumbMiddle),
            adjustingThumbIndex);
        break;
      case JSlider.HORIZONTAL:
        int halfThumbWidth = rect.width / 2;
        int thumbLeft = e.getX() - offset;
        int trackLeft = trackRect.x;
        int trackRight = trackRect.x + (trackRect.width - 1);
        thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
        thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
        setThumbLocationAt(thumbLeft, rect.y, adjustingThumbIndex);
        thumbMiddle = thumbLeft + halfThumbWidth;
        mSlider.setValueAt(ui.valueForXPosition(thumbMiddle),
            adjustingThumbIndex);
        break;
      }
    }
    public void mouseReleased(MouseEvent e) {
      if (!slider.isEnabled()) {
        return;
      }
      offset = 0;
      isDragging = false;
      mSlider.setValueIsAdjusting(false);
      mSlider.repaint();
    }
    public boolean shouldScroll(int direction) {
      return false;
    }
  }
}
class AssistantUIManager {
  public static ComponentUI createUI(JComponent c) {
    String componentName = c.getClass().getName();
    int index = componentName.lastIndexOf(".") + 1;
    StringBuffer sb = new StringBuffer();
    sb.append(componentName.substring(0, index));
    //
    // UIManager.getLookAndFeel().getName()
    // 
    // [ Metal ] [ Motif ] [ Mac ] [ Windows ]
    //   Metal CDE/Motif Macintosh Windows
    //
    String lookAndFeelName = UIManager.getLookAndFeel().getName();
    if (lookAndFeelName.startsWith("CDE/")) {
      lookAndFeelName = lookAndFeelName.substring(4, lookAndFeelName
          .length());
    }
    sb.append(lookAndFeelName);
    sb.append(componentName.substring(index));
    sb.append("UI");
    ComponentUI componentUI = getInstance(sb.toString());
    if (componentUI == null) {
      sb.setLength(0);
      sb.append(componentName.substring(0, index));
      sb.append("Basic");
      sb.append(componentName.substring(index));
      sb.append("UI");
      componentUI = getInstance(sb.toString());
    }
    return componentUI;
  }
  private static ComponentUI getInstance(String name) {
    try {
      return (ComponentUI) Class.forName(name).newInstance();
    } catch (ClassNotFoundException ex) {
    } catch (IllegalAccessException ex) {
      ex.printStackTrace();
    } catch (InstantiationException ex) {
      ex.printStackTrace();
    }
    return null;
  }
  public static void setUIName(JComponent c) {
    String key = c.getUIClassID();
    String uiClassName = (String) UIManager.get(key);
    if (uiClassName == null) {
      String componentName = c.getClass().getName();
      int index = componentName.lastIndexOf(".") + 1;
      StringBuffer sb = new StringBuffer();
      sb.append(componentName.substring(0, index));
      String lookAndFeelName = UIManager.getLookAndFeel().getName();
      if (lookAndFeelName.startsWith("CDE/")) {
        lookAndFeelName = lookAndFeelName.substring(4, lookAndFeelName
            .length());
      }
      sb.append(lookAndFeelName);
      sb.append(key);
      UIManager.put(key, sb.toString());
    }
  }
  public AssistantUIManager() {
  }
}