Get notified when the user has moved the JSplitPane divider

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.awt.*;
 
public class Main extends JFrame
{
   JSplitPane splitPane;
  
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
 
      final JLabel label = new JLabel();
      splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, 
                                 label, new JPanel());
      splitPane.setDividerSize(20);
      splitPane.setOneTouchExpandable(true);
 
      createMainMenuBar();
 
      getContentPane().add(splitPane);
 
      addComponentListener(new ComponentAdapter() {
         public void componentShown(ComponentEvent event) {
            splitPane.setDividerLocation(0.5);  
            label.setText("Divider location: " + splitPane.getDividerLocation());
                
            removeComponentListener(this);
         }
      });
 
      splitPane.addPropertyChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent pce) {
            if (pce.getPropertyName().equals(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
               label.setText("Divider location: " + splitPane.getDividerLocation());
            }
         }
      });
   }
 
   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);
   }
}

Changing the color of the title bar and border of a JInternalFrame

This worked for me, using the Windows Look and Feel.

To change the colors of the title bar, modify some properties:

   InternalFrame.activeTitleBackground
   InternalFrame.activeTitleForeground
   InternalFrame.inactiveTitleBackground
   InternalFrame.inactiveTitleForeground

To change the border, you can call setBorder on a JInternalFrame.

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() {
      modifyColors();
      desktop = new JDesktopPane(); 
      setContentPane(desktop);
      setJMenuBar(createMenuBar());
      createInternalFrame(); 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   }
 
   protected void modifyColors() {
      try {
         String name = UIManager.getSystemLookAndFeelClassName();
         UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
         UIManager.put("InternalFrame.activeTitleBackground", Color.red);
         UIManager.put("InternalFrame.activeTitleForeground", Color.blue);
         UIManager.put("InternalFrame.inactiveTitleBackground", Color.black);
         UIManager.put("InternalFrame.inactiveTitleForeground", Color.yellow);
      }
      catch(Exception e) { 
         e.printStackTrace();
      }
   }
 
   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); 
 
      frame.setBorder(BorderFactory.createLineBorder(Color.black));
 frame.updateUI();
      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);
   }
}

Creating an editable JComboBox

Use the method setEditable. Here’s an example:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
   
public class Main extends JFrame
{ 
   public Main() {
      getContentPane().setLayout(new BorderLayout());
      final JComboBox combobox = new JComboBox();
      final JList list = new JList(new DefaultListModel());
 
      getContentPane().add(BorderLayout.NORTH, combobox);
      getContentPane().add(BorderLayout.CENTER, list); 
 
      combobox.setEditable(true);
      combobox.addItemListener(new ItemListener() {
         public void itemStateChanged(ItemEvent ie) {
            if (ie.getStateChange() == ItemEvent.SELECTED) {
               ((DefaultListModel) list.getModel()).addElement(combobox.getSelectedItem());
               combobox.insertItemAt(combobox.getSelectedItem(), 0);
            }
         }
      });
 
      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);
   }
}

Scrolling a JList to the selected item

You can use the method ensureIndexIsVisible as shown 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();
      for (int i=0; i<50; i++) {
         v.addElement("Item #" + i);
      }
      final JList list = new JList(v);
 
      final JTextField textfield = new JTextField(5);
      getContentPane().add(new JLabel("Scroll to:"));
      getContentPane().add(textfield);
      JButton button = new JButton("Scroll!");
      getContentPane().add(button);
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            int n = Integer.parseInt(textfield.getText());
            list.ensureIndexIsVisible(n);
         }
      });
      getContentPane().add(new JScrollPane(list));    
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      setSize(200, 230);
   }
   
   public static void main(String[] args) {
      (new Main()).show();
   }
}

Adding a general JComponent to a JMenu

Create your own version of the JComponent that you would like to add to your Menu and have it implement the methods of MenuElement. Here’s an example that shows a JMenu with a JTextField and a JSlider as its elements:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
   
public class Main extends JFrame {
   public Main() {
      getContentPane().setLayout(new FlowLayout());
 
      JMenuBar menuBar = new JMenuBar();
      JMenu menu = new JMenu("Menu");
      JMenuItem menuItem1 = new JMenuItem("item 1");
      JMenuItem menuItem2 = new JMenuItem("item 2");
      JMenuItemTextField menuItem3 = new JMenuItemTextField(20);
      JMenuItem menuItem4 = new JMenuItem("item 4");
      JMenuItemSlider menuItem5 = new JMenuItemSlider();
      menuItem5.setMajorTickSpacing(20);
      menuItem5.setMinorTickSpacing(5);
      menuItem5.setPaintTicks(true);
      JMenuItem menuItem6 = new JMenuItem("item 6");
 
      menu.add(menuItem1);
      menu.add(menuItem2);
      menu.add(menuItem3);
      menu.add(menuItem4);
      menu.add(menuItem5);
      menu.add(menuItem6);
 
      menuBar.add(menu);
 
      this.setJMenuBar(menuBar); 
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      setSize(400, 400);
   }
  
