Java Swing Components : Example


  1. A simple Clock
  2. Dual Lists Demo
  3. ItemChooser
  4. JSpinField is a numeric field with 2 spin buttons to increase or decrease the value.
  5. LayeredPane With Warning
  6. JTreeTable component
  7. Auto Complete TextField
  8. A button which paints on it one or more scaled arrows in one of the cardinal directions
  9. Arrow Button
  10. The various table charting classes
  11. A panel from where you can choose a color based on it's HTML name
  12. A JOutlookBar provides a component that is similar to a JTabbedPane, but instead of maintaining tabs
  13. Another Link button
  14. LinkButton
  15. Separator Sample 2
  16. Separator Sample
  17. A demonstration of the JSeparator() component used in a toolbar-like
  18. Thumb Slider Example
  19. Thumb Slider Example 1
  20. Thumb Slider Example 2
  21. Slider With ToolTip Example
  22. A dialog that presents the user with a sequence of steps for completing a task.

A simple Clock

import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.text.DecimalFormat;
import java.util.Calendar;

/** A simple Clock */
public class Clock extends javax.swing.JComponent {
  protected DecimalFormat tflz, tf;

  protected boolean done = false;

  public Clock() {
    new Thread(new Runnable() {
      public void run() {
        while (!done) {
          Clock.this.repaint(); // request a redraw
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) { /* do nothing */
          }
        }
      }
    }).start();
    tf = new DecimalFormat("#0");
    tflz = new DecimalFormat("00");
  }

  public void stop() {
    done = true;
  }

  /* paint() - get current time and draw (centered) in Component. */
  public void paint(Graphics g) {
    Calendar myCal = Calendar.getInstance();
    StringBuffer sb = new StringBuffer();
    sb.append(tf.format(myCal.get(Calendar.HOUR)));
    sb.append(':');
    sb.append(tflz.format(myCal.get(Calendar.MINUTE)));
    sb.append(':');
    sb.append(tflz.format(myCal.get(Calendar.SECOND)));
    String s = sb.toString();
    FontMetrics fm = getFontMetrics(getFont());
    int x = (getSize().width - fm.stringWidth(s)) / 2;
    // System.out.println("Size is " + getSize());
    g.drawString(s, x, 10);
  }

  public Dimension getPreferredSize() {
    return new Dimension(100, 30);
  }

  public Dimension getMinimumSize() {
    return new Dimension(50, 10);
  }
}

Dual Lists Demo

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;

public class MainClass {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Dual List Box Tester");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    DualListBox dual = new DualListBox();
    dual.addSourceElements(new String[] { "One", "Two", "Three" });
    frame.add(dual, BorderLayout.CENTER);
    frame.setSize(400, 300);
    frame.setVisible(true);
  }

}

class DualListBox extends JPanel {

  private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);

  private static final String ADD_BUTTON_LABEL = "Add >>";

  private static final String REMOVE_BUTTON_LABEL = "<< Remove";

  private static final String DEFAULT_SOURCE_CHOICE_LABEL = "Available Choices";

  private static final String DEFAULT_DEST_CHOICE_LABEL = "Your Choices";

  private JLabel sourceLabel;

  private JList sourceList;

  private SortedListModel sourceListModel;

  private JList destList;

  private SortedListModel destListModel;

  private JLabel destLabel;

  private JButton addButton;

  private JButton removeButton;

  public DualListBox() {
    initScreen();
  }

  public String getSourceChoicesTitle() {
    return sourceLabel.getText();
  }

  public void setSourceChoicesTitle(String newValue) {
    sourceLabel.setText(newValue);
  }

  public String getDestinationChoicesTitle() {
    return destLabel.getText();
  }

  public void setDestinationChoicesTitle(String newValue) {
    destLabel.setText(newValue);
  }

  public void clearSourceListModel() {
    sourceListModel.clear();
  }

  public void clearDestinationListModel() {
    destListModel.clear();
  }

  public void addSourceElements(ListModel newValue) {
    fillListModel(sourceListModel, newValue);
  }

  public void setSourceElements(ListModel newValue) {
    clearSourceListModel();
    addSourceElements(newValue);
  }

  public void addDestinationElements(ListModel newValue) {
    fillListModel(destListModel, newValue);
  }

  private void fillListModel(SortedListModel model, ListModel newValues) {
    int size = newValues.getSize();
    for (int i = 0; i < size; i++) {
      model.add(newValues.getElementAt(i));
    }
  }

  public void addSourceElements(Object newValue[]) {
    fillListModel(sourceListModel, newValue);
  }

  public void setSourceElements(Object newValue[]) {
    clearSourceListModel();
    addSourceElements(newValue);
  }

  public void addDestinationElements(Object newValue[]) {
    fillListModel(destListModel, newValue);
  }

  private void fillListModel(SortedListModel model, Object newValues[]) {
    model.addAll(newValues);
  }

  public Iterator sourceIterator() {
    return sourceListModel.iterator();
  }

  public Iterator destinationIterator() {
    return destListModel.iterator();
  }

  public void setSourceCellRenderer(ListCellRenderer newValue) {
    sourceList.setCellRenderer(newValue);
  }

  public ListCellRenderer getSourceCellRenderer() {
    return sourceList.getCellRenderer();
  }

  public void setDestinationCellRenderer(ListCellRenderer newValue) {
    destList.setCellRenderer(newValue);
  }

  public ListCellRenderer getDestinationCellRenderer() {
    return destList.getCellRenderer();
  }

  public void setVisibleRowCount(int newValue) {
    sourceList.setVisibleRowCount(newValue);
    destList.setVisibleRowCount(newValue);
  }

  public int getVisibleRowCount() {
    return sourceList.getVisibleRowCount();
  }

  public void setSelectionBackground(Color newValue) {
    sourceList.setSelectionBackground(newValue);
    destList.setSelectionBackground(newValue);
  }

  public Color getSelectionBackground() {
    return sourceList.getSelectionBackground();
  }

  public void setSelectionForeground(Color newValue) {
    sourceList.setSelectionForeground(newValue);
    destList.setSelectionForeground(newValue);
  }

  public Color getSelectionForeground() {
    return sourceList.getSelectionForeground();
  }

  private void clearSourceSelected() {
    Object selected[] = sourceList.getSelectedValues();
    for (int i = selected.length - 1; i >= 0; --i) {
      sourceListModel.removeElement(selected[i]);
    }
    sourceList.getSelectionModel().clearSelection();
  }

  private void clearDestinationSelected() {
    Object selected[] = destList.getSelectedValues();
    for (int i = selected.length - 1; i >= 0; --i) {
      destListModel.removeElement(selected[i]);
    }
    destList.getSelectionModel().clearSelection();
  }

  private void initScreen() {
    setBorder(BorderFactory.createEtchedBorder());
    setLayout(new GridBagLayout());
    sourceLabel = new JLabel(DEFAULT_SOURCE_CHOICE_LABEL);
    sourceListModel = new SortedListModel();
    sourceList = new JList(sourceListModel);
    add(sourceLabel, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
        GridBagConstraints.NONE, EMPTY_INSETS, 0, 0));
    add(new JScrollPane(sourceList), new GridBagConstraints(0, 1, 1, 5, .5, 1,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, EMPTY_INSETS, 0, 0));

    addButton = new JButton(ADD_BUTTON_LABEL);
    add(addButton, new GridBagConstraints(1, 2, 1, 2, 0, .25, GridBagConstraints.CENTER,
        GridBagConstraints.NONE, EMPTY_INSETS, 0, 0));
    addButton.addActionListener(new AddListener());
    removeButton = new JButton(REMOVE_BUTTON_LABEL);
    add(removeButton, new GridBagConstraints(1, 4, 1, 2, 0, .25, GridBagConstraints.CENTER,
        GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    removeButton.addActionListener(new RemoveListener());

    destLabel = new JLabel(DEFAULT_DEST_CHOICE_LABEL);
    destListModel = new SortedListModel();
    destList = new JList(destListModel);
    add(destLabel, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
        GridBagConstraints.NONE, EMPTY_INSETS, 0, 0));
    add(new JScrollPane(destList), new GridBagConstraints(2, 1, 1, 5, .5, 1.0,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, EMPTY_INSETS, 0, 0));
  }

  private class AddListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      Object selected[] = sourceList.getSelectedValues();
      addDestinationElements(selected);
      clearSourceSelected();
    }
  }

  private class RemoveListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      Object selected[] = destList.getSelectedValues();
      addSourceElements(selected);
      clearDestinationSelected();
    }
  }

}

class SortedListModel extends AbstractListModel {

  SortedSet<Object> model;

  public SortedListModel() {
    model = new TreeSet<Object>();
  }

  public int getSize() {
    return model.size();
  }

  public Object getElementAt(int index) {
    return model.toArray()[index];
  }

  public void add(Object element) {
    if (model.add(element)) {
      fireContentsChanged(this, 0, getSize());
    }
  }

  public void addAll(Object elements[]) {
    Collection<Object> c = Arrays.asList(elements);
    model.addAll(c);
    fireContentsChanged(this, 0, getSize());
  }

  public void clear() {
    model.clear();
    fireContentsChanged(this, 0, getSize());
  }

  public boolean contains(Object element) {
    return model.contains(element);
  }

  public Object firstElement() {
    return model.first();
  }

  public Iterator iterator() {
    return model.iterator();
  }

  public Object lastElement() {
    return model.last();
  }

  public boolean removeElement(Object element) {
    boolean removed = model.remove(element);
    if (removed) {
      fireContentsChanged(this, 0, getSize());
    }
    return removed;
  }
}

ItemChooser

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * This class is a Swing component that presents a choice to the user. It allows
 * the choice to be presented in a JList, in a JComboBox, or with a bordered
 * group of JRadioButton components. Additionally, it displays the name of the
 * choice with a JLabel. It allows an arbitrary value to be associated with each
 * possible choice. Note that this component only allows one item to be selected
 * at a time. Multiple selections are not supported.
 */
public class ItemChooser extends JPanel {
  // These fields hold property values for this component
  String name; // The overall name of the choice

  String[] labels; // The text for each choice option

  Object[] values; // Arbitrary values associated with each option

  int selection; // The selected choice

  int presentation; // How the choice is presented

  // These are the legal values for the presentation field
  public static final int LIST = 1;

  public static final int COMBOBOX = 2;

  public static final int RADIOBUTTONS = 3;

  // These components are used for each of the 3 possible presentations
  JList list; // One type of presentation

  JComboBox combobox; // Another type of presentation

  JRadioButton[] radiobuttons; // Yet another type

  // The list of objects that are interested in our state
  ArrayList listeners = new ArrayList();

  // The constructor method sets everything up
  public ItemChooser(String name, String[] labels, Object[] values,
      int defaultSelection, int presentation) {
    // Copy the constructor arguments to instance fields
    this.name = name;
    this.labels = labels;
    this.values = values;
    this.selection = defaultSelection;
    this.presentation = presentation;

    // If no values were supplied, use the labels
    if (values == null)
      this.values = labels;

    // Now create content and event handlers based on presentation type
    switch (presentation) {
    case LIST:
      initList();
      break;
    case COMBOBOX:
      initComboBox();
      break;
    case RADIOBUTTONS:
      initRadioButtons();
      break;
    }
  }

  // Initialization for JList presentation
  void initList() {
    list = new JList(labels); // Create the list
    list.setSelectedIndex(selection); // Set initial state

    // Handle state changes
    list.addListSelectionListener(new ListSelectionListener() {
      public void valueChanged(ListSelectionEvent e) {
        ItemChooser.this.select(list.getSelectedIndex());
      }
    });

    // Lay out list and name label vertically
    this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); // vertical
    this.add(new JLabel(name)); // Display choice name
    this.add(new JScrollPane(list)); // Add the JList
  }

  // Initialization for JComboBox presentation
  void initComboBox() {
    combobox = new JComboBox(labels); // Create the combo box
//    combobox.setSelectedIndex(selection); // Set initial state

    // Handle changes to the state
    combobox.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        ItemChooser.this.select(combobox.getSelectedIndex());
      }
    });

    // Lay out combo box and name label horizontally
    this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    this.add(new JLabel(name));
    this.add(combobox);
  }

  // Initialization for JRadioButton presentation
  void initRadioButtons() {
    // Create an array of mutually exclusive radio buttons
    radiobuttons = new JRadioButton[labels.length]; // the array
    ButtonGroup radioButtonGroup = new ButtonGroup(); // used for exclusion
    ChangeListener listener = new ChangeListener() { // A shared listener
      public void stateChanged(ChangeEvent e) {
        JRadioButton b = (JRadioButton) e.getSource();
        if (b.isSelected()) {
          // If we received this event because a button was
          // selected, then loop through the list of buttons to
          // figure out the index of the selected one.
          for (int i = 0; i < radiobuttons.length; i++) {
            if (radiobuttons[i] == b) {
              ItemChooser.this.select(i);
              return;
            }
          }
        }
      }
    };

    // Display the choice name in a border around the buttons
    this.setBorder(new TitledBorder(new EtchedBorder(), name));
    this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

    // Create the buttons, add them to the button group, and specify
    // the event listener for each one.
    for (int i = 0; i < labels.length; i++) {
      radiobuttons[i] = new JRadioButton(labels[i]);
      if (i == selection)
        radiobuttons[i].setSelected(true);
      radiobuttons[i].addChangeListener(listener);
      radioButtonGroup.add(radiobuttons[i]);
      this.add(radiobuttons[i]);
    }
  }

  // These simple property accessor methods just return field values
  // These are read-only properties. The values are set by the constructor
  // and may not be changed.
  public String getName() {
    return name;
  }

  public int getPresentation() {
    return presentation;
  }

  public String[] getLabels() {
    return labels;
  }

  public Object[] getValues() {
    return values;
  }

  /** Return the index of the selected item */
  public int getSelectedIndex() {
    return selection;
  }

  /** Return the object associated with the selected item */
  public Object getSelectedValue() {
    return values[selection];
  }

  /**
   * Set the selected item by specifying its index. Calling this method
   * changes the on-screen display but does not generate events.
   */
  public void setSelectedIndex(int selection) {
    switch (presentation) {
    case LIST:
      list.setSelectedIndex(selection);
      break;
    case COMBOBOX:
      combobox.setSelectedIndex(selection);
      break;
    case RADIOBUTTONS:
      radiobuttons[selection].setSelected(true);
      break;
    }
    this.selection = selection;
  }

  /**
   * This internal method is called when the selection changes. It stores the
   * new selected index, and fires events to any registered listeners. The
   * event listeners registered on the JList, JComboBox, or JRadioButtons all
   * call this method.
   */
  protected void select(int selection) {
    this.selection = selection; // Store the new selected index
    if (!listeners.isEmpty()) { // If there are any listeners registered
      // Create an event object to describe the selection
      ItemChooser.Event e = new ItemChooser.Event(this, selection,
          values[selection]);
      // Loop through the listeners using an Iterator
      for (Iterator i = listeners.iterator(); i.hasNext();) {
        ItemChooser.Listener l = (ItemChooser.Listener) i.next();
        l.itemChosen(e); // Notify each listener of the selection
      }
    }
  }

  // These methods are for event listener registration and deregistration
  public void addItemChooserListener(ItemChooser.Listener l) {
    listeners.add(l);
  }

  public void removeItemChooserListener(ItemChooser.Listener l) {
    listeners.remove(l);
  }

  /**
   * This inner class defines the event type generated by ItemChooser objects
   * The inner class name is Event, so the full name is ItemChooser.Event
   */
  public static class Event extends java.util.EventObject {
    int selectedIndex; // index of the selected item

    Object selectedValue; // the value associated with it

    public Event(ItemChooser source, int selectedIndex, Object selectedValue) {
      super(source);
      this.selectedIndex = selectedIndex;
      this.selectedValue = selectedValue;
    }

    public ItemChooser getItemChooser() {
      return (ItemChooser) getSource();
    }

    public int getSelectedIndex() {
      return selectedIndex;
    }

    public Object getSelectedValue() {
      return selectedValue;
    }
  }

  /**
   * This inner interface must be implemented by any object that wants to be
   * notified when the current selection in a ItemChooser component changes.
   */
  public interface Listener extends java.util.EventListener {
    public void itemChosen(ItemChooser.Event e);
  }

  public static void main(String[] args) {
    // Create a window, arrange to handle close requests
    final JFrame frame = new JFrame("ItemChooser Demo");
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });

    // A "message line" to display results in
    final JLabel msgline = new JLabel(" ");
    args = new String[]{"a","b","c"};
    // Create a panel holding three ItemChooser components
    JPanel chooserPanel = new JPanel();
    final ItemChooser c1 = new ItemChooser("Choice #1", args, null, 0,
        ItemChooser.LIST);
    final ItemChooser c2 = new ItemChooser("Choice #2", args, null, 0,
        ItemChooser.COMBOBOX);
    final ItemChooser c3 = new ItemChooser("Choice #3", args, null, 0,
        ItemChooser.RADIOBUTTONS);

    // An event listener that displays changes on the message line
    ItemChooser.Listener l = new ItemChooser.Listener() {
      public void itemChosen(ItemChooser.Event e) {
        msgline.setText(e.getItemChooser().getName() + ": "
            + e.getSelectedIndex() + ": " + e.getSelectedValue());
      }
    };
    c1.addItemChooserListener(l);
    c2.addItemChooserListener(l);
    c3.addItemChooserListener(l);

    // Instead of tracking every change with a ItemChooser.Listener,
    // applications can also just query the current state when
    // they need it. Here's a button that does that.
    JButton report = new JButton("Report");
    report.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        // Note the use of multi-line italic HTML text
        // with the JOptionPane message dialog box.
        String msg = "<html><i>" + c1.getName() + ": "
            + c1.getSelectedValue() + "<br>" + c2.getName() + ": "
            + c2.getSelectedValue() + "<br>" + c3.getName() + ": "
            + c3.getSelectedValue() + "</i>";
        JOptionPane.showMessageDialog(frame, msg);
      }
    });

    // Add the 3 ItemChooser objects, and the Button to the panel
    chooserPanel.add(c1);
    chooserPanel.add(c2);
    chooserPanel.add(c3);
    chooserPanel.add(report);

    // Add the panel and the message line to the window
    Container contentPane = frame.getContentPane();
    contentPane.add(chooserPanel, BorderLayout.CENTER);
    contentPane.add(msgline, BorderLayout.SOUTH);

    // Set the window size and pop it up.
    frame.pack();
    frame.show();
  }

}
JSpinField is a numeric field with 2 spin buttons to increase or decrease the value.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * JSpinField is a numeric field with 2 spin buttons to increase or decrease the
 * value. It has the same interface as the "old" JSpinField but uses a JSpinner
 * internally (since J2SE SDK 1.4) rather than a scrollbar for emulating the
 * spin buttons.
 * 
 * @author Kai Toedter
 * @version $LastChangedRevision: 85 $
 * @version $LastChangedDate: 2006-04-28 13:50:52 +0200 (Fr, 28 Apr 2006) $
 */
