JMenuItem.setSelected() does not alter appearance of selected item? - java

I wish to programmatically cause a particular item on a menu to be selected and to display as such so that if Enter is pressed, the corresponding action will be performed. Ubnfortunately I find that neither JMenuItem.setSelected(), nor JPopupMenu.setSelectedItem() does what I want. Basically I want to happen what happens when either the arrow key is pressed or the mouse moves into the space of a particular item - the background color alters, indicating the selection. I did not program that, it just happens. Why don't these APIs do the same thing? This is driving me nuts. It should not be this hard. Is there some API that does what I want?

This kinda worked:
JMenuItem#setArmed(boolean);
although you don't see it unless you traverse the JMenus to get there. Perhaps if you call that on each menu above it?
EDIT:
Perhaps you want an accelerator for your menu item?
See: How To Use Menus: Enabling Keyboard Operation

java.awt.Robot can do Trick ;)
Consider the code Given below:
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.Robot;
public class JMenuFrame extends JFrame implements ActionListener
{
JMenuBar bar;
JMenu menu ;
String[] items;
JMenuItem[] menuItems;
JButton start;
Robot robot;
public void prepareAndShowGUI()
{
try
{
robot = new Robot();
}
catch (Exception ex){}
bar = new JMenuBar();
menu = new JMenu("File");
items = new String[]{"Open","Save","Save As","Quit"};
menuItems = new JMenuItem[items.length];
start = new JButton("Click me");
for (int i = 0 ; i < items.length ; i++)
{
menuItems[i] = new JMenuItem(items[i]);
menuItems[i].addActionListener(this);
menu.add(menuItems[i]);
}
bar.add(menu);
setJMenuBar(bar);
start.addActionListener(this);
getContentPane().add(start,BorderLayout.SOUTH);
setPreferredSize(new Dimension(300,400));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent evt)
{
if ("Click me".equals(evt.getActionCommand()))
{
menu.doClick();
if (robot!=null)
{
for (int i = 0 ; i<=2 ; i++) //Suppose you want to select 3rd MenuItem
{
if (!menuItems[i].isEnabled())
{
continue;
}
robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_UP);
}
}
}
else
{
JOptionPane.showMessageDialog(this,evt.getActionCommand()+" is pressed","Information",JOptionPane.INFORMATION_MESSAGE);
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
JMenuFrame mf = new JMenuFrame();
mf.prepareAndShowGUI();
}
});
}
}

It's ugly as sin, but this, in connection with splungebob's setArmed() answer above is the full solution:
First, make the menu a MenuKeyListener and add it a a MenuKeyListener to itself. Then:
public void menuKeyReleased(MenuKeyEvent e) {
if (e.getModifiers() == 0) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ENTER:
case KeyEvent.VK_SPACE:
for (MenuElement elem : this.getSubElements()) {
if (elem instanceof JMenuItem) {
JMenuItem item = (JMenuItem) elem;
if (item.isArmed()) {
Action action = item.getAction();
if (action != null) {
action.actionPerformed(new ActionEvent(this, 0, null));
e.consume();
setVisible(false);
}
}
}
}
}
}
}
I can't believe that this was this difficult. Swing has definite limitations when it comes to building keyboard-centric interfaces.