   public static void main(String[] args) {
      (new Main()).show();
   }
}
 
class JMenuItemTextField extends JTextField implements MenuElement
{
   public JMenuItemTextField(int length) {
      super(length);
   }
 
   public Component getComponent() {
      return this;
   }
 
   public MenuElement[] getSubElements() {
      return null;
   }
 
   public void menuSelectionChanged(boolean changed) {
   }
 
   public void processKeyEvent(KeyEvent kevt, MenuElement[] me, MenuSelectionManager msm) {
   }
 
   public void processMouseEvent(MouseEvent mevt, MenuElement[] me, MenuSelectionManager msm) {
   }
}
 
class JMenuItemSlider extends JSlider implements MenuElement
{
   public JMenuItemSlider() {
      super();
   }
 
   public Component getComponent() {
      return this;
   }
 
   public MenuElement[] getSubElements() {
      return null;
   }
 
   public void menuSelectionChanged(boolean changed) {
   }
 
   public void processKeyEvent(KeyEvent kevt, MenuElement[] me, MenuSelectionManager msm) {
   }
 
   public void processMouseEvent(MouseEvent mevt, MenuElement[] me, MenuSelectionManager msm) {
   }
}

Creating a numeric-only JTextfield

Here’s the code for a Swing component that only accepts digits.

Main.java:

import javax.swing.text.*;
import java.awt.event.*;
import javax.swing.*;
import java.text.*;
import java.awt.*;
   
public class Main extends JFrame {
   public Main() {
      getContentPane().setLayout(new GridLayout(2, 1));
 
      final JPasswordField passwordField = new JPasswordField(20);
      final JTextField textField = new JTextField(20);
      getContentPane().add(new NumberTextField(5));
      getContentPane().add(new NumberTextField(123, 7));
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      pack();
   }
 
   public static void main(String[] args) {
      (new Main()).show();
   }
}
 
class NumberTextField extends JTextField
{
   private NumberFormat nf;
 
   public NumberTextField(int cols) {
      super(cols);
      nf = NumberFormat.getNumberInstance();
   }
 
   public NumberTextField(int value, int cols) {
      this(cols);
      setValue(value);
   }
 
   public int getValue() {
      int n = 0;
      try {
         n = nf.parse(getText()).intValue();
      }
      catch(ParseException e) { }
      return n;
   }
 
   public void setValue(int value) {
      setText(nf.format(value));
   }
 
   protected Document createDefaultModel() {
      return new NumberDocument();
   }
 
   protected class NumberDocument extends PlainDocument {
      public void insertString(int offset, String string, AttributeSet as) 
            throws BadLocationException  {
         char [] src = string.toCharArray();
         char [] dest = new char[src.length];
         int count=0;
         for (int i=0; i < src.length; i++) {
            if (Character.isDigit(src[i])) 
               dest[count++] = src[i];
         }
         super.insertString(offset, new String(dest, 0, count), as);
      }
   }
}

Creating a multi-line text tooltip for a JLabel

An easy way is to have your tooltip text to be HTML:

   label.setToolTipText("<html>This is a two-line<br> tooltip text!</html>");

as in following example:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
                            
public class Main extends JFrame
{ 
   public Main() {
      getContentPane().setLayout(new FlowLayout());
      JLabel label = new JLabel("Mouse-over me!");
      getContentPane().add(label);
                         
      label.setToolTipText("<html>This is a two-line<br> tooltip text!</html>");
                         
      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);
   }
}

Get started with a simple JTable

