3

I want my GUI to make some checks when a JOptionPane appears. Because I can't find any other way, I though I can do those each time the application window loses focus(its just checking a string). For that reason I added the following code on my JFrame:

appFrame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowLostFocus(WindowEvent e) {
                System.out.println("Focus Lost");

            }
            @Override
            public void windowClosing(WindowEvent e) {
                //some other stuff here that work
            }
});

The window closing listener works fine. Although when the JFrame isn't focused nothing happens. Shouldn't "Focus Lost" be printed each time I switch from JFrame to some other window? Also, will this method be triggered when a JOptionPane is shown?

Giannis
  • 5,286
  • 15
  • 58
  • 113
  • This seems a backwards way of doing this. What triggers the optionPane to show? Why not call this code in this trigger? – Hovercraft Full Of Eels Apr 17 '12 at 20:52
  • The JOptionPane is shown when a string duplication is found on some other part of the application (doesn't have anything to do with GUI). From that JOptionPane a String changes and that change needs to be reflected on the JFrame title. Since these classes don't communicate I though the window listener would be an easy way to check for that change. – Giannis Apr 17 '12 at 20:55
  • Maybe stating the OS would be useful here... I am not sure if Windows sends a message to a window to tell that it no longer is active (or if the JVM processes it) – SJuan76 Apr 17 '12 at 20:57
  • Its on OSX that doesn't work but Id be more interested for it to work on Windows. – Giannis Apr 17 '12 at 20:58
  • *"From that JOptionPane a String changes"* Make the target frame the owner/component of the `JOptionPane` and change the string immediately after it disappears. BTW - your problem is still unclear to me & possibly other people. It might help to explain this in more specific terms like, describing what is in the string, the feature this supports.. – Andrew Thompson Apr 18 '12 at 04:22
  • the reason the focus event is not being received is obvious. see my answer below. – pstanton Apr 18 '12 at 06:34

3 Answers3

6

The key to me is that you want a change in the GUI triggered by a change of a String variable. The best way I see to solve this is to make the String variable a bound property by using PropertyChangeListenerSupport. This way you can have the GUI attach a PropertyChangeListener to the class that holds the String variable and then be notified when it changes allowing you to update the GUI appropriately.

If you go this route, consider giving the observed class a SwingPropertyChangeSupport field so that the listeners will be notified on the Swing event thread and hopefully avoid any Swing concurrency issues.

Here's a brief example:

import java.awt.Dimension;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class ShowPropertyChangeSupport {
   @SuppressWarnings("serial")
   private static void createAndShowGui() {
      final MainGUI mainGui = new MainGUI("Title");
      final ObservedClass observedClass = new ObservedClass();
      observedClass.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if (pcEvt.getPropertyName().equals(ObservedClass.BOUND_PROPERTY)) {
               mainGui.setTitle(pcEvt.getNewValue().toString());
            }
         }
      });

      mainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      mainGui.pack();
      mainGui.setLocationRelativeTo(null);
      mainGui.setVisible(true);

      int timerDelay = 6000; // every 6 seconds
      new Timer(timerDelay, new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent arg0) {
            String result = JOptionPane.showInputDialog(mainGui,
                  "Please enter a String", "Set GUI title", JOptionPane.PLAIN_MESSAGE);
            if (result != null) {
               observedClass.setBoundProperty(result);
            }
         }
      }){{setInitialDelay(1000);}}.start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

// ** note that I don't like extending JFrame,
// but will do this for sake of example simplicity
class MainGUI extends JFrame {
   public MainGUI(String title) {
      super(title);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(400, 300);
   }

}

class ObservedClass {
   public static final String BOUND_PROPERTY = "bound property";
   private String boundProperty = "";
   private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
         this);

   public SwingPropertyChangeSupport getSpcSupport() {
      return spcSupport;
   }

   public void setSpcSupport(SwingPropertyChangeSupport spcSupport) {
      this.spcSupport = spcSupport;
   }

   public String getBoundProperty() {
      return boundProperty;
   }

   public void setBoundProperty(String boundProperty) {
      String oldValue = this.boundProperty;
      String newValue = boundProperty;
      this.boundProperty = newValue;
      spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      spcSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      spcSupport.removePropertyChangeListener(listener);
   }

}

The key to all this in my mind is to use the listener so that the class with the bound property -- the String being listened to -- has no knowledge of the GUI, the listener, and the GUI, likewise has no knowledge of the class with the bound property. They are fully decoupled.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 1
    Yes, or more generally the application needs its own event queue -- a global place to register listeners and dispatch events. There are many ways of getting that, don't know if any specific is common. – Marko Topolnik Apr 17 '12 at 21:09
  • Thanks a lot for this piece of code its really interesting. I ended up doing something similar to observer pattern. Im sure I will need this in the future tho. – Giannis Apr 18 '12 at 11:36
  • @Giannis: that's all this is -- the observer pattern, but with a little extra window dressing, and so you're basically doing what I suggest. Good luck with your project! – Hovercraft Full Of Eels Apr 18 '12 at 14:29
  • @HFOE: Thanks for this code. I tried to modify it by creating a "setStringForDisplay(String s)" type method which displayed a String within a JPanel. I then tried firing that instead of "setTitle" and it didn't update it in the GUI. I'm not following something properly obviously but will keep tinkering. – Robert Aug 05 '12 at 20:55
5

I'm not going to go into why you are doing what you are doing, but it is not working as you expect for the following reason:

WindowAdapter is a convenience class so you can create one listener and register it for multiple types of events. You have only registered it for one set of events, you need to also register it for focus events via: Window.addWindowFocusListener()

WindowAdapter adapter = new WindowAdapter() {
        @Override
        public void windowLostFocus(WindowEvent e) {
            System.out.println("Focus Lost");
        }
        @Override
        public void windowClosing(WindowEvent e) {
            //some other stuff here that work
        }
    };
appFrame.addWindowListener(adapter);
appFrame.addWindowFocusListener(adapter);
pstanton
  • 35,033
  • 24
  • 126
  • 168
2

1) JOptionPane / modal JDialog have got modality issue, but modality could be advantage if all containers have got own owner, for real workaround you need to know (I'll talking about how can I do test that)

2) Please, with due respect, I don't know why you needed that, for why reasons I need to know about that, there is about business rules, you always need to know ...., and if is done on EDT

mKorbel
  • 109,525
  • 20
  • 134
  • 319