public class JSpinField extends JPanel implements ChangeListener, CaretListener, ActionListener,
    FocusListener {
  private static final long serialVersionUID = 1694904792717740650L;

  protected JSpinner spinner;

  /** the text (number) field */
  protected JTextField textField;
  protected int min;
  protected int max;
  protected int value;
  protected Color darkGreen;

  /**
   * Default JSpinField constructor. The valid value range is between
   * Integer.MIN_VALUE and Integer.MAX_VALUE. The initial value is 0.
   */
  public JSpinField() {
    this(Integer.MIN_VALUE, Integer.MAX_VALUE);
  }

  /**
   * JSpinField constructor with given minimum and maximum vaues and initial
   * value 0.
   */
  public JSpinField(int min, int max) {
    super();
    setName("JSpinField");
    this.min = min;
    if (max < min)
      max = min;
    this.max = max;
    value = 0;
    if (value < min)
      value = min;
    if (value > max)
      value = max;

    darkGreen = new Color(0, 150, 0);
    setLayout(new BorderLayout());
    textField = new JTextField();
    textField.addCaretListener(this);
    textField.addActionListener(this);
    textField.setHorizontalAlignment(SwingConstants.RIGHT);
    textField.setBorder(BorderFactory.createEmptyBorder());
    textField.setText(Integer.toString(value));
    textField.addFocusListener(this);
    spinner = new JSpinner() {
      private static final long serialVersionUID = -6287709243342021172L;
      private JTextField textField = new JTextField();

      public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        return new Dimension(size.width, textField.getPreferredSize().height);
      }
    };
    spinner.setEditor(textField);
    spinner.addChangeListener(this);
    // spinner.setSize(spinner.getWidth(), textField.getHeight());
    add(spinner, BorderLayout.CENTER);
  }

  public void adjustWidthToMaximumValue() {
    JTextField testTextField = new JTextField(Integer.toString(max));
    int width = testTextField.getPreferredSize().width;
    int height = testTextField.getPreferredSize().height;
    textField.setPreferredSize(new Dimension(width, height));
    textField.revalidate();
  }

  /**
   * Is invoked when the spinner model changes
   * 
   * @param e
   *            the ChangeEvent
   */
  public void stateChanged(ChangeEvent e) {
    SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel();
    int value = model.getNumber().intValue();
    setValue(value);
  }

  /**
   * Sets the value attribute of the JSpinField object.
   * 
   * @param newValue
   *            The new value
   * @param updateTextField
   *            true if text field should be updated
   */
  protected void setValue(int newValue, boolean updateTextField, boolean firePropertyChange) {
    int oldValue = value;
    if (newValue < min) {
      value = min;
    } else if (newValue > max) {
      value = max;
    } else {
      value = newValue;
    }

    if (updateTextField) {
      textField.setText(Integer.toString(value));
      textField.setForeground(Color.black);
    }

    if (firePropertyChange) {
      firePropertyChange("value", oldValue, value);
    }
  }

  /**
   * Sets the value. This is a bound property.
   * 
   * @param newValue
   *            the new value
   * 
   * @see #getValue
   */
  public void setValue(int newValue) {
    setValue(newValue, true, true);
    spinner.setValue(new Integer(value));
  }

  /**
   * Returns the value.
   * 
   * @return the value value
   */
  public int getValue() {
    return value;
  }

  /**
   * Sets the minimum value.
   * 
   * @param newMinimum
   *            the new minimum value
   * 
   * @see #getMinimum
   */
  public void setMinimum(int newMinimum) {
    min = newMinimum;
  }

  /**
   * Returns the minimum value.
   * 
   * @return the minimum value
   */
  public int getMinimum() {
    return min;
  }

  /**
   * Sets the maximum value and adjusts the preferred width.
   * 
   * @param newMaximum
   *            the new maximum value
   * 
   * @see #getMaximum
   */
  public void setMaximum(int newMaximum) {
    max = newMaximum;
  }

  /**
   * Sets the horizontal alignment of the displayed value.
   * 
   * @param alignment
   *            the horizontal alignment
   */
  public void setHorizontalAlignment(int alignment) {
    textField.setHorizontalAlignment(alignment);
  }

  /**
   * Returns the maximum value.
   * 
   * @return the maximum value
   */
  public int getMaximum() {
    return max;
  }

  /**
   * Sets the font property.
   * 
   * @param font
   *            the new font
   */
  public void setFont(Font font) {
    if (textField != null) {
      textField.setFont(font);
    }
  }

  /**
   * Sets the foreground
   * 
   * @param fg
   *            the foreground
   */
  public void setForeground(Color fg) {
    if (textField != null) {
      textField.setForeground(fg);
    }
  }

  /**
   * After any user input, the value of the textfield is proofed. Depending on
   * being an integer, the value is colored green or red.
   * 
   * @param e
   *            the caret event
   */
  public void caretUpdate(CaretEvent e) {
    try {
      int testValue = Integer.valueOf(textField.getText()).intValue();

      if ((testValue >= min) && (testValue <= max)) {
        textField.setForeground(darkGreen);
        setValue(testValue, false, true);
      } else {
        textField.setForeground(Color.red);
      }
    } catch (Exception ex) {
      if (ex instanceof NumberFormatException) {
        textField.setForeground(Color.red);
      }

      // Ignore all other exceptions, e.g. illegal state exception
    }

    textField.repaint();
  }

  /**
   * After any user input, the value of the textfield is proofed. Depending on
   * being an integer, the value is colored green or red. If the textfield is
   * green, the enter key is accepted and the new value is set.
   * 
   * @param e
   *            Description of the Parameter
   */
  public void actionPerformed(ActionEvent e) {
    if (textField.getForeground().equals(darkGreen)) {
      setValue(Integer.valueOf(textField.getText()).intValue());
    }
  }

  /**
   * Enable or disable the JSpinField.
   * 
   * @param enabled
   *            The new enabled value
   */
  public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    spinner.setEnabled(enabled);
    textField.setEnabled(enabled);
    /*
     * Fixes the background bug
     * 4991597 and sets the background explicitely to a
     * TextField.inactiveBackground.
     */
    if (!enabled) {
      textField.setBackground(UIManager.getColor("TextField.inactiveBackground"));
    }
  }

  /**
   * Returns the year chooser's spinner (which allow the focus to be set to
   * it).
   * 
   * @return Component the spinner or null, if the month chooser has no
   *         spinner
   */
  public Component getSpinner() {
    return spinner;
  }

  /**
   * Creates a JFrame with a JSpinField inside and can be used for testing.
   * 
   * @param s
   *            The command line arguments
   */
  public static void main(String[] s) {
    JFrame frame = new JFrame("JSpinField");
    frame.getContentPane().add(new JSpinField());
    frame.pack();
    frame.setVisible(true);
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
   */
  public void focusGained(FocusEvent e) {
  }

  /**
   * The value of the text field is checked against a valid (green) value. If
   * valid, the value is set and a property change is fired.
   */
  public void focusLost(FocusEvent e) {
    actionPerformed(null);
  }
}
LayeredPane With Warning
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JLayeredPane;
import javax.swing.JTextField;
import javax.swing.OverlayLayout;
import javax.swing.text.JTextComponent;

/**
 *
 * @author Romain Guy
 */
public class Layers extends javax.swing.JFrame {
    
    /** Creates new form Layers */
    public Layers() {
        initComponents();
        
        addLayeredValidator();
        addValidations();
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        javax.swing.JLabel jLabel1;
        javax.swing.JLabel jLabel2;
        javax.swing.JLabel jLabel3;
        javax.swing.JLabel jLabel4;
        javax.swing.JLabel jLabel5;
        javax.swing.JList jList1;
        javax.swing.JScrollPane jScrollPane1;
        javax.swing.JScrollPane jScrollPane2;

        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        firstName = new javax.swing.JTextField();
        lastName = new javax.swing.JTextField();
        phoneNumber = new javax.swing.JTextField();
        email = new javax.swing.JTextField();
        jScrollPane2 = new javax.swing.JScrollPane();
        address = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Layered Panes");
        jList1.setModel(new javax.swing.AbstractListModel() {
            String[] strings = { "Jeff Dinkins", "Richard Bair", "Amy Fowler", "Scott Violet", "Hans Muller", "Chris Campbell", "Chet Haase" };
            public int getSize() { return strings.length; }
            public Object getElementAt(int i) { return strings[i]; }
        });
        jScrollPane1.setViewportView(jList1);

        jLabel1.setText("First Name");

        jLabel2.setText("Last Name");

        jLabel3.setText("Phone");

        jLabel4.setText("Email");

        jLabel5.setText("Address");

        address.setColumns(15);
        address.setRows(5);
        jScrollPane2.setViewportView(address);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 152, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jLabel5)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jLabel4)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jLabel3)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jLabel2)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jLabel1))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(firstName, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE)
                    .add(lastName, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE)
                    .add(phoneNumber, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE)
                    .add(email, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE)
                    .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(layout.createSequentialGroup()
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel1)
                            .add(firstName, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel2)
                            .add(lastName, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel3)
                            .add(phoneNumber, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel4)
                            .add(email, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(jLabel5)
                            .add(jScrollPane2))
                        .add(6, 6, 6))
                    .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE))
                .addContainerGap())
        );
        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-489)/2, (screenSize.height-266)/2, 489, 266);
    }// </editor-fold>//GEN-END:initComponents
    
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Layers().setVisible(true);
            }
        });
    }

    private void addLayeredValidator() {
        validator = new Validator();
        
        JLayeredPane layeredPane = getRootPane().getLayeredPane();
        layeredPane.setLayout(new OverlayLayout(layeredPane));
        layeredPane.add(validator, (Integer) (JLayeredPane.DEFAULT_LAYER + 50));
        //validator.setBounds(0, 0, getWidth(), getHeight());
    }

    private void addValidations() {
        addValidationForText(address);
        addValidationForText(firstName);
        addValidationForText(lastName);
        addValidationForNumber(phoneNumber);
        addValidationForEmail(email);
    }

    private void addValidationForText(JTextComponent field) {
        field.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent focusEvent) {
            }
            
            public void focusLost(FocusEvent focusEvent) {
                JTextComponent field = (JTextComponent) focusEvent.getComponent();
                String text = field.getText();
                
                if (text.matches("[-A-Za-z ]*")) {
                    validator.addWarning(field);
                } else {
                    validator.removeWarning(field);
                }
            }
        });
    }

    private void addValidationForNumber(JTextComponent field) {
        field.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent focusEvent) {
            }
            
            public void focusLost(FocusEvent focusEvent) {
                JTextField field = (JTextField) focusEvent.getComponent();
                String text = field.getText();
                
                if (text.matches("[-()0-9 ]*")) {
                    validator.addWarning(field);
                } else {
                    validator.removeWarning(field);
                }
            }
        });
    }

    private void addValidationForEmail(JTextComponent field) {
        field.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent focusEvent) {
            }
            
            public void focusLost(FocusEvent focusEvent) {
                JTextComponent field = (JTextComponent) focusEvent.getComponent();
                String text = field.getText();
                
                if (text.matches("[^@]+@([^.]+\\.)+[^.]+")) {
                    validator.addWarning(field);
                } else {
                    validator.removeWarning(field);
                }
            }
        });
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextArea address;
    private javax.swing.JTextField email;
    private javax.swing.JTextField firstName;
    private javax.swing.JTextField lastName;
    private javax.swing.JTextField phoneNumber;
    // End of variables declaration//GEN-END:variables

    private Validator validator;
    
}
/*
 * Copyright (c) 2007, Romain Guy
 * 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 the TimingFramework project 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.
 */


/**
 *
 * @author Romain Guy <romain.guy@mac.com>
 */
class Validator extends JComponent {
    private Set<JComponent> invalidFields = new HashSet<JComponent>();
    private BufferedImage warningIcon;
    
    /** Creates a new instance of Validator */
    public Validator() {
        loadImages();
    }

    public void addWarning(JComponent field) {
        if (invalidFields.contains(field)) {
            invalidFields.remove(field);
            repaintBadge(field);
        }
    }

    public void removeWarning(JComponent field) {
        invalidFields.add(field);
        repaintBadge(field);
    }

    private void repaintBadge(JComponent field) {
        Point p = field.getLocationOnScreen();
        SwingUtilities.convertPointFromScreen(p, this);
        
        int x = p.x - warningIcon.getWidth() / 2;
        int y = (int) (p.y + field.getHeight() - warningIcon.getHeight() / 1.5);
        
        repaint(x, y, warningIcon.getWidth(), warningIcon.getHeight());
    }
    
