A Swing drag and drop code example

Drag and Drop is a powerful feature you should consider adding to your Java applications. When you check out the available tutorials on Java dnd, it may look fairly complex to you. Though it’s not all that hard, the hard part is remembering all the steps, objects and listeners involved. Here is a small example that shows you a basic drag and drop application (implemented with Swing).

A custom Drag and Drop (source) JList appears to the right and contains a file list of your root directory. Files with .txt extensions can be dragged upon our Drag and Drop JTextArea (target). The object transferred (Transferable) is a filename. When the drop occurs (you release the mouse button) the public void drop(DropTargetDropEvent event) is called. You can extract the filename from the Transferable object and go from there.

Main.java

import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.io.*;
 
public class Main extends JFrame
{
   private DNDJList list;
   private DNDJTextArea textarea;
 
   public static void main(String []args) {
      Main frame = new Main();
      frame.show();
   }
 
   public Main() {
      getContentPane().setLayout(new BorderLayout());
      list = new DNDJList(new DefaultListModel());
      getContentPane().add(BorderLayout.EAST, new JScrollPane(list));
      textarea = new DNDJTextArea();
      getContentPane().add(BorderLayout.CENTER, new JScrollPane(textarea));
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
 
      fillUpList("c:\");
 
      setSize(700, 300);
   }

   /**
    *  Fills up the JList with the entries in the specified directory
    */
   private void fillUpList(String directory) {
      File dir = new File(directory);
      File []files = dir.listFiles();
 
      DefaultListModel dlm = (DefaultListModel) list.getModel();
      for (int i=0; i<files.length; i++) {
         dlm.addElement(directory + files[i].getName());
      }
   }
}

Our drag and drop JTextArea: DNDJTextArea.java

import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;

import java.io.*;
import java.io.IOException;
 
import javax.swing.JTextArea;
 
public class DNDJTextArea extends JTextArea implements DropTargetListener
{
   DropTarget dropTarget = null;
  
   public DNDJTextArea() {
      // create a drop target
      dropTarget = new DropTarget(this, this);
   }
 
   public void dragEnter(DropTargetDragEvent event) { 
      event.acceptDrag(DnDConstants.ACTION_MOVE);
   }
 
   public void drop (DropTargetDropEvent event) {
      try {
         // get the object that is being transferred
         Transferable transferable = event.getTransferable();
       
         // DNDJTextArea accepts only Strings
         if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            event.acceptDrop(DnDConstants.ACTION_MOVE);
 
            String filename = (String) transferable.getTransferData(DataFlavor.stringFlavor);
            setText(readFile(filename));

            event.getDropTargetContext().dropComplete(true);
         } 
         else {
            event.rejectDrop();
         }
      }
      catch (UnsupportedFlavorException e) {
         setText(""+e);
         event.rejectDrop();
      }
      catch (Exception e) {
         setText(""+e);
         event.rejectDrop();
      } 
   }
 
   public void dragExit (DropTargetEvent event) { }
   public void dragOver (DropTargetDragEvent event) { }
   public void dropActionChanged (DropTargetDragEvent event) { }
 
   public String readFile(String filename) throws Exception {
      String LINEEND = System.getProperties().getProperty("line.separator");      
      StringBuffer sb = new StringBuffer();
      BufferedReader br = new BufferedReader(new FileReader(filename));
 
      String line;
      while ((line = br.readLine()) != null) {
         sb.append(line + LINEEND);
      }         
 
      return sb.toString();
   }
}

A drag and drop JList:DNDJList.java

import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.io.IOException;
import java.io.*;
 
import javax.swing.*;
 
public class DNDJList extends JList implements DragSourceListener, DragGestureListener    
{
   DragSource dragSource = null;
 
   public DNDJList(ListModel lm) {
      super(lm);
 
      // create a dragsource
      dragSource = new DragSource();
 
      // create a drag gesture recognizer
      dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
   }    
 
