I am writing an application in Java (I will admit I am a novice Java programmer, but experienced in others) and have a need to be able to set X11 window properties explicitly. Specifically, I need to be able to set the _NET_WM_STATE property to let the window manager know a child window is modal.
The definition of this property can be seen here:
http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#id2578152
This application has to be written in Java and will be running solely on a Linux/X11 platform so portability does not even have to be considered. When running on a standard Ubuntu PC the modality of the window is properly handled.
The window manager running on the platform where this application is expected to run does things above and beyond normal modality and expects the _NET_WM_STATE window property to be set.
From my research it seems that Java does not want to set these properties. I am guessing that this is so that it can handle the modality within the Java framework so that it will be have the same across platforms.
I have used xwininfo and xprop to see what kinds of things Java sets on child windows when they are modal vs non-modal. It seems there is basically nothing in the X11 window properties that is different between the two types of windows.
Here is some sample code of how the child window is created:
public class ChildDialog extends JDialog {
static final long serialVersionUID = 2;
public ChildDialog(JFrame frame, String title, boolean modal) {
super(frame, title, modal);
JPanel dgrid = new JPanel();
dgrid.setLayout(new BoxLayout(dgrid, BoxLayout.Y_AXIS));
dgrid.setBorder(BorderFactory.createLineBorder(Color.red, 3));
JLabel label = new JLabel(title);
JTextArea desc = new JTextArea("This is a child window.");
desc.setBackground(label.getBackground());
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
dgrid.add(label);
dgrid.add(desc);
dgrid.add(close);
this.setSize(300, 200);
this.add(dgrid);
this.setVisible(true);
}
}
This class is then instantiated when a button on the main window is clicked. Like this:
JButton spawnChild = new JButton("Child Window");
spawnChild.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new ChildDialog(getSelf(), "Child Window", false);
}
});
bgrid.add(spawnChild);
JButton spawnModalChild = new JButton("Child Window (Modal)");
spawnModalChild.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new ChildDialog(getSelf(), "Child Window (Modal)", true);
}
});
bgrid.add(spawnModalChild);
Any ideas?
Related
I have a bug with modal dialogs I'm running into with Java 8u151/8u152 that wasn't an issue in 8u112, and I'm not sure if I'm doing something inherently wrong, or if this is a JRE bug. This only affects Mac OS as far as I can tell (I'm running OS 10.12.6), so although my question isn't Mac-specific, you won't be able to observe this bug if you're not a Mac user.
I have a modal dialog created with the main JFrame as its parent. I then create a new modal dialog that I create from dialog #1. Is it wrong to set dialog #2's parent to the main JFrame as well? Or must it be dialog #2?
Asked another way: If both dialogs share the same parent (the main JFrame), but dialog #2 was created last, is it reasonable to expect that dialog #2 should be on top, or is the behaviour undefined?
With 8u112, dialog #2 was focused and on top. With 8u151, dialog #2 is focused but behind. If I click on either dialog, dialog #2 grabs focus and seems to behave appropriately for the most part. However, sometimes dialog #2 will remain on top of other applications (I believe this happens if I click on the main window, then back on the modal dialog... this part is definitely a bug).
Note: for Java 8u121 through (I think) 8u144, there were additional problems with modal dialogs that were supposed to have been fixed in 8u152 (I logged a bug report and checked an early access build several months ago, and things really were fixed back then).
Some sample code to illustrate what I'm doing:
import javax.swing.*;
import java.awt.*;
public class Main extends JFrame {
MyPanel panel;
public Main() {
setTitle("This is a frame");
setSize(300, 200);
panel = new MyPanel(this);
add(panel);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
Main frame = new Main();
frame.pack();
frame.setVisible(true);
});
}
private static class MyPanel extends JPanel {
int dialogCounter = 1;
final JFrame theParent;
public MyPanel(JFrame parent) {
super();
theParent = parent;
setPreferredSize(new Dimension(300, 200));
JButton button = new JButton("Press the button");
button.addActionListener(e -> showDialog(theParent));
add(button);
}
private void showDialog(Frame parent) {
JDialog dialog = new JDialog(parent, "This is dialog " + dialogCounter, true);
setupDialog(dialog);
}
private void setupDialog(JDialog dialog) {
JPanel dialogPanel = new JPanel();
dialogPanel.setPreferredSize(new Dimension(300, 200));
dialogPanel.add(new JLabel("Current dialog count: " + dialogCounter++));
JButton button = new JButton("Open a new modal dialog");
button.addActionListener(e -> showDialog(theParent));
dialogPanel.add(button);
dialog.add(dialogPanel);
dialog.pack();
dialog.setVisible(true);
}
}
}
This is a bug introduced in 8u151. It has already been fixed and should be backported to the next OpenJDK 8 update release.
Below is a workaround you could use:
dialog.addWindowListener(new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
dialog.removeWindowListener(this);
dialog.toFront();
}
});
I have two Jframes where frame1 has some text fields and when a button on frame1 is clicked, I open another JFrame which contains a search box and a JTable containing search results.
When I click on a result row on JTable, I want that particular values to be reflected in the frame1 text fields.
I tried passing the JFrame1's object as a parameter but I have no clear idea on how to achieve this.
Any help would be highly appreciated.
Thanks
First of all, your program design seems a bit off, as if you are using a JFrame for one of your windows where you should in fact be using a JDialog since it sounds as if one window should be dependent upon the other.
But regardless, you pass references of GUI objects the same as you would standard non-GUI Java code. If one window opens the other (the second often being the dialog), then the first window usually already holds a reference to the second window and can call methods off of it. The key often is when to have the first window call the second's methods to get its state. If the second is a modal dialog, then the when is easy -- immediately after the dialog returns which will be in the code immediately after you set the second dialog visible. If it is not a modal dialog, then you probably want to use a listener of some sort to know when to extract the information.
Having said this, the details will all depend on your program structure, and you'll need to tell us more about this if you want more specific help.
For a simple example that has one window open another, allows the user to enter text into the dialog windows JTextField, and then places the text in the first window's JTextField, please have a look at this:
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class WindowCommunication {
private static void createAndShowUI() {
JFrame frame = new JFrame("WindowCommunication");
frame.getContentPane().add(new MyFramePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// let's be sure to start Swing on the Swing event thread
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class MyFramePanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton openDialogeBtn = new JButton("Open Dialog");
// here my main gui has a reference to the JDialog and to the
// MyDialogPanel which is displayed in the JDialog
private MyDialogPanel dialogPanel = new MyDialogPanel();
private JDialog dialog;
public MyFramePanel() {
openDialogeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openTableAction();
}
});
field.setEditable(false);
field.setFocusable(false);
add(field);
add(openDialogeBtn);
}
private void openTableAction() {
// lazy creation of the JDialog
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
dialog = new JDialog(win, "My Dialog",
ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
}
dialog.setVisible(true); // here the modal dialog takes over
// this line starts *after* the modal dialog has been disposed
// **** here's the key where I get the String from JTextField in the GUI held
// by the JDialog and put it into this GUI's JTextField.
field.setText(dialogPanel.getFieldText());
}
}
class MyDialogPanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton okButton = new JButton("OK");
public MyDialogPanel() {
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
okButtonAction();
}
});
add(field);
add(okButton);
}
// to allow outside classes to get the text held by the JTextField
public String getFieldText() {
return field.getText();
}
// This button's action is simply to dispose of the JDialog.
private void okButtonAction() {
// win is here the JDialog that holds this JPanel, but it could be a JFrame or
// any other top-level container that is holding this JPanel
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
win.dispose();
}
}
}
You'd do a very similar technique to get information out of a JTable.
And again, if this information doesn't help you, then please tell us more about your program including showing us some of your code. The best code to show is a small compilable example, an SSCCE similar to what I've posted above.
I have two Jframes where frame1 has some text fields and when a button on frame1 is clicked, I open another JFrame which contains a search box and a JTable containing search results.
When I click on a result row on JTable, I want that particular values to be reflected in the frame1 text fields.
I tried passing the JFrame1's object as a parameter but I have no clear idea on how to achieve this.
Any help would be highly appreciated.
Thanks
First of all, your program design seems a bit off, as if you are using a JFrame for one of your windows where you should in fact be using a JDialog since it sounds as if one window should be dependent upon the other.
But regardless, you pass references of GUI objects the same as you would standard non-GUI Java code. If one window opens the other (the second often being the dialog), then the first window usually already holds a reference to the second window and can call methods off of it. The key often is when to have the first window call the second's methods to get its state. If the second is a modal dialog, then the when is easy -- immediately after the dialog returns which will be in the code immediately after you set the second dialog visible. If it is not a modal dialog, then you probably want to use a listener of some sort to know when to extract the information.
Having said this, the details will all depend on your program structure, and you'll need to tell us more about this if you want more specific help.
For a simple example that has one window open another, allows the user to enter text into the dialog windows JTextField, and then places the text in the first window's JTextField, please have a look at this:
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class WindowCommunication {
private static void createAndShowUI() {
JFrame frame = new JFrame("WindowCommunication");
frame.getContentPane().add(new MyFramePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// let's be sure to start Swing on the Swing event thread
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class MyFramePanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton openDialogeBtn = new JButton("Open Dialog");
// here my main gui has a reference to the JDialog and to the
// MyDialogPanel which is displayed in the JDialog
private MyDialogPanel dialogPanel = new MyDialogPanel();
private JDialog dialog;
public MyFramePanel() {
openDialogeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openTableAction();
}
});
field.setEditable(false);
field.setFocusable(false);
add(field);
add(openDialogeBtn);
}
private void openTableAction() {
// lazy creation of the JDialog
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
dialog = new JDialog(win, "My Dialog",
ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
}
dialog.setVisible(true); // here the modal dialog takes over
// this line starts *after* the modal dialog has been disposed
// **** here's the key where I get the String from JTextField in the GUI held
// by the JDialog and put it into this GUI's JTextField.
field.setText(dialogPanel.getFieldText());
}
}
class MyDialogPanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton okButton = new JButton("OK");
public MyDialogPanel() {
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
okButtonAction();
}
});
add(field);
add(okButton);
}
// to allow outside classes to get the text held by the JTextField
public String getFieldText() {
return field.getText();
}
// This button's action is simply to dispose of the JDialog.
private void okButtonAction() {
// win is here the JDialog that holds this JPanel, but it could be a JFrame or
// any other top-level container that is holding this JPanel
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
win.dispose();
}
}
}
You'd do a very similar technique to get information out of a JTable.
And again, if this information doesn't help you, then please tell us more about your program including showing us some of your code. The best code to show is a small compilable example, an SSCCE similar to what I've posted above.
I wrote the following code to have a JPopupMenu that allows multiple selection of different items.
The problem is that, as soon as the mouse enters one of the displayed JCheckboxMenuItems, the JPopupMenu gets closed. This issue doesn't occur if I replace JCheckboxMenuItem with, for example, JLabel but, for sure, JLabel doesn't work for my purpose.
Any idea of what could trigger this issue? Any idea of how this problem can be resolved in a better way? I apologize for the newbie question but I'm not a java developer. Thanks in advance for any help.
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedborder(),"Select Layers");
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
for (MyAction layer : layers) {
JCheckBoxMenuItem box = new JCheckBoxMenuItem(layer);
box.setIcon(new SquareIcon(myColor));
panel.add(box);
}
JPopup popup = new JidePopup();
popup.add(panel)
JButton button = new JButton("Layers");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
popup.show(button,0,button.getHeight())
}
});
Thats in the nature of JPopupMenus. They disappear when the invoker component loses the focus. But I found a little trick here.
Create your own class and extend it from JPopupMenu. Then override the setVisible method that it will only forward true to the super class and create an own method that will setVisible of the super class to false.
public class StayOpenPopup extends JPopupMenu{
public void setVisible(boolean visible){
if(visible == true)
super.setVisible(visible);
}
public void disappear() {
super.setVisible(false);
}
}
Then use it like this in your code
[...]
StayOpenPopup popup = new StayOpenPopup();
popup.add(panel);
[...]
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(popup.isVisible())
popup.disappear();
else popup.show(button,0,button.getHeight());
}
});
Now one click on button will show it. And it will stay visible until next click on Button.
I'm integrating an applet and I need to hack one of the dialog and change its modality.
My problem is I don't know Swing, and my attempts have no effect in practice.
Current implementation:
dialog.setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE);
dialog.repaint();
also tried
dialog.setModal(false);
So there is my question. How can I dynamically change the modality of an existing JDialog ?
don't know what you try to do ...
but maybe you can get something from here
public class Mainz extends JFrame implements ActionListener{
JButton showDialog = new JButton("show dialog");
public Mainz() {
setLayout(new FlowLayout());
showDialog.addActionListener(this);
add(showDialog);
setSize(200, 300);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
new Dialogz(this, false);
setEnabled(false);
}
public static void main(String[]args){
new Mainz();
}
}
class Dialogz extends JDialog{
JButton close = new JButton("close");
public Dialogz(JFrame owner,boolean modal) {
super(owner, modal);
setSize(100, 200);
add(close);
setLocationRelativeTo(owner);
setVisible(true);
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae){
closez();
}
});
}
void closez(){
setModalExclusionType(ModalExclusionType.APPLICATION_EXCLUDE);
System.out.println("modal exclusion befor = "+getModalExclusionType());
setModalExclusionType(ModalExclusionType.NO_EXCLUDE);
System.out.println("modal exclusion after = "+getModalExclusionType());
System.out.println("modality before ="+getModalityType());
setModal(true);
System.out.println("modality after ="+getModalityType());
getOwner().setEnabled(true);
Dialogz.this.dispose();
}
}
A hack for a hack:
you can change the modality of an existing dialog by calling the private method:
java.awt.Dialog.hideAndDisposePreHandler();
To call this private method - as an example:
private void executeMethod(final Class<?> clazz, final String methodName, final Object instance)
{
final Method method =
Iterables.getOnlyElement(Iterables.filter(
Arrays.asList(clazz.getDeclaredMethods()), new Predicate<Method>()
{
public boolean apply(final Method method)
{
return method.getName().equals(methodName);
}
}));
method.setAccessible(true);
try
{
method.invoke(instance);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw Throwables.propagate(e);
}
}
(This code requires Guava)
And finally call it:
final Dialog myDialog = ...;
executeMethod(Dialog.class, "hideAndDisposePreHandler", myDialog);
To change whether your dialog is modal or modeless, use setModalityType method.
When you call setModal(true), modality type is the same as calling setModalityType(Dialog.DEFAULT_MODALITY_TYPE). The default value is ModalityType.APPLICATION_MODAL.
When you call setModal(false), modality type is set to ModalityType.MODELESS.
The dialog should be not visible when you change its modality. Otherwise, it will not take effect until the dialog is hidden and then shown again.
Moreover the dialog itself has to be programmed to support different modality modes.
When you use a modal dialog, interaction with the owner window (and possibly with other application windows) is blocked. So you show the dialog, dialog.setVisible(true) and this method does not return until the dialog is closed. Then you use the data from the dialog.
A typical modal dialog is Open File: the application can't proceed until it knows which file to load.
In case of modeless dialog, the method dialog.setVisible(true) returns immediately (after showing the dialog on the screen). Pressing buttons in the dialog usually has some effect on other windows and dialogs. And you can interact with other windows of the application while the dialog is shown.
For example, a typical Find dialog selects a search string in the main window. You can return to the main window, and change text, then click Find again and so on.
If you need more help, I can show you a working sample with dialog which works in both modes: modal and modeless.
I guess that you haven't acquired the AWTPermission.toolkitModality permission for your applet.
Another problem could be that the exclusion type isn't supported on your platform -- you can check this with Toolkit.isModalExclusionTypeSupported(java.awt.Dialog.ModalExclusionType).