    private void loadImages() {
        try {
            warningIcon = ImageIO.read(getClass().getResource("dialog-warning.png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        for (JComponent invalid : invalidFields) {
            if (invalid.getParent() instanceof JViewport) {
                JViewport viewport = (JViewport) invalid.getParent();
                // the parent of the viewport is a JScrollPane
                invalid = (JComponent) viewport.getParent();
            }
            
            Point p = invalid.getLocationOnScreen();
            SwingUtilities.convertPointFromScreen(p, this);
            
            int x = p.x - warningIcon.getWidth() / 2;
            int y = (int) (p.y + invalid.getHeight() - warningIcon.getHeight() / 1.5);
            
            if (g.getClipBounds().intersects(x, y,
                    warningIcon.getWidth(), warningIcon.getHeight())) {
                g.drawImage(warningIcon, x, y, null);
            }
        }
    }
}
JTreeTable component
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.util.EventObject;

import javax.swing.AbstractCellEditor;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

/**
 * This example shows how to create a simple JTreeTable component,
 * by using a JTree as a renderer (and editor) for the cells in a
 * particular column in the JTable.
 *
 * @version 1.2 10/27/98
 *
 * @author Philip Milne
 * @author Scott Violet
 */
public class JTreeTable extends JTable {
    /** A subclass of JTree. */
    protected TreeTableCellRenderer tree;

    public JTreeTable(TreeTableModel treeTableModel) {
    super();

    // Create the tree. It will be used as a renderer and editor.
    tree = new TreeTableCellRenderer(treeTableModel);

    // Install a tableModel representing the visible rows in the tree.
    super.setModel(new TreeTableModelAdapter(treeTableModel, tree));

    // Force the JTable and JTree to share their row selection models.
    ListToTreeSelectionModelWrapper selectionWrapper = new
                            ListToTreeSelectionModelWrapper();
    tree.setSelectionModel(selectionWrapper);
    setSelectionModel(selectionWrapper.getListSelectionModel());

    // Install the tree editor renderer and editor.
    setDefaultRenderer(TreeTableModel.class, tree);
    setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());

    // No grid.
    setShowGrid(false);

    // No intercell spacing
    setIntercellSpacing(new Dimension(0, 0));

    // And update the height of the trees row to match that of
    // the table.
    if (tree.getRowHeight() < 1) {
        // Metal looks better like this.
        setRowHeight(18);
    }
    }

    /**
     * Overridden to message super and forward the method to the tree.
     * Since the tree is not actually in the component hieachy it will
     * never receive this unless we forward it in this manner.
     */
    public void updateUI() {
    super.updateUI();
    if(tree != null) {
        tree.updateUI();
    }
    // Use the tree's default foreground and background colors in the
    // table.
        LookAndFeel.installColorsAndFont(this, "Tree.background",
                                         "Tree.foreground", "Tree.font");
    }

    /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
     * paint the editor. The UI currently uses different techniques to
     * paint the renderers and editors and overriding setBounds() below
     * is not the right thing to do for an editor. Returning -1 for the
     * editing row in this case, ensures the editor is never painted.
     */
    public int getEditingRow() {
        return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 :
            editingRow;
    }

    /**
     * Overridden to pass the new rowHeight to the tree.
     */
    public void setRowHeight(int rowHeight) {
        super.setRowHeight(rowHeight);
    if (tree != null && tree.getRowHeight() != rowHeight) {
            tree.setRowHeight(getRowHeight());
    }
    }

    /**
     * Returns the tree that is being shared between the model.
     */
    public JTree getTree() {
    return tree;
    }

    /**
     * A TreeCellRenderer that displays a JTree.
     */
    public class TreeTableCellRenderer extends JTree implements
             TableCellRenderer {
    /** Last table/tree row asked to renderer. */
    protected int visibleRow;

    public TreeTableCellRenderer(TreeModel model) {
        super(model);
    }

    /**
     * updateUI is overridden to set the colors of the Tree's renderer
     * to match that of the table.
     */
    public void updateUI() {
        super.updateUI();
        // Make the tree's cell renderer use the table's cell selection
        // colors.
        TreeCellRenderer tcr = getCellRenderer();
        if (tcr instanceof DefaultTreeCellRenderer) {
        DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr);
        // For 1.1 uncomment this, 1.2 has a bug that will cause an
        // exception to be thrown if the border selection color is
        // null.
        // dtcr.setBorderSelectionColor(null);
        dtcr.setTextSelectionColor(UIManager.getColor
                       ("Table.selectionForeground"));
        dtcr.setBackgroundSelectionColor(UIManager.getColor
                        ("Table.selectionBackground"));
        }
    }

    /**
     * Sets the row height of the tree, and forwards the row height to
     * the table.
     */
    public void setRowHeight(int rowHeight) {
        if (rowHeight > 0) {
        super.setRowHeight(rowHeight);
        if (JTreeTable.this != null &&
            JTreeTable.this.getRowHeight() != rowHeight) {
            JTreeTable.this.setRowHeight(getRowHeight());
        }
        }
    }

    /**
     * This is overridden to set the height to match that of the JTable.
     */
    public void setBounds(int x, int y, int w, int h) {
        super.setBounds(x, 0, w, JTreeTable.this.getHeight());
    }

    /**
     * Sublcassed to translate the graphics such that the last visible
     * row will be drawn at 0,0.
     */
    public void paint(Graphics g) {
        g.translate(0, -visibleRow * getRowHeight());
        super.paint(g);
    }

    /**
     * TreeCellRenderer method. Overridden to update the visible row.
     */
    public Component getTableCellRendererComponent(JTable table,
                               Object value,
                               boolean isSelected,
                               boolean hasFocus,
                               int row, int column) {
        if(isSelected)
        setBackground(table.getSelectionBackground());
        else
        setBackground(table.getBackground());

        visibleRow = row;
        return this;
    }
    }


    /**
     * TreeTableCellEditor implementation. Component returned is the
     * JTree.
     */
     class TreeTableCellEditor extends AbstractCellEditor implements
             TableCellEditor {
    public Component getTableCellEditorComponent(JTable table,
                             Object value,
                             boolean isSelected,
                             int r, int c) {
        return tree;
    }

    /**
     * Overridden to return false, and if the event is a mouse event
     * it is forwarded to the tree.<p>
     * The behavior for this is debatable, and should really be offered
     * as a property. By returning false, all keyboard actions are
     * implemented in terms of the table. By returning true, the
     * tree would get a chance to do something with the keyboard
     * events. For the most part this is ok. But for certain keys,
     * such as left/right, the tree will expand/collapse where as
     * the table focus should really move to a different column. Page
     * up/down should also be implemented in terms of the table.
     * By returning false this also has the added benefit that clicking
     * outside of the bounds of the tree node, but still in the tree
     * column will select the row, whereas if this returned true
     * that wouldn't be the case.
     * <p>By returning false we are also enforcing the policy that
     * the tree will never be editable (at least by a key sequence).
     */
    public boolean isCellEditable(EventObject e) {
        if (e instanceof MouseEvent) {
        for (int counter = getColumnCount() - 1; counter >= 0;
             counter--) {
            if (getColumnClass(counter) == TreeTableModel.class) {
            MouseEvent me = (MouseEvent)e;
            MouseEvent newME = new MouseEvent(tree, me.getID(),
                   me.getWhen(), me.getModifiers(),
                   me.getX() - getCellRect(0, counter, true).x,
                   me.getY(), me.getClickCount(),
                                   me.isPopupTrigger());
            tree.dispatchEvent(newME);
            break;
            }
        }
        }
        return false;
    }

    public Object getCellEditorValue() {
      // TODO Auto-generated method stub
      return null;
    }
    }


    /**
     * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
     * to listen for changes in the ListSelectionModel it maintains. Once
     * a change in the ListSelectionModel happens, the paths are updated
     * in the DefaultTreeSelectionModel.
     */
    class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel {
    /** Set to true when we are updating the ListSelectionModel. */
    protected boolean         updatingListSelectionModel;

    public ListToTreeSelectionModelWrapper() {
        super();
        getListSelectionModel().addListSelectionListener
                                (createListSelectionListener());
    }

    /**
     * Returns the list selection model. ListToTreeSelectionModelWrapper
     * listens for changes to this model and updates the selected paths
     * accordingly.
     */
    ListSelectionModel getListSelectionModel() {
        return listSelectionModel;
    }

    /**
     * This is overridden to set <code>updatingListSelectionModel</code>
     * and message super. This is the only place DefaultTreeSelectionModel
     * alters the ListSelectionModel.
     */
    public void resetRowSelection() {
        if(!updatingListSelectionModel) {
        updatingListSelectionModel = true;
        try {
            super.resetRowSelection();
        }
        finally {
            updatingListSelectionModel = false;
        }
        }
        // Notice how we don't message super if
        // updatingListSelectionModel is true. If
        // updatingListSelectionModel is true, it implies the
        // ListSelectionModel has already been updated and the
        // paths are the only thing that needs to be updated.
    }

    /**
     * Creates and returns an instance of ListSelectionHandler.
     */
    protected ListSelectionListener createListSelectionListener() {
        return new ListSelectionHandler();
    }

    /**
     * If <code>updatingListSelectionModel</code> is false, this will
     * reset the selected paths from the selected rows in the list
     * selection model.
     */
    protected void updateSelectedPathsFromSelectedRows() {
        if(!updatingListSelectionModel) {
        updatingListSelectionModel = true;
        try {
            // This is way expensive, ListSelectionModel needs an
            // enumerator for iterating.
            int        min = listSelectionModel.getMinSelectionIndex();
            int        max = listSelectionModel.getMaxSelectionIndex();

            clearSelection();
            if(min != -1 && max != -1) {
            for(int counter = min; counter <= max; counter++) {
                if(listSelectionModel.isSelectedIndex(counter)) {
                TreePath     selPath = tree.getPathForRow
                                            (counter);

                if(selPath != null) {
                    addSelectionPath(selPath);
                }
                }
            }
            }
        }
        finally {
            updatingListSelectionModel = false;
        }
        }
    }

    /**
     * Class responsible for calling updateSelectedPathsFromSelectedRows
     * when the selection of the list changse.
     */
    class ListSelectionHandler implements ListSelectionListener {
        public void valueChanged(ListSelectionEvent e) {
        updateSelectedPathsFromSelectedRows();
        }
    }
    }
}

/*
 * The contents of this file are subject to the Sapient Public License
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * http://carbon.sf.net/License.html.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is The Carbon Component Framework.
 *
 * The Initial Developer of the Original Code is Sapient Corporation
 *
 * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
 */


/*
 * TreeTableModel.java
 *
 * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 */

/**
 * TreeTableModel is the model used by a JTreeTable. It extends TreeModel
 * to add methods for getting inforamtion about the set of columns each
 * node in the TreeTableModel may have. Each column, like a column in
 * a TableModel, has a name and a type associated with it. Each node in
 * the TreeTableModel can return a value for each of the columns and
 * set that value if isCellEditable() returns true.
 *
 * @author Philip Milne
 * @author Scott Violet
 */
 interface TreeTableModel extends TreeModel
{
    /**
     * Returns the number ofs availible column.
     */
    public int getColumnCount();

    /**
     * Returns the name for column number <code>column</code>.
     */
    public String getColumnName(int column);

    /**
     * Returns the type for column number <code>column</code>.
     */
    public Class getColumnClass(int column);

    /**
     * Returns the value to be displayed for node <code>node</code>,
     * at column number <code>column</code>.
     */
    public Object getValueAt(Object node, int column);

    /**
     * Indicates whether the the value for node <code>node</code>,
     * at column number <code>column</code> is editable.
     */
    public boolean isCellEditable(Object node, int column);

    /**
     * Sets the value for node <code>node</code>,
     * at column number <code>column</code>.
     */
    public void setValueAt(Object aValue, Object node, int column);
}

 /*
  * The contents of this file are subject to the Sapient Public License
  * Version 1.0 (the "License"); you may not use this file except in compliance
  * with the License. You may obtain a copy of the License at
  * http://carbon.sf.net/License.html.
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  * the specific language governing rights and limitations under the License.
  *
  * The Original Code is The Carbon Component Framework.
  *
  * The Initial Developer of the Original Code is Sapient Corporation
  *
  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
  */


 /*
  * @(#)TreeTableModelAdapter.java    1.2 98/10/27
  *
  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  * All rights reserved.
  *
  * This software is the confidential and proprietary information
  * of Sun Microsystems, Inc. ("Confidential Information").  You
  * shall not disclose such Confidential Information and shall use
  * it only in accordance with the terms of the license agreement
  * you entered into with Sun.
  */


 /**
  * This is a wrapper class takes a TreeTableModel and implements
  * the table model interface. The implementation is trivial, with
  * all of the event dispatching support provided by the superclass:
  * the AbstractTableModel.
  *
  * @version 1.2 10/27/98
  *
  * @author Philip Milne
  * @author Scott Violet
  */
  class TreeTableModelAdapter extends AbstractTableModel
 {
     JTree tree;
     TreeTableModel treeTableModel;

     public TreeTableModelAdapter(TreeTableModel treeTableModel, JTree tree) {
         this.tree = tree;
         this.treeTableModel = treeTableModel;

     tree.addTreeExpansionListener(new TreeExpansionListener() {
         // Don't use fireTableRowsInserted() here; the selection model
         // would get updated twice.
         public void treeExpanded(TreeExpansionEvent event) {
           fireTableDataChanged();
         }
             public void treeCollapsed(TreeExpansionEvent event) {
           fireTableDataChanged();
         }
     });

     // Install a TreeModelListener that can update the table when
     // tree changes. We use delayedFireTableDataChanged as we can
     // not be guaranteed the tree will have finished processing
     // the event before us.
     treeTableModel.addTreeModelListener(new TreeModelListener() {
         public void treeNodesChanged(TreeModelEvent e) {
         delayedFireTableDataChanged();
         }

         public void treeNodesInserted(TreeModelEvent e) {
         delayedFireTableDataChanged();
         }

         public void treeNodesRemoved(TreeModelEvent e) {
         delayedFireTableDataChanged();
         }

         public void treeStructureChanged(TreeModelEvent e) {
         delayedFireTableDataChanged();
         }
     });
     }

     // Wrappers, implementing TableModel interface.

     public int getColumnCount() {
     return treeTableModel.getColumnCount();
     }

     public String getColumnName(int column) {
     return treeTableModel.getColumnName(column);
     }

     public Class getColumnClass(int column) {
     return treeTableModel.getColumnClass(column);
     }

     public int getRowCount() {
     return tree.getRowCount();
     }

     protected Object nodeForRow(int row) {
     TreePath treePath = tree.getPathForRow(row);
     return treePath.getLastPathComponent();
     }

     public Object getValueAt(int row, int column) {
     return treeTableModel.getValueAt(nodeForRow(row), column);
     }

     public boolean isCellEditable(int row, int column) {
          return treeTableModel.isCellEditable(nodeForRow(row), column);
     }

     public void setValueAt(Object value, int row, int column) {
     treeTableModel.setValueAt(value, nodeForRow(row), column);
     }

     /**
      * Invokes fireTableDataChanged after all the pending events have been
      * processed. SwingUtilities.invokeLater is used to handle this.
      */
     protected void delayedFireTableDataChanged() {
     SwingUtilities.invokeLater(new Runnable() {
         public void run() {
         fireTableDataChanged();
         }
     });
     }
 }
/*
 * The contents of this file are subject to the Sapient Public License
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * http://carbon.sf.net/License.html.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is The Carbon Component Framework.
 *
 * The Initial Developer of the Original Code is Sapient Corporation
 *
 * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
 */

package org.sape.carbon.services.swing.treetable;

/*
 * @(#)AbstractTreeTableModel.java    1.2 98/10/27
 *
 * Copyright 1997, 1998 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.
 */

import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;

/**
 * @version 1.2 10/27/98
 * An abstract implementation of the TreeTableModel interface, handling the list
 * of listeners.
 * @author Philip Milne
 */

public abstract class AbstractTreeTableModel implements TreeTableModel {
    protected Object root;
    protected EventListenerList listenerList = new EventListenerList();

    public AbstractTreeTableModel(Object root) {
        this.root = root;
    }

    //
    // Default implmentations for methods in the TreeModel interface.
    //

    public Object getRoot() {
        return root;
    }

    public boolean isLeaf(Object node) {
        return getChildCount(node) == 0;
    }

    public void valueForPathChanged(TreePath path, Object newValue) {}

    // This is not called in the JTree's default mode: use a naive implementation.
    public int getIndexOfChild(Object parent, Object child) {
        for (int i = 0; i < getChildCount(parent); i++) {
        if (getChild(parent, i).equals(child)) {
            return i;
        }
        }
    return -1;
    }

    public void addTreeModelListener(TreeModelListener l) {
        listenerList.add(TreeModelListener.class, l);
    }

    public void removeTreeModelListener(TreeModelListener l) {
        listenerList.remove(TreeModelListener.class, l);
    }

    /*
     * Notify all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created using the parameters passed into
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeNodesChanged(Object source, Object[] path,
                                        int[] childIndices,
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // 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]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path,
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
            }
        }
    }

    /*
     * Notify all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created using the parameters passed into
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeNodesInserted(Object source, Object[] path,
                                        int[] childIndices,
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // 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]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path,
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
            }
        }
    }

    /*
     * Notify all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created using the parameters passed into
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeNodesRemoved(Object source, Object[] path,
                                        int[] childIndices,
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // 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]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path,
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
            }
        }
    }

    /*
     * Notify all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created using the parameters passed into
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeStructureChanged(Object source, Object[] path,
                                        int[] childIndices,
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // 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]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path,
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
            }
        }
    }

    //
    // Default impelmentations for methods in the TreeTableModel interface.
    //

    public Class getColumnClass(int column) { return Object.class; }

   /** By default, make the column with the Tree in it the only editable one.
    *  Making this column editable causes the JTable to forward mouse
    *  and keyboard events in the Tree column to the underlying JTree.
    */
    public boolean isCellEditable(Object node, int column) {
         return getColumnClass(column) == TreeTableModel.class;
    }

    public void setValueAt(Object aValue, Object node, int column) {}


    // Left to be implemented in the subclass:

    /*
     *   public Object getChild(Object parent, int index)
     *   public int getChildCount(Object parent)
     *   public int getColumnCount()
     *   public String getColumnName(Object node, int column)
     *   public Object getValueAt(Object node, int column)
     */
}
/*
 * The contents of this file are subject to the Sapient Public License
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * http://carbon.sf.net/License.html.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is The Carbon Component Framework.
 *
 * The Initial Developer of the Original Code is Sapient Corporation
 *
 * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
 */