Although I'm not sure I agree with the requirements (see my comment in the OP), I still wanted to give it a crack. The first half of the code is just setting up the GUI so that the condition can be creted by the user.
- A menu is created with 3 items, and 3 separate checkboxes are created to control the state of the menu items.
- If at any time only one menu item is enabled, auto-expand the menu and "pre-select" the item. The code to auto-expand the menu was ripped from JMenu.
- Add a MenuKeyListener to the menu to capture the user hitting the space bar while the menu tree is expanded.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
public class JMenuDemo implements Runnable
{
private final String[] ACTIONS = new String[]{"Open", "Print", "Close"};
private JMenu fileMenu;
private JMenuItem[] menuItems;
private JCheckBox[] checkBoxes;
public static void main(String args[])
{
SwingUtilities.invokeLater(new JMenuDemo());
}
public void run()
{
JPanel panel = new JPanel(new GridLayout(0,1));
panel.setBorder(BorderFactory.createTitledBorder("Enabled"));
menuItems = new JMenuItem[ACTIONS.length];
checkBoxes = new JCheckBox[ACTIONS.length];
for (int i = 0; i < ACTIONS.length; i++)
{
final int index = i;
final String action = ACTIONS[i];
menuItems[i] = new JMenuItem(action);
menuItems[i].setAccelerator(KeyStroke.getKeyStroke(action.charAt(0),
ActionEvent.ALT_MASK));
menuItems[i].setMnemonic(action.charAt(0));
menuItems[i].addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println(action);
}
});
checkBoxes[i] = new JCheckBox(action);
checkBoxes[i].setSelected(true);
checkBoxes[i].addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent event)
{
checkBoxChanged(index);
}
});
panel.add(checkBoxes[i]);
}
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(createJMenuBar());
frame.add(panel, BorderLayout.SOUTH);
frame.setSize(300, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void checkBoxChanged(int index)
{
menuItems[index].setEnabled(checkBoxes[index].isSelected());
evaluate();
}
private JMenuBar createJMenuBar()
{
fileMenu = new JMenu("File");
fileMenu.setMnemonic('F');
fileMenu.addMenuKeyListener(new MenuKeyListener()
{
#Override
public void menuKeyTyped(MenuKeyEvent event)
{
autoClick(event);
}
#Override
public void menuKeyPressed(MenuKeyEvent event) {}
#Override
public void menuKeyReleased(MenuKeyEvent event) {}
});
for (int i = 0; i < menuItems.length; i++)
{
fileMenu.add(menuItems[i]);
}
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
return menuBar;
}
private void autoClick(MenuKeyEvent event)
{
if (event.getModifiers() == 0 && event.getKeyChar() == KeyEvent.VK_SPACE)
{
for (JMenuItem menuItem : menuItems)
{
if (menuItem.isArmed())
{
menuItem.doClick();
MenuSelectionManager.defaultManager().setSelectedPath(null);
}
}
}
}
private void evaluate()
{
JMenuItem onlyOne = null;
for (JMenuItem menuItem : menuItems)
{
menuItem.setArmed(false);
if (menuItem.isEnabled())
{
if (onlyOne == null)
{
onlyOne = menuItem;
}
else
{
onlyOne = null;
break;
}
}
}
// Show the path if only one is enabled
if (onlyOne != null)
{
onlyOne.setArmed(true);
MenuElement me[] = buildMenuElementArray(fileMenu);
MenuSelectionManager.defaultManager().setSelectedPath(me);
}
}
/*
* Copied from JMenu
*/
private MenuElement[] buildMenuElementArray(JMenu leaf)
{
Vector<JComponent> elements = new Vector<JComponent>();
Component current = leaf.getPopupMenu();
while (true)
{
if (current instanceof JPopupMenu)
{
JPopupMenu pop = (JPopupMenu) current;
elements.insertElementAt(pop, 0);
current = pop.getInvoker();
}
else if (current instanceof JMenu)
{
JMenu menu = (JMenu) current;
elements.insertElementAt(menu, 0);
current = menu.getParent();
}
else if (current instanceof JMenuBar)
{
JMenuBar bar = (JMenuBar) current;
elements.insertElementAt(bar, 0);
MenuElement me[] = new MenuElement[elements.size()];
elements.copyInto(me);
return me;
}
}
}
}

jMenu1.doClick(); // this open the menu again

I found this setSelectedPath() works well in unison of mouse hover, unlike using menuItem.setArmed()
MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{popupMenu, menuItem});

Related

JMenuBar arrow keys work the opposite of what expected because componentOrientation is set to Right to Left