   public void dragGestureRecognized( DragGestureEvent event) {  
      String fileSelected = (String) getSelectedValue();
 
      if (fileSelected != null) {
         if (fileSelected.endsWith(".txt")) {
            // StringSelection implements Transferable, wraps the data into a transferable object
            StringSelection text = new StringSelection(fileSelected.toString()); 
        
            // start the dragging
            dragSource.startDrag(event, DragSource.DefaultMoveDrop, text, this);
         }
         else {
            SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  JOptionPane.showMessageDialog(SwingUtilities.getRootPane(DNDJList.this), 
                                                "Only .txt files can be dragged!", "Error",
                                                JOptionPane.ERROR_MESSAGE);
               } 
            });
         }
      } else {
         System.out.println( "nothing was selected");   
      }
   }
 
   public void dragDropEnd (DragSourceDropEvent event) { }
   public void dragEnter (DragSourceDragEvent event) { }
   public void dragExit (DragSourceEvent event) { }
   public void dragOver (DragSourceDragEvent event) { }
   public void dropActionChanged ( DragSourceDragEvent event) { }
}

Setting the JSplitPane divider location

Use the method setDividerLocation to set the divider. You can either set the new position by pixel or specify a percentage:

   // sets the divider at pixel 100
   splitPane.setDividerLocation(100);
   // sets the divider in the middle
   splitPane.setDividerLocation(.5);

The JSplitPane MUST be visible invoking this method
otherwise it will not have the desired effect. Here’s an example on how to set the divider location the moment when the JFrame becomes visible:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{
   JSplitPane splitPane;
  
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
 
      splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, 
                                 new JPanel(), new JPanel());
 
      createMainMenuBar();
 
      getContentPane().add(splitPane);
 
      addComponentListener(new ComponentAdapter() {
         public void componentShown(ComponentEvent event) {
            splitPane.setDividerLocation(0.5); 
                
            removeComponentListener(this);
         }
      });
   }
 
   public void createMainMenuBar() {
      JMenuBar mainBar = new JMenuBar();
      JMenu menu = new JMenu("JSplitPane");
      JMenuItem item1 = new JMenuItem("HORIZONTAL_SPLIT");
      JMenuItem item2 = new JMenuItem("VERTICAL_SPLIT");
      menu.add(item1);
      menu.add(item2);
      mainBar.add(menu);
      setJMenuBar(mainBar);
 
      item1.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
         }
      });
 
      item2.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
         }
      });
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 300);
      main.setVisible(true);
   }
}

Preventing a JInternalFrame from being closed

Just specify so when you create a JInternalFrame, in the constructor, or call the method setClosable(false).

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.net.*;
 
public class Main extends JFrame {
   JDesktopPane desktop;
   int nframes = 0;
  
   public Main() {
      desktop = new JDesktopPane(); 
      setContentPane(desktop);
      setJMenuBar(createMenuBar());
      createInternalFrame(); 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   }
 
   protected JMenuBar createMenuBar() {
      JMenuBar menuBar = new JMenuBar();
 
      JMenu createMenu = new JMenu("Create");
      createMenu.setMnemonic(KeyEvent.VK_C);
      JMenuItem newMenuItem = new JMenuItem("New");
      newMenuItem.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            createInternalFrame();
         }
      }); 
      newMenuItem.setMnemonic(KeyEvent.VK_N);
      createMenu.add(newMenuItem);
      menuBar.add(createMenu);
 
      return menuBar;
   }
 
   protected void createInternalFrame() {
      nframes++;
      String title = "JInternalFrame #" + nframes;
      JInternalFrame frame = new JInternalFrame(title,
         true,    // resizable
         true,    // closable
         true,    // maximizable
         true);   // iconifiable
      frame.setVisible(true); 
 
      // disable closing the frame
      frame.setClosable(false);
 
      desktop.add(frame);
      frame.setSize(200, 200);
      frame.setLocation(30*nframes, 30*nframes);
      try {
         frame.setSelected(true);
      } catch (java.beans.PropertyVetoException e) {}
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(500, 300);
      main.setVisible(true);
   }
}

Invoking a JPopupMenu from a JComboBox

Add a MouseListener:

import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
    