import java.util.EventObject;

import javax.swing.CellEditor;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;

public class AbstractCellEditor implements CellEditor {

    protected EventListenerList listenerList = new EventListenerList();

    public Object getCellEditorValue() { return null; }
    public boolean isCellEditable(EventObject e) { return true; }
    public boolean shouldSelectCell(EventObject anEvent) { return false; }
    public boolean stopCellEditing() { return true; }
    public void cancelCellEditing() {}

    public void addCellEditorListener(CellEditorListener l) {
    listenerList.add(CellEditorListener.class, l);
    }

    public void removeCellEditorListener(CellEditorListener l) {
    listenerList.remove(CellEditorListener.class, l);
    }

    /*
     * Notify all listeners that have registered interest for
     * notification on this event type.
     * @see EventListenerList
     */
    protected void fireEditingStopped() {
    // 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]==CellEditorListener.class) {
        ((CellEditorListener)listeners[i+1]).editingStopped(new ChangeEvent(this));
        }
    }
    }

    /*
     * Notify all listeners that have registered interest for
     * notification on this event type.
     * @see EventListenerList
     */
    protected void fireEditingCanceled() {
    // 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]==CellEditorListener.class) {
        ((CellEditorListener)listeners[i+1]).editingCanceled(new ChangeEvent(this));
        }
    }
    }
}
Auto Complete TextField

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;

import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.basic.BasicComboBoxEditor;

/**
 * A JTextField that will display a whole word as the user types a portion of
 * it. A better example would be, as the user types the first letter in the
 * word "text" the AutoCompleteTextField will display the letter "t" in the
 * components current foreground color and display the rest of the word "ext"
 * in the specified incomplete color (default is Color.GRAY.brighter()). The
 * displayed text changes as the user types "e" so now getText() would return
 * the string "te" and the incomplete portion displayed by the editor will be
 * "xt." If Enter is pressed while an incomplete word is being processes, then
 * the AutoCompleteTextField will replace what he user has typed with the
 * completed word and consider itself "finished," and if Enter is pressed a
 * second time, then it will fire a KeyEvent normally.
 * @author Brandon Buck
 * @version 1.0
 */
public class AutoCompleteTextField extends JTextField implements KeyListener,
            DocumentListener {
    private ArrayList<String> possibilities;
    private int currentGuess;
    private Color incompleteColor;
    private boolean areGuessing;
    private boolean caseSensitive;

    /**
     * Constructs a new AutoCompleteTextField with the 5 columns by default and
     * no case sensitivity on comparisons of guesses to entered text.
     */
    public AutoCompleteTextField() {
        this(5, false);
    }

    /**
     * Constructs a new AutoCompleteTextField with the specified number of
     * columns with no case sensitivity when comparing the current guess to the
     * text entered.
     * @param columns The number of columns you wish for the width of this AutoCompleteTextField.
     */
    public AutoCompleteTextField(int columns) {
        this(columns, false);
    }

    /**
     * Creates a new AutoCompleteTextField with the given number of columns in
     * width and the setting for case sensitivity when comparing the current
     * guess to the entered text.
     * @param columns The number of columns of text for this component.
     * @param caseSensitive <code>true</code> or <code>false</code> depending on if you want comparisons to be case sensitive or not.
     */
    public AutoCompleteTextField(int columns, boolean caseSensitive) {
        super.setColumns(columns);
        this.possibilities = new ArrayList<String>();
        this.incompleteColor = Color.GRAY.brighter();
        this.currentGuess = -1;
        this.areGuessing = false;
        this.caseSensitive = caseSensitive;
        this.addKeyListener(this);
        this.getDocument().addDocumentListener(this);
    }

    /** Add a new possibility to the list of possibilities.
     * Add a new possibility to the list of possibilities for the
     * AutoCommpleteTextField to process.
     * @param possibility The new possibility to add.
     */
    public void addPossibility(String possibility) {
        this.possibilities.add(possibility);
        Collections.sort(possibilities);
    }

    /** Removes a possibility from the list of possibilities.
     * Removes the given possibility from the list of possibilities so that it
     * will no longer be matched.
     * @param possibility The possibility to remove.
     */
    public void removePossibility(String possibility) {
        this.possibilities.remove(possibility);
    }

    /** Removes all possibilities in the list.
     * Removes every possibility in the list and starts over with an empty list
     * ready for new possibilities to be added.
     */
    public void removeAllPossibilities() {
        this.possibilities = new ArrayList<String>();
    }

    /** Sets the color to draw the incomplete guess in.
     * This sets the color that the incomplete guess text is drawn in.
     * @param incompleteColor The new color to draw the incomplete guess with.
     */
    public void setIncompleteColor(Color incompleteColor) {
        this.incompleteColor = incompleteColor;
    }

    /** Returns the current guess from the list of possibilities.
     * Returns the string at the location of the current guess in the list of 
     * possibilities.
     * @return The current guess as a String.
     */
    private String getCurrentGuess() {
        if (this.currentGuess != -1)
            return this.possibilities.get(this.currentGuess);

        return this.getText();
    }

    /**
     * Changes the current case sensitive setting to the given setting.
     * @param caseSensitive <code>true</code> or <code>false</code> depending on if you want comparisons to be case sensitive or not.
     */
    public void setCaseSensitive(boolean caseSensitive) {
        this.caseSensitive = caseSensitive;
    }

    private void findCurrentGuess() {
        String entered = this.getText();
        if (!this.caseSensitive)
            entered = entered.toLowerCase();

        for (int i = 0; i < this.possibilities.size(); i++) {
            currentGuess = -1;

            String possibility = this.possibilities.get(i);
            if (!this.caseSensitive)
                possibility = possibility.toLowerCase();
            if (possibility.startsWith(entered)) {
                this.currentGuess = i;
                break;
            }
        }
    }

    @Override
    public void setText(String text) {
        super.setText(text);
        this.areGuessing = false;
        this.currentGuess = -1;
    }

    @Override
    public void paintComponent(Graphics g) {
        String guess = this.getCurrentGuess();
        String drawGuess = guess;

        super.paintComponent(g);
        String entered = this.getText();
        Rectangle2D enteredBounds = g.getFontMetrics().getStringBounds(entered, g);

        if (!(this.caseSensitive)) {
            entered = entered.toLowerCase();
            guess = guess.toLowerCase();
        }
        
        if (!(guess.startsWith(entered)))
            this.areGuessing = false;

        if (entered != null && !(entered.equals("")) 
                && this.areGuessing) {
            String subGuess = drawGuess.substring(entered.length(), drawGuess.length());
            Rectangle2D subGuessBounds = g.getFontMetrics().getStringBounds(drawGuess, g);

            int centeredY = ((getHeight() / 2) + (int)(subGuessBounds.getHeight() / 2));

            g.setColor(this.incompleteColor);
            g.drawString(subGuess + "   press ENTER to send or \u2192 to fill", (int)(enteredBounds.getWidth()) + 2, centeredY - 2);
        }
    }

    public void keyTyped(KeyEvent e) { }

    public void keyPressed(KeyEvent e) { 
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            if (this.areGuessing) {
                this.setText(this.getCurrentGuess());
                this.areGuessing = false;
            }
        }

        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            if (this.areGuessing) {
                this.setText(this.getCurrentGuess());
                this.areGuessing = false;
                e.consume();
            }
        }
    }
    
    public void keyReleased(KeyEvent e) { }

    public void insertUpdate(DocumentEvent e) {
        String temp = this.getText();

        if (temp.length() == 1)
            this.areGuessing = true;

        if (this.areGuessing)
            this.findCurrentGuess();

    }

    public void removeUpdate(DocumentEvent e) {
        String temp = this.getText();

        if (!(this.areGuessing))
            this.areGuessing = true;

        if (temp.length() == 0)
            this.areGuessing = false;
        else if (this.areGuessing) {
            this.findCurrentGuess();
        }
    }

    public void changedUpdate(DocumentEvent e) { }
}

   

A button which paints on it one or more scaled arrows in one of the cardinal directions

   
//Revised from greef ui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics;

import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;

/**
 * A button which paints on it one or more scaled arrows in one of the cardinal directions.
 * @author Adrian BER
 */
public class ArrowIcon implements Icon {

    /** The cardinal direction of the arrow(s). */
    private int direction;

    /** The number of arrows. */
    private int arrowCount;

    /** The arrow size. */
    private int arrowSize;

    public ArrowIcon(int direction, int arrowCount, int arrowSize) {
        this.direction = direction;
        this.arrowCount = arrowCount;
        this.arrowSize = arrowSize;
    }

    /** Returns the cardinal direction of the arrow(s).
     * @see #setDirection(int)
     */
    public int getDirection() {
        return direction;
    }

    /** Sets the cardinal direction of the arrow(s).
     * @param direction the direction of the arrow(s), can be SwingConstants.NORTH,
     * SwingConstants.SOUTH, SwingConstants.WEST or SwingConstants.EAST
     * @see #getDirection()
     */
    public void setDirection(int direction) {
        this.direction = direction;
    }

    /** Returns the number of arrows. */
    public int getArrowCount() {
        return arrowCount;
    }

    /** Sets the number of arrows. */
    public void setArrowCount(int arrowCount) {
        this.arrowCount = arrowCount;
    }

    /** Returns the arrow size. */
    public int getArrowSize() {
        return arrowSize;
    }

    /** Sets the arrow size. */
    public void setArrowSize(int arrowSize) {
        this.arrowSize = arrowSize;
    }

    public void paintIcon(Component c, Graphics g, int x, int y) {
        // paint the arrows
        int w = getIconWidth();
        int h = getIconHeight();
        for (int i = 0; i < arrowCount; i++) {
            paintArrow(g,
                    (x + w - arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? arrowCount : 1)) / 2
                            + arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? i : 0),
                    (y + h - arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? 1 : arrowCount)) / 2
                            + arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? 0 : i)
                    );
        }
    }

    public int getIconWidth() {
        return arrowSize * (direction == SwingConstants.EAST
                || direction == SwingConstants.WEST ? arrowCount : 3);
    }

    public int getIconHeight() {
        return arrowSize * (direction == SwingConstants.NORTH
                || direction == SwingConstants.SOUTH ? arrowCount : 3);
    }

    private void paintArrow(Graphics g, int x, int y) {
        int mid, i, j;

        j = 0;
        arrowSize = Math.max(arrowSize, 2);
        mid = (arrowSize / 2) - 1;

        g.translate(x, y);

        switch (direction) {
            case SwingConstants.NORTH:
                for (i = 0; i < arrowSize; i++) {
                    g.drawLine(mid - i, i, mid + i, i);
                }
                break;
            case SwingConstants.SOUTH:
                j = 0;
                for (i = arrowSize - 1; i >= 0; i--) {
                    g.drawLine(mid - i, j, mid + i, j);
                    j++;
                }
                break;
            case SwingConstants.WEST:
                for (i = 0; i < arrowSize; i++) {
                    g.drawLine(i, mid - i, i, mid + i);
                }
                break;
            case SwingConstants.EAST:
                j = 0;
                for (i = arrowSize - 1; i >= 0; i--) {
                    g.drawLine(j, mid - i, j, mid + i);
                    j++;
                }
                break;
        }

        g.translate(-x, -y);
    }
}

Arrow Button

   


import javax.swing.*;
import java.awt.*;

/**
 * A button which paints on it one or more scaled arrows in one of the cardinal directions.
 * @author Adrian BER
 */
public class ArrowButton extends JButton {

    /** The cardinal direction of the arrow(s). */
    private int direction;

    /** The number of arrows. */
    private int arrowCount;

    /** The arrow size. */
    private int arrowSize;

    public ArrowButton(int direction, int arrowCount, int arrowSize) {
        setMargin(new Insets(0, 2, 0, 2));
        setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
        this.direction = direction;
        this.arrowCount = arrowCount;
        this.arrowSize = arrowSize;
    }

    /** Returns the cardinal direction of the arrow(s).
     * @see #setDirection(int)
     */
    public int getDirection() {
        return direction;
    }

    /** Sets the cardinal direction of the arrow(s).
     * @param direction the direction of the arrow(s), can be SwingConstants.NORTH,
     * SwingConstants.SOUTH, SwingConstants.WEST or SwingConstants.EAST
     * @see #getDirection()
     */
    public void setDirection(int direction) {
        this.direction = direction;
    }

    /** Returns the number of arrows. */
    public int getArrowCount() {
        return arrowCount;
    }

    /** Sets the number of arrows. */
    public void setArrowCount(int arrowCount) {
        this.arrowCount = arrowCount;
    }

    /** Returns the arrow size. */
    public int getArrowSize() {
        return arrowSize;
    }

    /** Sets the arrow size. */
    public void setArrowSize(int arrowSize) {
        this.arrowSize = arrowSize;
    }

    public Dimension getPreferredSize() {
        return getMinimumSize();
    }

    public Dimension getMinimumSize() {
        return new Dimension(
                arrowSize * (direction == SwingConstants.EAST
                        || direction == SwingConstants.WEST ? arrowCount : 3)
                + getBorder().getBorderInsets(this).left
                + getBorder().getBorderInsets(this).right
                ,
                arrowSize * (direction == SwingConstants.NORTH
                        || direction == SwingConstants.SOUTH ? arrowCount : 3)
                + getBorder().getBorderInsets(this).top
                + getBorder().getBorderInsets(this).bottom
                );
    }

    public Dimension getMaximumSize() {
        return getMinimumSize();
    }

    protected void paintComponent(Graphics g) {
        // this will paint the background
        super.paintComponent(g);

        Color oldColor = g.getColor();
        g.setColor(isEnabled() ? getForeground() : getForeground().brighter());

        // paint the arrows
        int w = getSize().width;
        int h = getSize().height;
        for (int i = 0; i < arrowCount; i++) {
            paintArrow(g,
                    (w - arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? arrowCount : 1)) / 2
                            + arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? i : 0),
                    (h - arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? 1 : arrowCount)) / 2
                            + arrowSize * (direction == SwingConstants.EAST
                            || direction == SwingConstants.WEST ? 0 : i),
                            g.getColor());
        }

        g.setColor(oldColor);
    }

    private void paintArrow(Graphics g, int x, int y, Color highlight) {
        int mid, i, j;

        Color oldColor = g.getColor();
        boolean isEnabled = isEnabled();

        j = 0;
        arrowSize = Math.max(arrowSize, 2);
        mid = (arrowSize / 2) - 1;

        g.translate(x, y);

        switch (direction) {
            case NORTH:
                for (i = 0; i < arrowSize; i++) {
                    g.drawLine(mid - i, i, mid + i, i);
                }
                if(!isEnabled)  {
                    g.setColor(highlight);
                    g.drawLine(mid-i+2, i, mid+i, i);
                }
                break;
            case SOUTH:
                if (!isEnabled) {
                    g.translate(1, 1);
                    g.setColor(highlight);
                    for (i = arrowSize - 1; i >= 0; i--) {
                        g.drawLine(mid - i, j, mid + i, j);
                        j++;
                    }
                    g.translate(-1, -1);
                    g.setColor(oldColor);
                }
                j = 0;
                for (i = arrowSize - 1; i >= 0; i--) {
                    g.drawLine(mid - i, j, mid + i, j);
                    j++;
                }
                break;
            case WEST:
                for (i = 0; i < arrowSize; i++) {
                    g.drawLine(i, mid - i, i, mid + i);
                }
                if(!isEnabled)  {
                    g.setColor(highlight);
                    g.drawLine(i, mid-i+2, i, mid+i);
                }
                break;
            case EAST:
                if(!isEnabled)  {
                    g.translate(1, 1);
                    g.setColor(highlight);
                    for(i = arrowSize-1; i >= 0; i--)   {
                        g.drawLine(j, mid-i, j, mid+i);
                        j++;
                    }
                    g.translate(-1, -1);
                    g.setColor(oldColor);
                }
                j = 0;
                for (i = arrowSize - 1; i >= 0; i--) {
                    g.drawLine(j, mid - i, j, mid + i);
                    j++;
                }
                break;
        }

        g.translate(-x, -y);
        g.setColor(oldColor);
    }
}
The various table charting classes
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

public class ChartTester extends JFrame {