After applying setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT) to the JMenuBar the arrow keys started to behave the opposite of what expected. Right arrow goes left and Left arrow goes right when trying to traverse through keyboard.
By the way, I'm using Java 11.0.12 on Windows 10.
Here is the code:
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
#SuppressWarnings("serial")
public class MenuBarProblem extends JFrame {
private JMenuBar menuBar;
private JMenu firstMenu;
private JMenuItem a;
private JMenuItem b;
private JMenu secondMenu;
private JMenuItem c;
private JMenuItem d;
private JMenu thirdMenu;
private JMenuItem e;
private JMenuItem f;
public MenuBarProblem() {
this.setLayout(new BorderLayout());
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setSize(screenSize);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.firstMenu = new JMenu("first");
this.a = new JMenuItem("a");
this.b = new JMenuItem("b");
this.firstMenu.add(this.a);
this.firstMenu.add(this.b);
this.secondMenu = new JMenu("second");
this.c = new JMenuItem("c");
this.d = new JMenuItem("d");
this.secondMenu.add(this.c);
this.secondMenu.add(this.d);
this.thirdMenu = new JMenu("third");
this.e = new JMenuItem("e");
this.f = new JMenuItem("f");
this.thirdMenu.add(this.e);
this.thirdMenu.add(this.f);
this.menuBar = new JMenuBar();
this.menuBar.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); //Here is the reason
this.menuBar.add(this.firstMenu);
this.menuBar.add(this.secondMenu);
this.menuBar.add(this.thirdMenu);
this.setJMenuBar(this.menuBar);
}
}
Here is also the main class which contains the main method:
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MenuBarProblem test = new MenuBarProblem();
test.setVisible(true);
}
});
}
}
Update: The issue turned out to be a bug and now it's been added to the bug database here.
Meanwhile the problem has been accepted as a bug (see above) and so the suggested solution is a temporary patch, build upon the internal implementation of the WindowsLookAndFeel.
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
#SuppressWarnings("serial")
public class MenuBarProblem extends JFrame {
private JMenuBar menuBar;
private JMenu firstMenu;
private JMenuItem a;
private JMenuItem b;
private JMenu secondMenu;
private JMenuItem c;
private JMenuItem d;
private JMenu thirdMenu;
private JMenuItem e;
private JMenuItem f;
private JMenu fourthMenu;
private JMenuItem g;
private JMenuItem h;
private KeyboardFocusManager keyboardFocusManager;
public MenuBarProblem() {
setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(400, 200));
add(panel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
firstMenu = new JMenu("first");
a = new JMenuItem("a");
b = new JMenuItem("b");
firstMenu.add(a);
firstMenu.add(b);
secondMenu = new JMenu("second");
c = new JMenuItem("c");
d = new JMenuItem("d");
secondMenu.add(c);
secondMenu.add(d);
thirdMenu = new JMenu("third");
e = new JMenuItem("e");
f = new JMenuItem("f");
thirdMenu.add(e);
thirdMenu.add(f);
fourthMenu = new JMenu("fourth");
g = new JMenuItem("g");
h = new JMenuItem("h");
fourthMenu.add(g);
fourthMenu.add(h);
menuBar = new JMenuBar();
menuBar.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); // Here is the reason
menuBar.add(firstMenu);
menuBar.add(secondMenu);
menuBar.add(thirdMenu);
menuBar.add(fourthMenu);
setJMenuBar(menuBar);
pack();
keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
keyboardFocusManager.addKeyEventDispatcher(new MSMKeyEventDispatcher());
}
private class MSMKeyEventDispatcher implements KeyEventDispatcher {
int index = -1;
private void select(int index) {
JMenu menu = menuBar.getMenu(index);
if (menu != null) {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
MenuElement path[] = new MenuElement[2];
path[0] = menuBar;
path[1] = menu;
msm.setSelectedPath(path);
}
}
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (MenuBarProblem.this.getFocusOwner() == null) {
return false;
}
if (e.getID() == KeyEvent.KEY_PRESSED) {
if (e.getKeyCode() == KeyEvent.VK_ALT) {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
if (msm.getSelectedPath().length == 0) {
index = 0;
select(index);
} else {
msm.setSelectedPath(null);
index = -1;
}
return true;
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
if (index >= 0) {
--index;
if (index < 0) {
index = menuBar.getComponentCount() - 1;
}
select(index);
return true;
}
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (index >= 0) {
++index;
if (index >= menuBar.getComponentCount()) {
index = 0;
}
select(index);
return true;
}
}
}
return false;
}
}
}
Addendum: The most simple idea for a solution is probably a KeyEventDispatcher switching the KeyCodes KeyEvent.VK_LEFT and KeyEvent.VK_RIGHT of the received KeyEvent and redispatching the changed KeyEvent. However, it turned out that this has absolutely no effect.

JMenuItems on a JPopupMenu sometimes aren't drawn