Using a simple Swing table in your GUI apps doesn’t require a lot of code. Create a one-dimensional containing the table headers and a two-dimensional array that represents the contents. Create a JTable with those two arrays and add it to the container. This is the simpliest example. All cells are editable and column widths can be adjusted.
If you want to have more control over your JTables, you can create your own TableModel, CellRenderer and CellEditor.

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame 
{
   public Main() {
      super("Table example, Wines from Bordeaux");
 
      Object[][] tabledata = {
            { "Chateau Meyney, St. Estephe", 	   new Integer(1994), "$18.75"},
            { "Chateau Montrose, St. Estephe", 	   new Integer(1975), "$54.25" },
            { "Chateau Gloria, St. Julien", 	   new Integer(1993), "$22.99" },
            { "Chateau Beychevelle, St. Julien",   new Integer(1970), "$61.63" },
            { "Chateau La Tour de Mons, Margeaux", new Integer(1975), "$57.03" },
            { "Chateau Brane-Cantenac, Margeaux",  new Integer(1978), "$49.92" },
      };
 
      String columnheaders[] = { "Wine", "Vintage", "Price" };
 
      JTable table = new JTable(tabledata, columnheaders);
      table.setPreferredScrollableViewportSize(new Dimension(500, 70));
      JScrollPane scrollPane = new JScrollPane(table);
 
      getContentPane().add(scrollPane);
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
 
      pack();
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.show();
   }
}

Creating a JTable with multiline cells

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



MultiLineCellExample.java:

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
 
/**
 * @version 1.0 11/09/98
 */
public class MultiLineCellExample extends JFrame {
  MultiLineCellExample() {
    super( &quot;Multi-Line Cell Example&quot; );
 
    DefaultTableModel dm = new DefaultTableModel() {
      public Class getColumnClass(int columnIndex) {
        return String.class;
      }
    };
    dm.setDataVector(new Object[][]{{&quot;ana&quot;,&quot;bnb&quot;,&quot;cnc&quot;},
                                    {&quot;AnA&quot;,&quot;BnB&quot;,&quot;CnC&quot;}},
                     new Object[]{&quot;1&quot;,&quot;2&quot;,&quot;3&quot;});
 
    JTable table = new JTable( dm );
    
    int lines = 2;
    table.setRowHeight( table.getRowHeight() * lines);
    
    //
    // table.setRowHeight(0);
    //
    // I got &quot;java.lang.IllegalArgumentException: New row height less than 1&quot;
    //
    table.setDefaultRenderer(String.class, new MultiLineCellRenderer());
    JScrollPane scroll = new JScrollPane( table );
    getContentPane().add( scroll );
    setSize( 400, 130 );
    setVisible(true);
  }
 
  public static void main(String[] args) {
    MultiLineCellExample frame = new MultiLineCellExample();
    frame.addWindowListener( new WindowAdapter() {
      public void windowClosing( WindowEvent e ) {
        System.exit(0);
      }
    });
  }
}
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
import java.awt.*;
 
/**
 * @version 1.0 11/09/98
 */
 
public class MultiLineCellRenderer extends JTextArea implements TableCellRenderer {
 
  public MultiLineCellRenderer() {
    setLineWrap(true);
    setWrapStyleWord(true);
    setOpaque(true);
  }
 
  public Component getTableCellRendererComponent(JTable table, Object value,
               boolean isSelected, boolean hasFocus, int row, int column) {
    if (isSelected) {
      setForeground(table.getSelectionForeground());
      setBackground(table.getSelectionBackground());
    } else {
      setForeground(table.getForeground());
      setBackground(table.getBackground());
    }
    setFont(table.getFont());
    if (hasFocus) {
      setBorder( UIManager.getBorder(&quot;Table.focusCellHighlightBorder&quot;) );
      if (table.isCellEditable(row, column)) {
        setForeground( UIManager.getColor(&quot;Table.focusCellForeground&quot;) );
        setBackground( UIManager.getColor(&quot;Table.focusCellBackground&quot;) );
      }
    } else {
      setBorder(new EmptyBorder(1, 2, 1, 2));
    }
    setText((value == null) ? &quot;&quot; : value.toString());
    return this;
  }
}

Having multi-font cells in a JTable

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



MultiFontCellTableExample.java:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
 
/**
 * @version 1.0 11/22/98
 */
public class MultiFontCellTableExample extends JFrame {
 
  public MultiFontCellTableExample() {
    super( "Multi-Font Cell Example" ); 
     
    AttributiveCellTableModel ml = new AttributiveCellTableModel(8,3);
    CellFont cellAtt =(CellFont)ml.getCellAttribute();
    JTable table = new JTable( ml );
    table.setRowHeight(26);
    table.setCellSelectionEnabled(true);
    table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
    table.setDefaultRenderer(Object.class ,new AttributiveCellRenderer());
    JScrollPane scroll = new JScrollPane( table );
 
    FontPanel fontPanel = new FontPanel(table, cellAtt);
    
    Box box = new Box(BoxLayout.X_AXIS);
    box.add(scroll);
    box.add(new JSeparator(SwingConstants.HORIZONTAL));
    box.add(fontPanel);
    getContentPane().add( box );
    setSize( 400, 200 );
    setVisible(true);
  }
 