  public ChartTester() {
    super("Simple JTable Test");
    setSize(300, 200);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    TableModel tm = new AbstractTableModel() {
      String data[][] = { { "Ron", "0.00", "68.68", "77.34", "78.02" },
          { "Ravi", "0.00", "70.89", "64.17", "75.00" },
          { "Maria", "76.52", "71.12", "75.68", "74.14" },
          { "James", "70.00", "15.72", "26.40", "38.32" },
          { "Ellen", "80.32", "78.16", "83.80", "85.72" } };

      String headers[] = { "", "Q1", "Q2", "Q3", "Q4" };

      public int getColumnCount() {
        return headers.length;
      }

      public int getRowCount() {
        return data.length;
      }

      public String getColumnName(int col) {
        return headers[col];
      }

      public Class getColumnClass(int col) {
        return (col == 0) ? String.class : Number.class;
      }

      public boolean isCellEditable(int row, int col) {
        return true;
      }

      public Object getValueAt(int row, int col) {
        return data[row][col];
      }

      public void setValueAt(Object value, int row, int col) {
        data[row][col] = (String) value;
        fireTableRowsUpdated(row, row);
      }
    };

    JTable jt = new JTable(tm);
    JScrollPane jsp = new JScrollPane(jt);
    getContentPane().add(jsp, BorderLayout.CENTER);

    final TableChartPopup tcp = new TableChartPopup(tm);
    JButton button = new JButton("Show me a chart of this table");
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        tcp.setVisible(true);
      }
    });
    getContentPane().add(button, BorderLayout.SOUTH);
  }

  public static void main(String args[]) {
    ChartTester ct = new ChartTester();
    ct.setVisible(true);
  }
}

//TableChartPopup.java
//A popup framework for showing a chart of table data. This class also
//turns on tooltips for the chart.
//

class TableChartPopup extends JFrame {

  public TableChartPopup(TableModel tm) {
    super("Table Chart");
    setSize(300, 200);
    TableChart tc = new TableChart(tm);
    getContentPane().add(tc, BorderLayout.CENTER);

    // Use the next line to turn on tooltips:
    ToolTipManager.sharedInstance().registerComponent(tc);
  }
}

//TableChart.java
//A chart-generating class that uses the TableModel interface to get
//its data.
//

class TableChart extends JComponent implements TableModelListener {

  protected TableModel model;

  protected ChartPainter cp;

  protected double[] percentages; // pie slices

  protected String[] labels; // labels for slices

  protected String[] tips; // tooltips for slices

  protected java.text.NumberFormat formatter = java.text.NumberFormat
      .getPercentInstance();

  public TableChart(TableModel tm) {
    setUI(cp = new PieChartPainter());
    setModel(tm);
  }

  public void setTextFont(Font f) {
    cp.setTextFont(f);
  }

  public Font getTextFont() {
    return cp.getTextFont();
  }

  public void setTextColor(Color c) {
    cp.setTextColor(c);
  }

  public Color getTextColor() {
    return cp.getTextColor();
  }

  public void setColor(Color[] clist) {
    cp.setColor(clist);
  }

  public Color[] getColor() {
    return cp.getColor();
  }

  public void setColor(int index, Color c) {
    cp.setColor(index, c);
  }

  public Color getColor(int index) {
    return cp.getColor(index);
  }

  public String getToolTipText(MouseEvent me) {
    if (tips != null) {
      int whichTip = cp.indexOfEntryAt(me);
      if (whichTip != -1) {
        return tips[whichTip];
      }
    }
    return null;
  }

  public void tableChanged(TableModelEvent tme) {
    // Rebuild the arrays only if the structure changed.
    updateLocalValues(tme.getType() != TableModelEvent.UPDATE);
  }

  public void setModel(TableModel tm) {
    // get listener code correct.
    if (tm != model) {
      if (model != null) {
        model.removeTableModelListener(this);
      }
      model = tm;
      model.addTableModelListener(this);
      updateLocalValues(true);
    }
  }

  public TableModel getModel() {
    return model;
  }

  // Run through the model and count every cell (except the very first column,
  // which we assume is the slice label column).
  protected void calculatePercentages() {
    double runningTotal = 0.0;
    for (int i = model.getRowCount() - 1; i >= 0; i--) {
      percentages[i] = 0.0;
      for (int j = model.getColumnCount() - 1; j >= 0; j--) {

        // First try the cell as a Number object.
        Object val = model.getValueAt(i, j);
        if (val instanceof Number) {
          percentages[i] += ((Number) val).doubleValue();
        } else if (val instanceof String) {
          // oops, it wasn't numeric...
          // Ok, so try it as a string
          try {
            percentages[i] += Double.valueOf(val.toString())
                .doubleValue();
          } catch (Exception e) {
            // not a numeric string...give up.
          }
        }
      }
      runningTotal += percentages[i];
    }

    // Make each entry a percentage of the total.
    for (int i = model.getRowCount() - 1; i >= 0; i--) {
      percentages[i] /= runningTotal;
    }
  }

  // This method just takes the percentages and formats them for use as
  // tooltips.
  protected void createLabelsAndTips() {
    for (int i = model.getRowCount() - 1; i >= 0; i--) {
      labels[i] = (String) model.getValueAt(i, 0);
      tips[i] = formatter.format(percentages[i]);
    }
  }

  // Call this method to update the chart. We try to be a bit efficient here
  // in that we allocate new storage arrays only if the new table has a
  // different number of rows.
  protected void updateLocalValues(boolean freshStart) {
    if (freshStart) {
      int count = model.getRowCount();
      if ((tips == null) || (count != tips.length)) {
        percentages = new double[count];
        labels = new String[count];
        tips = new String[count];
      }
    }
    calculatePercentages();
    createLabelsAndTips();

    // Now that everything's up-to-date, reset the chart painter with the
    // new
    // values.
    cp.setValues(percentages);
    cp.setLabels(labels);

    // Finally, repaint the chart.
    repaint();
  }
}

//PieChartPainter.java
//A pie chart implementation of the ChartPainter class.
//

class PieChartPainter extends ChartPainter {

  protected static PieChartPainter chartUI = new PieChartPainter();

  protected int originX, originY;

  protected int radius;

  private static double piby2 = Math.PI / 2.0;

  private static double twopi = Math.PI * 2.0;

  private static double d2r = Math.PI / 180.0; // Degrees to radians.

  private static int xGap = 5;

  private static int inset = 40;

  public int indexOfEntryAt(MouseEvent me) {
    int x = me.getX() - originX;
    int y = originY - me.getY(); // Upside-down coordinate system.

    // Is (x,y) in the circle?
    if (Math.sqrt(x * x + y * y) > radius) {
      return -1;
    }

    double percent = Math.atan2(Math.abs(y), Math.abs(x));
    if (x >= 0) {
      if (y <= 0) { // (IV)
        percent = (piby2 - percent) + 3 * piby2; // (IV)
      }
    } else {
      if (y >= 0) { // (II)
        percent = Math.PI - percent;
      } else { // (III)
        percent = Math.PI + percent;
      }
    }
    percent /= twopi;
    double t = 0.0;
    if (values != null) {
      for (int i = 0; i < values.length; i++) {
        if (t + values[i] > percent) {
          return i;
        }
        t += values[i];
      }
    }
    return -1;
  }

  public void paint(Graphics g, JComponent c) {
    Dimension size = c.getSize();
    originX = size.width / 2;
    originY = size.height / 2;
    int diameter = (originX < originY ? size.width - inset : size.height
        - inset);
    radius = (diameter / 2) + 1;
    int cornerX = (originX - (diameter / 2));
    int cornerY = (originY - (diameter / 2));

    int startAngle = 0;
    int arcAngle = 0;
    for (int i = 0; i < values.length; i++) {
      arcAngle = (int) (i < values.length - 1 ? Math
          .round(values[i] * 360) : 360 - startAngle);
      g.setColor(colors[i % colors.length]);
      g.fillArc(cornerX, cornerY, diameter, diameter, startAngle,
          arcAngle);
      drawLabel(g, labels[i], startAngle + (arcAngle / 2));
      startAngle += arcAngle;
    }
    g.setColor(Color.black);
    g.drawOval(cornerX, cornerY, diameter, diameter); // Cap the circle.
  }

  public void drawLabel(Graphics g, String text, double angle) {
    g.setFont(textFont);
    g.setColor(textColor);
    double radians = angle * d2r;
    int x = (int) ((radius + xGap) * Math.cos(radians));
    int y = (int) ((radius + xGap) * Math.sin(radians));
    if (x < 0) {
      x -= SwingUtilities.computeStringWidth(g.getFontMetrics(), text);
    }
    if (y < 0) {
      y -= g.getFontMetrics().getHeight();
    }
    g.drawString(text, x + originX, originY - y);
  }

  public static ComponentUI createUI(JComponent c) {
    return chartUI;
  }
}

//ChartPainter.java
//A simple chart-drawing UI base class. This class tracks the basic fonts
//and colors for various types of charts including pie and bar. The paint()
//method is abstract and must be implemented by subclasses for each type.
//

abstract class ChartPainter extends ComponentUI {

  protected Font textFont = new Font("Serif", Font.PLAIN, 12);

  protected Color textColor = Color.black;

  protected Color colors[] = new Color[] { Color.red, Color.blue,
      Color.yellow, Color.black, Color.green, Color.white, Color.gray,
      Color.cyan, Color.magenta, Color.darkGray };

  protected double values[] = new double[0];

  protected String labels[] = new String[0];

  public void setTextFont(Font f) {
    textFont = f;
  }

  public Font getTextFont() {
    return textFont;
  }

  public void setColor(Color[] clist) {
    colors = clist;
  }

  public Color[] getColor() {
    return colors;
  }

  public void setColor(int index, Color c) {
    colors[index] = c;
  }

  public Color getColor(int index) {
    return colors[index];
  }

  public void setTextColor(Color c) {
    textColor = c;
  }

  public Color getTextColor() {
    return textColor;
  }

  public void setLabels(String[] l) {
    labels = l;
  }

  public void setValues(double[] v) {
    values = v;
  }

  public abstract int indexOfEntryAt(MouseEvent me);

  public abstract void paint(Graphics g, JComponent c);
}
A panel from where you can choose a color based on it's HTML name
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.colorchooser.AbstractColorChooserPanel;

/**
 * This is a panel from where you can choose a color based on it's HTML name.
 * This panel was designed to be integrated in JColorChooser component.
 * <blockquote><code>
 * JColorChooser colorChooser = new JColorChooser();
 * colorChooser.addChooserPanel(new HTMLColorsChooserPanel());
 * </code></blockquote>
 * @author Adrian Ber
 * @see HTMLColors
 */
public class HTMLColorsChooserPanel extends AbstractColorChooserPanel {

    /** The name of this color chooser panel; will appear as the tab name
     *  in the color chooser
     */
    private String displayName;
    /** The name displayed for a color without a HTML name */
    private String nonHTMLColorDisplayName;

    /** the color chooser in which is included this panel. */
    private JColorChooser parent = null;

    /** The combo box filled with the HTML color names */
    private JComboBox colorsComboBox = new JComboBox();

    /** The label in front of the HTML color combo box. */
    private JLabel colorLabel;

    public HTMLColorsChooserPanel() {
        this("HTML Names", "<Custom>");
    }

    public HTMLColorsChooserPanel(String displayName) {
        this(displayName, "<Custom>");
    }

    public HTMLColorsChooserPanel(String displayName, String nonHTMlColorDisplayName) {
        this.displayName = displayName;
        this.nonHTMLColorDisplayName = nonHTMlColorDisplayName;
        buildChooser();
    }

    public Icon getSmallDisplayIcon() {
        return null;
    }

    public void updateChooser() {
        if (parent != null) {
            String x = HTMLColors.getName(getColorFromModel());
            if (x == null) x = nonHTMLColorDisplayName;
            colorsComboBox.setSelectedItem(x);
        }
    }

    public String getDisplayName() {
        return displayName;
    }

    public Icon getLargeDisplayIcon() {
        return null;
    }

    /** Initializes this color chooser components. */
    protected void buildChooser() {
        setLayout(new BorderLayout());
        colorsComboBox.addItem(nonHTMLColorDisplayName);
        for (String color : HTMLColors.colors()) {
            colorsComboBox.addItem(color);
        }
        colorsComboBox.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent ev) {
                if (parent == null) return;
                Object name = colorsComboBox.getSelectedItem();
                if ((name != null) && (!name.toString().equals(nonHTMLColorDisplayName))) {
                    parent.setColor(HTMLColors.getColor(name.toString()));
                }
            }
        });
        colorLabel = new JLabel(getColorLabelText());
        add(colorLabel, BorderLayout.WEST);
        add(colorsComboBox, BorderLayout.CENTER);
    }

    private String getColorLabelText() {
        String x = UIManager.getString("HTMLColorsChooserPanel.colorLabel");
        if (x == null) {
            x = "HTML Color:";
        }
        return x;
    }

    public void installChooserPanel(JColorChooser enclosingChooser) {
        parent = enclosingChooser;
        super.installChooserPanel(enclosingChooser);
    }

    public void updateUI() {
        super.updateUI();
        if (colorLabel != null)
            colorLabel.setText(getColorLabelText());
    }
}
class HTMLColors {

  /** Don't instantiate this, use only the static methods */
  private HTMLColors() {
  }

  /** map between color names and colors;
   * tough there are fields for every color we use a map because is a faster
   * way to get the color
   */
  private static Map<String, Color> name2color = new HashMap<String, Color>();
  /** map between colors and color names;
   * tough there are fields for every color we use a map because is a faster
   * way to get the color
   */
  private static Map<Color, String> color2name = new HashMap<Color, String>();

  /** Initialiase colors map */
  private static void initColorsMap() {
      Field[] fields = HTMLColors.class.getFields();
      for (Field field : fields) {
          if (field.getType().isAssignableFrom(Color.class)) {
              addColor(field.getName());
          }
      }
  }

  /** Used to initialize the map */
  private static void addColor(String colorName, Color color) {
      name2color.put(colorName, color);
      color2name.put(color, colorName);
  }

  /** Used to initialize the map */
  private static void addColor(String colorName) {
      addColor(colorName, getColorFromField(colorName));
  }

  /** Used to initialize the map */
  private static void addColor(String colorName, int colorRGB) {
      addColor(colorName, new Color(colorRGB));
  }

  /** Returns a color with the specified case-insensitive name. */
  private static Color getColorFromField(String name) {
      try {
          Field colorField = HTMLColors.class.getField(name.toLowerCase());
          return (Color) colorField.get(HTMLColors.class);
      }
      catch (NoSuchFieldException exc) {
      }
      catch (SecurityException exc) {
      }
      catch (IllegalAccessException exc) {
      }
      catch (IllegalArgumentException exc) {
      }
      return null;
  }

  /** Returns a color with the specified case-insensitive name.*/
  public static String getName(Color color) {
      return color2name.get(color);
  }

  /** Returns a color with the specified case-insensitive name.*/
  public static Color getColor(String name) {
      return name2color.get(name.toLowerCase());
  }

  /** Returns a collection of all color names */
  public static Collection<String> colors() {
      return name2color.keySet();
  }

  /** Transform a color string into a color object.
   *  @param s the color string
   *  @return the color object
   */
  public static Color decodeColor(String s) {
      if (s == null)
          return null;
      Color c;
      try {
          c = Color.decode(s);
      }
      catch (NumberFormatException exc) {
          c = HTMLColors.getColor(s);
      }
      return c;
  }