I have a JPopupMenu that is displayed when a JButton is held down. This menu contains a series of JMenuItems, each associated with an action, that change sometimes. The problem I'm having is that intermitently the JMenuItems aren't being drawn, I simply get a grey JPopupMenu, but the items appear if I move the mouse cursor over them. I thought the problem might be with not properly repainting the components after changes, but testing show the problem keeps happening even when there are no changes to the items. Here is the relevant code:
if (!listChanged) {
myPopupMenu.show(myButton, x, y);
} else {
List<String> menuList = getMenuList();
MyData data = getData();
myPopupMenu.removeAll();
for (int i = 0; i < menuList.size(); i++) {
String name = menuList.get(i);
JMenuItem item = new JMenuItem(new MyMenuAction(this, name,
data, i));
item.addActionListener(this);
myPopupMenu.add(item);
myPopupMenu.validate();
}
myPopupMenu.repaint();
myPopupMenu.show(myButton, x, y);
}
...
private static class MyMenuAction extends AbstractAction {
private MyClass parent;
private int index;
private MyData data;
public MyMenuAction (MyClass parent, String name,
MyData data, int index) {
super(name);
this.parent = parent;
this.index = index;
this.data = data;
}
#Override
public void actionPerformed(ActionEvent e) {
Object[] actionParameters;
try {
actionParameters = data.getParameters(index);
} catch (ImmediateException e1) {
log(e1.getMessage(), "Error");
return;
}
parent.myButtonAction(actionParameters);
}
}
Just to clarify, the actions are working fine and 8 times out of 10 the JPopupMenu and all the JMenuItems are drawn right, but I can't figure out why they don't appear sometimes (regardless of whether the list has changed or not). Any help would be appreciated.
EDIT:
Ok, following Andrew Thompson's recomendation, here is a short complete example. Many of the methods have been striped bare, but the basic is still there. Just click and hold the button "SHOW MENU" to display the JPopupMenu. Since the problem is intermitent, it may be necessary to do it several times until the problem occurs.
package main;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.UIManager;
public class MyClass implements ActionListener, MouseListener {
boolean listChanged = true;
boolean mousePressed = false;
long clickStart;
JPopupMenu myPopupMenu;
JButton myButton;
JFrame myFrame;
ArrayList<String> list;
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.start();
}
private void start() {
myFrame = new JFrame();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
System.out.println(e.getClass().getName() + " " + e.getMessage());
}
startList();
myButton = new JButton("SHOW MENU");
myPopupMenu = new JPopupMenu();
JToolBar toolbar = new JToolBar();
toolbar.add(new JButton("Button1"));
toolbar.add(new JButton("Button2"));
toolbar.add(new JButton("Button3"));
toolbar.add(new JButton("Button4"));
toolbar.add(new JButton("Button5"));
toolbar.add(myButton);
myButton.addMouseListener(this);
toolbar.setBorder(BorderFactory.createEtchedBorder(1));
JPanel emptyPanel = new JPanel();
myFrame.add(toolbar, BorderLayout.PAGE_START);
myFrame.add(emptyPanel, BorderLayout.CENTER);
myFrame.pack();
myFrame.setExtendedState(myFrame.getExtendedState()
| JFrame.MAXIMIZED_BOTH);
myFrame.setVisible(true);
}
public void showMenu() {
if (!listChanged) {
myPopupMenu.show(myButton, 0, myButton.getHeight());
} else {
listChanged = false;
List<String> menuList = getMenuList();
MyData data = getData();
myPopupMenu.removeAll();
for (int i = 0; i < menuList.size(); i++) {
String name = menuList.get(i);
JMenuItem item = new JMenuItem(new MyMenuAction(this, name,
data, i));
item.addActionListener(this);
myPopupMenu.add(item);
myPopupMenu.validate();
}
myPopupMenu.repaint();
myPopupMenu.show(myButton, 0, myButton.getHeight());
}
}
private void startList() {
list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
list.add("Item 5");
}
private List<String> getMenuList() {
return list;
}
public void myButtonAction() {
Object[] defaultParameters = getDefaultParameters();
myButtonAction(defaultParameters);
}
private Object[] getDefaultParameters() {
// Placeholder
return null;
}
public void myButtonAction(Object[] actionParameters) {
// Placeholder
}
private MyData getData() {
// Placeholder
return new MyData();
}
private void changeList(List<String> newList) {
list.clear();
list.addAll(newList);
listChanged = true;
}
#Override
public void actionPerformed(ActionEvent e) {
// Placeholder
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getSource() == myButton) {
mousePressed = true;
clickStart = System.currentTimeMillis();
new Thread(new Runnable() {
#Override
public void run() {
synchronized (this) {
while (mousePressed)
try {
this.wait(10);
if (System.currentTimeMillis() - clickStart > 300) {
MyClass.this.showMenu();
return;
}
} catch (InterruptedException e1) {
break;
}
MyClass.this.myButtonAction();
}
}
}).start();
}
}
#Override
public void mouseReleased(MouseEvent e) {
mousePressed = false;
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
private static class MyData {
public Object[] getParameters(int index) {
// Placeholder
return null;
}
}
private static class MyMenuAction extends AbstractAction {
private MyClass parent;
private int index;
private MyData data;
public MyMenuAction(MyClass parent, String name, MyData data, int index) {
super(name);
this.parent = parent;
this.index = index;
this.data = data;
}
#Override
public void actionPerformed(ActionEvent e) {
Object[] actionParameters;
try {
actionParameters = data.getParameters(index);
} catch (Exception e1) {
System.out.println(e1.getMessage());
return;
}
parent.myButtonAction(actionParameters);
}
}
}
I have a JPopupMenu that is displayed when a JButton is held down.
First of all I have a problem with a UI like that. The standard is to show a popup when you right click (in windows). Follow known standards. Read the section from the Swing tutorial on Bringing Up a Popup Menu for more information and working examples.
Secondly, I can't reproduce the problem (no matter how long I try). Random problems are usually caused by the GUI not being updated on the EDT. So don't use a Thread.
Instead use a Swing Timer.
Set the Timer to fire after 200ms at which time you display the menu. Code executed from the Timer IS invoked on the EDT. So you would restart() the Timer in mousePressed. and stop() the Timer in mouseReleased.

JInternalFrame As Modal