public class Main extends JFrame
{ 
   public Main() {
      getContentPane().setLayout(new FlowLayout());
      
      final JComboBox combobox = new JComboBox(new String[] { "Item 1", "Item 2", "Item 3" });
      final JPopupMenu popup = new JPopupMenu();
      JMenuItem mi1 = new JMenuItem("Popup Item 1");
      JMenuItem mi2 = new JMenuItem("Popup Item 2");
      popup.add(mi1);
      popup.add(mi2);
 
      ((JButton) combobox.getComponent(0)).addMouseListener(new MouseAdapter() {
         public void mousePressed(MouseEvent me) {
            if (me.isPopupTrigger()) {
               popup.show(combobox, me.getX(), me.getY());
            }
         }
 
         public void mouseReleased(MouseEvent me) {
            if (me.isPopupTrigger()) {
               popup.show(combobox, me.getX(), me.getY());
            }
         }
      });
 
      getContentPane().add(combobox);
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });      
   
      setSize(new Dimension(200, 200));
   } 
 
   public static void main(String[] args) throws Exception {
      Main main = new Main();
      main.setVisible(true);
   }
}

Changing the color of every element in a JList

Customize the CellRenderer as in following example:

import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
  
public class Main extends JFrame {
 
   public Main() {
      getContentPane().setLayout(new FlowLayout());
 
      Vector v = new Vector();
      final JList list = new JList(
         new Colorable[] { new ColoredItem("Item 1", Color.yellow),
                           new ColoredItem("Item 2"),
                           new ColoredItem("Item 3", Color.blue),
                           new ColoredItem("Item 4", Color.red),
                           new ColoredItem("Item 5", Color.green)
                         });
 
      list.setCellRenderer(new ColoredCellRenderer());
      getContentPane().add(new JScrollPane(list));    
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      setSize(200, 300);
   }
   
   public static void main(String[] args) {
      (new Main()).show();
   }
}
 
class ColoredCellRenderer extends DefaultListCellRenderer implements ListCellRenderer {
   public Component getListCellRendererComponent(
      JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)    
   {
      setText((value == null) ? "" : value.toString());
      Color c = ((Colorable) value).getColor();
      if (isSelected) {
         setBackground(list.getSelectionBackground());
         setForeground(c);
      }
      else {
         setBackground(list.getBackground());
         setForeground(c);
      }
 
      setEnabled(list.isEnabled());
      setFont(list.getFont());
 
      return this;
   }
}
 
class ColoredItem implements Colorable {
   Object  object;
   Color   color;
                            
   public ColoredItem(Object object, Color color) {
      this.object = object;
      this.color = color;
   }
                             
   ColoredItem(Object object) {
      this(object, Color.black);
   }
 
   public Color getColor() {
      return color;
   } 
 
   public void setColor(Color color) {
      this.color = color;
   }                           
                            
   public String toString() {
      return object.toString();
   }
}
                         
interface Colorable {
   public Color getColor();
   public void setColor(Color c);
}

Getting the JMenu object from the selected JMenuItem

Get the accessible parent from the accessibleContext.
Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
   
public class Main extends JFrame implements ActionListener {
   private JMenu menu1;
   private JMenu menu2;
 
   public Main() {
      getContentPane().setLayout(new FlowLayout());
 
      JMenuBar menuBar = new JMenuBar();
      menu1 = new JMenu("The weak sex");
      JMenuItem menuItem1 = new JMenuItem("male");
      menu2 = new JMenu("The strong sex");
      JMenuItem menuItem2 = new JMenuItem("female");
      menu1.add(menuItem1);
      menu2.add(menuItem2);
      menuBar.add(menu1);
      menuBar.add(menu2);
      menuItem1.addActionListener(this);
      menuItem2.addActionListener(this);
 
      this.setJMenuBar(menuBar); 
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      setSize(400, 400);
   }
 
   public void actionPerformed(ActionEvent ae) {
      JMenuItem menuItem = (JMenuItem) ae.getSource();
      JMenu menu = (JMenu) menuItem.getAccessibleContext().getAccessibleParent();
      System.out.println("Selected menu: " + menu.getText());
   }
 
   public static void main(String[] args) {
      (new Main()).show();
   }
}

Creating a multi-line text JLabel

The easiest way is to hardcode HTML inside your JLabel text. JLabel has the functionality to render HTML. Another way is to write a custom Jlabel UI for your l&f that is able to understand escape sequences like n.

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.text.*;
import java.awt.*;
   
public class Main extends JFrame {
   public Main() {
      getContentPane().setLayout(new FlowLayout());
 
      JLabel label = new JLabel("<html>Java resources at<br>esus.com<html>");
      getContentPane().add(label);     
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      pack();
   }
 
