L&F mapping JTable
The look and feel values chosen for the JTable under windows do not feel right to me (albeit that I cannot find many examples of tables under windows).
I am trying to provide some workarounds that follow what most people expect to happen but am a little unclear on what should happen on some key presses for the JTable in windows look and feel
Space handling
From one of the MS windows books it suggests that the space key is a selection key, thus when you press space a row in a table should be selected (and when you press again it should deselect the row?)
The JList has functionality for the space key
q1) JTable should have space bar handling out of the box?
(space, CTRL+space, shift+space)
I have not seen this raised as a bug.
Enter handling
The enter key press is supposed to activate the default button
or commit a text field under edit.
F2 puts a field under edit
q2) Should the enter key put an editable cell under edit (same mouse action as single or double click to edit)?
q3) if not should the first enter key stop cell editing and a second press activate the default button, with only a single enter to activate default button if a cell is not under edit
Escape handling
The escape key will cancel editing of a cell that is under edit.
q4) should the event not be passed on when editing has been cancelled so that dialogs with a canel button can be dismissed
or
q5) should the first escape cancel editing while the second escape will action the escape action of the window.
Any links to "highly regarded" useability/look and feel sites would be helpful - sun does not appear to provide much on this subject
I've read both their l&f and advanced l&f books and they just scratch the surface of look and feel without much realworld appliance to real gui issues.
Might be useful to others: Some of the code that I was trying to create goes like....
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.math.BigDecimal;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.CellEditor;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
/** Provides utility methods for the JTable and JCSortableTable classes
* @author */
public final class JTableUtil
* Constructor for JCTableUtil.
private JTableUtil()
super();
/** will resize the last column of a table to fill the gap
* if the width of the columns is narrower than the width of the
* table
* @param table to act upon
static void resizeLastColumn(final JTable table)
TableColumn lastColumn = getLastVisibleColumn(table);
if (lastColumn != null)
int gap = getLastColumnGap(table);
if (gap > 0)
int newLastColumnWidth = gap + lastColumn.getWidth();
lastColumn.setWidth(newLastColumnWidth);
lastColumn.setPreferredWidth(newLastColumnWidth);
redrawTable(table);
* Determines if there is a gap between the last column in a table
* and the border of the table.
* Works if the table is drawn on the page or is in the
* viewport of a scroll pane.
* @param table to act upon
* @return the gap in pixels between last column and table edge.
static int getLastColumnGap(final JTable table)
int widthTable = getTableWidth(table);
int lastVisColIndex = getLastVisibleColumnIndex(table);
TableColumnModel columnModel = table.getColumnModel();
int widthColumns = columnModel.getTotalColumnWidth();
final TableColumn lastColumn = columnModel.getColumn(lastVisColIndex);
// gap is number of pixels from right hand edge of last column
// to right hand edge of the table
int gap = widthTable - widthColumns;
return gap;
* Determines the width of a table returning the table width if
* table is painted onto a panel, but if the table is painted
* into a scroll pane then it is the scroll pane viewport width that is returned
* This is to capture that the width of the table may be less than the
* width of the viewport - ie if there is a gap after the last column in a table.
* @param table to act upon
* @return the width of the table in pixels
static int getTableWidth(final JTable table)
int widthTable = table.getWidth();
Object tableParent = table.getParent();
JViewport tableViewPort = null;
if (tableParent instanceof JViewport)
tableViewPort = (JViewport) tableParent;
widthTable = tableViewPort.getWidth();
return widthTable;
/** Cause the table to redraw wether table is painted on a panel
* or in a scroll pane
* @param table to act upon
static void redrawTable(final JTable table)
Component tableRegion = table;
Component tableParent = table.getParent();
if (tableParent instanceof JViewport)
tableRegion = tableParent;
tableRegion.invalidate();
tableRegion.validate();
/** Determines the last (right most) column in a table with a width
* greater than 0
* @param table to act upon
* @return index (0 based) of the last visible column.
static int getLastVisibleColumnIndex(JTable table)
TableColumnModel columnModel = table.getColumnModel();
boolean found = false;
int columnIndex = columnModel.getColumnCount();
while (!found && columnIndex > 0)
columnIndex--;
if (columnModel.getColumn(columnIndex).getWidth() != 0)
found = true;
return columnIndex;
/** Determines the last (right most) column in a table with a width
* greater than 0
* @param table to act upon
* @return TableColumn - the last visible column.
static TableColumn getLastVisibleColumn(JTable table)
TableColumnModel columnModel = table.getColumnModel();
return columnModel.getColumn(getLastVisibleColumnIndex(table));
* Add the currency symbol to a JCTable/JCListTable column header
* e.g. 'Salary' becomes 'Salary (�)'
public static void addCurrencySymbol(JTable table, Object columnKey)
//@todo - update to work with JTable
TableColumnModel columnModel = table.getTableHeader().getColumnModel();
int columnIndex = columnModel.getColumnIndex(columnKey);
TableColumn column = columnModel.getColumn(columnIndex);
Object object = column.getHeaderValue();
if (object instanceof JLabel)
JLabel columnLabel = (JLabel) object;
SlacCurrency.getInstance().addCurrencySymbol(columnLabel);
else
// @todo log here ???
// is above correct - need to get the JLabel rendered from the table
// in the scroll pane?
/** Provides a sum of a column of BigDecimals
* @param tableModel model containing column that you wish to sum
* @param column the column that you wish to be summed, must contain numeric values and not strings
public static BigDecimal calculateTotalForColumn(final TableModel tableModel, final int column)
int numRows = tableModel.getRowCount();
BigDecimal total = new BigDecimal("0");
for (int row = 0; row < numRows; row++)
Object value = tableModel.getValueAt(row, column);
if (value instanceof BigDecimal)
BigDecimal decimalValue = (BigDecimal) value;
total = total.add(decimalValue);
else
//Logger.logGeneralFailure("", ErrorCodes.GUI_ERROR, this);
return total;
* Provides the swing setting JTable.AUTO_RESIZE_OFF.
* <p>
* In this situation if the width of the columns is smaller than the width
* of the table then there will be a gap after the last column.
* <p>
* This method will add a ComponentListener to the table and or
* scroll pane viewport so that there is no gap after the last column.
* @param table to act upon
public static void configureAutoResizeOff(final JTable table)
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
Component tableParent = table.getParent();
JViewport tableViewPort = null;
ComponentAdapter gapFiller = new ComponentAdapter()
public void componentResized(ComponentEvent e)
super.componentResized(e);
JTableUtil.resizeLastColumn(table);
table.addComponentListener(gapFiller);
if (tableParent instanceof JViewport)
tableViewPort = (JViewport) tableParent;
tableViewPort.addComponentListener(gapFiller);
/** Method provides fixes to provide standard handling for keyboard and mouse usage
* <ul>
* <li> spacebar - selection handling of rows that have focus emphasis see fixSpacebarHandling()
* <li> initial focus handling - table should give visual feedback when gaining focus
* <li>
* </ul>
* @param table - the table to add standards handling to
public static void setupKeyboardMouseHandling(JTable table)
fixTableGainsFocusOnFirstEntry(table);
fixSpacebarHandling(table);
fixCtrlSpacebarHandling(table);
fixShiftSpacebarHandling(table);
fixCtrlUpDownHandling(table);
fixFocusHighlight();
fixEscEnter(table);
/** Add fixes to the look and feel handling for a JTable
* Enter on a table will do different things depending on the mode of the table
* <p>
* if a cell is being edited then
* Enter will do what normally happens to a field under edit - ie stop the editing and commit
* Escape will cancel the editing
* <p>
* if a cell is not under edit then
* Enter will activate the default button
* Escape will activate the default cancel button (see FrameUtil.addEscapeKeyAction())
static void fixEscEnter(final JTable table)
final RootPaneContainer root = (RootPaneContainer)SwingUtilities.windowForComponent(table);
final String escapeKey = "escapeAction";
Action escapeAction = new AbstractAction(escapeKey)
public void actionPerformed(ActionEvent actionEvent)
if(table.isEditing())
CellEditor editor = table.getCellEditor(table.getEditingRow(), table.getEditingColumn());
editor.cancelCellEditing();
else
Window parentWindow = SwingUtilities.windowForComponent(table);
Action windowEscapeAction = FrameUtil.getEscapeKeyAction(root);
windowEscapeAction.actionPerformed(actionEvent);
final String enterKey = "enterAction";
Action enterAction = new AbstractAction(enterKey)
public void actionPerformed(ActionEvent actionEvent)
if(table.isEditing())
CellEditor editor = table.getCellEditor(table.getEditingRow(), table.getEditingColumn());
editor.stopCellEditing();
else
root.getRootPane().getDefaultButton().doClick();
InputMap inputMap = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
table.getActionMap().put(escapeKey, escapeAction);
table.getActionMap().put(enterKey, enterAction);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), escapeKey);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), enterKey);
static void fixFocusHighlight()
Border focusCellHighlightBorder = new DashedLineBorder(Color.BLACK);
UIManager.put("Table.focusCellHighlightBorder", focusCellHighlightBorder);
/** If you do not setup a table to have a row selected then there is a bug where for example
* you use an accelerator key to put focus in the table and you get no visual feedback that
* the table has focus - ie no focus emphasis - dashed box
* @param table - the table to fix
static void fixTableGainsFocusOnFirstEntry(JTable table)
// for first time tabbing to table make sure that first cell has focus
// don't seem to need this fix under windows XP
table.getSelectionModel().removeSelectionInterval(0, 0);
/** fix spacebar handling
* java standards does not mention spacebar handling on a JTable but this is a windows
* standard feature and is also a feature on JList.
* Without spacebar handling on a JTable there would be no other keyboard handling that
* would allow you to select the current row.
* <p>
* Trying to follow windows standards since java does not list space bar handling for JTable.
* The following bahaviour can be seen in IBM code in project Java build path - tab libraries
* and in microsoft code in the administrative tools control panel dialog in windows XP
* <p>
* spacebar - select the current row without deselecting any others
* <p>
* handling should be fixed in merlin release 1.5
* see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
static void fixSpacebarHandling(JTable table)
KeyStroke ksSpace = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true);
final String ACTION_SPACE = "SPACE";
Action spaceAction = new AbstractAction(ACTION_SPACE)
public void actionPerformed(ActionEvent actionEvent)
ListSelectionModel selectModel =
((JTable) actionEvent.getSource()).getSelectionModel();
int currentRowIndex = selectModel.getAnchorSelectionIndex();
selectModel.addSelectionInterval(currentRowIndex, currentRowIndex);
table.getInputMap().put(ksSpace, ACTION_SPACE);
table.getActionMap().put(ACTION_SPACE, spaceAction);
/** fix ctrl + spacebar handling
* java standards does not mention spacebar handling on a JTable but this is a windows
* standard feature and is also a feature on JList.
* Without spacebar handling on a JTable there would be no other keyboard handling that
* would allow you to select the current row.
* <p>
* Trying to follow windows standards since java does not list space bar handling for JTable.
* The following bahaviour can be seen in IBM code in project Java build path - tab libraries
* and in microsoft code in the administrative tools control panel dialog in windows XP
* <p>
* ctrl + spacebar - toggle selection on the current row without deselecting any others
* <p>
* handling should be fixed in merlin release 1.5
* see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
static void fixCtrlSpacebarHandling(JTable table)
KeyStroke ksCtrlSpace = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, Event.CTRL_MASK, true);
final String ACTION_CTRL_SPACE = "CTRLSPACE";
Action ctrlSpaceAction = new AbstractAction(ACTION_CTRL_SPACE)
public void actionPerformed(ActionEvent actionEvent)
ListSelectionModel selectModel =
((JTable) actionEvent.getSource()).getSelectionModel();
int currentRowIndex = selectModel.getAnchorSelectionIndex();
boolean isCurrentRowSelected = selectModel.isSelectedIndex(currentRowIndex);
if (isCurrentRowSelected)
selectModel.removeSelectionInterval(currentRowIndex, currentRowIndex);
else
selectModel.addSelectionInterval(currentRowIndex, currentRowIndex);
table.getInputMap().put(ksCtrlSpace, ACTION_CTRL_SPACE);
table.getActionMap().put(ACTION_CTRL_SPACE, ctrlSpaceAction);
/** fix shift + spacebar handling
* java standards does not mention spacebar handling on a JTable but this is a windows
* standard feature and is also a feature on JList.
* Without spacebar handling on a JTable there would be no other keyboard handling that
* would allow you to select the current row.
* <p>
* Trying to follow windows standards since java does not list space bar handling for JTable.
* The following bahaviour can be seen in IBM code in project Java build path - tab libraries
* and in microsoft code in the administrative tools control panel dialog in windows XP
* <p>
* shift + spacebar - extend the selection from the anchor to the lead index.
* this might still be a bit funny in java 1.4.2 and code may need to be changed slightly
* <p>
* handling should be fixed in merlin release 1.5
* see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
static void fixShiftSpacebarHandling(JTable table)
KeyStroke ksShiftSpace = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, Event.SHIFT_MASK, true);
final String ACTION_SHIFT_SPACE = "SHIFTSPACE";
Action shiftSpaceAction = new AbstractAction(ACTION_SHIFT_SPACE)
public void actionPerformed(ActionEvent actionEvent)
ListSelectionModel selectModel =
((JTable) actionEvent.getSource()).getSelectionModel();
int currentRowIndex = selectModel.getAnchorSelectionIndex();
int startRowIndex = selectModel.getLeadSelectionIndex();
selectModel.setSelectionInterval(startRowIndex, currentRowIndex);
table.getInputMap().put(ksShiftSpace, ACTION_SHIFT_SPACE);
table.getActionMap().put(ACTION_SHIFT_SPACE, shiftSpaceAction);
/** fix ctrl + down or up handling - move focus emphasis up or down accordingly
* <p>
* handling should be fixed in merlin release 1.5
* see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
static void fixCtrlUpDownHandling(JTable table)
final String ACTION_UP = "CTRLUP";
KeyStroke ksCtrlUp = KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.CTRL_MASK, true);
KeyStroke ksCtrlDown = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.CTRL_MASK, true);
Action ctrlUpAction = new CtrlUpDownAction(ACTION_UP, true);
final String ACTION_DOWN = "CTRLDOWN";
Action ctrlDownAction = new CtrlUpDownAction(ACTION_DOWN, false);
table.getInputMap().put(ksCtrlUp, ACTION_UP);
table.getActionMap().put(ACTION_UP, ctrlUpAction);
table.getInputMap().put(ksCtrlDown, ACTION_DOWN);
table.getActionMap().put(ACTION_DOWN, ctrlDownAction);
* Action for moving focus emphasis up or down
class CtrlUpDownAction extends AbstractAction
private boolean m_isUp = true;
/** Ctor controlling direction of focus emphasis
* @param name of the action
* @param isUp - true if dirction of action is up, else false
public CtrlUpDownAction(String name, boolean isUp)
super(name);
m_isUp = isUp;
/** Moves the focus emphasis
* @param actionEvent - see javax.swing.AbstractAction class for details
public void actionPerformed(ActionEvent actionEvent)
JTable table = (JTable) actionEvent.getSource();
ListSelectionModel selectModel = table.getSelectionModel();
int nextRowIndex =
getNextRowIndex(table.getRowCount(), selectModel.getAnchorSelectionIndex());
if (selectModel.isSelectedIndex(nextRowIndex))
selectModel.addSelectionInterval(nextRowIndex, nextRowIndex);
else
selectModel.removeSelectionInterval(nextRowIndex, nextRowIndex);
/** Gets the index of the next row depending on direction up or down
* @param rowCount - number of rows in the table
* @param currentRowIndex - index of the row that has focus emphasis
* @return the index of the next row
private int getNextRowIndex(int rowCount, int currentRowIndex)
int nextRowIndex = -1;
if (m_isUp && currentRowIndex > 0)
// emphasis moving up one row
nextRowIndex = currentRowIndex - 1;
else if (!m_isUp && currentRowIndex < (rowCount - 1))
// emphasis moving down one row
nextRowIndex = currentRowIndex + 1;
return nextRowIndex;
/** Draws a dashed line (well more of a dotted line)
* Used for eg drawing focus emphasis rectangle
* Class draws a rectangle of chosen colour overlaid with a dashed white line.
class DashedLineBorder extends LineBorder
private BasicStroke m_lineStroke = new BasicStroke();
private BasicStroke m_dashStroke =
new BasicStroke(
m_lineStroke.getLineWidth(),
BasicStroke.CAP_BUTT,//m_lineStroke.getEndCap(),
m_lineStroke.getLineJoin(),
m_lineStroke.getMiterLimit(),
new float[] { 1,1,1 },
0);
* Constructor for DashedLineBorder.
* @param color
public DashedLineBorder(Color color)
super(color);
* Constructor for DashedLineBorder.
* @param color
* @param thickness
public DashedLineBorder(Color color, int thickness)
super(color, thickness);
* Constructor for DashedLineBorder.
* @param color
* @param thickness
* @param roundedCorners
public DashedLineBorder(Color color, int thickness, boolean roundedCorners)
super(color, thickness, roundedCorners);
/** Similar to LineBorder's method but draws a line in the chosen colour
* before drawing a white dashed line on top of it
* @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int)
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
Color oldColor = g.getColor();
int i;
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(m_lineStroke);
g.setColor(lineColor);
drawRect(g, x, y, width, height);
g2.setStroke(m_dashStroke);
g.setColor(Color.WHITE);
drawRect(g, x, y, width, height);
g.setColor(oldColor);
* @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int)
private void drawRect(Graphics g, int x, int y, int width, int height)
// extracted from Java LineBorder.paintBorder
int i;
/// PENDING(klobad) How/should do we support Roundtangles?
for (i = 0; i < thickness; i++)
if (!roundedCorners)
g.drawRect(x + i, y + i, width - i - i - 1, height - i - i - 1);
else
g.drawRoundRect(
x + i,
y + i,
width - i - i - 1,
height - i - i - 1,
thickness,
thickness);
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.ImageProducer;
import java.io.IOException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.RootPaneContainer;
* Provides static utility methods for JFrames.
public final class FrameUtil {
* No instances allowed. Just use the static methods.
private FrameUtil() {
super();
* Sets the button that is "activated" when the escape key is used.
* A bit like
* SwingUtilities.getRootPane(this).setDefaultButton(myButton);
* @param jFrame The JFrame or JDialog for which the escape key button should be set.
* @param button The button which should be clicked automatically when the escape key is pressed.
public static void setEscapeKeyButton(final RootPaneContainer jFrame, final JButton button) {
addEscapeKeyAction(jFrame, new AbstractAction() {
public void actionPerformed(final ActionEvent evt) {
button.doClick();
* Adds an action to the jframe's action map that is triggered by the Escape key.
* (If you just want to simulate a button click when the escape key is pressed then
* use setEscapeKeyButton.)
* A bit like
* SwingUtilities.getRootPane(this).setDefaultButton(myButton);
* @param jFrame The JFrame or JDialog for which the escape key action should be set.
* @param action The action that is executred when the escape key us pressed,
public static void addEscapeKeyAction(final RootPaneContainer jFrame, final Action action) {
String actionKey = "escapeAction";
KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
// using getRootPane rather than getContentPane, content pane doesn't work in all
// scenarios
InputMap inputMap = (jFrame.getRootPane()).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(escape, actionKey);
ActionMap actionMap = (jFrame.getRootPane()).getActionMap();
actionMap.put(actionKey, action);
* Finds the escape action for the given frame or dialog if one was added
* @param jFrame The JFrame or JDialog for which the escape key action should be set.
public static Action getEscapeKeyAction(final RootPaneContainer jFrame)
InputMap inputMap = (jFrame.getRootPane()).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
ActionMap actionMap = (jFrame.getRootPane()).getActionMap();
Object actionKey = inputMap.get(escapeKeyStroke);
Action escapeAction = null;
if(actionKey != null)
escapeAction = actionMap.get(actionKey);
return escapeAction;
From one of the MS windows books.....Going by the books, eh?
Well, apparently MS doesn't do so with Excel... Pressing space will simply trigger an edit!
q1) JTable should have space bar handling out of the box?
(space, CTRL+space, shift+space)
I have not seen this raised as a bug.A bug, no... a feature, maybe. Anyway, JTable provides most of the features needed for creating a table -- it's far from perfect..... adding support for CTRL+SPACE and SHIFT+SPACE is not all that hard to do.
q2) Should the enter key put an editable cell under edit and
q3) if not should the first enter key stop cell editing and a second press activate the default buttonIMHO, a good answer to your question can be found by running MS Excel and see how it's done.
The escape key will cancel editing of a cell that is under edit.and q4, q5
It should, as in Excel. But then, in Lotus 1-2-3, first escape will clear the edit buffer (unless it's empty) and the second escape will cancel editing. Who'is more programmatically correct (I don't know)?
;o)
V.V.
Similar Messages
-
Mapping of JTable Rows and columns and paint it to the JPanel
Hi,
I am using JTable and graphics object to draw the JTable on JPanel. But due to some lack of measurement I am not able to draw table cells correctly.
Apart from this on changing the fontSize I have to redraw it according to the requirement. But when the data size increases it draws absurdly.
I am using jTable.getCellRect() api to get the row width and height. Please help to redraw a JTable cell row and height on JPanel.
I am also attaching a sample code with this mail.
Thanks,
Ajay
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.*;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.table.*;
import java.util.*;
import java.awt.*;
public class SimpleTableDemo extends JPanel {
private boolean DEBUG = false;
private int spacing = 6;
private Map columnSizes = new HashMap();
String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
Object[][] data = {
{"Kathy", "Smith",
"SnowboardingXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new Integer(5), new Boolean(false)},
{"John", "Doe",
"Rowing", new Integer(3), new Boolean(true)},
{"Sue", "Black",
"Knitting", new Integer(2), new Boolean(false)},
{"Jane", "White",
"Speed reading", new Integer(20), new Boolean(true)},
{"Joe", "Brown",
"Pool", new Integer(10), new Boolean(false)}
final JTable table = new JTable(data, columnNames);
Panel1 panel;
public SimpleTableDemo() {
super(new GridLayout(3,0));
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
//table.setFillsViewportHeight(true);
if (DEBUG) {
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
printDebugData(table);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
add(scrollPane);
panel = new Panel1();
Rectangle rect = table.getCellRect(0,0,true);
panel.setX(table.getWidth());
panel.setY(0);
panel.setWidth(rect.width);
panel.setHeight(rect.height);
panel.setStr(table.getModel().getValueAt(0,0).toString());
panel.setModel(table);
add(panel);
final JComboBox jNumberComboBoxSize = new JComboBox();
jNumberComboBoxSize.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "11", "12", "14", "16", "18", "20", "24", "30", "36", "48", "72" }));
jNumberComboBoxSize.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jNumberComboBoxSizeActionPerformed(jNumberComboBoxSize);
JPanel panel2 = new JPanel();
panel2.add(jNumberComboBoxSize);
add(panel2);
adjustColumns();
private void jNumberComboBoxSizeActionPerformed(JComboBox jNumberComboBoxSize)
int fontSize = Integer.parseInt(jNumberComboBoxSize.getSelectedItem().toString());
table.setRowHeight(fontSize);
table.setFont(new Font("Serif", Font.BOLD, fontSize));
Rectangle rect = table.getCellRect(0,0,true);
panel.setX(0);
panel.setY(0);
// panel.setWidth(rect.width);
panel.setHeight(rect.height);
panel.setStr(table.getModel().getValueAt(0,0).toString());
panel.setModel(table);
panel.repaint();
table.revalidate();
private void printDebugData(JTable table) {
int numRows = table.getRowCount();
int numCols = table.getColumnCount();
javax.swing.table.TableModel model = table.getModel();
System.out.println("Value of data: ");
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + model.getValueAt(i, j));
System.out.println();
System.out.println("--------------------------");
* Adjust the widths of all the columns in the table
public void adjustColumns()
TableColumnModel tcm = table.getColumnModel();
for (int i = 0; i < tcm.getColumnCount(); i++)
adjustColumn(i);
* Adjust the width of the specified column in the table
public void adjustColumn(final int column)
TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (! tableColumn.getResizable()) return;
int columnHeaderWidth = getColumnHeaderWidth( column );
int columnDataWidth = getColumnDataWidth( column );
int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);
panel.setWidth(preferredWidth);
updateTableColumn(column, preferredWidth);
* Calculated the width based on the column name
private int getColumnHeaderWidth(int column)
TableColumn tableColumn = table.getColumnModel().getColumn(column);
Object value = tableColumn.getHeaderValue();
TableCellRenderer renderer = tableColumn.getHeaderRenderer();
if (renderer == null)
renderer = table.getTableHeader().getDefaultRenderer();
Component c = renderer.getTableCellRendererComponent(table, value, false, false, -1, column);
return c.getPreferredSize().width;
* Calculate the width based on the widest cell renderer for the
* given column.
private int getColumnDataWidth(int column)
int preferredWidth = 0;
int maxWidth = table.getColumnModel().getColumn(column).getMaxWidth();
for (int row = 0; row < table.getRowCount(); row++)
preferredWidth = Math.max(preferredWidth, getCellDataWidth(row, column));
// We've exceeded the maximum width, no need to check other rows
if (preferredWidth >= maxWidth)
break;
return preferredWidth;
* Get the preferred width for the specified cell
private int getCellDataWidth(int row, int column)
// Inovke the renderer for the cell to calculate the preferred width
TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
Component c = table.prepareRenderer(cellRenderer, row, column);
int width = c.getPreferredSize().width + table.getIntercellSpacing().width;
return width;
* Update the TableColumn with the newly calculated width
private void updateTableColumn(int column, int width)
final TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (! tableColumn.getResizable()) return;
width += spacing;
// Don't shrink the column width
width = Math.max(width, tableColumn.getPreferredWidth());
columnSizes.put(tableColumn, new Integer(tableColumn.getWidth()));
table.getTableHeader().setResizingColumn(tableColumn);
tableColumn.setWidth(width);
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("SimpleTableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
SimpleTableDemo newContentPane = new SimpleTableDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
class Panel1 extends JPanel
int x;
int y;
int width;
int height;
String str;
JTable model;
public void setModel(JTable model)
this.model = model;
public void setX(int x)
this.x = x;
public void setY(int y)
this.y = y;
public void setWidth(int w)
this.width = w;
public void setHeight(int h)
this.height = h;
public void setStr(String s)
this.str = s;
public void paint(Graphics g)
super.paint(g);
int initX= 0;
for(int row=0;row < 5; ++row)
initX = x;
for (int col=0;col < 5;++col)
g.drawRect(x,y,width,height);
g.drawString(model.getModel().getValueAt(row,col).toString(),x + 10,y + 10);
x = x + width;
x = initX;
y = y + height;
};an easy way would be to use setSize()
-
Mouselistner is not working with jtable in the browser
Hi
I am having a problem with jTable.
I added a mouselistener to table header to sort table but when i run that applet from my netbean ide it works fine but when i run that applet in my browser it doesn't work, i have tested, its not even generate mouseclick event .Please help me guys.
I call this function after calling initComponents() method of JApplet.
public void setTableAction()
//set mouselistener to sort table on click of table header
final JTableHeader head= jTable1.getTableHeader();
head.addMouseListener(new java.awt.event.MouseAdapter()
public void mouseClicked(java.awt.event.MouseEvent evt)
Vector data= ((DefaultTableModel)jTable1.getModel ()).getDataVector();
sortTable(data, head.columnAtPoint(evt.getPoint()));
//set action map to change the default action performed for enter key pressed
InputMap imap = jTable1.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
KeyStroke tabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
final Action oldAction= jTable1.getActionMap().get(imap.get(tabKey)); // get map to set enter key.
imap.put(enterKey, "enter"); // set enter key
Action newAction = new AbstractAction(){
public void actionPerformed(ActionEvent e) {
oldAction.actionPerformed(e);
JTable table= (JTable)e.getSource();
table.changeSelection(0,0,false,false);
if(table.isCellEditable(0,0))
String sTemp= (String)table.getValueAt(0,0);
if(sTemp.length()>0) {
if(bRenewItem)
retrieveRcodeDetails("",sTemp);
else
processRCodeDetails(sTemp, e);
}else
table.editCellAt(0,0);
jTable1.getActionMap().put("enter", newAction);
jTable1.setPreferredScrollableViewportSize(jTable1.getPreferredSize());
}Hi,
I also am using the Bépo layout with an encrypted drive and encountered the same problem: the Return key does not work.
It seems to work fine if you use the fr-bepo-latin9 keymap.
# /etc/vconsole.conf
KEYMAP=fr-bepo-latin9
But I also looked at the files /usr/share/kbd/keymaps/i386/bepo/fr-bepo.map.gz and /usr/share/kbd/keymaps/i386/bepo/fr-bepo-latin9.map.gz (you can open gzipped files in vim directly). fr-bepo-latin9.map.gz defines keycode 28 (Return) but fr-bepo.map.gz does not.
I modified fr-bepo.map.gz:
# vim /usr/share/kbd/keymaps/i386/bepo/fr-bepo.map.gz # Append that line : "keycode 28 = Return".
# mkinitcpio -p linux # Rebuild the initramfs.
The Return key now works, but the Backspace (14, "Delete") and Shift (54) keys don’t work. I found that both the cf.map.gz (french canadian layout) and fr-bepo-latin9.map.gz files define those keycodes as well as other non-printing keys so I copied the following lines from fr-bepo-latin9.map.gz to fr-bepo.map.gz:
keycode 1 = Escape Escape
keycode 14 = Delete Delete
keycode 15 = Tab Tab
keycode 28 = Return
keycode 29 = Control
keycode 42 = Shift
keycode 54 = Shift
keycode 56 = Alt
keycode 58 = Caps_Lock
keycode 97 = Control
It works! Don’t forget to rebuild the initramfs after you change the keymap file.
# mkinitcpio -p linux
I will send a message to the kbd and bépo projects mailing lists and report back. -
How can I change the default process of VK_ENTER in JTable
Now I have met a requirment to process the "enter" key event in JTable. But it is found that the JTable has a default process on the ENTER key press, and it will select the next row. Even the worse, the default process is invoked before the KeyListener I registered.
How can I remove that?
I have tried the follow ways: 1.remove several keys, such as "selectNextRow","selectNextRowExtendSelection", from the actionMap,
2.invoke processKeyBinding() in the constructor of my subclass of JTable.
But both lead to failure.
And I can't find the default process to the VK_ENTER in the java source code. I need help on this.
BTW, does anyone know how to eliminate the focus traversal between cells. When I press left arrow or right arrow, the focus traverses in the scope of the selected row.thanks
i also found solution for my poblem:
i have this code:
jMenuItem jMnItEdit;
jMnItEdit.setAccelerator( KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ENTER, 0));
also i had ActionListener bounded to this JMenuItem
but Ialso had in form JTable and it always grabbed my VK_ENTER event and that menu item wasn't working at all
but when i overrode that input map for JTable and set
tableInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "none");
everything started to work perfectly,
thanks a lot -
One model for JTree and JTable
Hi.
Is it possible for a JTree and a JTable to share one model?
Thank youHope u r not using Comonent TreeTable
If u have Tree & Table different components, just want to have a common model then u can try this,
It is nothing but default implementation given in DefaultTableModel
public class MyTreeTableModel extends DefaultTreeModel implements TableModel, Serializable {
protected Vector dataVector;
/** List of listeners */
protected EventListenerList listenerList = new EventListenerList();
/** The <code>Vector</code> of column identifiers. */
protected Vector columnIdentifiers;
public MyTreeTableModel(TreeNode root) {
this(root, false);
// constructor for TreeModel only
public MyTreeTableModel(TreeNode root, boolean asksAllowsChildren) {
super(root, asksAllowsChildren);
// constructor for TableModel only
public MyTreeTableModel() {
this(0, 0);
private static Vector newVector(int size) {
Vector v = new Vector(size);
v.setSize(size);
return v;
// constructor for TableModel only
public MyTreeTableModel(int rowCount, int columnCount) {
this(newVector(columnCount), rowCount);
// constructor for TableModel only
public MyTreeTableModel(Vector columnNames, int rowCount) {
super(null);
setDataVector(newVector(rowCount), columnNames);
// constructor for TableModel only
public MyTreeTableModel(Object[] columnNames, int rowCount) {
this(convertToVector(columnNames), rowCount);
// constructor for TableModel only
public MyTreeTableModel(Vector data, Vector columnNames) {
super(null);
setDataVector(data, columnNames);
// constructor for TableModel only
public MyTreeTableModel(Object[][] data, Object[] columnNames) {
super(null);
setDataVector(data, columnNames);
* Returns a default name for the column using spreadsheet conventions:
* A, B, C, ... Z, AA, AB, etc. If <code>column</code> cannot be found,
* returns an empty string.
* @param column the column being queried
* @return a string containing the default name of <code>column</code>
private String getDefaultColumnName(int column) {
String result = "";
for (; column >= 0; column = column / 26 - 1) {
result = (char)((char)(column%26)+'A') + result;
return result;
* Returns a column given its name.
* Implementation is naive so this should be overridden if
* this method is to be called often. This method is not
* in the <code>TableModel</code> interface and is not used by the
* <code>JTable</code>.
* @param columnName string containing name of column to be located
* @return the column with <code>columnName</code>, or -1 if not found
public int findColumn(String columnName) {
for (int i = 0; i < getColumnCount(); i++) {
if (columnName.equals(getColumnName(i))) {
return i;
return -1;
* Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
* @param columnIndex the column being queried
* @return the Object.class
public Class getColumnClass(int columnIndex) {
return Object.class;
// Managing Listeners
* Adds a listener to the list that's notified each time a change
* to the data model occurs.
* @param l the TableModelListener
public void addTableModelListener(TableModelListener l) {
listenerList.add(TableModelListener.class, l);
* Removes a listener from the list that's notified each time a
* change to the data model occurs.
* @param l the TableModelListener
public void removeTableModelListener(TableModelListener l) {
listenerList.remove(TableModelListener.class, l);
* Returns an array of all the table model listeners
* registered on this model.
* @return all of this model's <code>TableModelListener</code>s
* or an empty
* array if no table model listeners are currently registered
public TableModelListener[] getTableModelListeners() {
return (TableModelListener[])listenerList.getListeners(
TableModelListener.class);
// Fire methods
* Notifies all listeners that all cell values in the table's
* rows may have changed. The number of rows may also have changed
* and the <code>JTable</code> should redraw the
* table from scratch. The structure of the table (as in the order of the
* columns) is assumed to be the same.
public void fireTableDataChanged() {
fireTableChanged(new TableModelEvent(this));
* Notifies all listeners that the table's structure has changed.
* The number of columns in the table, and the names and types of
* the new columns may be different from the previous state.
* If the <code>JTable</code> receives this event and its
* <code>autoCreateColumnsFromModel</code>
* flag is set it discards any table columns that it had and reallocates
* default columns in the order they appear in the model. This is the
* same as calling <code>setModel(TableModel)</code> on the
* <code>JTable</code>.
public void fireTableStructureChanged() {
fireTableChanged(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
* Notifies all listeners that rows in the range
* <code>[firstRow, lastRow]</code>, inclusive, have been inserted.
* @param firstRow the first row
* @param lastRow the last row
public void fireTableRowsInserted(int firstRow, int lastRow) {
fireTableChanged(new TableModelEvent(this, firstRow, lastRow,
TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
* Notifies all listeners that rows in the range
* <code>[firstRow, lastRow]</code>, inclusive, have been updated.
* @param firstRow the first row
* @param lastRow the last row
public void fireTableRowsUpdated(int firstRow, int lastRow) {
fireTableChanged(new TableModelEvent(this, firstRow, lastRow,
TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE));
* Notifies all listeners that rows in the range
* <code>[firstRow, lastRow]</code>, inclusive, have been deleted.
* @param firstRow the first row
* @param lastRow the last row
public void fireTableRowsDeleted(int firstRow, int lastRow) {
fireTableChanged(new TableModelEvent(this, firstRow, lastRow,
TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
* Notifies all listeners that the value of the cell at
* <code>[row, column]</code> has been updated.
* @param row row of cell which has been updated
* @param column column of cell which has been updated
public void fireTableCellUpdated(int row, int column) {
fireTableChanged(new TableModelEvent(this, row, row, column));
* Forwards the given notification event to all
* <code>TableModelListeners</code> that registered
* themselves as listeners for this table model.
* @param e the event to be forwarded
public void fireTableChanged(TableModelEvent e) {
// 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==TableModelListener.class) {
((TableModelListener)listeners[i+1]).tableChanged(e);
* Returns an array of all the objects currently registered
* as <code><em>Foo</em>Listener</code>s
* upon this <code>AbstractTableModel</code>.
* <code><em>Foo</em>Listener</code>s are registered using the
* <code>add<em>Foo</em>Listener</code> method.
* <p>
* You can specify the <code>listenerType</code> argument
* with a class literal,
* such as
* <code><em>Foo</em>Listener.class</code>.
* For example, you can query a
* model <code>m</code>
* for its table model listeners with the following code:
* <pre>TableModelListener[] tmls = (TableModelListener[])(m.getListeners(TableModelListener.class));</pre>
* If no such listeners exist, this method returns an empty array.
* @param listenerType the type of listeners requested; this parameter
* should specify an interface that descends from
* <code>java.util.EventListener</code>
* @return an array of all objects registered as
* <code><em>Foo</em>Listener</code>s on this component,
* or an empty array if no such
* listeners have been added
* @exception ClassCastException if <code>listenerType</code>
* doesn't specify a class or interface that implements
* <code>java.util.EventListener</code>
public EventListener[] getListeners(Class listenerType) {
return listenerList.getListeners(listenerType);
* Returns the <code>Vector</code> of <code>Vectors</code>
* that contains the table's
* data values. The vectors contained in the outer vector are
* each a single row of values. In other words, to get to the cell
* at row 1, column 5: <p>
* <code>((Vector)getDataVector().elementAt(1)).elementAt(5);</code><p>
* @return the vector of vectors containing the tables data values
public Vector getDataVector() {
return dataVector;
private static Vector nonNullVector(Vector v) {
return (v != null) ? v : new Vector();
* Replaces the current <code>dataVector</code> instance variable
* with the new Vector of rows, <code>dataVector</code>.
* <code>columnIdentifiers</code> are the names of the new
* columns. The first name in <code>columnIdentifiers</code> is
* mapped to column 0 in <code>dataVector</code>. Each row in
* <code>dataVector</code> is adjusted to match the number of
* columns in <code>columnIdentifiers</code>
* either by truncating the <code>Vector</code> if it is too long,
* or adding <code>null</code> values if it is too short.
* <p>Note that passing in a <code>null</code> value for
* <code>dataVector</code> results in unspecified behavior,
* an possibly an exception.
* @param dataVector the new data vector
* @param columnIdentifiers the names of the columns
public void setDataVector(Vector dataVector, Vector columnIdentifiers) {
this.dataVector = nonNullVector(dataVector);
this.columnIdentifiers = nonNullVector(columnIdentifiers);
justifyRows(0, getRowCount());
fireTableStructureChanged();
* Replaces the value in the <code>dataVector</code> instance
* variable with the values in the array <code>dataVector</code>.
* The first index in the <code>Object[][]</code>
* array is the row index and the second is the column index.
* <code>columnIdentifiers</code> are the names of the new columns.
* @param dataVector the new data vector
* @param columnIdentifiers the names of the columns
public void setDataVector(Object[][] dataVector, Object[] columnIdentifiers) {
setDataVector(convertToVector(dataVector), convertToVector(columnIdentifiers));
* Equivalent to <code>fireTableChanged</code>.
* @param event the change event
public void newDataAvailable(TableModelEvent event) {
fireTableChanged(event);
// Manipulating rows
private void justifyRows(int from, int to) {
// Sometimes the MyTreeTableModel is subclassed
// instead of the AbstractTableModel by mistake.
// Set the number of rows for the case when getRowCount
// is overridden.
dataVector.setSize(getRowCount());
for (int i = from; i < to; i++) {
if (dataVector.elementAt(i) == null) {
dataVector.setElementAt(new Vector(), i);
((Vector)dataVector.elementAt(i)).setSize(getColumnCount());
* Ensures that the new rows have the correct number of columns.
* This is accomplished by using the <code>setSize</code> method in
* <code>Vector</code> which truncates vectors
* which are too long, and appends <code>null</code>s if they
* are too short.
* This method also sends out a <code>tableChanged</code>
* notification message to all the listeners.
* @param e this <code>TableModelEvent</code> describes
* where the rows were added.
* If <code>null</code> it assumes
* all the rows were newly added
public void newRowsAdded(TableModelEvent e) {
justifyRows(e.getFirstRow(), e.getLastRow() + 1);
fireTableChanged(e);
* Equivalent to <code>fireTableChanged</code>.
* @param event the change event
public void rowsRemoved(TableModelEvent event) {
fireTableChanged(event);
* Obsolete as of Java 2 platform v1.3. Please use <code>setRowCount</code> instead.
* Sets the number of rows in the model. If the new size is greater
* than the current size, new rows are added to the end of the model
* If the new size is less than the current size, all
* rows at index <code>rowCount</code> and greater are discarded. <p>
* @param rowCount the new number of rows
public void setNumRows(int rowCount) {
int old = getRowCount();
if (old == rowCount) {
return;
dataVector.setSize(rowCount);
if (rowCount <= old) {
fireTableRowsDeleted(rowCount, old-1);
else {
justifyRows(old, rowCount);
fireTableRowsInserted(old, rowCount-1);
* Sets the number of rows in the model. If the new size is greater
* than the current size, new rows are added to the end of the model
* If the new size is less than the current size, all
* rows at index <code>rowCount</code> and greater are discarded. <p>
public void setRowCount(int rowCount) {
setNumRows(rowCount);
* Adds a row to the end of the model. The new row will contain
* <code>null</code> values unless <code>rowData</code> is specified.
* Notification of the row being added will be generated.
* @param rowData optional data of the row being added
public void addRow(Vector rowData) {
insertRow(getRowCount(), rowData);
* Adds a row to the end of the model. The new row will contain
* <code>null</code> values unless <code>rowData</code> is specified.
* Notification of the row being added will be generated.
* @param rowData optional data of the row being added
public void addRow(Object[] rowData) {
addRow(convertToVector(rowData));
* Inserts a row at <code>row</code> in the model. The new row
* will contain <code>null</code> values unless <code>rowData</code>
* is specified. Notification of the row being added will be generated.
* @param row the row index of the row to be inserted
* @param rowData optional data of the row being added
* @exception ArrayIndexOutOfBoundsException if the row was invalid
public void insertRow(int row, Vector rowData) {
dataVector.insertElementAt(rowData, row);
justifyRows(row, row+1);
fireTableRowsInserted(row, row);
* Inserts a row at <code>row</code> in the model. The new row
* will contain <code>null</code> values unless <code>rowData</code>
* is specified. Notification of the row being added will be generated.
* @param row the row index of the row to be inserted
* @param rowData optional data of the row being added
* @exception ArrayIndexOutOfBoundsException if the row was invalid
public void insertRow(int row, Object[] rowData) {
insertRow(row, convertToVector(rowData));
private static int gcd(int i, int j) {
return (j == 0) ? i : gcd(j, i%j);
private static void rotate(Vector v, int a, int b, int shift) {
int size = b - a;
int r = size - shift;
int g = gcd(size, r);
for(int i = 0; i < g; i++) {
int to = i;
Object tmp = v.elementAt(a + to);
for(int from = (to + r) % size; from != i; from = (to + r) % size) {
v.setElementAt(v.elementAt(a + from), a + to);
to = from;
v.setElementAt(tmp, a + to);
* Moves one or more rows from the inlcusive range <code>start</code> to
* <code>end</code> to the <code>to</code> position in the model.
* After the move, the row that was at index <code>start</code>
* will be at index <code>to</code>.
* This method will send a <code>tableChanged</code> notification
* message to all the listeners. <p>
* <pre>
* Examples of moves:
* <p>
* 1. moveRow(1,3,5);
* a|B|C|D|e|f|g|h|i|j|k - before
* a|e|f|g|h|B|C|D|i|j|k - after
* <p>
* 2. moveRow(6,7,1);
* a|b|c|d|e|f|G|H|i|j|k - before
* a|G|H|b|c|d|e|f|i|j|k - after
* <p>
* </pre>
* @param start the starting row index to be moved
* @param end the ending row index to be moved
* @param to the destination of the rows to be moved
* @exception ArrayIndexOutOfBoundsException if any of the elements
* would be moved out of the table's range
public void moveRow(int start, int end, int to) {
int shift = to - start;
int first, last;
if (shift < 0) {
first = to;
last = end;
else {
first = start;
last = to + end - start;
rotate(dataVector, first, last + 1, shift);
fireTableRowsUpdated(first, last);
* Removes the row at <code>row</code> from the model. Notification
* of the row being removed will be sent to all the listeners.
* @param row the row index of the row to be removed
* @exception ArrayIndexOutOfBoundsException if the row was invalid
public void removeRow(int row) {
dataVector.removeElementAt(row);
fireTableRowsDeleted(row, row);
// Manipulating columns
* Replaces the column identifiers in the model. If the number of
* <code>newIdentifier</code>s is greater than the current number
* of columns, new columns are added to the end of each row in the model.
* If the number of <code>newIdentifier</code>s is less than the current
* number of columns, all the extra columns at the end of a row are
* discarded. <p>
* @param newIdentifiers vector of column identifiers. If
* <code>null</code>, set the model
* to zero columns
public void setColumnIdentifiers(Vector columnIdentifiers) {
setDataVector(dataVector, columnIdentifiers);
* Replaces the column identifiers in the model. If the number of
* <code>newIdentifier</code>s is greater than the current number
* of columns, new columns are added to the end of each row in the model.
* If the number of <code>newIdentifier</code>s is less than the current
* number of columns, all the extra columns at the end of a row are
* discarded. <p>
* @param newIdentifiers array of column identifiers.
* If <code>null</code>, set
* the model to zero columns
public void setColumnIdentifiers(Object[] newIdentifiers) {
setColumnIdentifiers(convertToVector(newIdentifiers));
* Sets the number of columns in the model. If the new size is greater
* than the current size, new columns are added to the end of the model
* with <code>null</code> cell values.
* If the new size is less than the current size, all columns at index
* <code>columnCount</code> and greater are discarded.
* @param columnCount the new number of columns in the model
public void setColumnCount(int columnCount) {
columnIdentifiers.setSize(columnCount);
justifyRows(0, getRowCount());
fireTableStructureChanged();
* Adds a column to the model. The new column will have the
* identifier <code>columnName</code>, which may be null. This method
* will send a
* <code>tableChanged</code> notification message to all the listeners.
* This method is a cover for <code>addColumn(Object, Vector)</code> which
* uses <code>null</code> as the data vector.
* @param columnName the identifier of the column being added
public void addColumn(Object columnName) {
addColumn(columnName, (Vector)null);
* Adds a column to the model. The new column will have the
* identifier <code>columnName</code>, which may be null.
* <code>columnData</code> is the
* optional vector of data for the column. If it is <code>null</code>
* the column is filled with <code>null</code> values. Otherwise,
* the new data will be added to model starting with the first
* element going to row 0, etc. This method will send a
* <code>tableChanged</code> notification message to all the listeners.
* @param columnName the identifier of the column being added
* @param columnData optional data of the column being added
public void addColumn(Object columnName, Vector columnData) {
columnIdentifiers.addElement(columnName);
if (columnData != null) {
int columnSize = columnData.size();
if (columnSize > getRowCount()) {
dataVector.setSize(columnSize);
justifyRows(0, getRowCount());
int newColumn = getColumnCount() - 1;
for(int i = 0; i < columnSize; i++) {
Vector row = (Vector)dataVector.elementAt(i);
row.setElementAt(columnData.elementAt(i), newColumn);
else {
justifyRows(0, getRowCount());
fireTableStructureChanged();
* Adds a column to the model. The new column will have the
* identifier <code>columnName</code>. <code>columnData</code> is the
* optional array of data for the column. If it is <code>null</code>
* the column is filled with <code>null</code> values. Otherwise,
* the new data will be added to model starting with the first
* element going to row 0, etc. This method will send a
* <code>tableChanged</code> notification message to all the listeners.
public void addColumn(Object columnName, Object[] columnData) {
addColumn(columnName, convertToVector(columnData));
// Implementing the TableModel interface
* Returns the number of rows in this data table.
* @return the number of rows in the model
public int getRowCount() {
return dataVector.size();
* Returns the number of columns in this data table.
* @return the number of columns in the model
public int getColumnCount() {
return columnIdentifiers.size();
* Returns the column name.
* @return a name for this column using the string value of the
* appropriate member in <code>columnIdentifiers</code>.
* If <code>columnIdentifiers</code> does not have an entry
* for this index, returns the default
* name provided by the superclass
public String getColumnName(int column) {
Object id = null;
// This test is to cover the case when
// getColumnCount has been subclassed by mistake ...
if (column < columnIdentifiers.size()) {
id = columnIdentifiers.elementAt(column);
return (id == null) ? getDefaultColumnName(column)
: id.toString();
* Returns true regardless of parameter values.
* @param row the row whose value is to be queried
* @param column the column whose value is to be queried
* @return true
public boolean isCellEditable(int row, int column) {
return true;
* Returns an attribute value for the cell at <code>row</code>
* and <code>column</code>.
* @param row the row whose value is to be queried
* @param column the column whose value is to be queried
* @return the value Object at the specified cell
* @exception ArrayIndexOutOfBoundsException if an invalid row or
* column was given
public Object getValueAt(int row, int column) {
Vector rowVector = (Vector)dataVector.elementAt(row);
return rowVector.elementAt(column);
* Sets the object value for the cell at <code>column</code> and
* <code>row</code>. <code>aValue</code> is the new value. This method
* will generate a <code>tableChanged</code> notification.
* @param aValue the new value; this can be null
* @param row the row whose value is to be changed
* @param column the column whose value is to be changed
* @exception ArrayIndexOutOfBoundsException if an invalid row or
* column was given
public void setValueAt(Object aValue, int row, int column) {
Vector rowVector = (Vector)dataVector.elementAt(row);
rowVector.setElementAt(aValue, column);
fireTableCellUpdated(row, column);
// Protected Methods
* Returns a vector that contains the same objects as the array.
* @param anArray the array to be converted
* @return the new vector; if <code>anArray</code> is <code>null</code>,
* returns <code>null</code>
protected static Vector convertToVector(Object[] anArray) {
if (anArray == null) {
return null;
Vector v = new Vector(anArray.length);
for (int i=0; i < anArray.length; i++) {
v.addElement(anArray[i]);
return v;
* Returns a vector of vectors that contains the same objects as the array.
* @param anArray the double array to be converted
* @return the new vector of vectors; if <code>anArray</code> is
* <code>null</code>, returns <code>null</code>
protected static Vector convertToVector(Object[][] anArray) {
if (anArray == null) {
return null;
Vector v = new Vector(anArray.length);
for (int i=0; i < anArray.length; i++) {
v.addElement(convertToVector(anArray[i]));
return v; -
JTable displays string in wrong order : my code problem or Java problem ?
I have the following code that displays data in a JTable, most of the time it works fine, but I've realized when I have a cell with values like : "Snowboarding"+"[123456789]"
It would display it as : [Snowboarding[123456789
For a string like : "Chasing toddlers"+"<123456789>"
It would display it as : <Chasing toddlers<123456789
It not only moved the last bracket to the front, but also changed its direction from "]" to "[" and from ">" to "<"
Is there anything wrong with my code ? Or is this a Java problem ?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.List;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
public class Table_Panel extends JPanel implements ItemListener
public static final long serialVersionUID=26362862L;
static final int Row_Color_Style_Default=-1,Row_Color_Style_None=0,Row_Color_Style_Blue_Gray=1,Row_Color_Style_Cyan_Gray=2,Row_Color_Style_Blue=3,
Row_Color_Style_Gray=4,Row_Color_Style_Red_Green=5,Row_Color_Style_Green_Yellow=6,Row_Color_Style_Red_Yellow=7,
Max_Header_Cell_Width=0,Header_Width=1,Cell_Width=2;
static int Row_Color_Style=Row_Color_Style_Cyan_Gray,Preffered_Width=Header_Width;
// static int Row_Color_Style=Row_Color_Style_None,Preffered_Width=Header_Width;
//boolean Debug=true,
boolean Debug=false,
// Use_Row_Colors=true,
Use_Row_Colors=false;
JFrame Table_Frame;
TableModel My_Model;
JScrollPane scrollPane=new JScrollPane();
public JTable Table;
static String columnNames[],Default_Delimiter=\",\";
Object[][] data;
static Dimension Screen_Size=Toolkit.getDefaultToolkit().getScreenSize();
int Column_Width[];
static Color Default_Selection_Color=new Color(250,135,200);
Color Selection_Color=Default_Selection_Color;
public Table_Panel(ResultSet RS,String Table_Name,boolean Show_External_Table,int Row_Color_Style,int Preffered_Width,boolean In_Original_Order)
String Value,Table_Column_Names[]=null;
Object[][] Table_Data=null;
int Row_Count,Index=0;
try
if (RS==null) return;
else
RS.last();
Row_Count=RS.getRow();
RS.beforeFirst();
ResultSetMetaData rsmd=RS.getMetaData();
int Column_Count=rsmd.getColumnCount();
Table_Column_Names=new String[Column_Count];
Table_Data=new Object[Row_Count][Column_Count];
for (int i=1;i<=Column_Count;i++) Table_Column_Names[i-1]=rsmd.getColumnLabel(i); // Get column names
//Out("Row_Count="+Row_Count+" Column_Count="+Column_Count);
while (RS.next()) // Get all rows
for (int i=1;i<=Column_Count;i++)
Value=RS.getString(i);
Table_Data[Index][i-1]=(Value==null)?"null":Value;
Index++;
// if (Test_Ct++>300) break;
catch (Exception e) { e.printStackTrace(); }
scrollPane=new Table_Panel(Table_Name,Table_Column_Names,Table_Data,Table_Data.length,false,Show_External_Table,false,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order).scrollPane;
public Table_Panel(String[] Table_Column_Names,Object[][] Table_Data,Color Selection_Color,boolean In_Original_Order)
this("",Table_Column_Names,Table_Data,Table_Data.length,false,false,false,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order);
public Table_Panel(String Table_Name,String[] Table_Column_Names,Object[][] Table_Data,boolean Show_External_Table,Color Selection_Color,boolean In_Original_Order)
this(Table_Name,Table_Column_Names,Table_Data,Table_Data.length,false,Show_External_Table,false,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order);
public Table_Panel(String Table_Name,String[] Table_Column_Names,Object[][] Table_Data,int Row_Count,boolean Is_Child,boolean Show_External_Table,boolean Manage_Attachment,int Row_Color_Style,int Preffered_Width,Color Selection_Color,boolean In_Original_Order)
columnNames=Table_Column_Names;
if (In_Original_Order) data=Table_Data;
else
data=new Object[Table_Data.length][columnNames.length];
for (int row=0;row<Table_Data.length;row++)
data[row]=new Object[columnNames.length];
for (int Column=0;Column<Table_Data[row].length;Column++) data[row][Column]=Table_Data[Table_Data.length-1-row][Column];
Column_Width=new int[columnNames.length];
this.Row_Color_Style=Row_Color_Style;
Use_Row_Colors=(Row_Color_Style!=Row_Color_Style_None);
this.Preffered_Width=Preffered_Width;
if (Selection_Color!=null) this.Selection_Color=Selection_Color;
// Out("this.Selection_Color="+this.Selection_Color);
// TableModel My_Model=new DefaultTableModel(Table_Data,columnNames)
TableModel My_Model=new DefaultTableModel(data,columnNames)
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)
// Out(row+","+col+"["+data[row][col]+"] data.length="+data.length+" data[0].length="+data[0].length);
return (data[row][col]==null)?"":((data[row][col] instanceof Boolean)?data[row][col]:data[row][col].toString()); // Non-boolean values will have bgcolor
public Class getColumnClass(int column)
Class returnValue;
if ((column>=0) && (column<getColumnCount())) returnValue=getValueAt(0,column).getClass();
else returnValue=Object.class;
return returnValue;
// 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<0) return false;
else return true;
// Don't need to implement this method unless your table's data can change.
public void setValueAt(Object value,int row,int col)
String Execution_Dir=getClass().getProtectionDomain().getCodeSource().getLocation().toString();
if (Debug) System.out.println("Execution_Dir="+Execution_Dir+"\nLast_Value="+Table_Grid_Cell_Renderer.Last_Value+" Setting value at [ "+row+","+col+" ] to "+value+" (an instance of "+value.getClass()+")");
if (Execution_Dir.toLowerCase().indexOf("c:/")!=-1 || value.getClass()==Boolean.class || !Use_Row_Colors) data[row][col]=value;
// else data[row][col]=(Table_Grid_CellRenderer.Last_Value==null)?value:value.toString().substring(Table_Grid_CellRenderer.Last_Value.toString().length());
fireTableCellUpdated(row,col);
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("--------------------------------------------------------------------------");
TableSorter sorter=new TableSorter(My_Model);
Table=new JTable(sorter)
// Table=new JTable(My_Model)
public static final long serialVersionUID=26362862L;
public String getToolTipText(MouseEvent e) // Implement table cell tool tips.
Object Cell_Tip;
String tip=null;
java.awt.Point p=e.getPoint();
int rowIndex=rowAtPoint(p);
int colIndex=columnAtPoint(p);
int realColumnIndex=convertColumnIndexToModel(colIndex);
Cell_Tip=getValueAt(rowIndex,colIndex);
// if (realColumnIndex == 2) //Sport column
tip=Cell_Tip.toString();
else if (realColumnIndex == 4) //Veggie column
TableModel model=getModel();
String firstName=(String)model.getValueAt(rowIndex,0);
String lastName=(String)model.getValueAt(rowIndex,1);
Boolean veggie=(Boolean)model.getValueAt(rowIndex,4);
if (Boolean.TRUE.equals(veggie)) tip=firstName+" "+lastName+" is a vegetarian";
else tip=firstName+" "+lastName+" is not a vegetarian";
else
// You can omit this part if you know you don't have any renderers that supply their own tool tips.
tip=super.getToolTipText(e);
return tip;
// RowSorter<TableModel> sorter=new TableRowSorter<TableModel>(My_Model);
// Table.setRowSorter(sorter);
Table.setSelectionBackground(this.Selection_Color);
int Table_Height=Table.getRowHeight()*Row_Count;
// sorter.addMouseListenerToHeaderInTable(Table); // ADDED THIS
sorter.setTableHeader(Table.getTableHeader());
if (Table_Column_Names.length>20) Table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
if (Manage_Attachment) Table.setPreferredScrollableViewportSize(new Dimension(500,(Table_Height>850)?850:Table_Height));
else Table.setPreferredScrollableViewportSize(new Dimension(1000,(Table_Height>850)?850:Table_Height));
if (Use_Row_Colors) Table.setDefaultRenderer(Object.class,new Table_Grid_Cell_Renderer());
//Create the scroll pane and add the table to it.
scrollPane=new JScrollPane(Table);
//Set up column sizes.
initColumnSizes(Table,My_Model);
//Add the scroll pane to this window.
if (Show_External_Table)
Table_Frame=new JFrame(Table_Name);
Table_Frame.getContentPane().add(scrollPane);
Table_Frame.addWindowListener(new WindowAdapter()
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { scrollPane.repaint(); }
public void windowGainedFocus(WindowEvent e) { scrollPane.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { scrollPane.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { scrollPane.repaint(); }
public void windowStateChanged(WindowEvent e) { scrollPane.repaint(); }
Table_Frame.pack();
Table_Frame.setBounds((Screen_Size.width-Table_Frame.getWidth())/2,(Screen_Size.height-Table_Frame.getHeight())/2,Table_Frame.getWidth(),Table_Frame.getHeight());
Table_Frame.setVisible(true);
if (Is_Child) Table_Frame.addComponentListener(new ComponentAdapter() { public void componentClosing(ComponentEvent e) { System.exit(0); } });
else add(scrollPane);
public Table_Panel(String File_Name) { this(File_Name,false,Row_Color_Style_Cyan_Gray,Preffered_Width,null,Default_Selection_Color,true); }
public Table_Panel(String File_Name,int Row_Color_Style,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style,Preffered_Width,null,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,String Delimiter,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style_Cyan_Gray,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,int Preffered_Width,String Delimiter,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,int Row_Color_Style,int Preffered_Width,String Delimiter,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,boolean Show_External_Table,boolean In_Original_Order) { this(File_Name,Show_External_Table,Row_Color_Style,Preffered_Width,null,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,boolean Show_External_Table,String Delimiter,boolean In_Original_Order) { this(File_Name,Show_External_Table,Row_Color_Style,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,boolean Show_External_Table,int Row_Color_Style,int Preffered_Width,String Delimiter,Color Selection_Color,boolean In_Original_Order)
String Table_Column_Names[],Column_Name_Line="";
int Row_Count=0,Index=0;
boolean Is_Child=false,Manage_Attachment=false;
StringTokenizer ST;
if (Delimiter==null) Delimiter=Default_Delimiter;
if (new File(File_Name).exists())
try
String Line,Str=Tool_Lib.Read_File(File_Name).toString();
ST=new StringTokenizer(Str,"\n");
Line=ST.nextToken();
Row_Count=ST.countTokens();
Object[][] Table_Data=new Object[Row_Count][];
if (Delimiter.equals(" ")) Line=Line.replaceAll(" {2,}"," ").trim(); // Replace multiple spaces with the delimiter space
Table_Column_Names=Line.split(Delimiter);
columnNames=Table_Column_Names;
for (int i=0;i<Table_Column_Names.length;i++) Column_Name_Line+=Table_Column_Names[i]+" ";
//Out("Column_Name_Line [ "+Table_Column_Names.length+" ]="+Column_Name_Line);
while (ST.hasMoreTokens())
Line=ST.nextToken();
//Out(Line);
if (Delimiter.equals(" ")) Line=Line.replaceAll(" {2,}"," ").trim(); // Replace multiple spaces with the delimiter space
if (Line.indexOf(Delimiter)!=-1) Table_Data[Index]=Line.split(Delimiter);
else
Table_Data[Index]=new Object[Table_Column_Names.length];
Table_Data[Index][0]=Line;
for (int i=1;i<Table_Column_Names.length;i++) Table_Data[Index]="";
Index++;
Line="";
for (int i=0;i<Table_Data.length;i++)
Line+="[ "+Table_Data[i].length+" ] ";
for (int j=0;j<Table_Data[i].length;j++) Line+=Table_Data[i][j]+" ";
Line+="\n";
Out(Line);
Table_Panel A_Table_Panel=new Table_Panel(File_Name,Table_Column_Names,Table_Data,Row_Count,Is_Child,Show_External_Table,Manage_Attachment,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order);
Table=A_Table_Panel.Table;
scrollPane=A_Table_Panel.scrollPane;
Column_Width=A_Table_Panel.Column_Width;
data=A_Table_Panel.data;
catch (Exception e) { e.printStackTrace(); }
public void setPreferredSize(Dimension A_Dimension) { scrollPane.setPreferredSize(A_Dimension); }
// This method picks good column sizes. If all column heads are wider than the column's cells' contents, then you can just use column.sizeWidthToFit().
void initColumnSizes(JTable table,TableModel model)
TableColumn column=null;
Component comp=null;
int headerWidth=0,cellWidth=0;
Object[] longValues=(data.length>0)?data[0]:null;
TableCellRenderer headerRenderer=table.getTableHeader().getDefaultRenderer();
if (data.length<1) return;
for (int i=0;i<model.getColumnCount();i++)
column=table.getColumnModel().getColumn(i);
comp=headerRenderer.getTableCellRendererComponent(null,column.getHeaderValue(),false,false,0,0);
headerWidth=comp.getPreferredSize().width;
comp=table.getDefaultRenderer(model.getColumnClass(i)).
getTableCellRendererComponent(table,longValues[i],false,false,0,i);
cellWidth=comp.getPreferredSize().width;
switch (Preffered_Width)
case Max_Header_Cell_Width : column.setPreferredWidth(Math.max(headerWidth,cellWidth));break;
case Header_Width : column.setPreferredWidth(headerWidth);break;
case Cell_Width : column.setPreferredWidth(cellWidth);break;
Column_Width[i]=column.getPreferredWidth();
if (Debug) Out("Initializing width of column "+i+". "+"headerWidth="+headerWidth+"; cellWidth="+cellWidth+" Column_Width["+i+"]="+Column_Width[i]);
// Detects a state change in any of the Lists. Resets the variable corresponding to the selected item in a particular List. Invokes changeFont with the currently selected fontname, style and size attributes.
public void itemStateChanged(ItemEvent e)
Out(e.toString());
if (e.getStateChange() != ItemEvent.SELECTED) return;
Object list=e.getSource();
public static void Out(String message) { System.out.println(message); }
// Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
static void Create_And_Show_GUI()
boolean Demo_External_Table=true;
// boolean Demo_External_Table=false;
final Table_Panel demo;
String[] columnNames={"First Names","Last Names","Sport","Num of Years","Vegetarian"};
Object[][] data={{"Mary","Campione","Snowboarding"+"[123456789]",new Integer(5),new Boolean(false)},
{"Alison","Huml","Rowing"+":123456789]",new Integer(3),new Boolean(true)},
{"Frank","Ni","Running"+":123456789", new Integer(12), new Boolean(false)},
{"Kathy","Walrath","Chasing toddlers"+"<123456789>", new Integer(2), new Boolean(false)},
{"Mark", "Andrews","Speed reading",new Integer(20),new Boolean(true)},
{"Angela","Lih","Teaching high school",new Integer(36),new Boolean(false)} };
// Row_Color_Style=Row_Color_Style_Default;
// Row_Color_Style=Row_Color_Style_None;
// Row_Color_Style=Row_Color_Style_Blue_Gray;
Row_Color_Style=Row_Color_Style_Cyan_Gray;
// Row_Color_Style=Row_Color_Style_Blue;
// Row_Color_Style=Row_Color_Style_Gray;
// Row_Color_Style=Row_Color_Style_Red_Green;
// Row_Color_Style=Row_Color_Style_Green_Yellow;
// Row_Color_Style=Row_Color_Style_Red_Yellow;
Preffered_Width=Max_Header_Cell_Width;
if (Demo_External_Table) demo=new Table_Panel("External Table Demo",columnNames,data,data.length,false,Demo_External_Table,false,Row_Color_Style,Max_Header_Cell_Width,Default_Selection_Color,true);
else
JFrame Table_Frame=new JFrame("Internal Table Demo");
// demo=new Table_Panel(Nm_Lib.Dir_A_Test+"ELX.csv",false,Row_Color_Style,Preffered_Width,null);
// demo=new Table_Panel(Nm_Lib.Dir_Stock_Data+"ABV_Data.txt",false,Row_Color_Style,Preffered_Width," ");
demo=new Table_Panel("C:/Dir_Stock_Data/EOP.txt",",",false);
// demo=new Table_Panel(Nm_Lib.Dir_Stock_Data+"ABV_Data.txt"," ",false);
Table_Frame.getContentPane().add(demo.scrollPane);
Table_Frame.addWindowListener(new WindowAdapter()
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { demo.repaint(); }
public void windowGainedFocus(WindowEvent e) { demo.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { demo.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { demo.repaint(); }
public void windowStateChanged(WindowEvent e) { demo.repaint(); }
Table_Frame.pack();
Table_Frame.setBounds((Screen_Size.width-Table_Frame.getWidth())/2,(Screen_Size.height-Table_Frame.getHeight())/2,Table_Frame.getWidth(),Table_Frame.getHeight());
Table_Frame.setVisible(true);
public static void main(String[] args)
// Schedule a job for the event-dispatching thread : creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } });
* TableSorter is a decorator for TableModels; adding sorting functionality to a supplied TableModel. TableSorter does not store
* or copy the data in its TableModel; instead it maintains a map from the row indexes of the view to the row indexes of the
* model. As requests are made of the sorter (like getValueAt(row, col)) they are passed to the underlying model after the row numbers
* have been translated via the internal mapping array. This way, the TableSorter appears to hold another copy of the table
* with the rows in a different order.
* <p/>
* TableSorter registers itself as a listener to the underlying model, just as the JTable itself would. Events recieved from the model
* are examined, sometimes manipulated (typically widened), and then passed on to the TableSorter's listeners (typically the JTable).
* If a change to the model has invalidated the order of TableSorter's rows, a note of this is made and the sorter will resort the
* rows the next time a value is requested.
* <p/>
* When the tableHeader property is set, either by using the setTableHeader() method or the two argument constructor, the table
* header may be used as a complete UI for TableSorter. The default renderer of the tableHeader is decorated with a renderer
* that indicates the sorting status of each column. In addition, a mouse listener is installed with the following behavior:
* <ul>
* <li>
* Mouse-click: Clears the sorting status of all other columns and advances the sorting status of that column through three
* values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED again).
* <li>
* SHIFT-mouse-click: Clears the sorting status of all other columns and cycles the sorting status of the column through the same
* three values, in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}.
* <li>
* CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except that the changes to the column do not cancel the statuses of columns
* that are already sorting - giving a way to initiate a compound sort.
* </ul>
* <p/>
* This is a long overdue rewrite of a class of the same name that first appeared in the swing table demos in 1997.
* @author Philip Milne
* @author Brendon McLean
* @author Dan van Enckevort
* @author Parwinder Sekhon
* @version 2.0 02/27/04
class TableSorter extends AbstractTableModel
public static final long serialVersionUID=26362862L;
protected TableModel tableModel;
public static final int DESCENDING = -1;
public static final int NOT_SORTED = 0;
public static final int ASCENDING = 1;
private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() { public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } };
public static final Comparator LEXICAL_COMPARATOR = new Comparator() { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } };
private Row[] viewToModel;
private int[] modelToView;
private JTableHeader tableHeader;
private MouseListener mouseListener;
private TableModelListener tableModelListener;
private Map<Class,Comparator> columnComparators = new HashMap<Class,Comparator>();
private List<Directive> sortingColumns = new ArrayList<Directive>();
public TableSorter()
this.mouseListener = new MouseHandler();
this.tableModelListener = new TableModelHandler();
public TableSorter(TableModel tableModel)
this();
setTableModel(tableModel);
public TableSorter(TableModel tableModel, JTableHeader tableHeader)
this();
setTableHeader(tableHeader);
setTableModel(tableModel);
private void clearSortingState()
viewToModel = null;
modelToView = null;
public TableModel getTableModel() { return tableModel; }
public void setTableModel(TableModel tableModel)
if (this.tableModel != null) { this.tableModel.removeTableModelListener(tableModelListener); }
this.tableModel = tableModel;
if (this.tableModel != null) { this.tableModel.addTableModelListener(tableModelListener); }
clearSortingState();
fireTableStructureChanged();
public JTableHeader getTableHeader() { return tableHeader; }
public void setTableHeader(JTableHeader tableHeader)
if (this.tableHeader != null)
this.tableHeader.removeMouseListener(mouseListener);
TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
if (defaultRenderer instanceof SortableHeaderRenderer) this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
this.tableHeader = tableHeader;
if (this.tableHeader != null)
this.tableHeader.addMouseListener(mouseListener);
this.tableHeader.setDefaultRenderer(
new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
public boolean isSorting() { return sortingColumns.size() != 0; }
private Directive getDirective(int column)
for (int i = 0; i < sortingColumns.size(); i++)
Directive directive = (Directive)sortingColumns.get(i);
if (directive.column == column) { return directive; }
return EMPTY_DIRECTIVE;
public int getSortingStatus(int column) { return getDirective(column).direction; }
private void sortingStatusChanged()
clearSortingState();
fireTableDataChanged();
if (tableHeader != null) { tableHeader.repaint(); }
public void setSortingStatus(int column, int status)
Directive directive = getDirective(column);
if (directive != EMPTY_DIRECTIVE) { sortingColumns.remove(directive); }
if (status != NOT_SORTED) { sortingColumns.add(new Directive(column, status)); }
sortingStatusChanged();
protected Icon getHeaderRendererIcon(int column, int size)
Directive directive = getDirective(column);
if (directive == EMPTY_DIRECTIVE) { return null; }
return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive));
private void cancelSorting()
sortingColumns.clear();
sortingStatusChanged();
public void setColumnComparator(Class type, Comparator comparator)
if (comparator == null) { columnComparators.remove(type); }
else { columnComparators.put(type, comparator); }
protected Comparator getComparator(int column)
Class columnType = tableModel.getColumnClass(column);
Comparator comparator = (Comparator) columnComparators.get(columnType);
if (comparator != null) { return comparator; }
if (Comparable.class.isAssignableFrom(columnType)) { return COMPARABLE_COMAPRATOR; }
return LEXICAL_COMPARATOR;
private Row[] getViewToModel()
if (viewToModel == null)
int tableModelRowCount = tableModel.getRowCount();
viewToModel = new Row[tableModelRowCount];
for (int row = 0; row < tableModelRowCount; row++) { viewToModel[row] = new Row(row); }
if (isSorting()) { Arrays.sort(viewToModel); }
return viewToModel;
public int modelIndex(int viewIndex) { return getViewToModel()[viewIndex].modelIndex; }
private int[] getModelToView()
if (modelToView == null)
int n = getViewToModel().length;
modelToView = new int[n];
for (int i = 0; i < n; i++) { modelToView[modelIndex(i)] = i; }
return modelToView;
// TableModel interface methods
public int getRowCount() { return (tableModel == null) ? 0 : tableModel.getRowCount(); }
public int getColumnCount() { return (tableModel == null) ? 0 : tableModel.getColumnCount(); }
public String getColumnName(int column) { return tableModel.getColumnName(column); }
public Class getColumnClass(int column) { return tableModel.getColumnClass(column); }
public boolean isCellEditable(int row, int column) { return tableModel.isCellEditable(modelIndex(row), column); }
public Object getValueAt(int row, int column) { return tableModel.getValueAt(modelIndex(row), column); }
public void setValueAt(Object aValue, int row, int column) { tableModel.setValueAt(aValue, modelIndex(row), column); }
// Helper classes
private class Row implements Comparable
private int modelIndex;
public Row(int index) { this.modelIndex = index; }
public int compareTo(Object o)
int row1 = modelIndex;
int row2 = ((Row) o).modelIndex;
for (Iterator it = sortingColumns.iterator(); it.hasNext();)
Directive directive = (Directive) it.next();
int column = directive.column;
Object o1 = tableModel.getValueAt(row1, column);
Object o2 = tableModel.getValueAt(row2, column);
int comparison = 0;
// Define null less than everything, except null.import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.List;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
public class Table_Panel extends JPanel implements ItemListener
public static final long serialVersionUID=26362862L;
static final int Row_Color_Style_Default=-1,Row_Color_Style_None=0,Row_Color_Style_Blue_Gray=1,Row_Color_Style_Cyan_Gray=2,Row_Color_Style_Blue=3,
Row_Color_Style_Gray=4,Row_Color_Style_Red_Green=5,Row_Color_Style_Green_Yellow=6,Row_Color_Style_Red_Yellow=7,
Max_Header_Cell_Width=0,Header_Width=1,Cell_Width=2;
static int Row_Color_Style=Row_Color_Style_Cyan_Gray,Preffered_Width=Header_Width;
// static int Row_Color_Style=Row_Color_Style_None,Preffered_Width=Header_Width;
//boolean Debug=true,
boolean Debug=false,
// Use_Row_Colors=true,
Use_Row_Colors=false;
JFrame Table_Frame;
TableModel My_Model;
JScrollPane scrollPane=new JScrollPane();
public JTable Table;
static String columnNames[],Default_Delimiter=",";
Object[][] data;
static Dimension Screen_Size=Toolkit.getDefaultToolkit().getScreenSize();
int Column_Width[];
static Color Default_Selection_Color=new Color(250,135,200);
Color Selection_Color=Default_Selection_Color;
public Table_Panel(ResultSet RS,String Table_Name,boolean Show_External_Table,int Row_Color_Style,int Preffered_Width,boolean In_Original_Order)
String Value,Table_Column_Names[]=null;
Object[][] Table_Data=null;
int Row_Count,Index=0;
try
if (RS==null) return;
else
RS.last();
Row_Count=RS.getRow();
RS.beforeFirst();
ResultSetMetaData rsmd=RS.getMetaData();
int Column_Count=rsmd.getColumnCount();
Table_Column_Names=new String[Column_Count];
Table_Data=new Object[Row_Count][Column_Count];
for (int i=1;i<=Column_Count;i++) Table_Column_Names[i-1]=rsmd.getColumnLabel(i); // Get column names
//Out("Row_Count="+Row_Count+" Column_Count="+Column_Count);
while (RS.next()) // Get all rows
for (int i=1;i<=Column_Count;i++)
Value=RS.getString(i);
Table_Data[Index][i-1]=(Value==null)?"null":Value;
Index++;
// if (Test_Ct++>300) break;
catch (Exception e) { e.printStackTrace(); }
scrollPane=new Table_Panel(Table_Name,Table_Column_Names,Table_Data,Table_Data.length,false,Show_External_Table,false,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order).scrollPane;
public Table_Panel(String[] Table_Column_Names,Object[][] Table_Data,Color Selection_Color,boolean In_Original_Order)
this("",Table_Column_Names,Table_Data,Table_Data.length,false,false,false,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order);
public Table_Panel(String Table_Name,String[] Table_Column_Names,Object[][] Table_Data,boolean Show_External_Table,Color Selection_Color,boolean In_Original_Order)
this(Table_Name,Table_Column_Names,Table_Data,Table_Data.length,false,Show_External_Table,false,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order);
public Table_Panel(String Table_Name,String[] Table_Column_Names,Object[][] Table_Data,int Row_Count,boolean Is_Child,boolean Show_External_Table,boolean Manage_Attachment,int Row_Color_Style,int Preffered_Width,Color Selection_Color,boolean In_Original_Order)
columnNames=Table_Column_Names;
if (In_Original_Order) data=Table_Data;
else
data=new Object[Table_Data.length][columnNames.length];
for (int row=0;row<Table_Data.length;row++)
data[row]=new Object[columnNames.length];
for (int Column=0;Column<Table_Data[row].length;Column++) data[row][Column]=Table_Data[Table_Data.length-1-row][Column];
Column_Width=new int[columnNames.length];
this.Row_Color_Style=Row_Color_Style;
Use_Row_Colors=(Row_Color_Style!=Row_Color_Style_None);
this.Preffered_Width=Preffered_Width;
if (Selection_Color!=null) this.Selection_Color=Selection_Color;
// Out("this.Selection_Color="+this.Selection_Color);
// TableModel My_Model=new DefaultTableModel(Table_Data,columnNames)
TableModel My_Model=new DefaultTableModel(data,columnNames)
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)
// Out(row+","+col+"["+data[row][col]+"] data.length="+data.length+" data[0].length="+data[0].length);
return (data[row][col]==null)?"":((data[row][col] instanceof Boolean)?data[row][col]:data[row][col].toString()); // Non-boolean values will have bgcolor
public Class getColumnClass(int column)
Class returnValue;
if ((column>=0) && (column<getColumnCount())) returnValue=getValueAt(0,column).getClass();
else returnValue=Object.class;
return returnValue;
// 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<0) return false;
else return true;
// Don't need to implement this method unless your table's data can change.
public void setValueAt(Object value,int row,int col)
String Execution_Dir=getClass().getProtectionDomain().getCodeSource().getLocation().toString();
if (Debug) System.out.println("Execution_Dir="+Execution_Dir+"\nLast_Value="+Table_Grid_Cell_Renderer.Last_Value+" Setting value at [ "+row+","+col+" ] to "+value+" (an instance of "+value.getClass()+")");
if (Execution_Dir.toLowerCase().indexOf("c:/")!=-1 || value.getClass()==Boolean.class || !Use_Row_Colors) data[row][col]=value;
// else data[row][col]=(Table_Grid_CellRenderer.Last_Value==null)?value:value.toString().substring(Table_Grid_CellRenderer.Last_Value.toString().length());
fireTableCellUpdated(row,col);
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("--------------------------------------------------------------------------");
TableSorter sorter=new TableSorter(My_Model);
Table=new JTable(sorter)
// Table=new JTable(My_Model)
public static final long serialVersionUID=26362862L;
public String getToolTipText(MouseEvent e) // Implement table cell tool tips.
Object Cell_Tip;
String tip=null;
java.awt.Point p=e.getPoint();
int rowIndex=rowAtPoint(p);
int colIndex=columnAtPoint(p);
int realColumnIndex=convertColumnIndexToModel(colIndex);
Cell_Tip=getValueAt(rowIndex,colIndex);
// if (realColumnIndex == 2) //Sport column
tip=Cell_Tip.toString();
else if (realColumnIndex == 4) //Veggie column
TableModel model=getModel();
String firstName=(String)model.getValueAt(rowIndex,0);
String lastName=(String)model.getValueAt(rowIndex,1);
Boolean veggie=(Boolean)model.getValueAt(rowIndex,4);
if (Boolean.TRUE.equals(veggie)) tip=firstName+" "+lastName+" is a vegetarian";
else tip=firstName+" "+lastName+" is not a vegetarian";
else
// You can omit this part if you know you don't have any renderers that supply their own tool tips.
tip=super.getToolTipText(e);
return tip;
// RowSorter<TableModel> sorter=new TableRowSorter<TableModel>(My_Model);
// Table.setRowSorter(sorter);
Table.setSelectionBackground(this.Selection_Color);
int Table_Height=Table.getRowHeight()*Row_Count;
// sorter.addMouseListenerToHeaderInTable(Table); // ADDED THIS
sorter.setTableHeader(Table.getTableHeader());
if (Table_Column_Names.length>20) Table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
if (Manage_Attachment) Table.setPreferredScrollableViewportSize(new Dimension(500,(Table_Height>850)?850:Table_Height));
else Table.setPreferredScrollableViewportSize(new Dimension(1000,(Table_Height>850)?850:Table_Height));
if (Use_Row_Colors) Table.setDefaultRenderer(Object.class,new Table_Grid_Cell_Renderer());
//Create the scroll pane and add the table to it.
scrollPane=new JScrollPane(Table);
//Set up column sizes.
initColumnSizes(Table,My_Model);
//Add the scroll pane to this window.
if (Show_External_Table)
Table_Frame=new JFrame(Table_Name);
Table_Frame.getContentPane().add(scrollPane);
Table_Frame.addWindowListener(new WindowAdapter()
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { scrollPane.repaint(); }
public void windowGainedFocus(WindowEvent e) { scrollPane.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { scrollPane.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { scrollPane.repaint(); }
public void windowStateChanged(WindowEvent e) { scrollPane.repaint(); }
Table_Frame.pack();
Table_Frame.setBounds((Screen_Size.width-Table_Frame.getWidth())/2,(Screen_Size.height-Table_Frame.getHeight())/2,Table_Frame.getWidth(),Table_Frame.getHeight());
Table_Frame.setVisible(true);
if (Is_Child) Table_Frame.addComponentListener(new ComponentAdapter() { public void componentClosing(ComponentEvent e) { System.exit(0); } });
else add(scrollPane);
public Table_Panel(String File_Name) { this(File_Name,false,Row_Color_Style_Cyan_Gray,Preffered_Width,null,Default_Selection_Color,true); }
public Table_Panel(String File_Name,int Row_Color_Style,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style,Preffered_Width,null,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,String Delimiter,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style_Cyan_Gray,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,int Preffered_Width,String Delimiter,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,int Row_Color_Style,int Preffered_Width,String Delimiter,boolean In_Original_Order) { this(File_Name,false,Row_Color_Style,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,boolean Show_External_Table,boolean In_Original_Order) { this(File_Name,Show_External_Table,Row_Color_Style,Preffered_Width,null,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,boolean Show_External_Table,String Delimiter,boolean In_Original_Order) { this(File_Name,Show_External_Table,Row_Color_Style,Preffered_Width,Delimiter,Default_Selection_Color,In_Original_Order); }
public Table_Panel(String File_Name,boolean Show_External_Table,int Row_Color_Style,int Preffered_Width,String Delimiter,Color Selection_Color,boolean In_Original_Order)
String Table_Column_Names[],Column_Name_Line="";
int Row_Count=0,Index=0;
boolean Is_Child=false,Manage_Attachment=false;
StringTokenizer ST;
if (Delimiter==null) Delimiter=Default_Delimiter;
if (new File(File_Name).exists())
try
String Line,Str=Tool_Lib.Read_File(File_Name).toString();
ST=new StringTokenizer(Str,"\n");
Line=ST.nextToken();
Row_Count=ST.countTokens();
Object[][] Table_Data=new Object[Row_Count][];
if (Delimiter.equals(" ")) Line=Line.replaceAll(" {2,}"," ").trim(); // Replace multiple spaces with the delimiter space
Table_Column_Names=Line.split(Delimiter);
columnNames=Table_Column_Names;
for (int i=0;i<Table_Column_Names.length;i++) Column_Name_Line+=Table_Column_Names[i]+" ";
//Out("Column_Name_Line [ "+Table_Column_Names.length+" ]="+Column_Name_Line);
while (ST.hasMoreTokens())
Line=ST.nextToken();
//Out(Line);
if (Delimiter.equals(" ")) Line=Line.replaceAll(" {2,}"," ").trim(); // Replace multiple spaces with the delimiter space
if (Line.indexOf(Delimiter)!=-1) Table_Data[Index]=Line.split(Delimiter);
else
Table_Data[Index]=new Object[Table_Column_Names.length];
Table_Data[Index][0]=Line;
for (int i=1;i<Table_Column_Names.length;i++) Table_Data[Index]="";
Index++;
Line="";
for (int i=0;i<Table_Data.length;i++)
Line+="[ "+Table_Data[i].length+" ] ";
for (int j=0;j<Table_Data[i].length;j++) Line+=Table_Data[i][j]+" ";
Line+="\n";
Out(Line);
Table_Panel A_Table_Panel=new Table_Panel(File_Name,Table_Column_Names,Table_Data,Row_Count,Is_Child,Show_External_Table,Manage_Attachment,Row_Color_Style,Preffered_Width,Selection_Color,In_Original_Order);
Table=A_Table_Panel.Table;
scrollPane=A_Table_Panel.scrollPane;
Column_Width=A_Table_Panel.Column_Width;
data=A_Table_Panel.data;
catch (Exception e) { e.printStackTrace(); }
public void setPreferredSize(Dimension A_Dimension) { scrollPane.setPreferredSize(A_Dimension); }
// This method picks good column sizes. If all column heads are wider than the column's cells' contents, then you can just use column.sizeWidthToFit().
void initColumnSizes(JTable table,TableModel model)
TableColumn column=null;
Component comp=null;
int headerWidth=0,cellWidth=0;
Object[] longValues=(data.length>0)?data[0]:null;
TableCellRenderer headerRenderer=table.getTableHeader().getDefaultRenderer();
if (data.length<1) return;
for (int i=0;i<model.getColumnCount();i++)
column=table.getColumnModel().getColumn(i);
comp=headerRenderer.getTableCellRendererComponent(null,column.getHeaderValue(),false,false,0,0);
headerWidth=comp.getPreferredSize().width;
comp=table.getDefaultRenderer(model.getColumnClass(i)).
getTableCellRendererComponent(table,longValues[i],false,false,0,i);
cellWidth=comp.getPreferredSize().width;
switch (Preffered_Width)
case Max_Header_Cell_Width : column.setPreferredWidth(Math.max(headerWidth,cellWidth));break;
case Header_Width : column.setPreferredWidth(headerWidth);break;
case Cell_Width : column.setPreferredWidth(cellWidth);break;
Column_Width[i]=column.getPreferredWidth();
if (Debug) Out("Initializing width of column "+i+". "+"headerWidth="+headerWidth+"; cellWidth="+cellWidth+" Column_Width["+i+"]="+Column_Width[i]);
// Detects a state change in any of the Lists. Resets the variable corresponding to the selected item in a particular List. Invokes changeFont with the currently selected fontname, style and size attributes.
public void itemStateChanged(ItemEvent e)
Out(e.toString());
if (e.getStateChange() != ItemEvent.SELECTED) return;
Object list=e.getSource();
public static void Out(String message) { System.out.println(message); }
// Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
static void Create_And_Show_GUI()
boolean Demo_External_Table=true;
// boolean Demo_External_Table=false;
final Table_Panel demo;
String[] columnNames={"First Names","Last Names","Sport","Num of Years","Vegetarian"};
Object[][] data={{"Mary","Campione","Snowboarding"+"[123456789]",new Integer(5),new Boolean(false)},
{"Alison","Huml","Rowing"+":123456789]",new Integer(3),new Boolean(true)},
{"Frank","Ni","Running"+":123456789", new Integer(12), new Boolean(false)},
{"Kathy","Walrath","Chasing toddlers"+"<123456789>", new Integer(2), new Boolean(false)},
{"Mark", "Andrews","Speed reading",new Integer(20),new Boolean(true)},
{"Angela","Lih","Teaching high school",new Integer(36),new Boolean(false)} };
// Row_Color_Style=Row_Color_Style_Default;
// Row_Color_Style=Row_Color_Style_None;
// Row_Color_Style=Row_Color_Style_Blue_Gray;
Row_Color_Style=Row_Color_Style_Cyan_Gray;
// Row_Color_Style=Row_Color_Style_Blue;
// Row_Color_Style=Row_Color_Style_Gray;
// Row_Color_Style=Row_Color_Style_Red_Green;
// Row_Color_Style=Row_Color_Style_Green_Yellow;
// Row_Color_Style=Row_Color_Style_Red_Yellow;
Preffered_Width=Max_Header_Cell_Width;
if (Demo_External_Table) demo=new Table_Panel("External Table Demo",columnNames,data,data.length,false,Demo_External_Table,false,Row_Color_Style,Max_Header_Cell_Width,Default_Selection_Color,true);
else
JFrame Table_Frame=new JFrame("Internal Table Demo");
// demo=new Table_Panel(Nm_Lib.Dir_A_Test+"ELX.csv",false,Row_Color_Style,Preffered_Width,null);
// demo=new Table_Panel(Nm_Lib.Dir_Stock_Data+"ABV_Data.txt",false,Row_Color_Style,Preffered_Width," ");
demo=new Table_Panel("C:/Dir_Stock_Data/EOP.txt",",",false);
// demo=new Table_Panel(Nm_Lib.Dir_Stock_Data+"ABV_Data.txt"," ",false);
Table_Frame.getContentPane().add(demo.scrollPane);
Table_Frame.addWindowListener(new WindowAdapter()
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { demo.repaint(); }
public void windowGainedFocus(WindowEvent e) { demo.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { demo.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { demo.repaint(); }
public void windowStateChanged(WindowEvent e) { demo.repaint(); }
Table_Frame.pack();
Table_Frame.setBounds((Screen_Size.width-Table_Frame.getWidth())/2,(Screen_Size.height-Table_Frame.getHeight())/2,Table_Frame.getWidth(),Table_Frame.getHeight());
Table_Frame.setVisible(true);
public static void main(String[] args)
// Schedule a job for the event-dispatching thread : creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } });
* TableSorter is a decorator for TableModels; adding sorting functionality to a supplied TableModel. TableSorter does not store
* or copy the data in its TableModel; instead it maintains a map from the row indexes of the view to the row indexes of the
* model. As requests are made of the sorter (like getValueAt(row, col)) they are passed to the underlying model after the row numbers
* have been translated via the internal mapping array. This way, the TableSorter appears to hold another copy of the table
* with the rows in a different order.
* <p/>
* TableSorter registers itself as a listener to the underlying model, just as the JTable itself would. Events recieved from the model
* are examined, sometimes manipulated (typically widened), and then passed on to the TableSorter's listeners (typically the JTable).
* If a change to the model has invalidated the order of TableSorter's rows, a note of this is made and the sorter will resort the
* rows the next time a value is requested.
* <p/>
* When the tableHeader property is set, either by using the setTableHeader() method or the two argument constructor, the table
* header may be used as a complete UI for TableSorter. The default renderer of the tableHeader is decorated with a renderer
* that indicates the sorting status of each column. In addition, a mouse listener is installed with the following behavior:
* <ul>
* <li>
* Mouse-click: Clears the sorting status of all other columns and advances the sorting status of that column through three
* values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED again).
* <li>
* SHIFT-mouse-click: Clears the sorting status of all other columns and cycles the sorting status of the column through the same
* three values, in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}.
* <li>
* CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except that the changes to the column do not cancel the statuses of columns
* that are already sorting - giving a way to initiate a compound sort.
* </ul>
* <p/>
* This is a long overdue rewrite of a class of the same name that first appeared in the swing table demos in 1997.
* @author Philip Milne
* @author Brendon McLean
* @author Dan van Enckevort
* @author Parwinder Sekhon
* @version 2.0 02/27/04
class TableSorter extends AbstractTableModel
public static final long serialVersionUID=26362862L;
protected TableModel tableModel;
public static final int DESCENDING = -1;
public static final int NOT_SORTED = 0;
public static final int ASCENDING = 1;
private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() { public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } };
public static final Comparator LEXICAL_COMPARATOR = new Comparator() { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } };
private Row[] viewToModel;
private int[] modelToView;
private JTableHeader tableHeader;
private MouseListener mouseListener;
private TableModelListener tableModelListener;
private Map<Class,Comparator> columnComparators = new HashMap<Class,Comparator>();
private List<Directive> sortingColumns = new ArrayList<Directive>();
public TableSorter()
this.mouseListener = new MouseHandler();
this.tableModelListener = new TableModelHandler();
public TableSorter(TableModel tableModel)
this();
setTableModel(tableModel);
public TableSorter(TableModel tableModel, JTableHeader tableHeader)
this();
setTableHeader(tableHeader);
setTableModel(tableModel);
private void clearSortingState()
viewToModel = null;
modelToView = null;
public TableModel getTableModel() { return tableModel; }
public void setTableModel(TableModel tableModel)
if (this.tableModel != null) { this.tableModel.removeTableModelListener(tableModelListener); }
this.tableModel = tableModel;
if (this.tableModel != null) { this.tableModel.addTableModelListener(tableModelListener); }
clearSortingState();
fireTableStructureChanged();
public JTableHeader getTableHeader() { return tableHeader; }
public void setTableHeader(JTableHeader tableHeader)
if (this.tableHeader != null)
this.tableHeader.removeMouseListener(mouseListener);
TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
if (defaultRenderer instanceof SortableHeaderRenderer) this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
this.tableHeader = tableHeader;
if (this.tableHeader != null)
this.tableHeader.addMouseListener(mouseListener);
this.tableHeader.setDefaultRenderer(
new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
public boolean isSorting() { return sortingColumns.size() != 0; }
private Directive getDirective(int column)
for (int i = 0; i < sortingColumns.size(); i++)
Directive directive = (Directive)sortingColumns.get(i);
if (directive.column == column) { return directive; }
return EMPTY_DIRECTIVE;
public int getSortingStatus(int column) { return getDirective(column).direction; }
private void sortingStatusChanged()
clearSortingState();
fireTableDataChanged();
if (tableHeader != null) { tableHeader.repaint(); }
public void setSortingStatus(int column, int status)
Directive directive = getDirective(column);
if (directive != EMPTY_DIRECTIVE) { sortingColumns.remove(directive); }
if (status != NOT_SORTED) { sortingColumns.add(new Directive(column, status)); }
sortingStatusChanged();
protected Icon getHeaderRendererIcon(int column, int size)
Directive directive = getDirective(column);
if (directive == EMPTY_DIRECTIVE) { return null; }
return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive));
private void cancelSorting()
sortingColumns.clear();
sortingStatusChanged();
public void setColumnComparator(Class type, Comparator comparator)
if (comparator == null) { columnComparators.remove(type); }
else { columnComparators.put(type, comparator); }
protected Comparator getComparator(int column)
Class columnType = tableModel.getColumnClass(column);
Comparator comparator = (Comparator) columnComparators.get(columnType);
if (comparator != null) { return comparator; }
if (Comparable.class.isAssignableFrom(columnType)) { return COMPARABLE_COMAPRATOR; }
return LEXICAL_COMPARATOR;
private Row[] getViewToModel()
if (viewToModel == null)
int tableModelRowCount = tableModel.getRowCount();
viewToModel = new Row[tableModelRowCount];
for (int row = 0; row < tableModelRowCount; row++) { viewToModel[row] = new Row(row); }
if (isSorting()) { Arrays.sort(viewToModel); }
return viewToModel;
public int modelIndex(int viewIndex) { return getViewToModel()[viewIndex].modelIndex; }
private int[] getModelToView()
if (modelToView == null)
int n = getViewToModel().length;
modelToView = new int[n];
for (int i = 0; i < n; i++) { modelToView[modelIndex(i)] = i; }
return modelToView;
// TableModel interface methods
public int getRowCount() { return (tableModel == null) ? 0 : tableModel.getRowCount(); }
public int getColumnCount() { return (tableModel == null) ? 0 : tableModel.getColumnCount(); }
public String getColumnName(int column) { return tableModel.getColumnName(column); }
public Class getColumnClass(int column) { return tableModel.getColumnClass(column); }
public boolean isCellEditable(int row, int column) { return tableModel.isCellEditable(modelIndex(row), column); }
public Object getValueAt(int row, int column) { return tableModel.getValueAt(modelIndex(row), column); }
public void setValueAt(Object aValue, int row, int column) { tableModel.setValueAt(aValue, modelIndex(row), column); }
// Helper classes
private class Row implements Comparable
private int modelIndex;
public Row(int index) { this.modelIndex = index; }
public int compareTo(Object o)
int row1 = modelIndex;
int row2 = ((Row) o).modelIndex;
for (Iterator it = sortingColumns.iterator(); it.hasNext();)
Directive directive = (Directive) it.next();
int column = directive.column;
Object o1 = tableModel.getValueAt(row1, column);
Object o2 = tableModel.getValueAt(row2, column);
int comparison = 0;
// Define null less than everything, except null.
if (o1 == null && o2 == null) { comparison = 0; }
else if (o1 == null) { comparison = -1; }
else if (o2 == null) { comparison = 1; }
else { comparison = getComparator(column).compare(o1, o2); }
if (comparison != 0) { return directive.direction == DESCENDING ? -comparison : comparison; }
return 0;
private class TableModelHandler implements TableModelListener
public void tableChanged(TableModelEvent e)
// If we're not sorting by anything -
Editing a JTable Feild (setValueAt)
Hi, I have a series of tables that each pull different data from a database and then desplay the data to the user. The user has the ability to update the shown data by editing the field(s) displayed in the table. To complete the update, I want to error check their change (to make sure they didn't leave the field blank for example) and then I want to update the database before allowing the new value to be the one that's shown in the table. In other words if error checking or updating the database result in an error, an error message is displayed and the origional value is left in the "updated" field.
What I was doing to accomplish this was adding a custom Table Model to each table with error checking code in the setValueAt method of the Table Model.
Now, I want a standard table model that all of my tables use to reduce the number of classes I have from 1 frame, x number of table models to 1 frame, 1 table model. The problem I'm having is, by standardizing the table model, I have to remove all error checking and database updates as they are unique to each individual table. I've tryed to add TableModelListeners to my frame but I found out that the TableModelListener is executed after the setValueAt in the Table Model. This won't work for me as, while I can error check and update the newly changed data, if there is an error, I can't revert back to what was previously displayed without recalling the database.
I guess what my questions are are: What can I do to stop setValueAt from running until after I've error checked. Also, what tells the table/table model about the end of an edit and calls setValueAt in the first place?
Any help would be wonderful. Thanks in advance!
--Ethanwhat I think you should do is this: extend this class (which i found on the sun site) and extend it for your needs.
the idea is to have a model as a proxy of the real model - this model holds a different model inside it and before activiating it it can do stuff, meaning - before call the "real" setValueAt you can do error checking.
I'm also adding the SortModel which I found to be very helpful - it allows you to sort any table with 0 code. (and it's a nice example to how to use the TableMap class.
all the luck to you.
btw, you will still have as many tableModels, just smaller in code.
public class TableMap extends AbstractTableModel
implements TableModelListener {
protected TableModel model;
public TableModel getModel() {
return model;
public void setModel(TableModel model) {
this.model = model;
model.addTableModelListener(this);
// By default, implement TableModel by forwarding all messages
// to the model.
public Object getValueAt(int aRow, int aColumn) {
return model.getValueAt(aRow, aColumn);
public void setValueAt(Object aValue, int aRow, int aColumn) {
model.setValueAt(aValue, aRow, aColumn);
public int getRowCount() {
return (model == null) ? 0 : model.getRowCount();
public int getColumnCount() {
return (model == null) ? 0 : model.getColumnCount();
public String getColumnName(int aColumn) {
return model.getColumnName(aColumn);
public Class getColumnClass(int aColumn) {
return model.getColumnClass(aColumn);
public boolean isCellEditable(int row, int column) {
return model.isCellEditable(row, column);
// Implementation of the TableModelListener interface,
// By default forward all events to all the listeners.
public void tableChanged(TableModelEvent e) {
fireTableChanged(e);
* A sorter for TableModels. The sorter has a model (conforming to TableModel)
* and itself implements TableModel. TableSorter does not store or copy
* the data in the TableModel, instead it maintains an array of
* integers which it keeps the same size as the number of rows in its
* model. When the model changes it notifies the sorter that something
* has changed eg. "rowsAdded" so that its internal array of integers
* can be reallocated. As requests are made of the sorter (like
* getValueAt(row, col) it redirects them to its model via the mapping
* array. That way the TableSorter appears to hold another copy of the table
* with the rows in a different order. The sorting algorthm used is stable
* which means that it does not move around rows when its comparison
* function returns 0 to denote that they are equivalent.
* @version 1.5 12/17/97
* @author Philip Milne
import java.util.*;
import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;
// Imports for picking up mouse events from the JTable.
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.InputEvent;
import javax.swing.JTable;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;
public class TableSorter extends TableMap {
int indexes[];
Vector sortingColumns = new Vector();
boolean ascending = true;
int compares;
public TableSorter() {
indexes = new int[0]; // for consistency
public TableSorter(TableModel model) {
setModel(model);
public void setModel(TableModel model) {
super.setModel(model);
reallocateIndexes();
public int compareRowsByColumn(int row1, int row2, int column) {
Class type = model.getColumnClass(column);
TableModel data = model;
// Check for nulls.
Object o1 = data.getValueAt(row1, column);
Object o2 = data.getValueAt(row2, column);
// If both values are null, return 0.
if (o1 == null && o2 == null) {
return 0;
} else if (o1 == null) { // Define null less than everything.
return -1;
} else if (o2 == null) {
return 1;
* We copy all returned values from the getValue call in case
* an optimised model is reusing one object to return many
* values. The Number subclasses in the JDK are immutable and
* so will not be used in this way but other subclasses of
* Number might want to do this to save space and avoid
* unnecessary heap allocation.
if (type.getSuperclass() == java.lang.Number.class) {
Number n1 = (Number)data.getValueAt(row1, column);
double d1 = n1.doubleValue();
Number n2 = (Number)data.getValueAt(row2, column);
double d2 = n2.doubleValue();
if (d1 < d2) {
return -1;
} else if (d1 > d2) {
return 1;
} else {
return 0;
} else if (type == java.util.Date.class) {
Date d1 = (Date)data.getValueAt(row1, column);
long n1 = d1.getTime();
Date d2 = (Date)data.getValueAt(row2, column);
long n2 = d2.getTime();
if (n1 < n2) {
return -1;
} else if (n1 > n2) {
return 1;
} else {
return 0;
} else if (type == String.class) {
String s1 = (String)data.getValueAt(row1, column);
String s2 = (String)data.getValueAt(row2, column);
int result = s1.compareTo(s2);
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
} else {
return 0;
} else if (type == Boolean.class) {
Boolean bool1 = (Boolean)data.getValueAt(row1, column);
boolean b1 = bool1.booleanValue();
Boolean bool2 = (Boolean)data.getValueAt(row2, column);
boolean b2 = bool2.booleanValue();
if (b1 == b2) {
return 0;
} else if (b1) { // Define false < true
return 1;
} else {
return -1;
} else {
Object v1 = data.getValueAt(row1, column);
String s1 = v1.toString();
Object v2 = data.getValueAt(row2, column);
String s2 = v2.toString();
int result = s1.compareTo(s2);
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
} else {
return 0;
public int compare(int row1, int row2) {
compares++;
for (int level = 0; level < sortingColumns.size(); level++) {
Integer column = (Integer)sortingColumns.elementAt(level);
int result = compareRowsByColumn(row1, row2, column.intValue());
if (result != 0) {
return ascending ? result : -result;
return 0;
public void reallocateIndexes() {
int rowCount = model.getRowCount();
// Set up a new array of indexes with the right number of elements
// for the new data model.
indexes = new int[rowCount];
// Initialise with the identity mapping.
for (int row = 0; row < rowCount; row++) {
indexes[row] = row;
public void tableChanged(TableModelEvent e) {
reallocateIndexes();
super.tableChanged(e);
public void checkModel() {
if (indexes.length != model.getRowCount()) {
System.err.println("Sorter not informed of a change in model.");
public void sort(Object sender) {
checkModel();
compares = 0;
// n2sort();
// qsort(0, indexes.length-1);
shuttlesort((int[])indexes.clone(), indexes, 0, indexes.length);
public void n2sort() {
for (int i = 0; i < getRowCount(); i++) {
for (int j = i+1; j < getRowCount(); j++) {
if (compare(indexes, indexes[j]) == -1) {
swap(i, j);
// This is a home-grown implementation which we have not had time
// to research - it may perform poorly in some circumstances. It
// requires twice the space of an in-place algorithm and makes
// NlogN assigments shuttling the values between the two
// arrays. The number of compares appears to vary between N-1 and
// NlogN depending on the initial order but the main reason for
// using it here is that, unlike qsort, it is stable.
public void shuttlesort(int from[], int to[], int low, int high) {
if (high - low < 2) {
return;
int middle = (low + high)/2;
shuttlesort(to, from, low, middle);
shuttlesort(to, from, middle, high);
int p = low;
int q = middle;
/* This is an optional short-cut; at each recursive call,
check to see if the elements in this subset are already
ordered. If so, no further comparisons are needed; the
sub-array can just be copied. The array must be copied rather
than assigned otherwise sister calls in the recursion might
get out of sinc. When the number of elements is three they
are partitioned so that the first set, [low, mid), has one
element and and the second, [mid, high), has two. We skip the
optimisation when the number of elements is three or less as
the first compare in the normal merge will produce the same
sequence of steps. This optimisation seems to be worthwhile
for partially ordered lists but some analysis is needed to
find out how the performance drops to Nlog(N) as the initial
order diminishes - it may drop very quickly. */
if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0) {
for (int i = low; i < high; i++) {
to[i] = from[i];
return;
// A normal merge.
for (int i = low; i < high; i++) {
if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
to[i] = from[p++];
else {
to[i] = from[q++];
public void swap(int i, int j) {
int tmp = indexes[i];
indexes[i] = indexes[j];
indexes[j] = tmp;
// The mapping only affects the contents of the data rows.
// Pass all requests to these rows through the mapping array: "indexes".
public Object getValueAt(int aRow, int aColumn) {
checkModel();
return model.getValueAt(indexes[aRow], aColumn);
public void setValueAt(Object aValue, int aRow, int aColumn) {
checkModel();
model.setValueAt(aValue, indexes[aRow], aColumn);
public void sortByColumn(int column) {
sortByColumn(column, true);
public void sortByColumn(int column, boolean ascending) {
this.ascending = ascending;
sortingColumns.removeAllElements();
sortingColumns.addElement(new Integer(column));
sort(this);
super.tableChanged(new TableModelEvent(this));
// There is no-where else to put this.
// Add a mouse listener to the Table to trigger a table sort
// when a column heading is clicked in the JTable.
public void addMouseListenerToHeaderInTable(JTable table) {
final TableSorter sorter = this;
final JTable tableView = table;
tableView.setColumnSelectionAllowed(false);
MouseAdapter listMouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
TableColumnModel columnModel = tableView.getColumnModel();
int viewColumn = columnModel.getColumnIndexAtX(e.getX());
int column = tableView.convertColumnIndexToModel(viewColumn);
if (e.getClickCount() == 1 && column != -1) {
int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK;
boolean ascending = (shiftPressed == 0);
sorter.sortByColumn(column, ascending);
JTableHeader th = tableView.getTableHeader();
th.addMouseListener(listMouseListener);
Glazberg. -
JTable sorting - problem when adding elements (complete code inside)
I�m writing this email with reference to a recent posting here but this time with the code example. (I apologize for the duplicated posting � this time it will be with the code)
Problem: when adding more elements to the JTable (sorted) the exception: ArrayIndexOutOfBoundsException is thrown.
Example: If the elements in the table are 10 and then the user requests for 8 � the table will produce the correct result. However, if the user will ask for 11 items (>10) the exception will be thrown.
The program: The program below (compiles and running). A JTable is constructed with 3 items, when you click the button - the return result should be 4 items - this will generate the error, WHY?
I would highly appreciate your thoughts why this is happening and most importantly � how to fix it.
Thanks a lot
3 files:
(1) TableSorterDemo
(2) Traveler
(3)TableSorter
//TableSorterDemo:
package sorter;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
* TableSorterDemo is like TableDemo, except that it
* inserts a custom model -- a sorter -- between the table
* and its data model. It also has column tool tips.
public class TableSorterDemo implements ActionListener
private JPanel superPanel;
private JButton clickMe = new JButton("click me to get diff data");
private boolean DEBUG = false;
private DefaultListModel defaultListModel;
private JTable table;
public TableSorterDemo()
superPanel = new JPanel(new BorderLayout());
defaultListModel = new DefaultListModel();
init1();
TableSorter sorter = new TableSorter(new MyTableModel(defaultListModel)); //ADDED THIS
table = new JTable(sorter); //NEW
sorter.setTableHeader(table.getTableHeader()); //ADDED THIS
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
//Set up tool tips for column headers.
table.getTableHeader().setToolTipText(
"Click to specify sorting; Control-Click to specify secondary sorting");
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
superPanel.add("Center", scrollPane);
superPanel.add("South",clickMe);
clickMe.addActionListener(this);
public JPanel getPanel()
return superPanel;
public void init1()
//in real life this will be done from the db
Traveler a = new Traveler();
Traveler b = new Traveler();
Traveler c = new Traveler();
a.setFirstName("Elvis");
a.setLastName("Presley");
a.setSprot("Ping Pong");
a.setNumYears(3);
a.setVegetarian(true);
b.setFirstName("Elton");
b.setLastName("John");
b.setSprot("Soccer");
b.setNumYears(2);
b.setVegetarian(true);
c.setFirstName("shaquille");
c.setLastName("oneil");
c.setSprot("Golf");
c.setNumYears(22);
c.setVegetarian(true);
defaultListModel.addElement(a);
defaultListModel.addElement(b);
defaultListModel.addElement(c);
public void init2()
//in real life this will be done from the db
Traveler d = new Traveler();
Traveler e = new Traveler();
Traveler f = new Traveler();
Traveler g = new Traveler();
d.setFirstName("John");
d.setLastName("Smith");
d.setSprot("Tennis");
d.setNumYears(32);
d.setVegetarian(true);
e.setFirstName("Ron");
e.setLastName("Cohen");
e.setSprot("Baseball");
e.setNumYears(12);
e.setVegetarian(true);
f.setFirstName("Donald");
f.setLastName("Mac Novice");
f.setSprot("Vallyball");
f.setNumYears(1);
f.setVegetarian(true);
g.setFirstName("Eithan");
g.setLastName("Superstar");
g.setSprot("Vallyball");
g.setNumYears(21);
g.setVegetarian(true);
defaultListModel.addElement(d);
defaultListModel.addElement(e);
defaultListModel.addElement(f);
defaultListModel.addElement(g);
class MyTableModel extends AbstractTableModel
private DefaultListModel myModel;
public MyTableModel(DefaultListModel m)
myModel=m;
private String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
public int getColumnCount()
return columnNames.length;
public int getRowCount()
return myModel.size();
public String getColumnName(int column)
return getNames()[column];
public String[] getNames()
String[] names = {"First Name", "Last Name", "Sport", "# of Years", "Vegetarian"};
return names;
public Object getValueAt(int row, int col)
return distributeObjectsInTable(row, col, (Traveler) myModel.elementAt(row));
public Object distributeObjectsInTable(int row, int col, Traveler tr)
switch(col)
case 0:
return tr.getFirstName();
case 1:
return tr.getLastName();
case 2:
return tr.getSprot();
case 3:
return new Integer(tr.getNumYears());
case 4:
return new Boolean (tr.isVegetarian());
default:
return "Error";
public Class getColumnClass(int c)
return getValueAt(0, c).getClass();
private static void createAndShowGUI()
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
JFrame frame = new JFrame("TableSorterDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
TableSorterDemo newContentPane = new TableSorterDemo();
newContentPane.getPanel().setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane.getPanel());
//Display the window.
frame.pack();
frame.setVisible(true);
public static void main(String[] args)
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable()
public void run()
createAndShowGUI();
public void actionPerformed(ActionEvent ae)
if (ae.getSource()==clickMe)
defaultListModel.removeAllElements();
init2(); //if the size of the model was less than 2 items - the result will be ok.
//in other words, if you commens the last 2 rows of this method (addElement(f) & g)
// the result will be fine.
table.updateUI();
}//(2) Traveler
package sorter;
public class Traveler
private String firstName;
private String lastName;
private String sprot;
private int numYears;
private boolean vegetarian;
public String getFirstName()
return firstName;
public String getLastName()
return lastName;
public int getNumYears()
return numYears;
public String getSprot()
return sprot;
public boolean isVegetarian()
return vegetarian;
public void setFirstName(String firstName)
this.firstName = firstName;
public void setLastName(String lastName)
this.lastName = lastName;
public void setNumYears(int numYears)
this.numYears = numYears;
public void setSprot(String sprot)
this.sprot = sprot;
public void setVegetarian(boolean vegetarian)
this.vegetarian = vegetarian;
}//(3)TableSorter
package sorter;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.*;
public class TableSorter extends AbstractTableModel {
protected TableModel tableModel;
public static final int DESCENDING = -1;
public static final int NOT_SORTED = 0;
public static final int ASCENDING = 1;
private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) o1).compareTo(o2);
public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
public int compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
private Row[] viewToModel;
private int[] modelToView;
private JTableHeader tableHeader;
private MouseListener mouseListener;
private TableModelListener tableModelListener;
private Map columnComparators = new HashMap();
private List sortingColumns = new ArrayList();
public TableSorter() {
this.mouseListener = new MouseHandler();
this.tableModelListener = new TableModelHandler();
public TableSorter(TableModel tableModel) {
this();
setTableModel(tableModel);
public TableSorter(TableModel tableModel, JTableHeader tableHeader) {
this();
setTableHeader(tableHeader);
setTableModel(tableModel);
private void clearSortingState() {
viewToModel = null;
modelToView = null;
public TableModel getTableModel() {
return tableModel;
public void setTableModel(TableModel tableModel) {
if (this.tableModel != null) {
this.tableModel.removeTableModelListener(tableModelListener);
this.tableModel = tableModel;
if (this.tableModel != null) {
this.tableModel.addTableModelListener(tableModelListener);
clearSortingState();
fireTableStructureChanged();
public JTableHeader getTableHeader() {
return tableHeader;
public void setTableHeader(JTableHeader tableHeader) {
if (this.tableHeader != null) {
this.tableHeader.removeMouseListener(mouseListener);
TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
if (defaultRenderer instanceof SortableHeaderRenderer) {
this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
this.tableHeader = tableHeader;
if (this.tableHeader != null) {
this.tableHeader.addMouseListener(mouseListener);
this.tableHeader.setDefaultRenderer(
new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
public boolean isSorting() {
return sortingColumns.size() != 0;
private Directive getDirective(int column) {
for (int i = 0; i < sortingColumns.size(); i++) {
Directive directive = (Directive)sortingColumns.get(i);
if (directive.column == column) {
return directive;
return EMPTY_DIRECTIVE;
public int getSortingStatus(int column) {
return getDirective(column).direction;
private void sortingStatusChanged() {
clearSortingState();
fireTableDataChanged();
if (tableHeader != null) {
tableHeader.repaint();
public void setSortingStatus(int column, int status) {
Directive directive = getDirective(column);
if (directive != EMPTY_DIRECTIVE) {
sortingColumns.remove(directive);
if (status != NOT_SORTED) {
sortingColumns.add(new Directive(column, status));
sortingStatusChanged();
protected Icon getHeaderRendererIcon(int column, int size) {
Directive directive = getDirective(column);
if (directive == EMPTY_DIRECTIVE) {
return null;
return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive));
private void cancelSorting() {
sortingColumns.clear();
sortingStatusChanged();
public void setColumnComparator(Class type, Comparator comparator) {
if (comparator == null) {
columnComparators.remove(type);
} else {
columnComparators.put(type, comparator);
protected Comparator getComparator(int column) {
Class columnType = tableModel.getColumnClass(column);
Comparator comparator = (Comparator) columnComparators.get(columnType);
if (comparator != null) {
return comparator;
if (Comparable.class.isAssignableFrom(columnType)) {
return COMPARABLE_COMAPRATOR;
return LEXICAL_COMPARATOR;
private Row[] getViewToModel() {
if (viewToModel == null) {
int tableModelRowCount = tableModel.getRowCount();
viewToModel = new Row[tableModelRowCount];
for (int row = 0; row < tableModelRowCount; row++) {
viewToModel[row] = new Row(row);
if (isSorting()) {
Arrays.sort(viewToModel);
return viewToModel;
public int modelIndex(int viewIndex)
return getViewToModel()[viewIndex].modelIndex;
private int[] getModelToView()
if (modelToView == null) {
int n = getViewToModel().length;
modelToView = new int[n];
for (int i = 0; i < n; i++) {
modelToView[modelIndex(i)] = i;
return modelToView;
// TableModel interface methods
public int getRowCount() {
return (tableModel == null) ? 0 : tableModel.getRowCount();
public int getColumnCount() {
return (tableModel == null) ? 0 : tableModel.getColumnCount();
public String getColumnName(int column) {
return tableModel.getColumnName(column);
public Class getColumnClass(int column) {
return tableModel.getColumnClass(column);
public boolean isCellEditable(int row, int column) {
return tableModel.isCellEditable(modelIndex(row), column);
public Object getValueAt(int row, int column) {
return tableModel.getValueAt(modelIndex(row), column);
public void setValueAt(Object aValue, int row, int column) {
tableModel.setValueAt(aValue, modelIndex(row), column);
// Helper classes
private class Row implements Comparable {
private int modelIndex;
public Row(int index) {
this.modelIndex = index;
public int compareTo(Object o) {
int row1 = modelIndex;
int row2 = ((Row) o).modelIndex;
for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
Directive directive = (Directive) it.next();
int column = directive.column;
Object o1 = tableModel.getValueAt(row1, column);
Object o2 = tableModel.getValueAt(row2, column);
int comparison = 0;
// Define null less than everything, except null.
if (o1 == null && o2 == null) {
comparison = 0;
} else if (o1 == null) {
comparison = -1;
} else if (o2 == null) {
comparison = 1;
} else {
comparison = getComparator(column).compare(o1, o2);
if (comparison != 0) {
return directive.direction == DESCENDING ? -comparison : comparison;
return 0;
private class TableModelHandler implements TableModelListener {
public void tableChanged(TableModelEvent e) {
// If we're not sorting by anything, just pass the event along.
if (!isSorting()) {
clearSortingState();
fireTableChanged(e);
return;
// If the table structure has changed, cancel the sorting; the
// sorting columns may have been either moved or deleted from
// the model.
if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
cancelSorting();
fireTableChanged(e);
return;
// We can map a cell event through to the view without widening
// when the following conditions apply:
// a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and,
// b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and,
// c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and,
// d) a reverse lookup will not trigger a sort (modelToView != null)
// Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS.
// The last check, for (modelToView != null) is to see if modelToView
// is already allocated. If we don't do this check; sorting can become
// a performance bottleneck for applications where cells
// change rapidly in different parts of the table. If cells
// change alternately in the sorting column and then outside of
// it this class can end up re-sorting on alternate cell updates -
// which can be a performance problem for large tables. The last
// clause avoids this problem.
int column = e.getColumn();
if (e.getFirstRow() == e.getLastRow()
&& column != TableModelEvent.ALL_COLUMNS
&& getSortingStatus(column) == NOT_SORTED
&& modelToView != null) {
int viewIndex = getModelToView()[e.getFirstRow()];
fireTableChanged(new TableModelEvent(TableSorter.this,
viewIndex, viewIndex,
column, e.getType()));
return;
// Something has happened to the data that may have invalidated the row order.
clearSortingState();
fireTableDataChanged();
return;
private class MouseHandler extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
JTableHeader h = (JTableHeader) e.getSource();
TableColumnModel columnModel = h.getColumnModel();
int viewColumn = columnModel.getColumnIndexAtX(e.getX());
int column = columnModel.getColumn(viewColumn).getModelIndex();
if (column != -1) {
int status = getSortingStatus(column);
if (!e.isControlDown()) {
cancelSorting();
// Cycle the sorting states through {NOT_SORTED, ASCENDING, DESCENDING} or
// {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift is pressed.
status = status + (e.isShiftDown() ? -1 : 1);
status = (status + 4) % 3 - 1; // signed mod, returning {-1, 0, 1}
setSortingStatus(column, status);
private static class Arrow implements Icon {
private boolean descending;
private int size;
private int priority;
public Arrow(boolean descending, int size, int priority) {
this.descending = descending;
this.size = size;
this.priority = priority;
public void paintIcon(Component c, Graphics g, int x, int y) {
Color color = c == null ? Color.GRAY : c.getBackground();
// In a compound sort, make each succesive triangle 20%
// smaller than the previous one.
int dx = (int)(size/2*Math.pow(0.8, priority));
int dy = descending ? dx : -dx;
// Align icon (roughly) with font baseline.
y = y + 5*size/6 + (descending ? -dy : 0);
int shift = descending ? 1 : -1;
g.translate(x, y);
// Right diagonal.
g.setColor(color.darker());
g.drawLine(dx / 2, dy, 0, 0);
g.drawLine(dx / 2, dy + shift, 0, shift);
// Left diagonal.
g.setColor(color.brighter());
g.drawLine(dx / 2, dy, dx, 0);
g.drawLine(dx / 2, dy + shift, dx, shift);
// Horizontal line.
if (descending) {
g.setColor(color.darker().darker());
} else {
g.setColor(color.brighter().brighter());
g.drawLine(dx, 0, 0, 0);
g.setColor(color);
g.translate(-x, -y);
public int getIconWidth() {
return size;
public int getIconHeight() {
return size;
private class SortableHeaderRenderer implements TableCellRenderer {
private TableCellRenderer tableCellRenderer;
public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) {
this.tableCellRenderer = tableCellRenderer;
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
Component c = tableCellRenderer.getTableCellRendererComponent(table,
value, isSelected, hasFocus, row, column);
if (c instanceof JLabel) {
JLabel l = (JLabel) c;
l.setHorizontalTextPosition(JLabel.LEFT);
int modelColumn = table.convertColumnIndexToModel(column);
l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize()));
return c;
private static class Directive {
private int column;
private int direction;
public Directive(int column, int direction) {
this.column = column;
this.direction = direction;
}The table listens to the TableModel for changes. Changing the table by adding/removing
rows or columns has no affect on its table model. If you make changes to the table model
the table will be notified by its TableModelListener and change its view. So tell
MyTableModel about the change of data:
public class TableSorterDemo implements ActionListener
MyTableModel tableModel;
public TableSorterDemo()
defaultListModel = new DefaultListModel();
init1();
tableModel = new MyTableModel(defaultListModel);
TableSorter sorter = new TableSorter(tableModel);
public void actionPerformed(ActionEvent ae)
if (ae.getSource()==clickMe)
defaultListModel.removeAllElements();
init2();
tableModel.fireTableStructureChanged();
} -
How to display the time in Swing jtable
I need to display the time in jTable.
The time is a date field in ORACLE database (connected via
Oracle Thin drivers).
Im totally stuck. Please help.
TIA.Thanks for your reply.
My problem is that I have to display time. This time is a DATE field
in ORACLE database.
Im using FORTE (UI). What the jTable display is the DATE (1900-01-01) which is not what I want. I want the time to appear in the jTable (hh.mm.ss a).
What I did was to use the TO_CHAR function of ORACLE to format the
date to display the correct time in the VIEW before mapping this view
to be used as a MODEL by the jTable.
It works now.
Much appreciate all your help. Thanks. -
How To Delete Columns In JTable?
Hi,
I created a JTable and added values through DefaultTableModel. I Have JButton to add columns if need. I have another JButton to remove columns by entering the columns header.. eg: If there are three columns like A,B,C, If i need to remove column "C" means. I just click the remove button and enter the column header in the dilaog box eg: "C". and the columns will deleted. It is working properly. but if i add a new column means the removed columns also getting added and the datas are also in that same column. how delete a column and it's data in a single shot.
How to add network drivers in a save as dialog box. I have using my code in Windows and also in mac os.
Thanks in Advance
by,
MohanOne possibility would be to use a DefaultTableModel to store the data but have JTable access it through your own table model which mapped the columns and altered the column count etc..Why go to all that trouble. If you just want to hide a column, then remove the TableColumn from the TableColumnManager.
The requirement was to remove the data as well.
Implementing AbstractTableModel is really less hassle than it looks. Generally you only have to override getColumnName, getValueAt, getRowCount, getColumnCount and (usually) getColumnClass.Nobody said it was difficult, by why reimplement everything from scratch? Just extend DTM to add your new functionality. -
Mapping editor not working with Java 5 classes
I have a small protoype web service which I wrote using Tomcat/Axis/Spring/Hibernate and using EJB3 annotations for the mappings. I want to port the persistence layer to Toplink.
I installed Toplink 9.0.4.1 and added the toplink libraries to my project and implemented the DAO and the spring bean defs.
I opened the mapping editor and created a project. When I try to add classes to it for mapping I get an error that it can't find the classes (even though I selected them from the chooser). I figured it might be that they were compiled with Java 5, so I switched the JRE_HOME in setenv.cmd to my Java 5 JRE. Now I can import the classes and see the attributes but when I click on any of them in the Navigator panel, the editor panel remains blank. If I now try to save, I get:
java.lang.NullPointerException
at oracle.toplink.workbench.ui.tools.CheckListModel.getRowCount(CheckListModel.java:119)
at javax.swing.JTable.checkLeadAnchor(JTable.java:2949)
at javax.swing.JTable.tableChanged(JTable.java:2993)
at javax.swing.JTable.setModel(JTable.java:2827)
at oracle.toplink.workbench.ui.tools.CheckList.initialize(CheckList.java:47)
at oracle.toplink.workbench.ui.tools.CheckList.<init>(CheckList.java:26)
It seems that the mapping workbench doesnt work with Java 5. What should I do?Ah, I see. I saw another post here stating that must use 10.1.3.
-
Tab and back-tab out of a JTable
Hi There,
I have been working for some time on improving the default behaviour of the tab key within editable JTables. It largely works as required now, but I'm stuck on getting back-tab to leave the table if on row 0, col 0. I register tab and back-tab action in the table's ActionMap that looks like this:
class TabAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
if (getRowCount() > 0) {
int row = getSelectedRow();
int column = getSelectedColumn();
if (row < 0 || column < 0) {
row = 0;
column = 0;
do {
if (++column >= getColumnCount()) {
row++;
column = 0;
} while (row < getRowCount() && ! isCellTabbable(row, column));
if (row < getRowCount()) {
changeSelection(row, column, false, false);
} else {
clearSelection();
transferFocus();
} else {
transferFocus();
class BackTabAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
if (getRowCount() > 0) {
int row = getSelectedRow();
int column = getSelectedColumn();
if (row < 0 || column < 0) {
row = getRowCount() - 1;
column = getColumnCount() - 1;
do {
if (--column < 0) {
row--;
column = getColumnCount() - 1;
} while (row >= 0 && ! isCellTabbable(row, column));
if (row >= 0) {
changeSelection(row, column, false, false);
} else {
clearSelection();
transferFocusBackward();
KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
// fm.upFocusCycle(BTTTable_Editable.this);
// fm.focusPreviousComponent(BTTTable_Editable.this);
} else {
// transferFocusBackward();
// KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
// fm.upFocusCycle(BTTTable_Editable.this);
// fm.focusPreviousComponent(BTTTable_Editable.this);
}transferFocus() to go to the next screen component works fine - as long as I callsetFocusTraversalPolicyProvider(true); when I create the JTable.
But the backward traversal just doesn't work - instead it does nothing the first time you press back-tab on row 0 col 0, then if you press it again, focus goes onto the last row/col of the table.
As an alternative, I thought maybe I could call the Ctrl-back-tab action, but I can't find that registered in the action map - can anyone tell me how ctrl-back-tab is called and whether I can call that code directly?
Any ideas?
Thanks,
TimWhere's the rest of your code? What good does posting
the Actions do if we can't execute your code and see
how you've installed the Actions on the table? Who
knows where the mistake is. It may be in the posted
code or it may be in some other code.Well I assume the Action is registered okay because back-tab within the table works fine, and in fact using a debugger I can see that the focus request code (transferFocusBackward) is being called at the right time. I followed it into that code and it appears that it is picking up the tableCellEditor as the "previous" component which is "why" it isn't working right - not that that helps me to fix it.
Anyway, just to confirm, here is a complete standalone testcase:import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
* <br>
* <br>
* Created on 14/11/2005 by Tim Ryan
public class TabTable extends JTable implements FocusListener {
protected KeyStroke TAB = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
protected KeyStroke BACK_TAB = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
InputEvent.SHIFT_DOWN_MASK);
protected KeyStroke LEFT_ARROW = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
protected KeyStroke RIGHT_ARROW = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
public TabTable(Object[][] rowData, Object[] columnNames) {
super(rowData, columnNames);
initialise();
public TabTable() {
super();
initialise();
private void initialise() {
addFocusListener(this);
InputMap inMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap actionMap = getActionMap();
inMap.put(LEFT_ARROW, "None");
inMap.put(RIGHT_ARROW, "None");
actionMap.put(inMap.get(TAB), new TabAction());
actionMap.put(inMap.get(BACK_TAB), new BackTabAction());
setCellSelectionEnabled(true);
putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
setFocusTraversalPolicyProvider(true);
public void focusGained(FocusEvent e) {
if (getRowCount() > 0 && getSelectedRow() < 0) {
editCellAt(0, 0);
getEditorComponent().requestFocusInWindow();
public void focusLost(FocusEvent e) {
public void changeSelection(int row, int column, boolean toggle, boolean extend) {
super.changeSelection(row, column, toggle, false);
if (editCellAt(row, column)) {
getEditorComponent().requestFocusInWindow();
* This class handles the back-tab operation on the table.
* It repeatedly steps the selection backwards until the focus
* ends up on a cell that is allowable (see <code>isCellTabbable()</code>).
* If already at the end of the table then focus is transferred out
* to the previous component on the screen.
class BackTabAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
if (getRowCount() > 0) {
int row = getSelectedRow();
int column = getSelectedColumn();
if (row < 0 || column < 0) {
row = getRowCount() - 1;
column = getColumnCount() - 1;
do {
if (--column < 0) {
row--;
column = getColumnCount() - 1;
} while (row >= 0 && ! isCellTabbable(row, column));
if (row >= 0) {
changeSelection(row, column, false, false);
} else {
clearSelection();
// transferFocusBackward();
KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent();
} else {
// transferFocusBackward();
KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent();
* This class handles the tab operation on the table.
* It repeatedly steps the selection forwards until the focus ends
* up on a cell that is allowable (see <code>isCellTabbable()</code>).
* If already at the end of the table then focus is transferred out
* to the next component on the screen.
class TabAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
if (getRowCount() > 0) {
int row = getSelectedRow();
int column = getSelectedColumn();
if (row < 0 || column < 0) {
row = 0;
column = 0;
do {
if (++column >= getColumnCount()) {
row++;
column = 0;
} while (row < getRowCount() && ! isCellTabbable(row, column));
if (row < getRowCount()) {
changeSelection(row, column, false, false);
} else {
clearSelection();
transferFocus();
} else {
transferFocus();
* Some cells can be tabbed to, but are not actually editable.
* @param row
* @param column
* @return
private boolean isCellTabbable(int row, int column) {
return (column % 2 == 0);
public boolean isCellEditable(int row, int column) {
return (column == 1 || column == 3);
* @param args
public static void main(String[] args) {
JFrame frame = new JFrame("Tables test");
frame.setName("TablesTest");
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panelMain = new JPanel(new GridBagLayout());
frame.add(panelMain);
Object[][] tableData = new Object[2][6];
Object[] columnHeadings = new Object[] {"H0", "H1", "H2", "H3", "H4", "H5"};
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.insets = new Insets(10, 10, 10, 10);
JTextField field1 = new JTextField("left", 8);
field1.setName("left");
panelMain.add(field1, gbc);
Dimension tableSize = new Dimension(300, 300);
BTTTable table3 = new BTTTable_Editable(tableData, columnHeadings);
table3.setName("Editable");
JScrollPane scroll3 = new JScrollPane(table3);
scroll3.setPreferredSize(tableSize);
panelMain.add(scroll3, gbc);
JTextField field2 = new JTextField("right", 8);
field2.setName("right");
gbc.gridwidth = GridBagConstraints.REMAINDER;
panelMain.add(field2, gbc);
frame.setVisible(true);
}I thought it might be the focusGained() code, but commenting that out makes no difference.
And here is the code I would use to go to the
previous component:
KeyboardFocusManager.getCurrentKeyboardFocusManager().
focusPreviousComponent();I tried that too, as you can see from my original post. It gives the same answer.
So the big question is why does the FocusManager think that the "previous" field to the table is an editor component within the table?
Regards,
Tim -
Jtable Update problem .. Please help !!!!!!!!
Hi ,
I am trying to get my updated Jtable, stored in a table of database over a previous table ......after updating it via drag n drop ....
But even after I change the cell position to make the changes ... it still takes up the old value of that cell and not the new one while writing the data in the database table...
Here is the code .... Please see it and tell me if it is possible :
package newpackage;
import java.sql.*;
import java.util.Vector;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Dimension;
import java.text.*;
import newpackage.ExcelExporter;
import java.awt.Dimension;
import javax.swing.border.*;
import javax.swing.table.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import java.awt.print.*;
import java.awt.*;
import java.io.*;
import java.util.Random.*;
import javax.swing.*;
import java.text.*;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
public class tab7le extends javax.swing.JFrame {
Vector columnNames = new Vector();
Vector data = new Vector();
Connection con;
Statement stat;
ResultSet rs;
int li_cols = 0;
Vector allRows;
Vector row;
Vector newRow;
Vector colNames;
String dbColNames[];
String pkValues[];
String tableName;
ResultSetMetaData myM;
String pKeyCol;
Vector deletedKeys;
Vector newRows;
boolean ibRowNew = false;
boolean ibRowInserted = false;
private Map<String, Color> colormap = new HashMap<String, Color>();
/** Creates new form tab7le */
public tab7le() {
populate();
initComponents();
public void updateDB(){
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
catch (ClassNotFoundException e){
System.out.println("Cannot Load Driver!");
try{
String url = "jdbc:odbc:FAMS";
con = DriverManager.getConnection(url);
stat = con.createStatement();
rs = stat.executeQuery("Select * from SubAllot");
deletedKeys = new Vector();
newRows = new Vector();
myM = rs.getMetaData();
tableName = myM.getTableName(1);
li_cols = myM.getColumnCount();
dbColNames = new String[li_cols];
for(int col = 0; col < li_cols; col ++){
dbColNames[col] = myM.getColumnName(col + 1);
allRows = new Vector();
while(rs.next()){
newRow = new Vector();
for(int i = 1; i <= li_cols; i++){
newRow.addElement(rs.getObject(i));
} // for
allRows.addElement(newRow);
} // while
catch(SQLException e){
System.out.println(e.getMessage());
String updateLine[] = new String[dbColNames.length];
try{
DatabaseMetaData dbData = con.getMetaData();
String catalog;
// Get the name of all of the columns for this table
String curCol;
colNames = new Vector();
ResultSet rset1 = dbData.getColumns(null,null,tableName,null);
while (rset1.next()) {
curCol = rset1.getString(4);
colNames.addElement(curCol);
rset1.close();
pKeyCol = colNames.firstElement().toString();
// Go through the rows and perform INSERTS/UPDATES/DELETES
int totalrows;
totalrows = allRows.size();
String dbValues[];
Vector currentRow = new Vector();
pkValues = new String[allRows.size()];
// Get column names and values
for(int i=0;i < totalrows;i++){
currentRow = (Vector) allRows.elementAt(i);
int numElements = currentRow.size();
dbValues = new String[numElements];
for(int x = 0; x < numElements; x++){
String classType = currentRow.elementAt(x).getClass().toString();
int pos = classType.indexOf("String");
if(pos > 0){ // we have a String
dbValues[x] = "'" + currentRow.elementAt(x) + "'";
updateLine[x] = dbColNames[x] + " = " + "'" + currentRow.elementAt(x) + "',";
if (dbColNames[x].toUpperCase().equals(pKeyCol.toUpperCase())){
pkValues[i] = currentRow.elementAt(x).toString() ;
pos = classType.indexOf("Integer");
if(pos > 0){ // we have an Integer
dbValues[x] = currentRow.elementAt(x).toString();
if (dbColNames[x].toUpperCase().equals(pKeyCol.toUpperCase())){
pkValues[i] = currentRow.elementAt(x).toString();
else{
updateLine[x] = dbColNames[x] + " = " + currentRow.elementAt(x).toString() + ",";
pos = classType.indexOf("Boolean");
if(pos > 0){ // we have a Boolean
dbValues[x] = currentRow.elementAt(x).toString();
updateLine[x] = dbColNames[x] + " = " + currentRow.elementAt(x).toString() + ",";
if (dbColNames[x].toUpperCase().equals(pKeyCol.toUpperCase())){
pkValues[i] = currentRow.elementAt(x).toString() ;
} // For Loop
// If we are here, we have read one entire row of data. Do an UPDATE or an INSERT
int numNewRows = newRows.size();
int insertRow = 0;
boolean newRowFound;
for (int z = 0;z < numNewRows;z++){
insertRow = ((Integer) newRows.get(z)).intValue();
if(insertRow == i+1){
StringBuffer InsertSQL = new StringBuffer();
InsertSQL.append("INSERT INTO " + tableName + " (");
for(int zz=0;zz<=dbColNames.length-1;zz++){
if (dbColNames[zz] != null){
InsertSQL.append(dbColNames[zz] + ",");
// Strip out last comma
InsertSQL.replace(InsertSQL.length()-1,InsertSQL.length(),")");
InsertSQL.append(" VALUES(" + pkValues[i] + ",");
for(int c=1;c < dbValues.length;c++){
InsertSQL.append(dbValues[c] + ",");
InsertSQL.replace(InsertSQL.length()-1,InsertSQL.length(),")");
System.out.println(InsertSQL.toString());
stat.executeUpdate(InsertSQL.toString());
ibRowInserted=true;
} // End of INSERT Logic
// If row has not been INSERTED perform an UPDATE
if(ibRowInserted == false){
StringBuffer updateSQL = new StringBuffer();
updateSQL.append("UPDATE " + tableName + " SET ");
for(int z=0;z<=updateLine.length-1;z++){
if (updateLine[z] != null){
updateSQL.append(updateLine[z]);
// Replace the last ',' in the SQL statement with a blank. Then add WHERE clause
updateSQL.replace(updateSQL.length()-1,updateSQL.length()," ");
updateSQL.append(" WHERE " + pKeyCol + " = " + pkValues[i] );
System.out.println(updateSQL.toString());
stat.executeUpdate(updateSQL.toString());
} //for
catch(Exception ex){
System.out.println("SQL Error! Cannot perform SQL UPDATE " + ex.getMessage());
// Delete records from the DB
try{
int numDeletes = deletedKeys.size();
String deleteSQL;
for(int i = 0; i < numDeletes;i++){
deleteSQL = "DELETE FROM " + tableName + " WHERE " + pKeyCol + " = " +
((Integer) deletedKeys.get(i)).toString();
System.out.println(deleteSQL);
stat.executeUpdate(deleteSQL);
// Assume deletes where successful. Recreate Vector holding PK Keys
deletedKeys = new Vector();
catch(Exception ex){
System.out.println(ex.getMessage());
public void populate()
try
// Connect to the Database
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("Jdbc:Odbc:FAMS"," "," ");
System.out.println("ok1");
// Read data from a table
String sql;
sql = "Select * from SubAllot";
System.out.println("ok1");
Statement stmt = con.createStatement();
System.out.println("ok1");
ResultSet rs = stmt.executeQuery( sql );
System.out.println("ok1");
ResultSetMetaData md = rs.getMetaData();
System.out.println("ok1");
int columns = md.getColumnCount();
for(int i = 0;i<columns;i++){
columnNames.addElement(md.getColumnName(i+1));
System.out.println("ok2");
while (rs.next())
Vector row = new Vector(columns);
for (int i = 1; i <columns+1; i++)
row.addElement( rs.getObject(i) );
data.addElement( row );
catch(Exception e){
e.printStackTrace();
public void dropmenu(JTable table,TableColumn subpref1) {
//Set up the editor for the sport cells.
JComboBox comboBox = new JComboBox();
for (int i = 0;i<=20;i++)
comboBox.addItem(i);
subpref1.setCellEditor(new DefaultCellEditor(comboBox));
//Set up tool tips for the sport cells.
DefaultTableCellRenderer renderer =
new DefaultTableCellRenderer();
renderer.setToolTipText("Click for combo box");
subpref1.setCellRenderer(renderer);
abstract class StringTransferHandler extends TransferHandler {
public int dropAction;
protected abstract String exportString(final JComponent c);
protected abstract void importString(final JComponent c, final String str);
@Override
protected Transferable createTransferable(final JComponent c) {
return new StringSelection(exportString(c));
@Override
public int getSourceActions(final JComponent c) {
return MOVE;
@Override
public boolean importData(final JComponent c, final Transferable t) {
if (canImport(c, t.getTransferDataFlavors())) {
try {
String str = (String) t.getTransferData(DataFlavor.stringFlavor);
importString(c, str);
return true;
} catch (UnsupportedFlavorException ufe) {
} catch (IOException ioe) {
return false;
@Override
public boolean canImport(final JComponent c, final DataFlavor[] flavors) {
for (int ndx = 0; ndx < flavors.length; ndx++) {
if (DataFlavor.stringFlavor.equals(flavors[ndx])) {
return true;
return false;
class TableTransferHandler extends StringTransferHandler {
private int dragRow;
private int[] dragColumns;
private BufferedImage[] image;
private int row;
private int[] columns;
public JTable target;
private Map<String, Color> colormap;
private TableTransferHandler(final Map<String, Color> colormap) {
this.colormap = colormap;
@Override
protected Transferable createTransferable(final JComponent c) {
JTable table = (JTable) c;
dragRow = table.getSelectedRow();
dragColumns = table.getSelectedColumns();
createDragImage(table);
return new StringSelection(exportString(c));
protected String exportString(final JComponent c) {
JTable table = (JTable) c;
row = table.getSelectedRow();
columns = table.getSelectedColumns();
StringBuffer buff = new StringBuffer();
colormap.clear();
for (int j = 0; j < columns.length; j++) {
Object val = table.getValueAt(row, columns[j]);
buff.append(val == null ? "" : val.toString());
if (j != columns.length - 1) {
buff.append(",");
colormap.put(row+","+columns[j], Color.LIGHT_GRAY);
table.repaint();
return buff.toString();
protected void importString(final JComponent c, final String str) {
target = (JTable) c;
DefaultTableModel model = (DefaultTableModel) target.getModel();
String[] values = str.split("\n");
int colCount = target.getSelectedColumn();
int max = target.getColumnCount();
for (int ndx = 0; ndx < values.length; ndx++) {
String[] data = values[ndx].split(",");
for (int i = 0; i < data.length; i++) {
String string = data;
if(colCount < max){
Object val = model.getValueAt(target.getSelectedRow(), colCount);
model.setValueAt(string, target.getSelectedRow(), colCount);
model.setValueAt(val, dragRow, dragColumns[i]);
colCount++;
public BufferedImage[] getDragImage() {
return image;
private void createDragImage(final JTable table) {
if (dragColumns != null) {
try {
image = new BufferedImage[dragColumns.length];
for (int i = 0; i < dragColumns.length; i++) {
Rectangle cellBounds = table.getCellRect(dragRow, i, true);
TableCellRenderer r = table.getCellRenderer(dragRow, i);
DefaultTableModel m = (DefaultTableModel) table.getModel();
JComponent lbl = (JComponent) r.getTableCellRendererComponent(table,
table.getValueAt(dragRow, dragColumns[i]), false, false, dragRow, i);
lbl.setBounds(cellBounds);
BufferedImage img = new BufferedImage(lbl.getWidth(), lbl.getHeight(),
BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D graphics = img.createGraphics();
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f));
lbl.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
lbl.paint(graphics);
graphics.dispose();
image[i] = img;
} catch (RuntimeException re) {
class TableDropTarget extends DropTarget {
private Insets autoscrollInsets = new Insets(20, 20, 20, 20);
private Rectangle rect2D = new Rectangle();
private TableTransferHandler handler;
public TableDropTarget(final TableTransferHandler h) {
super();
this.handler = h;
@Override
public void dragOver(final DropTargetDragEvent dtde) {
handler.dropAction = dtde.getDropAction();
JTable table = (JTable) dtde.getDropTargetContext().getComponent();
Point location = dtde.getLocation();
int row = table.rowAtPoint(location);
int column = table.columnAtPoint(location);
table.changeSelection(row, column, false, false);
paintImage(table, location);
autoscroll(table, location);
super.dragOver(dtde);
public void dragExit(final DropTargetDragEvent dtde) {
clearImage((JTable) dtde.getDropTargetContext().getComponent());
super.dragExit(dtde);
@Override
public void drop(final DropTargetDropEvent dtde) {
Transferable data = dtde.getTransferable();
JTable table = (JTable) dtde.getDropTargetContext().getComponent();
clearImage(table);
handler.importData(table, data);
super.drop(dtde);
private final void paintImage(final JTable table, final Point location) {
Point pt = new Point(location);
BufferedImage[] image = handler.getDragImage();
if (image != null) {
table.paintImmediately(rect2D.getBounds());
rect2D.setLocation(pt.x - 15, pt.y - 15);
int wRect2D = 0;
int hRect2D = 0;
for (int i = 0; i < image.length; i++) {
table.getGraphics().drawImage(image[i], pt.x - 15, pt.y - 15, table);
pt.x += image[i].getWidth();
if (hRect2D < image[i].getHeight()) {
hRect2D = image[i].getHeight();
wRect2D += image[i].getWidth();
rect2D.setSize(wRect2D, hRect2D);
private final void clearImage(final JTable table) {
table.paintImmediately(rect2D.getBounds());
private Insets getAutoscrollInsets() {
return autoscrollInsets;
private void autoscroll(final JTable table, final Point cursorLocation) {
Insets insets = getAutoscrollInsets();
Rectangle outer = table.getVisibleRect();
Rectangle inner = new Rectangle(outer.x + insets.left,
outer.y + insets.top,
outer.width - (insets.left + insets.right),
outer.height - (insets.top + insets.bottom));
if (!inner.contains(cursorLocation)) {
Rectangle scrollRect = new Rectangle(cursorLocation.x - insets.left,
cursorLocation.y - insets.top,
insets.left + insets.right,
insets.top + insets.bottom);
table.scrollRectToVisible(scrollRect);
/** 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">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
table = new javax.swing.JTable();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
table.setModel(new javax.swing.table.DefaultTableModel(
data, columnNames
jScrollPane1.setViewportView(table);
//populate();
table.getTableHeader().setReorderingAllowed(false);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setCellSelectionEnabled(true);
table.setDragEnabled(true);
TableTransferHandler th = new TableTransferHandler(colormap);
table.setTransferHandler(th);
table.setDropTarget(new TableDropTarget(th));
dropmenu(table, table.getColumnModel().getColumn(11));
jButton1.setText("Update");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
jButton2.setText("Ex");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(92, 92, 92)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 605, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGap(347, 347, 347)
.addComponent(jButton1)
.addGap(115, 115, 115)
.addComponent(jButton2)))
.addContainerGap(73, Short.MAX_VALUE))
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(47, 47, 47)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 354, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(58, 58, 58)
.addComponent(jButton1)
.addContainerGap(83, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jButton2)
.addGap(65, 65, 65))))
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
updateDB(); // TODO add your handling code here:
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
try {
String pathToDesktop = System.getProperty("user.home")+File.separator+"Desktop";
pathToDesktop = pathToDesktop + "//Final Allotment.xls";
ExcelExporter exp = new ExcelExporter();
exp.exportTable(table, new File(pathToDesktop));
JOptionPane.showMessageDialog(this,"File exported and saved on desktop!");
catch (IOException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
} // TODO add your handling code here:
* @param args the command line arguments
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new tab7le().setVisible(true);
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable table;
// End of variables declaration
Please help !!!!!!!!
Thanks in advance.....Here is the code Do you expect people to read through 400 lines of code to understand what you are doing?
Why post code with access to a database? We can't access the database.
Search the forum for my "Database Information" (without the space) example class which shows you how to refresh a table with new data.
If you need further help then you need to create a [Short, Self Contained, Compilable and Executable, Example Program (SSCCE)|http://homepage1.nifty.com/algafield/sscce.html], that demonstrates the incorrect behaviour. -
suppose you are editing a combobox in a jtable.
in the combobox, there are items like
A0
A20
C0
C20
E0
E20
my main goal is traverse and edit the table by keyboard.
when i go to a jcombobox, e.g. i hit the button C, it only focus the combo, not selecting item.
JComboBox comboBox = new JComboBox(arr){
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
int condition, boolean pressed) {
boolean retValue = super.processKeyBinding(ks, e, condition, pressed);
if (!retValue && isStartingCellEdit() && editor != null) {
//editor.setItem(String.valueOf(ks.getKeyChar()));
JComponent editorComponent = (JComponent) getEditor().getEditorComponent();
InputMap map = editorComponent.getInputMap(condition);
ActionMap am = editorComponent.getActionMap();
if(map!=null && am!=null && isEnabled()){
Object binding = map.get(ks);
Action action = (binding==null) ? null : am.get(binding);
if(action!=null){
SwingUtilities.notifyAction(action, ks, e, editorComponent,
e.getModifiers());
return retValue;
private boolean isStartingCellEdit() {
JTable table = (JTable) SwingUtilities.getAncestorOfClass(
JTable.class, this);
return table != null
&& table.isFocusOwner()
&& !Boolean.FALSE.equals((Boolean) table
.getClientProperty("JTable.autoStartsEdit"));
};so if i want to write C20,
when i pressed C, it selects the combo, so after pressing 2 and 0, it searches for 20.
how can i do this not to lose the first char?suppose you are editing a combobox in a jtable.
in the combobox, there are items like
A0
A20
C0
C20
E0
E20
my main goal is traverse and edit the table by keyboard.
when i go to a jcombobox, e.g. i hit the button C, it only focus the combo, not selecting item.
JComboBox comboBox = new JComboBox(arr){
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
int condition, boolean pressed) {
boolean retValue = super.processKeyBinding(ks, e, condition, pressed);
if (!retValue && isStartingCellEdit() && editor != null) {
//editor.setItem(String.valueOf(ks.getKeyChar()));
JComponent editorComponent = (JComponent) getEditor().getEditorComponent();
InputMap map = editorComponent.getInputMap(condition);
ActionMap am = editorComponent.getActionMap();
if(map!=null && am!=null && isEnabled()){
Object binding = map.get(ks);
Action action = (binding==null) ? null : am.get(binding);
if(action!=null){
SwingUtilities.notifyAction(action, ks, e, editorComponent,
e.getModifiers());
return retValue;
private boolean isStartingCellEdit() {
JTable table = (JTable) SwingUtilities.getAncestorOfClass(
JTable.class, this);
return table != null
&& table.isFocusOwner()
&& !Boolean.FALSE.equals((Boolean) table
.getClientProperty("JTable.autoStartsEdit"));
};so if i want to write C20,
when i pressed C, it selects the combo, so after pressing 2 and 0, it searches for 20.
how can i do this not to lose the first char? -
Hi , I have a problem add printing routine for printing to this code.
Sombody help me.
I no post all code but my prube no functioned.
thanks you
code :
package com.froses.tablemodels;
* SimpleDBTable.java
* A demonstration of using ODBC to read data from an Access database
* and display the values in a JTable.
* This file requires that the new.mdb file exists and has been mapped
* using ODBC to a datastore named 'newdb' with blank username and password.
* Gordon Branson January 2004
import javax.swing.*;
import java.awt.Dimension;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
public class SimpleDBTable extends JPanel {
private boolean DEBUG = true;
private int rows = 10, cols = 5;
private String url = "jdbc:odbc:Sego";
private Connection con;
private Statement stmt;
private JButton btnRead, btnDelete, btnWrite, btnClose;
/* Setup the table column names */
private String[] columnNames = {"Medico",
"Descripci�n",
"Obra social",
"Items",
"Codigo"};
/* declare an Object array large enough to hold the database table */
private Object[][] data = new Object[rows][cols];
private JTable table;
public SimpleDBTable() {
super(new BorderLayout());
/* Load ODBC diver */
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
} catch(java.lang.ClassNotFoundException e) {
System.err.print("ClassNotFoundException: ");
System.err.println(e.getMessage());
/* create the JTable */
table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 300));
/* Now read from the database */
populateTable();
if (DEBUG) {
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
printDebugData();
//Create the Title Label.
JLabel title = new JLabel("Database Access in Java");
title.setFont(new java.awt.Font("Arial", 1, 24));
title.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
//Add the label to this panel.
add("North",title);
//Create the scroll pane and add the JTable to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
add("Center",scrollPane);
// Create a button panel with a default layout
JPanel buttons = new JPanel();
// Create the buttons
btnRead = new JButton("Read");
btnClose = new JButton("Close");
btnWrite = new JButton("Write");
btnDelete = new JButton("Delete");
// Add action listeners
btnRead.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
populateTable();
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
deleteTable();
btnWrite.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
writeTable();
btnClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.exit(0);
// Add the buttons to the button panel
buttons.add(btnRead);
buttons.add(btnWrite);
buttons.add(btnDelete);
buttons.add(btnClose);
// Add the buttons panel to main panel
add("South",buttons);
private void populateTable(){
/* Define the SQL Query to get all data from the database table */
String query = "SELECT * FROM Datos";
/* First clear the JTable */
clearTable();
/* get a handle for the JTable */
javax.swing.table.TableModel model = table.getModel();
int r = 0;
try {
/* connect to the database */
con = DriverManager.getConnection(url, "", "");
stmt = con.createStatement();
/* run the Query getting the results into rs */
ResultSet rs = stmt.executeQuery(query);
while ((rs.next()) && (r<rows)) {
/* for each row get the fields
note the use of Object - necessary
to put the values into the JTable */
Object nm = rs.getObject("Medico_solic");
Object sup = rs.getObject("Descip_Item");
Object pri = rs.getObject("Obra_Social");
Object sal = rs.getObject("M�dico_opera");
Object tot = rs.getObject("Codigo_estudio");
model.setValueAt(nm, r, 0);
model.setValueAt(sup, r, 1);
model.setValueAt(pri, r, 2);
model.setValueAt(sal, r, 3);
model.setValueAt(tot, r, 4);
r++;
stmt.close();
con.close();
} catch(SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
private void deleteTable(){
emptyTable();
clearTable();
private void emptyTable(){
/* Define the SQL Query to get all data from the database table */
String query = "DELETE * FROM COFFEES";
try {
/* connect to the database */
con = DriverManager.getConnection(url, "", "");
stmt = con.createStatement();
/* run the Query */
stmt.executeUpdate(query);
stmt.close();
con.close();
} catch(SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
/* private void writeTable(){
/* First clear the table */
/* emptyTable();
PreparedStatement insertRow;// = con.prepareStatement(
String insertString = "INSERT INTO COFFEES " +
"VALUES (?, ?, ?, ?, ?)";
int numRows = table.getRowCount();
javax.swing.table.TableModel model = table.getModel();
Integer sup, sal, tot;
Double pri;
Object o;
if(DEBUG) System.out.println("\nDoing Write...");
try {
/* connect to the database */
/* con = DriverManager.getConnection(url, "", "");
insertRow = con.prepareStatement(insertString);
for (int r=0; r < numRows; r++) {
if (model.getValueAt(r, 0) != null){
insertRow.setString(1, (String) model.getValueAt(r, 0));
//o = model.getValueAt(r, 1);
if(DEBUG) System.out.println(model.getValueAt(r, 1).toString());
sup = new Integer((String) model.getValueAt(r, 1));
insertRow.setInt(2, sup.intValue());
pri = new Double((String) model.getValueAt(r, 2));
insertRow.setDouble(3, pri.doubleValue());
sal = new Integer((String) model.getValueAt(r, 3));
insertRow.setInt(4, sal.intValue());
tot = new Integer((String) model.getValueAt(r, 4));
insertRow.setInt(5, tot.intValue());
insertRow.executeUpdate();
System.out.println("Writing Row " + r);
insertRow.close();
con.close();
} catch(SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
clearTable();
private void writeTable(){
/* First clear the table */
emptyTable();
Statement insertRow;// = con.prepareStatement(
String baseString = "INSERT INTO Datos " +
"VALUES ('";
String insertString;
int numRows = table.getRowCount();
javax.swing.table.TableModel model = table.getModel();
Integer sup, sal, tot;
Double pri;
Object o;
if(DEBUG) System.out.println("\nDoing Write...");
try {
/* connect to the database */
con = DriverManager.getConnection(url, "", "");
for (int r=0; r < numRows; r++) {
if (model.getValueAt(r, 0) != null){
insertString = baseString + model.getValueAt(r, 0)+"',";
insertString = insertString + model.getValueAt(r, 1)+",";
insertString = insertString + model.getValueAt(r, 2)+",";
insertString = insertString + model.getValueAt(r, 3)+",";
insertString = insertString + model.getValueAt(r, 4)+");";
if(DEBUG) System.out.println(insertString);
insertRow = con.createStatement();
insertRow.executeUpdate(insertString);
System.out.println("Writing Row " + r);
insertRow.close();
con.close();
} catch(SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
clearTable();
private void clearTable(){
int numRows = table.getRowCount();
int numCols = table.getColumnCount();
javax.swing.table.TableModel model = table.getModel();
for (int i=0; i < numRows; i++) {
for (int j=0; j < numCols; j++) {
model.setValueAt(null, i, j);
private void printDebugData() {
int numRows = table.getRowCount();
int numCols = table.getColumnCount();
javax.swing.table.TableModel model = table.getModel();
System.out.println("Value of data: ");
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + model.getValueAt(i, j));
System.out.println();
System.out.println("--------------------------");
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
JFrame frame = new JFrame("SimpleDBTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
SimpleDBTable newContentPane = new SimpleDBTable();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}http://forum.java.sun.com/thread.jspa?threadID=626207
Maybe you are looking for
-
Voice Memos on my iPhone 5s, iOS 8.02, spontaneously stops recording twice at 25:44. Anyone else having this problem where voice Memos spontaneously stops recording? Sometimes it works fine, sometimes it stops after only a few minutes. Anyone care
-
How to use many external files?
Hi, for a single onetime load of a .csv file ,I create a file module,then use the flatfile wizard to specify my file. Now If I have to automatically load all files which are in certain location on Windows Server,how do i achieve this ? All my .csv fi
-
Why can't I log into my itunes and icoud from my iphone?
Why can't I log into my itunes and icloud from my iphone?
-
Ipad 2 restarting when connected to charger, when not connected it´s dead. It dies before i can reset it, every time it restarts it´s "on" less time. I managed to delete and reset all settings and files but dies before I can configure. When i connect
-
hi experts, please help me from following error. when i am working with file to rfc. file is picking and both sender and receiver cc's are showing successful but in IE i am getting following error. <?xml version="1.0" encoding="UTF-8" standalone="yes