In fact i know how to implement using CTRL+Z (Undo) and CTRL+Y (Redo) with one JTextField. But i have hundreds of Text Components in my Swing application, so is there a way to apply this for all Text Components in my application, so when i click CTRL+Z in any Text Component it would undo the last entry in that Field ?
I have tried to implement it in EventQueue, but it did not work !
To make all your text components "undoable", you can simply create them using your own subclass like:
public class MyTextField extends JTextField {
public MyTextField() {
final UndoManager undoMgr = new UndoManager();
// Add listener for undoable events
getDocument().addUndoableEditListener(new UndoableEditListener() {
public void undoableEditHappened(UndoableEditEvent pEvt) {
undoMgr.addEdit(pEvt.getEdit());
}
});
// Add undo/redo actions
getActionMap().put(UNDO_ACTION, new AbstractAction(UNDO_ACTION) {
public void actionPerformed(ActionEvent pEvt) {
try {
if (undoMgr.canUndo()) {
undoMgr.undo();
}
} catch (CannotUndoException e) {
e.printStackTrace();
}
}
});
getActionMap().put(REDO_ACTION, new AbstractAction(REDO_ACTION) {
public void actionPerformed(ActionEvent pEvt) {
try {
if (undoMgr.canRedo()) {
undoMgr.redo();
}
} catch (CannotRedoException e) {
e.printStackTrace();
}
}
});
// Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y)
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK),
UNDO_ACTION);
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK),
REDO_ACTION);
}
}
Then instead of creating JTextFields, create MyTextFields. The limitation is that you may also want to create another subclass for JTextArea and so on for other JTextComponents. So it is also possible to use an utility method to add Undo/Redo features to any existing JTextCompoent:
public final static String UNDO_ACTION = "Undo";
public final static String REDO_ACTION = "Redo";
public static void makeUndoable(JTextComponent pTextComponent) {
final UndoManager undoMgr = new UndoManager();
// Add listener for undoable events
pTextComponent.getDocument().addUndoableEditListener(new UndoableEditListener() {
public void undoableEditHappened(UndoableEditEvent evt) {
undoMgr.addEdit(evt.getEdit());
}
});
// Add undo/redo actions
pTextComponent.getActionMap().put(UNDO_ACTION, new AbstractAction(UNDO_ACTION) {
public void actionPerformed(ActionEvent evt) {
try {
if (undoMgr.canUndo()) {
undoMgr.undo();
}
} catch (CannotUndoException e) {
e.printStackTrace();
}
}
});
pTextComponent.getActionMap().put(REDO_ACTION, new AbstractAction(REDO_ACTION) {
public void actionPerformed(ActionEvent evt) {
try {
if (undoMgr.canRedo()) {
undoMgr.redo();
}
} catch (CannotRedoException e) {
e.printStackTrace();
}
}
});
// Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y)
pTextComponent.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK), UNDO_ACTION);
pTextComponent.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), REDO_ACTION);
}
The final and absolutely transparent solution would be to implement your own UI class using Lnf features, but you may want to think twice before making all TextComponents undoable, for memory consumptions reasons for example if you often perform huge text modifications to these components...
you can gel list of built_in KeyBindings short_cuts implemented in the API's
notice you have to check or prepare your code for all accesible Look and Feels
you can get built_in KeyBindings short_cuts and replace that as you expecting, System ClipBoard is very often modified,
never tried but on this forum you can foud out rellevant info How to change, replace KeyBindings short_cuts
list of built_in KeyBindings short_cuts
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import javax.swing.filechooser.*;
public class KeyBindings implements ItemListener {
private static final String PACKAGE = "javax.swing.";
private static final String[] COLUMN_NAMES = {"Action", "When Focused", "When In Focused Window", "When Ancestor"};
private static String selectedItem;
private JComponent contentPane;
private JMenuBar menuBar;
private JTable table;
private JComboBox comboBox;
private Hashtable<String, DefaultTableModel> models;
/*
* Constructor
*/
public KeyBindings() {
models = new Hashtable<String, DefaultTableModel>();
contentPane = new JPanel(new BorderLayout());
contentPane.add(buildNorthComponent(), BorderLayout.NORTH);
contentPane.add(buildCenterComponent(), BorderLayout.CENTER);
resetComponents();
}
/*
* The content pane should be added to a high level container
*/
public JComponent getContentPane() {
return contentPane;
}
/*
* A menu can also be added which provides the ability to switch
* between different LAF's.
*/
public JMenuBar getMenuBar() {
if (menuBar == null) {
menuBar = createMenuBar();
}
return menuBar;
}
/*
* This panel is added to the North of the content pane
*/
private JComponent buildNorthComponent() {
comboBox = new JComboBox();
JLabel label = new JLabel("Select Component:");
label.setDisplayedMnemonic('S');
label.setLabelFor(comboBox);
JPanel panel = new JPanel();
panel.setBorder(new EmptyBorder(15, 0, 15, 0));
panel.add(label);
panel.add(comboBox);
return panel;
}
/*
* Check the key name to see if it is the UI property
*/
private String checkForUIKey(String key) {
if (key.endsWith("UI") && key.indexOf('.') == -1) {
String componentName = key.substring(0, key.length() - 2);// Ignore these components
if (componentName.equals("PopupMenuSeparator") || componentName.equals("ToolBarSeparator") || componentName.equals("DesktopIcon")) {
return null;
} else {
return componentName;
}
}
return null;
}
/*
** Build the emtpy table to be added in the Center
*/
private JComponent buildCenterComponent() {
DefaultTableModel model = new DefaultTableModel(COLUMN_NAMES, 0);
table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
table.setAutoCreateColumnsFromModel(false);
table.getColumnModel().getColumn(0).setPreferredWidth(200);
table.getColumnModel().getColumn(1).setPreferredWidth(200);
table.getColumnModel().getColumn(2).setPreferredWidth(200);
table.getColumnModel().getColumn(3).setPreferredWidth(200);
Dimension d = table.getPreferredSize();
d.height = 350;
table.setPreferredScrollableViewportSize(d);
table.getTableHeader().setFocusable(true);
return new JScrollPane(table);
}
/*
* When the LAF is changed we need to reset all the items
*/
private void resetComponents() {
models.clear();
((DefaultTableModel) table.getModel()).setRowCount(0);
Vector<String> comboBoxItems = new Vector<String>(50);// buildItemsMap();
UIDefaults defaults = UIManager.getLookAndFeelDefaults();
for (Object key : defaults.keySet()) { // All Swing components will have a UI property.
String componentName = checkForUIKey(key.toString());
if (componentName != null) {
comboBoxItems.add(componentName);
}
}
Collections.sort(comboBoxItems);
comboBox.removeItemListener(this);
comboBox.setModel(new DefaultComboBoxModel(comboBoxItems));
comboBox.setSelectedIndex(-1);
comboBox.addItemListener(this);
comboBox.requestFocusInWindow();
if (selectedItem != null) {
comboBox.setSelectedItem(selectedItem);
}
}
/**
* Create menu bar
*/
private JMenuBar createMenuBar() {
JMenuBar menuBar1 = new JMenuBar();
menuBar1.add(createFileMenu());
menuBar1.add(createLAFMenu());
return menuBar1;
}
/**
* Create menu items for the Application menu
*/
private JMenu createFileMenu() {
JMenu menu = new JMenu("Application");
menu.setMnemonic('A');
menu.addSeparator();
menu.add(new ExitAction());
return menu;
}
/**
* Create menu items for the Look & Feel menu
*/
private JMenu createLAFMenu() {
ButtonGroup bg = new ButtonGroup();
JMenu menu = new JMenu("Look & Feel");
menu.setMnemonic('L');
String lafId = UIManager.getLookAndFeel().getID();
UIManager.LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels();
for (int i = 0; i < lafInfo.length; i++) {
String laf = lafInfo[i].getClassName();
String name = lafInfo[i].getName();
Action action = new ChangeLookAndFeelAction(laf, name);
JRadioButtonMenuItem mi = new JRadioButtonMenuItem(action);
menu.add(mi);
bg.add(mi);
if (name.equals(lafId)) {
mi.setSelected(true);
}
}
return menu;
}
/*
* Implement the ItemListener interface
*/
#Override
public void itemStateChanged(ItemEvent e) {
String componentName = (String) e.getItem();
changeTableModel(getClassName(componentName));
selectedItem = componentName;
}
/*
* Use the component name to build the class name
*/
private String getClassName(String componentName) {
if (componentName.equals("TableHeader")) {// The table header is in a child package
return PACKAGE + "table.JTableHeader";
} else {
return PACKAGE + "J" + componentName;
}
}
/*
* Change the TabelModel in the table for the selected component
*/
private void changeTableModel(String className) {
DefaultTableModel model = models.get(className); // Check if we have already built the table model for this component
if (model != null) {
table.setModel(model);
return;
}
model = new DefaultTableModel(COLUMN_NAMES, 0); // Create an empty table to start with
table.setModel(model);
models.put(className, model);
JComponent component = null; // Create an instance of the component so we can get the default Action map and Input maps
try {
if (className.endsWith("JFileChooser")) {// Hack so I don't have to sign the jar file for usage in Java Webstart
component = new JFileChooser(new DummyFileSystemView());
} else {
Object o = Class.forName(className).newInstance();
component = (JComponent) o;
}
} catch (Exception e) {
Object[] row = {e.toString(), "", "", ""};
model.addRow(row);
return;
}
ActionMap actionMap = component.getActionMap(); // Not all components have Actions defined
Object[] keys = actionMap.allKeys();
if (keys == null) {
Object[] row = {"No actions found", "", "", ""};
model.addRow(row);
return;
}
// In some ActionMaps a key of type Object is found (I have no idea why)
// which causes a ClassCastExcption when sorting so we will ignore it
// by converting that entry to the empty string
for (int i = 0; i < keys.length; i++) {
Object key = keys[i];
if (key instanceof String) {
continue;
} else {
keys[i] = "";
}
}
Arrays.sort(keys);
for (int i = 0; i < keys.length; i++) { // Create a new row in the model for every Action found
Object key = keys[i];
if (key != "") {
Object[] row = {key, "", "", ""};
model.addRow(row);
}
}
// Now check each InputMap to see if a KeyStroke is bound the the Action
updateModelForInputMap(model, 1, component.getInputMap(JComponent.WHEN_FOCUSED));
updateModelForInputMap(model, 2, component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW));
updateModelForInputMap(model, 3, component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
}
/*
* The model is potentially update for each of the 3 different InputMaps
*/
private void updateModelForInputMap(TableModel model, int column, InputMap inputMap) {
if (inputMap == null) {
return;
}
KeyStroke[] keys = inputMap.allKeys();
if (keys == null) {
return;
}
// The InputMap is keyed by KeyStroke, however we want to be able to
// access the action names that are bound to a KeyStroke so we will create
// a Hashtble that is keyed by action name.
// Note that multiple KeyStrokes can be bound to the same action name.
Hashtable<Object, String> actions = new Hashtable<Object, String>(keys.length);
for (int i = 0; i < keys.length; i++) {
KeyStroke key = keys[i];
Object actionName = inputMap.get(key);
Object value = actions.get(actionName);
if (value == null) {
actions.put(actionName, key.toString().replace("pressed ", ""));
} else {
actions.put(actionName, value + ", " + key.toString().replace("pressed ", ""));
}
}
for (int i = 0; i < model.getRowCount(); i++) {
// Now we can update the model for those actions that have KeyStrokes mapped to them
String o = actions.get(model.getValueAt(i, 0));
if (o != null) {
model.setValueAt(o.toString(), i, column);
}
}
}
/*
* Change the LAF and recreate the UIManagerDefaults so that the properties
* of the new LAF are correctly displayed.
*/
private class ChangeLookAndFeelAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private String laf;
protected ChangeLookAndFeelAction(String laf, String name) {
this.laf = laf;
putValue(Action.NAME, name);
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
}
#Override
public void actionPerformed(ActionEvent e) {
try {
JMenuItem mi = (JMenuItem) e.getSource();
JPopupMenu popup = (JPopupMenu) mi.getParent();
JRootPane rootPane = SwingUtilities.getRootPane(popup.getInvoker());
Component c = rootPane.getContentPane().getComponent(0);
rootPane.getContentPane().remove(c);
UIManager.setLookAndFeel(laf);
KeyBindings bindings = new KeyBindings();
rootPane.getContentPane().add(bindings.getContentPane());
SwingUtilities.updateComponentTreeUI(rootPane);
rootPane.requestFocusInWindow();
} catch (Exception ex) {
System.out.println("Failed loading L&F: " + laf);
System.out.println(ex);
}
}
}
private class ExitAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public ExitAction() {
putValue(Action.NAME, "Exit");
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_X));
}
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
private class DummyFileSystemView extends FileSystemView {
#Override
public File createNewFolder(File containingDir) {
return null;
}
#Override
public File getDefaultDirectory() {
return null;
}
#Override
public File getHomeDirectory() {
return null;
}
}
private static void createAndShowGUI() {
KeyBindings application = new KeyBindings();
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Key Bindings");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(application.getMenuBar());
frame.getContentPane().add(application.getContentPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
//UIManager.put("swing.boldMetal", Boolean.FALSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
If you want to run "global events" on your application regardless of where you your current focus is, you'll need to work with the KeyboardFocusManager :
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
kfm.addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
// do your stuff here
return done;
}
});
Hope this helps.
Related
I have this Inventory System app and should be working on other windows now but unable to cause I'm stuck with this fairly simple problem.
The buttons Add Item and Stock Transfer are displaying blank windows when it should be displaying another class I made.
public class InventoryMainUI extends javax.swing.JPanel implements ActionListener {
private boolean DEBUG = false;
private JTable table;
private JTextField searchitem;
private TableRowSorter<TableModel> sorter;
private JTextField jTextField1;
public JButton STbutton;
protected JButton b1, b2, b3;
public InventoryMainUI() {
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
//Create a table with a sorter.
TableModel model = new TableModel();
sorter = new TableRowSorter<TableModel>(model);
table = new JTable(model);
table.setRowSorter(sorter);
table.setPreferredScrollableViewportSize(new Dimension(500, 250));
table.setFillsViewportHeight(true);
//For the purposes of this example, better to have a single
//selection.
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//When selection changes, provide user with row numbers for
//both view and model.
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
add(scrollPane);
//Create a separate form for searchitem and jTextField1
JPanel mainpanel = new JPanel();
JLabel srch = new JLabel("Search Item (case sensitive):");
mainpanel.add(srch);
searchitem = new JTextField();
searchitem.setColumns(40);
//Whenever searchitem changes, invoke newFilter.
searchitem.getDocument().addDocumentListener(
new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
newFilter();
}
public void insertUpdate(DocumentEvent e) {
newFilter();
}
public void removeUpdate(DocumentEvent e) {
newFilter();
}
});
srch.setLabelFor(searchitem);
mainpanel.add(searchitem);
jTextField1 = new JTextField();
mainpanel.add(jTextField1);
jTextField1.setVisible(false);
JButton aditem = new JButton("Add Item");
mainpanel.add(aditem);
aditem.addActionListener(this);
JButton STransfer = new JButton("Stock Transfer");
mainpanel.add(STransfer);
STransfer.addActionListener(this);
add(mainpanel);
}
public void actionPerformed(ActionEvent e){
AddItemUI aditemui = new AddItemUI();
aditemui.setVisible(true);
aditemui.setSize(500,400);
}
public void ActionPerformed(java.awt.event.ActionEvent evt)
{
new StockTransfer().setVisible(true);
//StockTransfer().setSize(550,650);
}
/**
* Update the row filter regular expression from the expression in
* the text box.
*/
private void newFilter() {
RowFilter<TableModel, Object> rf = null;
//If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter(searchitem.getText(), 0);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
class TableModel extends AbstractTableModel {
//,"Perishable","Quantity"
private String[] columnName = {"Item","PIN","Delivered","Obtained","Expiry"};
private Object[][] data = {
{"AMD Ryzen 3 1300x", "2027",
"7/12/17", new Integer(5), new Boolean(false)},
{"Intel i7-6700K 8M Skylake", "4531",
"7/12/17", new Integer(3), new Boolean(true)},
{"Razer Blackwidow Chroma v2", "6742",
"7/18/17", new Integer(2), new Boolean(false)},
{" Nvidia GTX Titan Black", "9441",
"7/13/17", new Integer(20), new Boolean(true)},
{"CORSAIR Hydro Series H60", "1134",
"7/13/17", new Integer(10), new Boolean(false)}
};
public int getColumnCount() {
return columnName.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int clmns) {
return columnName[clmns];
}
public Object getValueAt(int rows, int clmns) {
return data[rows][clmns];
}
/*
* 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 < 2) {
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) {
if (DEBUG) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value
+ " (an instance of "
+ value.getClass() + ")");
}
data[row][col] = value;
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("--------------------------");
}
}
/**
* 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("Inventory");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
InventoryMainUI newContentPane = new InventoryMainUI();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.setSize(800,500);
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(InventoryMainUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(InventoryMainUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(InventoryMainUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(InventoryMainUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I've been trying to find solutions online, please help, its due soon, any help would be appreciated.
Please post the code of your AddItemUI and StockItem classes.
That is probably the only way we can help you.
Do these two classes extend JFrame and are the frames filled with your desired components when calling the constructor?
I'm creating a simulator using Java Swing. I used JComboBox to display units of utilities such as "KW, KL, KM" etc to measure Power, Water and distance. It's simple to add bunch of items to a JComboBox. User select a unit and the JFrame will save the selection when a "save" button is clicked.
JComboBox comboBox = new JComboBox();
for(ValueUnits u: ValueUnits.values()){
comboBox.addItem(u.returnUnits());
}
comboBox.setSelectedIndex(-1);
unitColumn.setCellEditor(new DefaultCellEditor(comboBox));
Now I want to create an multi-layer JComboBox (perhaps JMenu?). The function of such should behave as a multi-layer JMenu. When the JComboBox is clicked, it will show the first layer - category such as "Electricity, Water, Distance...", Then when mouse hover over Electricity, a list of Electricity units such as "KW, MW, W ..." will show. These collections are fetched from Enumerations. I wonder what's the most correct way to create such component.
Thank you so much world!
Maybe use 2 combo boxes? That is you select a value in the first and the units are displayed in the second:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class ComboBoxTwo extends JPanel implements ActionListener
{
private JComboBox<String> mainComboBox;
private JComboBox<String> subComboBox;
private Hashtable<String, String[]> subItems = new Hashtable<String, String[]>();
public ComboBoxTwo()
{
String[] items = { "Select Item", "Color", "Shape", "Fruit" };
mainComboBox = new JComboBox<String>( items );
mainComboBox.addActionListener( this );
// prevent action events from being fired when the up/down arrow keys are used
mainComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
add( mainComboBox );
// Create sub combo box with multiple models
subComboBox = new JComboBox<String>();
subComboBox.setPrototypeDisplayValue("XXXXXXXXXX"); // JDK1.4
add( subComboBox );
String[] subItems1 = { "Select Color", "Red", "Blue", "Green" };
subItems.put(items[1], subItems1);
String[] subItems2 = { "Select Shape", "Circle", "Square", "Triangle" };
subItems.put(items[2], subItems2);
String[] subItems3 = { "Select Fruit", "Apple", "Orange", "Banana" };
subItems.put(items[3], subItems3);
}
public void actionPerformed(ActionEvent e)
{
String item = (String)mainComboBox.getSelectedItem();
Object o = subItems.get( item );
if (o == null)
{
subComboBox.setModel( new DefaultComboBoxModel() );
}
else
{
subComboBox.setModel( new DefaultComboBoxModel( (String[])o ) );
}
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ComboBoxTwo() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Use It...
public class ComboLayer extends javax.swing.JPanel {
String Category1 = null;
String Category2 = null;
String Category3 = null;
Hashtable<String, String> hsItems;
public ComboLayer() {
this.hsItems = new Hashtable<>();
hsItems.put("Main", "Power,Water,Distance");
hsItems.put("Power", "KW,MW,W");
hsItems.put("Water", "ML,L,KL");
hsItems.put("Distance", "CM,M,KM");
initComponents();
String[] item = hsItems.get("Main").split(",");
for (String i : item) {
cmbItem.addItem(i);
}
cmbItem.addItem("Back");
cmbItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String selectText = null;
selectText = (String) cmbItem.getSelectedItem();
Enumeration enmKeys = hsItems.keys();
while (enmKeys.hasMoreElements()) {
String sKey = (String) enmKeys.nextElement();
if (selectText.equalsIgnoreCase("back")) {
if (hsItems.get(sKey).contains(cmbItem.getItemAt(0).toString())) {
Enumeration enumkeysBack = hsItems.keys();
while (enumkeysBack.hasMoreElements()) {
String sKeyBack = (String) enumkeysBack.nextElement();
if (hsItems.get(sKeyBack).contains(sKey)) {
String[] item = hsItems.get(sKeyBack).split(",");
cmbItem.removeAllItems();
for (String i : item) {
cmbItem.addItem(i);
}
if (!sKeyBack.equalsIgnoreCase("Main")) {
cmbItem.addItem("Back");
}
break;
}
}
}
} else if (sKey.contains(selectText)) {
String[] item = hsItems.get(sKey).split(",");
cmbItem.removeAllItems();
for (String i : item) {
cmbItem.addItem(i);
}
if (!sKey.equalsIgnoreCase("Main")) {
cmbItem.addItem("Back");
}
break;
}
}
}
});
}
public static void main(String... arg) {
ComboLayer cmbLyr = new ComboLayer();
JDialog dg = new JDialog();
dg.add(cmbLyr);
dg.setVisible(true);
}
Here's a combo with the effect of a custom popup of JMenu's. The actual popup is hidden, and a second popup is displayed when appropriate.
Upon selection of a JMenuItem, the combo is populated with just that one item, displayed in breadcrumb format.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
public class JMenuComboBoxDemo implements Runnable
{
private Map<String, String[]> menuData;
private JComboBox<String> combo;
private AbstractButton arrowButton;
private JPopupMenu popupMenu;
private List<String> flattenedData;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new JMenuComboBoxDemo());
}
public JMenuComboBoxDemo()
{
menuData = new HashMap<String, String[]>();
menuData.put("Colors", new String[]{"Black", "Blue"});
menuData.put("Flavors", new String[]{"Lemon", "Lime"});
popupMenu = new JPopupMenu();
popupMenu.setBorder(new MatteBorder(1, 1, 1, 1, Color.DARK_GRAY));
List<String> categories = new ArrayList<String>(menuData.keySet());
Collections.sort(categories);
// copy of the menuData, flattened into a List
flattenedData = new ArrayList<String>();
for (String category : categories)
{
JMenu menu = new JMenu(category);
for (String itemName : menuData.get(category))
{
menu.add(createMenuItem(itemName));
flattenedData.add(category + " > " + itemName);
}
popupMenu.add(menu);
}
combo = new JComboBox<String>();
combo.setPrototypeDisplayValue("12345678901234567890");
combo.setUI(new EmptyComboBoxUI());
for (Component comp : combo.getComponents())
{
if (comp instanceof AbstractButton)
{
arrowButton = (AbstractButton) comp;
}
}
arrowButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setPopupVisible(! popupMenu.isVisible());
}
});
combo.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
setPopupVisible(! popupMenu.isVisible());
}
});
}
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = frame.getContentPane();
c.setLayout(new FlowLayout());
c.add(new JLabel("Options"));
c.add(combo);
frame.setSize(300, 200);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
/*
* Toggle the visibility of the custom popup.
*/
private void setPopupVisible(boolean visible)
{
if (visible)
{
popupMenu.show(combo, 0, combo.getSize().height);
}
else
{
popupMenu.setVisible(false);
}
}
/*
* Create a JMenuItem whose listener will display
* the item in the combo.
*/
private JMenuItem createMenuItem(final String name)
{
JMenuItem item = new JMenuItem(name);
item.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setComboSelection(name);
}
});
return item;
}
/*
* Search for the given name in the flattened list of menu items.
* If found, add that item to the combo and select it.
*/
private void setComboSelection(String name)
{
Vector<String> items = new Vector<String>();
for (String item : flattenedData)
{
/*
* We're cheating here: if two items have the same name
* (Fruit->Orange and Color->Orange, for example)
* the wrong one may get selected. This should be more sophisticated
* (left as an exercise to the reader)
*/
if (item.endsWith(name))
{
items.add(item);
break;
}
}
combo.setModel(new DefaultComboBoxModel<String>(items));
if (items.size() == 1)
{
combo.setSelectedIndex(0);
}
}
/*
* Prevents the default popup from being displayed
*/
class EmptyComboBoxUI extends MetalComboBoxUI
{
#Override
protected ComboPopup createPopup()
{
BasicComboPopup thePopup = (BasicComboPopup) super.createPopup();
thePopup.setPreferredSize(new Dimension(0,0)); // oh, the horror!
return thePopup;
}
}
}
Recently I solved a "mysterious" IOException that I got while DnD items from a JList to the JTable objects. Apparently objects that I transfer must be serializable. Is this "a must", or there is a way to avoid serialization?
One thing I must note - the type I am transferring is in a different package.
You can write a custom TransferHandler. For example I believe a TranferHandler for a JTabel will export a String that is comma delimited and then the import will parse the string to add each token as a different column.
So in your case you could export you data the same way. Then on you import you would need to be able to recreate your custom Object using the parsed tokens.
Take a look at the Swing tutorial on Drag and Drop and Data Transfer for more information and examples.
Or maybe easier if the DnD is only between your Java application than you can pass the actual reference to the object. Here is an example of my attempt to do something like this by dragging a Swing component between panels:
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.text.*;
import java.io.*;
public class DragComponent extends JPanel
{
// public final static DataFlavor COMPONENT_FLAVOR = new DataFlavor(Component[].class, "Component Array");
public static DataFlavor COMPONENT_FLAVOR;
public DragComponent()
{
try
{
COMPONENT_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" + Component[].class.getName() + "\"");
}
catch(Exception e)
{
System.out.println(e);
}
setLayout(null);
setTransferHandler( new PanelHandler() );
MouseListener listener = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
JComponent c = (JComponent) e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.MOVE);
}
};
TransferHandler handler = new ComponentHandler();
for (int i = 0; i < 5; i++)
{
JLabel label = new JLabel("Label " + i);
label.setSize( label.getPreferredSize() );
label.setLocation(30 * (i+1), 30 * (i+1));
label.addMouseListener( listener );
label.setTransferHandler( handler );
add( label );
}
}
private static void createAndShowUI()
{
DragComponent north = new DragComponent();
north.setBackground(Color.RED);
north.setPreferredSize( new Dimension(200, 200) );
DragComponent south = new DragComponent();
south.setBackground(Color.YELLOW);
south.setPreferredSize( new Dimension(200, 200) );
JFrame frame = new JFrame("DragComponent");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(north, BorderLayout.NORTH);
frame.add(south, BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
class ComponentHandler extends TransferHandler
{
#Override
public int getSourceActions(JComponent c)
{
return MOVE;
}
#Override
public Transferable createTransferable(final JComponent c)
{
return new Transferable()
{
#Override
public Object getTransferData(DataFlavor flavor)
{
Component[] components = new Component[1];
components[0] = c;
return components;
}
#Override
public DataFlavor[] getTransferDataFlavors()
{
DataFlavor[] flavors = new DataFlavor[1];
flavors[0] = DragComponent.COMPONENT_FLAVOR;
return flavors;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return flavor.equals(DragComponent.COMPONENT_FLAVOR);
}
};
}
#Override
public void exportDone(JComponent c, Transferable t, int action)
{
System.out.println(c.getBounds());
}
}
class PanelHandler extends TransferHandler
{
#Override
public boolean canImport(TransferSupport support)
{
if (!support.isDrop())
{
return false;
}
boolean canImport = support.isDataFlavorSupported(DragComponent.COMPONENT_FLAVOR);
return canImport;
}
#Override
public boolean importData(TransferSupport support)
{
if (!canImport(support))
{
return false;
}
Component[] components;
try
{
components = (Component[])support.getTransferable().getTransferData(DragComponent.COMPONENT_FLAVOR);
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
Component component = components[0];
Container container = (Container)support.getComponent();
container.add(component);
// container.revalidate();
// container.repaint();
container.getParent().revalidate();
container.getParent().repaint();
JLabel label = (JLabel)component;
DropLocation location = support.getDropLocation();
System.out.println(label.getText() + " + " + location.getDropPoint());
label.setLocation( location.getDropPoint() );
return true;
}
}
I'm trying to create a form with a JList and a button
What I want to do, is select an item from the JList, and then press the button to perform an action dependant on the selection. However, as soon as the button is clicked, the JList loses focus, and the selection disappears, making the button unable to determine what element was selected in the JList.
Is there a solution to this?
Thanks in advance :)
I can't see any issue with example from Oracle JList tutorial
have look at ListSelectionListener
maybe carefully with SelectionModes
can you please to describe whats is your goal
code example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class ListDemo extends JPanel implements ListSelectionListener {
private JList list;
private DefaultListModel listModel;
private static final String hireString = "Hire";
private static final String fireString = "Fire";
private JButton fireButton;
private JTextField employeeName;
public ListDemo() {
super(new BorderLayout());
listModel = new DefaultListModel();
listModel.addElement("Jane Doe");
listModel.addElement("John Smith");
listModel.addElement("Kathy Green");
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.addListSelectionListener(this);
list.setVisibleRowCount(5);
JScrollPane listScrollPane = new JScrollPane(list);
JButton hireButton = new JButton(hireString);
HireListener hireListener = new HireListener(hireButton);
hireButton.setActionCommand(hireString);
hireButton.addActionListener(hireListener);
hireButton.setEnabled(false);
fireButton = new JButton(fireString);
fireButton.setActionCommand(fireString);
fireButton.addActionListener(new FireListener());
employeeName = new JTextField(10);
employeeName.addActionListener(hireListener);
employeeName.getDocument().addDocumentListener(hireListener);
String name = listModel.getElementAt(list.getSelectedIndex()).toString();
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
buttonPane.add(fireButton);
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(new JSeparator(SwingConstants.VERTICAL));
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(employeeName);
buttonPane.add(hireButton);
buttonPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(listScrollPane, BorderLayout.CENTER);
add(buttonPane, BorderLayout.PAGE_END);
}
class FireListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
int index = list.getSelectedIndex();
listModel.remove(index);
int size = listModel.getSize();
if (size == 0) { //Nobody's left, disable firing.
fireButton.setEnabled(false);
} else { //Select an index.
if (index == listModel.getSize()) {
index--;
}
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
}
}
class HireListener implements ActionListener, DocumentListener {
private boolean alreadyEnabled = false;
private JButton button;
public HireListener(JButton button) {
this.button = button;
}
public void actionPerformed(ActionEvent e) {
String name = employeeName.getText();
if (name.equals("") || alreadyInList(name)) {
Toolkit.getDefaultToolkit().beep();
employeeName.requestFocusInWindow();
employeeName.selectAll();
return;
}
int index = list.getSelectedIndex(); //get selected index
if (index == -1) { //no selection, so insert at beginning
index = 0;
} else { //add after the selected item
index++;
}
listModel.insertElementAt(employeeName.getText(), index);
//listModel.addElement(employeeName.getText());
employeeName.requestFocusInWindow();
employeeName.setText("");
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
protected boolean alreadyInList(String name) {
return listModel.contains(name);
}
public void insertUpdate(DocumentEvent e) {
enableButton();
}
public void removeUpdate(DocumentEvent e) {
handleEmptyTextField(e);
}
public void changedUpdate(DocumentEvent e) {
if (!handleEmptyTextField(e)) {
enableButton();
}
}
private void enableButton() {
if (!alreadyEnabled) {
button.setEnabled(true);
}
}
private boolean handleEmptyTextField(DocumentEvent e) {
if (e.getDocument().getLength() <= 0) {
button.setEnabled(false);
alreadyEnabled = false;
return true;
}
return false;
}
}
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (list.getSelectedIndex() == -1) {
fireButton.setEnabled(false);
} else {
fireButton.setEnabled(true);
}
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("ListDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ListDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Just have a field that you update every time a selection is made. You could do this by adding a listener on your JList.
private class selectListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
currentSelected = list.getSelectedIndex();
}
}
}
Then use that field when your button is pressed.
How can I register a custom data flavour, such that when I call
TransferHandler.TransferSupport.isDataFlavorSupported()
it returns true?
The flavour is initialised like so
private final DataFlavor localObjectFlavor = new ActivationDataFlavor(DataManagerTreeNode.class, DataFlavor.javaJVMLocalObjectMimeType, "DataManagerTreeNode flavour");
Many thanks
I saw only once times correct code for JTable and DnD, offical code by Oracle (former Sun)
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;
public class FillViewportHeightDemo extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private DefaultListModel model = new DefaultListModel();
private int count = 0;
private JTable table;
private JCheckBoxMenuItem fillBox;
private DefaultTableModel tableModel;
private static String getNextString(int count) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < 5; i++) {
buf.append(String.valueOf(count));
buf.append(",");
}
buf.deleteCharAt(buf.length() - 1); // remove last newline
return buf.toString();
}
private static DefaultTableModel getDefaultTableModel() {
String[] cols = {"Foo", "Toto", "Kala", "Pippo", "Boing"};
return new DefaultTableModel(null, cols);
}
public FillViewportHeightDemo() {
super("Empty Table DnD Demo");
tableModel = getDefaultTableModel();
table = new JTable(tableModel);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setDropMode(DropMode.INSERT_ROWS);
table.setTransferHandler(new TransferHandler() {
private static final long serialVersionUID = 1L;
#Override
public boolean canImport(TransferSupport support) {
if (!support.isDrop()) { // for the demo, we'll only support drops (not clipboard paste)
return false;
}
if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) { // we only import Strings
return false;
}
return true;
}
#Override
public boolean importData(TransferSupport support) { // if we can't handle the import, say so
if (!canImport(support)) {
return false;
}
JTable.DropLocation dl = (JTable.DropLocation) support.getDropLocation();// fetch the drop location
int row = dl.getRow();
String data; // fetch the data and bail if this fails
try {
data = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor);
} catch (UnsupportedFlavorException e) {
return false;
} catch (IOException e) {
return false;
}
String[] rowData = data.split(",");
tableModel.insertRow(row, rowData);
Rectangle rect = table.getCellRect(row, 0, false);
if (rect != null) {
table.scrollRectToVisible(rect);
}
model.removeAllElements(); // demo stuff - remove for blog
model.insertElementAt(getNextString(count++), 0); // end demo stuff
return true;
}
});
JList dragFrom = new JList(model);
dragFrom.setFocusable(false);
dragFrom.setPrototypeCellValue(getNextString(100));
model.insertElementAt(getNextString(count++), 0);
dragFrom.setDragEnabled(true);
dragFrom.setBorder(BorderFactory.createLoweredBevelBorder());
dragFrom.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
if (SwingUtilities.isLeftMouseButton(me) && me.getClickCount() % 2 == 0) {
String text = (String) model.getElementAt(0);
String[] rowData = text.split(",");
tableModel.insertRow(table.getRowCount(), rowData);
model.removeAllElements();
model.insertElementAt(getNextString(count++), 0);
}
}
});
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
JPanel wrap = new JPanel();
wrap.add(new JLabel("Drag from here:"));
wrap.add(dragFrom);
p.add(Box.createHorizontalStrut(4));
p.add(Box.createGlue());
p.add(wrap);
p.add(Box.createGlue());
p.add(Box.createHorizontalStrut(4));
getContentPane().add(p, BorderLayout.NORTH);
JScrollPane sp = new JScrollPane(table);
getContentPane().add(sp, BorderLayout.CENTER);
fillBox = new JCheckBoxMenuItem("Fill Viewport Height");
fillBox.addActionListener(this);
JMenuBar mb = new JMenuBar();
JMenu options = new JMenu("Options");
mb.add(options);
setJMenuBar(mb);
JMenuItem clear = new JMenuItem("Reset");
clear.addActionListener(this);
options.add(clear);
options.add(fillBox);
getContentPane().setPreferredSize(new Dimension(260, 180));
}
#Override
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == fillBox) {
table.setFillsViewportHeight(fillBox.isSelected());
} else {
tableModel.setRowCount(0);
count = 0;
model.removeAllElements();
model.insertElementAt(getNextString(count++), 0);
}
}
private static void createAndShowGUI() {//Create and set up the window.
FillViewportHeightDemo test = new FillViewportHeightDemo();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.pack(); //Display the window.
test.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {//Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}