  public static final Color aliceblue = new Color(0xf0f8ff);
  public static final Color antiquewhite = new Color(0xfaebd7);
  public static final Color aqua = new Color(0x00ffff);
  public static final Color aquamarine = new Color(0x7fffd4);
  public static final Color azure = new Color(0xf0ffff);
  public static final Color beige = new Color(0xf5f5dc);
  public static final Color bisque = new Color(0xffe4c4);
  public static final Color black = new Color(0x000000);
  public static final Color blanchedalmond = new Color(0xffebcd);
  public static final Color blue = new Color(0x0000ff);
  public static final Color blueviolet = new Color(0x8a2be2);
  public static final Color brown = new Color(0xa52a2a);
  public static final Color burlywood = new Color(0xdeb887);
  public static final Color cadetblue = new Color(0x5f9ea0);
  public static final Color chartreuse = new Color(0x7fff00);
  public static final Color chocolate = new Color(0xd2691e);
  public static final Color coral = new Color(0xff7f50);
  public static final Color cornflowerblue = new Color(0x6495ed);
  public static final Color cornsilk = new Color(0xfff8dc);
  public static final Color crimson = new Color(0xdc143c);
  public static final Color cyan = new Color(0x00ffff);
  public static final Color darkblue = new Color(0x00008b);
  public static final Color darkcyan = new Color(0x008b8b);
  public static final Color darkgoldenrod = new Color(0xb8860b);
  public static final Color darkgray = new Color(0xa9a9a9);
  public static final Color darkgreen = new Color(0x006400);
  public static final Color darkkhaki = new Color(0xbdb76b);
  public static final Color darkmagenta = new Color(0x8b008b);
  public static final Color darkolivegreen = new Color(0x556b2f);
  public static final Color darkorange = new Color(0xff8c00);
  public static final Color darkorchid = new Color(0x9932cc);
  public static final Color darkred = new Color(0x8b0000);
  public static final Color darksalmon = new Color(0xe9967a);
  public static final Color darkseagreen = new Color(0x8fbc8f);
  public static final Color darkslateblue = new Color(0x483d8b);
  public static final Color darkslategray = new Color(0x2f4f4f);
  public static final Color darkturquoise = new Color(0x00ced1);
  public static final Color darkviolet = new Color(0x9400d3);
  public static final Color deeppink = new Color(0xff1493);
  public static final Color deepskyblue = new Color(0x00bfff);
  public static final Color dimgray = new Color(0x696969);
  public static final Color dodgerblue = new Color(0x1e90ff);
  public static final Color firebrick = new Color(0xb22222);
  public static final Color floralwhite = new Color(0xfffaf0);
  public static final Color forestgreen = new Color(0x228b22);
  public static final Color fuchsia = new Color(0xff00ff);
  public static final Color gainsboro = new Color(0xdcdcdc);
  public static final Color ghostwhite = new Color(0xf8f8ff);
  public static final Color gold = new Color(0xffd700);
  public static final Color goldenrod = new Color(0xdaa520);
  public static final Color gray = new Color(0x808080);
  public static final Color green = new Color(0x008000);
  public static final Color greenyellow = new Color(0xadff2f);
  public static final Color honeydew = new Color(0xf0fff0);
  public static final Color hotpink = new Color(0xff69b4);
  public static final Color indianred = new Color(0xcd5c5c);
  public static final Color indigo = new Color(0x4b0082);
  public static final Color ivory = new Color(0xfffff0);
  public static final Color khaki = new Color(0xf0e68c);
  public static final Color lavender = new Color(0xe6e6fa);
  public static final Color lavenderblush = new Color(0xfff0f5);
  public static final Color lawngreen = new Color(0x7cfc00);
  public static final Color lemonchiffon = new Color(0xfffacd);
  public static final Color lightblue = new Color(0xadd8e6);
  public static final Color lightcoral = new Color(0xf08080);
  public static final Color lightcyan = new Color(0xe0ffff);
  public static final Color lightgoldenrodyellow = new Color(0xfafad2);
  public static final Color lightgreen = new Color(0x90ee90);
  public static final Color lightgrey = new Color(0xd3d3d3);
  public static final Color lightpink = new Color(0xffb6c1);
  public static final Color lightsalmon = new Color(0xffa07a);
  public static final Color lightseagreen = new Color(0x20b2aa);
  public static final Color lightskyblue = new Color(0x87cefa);
  public static final Color lightslategray = new Color(0x778899);
  public static final Color lightsteelblue = new Color(0xb0c4de);
  public static final Color lightyellow = new Color(0xffffe0);
  public static final Color lime = new Color(0x00ff00);
  public static final Color limegreen = new Color(0x32cd32);
  public static final Color linen = new Color(0xfaf0e6);
  public static final Color magenta = new Color(0xff00ff);
  public static final Color maroon = new Color(0x800000);
  public static final Color mediumaquamarine = new Color(0x66cdaa);
  public static final Color mediumblue = new Color(0x0000cd);
  public static final Color mediumorchid = new Color(0xba55d3);
  public static final Color mediumpurple = new Color(0x9370db);
  public static final Color mediumseagreen = new Color(0x3cb371);
  public static final Color mediumslateblue = new Color(0x7b68ee);
  public static final Color mediumspringgreen = new Color(0x00fa9a);
  public static final Color mediumturquoise = new Color(0x48d1cc);
  public static final Color mediumvioletred = new Color(0xc71585);
  public static final Color midnightblue = new Color(0x191970);
  public static final Color mintcream = new Color(0xf5fffa);
  public static final Color mistyrose = new Color(0xffe4e1);
  public static final Color moccasin = new Color(0xffe4b5);
  public static final Color navajowhite = new Color(0xffdead);
  public static final Color navy = new Color(0x000080);
  public static final Color oldlace = new Color(0xfdf5e6);
  public static final Color olive = new Color(0x808000);
  public static final Color olivedrab = new Color(0x6b8e23);
  public static final Color orange = new Color(0xffa500);
  public static final Color orangered = new Color(0xff4500);
  public static final Color orchid = new Color(0xda70d6);
  public static final Color palegoldenrod = new Color(0xeee8aa);
  public static final Color palegreen = new Color(0x98fb98);
  public static final Color paleturquoise = new Color(0xafeeee);
  public static final Color palevioletred = new Color(0xdb7093);
  public static final Color papayawhip = new Color(0xffefd5);
  public static final Color peachpuff = new Color(0xffdab9);
  public static final Color peru = new Color(0xcd853f);
  public static final Color pink = new Color(0xffc0cb);
  public static final Color plum = new Color(0xdda0dd);
  public static final Color powderblue = new Color(0xb0e0e6);
  public static final Color purple = new Color(0x800080);
  public static final Color red = new Color(0xff0000);
  public static final Color rosybrown = new Color(0xbc8f8f);
  public static final Color royalblue = new Color(0x4169e1);
  public static final Color saddlebrown = new Color(0x8b4513);
  public static final Color salmon = new Color(0xfa8072);
  public static final Color sandybrown = new Color(0xf4a460);
  public static final Color seagreen = new Color(0x2e8b57);
  public static final Color seashell = new Color(0xfff5ee);
  public static final Color sienna = new Color(0xa0522d);
  public static final Color silver = new Color(0xc0c0c0);
  public static final Color skyblue = new Color(0x87ceeb);
  public static final Color slateblue = new Color(0x6a5acd);
  public static final Color slategray = new Color(0x708090);
  public static final Color snow = new Color(0xfffafa);
  public static final Color springgreen = new Color(0x00ff7f);
  public static final Color steelblue = new Color(0x4682b4);
  public static final Color tan = new Color(0xd2b48c);
  public static final Color teal = new Color(0x008080);
  public static final Color thistle = new Color(0xd8bfd8);
  public static final Color tomato = new Color(0xff6347);
  public static final Color turquoise = new Color(0x40e0d0);
  public static final Color violet = new Color(0xee82ee);
  public static final Color wheat = new Color(0xf5deb3);
  public static final Color white = new Color(0xffffff);
  public static final Color whitesmoke = new Color(0xf5f5f5);
  public static final Color yellow = new Color(0xffff00);
  public static final Color yellowgreen = new Color(0x9acd32);

  static {
      initColorsMap();
  }

}
A JOutlookBar provides a component that is similar to a JTabbedPane, but instead of maintaining tabs
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * A JOutlookBar provides a component that is similar to a JTabbedPane, but
 * instead of maintaining tabs, it uses Outlook-style bars to control the
 * visible component
 */
public class JAccordion extends JPanel implements ActionListener {
  private static final long serialVersionUID = -2760245005186933366L;
  /**
   * The top panel: contains the buttons displayed on the top of the
   * JOutlookBar
   */
  private JPanel topPanel = new JPanel(new GridLayout(1, 1));

  /**
   * The bottom panel: contains the buttons displayed on the bottom of the
   * JOutlookBar
   */
  private JPanel bottomPanel = new JPanel(new GridLayout(1, 1));

  /**
   * A LinkedHashMap of bars: we use a linked hash map to preserve the order
   * of the bars
   */
  private Map<String, BarInfo> bars = new LinkedHashMap<String, BarInfo>();

  /**
   * The currently visible bar (zero-based index)
   */
  private int visibleBar = 0;

  /**
   * A place-holder for the currently visible component
   */
  private JComponent visibleComponent = null;

  /**
   * Creates a new JOutlookBar; after which you should make repeated calls to
   * addBar() for each bar
   */
  public JAccordion() {
    this.setLayout(new BorderLayout());
    this.add(topPanel, BorderLayout.NORTH);
    this.add(bottomPanel, BorderLayout.SOUTH);
  }

  /**
   * Adds the specified component to the JOutlookBar and sets the bar's name
   * 
   * @param name
   *            The name of the outlook bar
   * @param componenet
   *            The component to add to the bar
   */
  public void addBar(String name, JComponent component) {
    BarInfo barInfo = new BarInfo(name, component);
    barInfo.getButton().addActionListener(this);
    this.bars.put(name, barInfo);
    render();
  }

  /**
   * Adds the specified component to the JOutlookBar and sets the bar's name
   * 
   * @param name
   *            The name of the outlook bar
   * @param icon
   *            An icon to display in the outlook bar
   * @param componenet
   *            The component to add to the bar
   */
  public void addBar(String name, Icon icon, JComponent component) {
    BarInfo barInfo = new BarInfo(name, icon, component);
    barInfo.getButton().addActionListener(this);
    this.bars.put(name, barInfo);
    render();
  }

  /**
   * Removes the specified bar from the JOutlookBar
   * 
   * @param name
   *            The name of the bar to remove
   */
  public void removeBar(String name) {
    this.bars.remove(name);
    render();
  }

  /**
   * Returns the index of the currently visible bar (zero-based)
   * 
   * @return The index of the currently visible bar
   */
  public int getVisibleBar() {
    return this.visibleBar;
  }

  /**
   * Programmatically sets the currently visible bar; the visible bar index
   * must be in the range of 0 to size() - 1
   * 
   * @param visibleBar
   *            The zero-based index of the component to make visible
   */
  public void setVisibleBar(int visibleBar) {
    if (visibleBar > 0 && visibleBar < this.bars.size() - 1) {
      this.visibleBar = visibleBar;
      render();
    }
  }

  /**
   * Causes the outlook bar component to rebuild itself; this means that it
   * rebuilds the top and bottom panels of bars as well as making the
   * currently selected bar's panel visible
   */
  public void render() {
    // Compute how many bars we are going to have where
    int totalBars = this.bars.size();
    int topBars = this.visibleBar + 1;
    int bottomBars = totalBars - topBars;

    // Get an iterator to walk through out bars with
    Iterator<String> itr = this.bars.keySet().iterator();

    // Render the top bars: remove all components, reset the GridLayout to
    // hold to correct number of bars, add the bars, and "validate" it to
    // cause it to re-layout its components
    this.topPanel.removeAll();
    GridLayout topLayout = (GridLayout) this.topPanel.getLayout();
    topLayout.setRows(topBars);
    BarInfo barInfo = null;
    for (int i = 0; i < topBars; i++) {
      String barName = (String) itr.next();
      barInfo = (BarInfo) this.bars.get(barName);
      this.topPanel.add(barInfo.getButton());
    }
    this.topPanel.validate();

    // Render the center component: remove the current component (if there
    // is one) and then put the visible component in the center of this
    // panel
    if (this.visibleComponent != null) {
      this.remove(this.visibleComponent);
    }
    this.visibleComponent = barInfo.getComponent();
    this.add(visibleComponent, BorderLayout.CENTER);

    // Render the bottom bars: remove all components, reset the GridLayout
    // to
    // hold to correct number of bars, add the bars, and "validate" it to
    // cause it to re-layout its components
    this.bottomPanel.removeAll();
    GridLayout bottomLayout = (GridLayout) this.bottomPanel.getLayout();
    bottomLayout.setRows(bottomBars);
    for (int i = 0; i < bottomBars; i++) {
      String barName = (String) itr.next();
      barInfo = (BarInfo) this.bars.get(barName);
      this.bottomPanel.add(barInfo.getButton());
    }
    this.bottomPanel.validate();

    // Validate all of our components: cause this container to re-layout its
    // subcomponents
    validate();
  }

  /**
   * Invoked when one of our bars is selected
   */
  public void actionPerformed(ActionEvent e) {
    int currentBar = 0;
    for (Iterator<String> i = this.bars.keySet().iterator(); i.hasNext();) {
      String barName = (String) i.next();
      BarInfo barInfo = (BarInfo) this.bars.get(barName);
      if (barInfo.getButton() == e.getSource()) {
        // Found the selected button
        this.visibleBar = currentBar;
        render();
        return;
      }
      currentBar++;
    }
  }

  /**
   * Debug, dummy method
   */
  public static JPanel getDummyPanel(String name) {
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(new JLabel(name, JLabel.CENTER));
    return panel;
  }

  /**
   * Debug test...
   */
  public static void main(String[] args) {
    JFrame frame = new JFrame("JOutlookBar Test");

    JAccordion outlookBar = new JAccordion();
    outlookBar.addBar("One",
        getDummyPanel("Onexxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxo"));
    outlookBar.addBar("Two", getDummyPanel("Two"));
    outlookBar.addBar("Three", getDummyPanel("Three"));
    outlookBar.addBar("Four", getDummyPanel("Four"));
    outlookBar.addBar("Five", getDummyPanel("Five"));
    outlookBar.setVisibleBar(2);
    // outlookBar.setPreferredSize(new Dimension(400, 400));

    frame.getContentPane().setLayout(new BorderLayout());
    frame.getContentPane().add(outlookBar, BorderLayout.CENTER);

    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    frame.setLocation(d.width / 2 - 400, d.height / 2 - 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);

  }

  /**
   * Internal class that maintains information about individual Outlook bars;
   * specifically it maintains the following information:
   * 
   * name The name of the bar button The associated JButton for the bar
   * component The component maintained in the Outlook bar
   */
  class BarInfo {
    /**
     * The name of this bar
     */
    private String name;

    /**
     * The JButton that implements the Outlook bar itself
     */
    private JButton button;

    /**
     * The component that is the body of the Outlook bar
     */
    private JComponent component;

    /**
     * Creates a new BarInfo
     * 
     * @param name
     *            The name of the bar
     * @param component
     *            The component that is the body of the Outlook Bar
     */
    public BarInfo(String name, JComponent component) {
      this.name = name;
      this.component = component;
      this.button = new JButton(name);
    }

    /**
     * Creates a new BarInfo
     * 
     * @param name
     *            The name of the bar
     * @param icon
     *            JButton icon
     * @param component
     *            The component that is the body of the Outlook Bar
     */
    public BarInfo(String name, Icon icon, JComponent component) {
      this.name = name;
      this.component = component;
      this.button = new JButton(name, icon);
    }

    /**
     * Returns the name of the bar
     * 
     * @return The name of the bar
     */
    public String getName() {
      return this.name;
    }

    /**
     * Sets the name of the bar
     * 
     * @param The
     *            name of the bar
     */
    public void setName(String name) {
      this.name = name;
    }

    /**
     * Returns the outlook bar JButton implementation
     * 
     * @return The Outlook Bar JButton implementation
     */
    public JButton getButton() {
      return this.button;
    }

    /**
     * Returns the component that implements the body of this Outlook Bar
     * 
     * @return The component that implements the body of this Outlook Bar
     */
    public JComponent getComponent() {
      return this.component;
    }
  }
}


Another Link button
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonUI;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;

/**
 * A button targeted to be used as an hyperlink. Most UI will make it
 * transparent and it will react on mouse over by changing the cursor to the
 * hand cursor.
 * 
 * @javabean.class name="JLinkButton" shortDescription="A button looking as an
 *                 hyperlink." stopClass="java.awt.Component"
 * 
 * @javabean.attribute name="isContainer" value="Boolean.FALSE" rtexpr="true"
 * 
 * @javabean.icons mono16="JLinkButton16-mono.gif" color16="JLinkButton16.gif"
 *                 mono32="JLinkButton32-mono.gif" color32="JLinkButton32.gif"
 */
public class AnotherLinkButton extends JButton {

  public AnotherLinkButton() {
    super();
  }

  public AnotherLinkButton(String text) {
    super(text);
  }

  public AnotherLinkButton(String text, Icon icon) {
    super(text, icon);
  }

  public AnotherLinkButton(Action a) {
    super(a);
  }

  public AnotherLinkButton(Icon icon) {
    super(icon);
  }

  public void updateUI() {
    setUI(new WindowsLinkButtonUI());
  }

  public static void main(String[] args) throws Exception {
    JFrame frame = new JFrame("JLinkButton");
    frame.getContentPane().setLayout(new BorderLayout());
    frame.getContentPane().add("Center", new AnotherLinkButton("www.java2s.com"));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocation(100, 100);
    frame.setVisible(true);
  }

}

class BasicLinkButtonUI extends BasicButtonUI {

  public static ComponentUI createUI(JComponent c) {
    return new BasicLinkButtonUI();
  }

  private static Rectangle viewRect = new Rectangle();

  private static Rectangle textRect = new Rectangle();

  private static Rectangle iconRect = new Rectangle();

  private static MouseListener handCursorListener = new HandCursor();

  protected int dashedRectGapX;

  protected int dashedRectGapY;

  protected int dashedRectGapWidth;