I Have the following code:
import java.awt.AWTEvent;
import java.awt.ActiveEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.MenuComponent;
import java.awt.event.MouseEvent;
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
public class modalInternalFrame extends JInternalFrame {
// indica si aquest es modal o no.
boolean modal = false;
#Override
public void show() {
super.show();
if (this.modal) {
startModal();
}
}
#Override
public void setVisible(boolean value) {
super.setVisible(value);
if (modal) {
if (value) {
startModal();
} else {
stopModal();
}
}
}
private synchronized void startModal() {
try {
if (SwingUtilities.isEventDispatchThread()) {
EventQueue theQueue =
getToolkit().getSystemEventQueue();
while (isVisible()) {
AWTEvent event = theQueue.getNextEvent();
Object source = event.getSource();
boolean dispatch = true;
if (event instanceof MouseEvent) {
MouseEvent e = (MouseEvent) event;
MouseEvent m =
SwingUtilities.convertMouseEvent((Component) e.getSource(), e, this);
if (!this.contains(m.getPoint()) && e.getID() != MouseEvent.MOUSE_DRAGGED) {
dispatch = false;
}
}
if (dispatch) {
if (event instanceof ActiveEvent) {
((ActiveEvent) event).dispatch();
} else if (source instanceof Component) {
((Component) source).dispatchEvent(
event);
} else if (source instanceof MenuComponent) {
((MenuComponent) source).dispatchEvent(
event);
} else {
System.err.println(
"Unable to dispatch: " + event);
}
}
}
} else {
while (isVisible()) {
wait();
}
}
} catch (InterruptedException ignored) {
}
}
private synchronized void stopModal() {
notifyAll();
}
public void setModal(boolean modal) {
this.modal = modal;
}
public boolean isModal() {
return this.modal;
}
}
Then I used the NetBeans GUI to draw my JInternalFrame, but just changed the code in the class declaration to extend modalInternalFrame instead of JInternalFrame:
public class myDialog extends modalInternalFrame {
....
and then used this to actually display it from my top-level "desktop" JFrame (containing jDesktopPane1):
myDialog d = new myDialog();
d.setModal(true);
d.setBounds(160, 180, 550, 450);
jDesktopPane1.add(d);
d.setVisible(true);
My Problem is: If the internal frame has JComboBox or PopupMenu, when part of PopupMenu is out of the internal frame's boundry that part don't handle mouse event (you cann't scroll that part).
Any Ideas?
How about using the JOptionPane.showInternalMessageDialog(...):
I am running JDK 1.7.0_21 on Windows 7 x64:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class ModalInternalFrameTest {
private final JDesktopPane desktop = new JDesktopPane();
private final String[] items = new String[] {
"bananas", "pizza", "hot dogs", "ravioli"
};
private final Action openAction = new AbstractAction("open") {
#Override public void actionPerformed(ActionEvent e) {
JComboBox<String> combo = new JComboBox<String>(items);
combo.setEditable(true);
JOptionPane.showInternalMessageDialog(desktop, combo);
System.out.println(combo.getSelectedItem());
}
};
public JComponent makeUI(JFrame frame) {
frame.setJMenuBar(createMenuBar());
JButton button = new JButton(openAction);
button.setMnemonic(KeyEvent.VK_S);
JInternalFrame internal = new JInternalFrame("Button");
internal.getContentPane().add(button);
internal.setBounds(20, 20, 100, 100);
desktop.add(internal);
internal.setVisible(true);
JButton b = new JButton(new AbstractAction("beep") {
#Override public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().beep();
}
});
b.setMnemonic(KeyEvent.VK_B);
JPanel p = new JPanel(new BorderLayout());
p.add(b, BorderLayout.SOUTH);
p.add(desktop);
return p;
}
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Frame");
menu.setMnemonic(KeyEvent.VK_F);
menuBar.add(menu);
JMenuItem menuItem = new JMenuItem(openAction);
menuItem.setMnemonic(KeyEvent.VK_1);
menuItem.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
menu.add(menuItem);
return menuBar;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ModalInternalFrameTest().makeUI(f));
f.setSize(640, 480);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
There are three types of popups:
light weight
medium weight
heavy weight
The only one that works in a modal state is the heavy weight popup. The "offical" way to change the popup's weight is through the setLightWeightPopupEnabled(boolean aFlag) method.
If you set it false the popup will be medium weight when it's inside the application frame and heavy weight when exceeds the frame bounds.
To force heavy weight, you have to use a client property called javax.swing.ClientPropertyKey.PopupFactory_FORCE_HEAVYWEIGHT_POPUP. With this property you can force popups (or every popup in a container) to be heavy weight. But the only way to access it is through reflection because it's private.
Here is an example code:
try {
Class<?> enumElement = Class.forName("javax.swing.ClientPropertyKey");
Object[] constants = enumElement.getEnumConstants();
putClientProperty(constants[3], Boolean.TRUE);
}
catch(ClassNotFoundException ex) {}
If you put this inside your modalInternalFrame's contructor, then every popup placed on it will be heavy weight.

How can I make dual-lines in a JTabbedPane

