How to override the JPopupMenu method show? - java

my goal is to highlight a jlist item after a rightclick then show a jpopupmenu..
I read advice that overriding the method show is the way..
in my case i declare my jpopupmenu at the beginning of my class
JPopupMenu popupMenu = new JPopupMenu();
JMenuItem masterlist,settings;
then i have a method to set up my menuItems
public void setPopupMenu(int depth//ignore this var){
//popupMenu = new JPopupMenu();
popupMenu.removeAll();
masterlist = new JMenuItem("Masterlist");
settings = new JMenuItem("Settings");
//action for marsterlist
masterlist.addActionListener(new ActionListener(){
//stuff here
}
});
//action for settings
settings.addActionListener(new ActionListener(){
//stuff here
}
});
popupMenu.add(masterlist);
popupMenu.add(settings);
}
and my list is in a different method
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
list.setComponentPopupMenu(popupMenu);
I tried putting this on a mouseAdapter of my list but the popupmenu fires first and ignores highlighting...
if ( SwingUtilities.isRightMouseButton(mouseEvent) )//highlight the right clicked item
{
int row = list.locationToIndex(mouseEvent.getPoint());
list.setSelectedIndex(row);
String val = (String)list.getSelectedValue();
System.out.println(val);
}
i know that overriding is something like this
popupmenu = new JPopupMenu(){
#Override
public void show(){}
}
but i cant do that because i am manipulating the menuitems on a method...
or is there any other approach that anyone can suggest...

Rather then trying to modify the state of the JPopupMenu, why not simply modify the state of the menu item when you detect a right click...
So, basically, I make use of the Actions API to define a menu item for a JPopupMenu, which allows it to register a ListSelectionListener to the underlying JList...
public class ShowItemAction extends AbstractAction {
private JList list;
public ShowItemAction(JList list) {
this.list = new JList();
putValue(NAME, "Showing ...");
list.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
int index = list.getSelectedIndex();
String value = list.getSelectedValue().toString();
putValue(NAME, "Showing " + value + " # " + index);
}
}
});
}
#Override
public void actionPerformed(ActionEvent e) {
// The actual action to be performed when selected
}
}
All this does is, when the selection is changed, is change the text (NAME) of the action, which changes the text of the menu item, based on the currently selected row.
When I create the JList, I assign it a JPopupMenu via the setComponentPopupMenu method, which means I no longer need to care about it and it will be displayed appropriately based on the current look and feel requirements
JList list = new JList(model);
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new ShowItemAction(list));
list.setComponentPopupMenu(popupMenu);
I then add a MouseListener to the JList which I use to change the row selection when you right click on the JList...
list.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JList list = (JList)e.getComponent();
if (SwingUtilities.isRightMouseButton(e)) {
int row = list.locationToIndex(e.getPoint());
list.setSelectedIndex(row);
}
}
});
This not entirely fool proof, as if you right click the JList while the popup is visible, the MouseListener doesn't appear to get notified :P
Runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import static javax.swing.Action.NAME;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultListModel model = new DefaultListModel();
model.addElement("One");
model.addElement("Two");
model.addElement("Three");
model.addElement("Four");
JList list = new JList(model);
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new ShowItemAction(list));
list.setComponentPopupMenu(popupMenu);
list.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
int row = list.locationToIndex(e.getPoint());
list.setSelectedIndex(row);
}
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(list));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ShowItemAction extends AbstractAction {
private JList list;
public ShowItemAction(JList list) {
this.list = new JList();
putValue(NAME, "Showing ...");
list.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
int index = list.getSelectedIndex();
String value = list.getSelectedValue().toString();
putValue(NAME, "Showing " + value + " # " + index);
}
}
});
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
}

Related

Drag and Drop from JTable to JLayeredPane