   public static void main(String[] args) {
      (new Main()).show();
   }
}

Previewing textfiles in a JFileChooser

Use the setAccessory method!

Main.java:

import javax.swing.filechooser.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.awt.*;
import java.io.*;
  
public class Main extends JFrame {
   public static void main(String[] args) {
      JFileChooser fc = new JFileChooser();
 
      // show only directories and .txt files       
      fc.addChoosableFileFilter(new TextfileFilter());
 
      // set the accessory JComponent to our panel that previews textfiles
      fc.setAccessory(new TextfilePreview(fc));
 
      int returnVal = fc.showDialog(new JFrame(), "Open");
 
      if (returnVal == JFileChooser.APPROVE_OPTION) {
         File file = fc.getSelectedFile();
      }
 
      System.exit(0);
   }
}
 
class TextfileFilter extends javax.swing.filechooser.FileFilter
{
   public boolean accept(File f) {
      if (f.isDirectory()) return true;
 
      return f.getName().toLowerCase().endsWith(".txt");
   }
 
   public String getDescription() {
      return "TextFiles (*.txt)";
   }
}
 
class TextfilePreview extends JPanel
                      implements PropertyChangeListener {
   File textfile;
   JTextArea ta;
 
   public TextfilePreview(JFileChooser fc) {
      fc.addPropertyChangeListener(this);
      ta = new JTextArea();
      JScrollPane sp = new JScrollPane(ta);
      setLayout(new BorderLayout());
      add(BorderLayout.CENTER, sp);
      setPreferredSize(new Dimension(150, 200));
   }
 
   public void loadTextfile() {
      if (textfile == null) return;
 
      try {
         ta.setText("");
         BufferedReader br = new BufferedReader(new FileReader(textfile));
         String line;
         while ((line = br.readLine()) != null) {
            ta.append(line + "n");
         }
         br.close();
      }
      catch(IOException e) {
      }
   } 
 
   public void propertyChange(PropertyChangeEvent e) {
      String prop = e.getPropertyName();
      if (prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
         textfile = (File) e.getNewValue();
         if (isShowing()) {
            loadTextfile();
            repaint();
         }
      }
   }
}

Creating a JTable with hideable columns