  protected int dashedRectGapHeight;

  private Color focusColor;

  protected void installDefaults(AbstractButton b) {
    super.installDefaults(b);

    b.setOpaque(false);
    b.setBorderPainted(false);
    b.setRolloverEnabled(true);

    dashedRectGapX = UIManager.getInt("ButtonUI.dashedRectGapX");
    dashedRectGapY = UIManager.getInt("ButtonUI.dashedRectGapY");
    dashedRectGapWidth = UIManager.getInt("ButtonUI.dashedRectGapWidth");
    dashedRectGapHeight = UIManager.getInt("ButtonUI.dashedRectGapHeight");
    focusColor = UIManager.getColor("ButtonUI.focus");

    b.setHorizontalAlignment(AbstractButton.LEFT);
  }

  protected void installListeners(AbstractButton b) {
    super.installListeners(b);
    b.addMouseListener(handCursorListener);
  }

  protected void uninstallListeners(AbstractButton b) {
    super.uninstallListeners(b);
    b.removeMouseListener(handCursorListener);
  }

  protected Color getFocusColor() {
    return focusColor;
  }

  public void paint(Graphics g, JComponent c) {
    AbstractButton b = (AbstractButton) c;
    ButtonModel model = b.getModel();

    FontMetrics fm = g.getFontMetrics();

    Insets i = c.getInsets();

    viewRect.x = i.left;
    viewRect.y = i.top;
    viewRect.width = b.getWidth() - (i.right + viewRect.x);
    viewRect.height = b.getHeight() - (i.bottom + viewRect.y);

    textRect.x = textRect.y = textRect.width = textRect.height = 0;
    iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;

    Font f = c.getFont();
    g.setFont(f);

    // layout the text and icon
    String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), b
        .getIcon(), b.getVerticalAlignment(), b
        .getHorizontalAlignment(), b.getVerticalTextPosition(), b
        .getHorizontalTextPosition(), viewRect, iconRect, textRect, b
        .getText() == null ? 0 : b.getIconTextGap());

    clearTextShiftOffset();

    // perform UI specific press action, e.g. Windows L&F shifts text
    if (model.isArmed() && model.isPressed()) {
      paintButtonPressed(g, b);
    }

    // Paint the Icon
    if (b.getIcon() != null) {
      paintIcon(g, c, iconRect);
    }

    Composite oldComposite = ((Graphics2D) g).getComposite();

    if (model.isRollover()) {
      ((Graphics2D) g).setComposite(AlphaComposite.getInstance(
          AlphaComposite.SRC_OVER, 0.5f));
    }

    if (text != null && !text.equals("")) {
      View v = (View) c.getClientProperty(BasicHTML.propertyKey);
      if (v != null) {
        textRect.x += getTextShiftOffset();
        textRect.y += getTextShiftOffset();
        v.paint(g, textRect);
        textRect.x -= getTextShiftOffset();
        textRect.y -= getTextShiftOffset();
      } else {
        paintText(g, b, textRect, text);
      }
    }

    if (b.isFocusPainted() && b.hasFocus()) {
      // paint UI specific focus
      paintFocus(g, b, viewRect, textRect, iconRect);
    }

    ((Graphics2D) g).setComposite(oldComposite);
  }

  protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect,
      Rectangle textRect, Rectangle iconRect) {
    if (b.getParent() instanceof JToolBar) {
      // Windows doesn't draw the focus rect for buttons in a toolbar.
      return;
    }

    // focus painted same color as text
    int width = b.getWidth();
    int height = b.getHeight();
    g.setColor(getFocusColor());
    BasicGraphicsUtils.drawDashedRect(g, dashedRectGapX, dashedRectGapY,
        width - dashedRectGapWidth, height - dashedRectGapHeight);
  }

  protected void paintButtonPressed(Graphics g, AbstractButton b) {
    setTextShiftOffset();
  }

  static class HandCursor extends MouseAdapter {
    public void mouseEntered(MouseEvent e) {
      e.getComponent().setCursor(
          Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    }

    public void mouseExited(MouseEvent e) {
      e.getComponent().setCursor(Cursor.getDefaultCursor());
    }
  }
}

class WindowsLinkButtonUI extends BasicLinkButtonUI {

  private static WindowsLinkButtonUI buttonUI = new WindowsLinkButtonUI();

  public static ComponentUI createUI(JComponent c) {
    return buttonUI;
  }

  protected void paintButtonPressed(Graphics g, AbstractButton b) {
    setTextShiftOffset();
  }
}


import java.awt.Color;
import java.awt.Cursor;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.net.URL;

import javax.swing.Action;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalButtonUI;

public class JLinkButton extends JButton {
  private static final String uiString = "LinkButtonUI";

  public static final int ALWAYS_UNDERLINE = 0;

  public static final int HOVER_UNDERLINE = 1;

  public static final int NEVER_UNDERLINE = 2;

  public static final int SYSTEM_DEFAULT = 3;

  private int linkBehavior;

  private Color linkColor;

  private Color colorPressed;

  private Color visitedLinkColor;

  private Color disabledLinkColor;

  private URL buttonURL;

  private Action defaultAction;

  private boolean isLinkVisited;

  public static void main(String[] a) {
    JFrame f = new JFrame();
    f.getContentPane().setLayout(new GridLayout(0,2));
    f.getContentPane().add(new JLinkButton("www.java2s.com"));
    f.getContentPane().add(new JLinkButton("www.java2s.com/ExampleCode/CatalogExampleCode.htm"));
    f.setSize(600, 200);
    f.setVisible(true);
  }

  public JLinkButton() {
    this(null, null, null);
  }

  public JLinkButton(Action action) {
    this();
    setAction(action);
  }

  public JLinkButton(Icon icon) {
    this(null, icon, null);
  }

  public JLinkButton(String s) {
    this(s, null, null);
  }

  public JLinkButton(URL url) {
    this(null, null, url);
  }

  public JLinkButton(String s, URL url) {
    this(s, null, url);
  }

  public JLinkButton(Icon icon, URL url) {
    this(null, icon, url);
  }

  public JLinkButton(String text, Icon icon, URL url) {
    super(text, icon);
    linkBehavior = SYSTEM_DEFAULT;
    linkColor = Color.blue;
    colorPressed = Color.red;
    visitedLinkColor = new Color(128, 0, 128);
    if (text == null && url != null)
      setText(url.toExternalForm());
    setLinkURL(url);
    setCursor(Cursor.getPredefinedCursor(12));
    setBorderPainted(false);
    setContentAreaFilled(false);
    setRolloverEnabled(true);
    addActionListener(defaultAction);
  }

  public void updateUI() {
    setUI(BasicLinkButtonUI.createUI(this));
  }

  private void setDefault() {
    UIManager.getDefaults().put("LinkButtonUI", "BasicLinkButtonUI");
  }

  public String getUIClassID() {
    return "LinkButtonUI";
  }

  protected void setupToolTipText() {
    String tip = null;
    if (buttonURL != null)
      tip = buttonURL.toExternalForm();
    setToolTipText(tip);
  }

  public void setLinkBehavior(int bnew) {
    checkLinkBehaviour(bnew);
    int old = linkBehavior;
    linkBehavior = bnew;
    firePropertyChange("linkBehavior", old, bnew);
    repaint();
  }

  private void checkLinkBehaviour(int beha) {
    if (beha != ALWAYS_UNDERLINE && beha != HOVER_UNDERLINE
        && beha != NEVER_UNDERLINE && beha != SYSTEM_DEFAULT)
      throw new IllegalArgumentException("Not a legal LinkBehavior");
    else
      return;
  }

  public int getLinkBehavior() {
    return linkBehavior;
  }

  public void setLinkColor(Color color) {
    Color colorOld = linkColor;
    linkColor = color;
    firePropertyChange("linkColor", colorOld, color);
    repaint();
  }

  public Color getLinkColor() {
    return linkColor;
  }

  public void setActiveLinkColor(Color colorNew) {
    Color colorOld = colorPressed;
    colorPressed = colorNew;
    firePropertyChange("activeLinkColor", colorOld, colorNew);
    repaint();
  }

  public Color getActiveLinkColor() {
    return colorPressed;
  }

  public void setDisabledLinkColor(Color color) {
    Color colorOld = disabledLinkColor;
    disabledLinkColor = color;
    firePropertyChange("disabledLinkColor", colorOld, color);
    if (!isEnabled())
      repaint();
  }

  public Color getDisabledLinkColor() {
    return disabledLinkColor;
  }

  public void setVisitedLinkColor(Color colorNew) {
    Color colorOld = visitedLinkColor;
    visitedLinkColor = colorNew;
    firePropertyChange("visitedLinkColor", colorOld, colorNew);
    repaint();
  }

  public Color getVisitedLinkColor() {
    return visitedLinkColor;
  }

  public URL getLinkURL() {
    return buttonURL;
  }

  public void setLinkURL(URL url) {
    URL urlOld = buttonURL;
    buttonURL = url;
    setupToolTipText();
    firePropertyChange("linkURL", urlOld, url);
    revalidate();
    repaint();
  }

  public void setLinkVisited(boolean flagNew) {
    boolean flagOld = isLinkVisited;
    isLinkVisited = flagNew;
    firePropertyChange("linkVisited", flagOld, flagNew);
    repaint();
  }

  public boolean isLinkVisited() {
    return isLinkVisited;
  }

  public void setDefaultAction(Action actionNew) {
    Action actionOld = defaultAction;
    defaultAction = actionNew;
    firePropertyChange("defaultAction", actionOld, actionNew);
  }

  public Action getDefaultAction() {
    return defaultAction;
  }

  protected String paramString() {
    String str;
    if (linkBehavior == ALWAYS_UNDERLINE)
      str = "ALWAYS_UNDERLINE";
    else if (linkBehavior == HOVER_UNDERLINE)
      str = "HOVER_UNDERLINE";
    else if (linkBehavior == NEVER_UNDERLINE)
      str = "NEVER_UNDERLINE";
    else
      str = "SYSTEM_DEFAULT";
    String colorStr = linkColor == null ? "" : linkColor.toString();
    String colorPressStr = colorPressed == null ? "" : colorPressed
        .toString();
    String disabledLinkColorStr = disabledLinkColor == null ? ""
        : disabledLinkColor.toString();
    String visitedLinkColorStr = visitedLinkColor == null ? ""
        : visitedLinkColor.toString();
    String buttonURLStr = buttonURL == null ? "" : buttonURL.toString();
    String isLinkVisitedStr = isLinkVisited ? "true" : "false";
    return super.paramString() + ",linkBehavior=" + str + ",linkURL="
        + buttonURLStr + ",linkColor=" + colorStr + ",activeLinkColor="
        + colorPressStr + ",disabledLinkColor=" + disabledLinkColorStr
        + ",visitedLinkColor=" + visitedLinkColorStr
        + ",linkvisitedString=" + isLinkVisitedStr;
  }
}

class BasicLinkButtonUI extends MetalButtonUI {
  private static final BasicLinkButtonUI ui = new BasicLinkButtonUI();

  public BasicLinkButtonUI() {
  }

  public static ComponentUI createUI(JComponent jcomponent) {
    return ui;
  }

  protected void paintText(Graphics g, JComponent com, Rectangle rect,
      String s) {
    JLinkButton bn = (JLinkButton) com;
    ButtonModel bnModel = bn.getModel();
    Color color = bn.getForeground();
    Object obj = null;
    if (bnModel.isEnabled()) {
      if (bnModel.isPressed())
        bn.setForeground(bn.getActiveLinkColor());
      else if (bn.isLinkVisited())
        bn.setForeground(bn.getVisitedLinkColor());

      else
        bn.setForeground(bn.getLinkColor());
    } else {
      if (bn.getDisabledLinkColor() != null)
        bn.setForeground(bn.getDisabledLinkColor());
    }
    super.paintText(g, com, rect, s);
    int behaviour = bn.getLinkBehavior();
    boolean drawLine = false;
    if (behaviour == JLinkButton.HOVER_UNDERLINE) {
      if (bnModel.isRollover())
        drawLine = true;
    } else if (behaviour == JLinkButton.ALWAYS_UNDERLINE || behaviour == JLinkButton.SYSTEM_DEFAULT)
      drawLine = true;
    if (!drawLine)
      return;
    FontMetrics fm = g.getFontMetrics();
    int x = rect.x + getTextShiftOffset();
    int y = (rect.y + fm.getAscent() + fm.getDescent() + getTextShiftOffset()) - 1;
    if (bnModel.isEnabled()) {
      g.setColor(bn.getForeground());
      g.drawLine(x, y, (x + rect.width) - 1, y);
    } else {
      g.setColor(bn.getBackground().brighter());
      g.drawLine(x, y, (x + rect.width) - 1, y);
    }
  }
}
Separator Sample 2
import java.awt.Container;
import java.awt.GridLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSeparator;

public class AnotherSeparatorSample {
  public static void main(String args[]) {
    JFrame f = new JFrame("JSeparator Sample");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Container content = f.getContentPane();
    content.setLayout(new GridLayout(0, 1));
    JLabel above = new JLabel("Above Separator");
    content.add(above);
    JSeparator separator = new JSeparator();
    content.add(separator);
    JLabel below = new JLabel("Below Separator");
    content.add(below);
    f.setSize(300, 100);
    f.setVisible(true);
  }
}

Separator Sample
import java.awt.BorderLayout;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.JSeparator;

public class SeparatorSample {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Separator Example");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JSeparator north = new JSeparator(JSeparator.HORIZONTAL);
    JSeparator south = new JSeparator(JSeparator.VERTICAL);
    JSeparator east = new JSeparator(JSeparator.HORIZONTAL);
    JSeparator west = new JSeparator(JSeparator.VERTICAL);
    Container contentPane = frame.getContentPane();
    contentPane.add(north, BorderLayout.NORTH);
    contentPane.add(south, BorderLayout.SOUTH);
    contentPane.add(east, BorderLayout.EAST);
    contentPane.add(west, BorderLayout.WEST);
    frame.setSize(350, 250);
    frame.setVisible(true);
  }
}
A demonstration of the JSeparator() component used in a toolbar-like

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;

public class SeparatorExample extends JPanel {

  public SeparatorExample() {
    super(true);

    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    Box box1 = new Box(BoxLayout.X_AXIS);
    Box box2 = new Box(BoxLayout.X_AXIS);
    Box box3 = new Box(BoxLayout.X_AXIS);

    box1.add(new JButton("Press Me"));
    box1.add(new JButton("No Me!"));
    box1.add(new JButton("Ignore Them!"));
    box2.add(new JSeparator());
    box3.add(new JButton("I'm the Button!"));
    box3.add(new JButton("It's me!"));
    box3.add(new JButton("Go Away!"));

    add(box1);
    add(box2);
    add(box3);
  }

  public static void main(String s[]) {

    SeparatorExample example = new SeparatorExample();

    JFrame frame = new JFrame("Separator Example");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(example);
    frame.pack();
    frame.setVisible(true);
  }
}
Thumb Slider Example
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() {
  }

}

Thumb Slider Example 1
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
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 com.sun.java.swing.plaf.motif.MotifSliderUI;

/**
 * @version 1.0 9/3/99
 */
public class MThumbSliderExample1 extends JFrame {
  public MThumbSliderExample1() {
    super("MThumbSlider Example");

    JSlider slider = new JSlider();

    int n = 2;
    MThumbSlider mSlider = new MThumbSlider(n);
    mSlider.setValueAt(25, 0);
    mSlider.setValueAt(75, 1);

    getContentPane().setLayout(new FlowLayout());
    getContentPane().add(slider);
    getContentPane().add(mSlider);
  }