How to create Drag and drop between JTable and JLayeredPane.
Does anyone have any ideas how to implement this?
I need transfer object by Drag and drop between JTable and JLayeredPane.
You have to do a few things at minimum to get this to work:
Call setDragEnabled(true) on your JTable instance
Set the JTables and JLayeredPanes DropTarget
Create a draggable Component to add to the JLayeredPane
To set a Components DropTarget, call the setDropTarget(DropTarget d) method and pass an anonymous inner class as the argument. Code that drops the Transferables data into the Component should be located in the drop(DropTargetDropEvent dtde) method.
The below snippet shows how to receive a Transferable and insert it into the row of a JTable where the drop was initiated:
table.setDropTarget(new DropTarget() {
#Override
public synchronized void drop(DropTargetDropEvent dtde) {
try {
// get row to put new item in
int row = table.rowAtPoint(dtde.getLocation());
// inserting row:
((DefaultTableModel) table.getModel()).insertRow(
//if the row wasn't found, add it to the end of the JTable
row == -1 ? table.getRowCount() : row,
// pass string flavor of transferable data as row data parameter
new Object[] {dtde.getTransferable()
.getTransferData(DataFlavor.stringFlavor)});
} catch(UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
});
This below snippet again receives a Transferable but instead adds it to the end of a JLayeredPane. Note that components are added to the end of the JLayeredPane because I assign a BoxLayout(SwingConstants.VERTICAL) to it.
layeredPane.setDropTarget(new DropTarget() {
#Override
public synchronized void drop(DropTargetDropEvent dtde) {
try {
// create draggable label to add to layered pane
// creating this class will be explained next
final DraggableLabel label = new DraggableLabel(
(String) dtde.getTransferable()
.getTransferData(DataFlavor.stringFlavor));
// add label to layered pane
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
layeredPane.revalidate();
layeredPane.repaint();
} catch(UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
});
To create a draggable Component (In this example a JLabel), you must:
Create a new class that extends JLabel and implements DragGestureListener and DragSourceListener
Override all the necessary methods
In the constructor:
Create a new DragSource
Create a DragGestureRecognizer and assign it to this the JLabel subclass and this the DragGestureListener
Start a drag using the DragSource in the dragGestureRecognized(DragGestureEvent evt) method
class DraggableLabel extends JLabel implements DragGestureListener, DragSourceListener {
private DragSource dragSource;
DraggableLabel(String text) {
super(text);
// create the drag source
dragSource = new DragSource();
// assign this component a DragGestureRecognizer
dragSource.createDefaultDragGestureRecognizer(
this, DnDConstants.ACTION_COPY_OR_MOVE, this);
// for contrast with other items in layered pane
setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
}
#Override
public void dragGestureRecognized(DragGestureEvent evt) {
dragSource.startDrag(
evt, //trigger event
DragSource.DefaultMoveDrop, // icon
new StringSelection(getText()), // transferable data
this); // this the DragSourceListener
}
#Override
public void dragEnter(DragSourceDragEvent evt) {}
#Override
public void dragOver(DragSourceDragEvent evt) {}
#Override
public void dragExit(DragSourceEvent evt) {}
#Override
public void dropActionChanged(DragSourceDragEvent evt) {}
#Override
public void dragDropEnd(DragSourceDropEvent evt) {}
}
Full working example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
class DragDropExample {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
final JFrame frame = new JFrame("JLabel Text Example");
final JTable table = new JTable(new DefaultTableModel(
new Object[][] {
{"Item 1"},
{"Item 2"},
{"Item 3"},
{"Item 4"},
{"Item 5"}},
new String[] {"Column Name"}));
table.setDragEnabled(true);
table.setDropTarget(new DropTarget() {
#Override
public synchronized void drop(DropTargetDropEvent dtde) {
try {
// get row to put new item in
int row = table.rowAtPoint(dtde.getLocation());
// inserting row:
((DefaultTableModel) table.getModel()).insertRow(
//if the row wasn't found, add it to the end of the JTable
row == -1 ? table.getRowCount() : row,
// pass string flavor of transferable data as row data parameter
new Object[] {dtde.getTransferable()
.getTransferData(DataFlavor.stringFlavor)});
} catch(UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
});
final JLayeredPane layeredPane = new JLayeredPane() {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 300);
}
};
layeredPane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
layeredPane.setLayout(new BoxLayout(layeredPane, BoxLayout.Y_AXIS));
layeredPane.setDropTarget(new DropTarget() {
#Override
public synchronized void drop(DropTargetDropEvent dtde) {
try {
// create draggable label to add to layered pane
// creating this class will be explained next
final DraggableLabel label = new DraggableLabel(
(String) dtde.getTransferable()
.getTransferData(DataFlavor.stringFlavor));
// add label to layered pane
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
layeredPane.revalidate();
layeredPane.repaint();
} catch(UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
});
frame.getContentPane().add(table, BorderLayout.WEST);
frame.getContentPane().add(layeredPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
static class DraggableLabel extends JLabel implements DragGestureListener, DragSourceListener {
private DragSource dragSource;
DraggableLabel(String text) {
super(text);
// create the drag source
dragSource = new DragSource();
// assign this component a DragGestureRecognizer
dragSource.createDefaultDragGestureRecognizer(
this, DnDConstants.ACTION_COPY_OR_MOVE, this);
// for contrast with other items
setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
}
#Override
public void dragGestureRecognized(DragGestureEvent evt) {
dragSource.startDrag(
evt, //trigger event
DragSource.DefaultMoveDrop,
new StringSelection(getText()),
this);
}
#Override
public void dragEnter(DragSourceDragEvent evt) {}
#Override
public void dragOver(DragSourceDragEvent evt) {}
#Override
public void dragExit(DragSourceEvent evt) {}
#Override
public void dropActionChanged(DragSourceDragEvent evt) {}
#Override
public void dragDropEnd(DragSourceDropEvent evt) {}
}
}
DropTarget dt = new DropTarget(Droptargetobject, new DropTargetAdapter() {
#Override
public void dragEnter(DropTargetDragEvent dtde) {
//executed if mouse with items enters object
}
#Override
public void drop(DropTargetDropEvent dtde) {
//executed if you drop the item
}
});
and to implement the drag use a java.awt.dnd.DragGestureEvent;.
i hope you understand what i mean
imports i used:
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;

Double click event on JList element

I've got a problem, and I can't find an answer by myself.
I'm making a simple chat in java, and I populated a JList with names. When a name is double-clicked, that name needs to be passed as an argument to another frame (as a recipient name). But, double click does not work
I've got a main class InstantMessageFrame, in which a JList named friends is initialized and filled with an array of strings.
private JList<String> friends;
String names[] = { "Ana", "Banana", "Cikla", "Doris", "Ema", "Mirna","Matea","Veronika","Vera","Marta","Mirta","Davor","Marko","Matko","Kloki" };
JList<String> friends = new JList<String>(names);
Also,I added a listener to my JList
DisplayMessageDialog dmd = new DisplayMessageDialog();
friends.addMouseListener(dmd);
This is my DisplayMessageDialog class which checks if there was a double click. If there is a double click, a new Frame should appear. None of the lines in the first "if" statement executes (one with the e.getClickCount())
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DisplayMessageDialog extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{
JList theList = (JList) e.getSource();
if (e.getClickCount() == 2)
{
int index = theList.locationToIndex(e.getPoint());
if (index >= 0)
{
Object o = theList.getModel().getElementAt(index);
InstantMessageDialog imd = new InstantMessageDialog(null, o.toString());
imd.setVisible(true);
System.out.println("Double-clicked on: " + o.toString());
}
}
}
}
This is how it should look like:
http://i.stack.imgur.com/Bex5U.png
And when double clicked,a new frame should appear (in code "InstantMessageDialog" object)
http://i.stack.imgur.com/yjvW6.png
And it should look like this.
So working off your out-of-context code snippets, this example seems to work just fine, so I can only surmise that your problem is else where in your code that you're not showing us...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JList<String> friends;
public TestPane() {
String names[] = {"Ana", "Banana", "Cikla", "Doris", "Ema", "Mirna", "Matea", "Veronika", "Vera", "Marta", "Mirta", "Davor", "Marko", "Matko", "Kloki"};
JList<String> friends = new JList<String>(names);
setLayout(new BorderLayout());
add(new JScrollPane(friends));
DisplayMessageDialog dmd = new DisplayMessageDialog();
friends.addMouseListener(dmd);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class DisplayMessageDialog extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
JList theList = (JList) e.getSource();
// if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
if (e.getClickCount() == 2) {
int index = theList.locationToIndex(e.getPoint());
if (index >= 0) {
Object o = theList.getModel().getElementAt(index);
//InstantMessageDialog imd = new InstantMessageDialog(null, o.toString());
//imd.setVisible(true);
JOptionPane.showMessageDialog(theList, "Double-clicked on: " + o.toString());
System.out.println("Double-clicked on: " + o.toString());
}
}
}
}
}
I might add though, you "double click" check probably should be something more like...
if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
Assuming your really only want to respond to left mouse clicks
When a name is double-clicked...
When designing a GUI it is always a good idea to provide support for the mouse and keyboard.
Check out List Action. It adds Action support to a JList. All you need to do is provide the Action. Then the Action can be invoked by:
double clicking, or
using the Enter key.
The most basic example of using the ListAction would be:
String[] data = { "zero", "one", "two", "three", "four", "five" };
JList list = new JList( data );
Action displayAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JList list = (JList)e.getSource();
System.out.println(list.getSelectedValue());
}
};
ListAction la = new ListAction(list, displayAction);

Stepped ComboBox refresh

A Stepped ComboBox is very useful to make the drop-down pop-up wider than the text field. However when new content is added to the list, the pop-up gets its initial width back.
By default
After refresh (new element)
SSCCE
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
public class SteppedComboBoxRefresh extends JFrame {
private List<String> list;
private final SteppedComboBox combo;
public SteppedComboBoxRefresh() {
super("SteppedComboBox Refresh");
this.list = new ArrayList<String>(Arrays.asList(new String[]{
"AAA", "AAAAAA"
}));
this.combo = new SteppedComboBox(this.list.toArray());
this.combo.setDimensions(50);
JButton addButton = new JButton("Add longer string");
addButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
list.add(list.get(list.size()-1) + "AAA");
combo.setModel(new DefaultComboBoxModel(list.toArray()));
combo.setDimensions(50);
}
});
getContentPane().setLayout(new FlowLayout());
getContentPane().add(this.combo);
getContentPane().add(addButton);
}
public static void main (String args[]) {
SteppedComboBoxRefresh f = new SteppedComboBoxRefresh();
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize (300, 100);
f.setVisible(true);
}
}
class SteppedComboBoxUI extends MetalComboBoxUI {
#Override
protected ComboPopup createPopup() {
BasicComboPopup popup = new BasicComboPopup( this.comboBox ) {
#Override
public void show() {
Dimension popupSize = ((SteppedComboBox)this.comboBox).getPopupSize();
popupSize.setSize( popupSize.width,
getPopupHeightForRowCount( this.comboBox.getMaximumRowCount() ) );
Rectangle popupBounds = computePopupBounds( 0,
this.comboBox.getBounds().height, popupSize.width, popupSize.height);
this.scroller.setMaximumSize( popupBounds.getSize() );
this.scroller.setPreferredSize( popupBounds.getSize() );
this.scroller.setMinimumSize( popupBounds.getSize() );
this.list.invalidate();
int selectedIndex = this.comboBox.getSelectedIndex();
if ( selectedIndex == -1 ) {
this.list.clearSelection();
} else {
this.list.setSelectedIndex( selectedIndex );
}
this.list.ensureIndexIsVisible( this.list.getSelectedIndex() );
setLightWeightPopupEnabled( this.comboBox.isLightWeightPopupEnabled() );
show( this.comboBox, popupBounds.x, popupBounds.y );
}
};
popup.getAccessibleContext().setAccessibleParent(this.comboBox);
return popup;
}
}
class SteppedComboBox extends JComboBox {
protected int popupWidth;
public SteppedComboBox(ComboBoxModel aModel) {
super(aModel);
setUI(new SteppedComboBoxUI());
this.popupWidth = 0;
}
public SteppedComboBox(final Object[] items) {
super(items);
setUI(new SteppedComboBoxUI());
this.popupWidth = 0;
}
public SteppedComboBox(Vector items) {
super(items);
setUI(new SteppedComboBoxUI());
this.popupWidth = 0;
}
public void setPopupWidth(int width) {
this.popupWidth = width;
}
public Dimension getPopupSize() {
Dimension size = getSize();
if (this.popupWidth < 1) {
this.popupWidth = size.width;
}
return new Dimension(this.popupWidth, size.height);
}
public void setDimensions(int width) {
Dimension d = getPreferredSize();
setPreferredSize(new Dimension(width, d.height));
setPopupWidth(d.width);
}
}
The ComboBox still uses its previous PreferredSize. It's needed to set the preferred size back to null, so that we get the size which is preferred by the new content in the list.
void javax.swing.JComponent.setPreferredSize(Dimension preferredSize)
Sets the preferred size of this component. If preferredSize is null, the UI will be asked for the preferred size.
public void setDimensions(int width) {
setPreferredSize(null);
Dimension d = getPreferredSize();
setPreferredSize(new Dimension(width, d.height));
setPopupWidth(d.width);
}
Result
You could use the Combo Box Popup.
It is a more flexible version of the Stepped Combo Box. Best of all it can be used on any combo box since the logic is implemented in a `PopupMenuListener'.
You can control the maximum width of the popup. You can even have the popup display above the combo box instead of below.

Set Text in Editor Component in Combobox

My goal is to create a filtered combobox.
I create an editable combobox. When the user enters text, the code searches matching items in the model. When the user leaves the combobox, the code selects the selected item from the list. Further, the text from the editor component gets the value of the selected item.
When the focus goes back to the combobox via shift + tab, the editor component gets the text of the selected element. After releasing the shift key, the keylistener runs. In this case, the editor component contains the text entered previously, e.g., the value set when the focus is lost.
How can I set the text of the editorComponent to make it persist?
Here's the code:
package de.ccw.reports.gui.incomingOrder.MyComboBox;
import java.awt.FlowLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.text.JTextComponent;
import de.ccw.commons.ui.comp.XComboBox;
public class FilterableComboBox<E> extends XComboBox<E> {
ComboBoxModel<E> originalModel;
DefaultComboBoxModel<E> filteredModel;
JTextComponent editorComp;
JList<E> list;
public FilterableComboBox(ComboBoxModel<E> aModel) {
super();
receivePopupList();
originalModel = aModel;
filteredModel = new DefaultComboBoxModel<>();
setModel(filteredModel);
editorComp = (JTextComponent) getEditor().getEditorComponent();
editorComp.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
System.out.println("focusLostStart=" + getSelectedItem() + "|" + editorComp.getText());
String text = editorComp.getText();
editorComp.setText("");
setSelectedItem(null);
if(list.getSelectedIndex() != -1 && text.isEmpty() == false){
setSelectedIndex(list.getSelectedIndex());
editorComp.setText(getSelectedItem().toString());
}
System.out.println("focusLostEnd=" + getSelectedItem() + "|" + editorComp.getText());
}
#Override
public void focusGained(FocusEvent e) {
System.out.println("focusGainedStart=" + getSelectedItem() + "|" + editorComp.getText());
E element = getSelectedItem();
String text = editorComp.getText();
performModelFilter(null);
showPopup();
setSelectedItem(element);
editorComp.setText(text);
editorComp.selectAll();
System.out.println("focusGainedEnd=" + getSelectedItem() + "|" + editorComp.getText());
}
});
editorComp.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
}
String filterBkp = "";
#Override
public void keyReleased(KeyEvent e) {
String filter = editorComp.getText();
System.out.println("keyReleased-" + filterBkp + filter);
if(filter.equals(filterBkp) == false)
refreshModel();
filterBkp = filter;
}
#Override
public void keyPressed(KeyEvent e) {
}
});
setEditable(true);
}
#SuppressWarnings("unchecked")
private void receivePopupList() {
BasicComboPopup popup = (BasicComboPopup) getAccessibleContext().getAccessibleChild(0);
list = popup.getList();
}
private void refreshModel() {
String filter = editorComp.getText();
performModelFilter(filter);
editorComp.setText(filter);
}
private void performModelFilter(String filter) {
System.out.println("performModelFilter-" + filter);
filteredModel.removeAllElements();
for(int i = 0; i < originalModel.getSize(); i++){
E element = originalModel.getElementAt(i);
String value = element.toString().toUpperCase();
if (filter == null || value.contains(filter.toUpperCase())) {
filteredModel.addElement(element);
}
}
}
public static void main(String args[]){
FilterableComboBox<String> combo = new FilterableComboBox<>(new DefaultComboBoxModel<>(new String[]{"abc", "def", "ghi", "jkl", "mnoabc", "pqrdef"}));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new JTextField(20));
frame.add(combo);
frame.add(new JTextField(20));
frame.pack();
frame.setVisible(true);
}
}

JButton does not change color when button is typed

I have a very small question. Now I wrote the code for creating a virtual keyboard. I want the color of the button to change when it is typed. Here is my code:
public class ButtonColor implements KeyListener {
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar()=='a') {
A.setBackground(Color.red);
}
}
}
]
Whenever I press A, nothing happens. When I add this line:
JOptionPane.showMessageDialog(null, "A was typed");
then type a, the message appears and after I click OK the button changes color. Why does that happen? How can I fix this problem?
There could be any number of reasons why this doesn't work for you, for starters, the button may be transparent (opaque == false)
I would strongly recommend against KeyListener in favour of Key Bindings as KeyListener has issues with focus...
For example...
The following uses the key bindings API in order to respond to a given key stroke, depending on if it's a key press or release event, it will set the background color and opacity state accordingly and even sets the buttons pressed state...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class KeyboardTest {
public static void main(String[] args) {
new KeyboardTest();
}
public KeyboardTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JButton btnA = createButton("A");
JButton btnB = createButton("B");
JButton btnC = createButton("C");
JButton btnD = createButton("D");
JButton btnE = createButton("E");
add(btnA);
add(btnB);
add(btnC);
add(btnD);
add(btnE);
addKeyBinding(btnA, "A", KeyEvent.VK_A);
addKeyBinding(btnB, "B", KeyEvent.VK_B);
addKeyBinding(btnC, "C", KeyEvent.VK_C);
addKeyBinding(btnD, "D", KeyEvent.VK_D);
addKeyBinding(btnE, "E", KeyEvent.VK_E);
}
protected JButton createButton(String text) {
JButton btn = new JButton(text);
btn.setFocusable(false);
return btn;
}
protected void addKeyBinding(JButton btn, String name, int virtualKey) {
ActionMap am = getActionMap();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name + ".pressed");
im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name + ".released");
am.put(name + ".pressed", new KeyAction(btn, true));
am.put(name + ".released", new KeyAction(btn, false));
}
}
public class KeyAction extends AbstractAction {
private JButton btn;
private boolean highlight;
public KeyAction(JButton btn, boolean highlight) {
this.btn = btn;
this.highlight = highlight;
}
#Override
public void actionPerformed(ActionEvent e) {
if (highlight) {
btn.getModel().setPressed(true);
btn.setBackground(Color.RED);
btn.setOpaque(true);
} else {
btn.getModel().setPressed(false);
btn.setBackground(null);
btn.setOpaque(false);
}
}
}
}
Updated
If you also use btn.getModel().setArmed(...); you will get a much more "bolded" response, which produces better visual feedback...IMHO

Categories