  class FontPanel extends JPanel {
    String[] str_size  = {"10","12","14","16","20","24"};
    String[] str_style = {"PLAIN","BOLD","ITALIC"};
    JComboBox name,style,size;
 
    FontPanel(final JTable table, final CellFont cellAtt) {
      setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
      Box box = new Box(BoxLayout.X_AXIS);
      JPanel p2 = new JPanel(new GridLayout(3,1));
      JPanel p3 = new JPanel(new GridLayout(3,1));
      JPanel p4 = new JPanel(new FlowLayout());
      p2.add(new JLabel("Name:"));
      p2.add(new JLabel("Style:"));    
      p2.add(new JLabel("Size:"));
      Toolkit toolkit = Toolkit.getDefaultToolkit();
      name  = new JComboBox(toolkit.getFontList());
      style = new JComboBox(str_style);
      size  = new JComboBox(str_size);
      size.setEditable(true);
      JButton b_apply   = new JButton("Apply");
      b_apply.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          int[] columns = table.getSelectedColumns();
          int[] rows    = table.getSelectedRows();
          if ((rows == null) || (columns == null)) return;
          if ((rows.length<1)||(columns.length<1)) return;
          Font font = new Font((String)name.getSelectedItem(),
                              style.getSelectedIndex(),
              Integer.parseInt((String)size.getSelectedItem()));
          cellAtt.setFont(font, rows, columns);
          table.clearSelection();
          table.revalidate();
          table.repaint();        
        }
      });
      p3.add(name);
      p3.add(style);
      p3.add(size);
      p4.add(b_apply);
      box.add(p2);
      box.add(p3);
      add(box);
      add(p4);
    }
  }
   
  public static void main(String[] args) {
    MultiFontCellTableExample frame = new MultiFontCellTableExample();
    frame.addWindowListener( new WindowAdapter() {
      public void windowClosing( WindowEvent e ) {
        System.exit(0);
      }
    });
  }
}

AttributiveCellTableModel.java:

import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
 
 
/**
 * @version 1.0 11/22/98
 */
 
public class AttributiveCellTableModel extends DefaultTableModel {
 
  protected CellAttribute cellAtt;
    