  public static void main(String args[]) {
    try {
      UIManager
          .setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
    } catch (Exception ex) {
      System.err.println("Error loading L&F: " + ex);
    }

    MThumbSliderExample1 f = new MThumbSliderExample1();
    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 MotifMThumbSliderUI extends MotifSliderUI implements
    MThumbSliderAdditional {

  MThumbSliderAdditionalUI additonalUi;

  MouseInputAdapter mThumbTrackListener;

  public static ComponentUI createUI(JComponent c) {
    return new MotifMThumbSliderUI((JSlider) c);
  }

  public MotifMThumbSliderUI() {
    super(null);
  }

  public MotifMThumbSliderUI(JSlider b) {
    super(b);
  }

  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() {
  }

  Rectangle zeroRect = new Rectangle();

  public void paint(Graphics g, JComponent c) {

    Rectangle clip = g.getClipBounds();
    thumbRect = zeroRect;

    super.paint(g, c);

    int thumbNum = additonalUi.getThumbNum();
    Rectangle[] thumbRects = additonalUi.getThumbRects();

    for (int i = thumbNum - 1; 0 <= i; i--) {
      if (clip.intersects(thumbRects[i])) {
        thumbRect = thumbRects[i];
        paintThumb(g);
      }
    }
  }

  protected void installKeyboardActions(JSlider slider) {
  }

  protected void uninstallKeyboardActions(JSlider slider) {
  }

  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() {
  }

}
Thumb Slider Example 2
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
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;

/**
 * @version 1.0 9/3/99
 */
public class MThumbSliderExample2 extends JFrame {
  public MThumbSliderExample2() {
    super("MThumbSlider Example");

    JSlider slider = new JSlider();
    slider.setUI(new javax.swing.plaf.basic.BasicSliderUI(slider));

    int n = 2;
    MThumbSlider mSlider = new MThumbSlider(n);
    mSlider.setValueAt(25, 0);
    mSlider.setValueAt(75, 1);
    mSlider.setUI(new BasicMThumbSliderUI());

    getContentPane().setLayout(new FlowLayout());
    getContentPane().add(slider);
    getContentPane().add(mSlider);
  }

  public static void main(String args[]) {
    try {
      UIManager
          .setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
    } catch (Exception ex) {
      System.err.println("Error loading L&F: " + ex);
    }

    MThumbSliderExample2 f = new MThumbSliderExample2();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    f.setSize(300, 100);
    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 BasicMThumbSliderUI extends BasicSliderUI implements
    MThumbSliderAdditional {

  MThumbSliderAdditionalUI additonalUi;

  MouseInputAdapter mThumbTrackListener;

  public static ComponentUI createUI(JComponent c) {
    return new BasicMThumbSliderUI((JSlider) c);
  }

  public BasicMThumbSliderUI() {
    super(null);
  }

  public BasicMThumbSliderUI(JSlider b) {
    super(b);
  }

  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() {
  }

  Rectangle zeroRect = new Rectangle();

  public void paint(Graphics g, JComponent c) {

    Rectangle clip = g.getClipBounds();
    thumbRect = zeroRect;

    super.paint(g, c);

    int thumbNum = additonalUi.getThumbNum();
    Rectangle[] thumbRects = additonalUi.getThumbRects();

    for (int i = thumbNum - 1; 0 <= i; i--) {
      if (clip.intersects(thumbRects[i])) {
        thumbRect = thumbRects[i];

        paintThumb(g);

      }
    }
  }

  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() {
  }

}
Slider With ToolTip Example

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * @version 1.0 10/4/99
 */  
public class SliderWithToolTipExample extends JFrame {
  public SliderWithToolTipExample() {
    super("Mad Level");  
    
    JSlider s = new JSlider(JSlider.VERTICAL, 0, 120, 60) {
      String[] tooltips = {"Call 911",
                           "Seeing red",
                           "Really mad",
                           "Ticked off",
                           "Slightly peeved",
                           "Oh bother",
                           "Feel good"};
            
      public String getToolTipText(MouseEvent e) {
        Point p = e.getPoint();
        Rectangle rect = new Rectangle();
        rect = getBounds(rect);
        int n = getLabelTable().size();
        int index = n * p.y / rect.height;
        return tooltips[index];
      }
    };
    
    s.setPaintTicks(true);
    s.setMajorTickSpacing(20);
    s.setPaintLabels( true );
    s.putClientProperty( "JSlider.isFilled", Boolean.TRUE ); 
        
    s.setToolTipText("");
    
    getContentPane().setLayout(new FlowLayout());
    getContentPane().add(s);
  }

  public static void main (String args[]) {
    SliderWithToolTipExample f = new SliderWithToolTipExample();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
  System.exit(0);
      }
    });
    f.setSize (120, 250);
    f.show();
  }
}

A dialog that presents the user with a sequence of steps for completing a task.
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * A dialog that presents the user with a sequence of steps for completing a
 * task. The dialog contains "Next" and "Previous" buttons, allowing the user to
 * navigate through the task.
 * <P>
 * When the user backs up by one or more steps, the dialog keeps the completed
 * steps so that they can be reused if the user doesn't change anything - this
 * handles the cases where the user backs up a few steps just to review what has
 * been completed.
 * <p>
 * But if the user changes some options in an earlier step, then the dialog may
 * have to discard the later steps and have them repeated.
 * <P>
 * THIS CLASS IS NOT WORKING CORRECTLY YET.
 * 
 * 
 * @author David Gilbert
 */
public class WizardDialog extends JDialog implements ActionListener {

  /** The end result of the wizard sequence. */
  private Object result;

  /** The current step in the wizard process (starting at step zero). */
  private int step;

  /** A reference to the current panel. */
  private WizardPanel currentPanel;

  /**
   * A list of references to the panels the user has already seen - used for
   * navigating through the steps that have already been completed.
   */
  private java.util.List panels;

  /** A handy reference to the "previous" button. */
  private JButton previousButton;

  /** A handy reference to the "next" button. */
  private JButton nextButton;

  /** A handy reference to the "finish" button. */
  private JButton finishButton;

  /** A handy reference to the "help" button. */
  private JButton helpButton;

  /**
   * Standard constructor - builds and returns a new WizardDialog.
   * 
   * @param owner
   *          the owner.
   * @param modal
   *          modal?
   * @param title
   *          the title.
   * @param firstPanel
   *          the first panel.
   */
  public WizardDialog(final JDialog owner, final boolean modal, final String title,
      final WizardPanel firstPanel) {

    super(owner, title + " : step 1", modal);
    this.result = null;
    this.currentPanel = firstPanel;
    this.step = 0;
    this.panels = new ArrayList();
    this.panels.add(firstPanel);
    setContentPane(createContent());

  }

  /**
   * Standard constructor - builds a new WizardDialog owned by the specified
   * JFrame.
   * 
   * @param owner
   *          the owner.
   * @param modal
   *          modal?
   * @param title
   *          the title.
   * @param firstPanel
   *          the first panel.
   */
  public WizardDialog(final JFrame owner, final boolean modal, final String title,
      final WizardPanel firstPanel) {

    super(owner, title + " : step 1", modal);
    this.result = null;
    this.currentPanel = firstPanel;
    this.step = 0;
    this.panels = new ArrayList();
    this.panels.add(firstPanel);
    setContentPane(createContent());
  }

  /**
   * Returns the result of the wizard sequence.
   * 
   * @return the result.
   */
  public Object getResult() {
    return this.result;
  }

  /**
   * Returns the total number of steps in the wizard sequence, if this number is
   * known. Otherwise this method returns zero. Subclasses should override this
   * method unless the number of steps is not known.
   * 
   * @return the number of steps.
   */
  public int getStepCount() {
    return 0;
  }

  /**
   * Returns true if it is possible to back up to the previous panel, and false
   * otherwise.
   * 
   * @return boolean.
   */
  public boolean canDoPreviousPanel() {
    return (this.step > 0);
  }

  /**
   * Returns true if there is a 'next' panel, and false otherwise.
   * 
   * @return boolean.
   */
  public boolean canDoNextPanel() {
    return this.currentPanel.hasNextPanel();
  }

  /**
   * Returns true if it is possible to finish the sequence at this point
   * (possibly with defaults for the remaining entries).
   * 
   * @return boolean.
   */
  public boolean canFinish() {
    return this.currentPanel.canFinish();
  }

  /**
   * Returns the panel for the specified step (steps are numbered from zero).
   * 
   * @param step
   *          the current step.
   * 
   * @return the panel.
   */
  public WizardPanel getWizardPanel(final int step) {
    if (step < this.panels.size()) {
      return (WizardPanel) this.panels.get(step);
    } else {
      return null;
    }
  }

  /**
   * Handles events.
   * 
   * @param event
   *          the event.
   */
  public void actionPerformed(final ActionEvent event) {
    final String command = event.getActionCommand();
    if (command.equals("nextButton")) {
      next();
    } else if (command.equals("previousButton")) {
      previous();
    } else if (command.equals("finishButton")) {
      finish();
    }
  }

  /**
   * Handles a click on the "previous" button, by displaying the previous panel
   * in the sequence.
   */
  public void previous() {
    if (this.step > 0) {
      final WizardPanel previousPanel = getWizardPanel(this.step - 1);
      // tell the panel that we are returning
      previousPanel.returnFromLaterStep();
      final Container content = getContentPane();
      content.remove(this.currentPanel);
      content.add(previousPanel);
      this.step = this.step - 1;
      this.currentPanel = previousPanel;
      setTitle("Step " + (this.step + 1));
      enableButtons();
      pack();
    }
  }

  /**
   * Displays the next step in the wizard sequence.
   */
  public void next() {

    WizardPanel nextPanel = getWizardPanel(this.step + 1);
    if (nextPanel != null) {
      if (!this.currentPanel.canRedisplayNextPanel()) {
        nextPanel = this.currentPanel.getNextPanel();
      }
    } else {
      nextPanel = this.currentPanel.getNextPanel();
    }

    this.step = this.step + 1;
    if (this.step < this.panels.size()) {
      this.panels.set(this.step, nextPanel);
    } else {
      this.panels.add(nextPanel);
    }

    final Container content = getContentPane();
    content.remove(this.currentPanel);
    content.add(nextPanel);

    this.currentPanel = nextPanel;
    setTitle("Step " + (this.step + 1));
    enableButtons();
    pack();

  }

  /**
   * Finishes the wizard.
   */
  public void finish() {
    this.result = this.currentPanel.getResult();
    setVisible(false);
  }

  /**
   * Enables/disables the buttons according to the current step. A good idea
   * would be to ask the panels to return the status...
   */
  private void enableButtons() {
    this.previousButton.setEnabled(this.step > 0);
    this.nextButton.setEnabled(canDoNextPanel());
    this.finishButton.setEnabled(canFinish());
    this.helpButton.setEnabled(false);
  }

  /**
   * Checks, whether the user cancelled the dialog.
   * 
   * @return false.
   */
  public boolean isCancelled() {
    return false;
  }

  /**
   * Creates a panel containing the user interface for the dialog.
   * 
   * @return the panel.
   */
  public JPanel createContent() {

    final JPanel content = new JPanel(new BorderLayout());
    content.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
    content.add((JPanel) this.panels.get(0));
    final L1R3ButtonPanel buttons = new L1R3ButtonPanel("Help", "Previous", "Next", "Finish");

    this.helpButton = buttons.getLeftButton();
    this.helpButton.setEnabled(false);

    this.previousButton = buttons.getRightButton1();
    this.previousButton.setActionCommand("previousButton");
    this.previousButton.addActionListener(this);
    this.previousButton.setEnabled(false);

    this.nextButton = buttons.getRightButton2();
    this.nextButton.setActionCommand("nextButton");
    this.nextButton.addActionListener(this);
    this.nextButton.setEnabled(true);

    this.finishButton = buttons.getRightButton3();
    this.finishButton.setActionCommand("finishButton");
    this.finishButton.addActionListener(this);
    this.finishButton.setEnabled(false);

    buttons.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
    content.add(buttons, BorderLayout.SOUTH);

    return content;
  }

}

/*
 * JCommon : a free general purpose class library for the Java(tm) platform
 * 
 * 
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info: http://www.jfree.org/jcommon/index.html
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. in the
 * United States and other countries.]
 * 
 * ---------------- WizardPanel.java ---------------- (C) Copyright 2000-2004,
 * by Object Refinery Limited.
 * 
 * Original Author: David Gilbert (for Object Refinery Limited); Contributor(s): -;
 * 
 * $Id: WizardPanel.java,v 1.5 2007/11/02 17:50:36 taqua Exp $
 * 
 * Changes (from 26-Oct-2001) -------------------------- 26-Oct-2001 : Changed
 * package to com.jrefinery.ui.*; 14-Oct-2002 : Fixed errors reported by
 * Checkstyle (DG);
 * 
 */

/**
 * A panel that provides the user interface for a single step in a WizardDialog.
 * 
 * @author David Gilbert
 */
abstract class WizardPanel extends JPanel {

  /** The owner. */
  private WizardDialog owner;

  /**
   * Creates a new panel.
   * 
   * @param layout
   *          the layout manager.
   */
  protected WizardPanel(final LayoutManager layout) {
    super(layout);
  }

  /**
   * Returns a reference to the dialog that owns the panel.
   * 
   * @return the owner.
   */
  public WizardDialog getOwner() {
    return this.owner;
  }

  /**
   * Sets the reference to the dialog that owns the panel (this is called
   * automatically by the dialog when the panel is added to the dialog).
   * 
   * @param owner
   *          the owner.
   */
  public void setOwner(final WizardDialog owner) {
    this.owner = owner;
  }

  /**
   * Returns the result.
   * 
   * @return the result.
   */
  public Object getResult() {
    return null;
  }

  /**
   * This method is called when the dialog redisplays this panel as a result of
   * the user clicking the "Previous" button. Inside this method, subclasses
   * should make a note of their current state, so that they can decide what to
   * do when the user hits "Next".
   */
  public abstract void returnFromLaterStep();

  /**
   * Returns true if it is OK to redisplay the last version of the next panel,
   * or false if a new version is required.
   * 
   * @return boolean.
   */
  public abstract boolean canRedisplayNextPanel();

  /**
   * Returns true if there is a next panel.
   * 
   * @return boolean.
   */
  public abstract boolean hasNextPanel();

  /**
   * Returns true if it is possible to finish from this panel.
   * 
   * @return boolean.
   */
  public abstract boolean canFinish();

  /**
   * Returns the next panel in the sequence, given the current user input.
   * Returns null if this panel is the last one in the sequence.
   * 
   * @return the next panel in the sequence.
   */
  public abstract WizardPanel getNextPanel();

}

/*
 * JCommon : a free general purpose class library for the Java(tm) platform
 * 
 * 
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info: http://www.jfree.org/jcommon/index.html
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. in the
 * United States and other countries.]
 * 
 * -------------------- L1R3ButtonPanel.java -------------------- (C) Copyright
 * 2000-2004, by Object Refinery Limited.
 * 
 * Original Author: David Gilbert (for Object Refinery Limited); Contributor(s): -;
 * 
 * $Id: L1R3ButtonPanel.java,v 1.5 2007/11/02 17:50:36 taqua Exp $
 * 
 * Changes (from 26-Oct-2001) -------------------------- 26-Oct-2001 : Changed
 * package to com.jrefinery.ui.* (DG); 26-Jun-2002 : Removed unnecessary import
 * (DG); 14-Oct-2002 : Fixed errors reported by Checkstyle (DG);
 * 
 */

/**
 * A 'ready-made' panel that has one button on the left and three buttons on the
 * right - nested panels and layout managers take care of resizing.
 * 
 * @author David Gilbert
 */
class L1R3ButtonPanel extends JPanel {

  /** The left button. */
  private JButton left;

  /** The first button on the right of the panel. */
  private JButton right1;

  /** The second button on the right of the panel. */
  private JButton right2;

  /** The third button on the right of the panel. */
  private JButton right3;

  /**
   * Standard constructor - creates panel with the specified button labels.
   * 
   * @param label1
   *          the label for button 1.
   * @param label2
   *          the label for button 2.
   * @param label3
   *          the label for button 3.
   * @param label4
   *          the label for button 4.
   */
  public L1R3ButtonPanel(final String label1, final String label2, final String label3,
      final String label4) {

    setLayout(new BorderLayout());

    // create the pieces...
    final JPanel panel = new JPanel(new BorderLayout());
    final JPanel panel2 = new JPanel(new BorderLayout());
    this.left = new JButton(label1);
    this.right1 = new JButton(label2);
    this.right2 = new JButton(label3);
    this.right3 = new JButton(label4);

    // ...and put them together
    panel.add(this.left, BorderLayout.WEST);
    panel2.add(this.right1, BorderLayout.EAST);
    panel.add(panel2, BorderLayout.CENTER);
    panel.add(this.right2, BorderLayout.EAST);
    add(panel, BorderLayout.CENTER);
    add(this.right3, BorderLayout.EAST);

  }

  /**
   * Returns a reference to button 1, allowing the caller to set labels,
   * action-listeners etc.
   * 
   * @return the left button.
   */
  public JButton getLeftButton() {
    return this.left;
  }

  /**
   * Returns a reference to button 2, allowing the caller to set labels,
   * action-listeners etc.
   * 
   * @return the right button 1.
   */
  public JButton getRightButton1() {
    return this.right1;
  }

  /**
   * Returns a reference to button 3, allowing the caller to set labels,
   * action-listeners etc.
   * 
   * @return the right button 2.
   */
  public JButton getRightButton2() {
    return this.right2;
  }

  /**
   * Returns a reference to button 4, allowing the caller to set labels,
   * action-listeners etc.
   * 
   * @return the right button 3.
   */
  public JButton getRightButton3() {
    return this.right3;
  }

}