I am using a JXComboBox as cell editor in a modified JXTable/RXTable with custom models. I am noticing a delay when starting to type inside the cell.
Edit: also, if you type more keys faster in the box, the first one you typed will not appear first in the editor.
The table:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
import org.jdesktop.swingx.JXTable;
/**
* This is a modified version of RXTable following this thread:
* http://stackoverflow.com/questions/7365397/combining-jxtable-with-rxtable.
* The JRXTable provides some extensions to the default JTable
*
* 1) Select All editing - when a text related cell is placed in editing mode
* the text is selected. Controlled by invoking a "setSelectAll..." method.
*
* 2) reorderColumns - static convenience method for reodering table columns
*/
public class JRXTable extends JXTable {
private boolean isSelectAllForMouseEvent = false;
private boolean isSelectAllForActionEvent = false;
private boolean isSelectAllForKeyEvent = false;
//
// Constructors
//
/**
* Constructs a default
* <code>JRXTable</code> that is initialized with a default data model, a
* default column model, and a default selection model.
*/
public JRXTable() {
this(null, null, null);
}
/**
* Constructs a
* <code>JRXTable</code> that is initialized with
* <code>dm</code> as the data model, a default column model, and a default
* selection model.
*
* #param dm the data model for the table
*/
public JRXTable(TableModel dm) {
this(dm, null, null);
}
/**
* Constructs a
* <code>JRXTable</code> that is initialized with
* <code>dm</code> as the data model,
* <code>cm</code> as the column model, and a default selection model.
*
* #param dm the data model for the table
* #param cm the column model for the table
*/
public JRXTable(TableModel dm, TableColumnModel cm) {
this(dm, cm, null);
}
/**
* Constructs a
* <code>JRXTable</code> that is initialized with
* <code>dm</code> as the data model,
* <code>cm</code> as the column model, and
* <code>sm</code> as the selection model. If any of the parameters are
* <code>null</code> this method will initialize the table with the
* corresponding default model. The
* <code>autoCreateColumnsFromModel</code> flag is set to false if
* <code>cm</code> is non-null, otherwise it is set to true and the column
* model is populated with suitable
* <code>TableColumns</code> for the columns in
* <code>dm</code>.
*
* #param dm the data model for the table
* #param cm the column model for the table
* #param sm the row selection model for the table
*/
public JRXTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
super(dm, cm, sm);
}
/**
* Constructs a
* <code>JRXTable</code> with
* <code>numRows</code> and
* <code>numColumns</code> of empty cells using
* <code>DefaultTableModel</code>. The columns will have names of the form
* "A", "B", "C", etc.
*
* #param numRows the number of rows the table holds
* #param numColumns the number of columns the table holds
*/
public JRXTable(int numRows, int numColumns) {
this(new DefaultTableModel(numRows, numColumns));
}
/**
* Constructs a
* <code>JRXTable</code> to display the values in the
* <code>Vector</code> of
* <code>Vectors</code>,
* <code>rowData</code>, with column names,
* <code>columnNames</code>. The
* <code>Vectors</code> contained in
* <code>rowData</code> should contain the values for that row. In other
* words, the value of the cell at row 1, column 5 can be obtained with the
* following code:
* <p>
* <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
* <p>
*
* #param rowData the data for the new table
* #param columnNames names of each column
*/
public JRXTable(Vector rowData, Vector columnNames) {
this(new DefaultTableModel(rowData, columnNames));
}
/**
* Constructs a
* <code>JRXTable</code> to display the values in the two dimensional array,
* <code>rowData</code>, with column names,
* <code>columnNames</code>.
* <code>rowData</code> is an array of rows, so the value of the cell at row
* 1, column 5 can be obtained with the following code:
* <p>
* <pre> rowData[1][5]; </pre>
* <p>
* All rows must be of the same length as
* <code>columnNames</code>.
* <p>
*
* #param rowData the data for the new table
* #param columnNames names of each column
*/
public JRXTable(final Object[][] rowData, final Object[] columnNames) {
super(rowData, columnNames);
}
//
// Overridden methods
//
/*
* Override to provide Select All editing functionality
*/
#Override
public boolean editCellAt(int row, int column, EventObject e) {
boolean result = super.editCellAt(row, column, e);
// my editing
//
// if (e instanceof KeyEvent && isSelectAllForKeyEvent) {
// KeyEvent keyEvent = (KeyEvent) e;
// Character keyChar = keyEvent.getKeyChar();
// if (keyChar == KeyEvent.VK_ESCAPE) {
// return result;
// }
// }
// my editing
if (isSelectAllForMouseEvent
|| isSelectAllForActionEvent
|| isSelectAllForKeyEvent) {
selectAll(e);
}
return result;
}
/*
* Select the text when editing on a text related cell is started
*/
private void selectAll(EventObject e) {
final Component editor = getEditorComponent();
// add suport for the text editor from a ComboBox
// move to editCellAt method?
if (getEditorComponent() instanceof JComboBox) {
final JComboBox combo = (JComboBox) getEditorComponent();
ComboBoxEditor comboEditor = combo.getEditor();
final JTextField comboTextField = (JTextField) comboEditor.getEditorComponent();
// comboEditor.selectAll();
if (e instanceof KeyEvent && isSelectAllForKeyEvent) {
KeyEvent keyEvent = (KeyEvent) e;
final Character keyChar = keyEvent.getKeyChar();
if (keyChar == KeyEvent.VK_ESCAPE) {
System.out.println("escape");
// combo.getFocusCycleRootAncestor().requestFocus();
// combo.transferFocus();
} else {
comboEditor.selectAll();
comboTextField.setText(comboTextField.getText());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// set null value if the Delete key is pressed
if (keyChar == KeyEvent.VK_DELETE) {
combo.setSelectedItem(null);
comboTextField.setText(null);
} else {
comboTextField.selectAll();
// comboTextField.setText("");
}
}
});
}
}
return;
}
if (editor == null
|| !(editor instanceof JTextComponent
|| editor instanceof JFormattedTextField)) {
return;
}
if (e == null) {
((JTextComponent) editor).selectAll();
return;
}
// Typing in the cell was used to activate the editor
if (e instanceof KeyEvent && isSelectAllForKeyEvent) {
((JTextComponent) editor).selectAll();
return;
}
// If the cell we are dealing with is a JFormattedTextField
// force to commit, and invoke selectall
if (editor instanceof JFormattedTextField) {
invokeSelectAll((JFormattedTextField) editor);
return;
}
// F2 was used to activate the editor
if (e instanceof ActionEvent && isSelectAllForActionEvent) {
((JTextComponent) editor).selectAll();
return;
}
// A mouse click was used to activate the editor.
// Generally this is a double click and the second mouse click is
// passed to the editor which would remove the text selection unless
// we use the invokeLater()
if (e instanceof MouseEvent && isSelectAllForMouseEvent) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
((JTextComponent) editor).selectAll();
}
});
}
}
private void invokeSelectAll(final JFormattedTextField editor) {
// old trick: force to commit, and invoke selectall
editor.setText(editor.getText());
SwingUtilities.invokeLater(new Runnable() {
public void run() {
editor.selectAll();
}
});
}
//
// Newly added methods
//
/*
* Sets the Select All property for for all event types
*/
public void setSelectAllForEdit(boolean isSelectAllForEdit) {
setSelectAllForMouseEvent(isSelectAllForEdit);
setSelectAllForActionEvent(isSelectAllForEdit);
setSelectAllForKeyEvent(isSelectAllForEdit);
}
/*
* Set the Select All property when editing is invoked by the mouse
*/
public void setSelectAllForMouseEvent(boolean isSelectAllForMouseEvent) {
this.isSelectAllForMouseEvent = isSelectAllForMouseEvent;
}
/*
* Set the Select All property when editing is invoked by the "F2" key
*/
public void setSelectAllForActionEvent(boolean isSelectAllForActionEvent) {
this.isSelectAllForActionEvent = isSelectAllForActionEvent;
}
/*
* Set the Select All property when editing is invoked by
* typing directly into the cell
*/
public void setSelectAllForKeyEvent(boolean isSelectAllForKeyEvent) {
this.isSelectAllForKeyEvent = isSelectAllForKeyEvent;
}
//
// Static, convenience methods
//
/**
* Convenience method to order the table columns of a table. The columns are
* ordered based on the column names specified in the array. If the column
* name is not found then no column is moved. This means you can specify a
* null value to preserve the current order of a given column.
*
* #param table the table containing the columns to be sorted
* #param columnNames an array containing the column names in the order they
* should be displayed
*/
public static void reorderColumns(JTable table, Object... columnNames) {
TableColumnModel model = table.getColumnModel();
for (int newIndex = 0; newIndex < columnNames.length; newIndex++) {
try {
Object columnName = columnNames[newIndex];
int index = model.getColumnIndex(columnName);
model.moveColumn(index, newIndex);
} catch (IllegalArgumentException e) {
}
}
}
} // End of Class JRXTable
The test class:
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.HashMap;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.table.DefaultTableModel;
import org.jdesktop.swingx.JXComboBox;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
import org.jdesktop.swingx.renderer.DefaultListRenderer;
import org.jdesktop.swingx.renderer.DefaultTableRenderer;
import org.jdesktop.swingx.renderer.StringValue;
public class TestSwingXComboCellEditor {
public static void main(String[] args) {
TestSwingXComboCellEditor test = new TestSwingXComboCellEditor();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create and add a tabbed pane to the frame
JTabbedPane tabbedPane = new JTabbedPane();
frame.getContentPane().add(tabbedPane);
//create a table and add it to a scroll pane in a new tab
JRXTable table = new JRXTable();
table.setModel(new DefaultTableModel(new Object[]{"A", "B"}, 5));
table.setSelectAllForEdit(true);
JScrollPane scrollPane = new JScrollPane(table);
tabbedPane.addTab("test", scrollPane);
// create a simple JComboBox and set is as table cell editor on column A
UserRepository rep = new UserRepository();
UserInfo[] comboElements = rep.getAllUsers();
DefaultComboBoxModel model = new DefaultComboBoxModel(comboElements);
JXComboBox comboBox = new JXComboBox(model);
StringValue stringValue = new StringValue() {
public String getString(Object value) {
if (value instanceof UserInfo) {
UserInfo userInfo = (UserInfo) value;
return userInfo.getFirstName();
} else {
return "";
}
}
};
ComboBoxCellEditor cellEditor = new ComboBoxCellEditor(comboBox);
comboBox.setRenderer(new DefaultListRenderer(stringValue));
comboBox.setEditable(true);
AutoCompleteDecorator.decorate(comboBox, new ObjectToStringConverter() {
#Override
public String getPreferredStringForItem(Object item) {
if (item instanceof UserInfo) {
return ((UserInfo) item).getFirstName();
} else {
return null;
}
}
});
table.getColumn("A").setCellEditor(cellEditor);
table.getColumn("A").setCellRenderer(new DefaultTableRenderer(stringValue));
// pack and show frame
frame.pack();
frame.setVisible(true);
}
public class UserInfo {
private String firstName;
private String lastName;
public UserInfo(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
public class UserRepository {
UserInfo[] comboElements;
HashMap<String, UserInfo> objectsMap;
public UserRepository() {
comboElements = new UserInfo[5];
comboElements[0] = new UserInfo("John", "Doe");
comboElements[1] = new UserInfo("Betty", "Doe");
comboElements[2] = new UserInfo("Elenor", "Smith");
comboElements[3] = new UserInfo("Helen", "Kelly");
comboElements[4] = new UserInfo("Joe", "Black");
objectsMap = new HashMap<>();
for (int i = 0; i < 5; i++) {
objectsMap.put(comboElements[i].getFirstName(), comboElements[i]);
}
}
public UserInfo getUserInfo(String name) {
return objectsMap.get(name);
}
public UserInfo[] getAllUsers() {
return comboElements;
}
}
}
Related
I found this Custom fileTree Component from github to implement it into my project, I'm using this JTree component to access and display file information like file name, file path, file type in a JTable. The project is about File Tagging, I've implemented all necessary features related to that! The only thing remaining is an issue that I need to rectify in fileTree Component
Here is the code of all the java classes:
Constants.java
package jtree;
public class Constants {
/**
* the name of the OS as given by the Java system property "os.name"
*/
public final static String osname = System.getProperty("os.name");
/**
* true if the program is running on OS X
*/
public final static boolean isOSX = osname.equalsIgnoreCase("Mac OS X");
/**
* true if the program is running on Linux
*/
public final static boolean isLinux = osname.equalsIgnoreCase("Linux");
/**
* true if the program is running on Solaris
*/
public final static boolean isSolaris = osname.equalsIgnoreCase("SunOS");
/**
* true if the program is running on Windows Vista
*/
public final static boolean isVista = osname.equalsIgnoreCase("Windows Vista");
/**
* true if the program is running on Windows
*/
public final static boolean isWindows = !(isOSX || isLinux || isSolaris);
}
FileTree.java
package jtree;
import java.awt.Component;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.JFileChooser;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
public class FileTree extends JTree {
/** Creates a new instance of FileTree */
public FileTree() {
super(new DefaultTreeModel(new DefaultMutableTreeNode("root")));
fileTreeModel = (DefaultTreeModel)treeModel;
showHiddenFiles = false;
showFiles = true;
navigateOSXApps = false;
initComponents();
initListeners();
}
/**
* returns the data model used by the FileTree. This method returns the same value
* as <code>getModel()</code>, with the only exception being that this method
* returns a <code>DefaultTreeModel</code>
* #return the data model used by the <code>FileTree</code>
*/
public DefaultTreeModel getFileTreeModel() {
return fileTreeModel;
}
/**
* returns the selected file in the tree. If there are multiple selections in the
* tree, then it will return the <code>File</code> associated with the value
* returned from <code>getSelectionPath</code>. You can enable/disable mutliple
* selections by changing the mode of the <code>TreeSelectionModel</code>.
* #return the selected file in the tree
*/
public File getSelectedFile() {
TreePath treePath = getSelectionPath();
if (treePath == null)
return null;
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
FileTreeNode fileTreeNode = (FileTreeNode)treeNode.getUserObject();
return fileTreeNode.file;
}
/**
* returns an array of the files selected in the tree. To enable/disable multiple
* selections, you can change the selection mode in the
* <code>TreeSelectionModel</code>.
* #return an array of the files selected in the tree
*/
public File[] getSelectedFiles() {
TreePath[] treePaths = getSelectionPaths();
if (treePaths == null)
return null;
File [] files = new File[treePaths.length];
for (int i=0; i<treePaths.length; i++)
{
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePaths[i].getLastPathComponent();
FileTreeNode fileTreeNode = (FileTreeNode)treeNode.getUserObject();
files[i] = fileTreeNode.file;
}
return files;
}
/**
* initializes class members
*/
private void initComponents() {
if (Constants.isWindows)
fsv = FileSystemView.getFileSystemView();
initRoot();
setCellRenderer(new FileTreeCellRenderer());
setEditable(false);
}
/**
* sets up the listeners for the tree
*/
private void initListeners() {
addTreeExpansionListener(new TreeExpansionListener() {
public void treeCollapsed(TreeExpansionEvent event) {
}
public void treeExpanded(TreeExpansionEvent event) {
TreePath path = event.getPath();
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) path.getLastPathComponent();
treeNode.removeAllChildren();
populateSubTree(treeNode);
fileTreeModel.nodeStructureChanged(treeNode);
}
});
FileTreeListener ftl = new FileTreeListener(this);
addMouseListener(ftl);
}
/**
* initializes the tree model
*/
private void initRoot() {
File[] roots = null;
if (Constants.isWindows)
roots = fsv.getRoots();
else
roots = File.listRoots();
if (roots.length == 1)
{
rootNode = new DefaultMutableTreeNode(new FileTreeNode(roots[0]));
populateSubTree(rootNode);
}
else if (roots.length > 1)
{
rootNode = new DefaultMutableTreeNode("Computer");
for (File root:roots)
rootNode.add(new DefaultMutableTreeNode(root));
}
else
rootNode = new DefaultMutableTreeNode("Error");
fileTreeModel.setRoot(rootNode);
}
/**
* returns true if deleting is allowed in the tree, false otherwise. The default
* value is false.
* #return true if deleting is allowed in the tree, false otherwise
*/
public boolean isDeleteEnabled() {
return allowDelete;
}
/**
* returns true if the user can navigate into OS X application bundles. false
* otherwise
* #return true if the user can navigate into OS X application bundles
*/
public boolean isNavigateOSXApps() {
return navigateOSXApps;
}
/**
* returns true if files will be shown in the tree, false otherwise. Default value
* is true.
* #return true if files will be shown in the tree, false otherwise
*/
public boolean isShowFiles() {
return showFiles;
}
/**
* returns true if the tree will show hidden files, false otherwise. Default value
* is false.
* #return true if the tree will show hidden files, false otherwise
*/
public boolean isShowHiddenFiles() {
return showHiddenFiles;
}
/**
* called whenever a node is expanded
* #param node the node to expand
*/
private void populateSubTree(DefaultMutableTreeNode node) {
Object userObject = node.getUserObject();
if (userObject instanceof FileTreeNode)
{
FileTreeNode fileTreeNode = (FileTreeNode)userObject;
File []files = fileTreeNode.file.listFiles();
// Windows displays directories before regular files, so we're going
// to sort the list of files such that directories appear first
if (Constants.isWindows)
{
Arrays.sort(files, new Comparator<File>() {
public int compare(File f1, File f2) {
boolean f1IsDir = f1.isDirectory();
boolean f2IsDir = f2.isDirectory();
if (f1IsDir == f2IsDir)
return f1.compareTo(f2);
if (f1IsDir && !f2IsDir)
return -1;
// here we assume that f1 is a file, and f2 is a directory
return 1;
}
});
}
else
Arrays.sort(files);
for (File file:files)
{
if (file.isFile() && !showFiles)
continue;
if (!showHiddenFiles && file.isHidden())
continue;
FileTreeNode subFile = new FileTreeNode(file);
DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(subFile);
if (file.isDirectory())
{
if (!Constants.isOSX || navigateOSXApps || !file.getName().endsWith(".app"))
subNode.add(new DefaultMutableTreeNode("Fake"));
}
node.add(subNode);
}
}
}
/**
* Expands the tree to the <code>File</code> specified by the argument, and selects
* it as well. If the <code>currFile</code> does not exist or is null, calling this
* method will have no effect.
* #param currFile The file or directory to expand the tree to and select.
*/
public void setCurrentFile(File currFile) {
if (currFile == null || !currFile.exists())
return;
String path = currFile.getPath();
String [] pathParts = null;
if (Constants.isWindows)
pathParts = path.split("\\\\");
else
pathParts = path.split(File.separator);
if (Constants.isWindows)
{
int childCount = rootNode.getChildCount();
DefaultMutableTreeNode myComputer = null;
for (int i=0; i<childCount; i++)
{
FileTreeNode fileTreeNode =
(FileTreeNode)((DefaultMutableTreeNode)rootNode.getChildAt(i)).getUserObject();
if (fileTreeNode.file.getPath().equals(FileTreeNode.WINDOWS_MYCOMPUTER))
{
myComputer = (DefaultMutableTreeNode)rootNode.getChildAt(i);
TreePath treePath = new TreePath(myComputer.getPath());
expandPath(treePath);
break;
}
}
DefaultMutableTreeNode currNode = myComputer;
for (String part:pathParts)
{
childCount = currNode.getChildCount();
for (int i=0; i<childCount; i++)
{
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)currNode.getChildAt(i);
FileTreeNode fileTreeNode = (FileTreeNode)childNode.getUserObject();
String pathName = fileTreeNode.file.getName();
if (pathName.length() == 0)
pathName = fileTreeNode.file.getPath().substring(0, 2);
if (pathName.equals(part))
{
TreePath treePath = new TreePath(childNode.getPath());
expandPath(treePath);
selectionModel.setSelectionPath(treePath);
currNode = childNode;
break;
}
}
}
}
else
{
DefaultMutableTreeNode currNode = rootNode;
for (String part:pathParts)
{
int childCount = currNode.getChildCount();
for (int i=0; i<childCount; i++)
{
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)currNode.getChildAt(i);
FileTreeNode fileTreeNode = (FileTreeNode)childNode.getUserObject();
if (fileTreeNode.file.getName().equals(part))
{
TreePath treePath = new TreePath(childNode.getPath());
expandPath(treePath);
selectionModel.setSelectionPath(treePath);
currNode = childNode;
break;
}
}
}
}
}
/**
* Allow or disallow the user to delete files from the tree view.
* #param allowDelete <code>true</code> allows deleting of files/directories. <code>false</code> does
* not.
*/
public void setDeleteEnabled(boolean allowDelete) {
this.allowDelete = allowDelete;
}
/**
* Toggle the showing of files in the tree (as opposed to just directories)
* #param showFiles <code>true</code> shows files in the tree. <code>false</code> does not.
*/
public void setShowFiles(boolean showFiles) {
if (this.showFiles != showFiles)
{
this.showFiles = showFiles;
initRoot();
}
}
/**
* Allows or disallows the showing of hidden files and directories in the tree.
* #param showHiddenFiles <code>true</code> shows hidden files. <code>false</code> does not.
*/
public void setShowHiddenFiles(boolean showHiddenFiles) {
if (showHiddenFiles != this.showHiddenFiles)
{
this.showHiddenFiles = showHiddenFiles;
initRoot();
}
}
/**
* sets whether the user can navigate into OS X application bundles (.app). The
* default value is <code>false</code>
* #param navigateOSXApps if true, users will be able to navigate into OS X application bundles. set it
* to false to disallow navigating bundles.
*/
public void setNavigateOSXApps(boolean navigateOSXApps) {
this.navigateOSXApps = navigateOSXApps;
}
/**
* the root node of the <code>FileTree</code>
*/
protected DefaultMutableTreeNode rootNode;
/**
* the <code>TreeModel</code> for this object. The same value as the <code>JTree</code>
* treeModel member.
*/
protected DefaultTreeModel fileTreeModel;
/**
* just a filesystemview used to get icons for nodes in Windows
*/
protected FileSystemView fsv;
/**
* whether or not to show hidden files
*/
protected boolean showHiddenFiles;
/**
* whether or not to show files
*/
protected boolean showFiles;
/**
* whether to allow deleting of files
*/
protected boolean allowDelete;
/**
* allows/disallows navigating into OS X application bundles
*/
protected boolean navigateOSXApps;
/**
* A subclass of DefaultTreeCellRenderer that is responsible for rendering the
* nodes and their icons
*/
private class FileTreeCellRenderer extends DefaultTreeCellRenderer {
/**
* just a simple constructor
*/
public FileTreeCellRenderer() {
fileChooser = new JFileChooser();
}
/**
* returns a renderered node for the tree
* #param tree the tree to render the node for
* #param value the value of the node
* #param selected if the node is selected
* #param expanded if it's expanded
* #param leaf if its a leaf or not
* #param row the row number
* #param hasFocus if it has focus
* #return a renderered node for the tree
*/
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
if (userObject instanceof FileTreeNode)
{
FileTreeNode fileTreeNode = (FileTreeNode)userObject;
if (!Constants.isWindows)
{
try { setIcon(fileChooser.getIcon(fileTreeNode.file)); }
catch (Exception e) { e.printStackTrace(); }
}
else
{
try { setIcon(fsv.getSystemIcon(fileTreeNode.file)); }
catch (Exception e) { e.printStackTrace(); }
}
}
return this;
}
/**
* used to obtain icons for non-Windows OSes
*/
private JFileChooser fileChooser;
}
}
FileTreeListener.Java
package jtree;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
public class FileTreeListener extends MouseAdapter {
/**
* Creates a new instance of FileTreeListener
* #param fileTree the <code>FileTree</code> to listen for
*/
public FileTreeListener(FileTree fileTree) {
if (fileTree == null)
throw new IllegalArgumentException("Null argument not allowed");
this.fileTree = fileTree;
}
/**
* Listens for right-clicks on the tree.
* #param e contains information about the mouse click event
*/
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3)
rightClick(e.getX(), e.getY());
}
/**
*
* #param x the x coordinate of the mouse when it was pressed
* #param y the y coordinate of the mouse when it was pressed
*/
private void rightClick(int x, int y) {
TreePath treePath = fileTree.getPathForLocation(x, y);
if (treePath == null)
return;
if (!fileTree.isDeleteEnabled())
return;
JPopupMenu popup = new JPopupMenu();
popup.add(new DeleteFileAction(treePath));
popup.show(fileTree, x, y);
}
/**
* the <code>FileTree</code> to listen on
*/
private FileTree fileTree;
/**
* feature not implemented
*/
private class RenameAction extends AbstractAction {
public RenameAction(TreePath treePath) {
this.treePath = treePath;
putValue(Action.NAME, "Rename");
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
fileTreeNode = (FileTreeNode)treeNode.getUserObject();
if (!fileTreeNode.file.canWrite())
setEnabled(false);
}
#Override
public void actionPerformed(ActionEvent e) {
fileTree.startEditingAtPath(treePath);
}
private TreePath treePath;
private FileTreeNode fileTreeNode;
}
private class DeleteFileAction extends AbstractAction {
/**
* constructor for the action to delete a file or directory
* #param treePath the treepath of the node to act on
*/
public DeleteFileAction(TreePath treePath) {
this.treePath = treePath;
if (Constants.isOSX)
putValue(Action.NAME, "Move to Trash");
else
{
putValue(Action.NAME, "Delete");
putValue(Action.MNEMONIC_KEY, KeyEvent.VK_D);
}
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
fileTreeNode = (FileTreeNode)treeNode.getUserObject();
if (!fileTreeNode.file.canWrite())
setEnabled(false);
}
/**
* the action called when the user wants to delete a file or directory
* #param e information about the event that caused this method to be called
*/
public void actionPerformed(ActionEvent e) {
int choice = JOptionPane.showConfirmDialog(fileTree.getRootPane(),
"Are you sure you want to delete '" + fileTreeNode.file.getName()+"'?",
"Confirm delete",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (choice == 1)
return; // they selected no
boolean success = false;
if (fileTreeNode.file.isDirectory())
success = deleteDirectory(fileTreeNode.file);
else
success = fileTreeNode.file.delete();
if (success)
{
fileTree.getFileTreeModel().removeNodeFromParent(
(DefaultMutableTreeNode)treePath.getLastPathComponent());
}
}
/**
* deletes a directory and its content
* #param dir The directory to delete
* #return true on success, false otherwise
*/
private boolean deleteDirectory(File dir) {
if (dir == null || !dir.exists() || !dir.isDirectory())
return false;
boolean success = true;
File [] list = dir.listFiles();
for (File file:list)
{
if (file.isDirectory())
{
if (!deleteDirectory(file))
success = false;
}
else
{
if (!file.delete())
success = false;
}
}
if (!dir.delete()) // finally delete the actual directory
success = false;
return success;
}
/**
* The <code>TreePath</code> to the node that will be deleted.
*/
private TreePath treePath;
/**
* The <code>FileTreeNode</code> stored inside the <code>DefaultMutableTreeNode</code>'s
* user object
*/
private FileTreeNode fileTreeNode;
}
}
FileTreeNode.java
package jtree;
import java.io.File;
public class FileTreeNode {
/**
* Creates a new instance of FileTreeNode
* #param file The <code>File</code> that will be represented by this class.
*/
public FileTreeNode(File file) {
if (file == null)
throw new IllegalArgumentException("Null file not allowed");
this.file = file;
}
/**
* returns the representation of this <code>File</code> best suited for use in
* the <code>FileTree</code>.
* #return the representation of this <code>File</code> as a <code>String</code>
*/
public String toString() {
String name = file.getName();
if (!Constants.isWindows)
return name;
if (name.length() == 0)
return file.getPath();
if (Constants.isVista)
{
if (name.equals(WINDOWS_MYCOMPUTER)){
return "Computer";
}
if ("::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}".equals(name)){
return "My Network";
}
return name;
}
if (name.equals(WINDOWS_MYCOMPUTER)){
return "This PC";
}
if ("::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}".equals(name)){
return "Network";
}
if ("::{031E4825-7B94-4DC3-B131-E946B44C8DD5}".equals(name)){
return "Library-MS";
}
return name;
}
/**
* the object being represented
*/
public File file;
/**
* the hex string that represents 'My Computer' in Windows
*/
public static final String WINDOWS_MYCOMPUTER = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
/**
* the hex string that represents 'My Network Places' in Win2k and XP
*/
public static final String WINDOWS_MYNETWORKPLACES = "::{208D2C60-3AEA-1069-A2D7-08002B30309D}";
/**
* the hex string that represents 'Network' in Vista
*/
public static final String WINDOWSVISTA_NETWORK = "::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}";
}
Main.java
package jtree;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class Main {
/** Creates a new instance of Main */
public Main() {
}
public static void main(String[] args) {
JFrame jframe = new JFrame();
jframe.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container container = jframe.getContentPane();
container.setLayout(new BorderLayout());
FileTree fileTree = new FileTree();
fileTree.setShowHiddenFiles(false);
fileTree.setDeleteEnabled(true);
JScrollPane scrollPane = new JScrollPane(fileTree);
container.add(scrollPane, BorderLayout.CENTER);
jframe.setSize(300, 450);
jframe.setLocationByPlatform(true);
jframe.setVisible(true);
}
}
I've been trying to implement the Timer function for 3 or 5 seconds to update the JTree using the updateUI(), repaint(), revalidate() methods including the DefaultTreeModel. I'm still unable to keep the file JTree updated whenever a file is renamed, moved or deleted. Can someone please help me rectify this issue and guide me where to place the code snippet if written by you?
I have one JInternalframe in which there are many JRadioButton and JLabel. I want to Retrive Data from Database and display on this JRadioButton and JLabel
Since there can be any numbers of row in Database which will be fetched.
I had successfullly Fetched the data from database but not able to set it on JRadioButton and JLabel
void setData(String s1, String s2, String s3, int cnt) {
//Setting JRadioButton and JLabel Coding Part Here
}
In above function in Variable s1,s2,s3 data exist which i want to Display and in Variable cnt there is value of counter which increment on resultset.next()
Can anyone give me some tips on how to display data on JRadiobutton and JLabel
JRadioButton Variale name is something like BT1,BT2,BT3... So i tried
Since the data is dynamic the GUI should be dynamic as well.
Consider the following example:
import javax.swing.JLabel;
import javax.swing.JRadioButton;
public class GuiRow {
/** jRadioButton */
private JRadioButton jRadioButton;
/** studentIdLabel */
private JLabel studentIdLabel;
/** nameLabel */
private JLabel nameLabel;
/** courseLabel */
private JLabel courseLabel;
/**
* Constructs a new instance.
*
* #param studentIdtext
* #param nameText
* #param courseText
*/
public GuiRow(String studentIdtext, String nameText, String courseText) {
this.setjRadioButton(new JRadioButton(""));
// TODO configure radio button
this.setStudentIdLabel(new JLabel(studentIdtext));
this.setNameLabel(new JLabel(nameText));
this.setCourseLabel(new JLabel(nameText));
}
/**
* Get jRadioButton.
*
* #return jRadioButton
*/
public JRadioButton getjRadioButton() {
return this.jRadioButton;
}
/**
* Set jRadioButton.
*
* #param jRadioButton
*/
public void setjRadioButton(JRadioButton jRadioButton) {
this.jRadioButton = jRadioButton;
}
/**
* Get studentIdLabel.
*
* #return studentIdLabel
*/
public JLabel getStudentIdLabel() {
return this.studentIdLabel;
}
/**
* Set studentIdLabel.
*
* #param studentIdLabel
*/
public void setStudentIdLabel(JLabel studentIdLabel) {
this.studentIdLabel = studentIdLabel;
}
/**
* Get nameLabel.
*
* #return nameLabel
*/
public JLabel getNameLabel() {
return this.nameLabel;
}
/**
* Set nameLabel.
*
* #param nameLabel
*/
public void setNameLabel(JLabel nameLabel) {
this.nameLabel = nameLabel;
}
/**
* Get courseLabel.
*
* #return courseLabel
*/
public JLabel getCourseLabel() {
return this.courseLabel;
}
/**
* Set courseLabel.
*
* #param courseLabel
*/
public void setCourseLabel(JLabel courseLabel) {
this.courseLabel = courseLabel;
}
}
This is a simple Object to create and organize a row in your container (probably JPanel, right?)
When you are parsing the resultset you need to build a list of such objects and an add the data in there.
Here is a simple example on how to parse and display the data, which you can split into 2 or more methods and place them in the appropriate entities:
int resultsetSize = resultset.getFetchSize();
//init the list
List<GuiRow> guiRowList = new ArrayList<GuiRow>(resultsetSize);
while (resultset.next()) {
//get the column values
String id = resultset.getString("studentId");
String name = resultset.getString("studentName");
String course = resultset.getString("course");
//create the the entry
GuiRow guiRow = new GuiRow(id, name, course);
//add it to the list
guiRowList.add(guiRow);
}
//now that you have a nice list of rows add them one by one to the container
JPanel container = new JPanel(new GridLayout(resultsetSize, 4));
//further container configuration could be done here ...
for (GuiRow row : guiRowList) {
container.add(row.getjRadioButton());
container.add(row.getStudentIdLabel());
container.add(row.getNameLabel());
container.add(row.getCourseLabel());
}
I have a jtable with a custom editor and renderer applied to a column to turn the column's contents into a Button, however once i press on any of the buttons, the table loses focus, and the only interactive object in the panel is the button that was just clicked on, which can be clicked on repeatedly.
Here is the button renderer code:
public class ButtonRenderer implements TableCellRenderer {
private Border dsd_originalBorder;
private int in_mnemonic;
private Border dsd_focusBorder;
private JButton dsd_renderButton;
/**
* empty constructor
*/
public ButtonRenderer() {
dsd_renderButton = new JButton();
}
public Component getTableCellRendererComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, boolean bo_hasFocus, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (bo_isSelected) {
dsd_renderButton.setForeground(dsd_table.getSelectionForeground());
dsd_renderButton.setBackground(dsd_table.getSelectionBackground());
} else {
dsd_renderButton.setForeground(dsd_table.getForeground());
dsd_renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (bo_hasFocus) {
dsd_renderButton.setBorder(dsd_focusBorder);
} else {
dsd_renderButton.setBorder(dsd_originalBorder);
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (dsd_value == null) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon(null);
} else if (dsd_value instanceof Icon) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon((Icon) dsd_value);
} else {
dsd_renderButton.setText(dsd_value.toString());
dsd_renderButton.setIcon(null);
}
return dsd_renderButton;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic() {
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param p_in_mnemonic
* the mnemonic
*/
public void m_setMnemonic(int p_in_mnemonic) {
this.in_mnemonic = p_in_mnemonic;
dsd_renderButton.setMnemonic(p_in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsd_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsd_focusBorder) {
this.dsd_focusBorder = dsd_focusBorder;
}
Here is the button Editor code:
public class ButtonEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, MouseListener{
private JTable dsd_table;
private int in_mnemonic;
private Border dsd_originalBorder;
private Border dsd_focusBorder;
private JButton dsd_editButton;
private Object dsd_editorValue;
private boolean bo_isButtonColumnEditor;
/**
* Constructor
* #param dsdp_table the table to which the editor is going to be applied.
* #param action the action which is to be executed when the button is clicked
*/
public ButtonEditor(JTable dsdp_table) {
this.dsd_table = dsdp_table;
dsd_editButton = new JButton();
dsd_editButton.setFocusPainted( false );
dsd_editButton.addActionListener( this );
dsd_originalBorder = dsd_editButton.getBorder();
m_setFocusBorder( new LineBorder(Color.BLUE) );
dsdp_table.addMouseListener( this );
}
public Object getCellEditorValue() {
return dsd_editorValue;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic()
{
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param in_mnemonic the mnemonic
*/
public void m_setMnemonic(int in_mnemonic)
{
this.in_mnemonic = in_mnemonic;
dsd_editButton.setMnemonic(in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsdp_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsdp_focusBorder) {
this.dsd_focusBorder = dsdp_focusBorder;
dsd_editButton.setBorder(dsdp_focusBorder);
}
public void actionPerformed(ActionEvent arg0) {
int in_modelRow = dsd_table.convertRowIndexToModel( dsd_table.getEditingRow() );
fireEditingStopped();
// Here i start a custom thread to run in the background.
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (dsd_table.isEditing() && dsd_table.getCellEditor() == this)
bo_isButtonColumnEditor = true;
}
public void mouseReleased(MouseEvent arg0) {
if (bo_isButtonColumnEditor && dsd_table.isEditing())
dsd_table.getCellEditor().stopCellEditing();
bo_isButtonColumnEditor = false;
}
public void addCellEditorListener(CellEditorListener arg0) {
}
public void cancelCellEditing() {
}
public void removeCellEditorListener(CellEditorListener arg0) {
}
public boolean shouldSelectCell(EventObject arg0) {
return false;
}
public boolean stopCellEditing() {
return false;
}
public Component getTableCellEditorComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (dsd_value == null)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( null );
}
else if (dsd_value instanceof Icon)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( (Icon)dsd_value );
}
else
{
dsd_editButton.setText( dsd_value.toString() );
dsd_editButton.setIcon( null );
}
bo_isButtonColumnEditor = true;
this.dsd_editorValue = dsd_value;
dsd_editButton.setBorder(dsd_originalBorder);
return dsd_editButton;
}
public boolean isCellEditable(EventObject e){
return true;
}
public Border m_getoriginalBorder() {
return dsd_originalBorder;
}
public void m_set_originalBorder(Border dsd_originalBorder) {
this.dsd_originalBorder = dsd_originalBorder;
}
}
Here is how i am assigning the editor and renderer to the table
public static void m_setButtonColumnConfiguration(JTable table) {
ButtonEditor dsd_btn_edit = new ButtonEditor(table);
dsd_btn_edit.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/).setCellEditor(dsd_btn_edit);
ButtonRenderer dsd_btn_rend = new ButtonRenderer();
dsd_btn_rend.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/)
.setCellRenderer(dsd_btn_rend);
}
Button, however once i press on any of the buttons, the table loses focus
Yes, anytime you click on a component it gets focus, so this makes sense.
and the only interactive object in the panel is the button that was just clicked on,
Not sure I understand this statement. You can click on the table again and it will regain focus. You can then navigate around the table.
Maybe you are just suggesting the button renderer/editor isn't working the way you expect. We can't test this because you didn't post a SSCCE.
Anyway, check out Table Button Column for another example of a button renderer/editor that responds to mouse clicks or key board events.
I have to build a complex GUI in JAVA with Swing (for the moment I have near 80 classes).
The graphic partv of the application is split as follows: a first series of tabs (eg "Management", "Administration", "Configuration"), then a second level (for example, "User", "Group", "Game"). For now I'm two grade levels (one for each level of tabs). The next level is a JPanel that manages a business object (my whole GUI is built around my business model), at this level there are 2 type of JPanel: who manages simple objects (eg, "User", "Category" , "Game", "Level") and those which manages objects "composite primary key" (eg "User_Game" which represent the form of a double-entry table for each game level for all users).
My second level of tabs can contain multiple JPanel.
When my JPanel manages a single object is composed of a JTable and two buttons (Add and Remove) on which I put events, if not it is a simple JTable. When I have foreign keys (eg "Group" for "User" and "Category" to "Game" or "Level" to "User_Game") it is a JComboBox that takes its information directly from JTableModel. When it comes to managing a JTable object to "composite primary key" the columns and rows also directly dependent models (eg "Game" and "User" "User_Game").
Each has its own JTable model that deals with the persistence layer (Hibernate for information) and other TableModel.
To manage changes (such as adding, modifying or deleting a "User") I use the code below:
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
/*
* This class listens for changes made to the data in the table via the
* TableCellEditor. When editing is started, the value of the cell is saved
* When editing is stopped the new value is saved. When the oold and new
* values are different, then the provided Action is invoked.
*
* The source of the Action is a TableCellListener instance.
*/
public class TabCellListener implements PropertyChangeListener, Runnable
{
private JTable table;
private Action action;
private int row;
private int column;
private Object oldValue;
private Object newValue;
/**
* Create a TableCellListener.
*
* #param table the table to be monitored for data changes
* #param action the Action to invoke when cell data is changed
*/
public TabCellListener(JTable table, Action action)
{
this.table = table;
this.action = action;
this.table.addPropertyChangeListener( this );
this.table.getModel().addTableModelListener(new ModelListenerTableGui(this.table, this.action));
}
/**
* Create a TableCellListener with a copy of all the data relevant to
* the change of data for a given cell.
*
* #param row the row of the changed cell
* #param column the column of the changed cell
* #param oldValue the old data of the changed cell
* #param newValue the new data of the changed cell
*/
private CellListenerTableGui(JTable table, int row, int column, Object oldValue, Object newValue)
{
this.table = table;
this.row = row;
this.column = column;
this.oldValue = oldValue;
this.newValue = newValue;
}
/**
* Get the column that was last edited
*
* #return the column that was edited
*/
public int getColumn()
{
return column;
}
/**
* Get the new value in the cell
*
* #return the new value in the cell
*/
public Object getNewValue()
{
return newValue;
}
/**
* Get the old value of the cell
*
* #return the old value of the cell
*/
public Object getOldValue()
{
return oldValue;
}
/**
* Get the row that was last edited
*
* #return the row that was edited
*/
public int getRow()
{
return row;
}
/**
* Get the table of the cell that was changed
*
* #return the table of the cell that was changed
*/
public JTable getTable()
{
return table;
}
//
// Implement the PropertyChangeListener interface
//
#Override
public void propertyChange(PropertyChangeEvent e)
{
// A cell has started/stopped editing
if ("tableCellEditor".equals(e.getPropertyName()))
{
if (table.isEditing())
processEditingStarted();
else
processEditingStopped();
}
}
/*
* Save information of the cell about to be edited
*/
private void processEditingStarted()
{
// The invokeLater is necessary because the editing row and editing
// column of the table have not been set when the "tableCellEditor"
// PropertyChangeEvent is fired.
// This results in the "run" method being invoked
SwingUtilities.invokeLater( this );
}
/*
* See above.
*/
#Override
public void run()
{
row = table.convertRowIndexToModel( table.getEditingRow() );
column = table.convertColumnIndexToModel( table.getEditingColumn() );
oldValue = table.getModel().getValueAt(row, column);
newValue = null;
}
/*
* Update the Cell history when necessary
*/
private void processEditingStopped()
{
newValue = table.getModel().getValueAt(row, column);
// The data has changed, invoke the supplied Action
if ((newValue == null && oldValue != null) || (newValue != null && !newValue.equals(oldValue)))
{
// Make a copy of the data in case another cell starts editing
// while processing this change
CellListenerTableGui tcl = new CellListenerTableGui(
getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
ActionEvent event = new ActionEvent(
tcl,
ActionEvent.ACTION_PERFORMED,
"");
action.actionPerformed(event);
}
}
}
And the following action:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
public class UpdateTableListener<N> extends AbstractTableListener implements Action
{
protected boolean enabled;
public UpdateTableListener(AbstractTableGui<N> obs)
{
super(obs);
this.enabled = true;
}
#Override
public void actionPerformed(ActionEvent e)
{
if (null != e && e.getSource() instanceof CellListenerTableGui)
{
TabCellListener tcl = (TabCellListener)e.getSource();
this.obs.getModel().setValueAt(tcl.getNewValue(), tcl.getRow(), tcl.getColumn());
int sel = this.obs.getModel().getNextRequiredColumn(tcl.getRow());
if (sel == -1)
this.obs.getModel().save(tcl.getRow());
}
}
#Override
public void addPropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public Object getValue(String arg0)
{
return null;
}
#Override
public boolean isEnabled()
{
return this.enabled;
}
#Override
public void putValue(String arg0, Object arg1)
{
}
#Override
public void removePropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public void setEnabled(boolean arg0)
{
this.enabled = arg0;
}
}
This code works well, data are well persisted.
Then I add this code to refresh dependent components:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
public class ChangeTableListener implements Action
{
protected AbstractTableGui table;
public ChangeTableListener(AbstractTableGui table)
{
this.table = table;
}
#Override
public void actionPerformed(ActionEvent arg0)
{
this.table.getModel().fireTableDataChanged();
this.table.repaint();
}
#Override
public void addPropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public Object getValue(String arg0)
{
return null;
}
#Override
public boolean isEnabled()
{
return false;
}
#Override
public void putValue(String arg0, Object arg1)
{
}
#Override
public void removePropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public void setEnabled(boolean arg0)
{
}
}
My TableModel.fireTableDataChanged rebuild JTable content (calle super.fireTableDataChanged and fireTableStructureChanged), JTable.repaint reset the Renderers, and it works for Combobox (forein keys) and it update well title on double-entry tables, but it can't add or delete columns or rows on double-entry tables.
Moreover I see more high latency if there is the slightest change.
My question is simple: how do you manage inter-dependent components?
For your help,
In advance,
Thanks.
Edit :
Here an example of TableCellEditor.
import javax.swing.DefaultCellEditor;
import javax.swing.JTextField;
public class TextColumnEditor extends DefaultCellEditor
{
public TextColumnEditor()
{
super(new JTextField());
}
public boolean stopCellEditing()
{
Object v = this.getCellEditorValue();
if(v == null || v.toString().length() == 0)
{
this.fireEditingCanceled();
return false;
}
return super.stopCellEditing();
}
}
An example of TableModel :
import java.util.ArrayList;
public class GroupModelTable extends AbstractModelTable<Groups>
{
protected GroupsService service;
public GroupModelTable(AbstractTableGui<Groups> content)
{
super(content, new ArrayList<String>(), new ArrayList<Groups>());
this.headers.add("Group");
this.content.setModel(this);
this.service = new GroupsService();
this.setLines(this.service.search(new Groups()));
}
public Object getValueAt(int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case 0:
return this.lines.get(rowIndex).getTitle();
default:
return "";
}
}
public void setValueAt(Object aVal, int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case 0:
this.lines.get(rowIndex).setTitle(aVal.toString());
break;
default:
break;
}
}
#Override
public Groups getModel(int line, int column)
{
return null;
}
#Override
public Groups getModel(int line)
{
return this.lines.get(line);
}
public boolean isCellEditable(int row, int column)
{
return true;
}
#Override
public GroupModelTableGui newLine()
{
this.lines.add(new Groups());
return this;
}
#Override
public int getNextRequiredColumn(int row)
{
Groups g = this.getModel(row);
if (g != null && g.getTitle() != null && g.getTitle().length() > 0)
return -1;
return 0;
}
#Override
public void save(int row)
{
Groups g = this.getModel(row);
if (g != null)
{
try
{
if (g.getId() == null)
this.service.create(g);
else
this.service.update(g);
}
catch (Exception e)
{
}
}
}
#Override
public void removeRow(int row)
{
Groups g = this.getModel(row);
if (g != null)
{
try
{
if (g.getId() != null)
this.service.delete(g);
super.removeRow(row);
}
catch (Exception e)
{
}
}
}
}
An example of Table :
public class GroupTable extends AbstractTable<Groups>
{
public GroupTable()
{
new GroupModelTableGui(this);
new CellListenerTableGui(this.getContent(), new UpdateTableListenerGui<Groups>(this));
this.getContent().getColumnModel().getColumn(0).setCellEditor(new TextColumnEditorGui());
}
}
I hope it will help you to understand :/
Your TabCellListener is unfamiliar to me. Your TableCellEditor should not interact with the TableModel directly. It should implement getTableCellEditorComponent() and getCellEditorValue(), as shown in this example. When the editor concludes, the model will have the new value. Your TableModel should handle persistence. More than one view may listen to a single TableModel via TableModelListener.
Addendum: Your CellEditor, TextColumnEditor, probably shouldn't invoke fireEditingCanceled(). Pressing Escape should be sufficient to revert the edit, as shown in this example. You might also look at the related tutorial section and example.
I am making a hospital project in Java, I have made a JTable which is fetching Hospital Name and a Hospital image link i.e "Click to see more" from SQL database. My problem is that the data is successfully fetched from database to the table, but I can't click the link which is in the table cell.
How to make the link active?
Consider using JXTable (a class of SwingX): it supports a hyperlink renderer which can be configured to do any action, based on the cell value
JXTable table = new JXTable(myModel);
AbstractHyperlinkAction<Object> simpleAction = new AbstractHyperlinkAction<Object>(null) {
public void actionPerformed(ActionEvent e) {
// here goes what you want to do on activating the hyperlink
//LOG.info("hit: " + getTarget());
}
};
TableCellRenderer renderer = new DefaultTableRenderer(
new HyperlinkProvider(simpleAction));
table.getColumnExt(0).setEditable(false);
table.getColumnExt(0).setCellRenderer(renderer);
You can either make a TableCellEditor whose isCellEditable method could be used to activate on a single mouse click. Frankly, this just get messy.
Or, you could attach a MouseListener to the table directly and monitor for the mouseClicked event.
On the clicked event, you could need to getSelectedColumn and getSelectedRow to determine if they've clicked on the column you want and get the link value from the selected cell, using getValueAt
You'll need to take into consideration that the table may be sorted or the columns are no longer in the order you started them in (the user may have moved them).
Then you'll need convertColumnIndexToView and convertRowIndexToModel
Simple :D
I think that avoiding an external library is a better way to go as MadProgrammer suggests, but I guess swingx is still pure java. Anyway, I would use the mouseClicked listener and then open uri in the way described in this question How to add hyperlink in JLabel.
JTable table = new JTable();
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int row = table.getSelectedRow();
int col = table.getSelectedColumn();
//build your address / link
URI uri = new URI("http: your link here");
//see below
open(uri);
}
});
//Then elsewhere as from the McDowell answer
private static void open(URI uri) {
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().browse(uri);
} catch (IOException e) { /* TODO: error handling */ }
} else { /* TODO: error handling */ }
}
I solved adding a button inside the cells clickable, I followed this tutorial Table Button Column from Rob Camick:
String[] columnNames = {"First Name", "Last Name", ""};
Object[][] data =
{
{"Homer", "Simpson", "delete Homer"},
{"Madge", "Simpson", "delete Madge"},
{"Bart", "Simpson", "delete Bart"},
{"Lisa", "Simpson", "delete Lisa"},
};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
JTable table = new JTable( model );
Then:
Action delete = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JTable table = (JTable)e.getSource();
int modelRow = Integer.valueOf( e.getActionCommand() );
((DefaultTableModel)table.getModel()).removeRow(modelRow);
}
};
ButtonColumn buttonColumn = new ButtonColumn(table, delete, 2);
buttonColumn.setMnemonic(KeyEvent.VK_D);
Where ButtonColumn class is:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
/**
* The ButtonColumn class provides a renderer and an editor that looks like a
* JButton. The renderer and editor will then be used for a specified column
* in the table. The TableModel will contain the String to be displayed on
* the button.
*
* The button can be invoked by a mouse click or by pressing the space bar
* when the cell has focus. Optionally a mnemonic can be set to invoke the
* button. When the button is invoked the provided Action is invoked. The
* source of the Action will be the table. The action command will contain
* the model row number of the button that was clicked.
*
*/
public class ButtonColumn extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor, ActionListener, MouseListener
{
private JTable table;
private Action action;
private int mnemonic;
private Border originalBorder;
private Border focusBorder;
private JButton renderButton;
private JButton editButton;
private Object editorValue;
private boolean isButtonColumnEditor;
/**
* Create the ButtonColumn to be used as a renderer and editor. The
* renderer and editor will automatically be installed on the TableColumn
* of the specified column.
*
* #param table the table containing the button renderer/editor
* #param action the Action to be invoked when the button is invoked
* #param column the column to which the button renderer/editor is added
*/
public ButtonColumn(JTable table, Action action, int column)
{
this.table = table;
this.action = action;
renderButton = new JButton();
editButton = new JButton();
editButton.setFocusPainted( false );
editButton.addActionListener( this );
originalBorder = editButton.getBorder();
setFocusBorder( new LineBorder(Color.BLUE) );
TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer( this );
columnModel.getColumn(column).setCellEditor( this );
table.addMouseListener( this );
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border getFocusBorder()
{
return focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param focusBorder the foreground color
*/
public void setFocusBorder(Border focusBorder)
{
this.focusBorder = focusBorder;
editButton.setBorder( focusBorder );
}
public int getMnemonic()
{
return mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param mnemonic the mnemonic
*/
public void setMnemonic(int mnemonic)
{
this.mnemonic = mnemonic;
renderButton.setMnemonic(mnemonic);
editButton.setMnemonic(mnemonic);
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
if (value == null)
{
editButton.setText( "" );
editButton.setIcon( null );
}
else if (value instanceof Icon)
{
editButton.setText( "" );
editButton.setIcon( (Icon)value );
}
else
{
editButton.setText( value.toString() );
editButton.setIcon( null );
}
this.editorValue = value;
return editButton;
}
#Override
public Object getCellEditorValue()
{
return editorValue;
}
//
// Implement TableCellRenderer interface
//
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if (isSelected)
{
renderButton.setForeground(table.getSelectionForeground());
renderButton.setBackground(table.getSelectionBackground());
}
else
{
renderButton.setForeground(table.getForeground());
renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (hasFocus)
{
renderButton.setBorder( focusBorder );
}
else
{
renderButton.setBorder( originalBorder );
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (value == null)
{
renderButton.setText( "" );
renderButton.setIcon( null );
}
else if (value instanceof Icon)
{
renderButton.setText( "" );
renderButton.setIcon( (Icon)value );
}
else
{
renderButton.setText( value.toString() );
renderButton.setIcon( null );
}
return renderButton;
}
//
// Implement ActionListener interface
//
/*
* The button has been pressed. Stop editing and invoke the custom Action
*/
public void actionPerformed(ActionEvent e)
{
int row = table.convertRowIndexToModel( table.getEditingRow() );
fireEditingStopped();
// Invoke the Action
ActionEvent event = new ActionEvent(
table,
ActionEvent.ACTION_PERFORMED,
"" + row);
action.actionPerformed(event);
}
//
// Implement MouseListener interface
//
/*
* When the mouse is pressed the editor is invoked. If you then then drag
* the mouse to another cell before releasing it, the editor is still
* active. Make sure editing is stopped when the mouse is released.
*/
public void mousePressed(MouseEvent e)
{
if (table.isEditing()
&& table.getCellEditor() == this)
isButtonColumnEditor = true;
}
public void mouseReleased(MouseEvent e)
{
if (isButtonColumnEditor
&& table.isEditing())
table.getCellEditor().stopCellEditing();
isButtonColumnEditor = false;
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}