I'm not sure how much of sense my title does but since you are suppose to have somewhat of a good title this was the best I came up with, so what I actually mean is...
Let's say in theory I've got 10 tabs, and instead of having them all compressed together in 1 line I'd like to split them in 2, so I'd have 5 tabs on the upper side and 5 on the lower.
Example pic:
not clear your question, but there are basic methods for Tabs in the JTabbedPane
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
public class TabComponentsDemo extends JFrame {
private static final long serialVersionUID = 1L;
private final int tabNumber = 15;
private final JTabbedPane pane = new JTabbedPane();
private JMenuItem tabComponentsItem;
private JMenuItem scrollLayoutItem;
public TabComponentsDemo(String title) {
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initMenu();
add(pane);
}
public void runTest() {
pane.removeAll();
for (int i = 0; i < tabNumber; i++) {
String title = "Tab " + i;
pane.add(title, new JLabel(title));
//initTabComponent(i);
}
tabComponentsItem.setSelected(true);
pane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
scrollLayoutItem.setSelected(false);
setSize(new Dimension(400, 200));
setLocationRelativeTo(null);
setVisible(true);
}
/*private void initTabComponent(int i) {
pane.setTabComponentAt(i, new ButtonTabComponent(pane));
}*/
private void initMenu() {//Setting menu
JMenuBar menuBar = new JMenuBar();//create Options menu
tabComponentsItem = new JCheckBoxMenuItem("Use TabComponents", true);
tabComponentsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.ALT_MASK));
tabComponentsItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < pane.getTabCount(); i++) {
if (tabComponentsItem.isSelected()) {
//initTabComponent(i);
} else {
pane.setTabComponentAt(i, null);
}
}
}
});
scrollLayoutItem = new JCheckBoxMenuItem("Set ScrollLayout");
scrollLayoutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.ALT_MASK));
scrollLayoutItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (pane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) {
pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
} else {
pane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
}
}
});
JMenuItem resetItem = new JMenuItem("Reset JTabbedPane");
resetItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_MASK));
resetItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
runTest();
}
});
JMenu optionsMenu = new JMenu("Options");
optionsMenu.add(tabComponentsItem);
optionsMenu.add(scrollLayoutItem);
optionsMenu.add(resetItem);
menuBar.add(optionsMenu);
setJMenuBar(menuBar);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
UIManager.put("swing.boldMetal", Boolean.FALSE);
new TabComponentsDemo("TabComponentsDemo").runTest();
}
});
}
}
If you want them all displayed in a single row (which I think is what the latter part of your description indicates...), you'll want to set the tab layout policy to JTabbedPane.SCROLL_TAB_LAYOUT.
Here's an example with an image.

Toggle read-only in Java