Courtesy of Nobuo Tamemasa (http://www2.gol.com/users/tame/swing/examples/JTableExamples8.html)



HideColumnTableExample.java:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
 
/**
 * @version 1.0 05/31/99
 */
public class HideColumnTableExample extends JFrame {

  public HideColumnTableExample(){
    super( "HideColumnTable Example" );
    
    JTable table = new JTable(5, 7);
    ColumnButtonScrollPane pane = new ColumnButtonScrollPane(table);
    getContentPane().add(pane);
  }

  public static void main(String[] args) {
    HideColumnTableExample frame = new HideColumnTableExample();
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    frame.setSize( 400, 100 );
    frame.setVisible(true);
  }
}

ColumnButtonScrollPane.java:

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import javax.swing.event.*;
import jp.gr.java_conf.tame.swing.layout.*;
import jp.gr.java_conf.tame.swing.border.*;
import jp.gr.java_conf.tame.swing.icon.*;
 
/**
 * @version 1.0 05/31/99
 */
public class ColumnButtonScrollPane extends JScrollPane {
  Component columnButton;

  public ColumnButtonScrollPane(JTable table) {
    super(table);
    TableColumnModel cm = table.getColumnModel();
    LimitedTableHeader header = new LimitedTableHeader(cm);
    table.setTableHeader(header);
    columnButton = createUpperCorner(header);
    setCorner(UPPER_RIGHT_CORNER, columnButton);
    setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
    ColumnButtonScrollPaneLayout layout = new ColumnButtonScrollPaneLayout();
    setLayout(layout);
    layout.syncWithScrollPane(this);
  }
  
  protected Component createUpperCorner(JTableHeader header) {
    ColumnButton corner = new ColumnButton(header);
    return corner;
  }

  
  public class LimitedTableHeader extends JTableHeader {
    public LimitedTableHeader(TableColumnModel cm) {
      super(cm);
    }
       
    // actually, this is a not complete way. but easy one.
    // you can see last column painted wider, short time :) 
    // If you don't like this kind cheap fake,
    // you have to overwrite the paint method in UI class.
    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      columnButton.repaint();
    }    
  }

  
  public class ColumnButton extends JPanel {    
    JTable table;
    TableColumnModel cm;
    JButton revealButton; 
    JButton hideButton;
    Stack stack;  
    
    public ColumnButton(JTableHeader header) {
      setLayout(new GridLayout(1,2));
      setBorder(new LinesBorder(SystemColor.controlShadow, new Insets(0,1,0,0)));
      
      stack = new Stack();
      table = header.getTable();
      cm    = table.getColumnModel();
      
      revealButton = createButton(header, SwingConstants.WEST);
      hideButton   = createButton(header, SwingConstants.EAST);
      add(revealButton);
      add(hideButton);   
         
      revealButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {      
          TableColumn column = (TableColumn)stack.pop();
          cm.addColumn(column);
          if (stack.empty()) {
            revealButton.setEnabled(false);
          }
          hideButton.setEnabled(true);
          table.sizeColumnsToFit(-1);
        }
      });
      hideButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {      
          int n = cm.getColumnCount();
          TableColumn column = cm.getColumn(n -1);
          stack.push(column);
          cm.removeColumn(column);
          if (n < 3) {
            hideButton.setEnabled(false);
          }
          revealButton.setEnabled(true);
          table.sizeColumnsToFit(-1);
        }
      });
      
      if (1 < cm.getColumnCount()) {
        hideButton.setEnabled(true);
      } else {
        hideButton.setEnabled(false);
      }
      revealButton.setEnabled(false);
    }
    
    protected JButton createButton(JTableHeader header,
                                   int direction) {
      //int iconHeight = header.getPreferredSize().height - 6;
      int iconHeight = 8;
      JButton button = new JButton();
      button.setIcon(new ArrowIcon(iconHeight, direction, true));
      button.setDisabledIcon(new ArrowIcon(iconHeight, direction, false));
      button.setRequestFocusEnabled(false);
      button.setForeground(header.getForeground());
      button.setBackground(header.getBackground());
      button.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
      return button;
    }    
  }
}

ColumnButtonScrollPaneLayout.java:

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


/**
 * @version 1.0 05/29/99
 */
public class ColumnButtonScrollPaneLayout extends  ScrollPaneLayout {

  public ColumnButtonScrollPaneLayout() {
    super.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
  }

  public void setVerticalScrollBarPolicy(int x) {
    // VERTICAL_SCROLLBAR_ALWAYS
    super.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
  }
  
  public void layoutContainer(Container parent) {
    super.layoutContainer(parent);
    
    if ((colHead == null) || (! colHead.isVisible()) ||
        (upperRight == null) || (vsb == null)) {
      return;
    }
    
    Rectangle vsbR = new Rectangle(0, 0, 0, 0);
    vsbR = vsb.getBounds(vsbR);
    
    Rectangle colHeadR = new Rectangle(0, 0, 0, 0);
    colHeadR = colHead.getBounds(colHeadR);
    colHeadR.width -= vsbR.width;
    colHead.getBounds(colHeadR);
    
    Rectangle upperRightR = upperRight.getBounds();
    upperRightR.x     -= vsbR.width;
    upperRightR.width += vsbR.width + 1;
    upperRight.setBounds(upperRightR);
  }  
}

LinesBorder.java:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
 
/**
 * @version 1.0 03/09/99
 */
public class LinesBorder extends AbstractBorder implements SwingConstants { 
  protected int northThickness;
  protected int southThickness;
  protected int eastThickness;
  protected int westThickness;  
  protected Color northColor;
  protected Color southColor;
  protected Color eastColor;
  protected Color westColor;
  
  public LinesBorder(Color color) {
    this(color, 1);
  }

  public LinesBorder(Color color, int thickness)  {
    setColor(color);
    setThickness(thickness);
  }

  public LinesBorder(Color color, Insets insets)  {
    setColor(color);
    setThickness(insets);
  }

  public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
    Color oldColor = g.getColor();
    