  public AttributiveCellTableModel() {
    this((Vector)null, 0);
  }
  public AttributiveCellTableModel(int numRows, int numColumns) {
    Vector names = new Vector(numColumns);
    names.setSize(numColumns);
    setColumnIdentifiers(names);
    dataVector = new Vector();
    setNumRows(numRows);
    cellAtt = new DefaultCellAttribute(numRows,numColumns);
  }
  public AttributiveCellTableModel(Vector columnNames, int numRows) {
    setColumnIdentifiers(columnNames);
    dataVector = new Vector();
    setNumRows(numRows);
    cellAtt = new DefaultCellAttribute(numRows,columnNames.size());
  }
  public AttributiveCellTableModel(Object[] columnNames, int numRows) {
    this(convertToVector(columnNames), numRows);
  }  
  public AttributiveCellTableModel(Vector data, Vector columnNames) {
    setDataVector(data, columnNames);
  }
  public AttributiveCellTableModel(Object[][] data, Object[] columnNames) {
    setDataVector(data, columnNames);
  }
 
     
  public void setDataVector(Vector newData, Vector columnNames) {
    if (newData == null)
      throw new IllegalArgumentException("setDataVector() - Null parameter");
    dataVector = new Vector(0);
    setColumnIdentifiers(columnNames);
    dataVector = newData;
    
    //
    cellAtt = new DefaultCellAttribute(dataVector.size(),
                                       columnIdentifiers.size());
    
    newRowsAdded(new TableModelEvent(this, 0, getRowCount()-1,
                 TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  }
 
  public void addColumn(Object columnName, Vector columnData) {
    if (columnName == null)
      throw new IllegalArgumentException("addColumn() - null parameter");
    columnIdentifiers.addElement(columnName);
    int index = 0;
    Enumeration enumeration = dataVector.elements();
    while (enumeration.hasMoreElements()) {
      Object value;
      if ((columnData != null) && (index < columnData.size()))
          value = columnData.elementAt(index);
      else
        value = null;
      ((Vector)enumeration.nextElement()).addElement(value);
      index++;
    }
 
    //
    cellAtt.addColumn();
 
    fireTableStructureChanged();
  }
 
  public void addRow(Vector rowData) {
    Vector newData = null;
    if (rowData == null) {
      newData = new Vector(getColumnCount());
    }
    else {
      rowData.setSize(getColumnCount());
    }
    dataVector.addElement(newData);
 
    //
    cellAtt.addRow();
 
    newRowsAdded(new TableModelEvent(this, getRowCount()-1, getRowCount()-1,
       TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  }
 
  public void insertRow(int row, Vector rowData) {
    if (rowData == null) {
      rowData = new Vector(getColumnCount());
    }
    else {
      rowData.setSize(getColumnCount());
    }
 
    dataVector.insertElementAt(rowData, row);
 
    //
    cellAtt.insertRow(row);
 
    newRowsAdded(new TableModelEvent(this, row, row,
       TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  }
 
  public CellAttribute getCellAttribute() {
    return cellAtt;
  }
 
  public void setCellAttribute(CellAttribute newCellAtt) {
    int numColumns = getColumnCount();
    int numRows    = getRowCount();
    if ((newCellAtt.getSize().width  != numColumns) ||
        (newCellAtt.getSize().height != numRows)) {
      newCellAtt.setSize(new Dimension(numRows, numColumns));
    }
    cellAtt = newCellAtt;
    fireTableDataChanged();
  }
 
  /*
  public void changeCellAttribute(int row, int column, Object command) {
    cellAtt.changeAttribute(row, column, command);
  }
 
  public void changeCellAttribute(int[] rows, int[] columns, Object command) {
    cellAtt.changeAttribute(rows, columns, command);
  }
  */
     
}

DefaultCellAttribute.java:

import java.awt.*;
 
/** 
 * @version 1.0 11/22/98
 */
 
public class DefaultCellAttribute 
//    implements CellAttribute ,CellSpan  {
      implements CellAttribute ,CellSpan ,ColoredCell ,CellFont {

  //
  // !!!! CAUTION !!!!!
  // these values must be synchronized to Table data
  //
  protected int rowSize;
  protected int columnSize;
  protected int[][][] span;                   // CellSpan
  protected Color[][] foreground;             // ColoredCell
  protected Color[][] background;             //
  protected Font[][]  font;                   // CellFont
   
  public DefaultCellAttribute() {
    this(1,1);
  }
   
  public DefaultCellAttribute(int numRows, int numColumns) {
    setSize(new Dimension(numColumns, numRows));
  }
 
  protected void initValue() {
    for(int i=0; i<span.length;i++) {
      for(int j=0; j<span[i].length; j++) {
        span[i][j][CellSpan.COLUMN] = 1;
        span[i][j][CellSpan.ROW]    = 1;
      }
    }
  }
 
 
  //
  // CellSpan
  //
  public int[] getSpan(int row, int column) {
    if (isOutOfBounds(row, column)) {
      int[] ret_code = {1,1};
      return ret_code;
    }
    return span[row][column];
  }
 
  public void setSpan(int[] span, int row, int column) {
    if (isOutOfBounds(row, column)) return;
    this.span[row][column] = span;
  }
       
  public boolean isVisible(int row, int column) {
    if (isOutOfBounds(row, column)) return false;
    if ((span[row][column][CellSpan.COLUMN] < 1)
      ||(span[row][column][CellSpan.ROW]    < 1)) return false;
    return true;
  }
 
  public void combine(int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns)) return;
    int    rowSpan  = rows.length;
    int columnSpan  = columns.length;
    int startRow    = rows[0];
    int startColumn = columns[0];
    for (int i=0;i<rowSpan;i++) {
      for (int j=0;j<columnSpan;j++) {
        if ((span[startRow +i][startColumn +j][CellSpan.COLUMN] != 1)
          ||(span[startRow +i][startColumn +j][CellSpan.ROW]    != 1)) {
          //System.out.println("can't combine");
          return ;
        }
      }
    }
    for (int i=0,ii=0;i<rowSpan;i++,ii--) {
      for (int j=0,jj=0;j<columnSpan;j++,jj--) {
        span[startRow +i][startColumn +j][CellSpan.COLUMN] = jj;
        span[startRow +i][startColumn +j][CellSpan.ROW]    = ii;
        //System.out.println("r " +ii +"  c " +jj);
      }
    }
    span[startRow][startColumn][CellSpan.COLUMN] = columnSpan;
    span[startRow][startColumn][CellSpan.ROW]    =    rowSpan;
    
  }
 
  public void split(int row, int column) {
    if (isOutOfBounds(row, column)) return;
    int columnSpan = span[row][column][CellSpan.COLUMN];
    int    rowSpan = span[row][column][CellSpan.ROW];
    for (int i=0;i<rowSpan;i++) {
      for (int j=0;j<columnSpan;j++) {
        span[row +i][column +j][CellSpan.COLUMN] = 1;
        span[row +i][column +j][CellSpan.ROW]    = 1;
      }
    }
  }
 
 
  //
  // ColoredCell
  //
  public Color getForeground(int row, int column) {
    if (isOutOfBounds(row, column)) return null;
    return foreground[row][column];
  }
  public void setForeground(Color color, int row, int column) {
    if (isOutOfBounds(row, column)) return;
    foreground[row][column] = color;
  }
  public void setForeground(Color color, int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns)) return;
    setValues(foreground, color, rows, columns);
  }
  public Color getBackground(int row, int column) {
    if (isOutOfBounds(row, column)) return null;
    return background[row][column];
  }
  public void setBackground(Color color, int row, int column) {
    if (isOutOfBounds(row, column)) return;
    background[row][column] = color;
  }
  public void setBackground(Color color, int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns)) return;
    setValues(background, color, rows, columns);
  }
  //
 
 
  //
  // CellFont
  //
  public Font getFont(int row, int column) {
    if (isOutOfBounds(row, column)) return null;
    return font[row][column];
  }
  public void setFont(Font font, int row, int column) {
    if (isOutOfBounds(row, column)) return;
    this.font[row][column] = font;
  }
  public void setFont(Font font, int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns)) return;
    setValues(this.font, font, rows, columns);
  }
  // 
 
 
  //
  // CellAttribute
  //
  public void addColumn() {
    int[][][] oldSpan = span;
    int numRows    = oldSpan.length;
    int numColumns = oldSpan[0].length;
    span = new int[numRows][numColumns + 1][2];
    System.arraycopy(oldSpan,0,span,0,numRows);
    for (int i=0;i<numRows;i++) {
      span[i][numColumns][CellSpan.COLUMN] = 1;
      span[i][numColumns][CellSpan.ROW]    = 1;
    }
  }
 
  public void addRow() {
    int[][][] oldSpan = span;
    int numRows    = oldSpan.length;
    int numColumns = oldSpan[0].length;
    span = new int[numRows + 1][numColumns][2];
    System.arraycopy(oldSpan,0,span,0,numRows);
    for (int i=0;i<numColumns;i++) {
      span[numRows][i][CellSpan.COLUMN] = 1;
      span[numRows][i][CellSpan.ROW]    = 1;
    }
  }
 
  public void insertRow(int row) {
    int[][][] oldSpan = span;
    int numRows    = oldSpan.length;
    int numColumns = oldSpan[0].length;
    span = new int[numRows + 1][numColumns][2];
    if (0 < row) {
      System.arraycopy(oldSpan,0,span,0,row-1);
    }
    System.arraycopy(oldSpan,0,span,row,numRows - row);
    for (int i=0;i<numColumns;i++) {
      span[row][i][CellSpan.COLUMN] = 1;
      span[row][i][CellSpan.ROW]    = 1;
    }
  }
 
  public Dimension getSize() {
    return new Dimension(rowSize, columnSize);
  }
 
  public void setSize(Dimension size) {
    columnSize = size.width;
    rowSize    = size.height;
    span = new int[rowSize][columnSize][2];   // 2: COLUMN,ROW
    foreground = new Color[rowSize][columnSize];
    background = new Color[rowSize][columnSize];
    font = new Font[rowSize][columnSize];
    initValue();
  }
 
  /*
  public void changeAttribute(int row, int column, Object command) {
  }
 
  public void changeAttribute(int[] rows, int[] columns, Object command) {
  }
  */
 
  protected boolean isOutOfBounds(int row, int column) {
    if ((row    < 0)||(rowSize    <= row)
      ||(column < 0)||(columnSize <= column)) {
      return true;
    }
    return false;
  }
 
  protected boolean isOutOfBounds(int[] rows, int[] columns) {
    for (int i=0;i<rows.length;i++) {
      if ((rows[i] < 0)||(rowSize <= rows[i])) return true;
    }
    for (int i=0;i<columns.length;i++) {
      if ((columns[i] < 0)||(columnSize <= columns[i])) return true;
    }
    return false;
  }
 
  protected void setValues(Object[][] target, Object value,
                           int[] rows, int[] columns) {
    for (int i=0;i<rows.length;i++) {
      int row = rows[i];
      for (int j=0;j<columns.length;j++) {
        int column = columns[j];
        target[row][column] = value;
      }
    }
  }
}