Is there a way to toggle a read-only mode so when you click any object in your window it simply returns what you clicked, ignoring the object's usual event handling? IE, while in this "read-only" mode, if you click on a Button, it simply returns the button, not actually pressing the button. Then I could do something like:
if ("thing pressed" == button) "do this";
else if ("thing pressed" == panel) "do that";
else "do nothing";
Here's my code, its a frame with 3 colored boxes. Clicking the 2nd box, the 3rd box, or the background will display a message. Clicking box 1 does nothing. I like using new mouse adapters so I want to do it this way.
Now what I want is when you click box 1, box 1 is treated as selected (if that helps you get the picture). Then if you click anywhere, including box 1 again, box 1 is deselected and nothing else (meaning that box 2, box 3. or the background's message will display). At that time, only if box 2 or 3 were clicked, they will still not display their normal message but a different message would be displayed.
I'm very sorry if I come off a little short.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Labels {
public static void main(String[] args) {
new Labels();
}
Square l1, l2, l3;
public Labels() {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
l1 = new Square();
l2 = new Square();
l3 = new Square();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(120, 150);
frame.setResizable(false);
panel.setVisible(true);
panel.setLayout(null);
l1.setLocation(5, 5);
l2.setLocation(5, 60);
l3.setLocation(60, 5);
l2.setColor("yellow");
l3.setColor("black");
l1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
//do nothing
}
});
l2.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pushed label 2");
}
});
l3.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pushed label 3");
}
});
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("pushed background");
}
});
frame.add(panel);
panel.add(l1);
panel.add(l2);
panel.add(l3);
}
class Square extends JLabel{
Color color = Color.blue;
public Square() {
// TODO Auto-generated constructor stub\
setVisible(true);
setSize(50,50);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(color);
g.fillRect(0, 0, 50, 50);
}
public void setColor(String color){
if (color == "white") this.color = Color.white;
else if (color == "black") this.color = Color.black;
else if (color == "yellow") this.color = Color.yellow;
else {
System.out.println("Invalid color");
return;
}
repaint();
}
}
}
Don't disable anything. Simply change the state of your class, perhaps by using a few boolean flag variables/fields and change these flags depending on what is pressed.
So have boolean fields called label1PressedLast, label2PressedLast, and label3PressedLast or something similar, and when a label is pressed, check the states of all other flags and have your program's behavior change depending on the state of these flags and the label that was just pressed. Then set all flags to false except for the one corresponding to the label that was just pressed.
For example, this little program reacts only if the first and then the third JLabel have been pressed:
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
public class FlagEg extends JPanel {
private static final int LABEL_COUNT = 3;
private JLabel[] labels = new JLabel[LABEL_COUNT];
private boolean[] flags = new boolean[LABEL_COUNT];
public FlagEg() {
setLayout(new GridLayout(1, 0, 20, 0));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// panel mouse listener
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
inactivateAll();
}
});
MouseListener labelsMouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvt) {
myMousePressed(mouseEvt);
}
};
// create JLabels and add MouseListener
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel("Label " + (i + 1));
labels[i].addMouseListener(labelsMouseListener);
labels[i].setOpaque(true);
labels[i].setBorder(BorderFactory.createLineBorder(Color.black));
add(labels[i]);
}
}
private void inactivateAll() {
for (int i = 0; i < labels.length; i++) {
labels[i].setBackground(null);
flags[i] = false;
}
}
private void myMousePressed(MouseEvent mouseEvt) {
JLabel label = (JLabel) mouseEvt.getSource();
// which label was pressed?
int index = -1;
for (int i = 0; i < labels.length; i++) {
if (label == labels[i]) {
index = i;
}
}
// check if first label and then third pressed:
if (flags[0] && index == 2) {
System.out.println("first and then third label pressed!");
}
// reset all labels and flags to initial state
inactivateAll();
// set pressed label background color and set flag of label just pressed
labels[index].setBackground(Color.pink);
flags[index] = true;
}
private static void createAndShowGui() {
FlagEg mainPanel = new FlagEg();
JFrame frame = new JFrame("Flag Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Logic iteration two: only label 1 is the "primer" JLabel. This is actually easier to implement, because now you only need one boolean flag, that representing label 1 being pressed:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class FlagEg2 extends JPanel {
private static final int LABEL_COUNT = 3;
private JLabel[] labels = new JLabel[LABEL_COUNT];
private boolean label1Flag = false;
public FlagEg2() {
setLayout(new GridLayout(1, 0, 20, 0));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// panel mouse listener
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
inactivateAll();
}
});
MouseListener labelsMouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvt) {
myMousePressed(mouseEvt);
}
};
// create JLabels and add MouseListener
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel("Label " + (i + 1));
labels[i].addMouseListener(labelsMouseListener);
labels[i].setOpaque(true);
labels[i].setBorder(BorderFactory.createLineBorder(Color.black));
add(labels[i]);
}
}
private void inactivateAll() {
for (int i = 0; i < labels.length; i++) {
labels[i].setBackground(null);
label1Flag = false;
}
}
private void myMousePressed(MouseEvent mouseEvt) {
JLabel label = (JLabel) mouseEvt.getSource();
// which label was pressed?
int index = -1;
for (int i = 0; i < labels.length; i++) {
if (label == labels[i]) {
index = i;
}
}
if (label1Flag) {
if (index == 1) {
System.out.println("Label 1 and label 2 pressed");
} else if (index == 2) {
System.out.println("Label 1 and label 3 pressed");
}
}
// reset all labels and flags to initial state
inactivateAll();
// if label1, then activate it
if (index == 0) {
labels[0].setBackground(Color.pink);
label1Flag = true;
}
}
private static void createAndShowGui() {
FlagEg2 mainPanel = new FlagEg2();
JFrame frame = new JFrame("Flag Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
package javaapplication6;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* #author Jan Vorcak <vorcak#mail.muni.cz>
*/
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
MouseListener listener = new MouseAdapter() {
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if(e.getComponent() instanceof JLabel) {
count++;
if (count >= 2) {
System.out.println("clicked 2 times on labels");
count = 0;
}
} else {
count = 0;
}
}
};
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel l1 = new JLabel("Label 1");
JLabel l2 = new JLabel("Label 2");
JLabel l3 = new JLabel("Label 3");
l1.addMouseListener(listener);
l2.addMouseListener(listener);
l3.addMouseListener(listener);
frame.addMouseListener(listener); // or panel.addMouseListener(listener);
panel.add(l1);
panel.add(l2);
panel.add(l3);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
You could want to create a listener that do the job for using the putClientProperty method of JComponent.
public class JComponentClickCountListener extends MouseAdapter {
private final Integer ONE = 1;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getComponent() instanceof JComponent) {
JComponent jComponent = (JComponent) e.getComponent();
Object property = jComponent.getClientProperty(JComponentClickCountListener.class);
if (property instanceof Number) {
property = ONE + ((Number) property).intValue();
}
else {
property = ONE;
}
jComponent.putClientProperty(JComponentClickCountListener.class, property);
}
}
}
Then in your code you can decide to have a single instace of that class for all of your components or create a new one each time.
This could give you the advantage of using the propertyChangeListener for future actions.
PS.
The code example do not represent all logic for OP question but i could by used as solid base. Later on i will try to update it. To cover that.
EDIT2:
I think that you should separate the logic, of selection and action over selected items. Then the task is divided into two tasks. First is the possibility to store the information about it state, clicked active, clicked again inactive. The second tasks it to operate on that status when a jComponent status was changed.
This is an simple example that i wrote, the functionality is to highlight the background of labels when the are selected and remove it when it was clicked again or the panel was clicked remove all selections.
This example is divided to three elements Enum, Iterface and class that manage the logic of selection
Enum - we store the possible statuses and a property key.
public enum JComponentActivationStatus {
NONE,
ACTIVE,
INACTIVE;
public static final String PROPERTY_KEY = JComponentActivationStatus.class.getCanonicalName();
}
Interface - provide a delegate for action to be taken when jcomponenet status change.
public abstract interface JComponenetActivationStatusChangeAction<T extends JComponent> {
public abstract void onActivation(T object);
public abstract void onDeactivation(T object);
}
Class - This class mange the status logic of jcomponents.
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
public class JComponenetActivationManager {
public static <T extends JComponent> T addMouseStatusControl(T jComponent) {
jComponent.addMouseListener(new JComponentMouseStatusModyfier());
return jComponent;
}
public static <T extends JComponent> T addActivationStatusChangeAction(T jComponenet, JComponenetActivationStatusChangeAction<T> statusChangeAction) {
jComponenet.addPropertyChangeListener(craeteJCompositeActivationStatusChangeListener(statusChangeAction));
return jComponenet;
}
public static <T extends JComponent> PropertyChangeListener craeteJCompositeActivationStatusChangeListener(JComponenetActivationStatusChangeAction<T> action) {
return new JComponentStatusPropertyChangeListener<T>(action);
}
/**
* Class that set the status for the JComponet after doubClicl
*/
private final static class JComponentMouseStatusModyfier extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
if(e.getComponent() instanceof JComponent) {
JComponent jComponent = (JComponent) e.getComponent();
Object propertyValue = jComponent.getClientProperty(JComponentActivationStatus.PROPERTY_KEY);
if(JComponentActivationStatus.ACTIVE.equals(propertyValue)) { //We check that the ACTIVE status is already selected, if so we inactive.
propertyValue = JComponentActivationStatus.INACTIVE; //If so we inactive it.
} else {
propertyValue = JComponentActivationStatus.ACTIVE; // Otherwise we set it as active
}
jComponent.putClientProperty(JComponentActivationStatus.PROPERTY_KEY, propertyValue); // We use the property key form status
}
}
}
/**
* Help class that fire the actions after status is changed
*/
private static final class JComponentStatusPropertyChangeListener<T extends JComponent> implements PropertyChangeListener {
private final JComponenetActivationStatusChangeAction<T> statusChangeAction;
/**
*
*/
public JComponentStatusPropertyChangeListener(JComponenetActivationStatusChangeAction<T> statusChangeAction) {
if(statusChangeAction == null) {
throw new IllegalArgumentException("action can not be null at this point");
}
this.statusChangeAction = statusChangeAction;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if(JComponentActivationStatus.PROPERTY_KEY.equals(evt.getPropertyName())) {
if(JComponentActivationStatus.ACTIVE.equals(evt.getNewValue())) {
statusChangeAction.onActivation((T) evt.getSource());
}
if(JComponentActivationStatus.INACTIVE.equals(evt.getNewValue())){
statusChangeAction.onDeactivation((T) evt.getSource());
}
}
}
}
}
That class contain two public static method, that allow the developer to add the functionality to mange status to any jComponent object, add subscribe the action handler if any change occur.
At the end we have the main method that test our solution
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel l1 = new JLabel("Label 1");
JLabel l2 = new JLabel("Label 2");
JLabel l3 = new JLabel("Label 3");
panel.setBackground(Color.CYAN);
addMouseStatusControl(panel);
addMouseStatusControl(l1);
addMouseStatusControl(l2);
addMouseStatusControl(l3);
JComponenetActivationStatusChangeAction<JLabel> activeBackground = new JComponenetActivationStatusChangeAction<JLabel>() {
#Override
public void onActivation(JLabel object) {
object.setOpaque(true);
object.setBackground(Color.YELLOW);
}
#Override
public void onDeactivation(JLabel object) {
object.setOpaque(false);
object.setBackground(object.getParent().getBackground());
}
};
JComponenetActivationStatusChangeAction<JPanel> deactivateChildrens = new JComponenetActivationStatusChangeAction<JPanel>() {
#Override
public void onDeactivation(JPanel object) {
}
#Override
public void onActivation(JPanel object) {
for(Component component : object.getComponents()) {
if(component instanceof JComponent) {
((JComponent) component).putClientProperty(JComponentActivationStatus.PROPERTY_KEY,JComponentActivationStatus.INACTIVE);
}
}
}
};
addActivationStatusChangeAction(l1, activeBackground);
addActivationStatusChangeAction(l2, activeBackground);
addActivationStatusChangeAction(l3, activeBackground);
addActivationStatusChangeAction(panel, deactivateChildrens);
panel.add(l1);
panel.add(l2);
panel.add(l3);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
The solution is very flexible and extendable in case you will need to add more labels.
The example is for those that want to learn. Any comment would be appreciate.

Categories