    g.setColor(northColor);
    for (int i = 0; i < northThickness; i++)  {
      g.drawLine(x, y+i, x+width-1, y+i);
    }
    g.setColor(southColor);
    for (int i = 0; i < southThickness; i++)  {
      g.drawLine(x, y+height-i-1, x+width-1, y+height-i-1);
    }
    g.setColor(eastColor);
    for (int i = 0; i < westThickness; i++)  {
      g.drawLine(x+i, y, x+i, y+height-1);
    }
    g.setColor(westColor);
    for (int i = 0; i < eastThickness; i++)  {
      g.drawLine(x+width-i-1, y, x+width-i-1, y+height-1);
    }

    g.setColor(oldColor);
  }

  public Insets getBorderInsets(Component c)       {
    return new Insets(northThickness, westThickness, southThickness, eastThickness);
  }

  public Insets getBorderInsets(Component c, Insets insets) {
    return new Insets(northThickness, westThickness, southThickness, eastThickness);    
  }


  public boolean isBorderOpaque() { return true; }
    
  public void setColor(Color c) {
    northColor = c;
    southColor = c;
    eastColor  = c;
    westColor  = c;
  }
  
  public void setColor(Color c, int direction) {
    switch (direction) {
      case NORTH: northColor = c; break;
      case SOUTH: southColor = c; break;
      case EAST:  eastColor  = c; break;
      case WEST:  westColor  = c; break;
      default: 
    }
  }
    
  public void setThickness(int n) {
    northThickness = n;
    southThickness = n;
    eastThickness  = n;
    westThickness  = n;
  }
    
  public void setThickness(Insets insets) {
    northThickness = insets.top;
    southThickness = insets.bottom;
    eastThickness  = insets.right;
    westThickness  = insets.left;
  }
  
  public void setThickness(int n, int direction) {
    switch (direction) {
      case NORTH: northThickness = n; break;
      case SOUTH: southThickness = n; break;
      case EAST:  eastThickness  = n; break;
      case WEST:  westThickness  = n; break;
      default: 
    }
  }

  public void append(LinesBorder b, boolean isReplace) {
    if (isReplace) {
      northThickness = b.northThickness;
      southThickness = b.southThickness;
      eastThickness  = b.eastThickness;
      westThickness  = b.westThickness;
    } else {
      northThickness = Math.max(northThickness ,b.northThickness);
      southThickness = Math.max(southThickness ,b.southThickness);
      eastThickness  = Math.max(eastThickness  ,b.eastThickness);
      westThickness  = Math.max(westThickness  ,b.westThickness);
    }
  }

  public void append(Insets insets, boolean isReplace) {
    if (isReplace) {
      northThickness = insets.top;
      southThickness = insets.bottom;
      eastThickness  = insets.right;
      westThickness  = insets.left;
    } else {
      northThickness = Math.max(northThickness ,insets.top);
      southThickness = Math.max(southThickness ,insets.bottom);
      eastThickness  = Math.max(eastThickness  ,insets.right);
      westThickness  = Math.max(westThickness  ,insets.left);
    }
  }
}

ArrowIcon.java:

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
  
/**
 * @version 1.0 02/26/99
 */
public class ArrowIcon implements Icon, SwingConstants {
  private static final int DEFAULT_SIZE = 11;
  //private static final int DEFAULT_SIZE = 5;

  private int size;
  private int iconSize;
  private int direction;
  private boolean isEnabled;
  private BasicArrowButton iconRenderer;

  public ArrowIcon(int direction, boolean isPressedView) {
    this(DEFAULT_SIZE, direction, isPressedView);
  }

  public ArrowIcon(int iconSize, int direction, boolean isEnabled) {
    this.size = iconSize / 2;
    this.iconSize = iconSize;
    this.direction = direction;
    this.isEnabled = isEnabled;
    iconRenderer = new BasicArrowButton(direction);
  }

  public void paintIcon(Component c, Graphics g, int x, int y) {
    iconRenderer.paintTriangle(g, x, y, size, direction, isEnabled);
  }

  public int getIconWidth() {
    //int retCode;
    switch (direction) {
      case NORTH:
      case SOUTH: return iconSize;
      case EAST:
      case WEST:  return size;
    }
    return iconSize;
  }

  public int getIconHeight() {
    switch (direction) {
      case NORTH:
      case SOUTH: return size;
      case EAST:
      case WEST:  return iconSize;
    }
    return size;
  }
}