CellAttribute.java:

import java.awt.*;

/**
 * @version 1.0 11/22/98
 */

public interface CellAttribute {
  public void addColumn();
  public void addRow();
  public void insertRow(int row);
  public Dimension getSize();
  public void setSize(Dimension size);
}

ColoredCell.java:

import java.awt.*;
 
/**
 * @version 1.0 11/22/98
 */
 
public interface ColoredCell {
   
  public Color getForeground(int row, int column);
  public void setForeground(Color color, int row, int column);
  public void setForeground(Color color, int[] rows, int[] columns);
  public Color getBackground(int row, int column);
  public void setBackground(Color color, int row, int column);
  public void setBackground(Color color, int[] rows, int[] columns);
}

CellFont.java:

/*
 * (swing1.1beta3)
 * 
 */
 
import java.awt.*;
 
/**
 * @version 1.0 11/22/98
 */
 
public interface CellFont {
  public Font getFont(int row, int column);
  public void setFont(Font font, int row, int column);
  public void setFont(Font font, int[] rows, int[] columns);
}

CellSpan.java:

/*
 * (swing1.1beta3)
 * 
 */
 
/**
 * @version 1.0 11/22/98
 */
 
public interface CellSpan {
  public final int ROW    = 0;
  public final int COLUMN = 1;
   
