import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.DefaultCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.JLabel; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.BorderFactory; import javax.swing.border.Border; import javax.swing.JScrollPane; import javax.swing.JPanel; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.JOptionPane; import java.awt.*; import java.awt.event.*; /** * This is like TableEditDemo, except that it substitutes a * Favorite Color column for the Last Name column and specifies * a custom cell renderer and editor for the color data. */ public class TableDialogEditDemo extends JFrame { private boolean DEBUG = false; public TableDialogEditDemo() { super("TableDialogEditDemo"); MyTableModel myModel = new MyTableModel(); JTable table = new JTable(myModel); table.setPreferredScrollableViewportSize(new Dimension(500, 70)); //Create the scroll pane and add the table to it. JScrollPane scrollPane = new JScrollPane(table); //Set up renderer and editor for the Favorite Color column. setUpColorRenderer(table); setUpColorEditor(table); //Set up real input validation for integer data. setUpIntegerEditor(table); //Add the scroll pane to this window. //XXX: Perhaps a scroll pane should never be a content pane? setContentPane(scrollPane); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } class ColorRenderer extends JLabel implements TableCellRenderer { Border unselectedBorder = null; Border selectedBorder = null; boolean isBordered = true; public ColorRenderer(boolean isBordered) { super(); this.isBordered = isBordered; setOpaque(true); //MUST do this for background to show up. } public Component getTableCellRendererComponent( JTable table, Object color, boolean isSelected, boolean hasFocus, int row, int column) { setBackground((Color)color); if (isBordered) { if (isSelected) { if (selectedBorder == null) { selectedBorder = BorderFactory.createMatteBorder(2,5,2,5, table.getSelectionBackground()); } setBorder(selectedBorder); } else { if (unselectedBorder == null) { unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5, table.getBackground()); } setBorder(unselectedBorder); } } return this; } } private void setUpColorRenderer(JTable table) { table.setDefaultRenderer(Color.class, new ColorRenderer(true)); } //Set up the editor for the Color cells. private void setUpColorEditor(JTable table) { //First, set up the button that brings up the dialog. final JButton button = new JButton("") { public void setText(String s) { //Button never shows text -- only color. } }; button.setBackground(Color.white); button.setBorderPainted(false); button.setMargin(new Insets(0,0,0,0)); //Now create an editor to encapsulate the button, and //set it up as the editor for all Color cells. final ColorEditor colorEditor = new ColorEditor(button); table.setDefaultEditor(Color.class, colorEditor); //Set up the dialog that the button brings up. final JColorChooser colorChooser = new JColorChooser(); //XXX: PENDING: add the following when setPreviewPanel //XXX: starts working. //JComponent preview = new ColorRenderer(false); //preview.setPreferredSize(new Dimension(50, 10)); //colorChooser.setPreviewPanel(preview); ActionListener okListener = new ActionListener() { public void actionPerformed(ActionEvent e) { colorEditor.currentColor = colorChooser.getColor(); } }; final JDialog dialog = JColorChooser.createDialog(button, "Pick a Color", true, colorChooser, okListener, null); //XXXDoublecheck this is OK //Here's the code that brings up the dialog. button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { button.setBackground(colorEditor.currentColor); colorChooser.setColor(colorEditor.currentColor); //Without the following line, the dialog comes up //in the middle of the screen. //dialog.setLocationRelativeTo(button); dialog.show(); } }); } /* * The editor button that brings up the dialog. * We extend DefaultCellEditor for convenience, * even though it mean we have to create a dummy * check box. Another approach would be to copy * the implementation of TableCellEditor methods * from the source code for DefaultCellEditor. */ class ColorEditor extends DefaultCellEditor { Color currentColor = null; public ColorEditor(JButton b) { super(new JCheckBox()); //Unfortunately, the constructor //expects a check box, combo box, //or text field. editorComponent = b; setClickCountToStart(1); //This is usually 1 or 2. //Must do this so that editing stops when appropriate. b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { fireEditingStopped(); } }); } protected void fireEditingStopped() { super.fireEditingStopped(); } public Object getCellEditorValue() { return currentColor; } public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { ((JButton)editorComponent).setText(value.toString()); currentColor = (Color)value; return editorComponent; } } private void setUpIntegerEditor(JTable table) { //Set up the editor for the integer cells. final WholeNumberField integerField = new WholeNumberField(0, 5); integerField.setHorizontalAlignment(WholeNumberField.RIGHT); DefaultCellEditor integerEditor = new DefaultCellEditor(integerField) { //Override DefaultCellEditor's getCellEditorValue method //to return an Integer, not a String: public Object getCellEditorValue() { return new Integer(integerField.getValue()); } }; table.setDefaultEditor(Integer.class, integerEditor); } class MyTableModel extends AbstractTableModel { final String[] columnNames = {"First Name", "Favorite Color", "Sport", "# of Years", "Vegetarian"}; final Object[][] data = { {"Mary", new Color(153, 0, 153), "Snowboarding", new Integer(5), new Boolean(false)}, {"Alison", new Color(51, 51, 153), "Rowing", new Integer(3), new Boolean(true)}, {"Kathy", new Color(51, 102, 51), "Chasing toddlers", new Integer(2), new Boolean(false)}, {"Mark", Color.blue, "Speed reading", new Integer(20), new Boolean(true)}, {"Philip", Color.pink, "Pool", new Integer(7), new Boolean(false)} }; public int getColumnCount() { return columnNames.length; } public int getRowCount() { return data.length; } public String getColumnName(int col) { return columnNames[col]; } public Object getValueAt(int row, int col) { return data[row][col]; } /* * JTable uses this method to determine the default renderer/ * editor for each cell. If we didn't implement this method, * then the last column would contain text ("true"/"false"), * rather than a check box. */ public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } /* * Don't need to implement this method unless your table's * editable. */ public boolean isCellEditable(int row, int col) { //Note that the data/cell address is constant, //no matter where the cell appears onscreen. if (col < 1) { return false; } else { return true; } } public void setValueAt(Object value, int row, int col) { if (DEBUG) { System.out.println("Setting value at " + row + "," + col + " to " + value + " (an instance of " + value.getClass() + ")"); } data[row][col] = value; if (DEBUG) { System.out.println("New value of data:"); printDebugData(); } } private void printDebugData() { int numRows = getRowCount(); int numCols = getColumnCount(); for (int i=0; i < numRows; i++) { System.out.print(" row " + i + ":"); for (int j=0; j < numCols; j++) { System.out.print(" " + data[i][j]); } System.out.println(); } System.out.println("--------------------------"); } } public static void main(String[] args) { TableDialogEditDemo frame = new TableDialogEditDemo(); frame.pack(); frame.setVisible(true); } }