  public int[] getSpan(int row, int column);
  public void setSpan(int[] span, int row, int column);
   
  public boolean isVisible(int row, int column);
 
  public void combine(int[] rows, int[] columns);
  public void split(int row, int column);
}

MultiSpanCellTable.java:

import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.plaf.basic.*;
import javax.swing.event.*;
 
/**
 * @version 1.0 11/26/98
 */
 
public class MultiSpanCellTable extends JTable {
 
  public MultiSpanCellTable(TableModel model) {
    super(model);
    setUI(new MultiSpanCellTableUI());
    getTableHeader().setReorderingAllowed(false);
    setCellSelectionEnabled(true);
    setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
  }
   
  public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
    Rectangle sRect = super.getCellRect(row,column,includeSpacing);
    if ((row <0) || (column<0) ||
        (getRowCount() <= row) || (getColumnCount() <= column)) {
        return sRect;
    }
    CellSpan cellAtt = (CellSpan)((AttributiveCellTableModel)getModel()).getCellAttribute();
    if (! cellAtt.isVisible(row,column)) {
      int temp_row    = row;
      int temp_column = column;
      row    += cellAtt.getSpan(temp_row,temp_column)[CellSpan.ROW];
      column += cellAtt.getSpan(temp_row,temp_column)[CellSpan.COLUMN];      
    }
    int[] n = cellAtt.getSpan(row,column);
 
    int index = 0;
    int columnMargin = getColumnModel().getColumnMargin();
    Rectangle cellFrame = new Rectangle();
    int aCellHeight = rowHeight + rowMargin;
    cellFrame.y = row * aCellHeight;
    cellFrame.height = n[CellSpan.ROW] * aCellHeight;
     
    Enumeration enumeration = getColumnModel().getColumns();
    while (enumeration.hasMoreElements()) {
      TableColumn aColumn = (TableColumn)enumeration.nextElement();
      cellFrame.width = aColumn.getWidth() + columnMargin;
      if (index == column) break;
      cellFrame.x += cellFrame.width;
      index++;
    }
    for (int i=0;i< n[CellSpan.COLUMN]-1;i++) {
      TableColumn aColumn = (TableColumn)enumeration.nextElement();
      cellFrame.width += aColumn.getWidth() + columnMargin;
    }
     
    if (!includeSpacing) {
      Dimension spacing = getIntercellSpacing();
      cellFrame.setBounds(cellFrame.x +      spacing.width/2,
                          cellFrame.y +      spacing.height/2,
                          cellFrame.width -  spacing.width,
                          cellFrame.height - spacing.height);
    }
    return cellFrame;
  }
   
   
  private int[] rowColumnAtPoint(Point point) {
    int[] retValue = {-1,-1};
    int row = point.y / (rowHeight + rowMargin);
    if ((row <0)||(getRowCount() <= row)) return retValue;
    int column = getColumnModel().getColumnIndexAtX(point.x);
 
    CellSpan cellAtt = (CellSpan)((AttributiveCellTableModel)getModel()).getCellAttribute();
 
    if (cellAtt.isVisible(row,column)) {
      retValue[CellSpan.COLUMN] = column;
      retValue[CellSpan.ROW   ] = row;
      return retValue;
    }
    retValue[CellSpan.COLUMN] = column + cellAtt.getSpan(row,column)[CellSpan.COLUMN];
    retValue[CellSpan.ROW   ] = row + cellAtt.getSpan(row,column)[CellSpan.ROW];
    return retValue;
  }
 
   
  public int rowAtPoint(Point point) {
    return rowColumnAtPoint(point)[CellSpan.ROW];
  }
 
  public int columnAtPoint(Point point) {
    return rowColumnAtPoint(point)[CellSpan.COLUMN];
  }
  
  public void columnSelectionChanged(ListSelectionEvent e) {
    repaint();
  }
 
  public void valueChanged(ListSelectionEvent e) {
    int firstIndex = e.getFirstIndex();
    int  lastIndex = e.getLastIndex();
    if (firstIndex == -1 && lastIndex == -1) { // Selection cleared.
      repaint();
    }
    Rectangle dirtyRegion = getCellRect(firstIndex, 0, false);
    int numCoumns = getColumnCount();
    int index = firstIndex;
    for (int i=0;i<numCoumns;i++) {
      dirtyRegion.add(getCellRect(index, i, false));
    }
    index = lastIndex;
    for (int i=0;i<numCoumns;i++) {
      dirtyRegion.add(getCellRect(index, i, false));
    }
    repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
  }
  
}

MultiSpanCellTableUI.java:

import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.plaf.basic.*;
 
/**
 * @version 1.0 11/26/98
 */
 
public class MultiSpanCellTableUI extends BasicTableUI {
 
  public void paint(Graphics g, JComponent c) {
    Rectangle oldClipBounds = g.getClipBounds();
    Rectangle clipBounds    = new Rectangle(oldClipBounds);
    int tableWidth   = table.getColumnModel().getTotalColumnWidth();
    clipBounds.width = Math.min(clipBounds.width, tableWidth);
    g.setClip(clipBounds);
 
    int firstIndex = table.rowAtPoint(new Point(0, clipBounds.y));
    int  lastIndex = table.getRowCount()-1;
 
    Rectangle rowRect = new Rectangle(0,0,
      tableWidth, table.getRowHeight() + table.getRowMargin());
    rowRect.y = firstIndex*rowRect.height;
 
    for (int index = firstIndex; index <= lastIndex; index++) {
      if (rowRect.intersects(clipBounds)) {
        //System.out.println();                  // debug
        //System.out.print("" + index +": ");    // row
        paintRow(g, index);
      }
      rowRect.y += rowRect.height;
    }
    g.setClip(oldClipBounds);
  }
 
  private void paintRow(Graphics g, int row) {
    Rectangle rect = g.getClipBounds();
    boolean drawn  = false;
    
    AttributiveCellTableModel tableModel = (AttributiveCellTableModel)table.getModel();
    CellSpan cellAtt = (CellSpan)tableModel.getCellAttribute();
    int numColumns = table.getColumnCount();
 
    for (int column = 0; column < numColumns; column++) {
      Rectangle cellRect = table.getCellRect(row,column,true);
      int cellRow,cellColumn;
      if (cellAtt.isVisible(row,column)) {
        cellRow    = row;
        cellColumn = column;
          //  System.out.print("   "+column+" ");  // debug
      } else {
        cellRow    = row + cellAtt.getSpan(row,column)[CellSpan.ROW];
        cellColumn = column + cellAtt.getSpan(row,column)[CellSpan.COLUMN];
          //  System.out.print("  ("+column+")");  // debug
      }
      if (cellRect.intersects(rect)) {
        drawn = true;
        paintCell(g, cellRect, cellRow, cellColumn);
      } else {
        if (drawn) break;
      } 
    }
 
  }
 
  private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {
    int spacingHeight = table.getRowMargin();
    int spacingWidth  = table.getColumnModel().getColumnMargin();
 
    Color c = g.getColor();
    g.setColor(table.getGridColor());
    g.drawRect(cellRect.x,cellRect.y,cellRect.width-1,cellRect.height-1);
    g.setColor(c);
 
    cellRect.setBounds(cellRect.x + spacingWidth/2, cellRect.y + spacingHeight/2,
                       cellRect.width - spacingWidth, cellRect.height - spacingHeight);
 
    if (table.isEditing() && table.getEditingRow()==row &&
        table.getEditingColumn()==column) {
      Component component = table.getEditorComponent();
      component.setBounds(cellRect);
      component.validate();
    }
    else {
      TableCellRenderer renderer = table.getCellRenderer(row, column);
      Component component = table.prepareRenderer(renderer, row, column);

      if (component.getParent() == null) {
        rendererPane.add(component);
      }
      rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
                                  cellRect.width, cellRect.height, true);
    }
  }    
}