Welcome to my site

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ed ut perspiciatis unde omnis iste.

" Small is the number of people who see with their own eyes, think with their own minds and feel with their own hearts " (Albert Hermann Einstein)

= contact me at ndre@engineer.com or click on my facebook badge =

Tampilkan postingan dengan label JAVA. Tampilkan semua postingan
Tampilkan postingan dengan label JAVA. Tampilkan semua postingan

Writing GUI Layout Managers

It is actually increadibly straightforward writing your own layout manager. This layout manager took me one lunch time, i.e. less than 1 hour. Permanent employees at that company get free lunches, but as a contractor I had to pay, so I programmed instead of ate in those days. Nowadays I simply talk about work during lunch and charge the time ;-)
Seriously, it really is easy. When I tell people that I've written my own LayoutManager to do the layout for me they get all boggle-eyed and start holding up crucifixes, garlic or search for the silver bullet. (Those in the know, know that there is no silver bullet for software development.)
//: WildLayoutManager.java
import java.awt.*;

public class WildLayoutManager implements LayoutManager {
  // these are the constraints possible with the WildLayoutManager
  public static final String LEFT = "Left";
  public static final String RIGHT = "Right";
  public static final String MIDDLE = "Middle";

  // We keep handles to three components, left, right and middle
  private Component left;
  private Component right;
  private Component middle;

  // we need to be able to add components.  if two components are added
  // with the same constraint we keep the last one
  public void addLayoutComponent(String name, Component comp) {
    if (LEFT.equals(name)) {
      left = comp;
    } else if (RIGHT.equals(name)) {
      right = comp;
    } else if (MIDDLE.equals(name)) {
      middle = comp;
    } else {
      throw new IllegalArgumentException(
        "cannot add to layout: unknown constraint: " + name);
    }
  }

  // here we remove the component - first find it!
  public void removeLayoutComponent(Component comp) {
    if (comp == left) {
      left = null;
    } else if (comp == right) {
      right = null;
    } else if (comp == middle) {
      middle = null;
    }
  }

  // The minimum dimension we're happy with is the preferred size
  // this could be more fancy by using the minimum sizes of each component
  public Dimension minimumLayoutSize(Container parent) {
    return preferredLayoutSize(parent);
  }

  // Here we work out the preferred size of the component, which is used
  // by methods such as pack() to work out how big the window should be
  public Dimension preferredLayoutSize(Container parent) {
    Dimension dim = new Dimension(0, 0);
    // get widest preferred width for left && right
    // get highest preferred height for left && right
    // add preferred width of middle
    int widestWidth = 0;
    int highestHeight = 0;
    if ((left != null) && left.isVisible()) {
      widestWidth = Math.max(widestWidth, left.getPreferredSize().width);
      highestHeight =
        Math.max(highestHeight, left.getPreferredSize().height);
    }
    if ((right != null) && right.isVisible()) {
      widestWidth = Math.max(widestWidth, right.getPreferredSize().width);
      highestHeight =
        Math.max(highestHeight, right.getPreferredSize().height);
    }
    dim.width = widestWidth * 2;
    dim.height = highestHeight;
    if ((middle != null) && middle.isVisible()) {
      dim.width += middle.getPreferredSize().width;
      dim.height = Math.max(dim.height, middle.getPreferredSize().height);
    }

    Insets insets = parent.getInsets();
    dim.width += insets.left + insets.right;
    dim.height += insets.top + insets.bottom;

    return dim;
  }

  // this is the brain of the layout manager, albeit rather small.
  // I told you this is straightforward...
  public void layoutContainer(Container target) {
    // these variables hold the position where we can draw components
    // taking into account insets
    Insets insets = target.getInsets();
    int north = insets.top;
    int south = target.getSize().height - insets.bottom;
    int west = insets.left;
    int east = target.getSize().width - insets.right;

    // we first find the width of the left and right components
    int widestWidth = 0;
    if ((left != null) && left.isVisible()) {
      widestWidth = Math.max(widestWidth, left.getPreferredSize().width);
    }
    if ((right != null) && right.isVisible()) {
      widestWidth = Math.max(widestWidth, right.getPreferredSize().width);
    }
    if ((middle != null) && middle.isVisible()) {
      widestWidth = Math.max(widestWidth,
        (east - west - middle.getPreferredSize().width) / 2);
    }

    // next we set the size of the left component equal to the widest width
    // and whole height, and we set the bounds from North-West corner
    if ((left != null) && left.isVisible()) {
      left.setSize(widestWidth, south - north);
      left.setBounds(west, north, widestWidth, south - north);
    }
    // next we set the size of right component equal to the widest width
    // and whole height, and we set the bounds from North-East corner
    if ((right != null) && right.isVisible()) {
      right.setSize(widestWidth, south - north);
      right.setBounds(east-widestWidth, north, widestWidth, south - north);
    }
    // lastly we set the size of the middle component equals to the
    // remaining width, which should be equal to the middle object's
    // preferred width and we set the height equal to the middle object's
    // preferred height
    if ((middle != null) && middle.isVisible()) {
      middle.setSize(east - west - widestWidth * 2,
        middle.getPreferredSize().height);
      middle.setBounds(
        west+widestWidth,
        north + (south - north - middle.getPreferredSize().height)/2,
        east - west - widestWidth * 2,
        middle.getPreferredSize().height);
    }
  }
}
You see, it really was quite simple. Here is an example frame that tries out the new layout manager:
//: WildLayoutExample.java
import java.awt.*;
import javax.swing.*;

public class WildLayoutExample extends JFrame {
  public WildLayoutExample() {
    super("WildLayoutExample");
    setSize(new Dimension(400, 300));
    getContentPane().setLayout(new WildLayoutManager());
    // construct the left panel
    JPanel leftPanel = new JPanel(new BorderLayout());
    leftPanel.add(new JLabel("Left Label"), BorderLayout.NORTH);
    leftPanel.add(new JTree(), BorderLayout.CENTER);
    // construct the middle panel
    JPanel middlePanel = new JPanel(new GridLayout(0,1,5,5));
    middlePanel.add(new JButton("Add >"), null);
    middlePanel.add(new JButton("<< Remove All"), null);
    // construct the right panel
    JPanel rightPanel = new JPanel(new BorderLayout());
    rightPanel.add(new JLabel("Right Label"), BorderLayout.NORTH);
    rightPanel.add(new JTextArea("jTextArea1"), BorderLayout.CENTER);
    // add the panels to the content pane using our new layout manager
    getContentPane().add(leftPanel, WildLayoutManager.LEFT);
    getContentPane().add(middlePanel, WildLayoutManager.MIDDLE);
    getContentPane().add(rightPanel, WildLayoutManager.RIGHT);
  }
  public static void main(String[] args) {
    WildLayoutExample frame = new WildLayoutExample();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // JDK 1.3 !
    frame.setVisible(true);
  }
}
You could use the idea of custom layout managers to create all sorts of interesting layouts, such as a special form layout for designing forms for your business application. I do not see any particular problems with writing your own layout manager, especially if there is some layout that you want to use quite often. Just don't use absolute layouts, whatever you do!!!
And remember: let's be careful out there!

created by Dr. Heinz M. Kabutz

Setting focus to second component of modal dialog

A few weeks ago I got stumped by a seemingly simple problem. I was trying to write a login dialog that would remember the last username entered and put the focus on the password field if an old username was found. I battled against the tide of Swing, even posted a question to the local Java User Group mailing list, but eventually I performed some obscure tricks to conquer this basic beginner's problem.
---
Warning Advanced:
A problem with dialogs is that they are very often not bound to a parent frame, especially modal dialogs. This is not very good, because if you move to another application and move back to your Java application via the task bar in Windows, you cannot see the dialog. This single "bug" has caused a lot of confusion for users who think their Java application has hung up, but if they ALT+TAB to the application they can see the dialog again. A good solution is to create a frame at position -1000, -1000 and use that as the owner if the dialog does not have an owner. It is also possible to write a class which works out when a new window is shown and maps the title to the frame. This way you can find existing frames given a title. No, I won't tell you in this newsletter how to do that, no space.
---

My LoginDialog looked something like this:
//: LoginDialog.java
import javax.swing.*;
import java.awt.*;
public class LoginDialog extends JDialog {
  private final JTextField userName = new JTextField(8);
  private final JPasswordField password = new JPasswordField(8);
  public LoginDialog(Frame owner) {
    super(owner, "Login Dialog", true);
    getContentPane().setLayout(new GridLayout(0,2,5,5));
    getContentPane().add(new JLabel("Username:"));
    getContentPane().add(userName);
    getContentPane().add(new JLabel("Password:"));
    getContentPane().add(password);
    pack();
    Windows.centerOnScreen(this);
    show();
  }
  public String getUserName() { return userName.getText(); }
  public String getPassword() { return password.getText(); }
  public static void main(String[] args) {
    JFrame owner = new JFrame("Login Dialog");
    owner.setLocation(-1000, -1000);
    owner.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    owner.show();
    new LoginDialog(owner);
  }
}
//: Windows.java
import java.awt.*;
public class Windows {
  public static void centerOnScreen(Window window) {
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    window.setLocation(
      (d.width - window.getSize().width) / 2,
      (d.height - window.getSize().height) / 2);
  }
}
As mentioned before, I wanted my focus to start on the password field, rather than the user name field. So, the obvious place to set the focus is after the call to "centerOnScreen", i.e. change the code to
// ...
  pack();
  centerOnScreen(this);
  password.requestFocus();
  show();
}
// ...
Unfortunately, you can only change the focus to components which are visible on the screen, and since the dialog has not been shown yet, trying to set the focus has no effect.
The obvious solution to this problem is to request the focus after the show() has been called. But, since this is a modal dialog, show will only return once the dialog has been closed, so even though the component is now visible, we will only request focus once we have closed the dialog, which does not help us awefully much.
Again, the seemingly obvious solution to this problem is to call the requestFocus method using SwingUtilities.invokeLater(), but you are not guaranteed that the dialog will then be visible, and if it is not, you again have no effect. You could of course wait for 10 seconds and then request focus, but that would result in a rather awkward user interface.
I posted this problem to a local Java user group and got one response to how this could be solved. But first I will show you my solution, which is terribly obscure, but I could not come up with anything better. Please send me your solutions if they differ from these:

Solutions 1

We want to pass the focus on as soon as we get the focus in the username field. We thus add a focus listener to the userName field, which transfers the focus to the next component when the focusGained method is called. We only want to do that when the dialog is constructed, so when the focusLost method is called we remove the listener again. LoginDialog would now look like this:
//: LoginDialog2.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class LoginDialog2 extends JDialog {
  private final JTextField userName = new JTextField(8);
  private final JPasswordField password = new JPasswordField(8);
  public LoginDialog2(Frame owner) {
    super(owner, "Login Dialog", true);
    getContentPane().setLayout(new GridLayout(0,2,5,5));
    getContentPane().add(new JLabel("Username:"));
    getContentPane().add(userName);
    getContentPane().add(new JLabel("Password:"));
    getContentPane().add(password);
    pack();
    Windows.centerOnScreen(this);
    userName.addFocusListener(new FocusListener() {
      public void focusGained(FocusEvent e) {
        userName.transferFocus();
      }
      public void focusLost(FocusEvent e) {
        userName.removeFocusListener(this); // refers to listener
      }
    });
    show();
  }
  public String getUserName() { return userName.getText(); }
  public String getPassword() { return password.getText(); }
  public static void main(String[] args) {
    JFrame owner = new JFrame("Login Dialog");
    owner.setLocation(-1000, -1000);
    owner.show();
    owner.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    new LoginDialog2(owner);
  }
}
Yes, it is fairly obscure, but so is solution # 2, given to me by my "Bruce Eckel Handson" student, Charl Smit from CCH in South Africa. Thanks Charl.

Solution 2

What we can also do is issue a focus gained event for the password field which will be actualised once the event queue gets a chance.
//: LoginDialog3.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class LoginDialog3 extends JDialog {
  private final JTextField userName = new JTextField(8);
  private final JPasswordField password = new JPasswordField(8);
  public LoginDialog3(Frame owner) {
    super(owner, "Login Dialog", true);
    getContentPane().setLayout(new GridLayout(0,2,5,5));
    getContentPane().add(new JLabel("Username:"));
    getContentPane().add(userName);
    getContentPane().add(new JLabel("Password:"));
    getContentPane().add(password);
    pack();
    Windows.centerOnScreen(this);
    changeFocus(userName, password);
    show();
  }
  private void changeFocus(final Component source,
      final Component target) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        target.dispatchEvent(
          new FocusEvent(source, FocusEvent.FOCUS_GAINED));
      }
    });
  }
  public String getUserName() { return userName.getText(); }
  public String getPassword() { return password.getText(); }
  public static void main(String[] args) {
    JFrame owner = new JFrame("Login Dialog");
    owner.setLocation(-1000, -1000);
    owner.show();
    owner.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    new LoginDialog3(owner);
  }
}
This also works perfectly, but I cannot decide which is more obscure. I suppose the 2nd solution is "better" because we can move the focus changing code out of the class into a general GUI utilities class and do this type of focus changing in a consistent way throughout the project. Also, it is probably easier with the 2nd solution to hop to any component on the screen, rather than just transfer the focus to the next component.
You be the judge. Please let me know if you have a better solution to this problem.
Until next week, when I will look at what happens when you send GUI components over the network, ideas sponsored by Niko Brummer.

created by Dr. Heinz M. Kabutz

Serializing GUI Components Across Network

When Swing came out, I was puzzled by the following warning in the javadocs:
Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. A future release of Swing will provide support for long term persistence.
My thoughts were: No one in their right mind would want to serialize GUI components, so why are they serializable in the first place? Luckily for me, some of my friends are not in their right mind :) Many thanks to twin-dad-to-be Niko Brummer for this extremely interesting, and probably totally useless idea, though he assures me that he is using it in a real program. Some of the code in this newsletter is from him, other parts I have added. You can easily spot his code by the absense of bugs.
There are some very interesting gotchas that occur because of the dear Swing event dispatch thread, who, having failed at all attempts to find a mate, is still single, and will be for the forseeable future. Due to his hermit nature, we have to be very careful that we do not read from the GUI components or write to them from any thread except the GUI thread, or else face the wrath of a thousand deadlocks (see first newsletter in this series).
If you are serializing the component in response to pushing a GUI button, it will work, because then you are using the event dispatch thread. If you are doing it from any other thread, you may get problems (in Niko's case the event dispatch thread crashed). The solution is to wrap the gui component in an object of which you control the serialization by specifying your own writeObject() method.
I have written a ComponentSerializer class which wraps the read write functionality and you can either write a Component to an OutputStream or read it from an InputStream without having to worry about what thread you are currently in.
//: ComponentSerializer.java
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.lang.reflect.*; // wouldn't be right for me to send
             // you a newsletter that doesn't use reflection :)

public class ComponentSerializer {
  public void write(Component comp, OutputStream out)
      throws IOException {
    System.out.println("writing " + comp);
    ObjectOutputStream oout = new ObjectOutputStream(out);
    oout.writeObject(new ComponentEncapsulator(comp));
    oout.reset();
    oout.flush();
  }
  public Component read(InputStream in)
      throws IOException, ClassNotFoundException {
    System.out.println("reading component");
    ObjectInputStream oin = new ObjectInputStream(in);
    ComponentEncapsulator enc =
      (ComponentEncapsulator)oin.readObject();
    return enc.getComponent();
  }
  private class ComponentEncapsulator implements Serializable {
    private final Component comp;
    public ComponentEncapsulator(Component comp) {
      this.comp = comp;
    }
    public Component getComponent() {
      return comp;
    }
    private IOException defaultWriteException;
    private void writeObject(final ObjectOutputStream out)
        throws IOException {
      if (SwingUtilities.isEventDispatchThread()) {
        // This is all that is necessary if we are already in
        // the event dispatch thread, e.g. a user clicked a
        // button which caused the object to be serialized
        out.defaultWriteObject();
      } else {
        try {
          // we want to wait until the object has been written
          // before continuing.  If we called this from the
          // event dispatch thread we would get an exception
          SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
              try {
                // easiest way to indicate to the enclosing class
                // that an exception occurred is to have a member
                // which keeps the IOException
                defaultWriteException = null;
                // we call the actual write object method
                out.defaultWriteObject();
              } catch(IOException ex) {
                // oops, an exception occurred, remember the
                // exception object
                defaultWriteException = ex;
              }
            }
          });
          if (defaultWriteException != null) {
            // an exception occurred in the code above, throw it!
            throw defaultWriteException;
          }
        } catch(InterruptedException ex) {
          // I'm not quite sure what do here, perhaps:
          Thread.currentThread().interrupt();
          return;
        } catch(InvocationTargetException ex) {
          // This can actually only be a RuntimeException or an
          // Error - in either case we want to rethrow them
          Throwable target = ex.getTargetException();
          if (target instanceof RuntimeException) {
            throw (RuntimeException)target;
          } else if (target instanceof Error) {
            throw (Error)target;
          }
          ex.printStackTrace(); // this should not happen!
          throw new RuntimeException(ex.toString());
        }
      }
    }
  }
}
I apologize for all the comments in the ComponentSerializer, as we all know, too many comments are often indicative of poorly written code, but I cannot think of a simpler, yet correct, way of doing this. This ComponentSerializer class should handle just about any java.awt.Component derivative thrown at it from any thread, except for components which reference non-serializable components.
We can then write a GUIServer, which accepts a ComponentEncapsulator via TCP/IP and constructs a JFrame containing the component within the ComponentEncapsulator. We only cater for one component per socket, but that could easily be changed.
//: GUIServer.java
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;

public class GUIServer {
  public static final int PORT = 4123;
  private static final ComponentSerializer compser =
    new ComponentSerializer();
  public GUIServer() throws IOException {
    System.out.println("Super-duper GUI SERVER started");
    ServerSocket ss = new ServerSocket(PORT);
    while(true) {
      Socket socket = ss.accept();
      try {
        JFrame frame = new JFrame(
          "Component received from " + socket);
        Component comp = compser.read(socket.getInputStream());
        frame.getContentPane().add(comp);
        frame.pack();
        frame.show();
      } catch(IOException ex) {
        ex.printStackTrace();
      } catch(ClassNotFoundException ex) {
        ex.printStackTrace();
      }
    }
  }
  public static void main(String[] args) throws IOException {
    new GUIServer();
  }
}
We can then take a Component, for example a JScrollPane containing a JTable, and send it to any OutputStream, e.g. to the network, to the file system for short-term storage, etc. The warning in the Swing source essentially tells us we should not write GUI components onto DAT tapes for long-term archiving.
//: GUIExample.java
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;

public class GUIExample extends JFrame {
  public static final int PORT = 4123;
  private static final ComponentSerializer compser =
    new ComponentSerializer();
  private JScrollPane scrollPane;
  public GUIExample() {
    super("GUIExample Frame");
    scrollPane = new JScrollPane(new JTable(3,4));
    getContentPane().add(scrollPane);
    getContentPane().add(new JButton(
      new AbstractAction("Serialize Table") {
        public void actionPerformed(ActionEvent e) {
          System.out.println("Now we serialize synchronously");
          try {
            Socket socket = new Socket("localhost", PORT);
            compser.write(scrollPane, socket.getOutputStream());
            socket.close();
          } catch(IOException ex) {
            ex.printStackTrace();
          }
        }
      }), BorderLayout.SOUTH);
    setSize(400, 200);
    show();
  }
  public static void main(String[] args) throws Exception {
    GUIExample ex = new GUIExample();
    ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }
}
Please try this out - I was quite amazed that it actually worked! Start the GUIServer class, then start the GUIExample and press the button. The GUIServer should now open up another JFrame containing the JTable. Now edit the original JTable (press enter after editing, otherwise you'll get an exception when you try to serialize the table), and click on the button again. The GUIServer should now open up yet another JFrame with the JTable containing the latest values. Though I cannot think of a good application for this at the moment, I think it's quite neat that you can do that.
I've also written a second example to show you how to do an asynchronous serialization of components via another thread.
//: GUIExample2
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;

public class GUIExample2 extends JFrame {
  public static final int PORT = 4123;
  private static final ComponentSerializer compser =
    new ComponentSerializer();
  private JPanel personalData;
  public GUIExample2() {
    super("Asynchronous Sending GUIExample2 Frame");
    personalData = new JPanel(new GridLayout(0, 2, 5, 5));
    personalData.add(new JLabel("Name: "));
    personalData.add(new JTextField());
    personalData.add(new JLabel("Age: "));
    personalData.add(new JTextField());
    getContentPane().add(personalData, BorderLayout.NORTH);
    getContentPane().add(new JButton(
      new AbstractAction("Serialize Personal Data") {
        public void actionPerformed(ActionEvent e) {
          asyncSerialize(personalData);
        }
      }), BorderLayout.SOUTH);
    setSize(400, 200);
    show();
  }
  private void asyncSerialize(final Component comp) {
    new Thread() { {start();} // start from initializer block
      public void run() {
        try {
          Socket socket = new Socket("localhost", PORT);
          compser.write(comp, socket.getOutputStream());
          socket.close();
        } catch(IOException ex) {
          ex.printStackTrace();
        }
      }
    };
  }
  public static void main(String[] args) throws Exception {
    GUIExample2 ex = new GUIExample2();
    ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }
}
When you try this out, you'll notice that the entire JPanel gets sent across the network to the server.
Were I to use this in a production environment framework, I would add the option of writing asynchronously to the ComponentSerializer and probably do the writing via a ThreadPool.
Next week I will demonstrate that the following can be true:
"hi there".equals("cheers !")


created by Dr. Heinz M. Kabutz

Follow-up (JAVA)

Hi again,
Imagine my horror this morning when I tried to run the code from last nights newsletter and discovered that it generated an exception! On frantic searching I figured that the JBuilder 3.0 compiler produces a different result to the SUN JDK 1.3 compiler. I had only run the program from within JBuilder, so I got quite a surprise that it did not work in SUN. The reason I'm blaming the compiler is because when I ran the class files generated by JBuilder 3.0 with the JDK 1.3 VM it works perfectly.
The problem is that if you call out.defaultWriteObject(), the two compilers have different ideas of which object you are calling this from, because you are inside an inner class. The SUN compiler thinks you are calling it from ComponentSerializer and that is not serializable.
The solution is quite simple, just move the ComponentEncapsulator class out of the ComponentSerializer class and it should work with the JDK 1.3 compiler.
We thus end up with ComponentSerializer:
//: ComponentSerializer.java
import java.io.*;
import java.awt.*;
public class ComponentSerializer {
  public void write(Component comp, OutputStream out)
      throws IOException {
    System.out.println("writing " + comp);
    ObjectOutputStream oout = new ObjectOutputStream(out);
    oout.writeObject(new ComponentEncapsulator(comp));
    oout.reset();
    oout.flush();
  }
  public Component read(InputStream in)
      throws IOException, ClassNotFoundException {
    System.out.println("reading component");
    ObjectInputStream oin = new ObjectInputStream(in);
    ComponentEncapsulator enc =
      (ComponentEncapsulator)oin.readObject();
    return enc.getComponent();
  }
}
and ComponentEncapsulator:
//: ComponentEncapsulator.java
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.lang.reflect.*; // wouldn't be right for me to send
           // you a newsletter that doesn't use reflection :)
class ComponentEncapsulator implements Serializable {
  private final Component comp;
  private IOException defaultWriteException;
  public ComponentEncapsulator(Component comp) {
    this.comp = comp;
  }
  public Component getComponent() {
    return comp;
  }
  private void writeObject(final ObjectOutputStream out)
      throws IOException {
    if (SwingUtilities.isEventDispatchThread()) {
      // This is all that is necessary if we are already in
      // the event dispatch thread, e.g. a user clicked a
      // button which caused the object to be written
      out.defaultWriteObject();
    } else {
      try {
        // we want to wait until the object has been written
        // before continuing.  If we called this from the
        // event dispatch thread we would get an exception
        SwingUtilities.invokeAndWait(new Runnable() {
          public void run() {
            try {
              // easiest way to indicate to the enclosing class
              // that an exception occurred is to have a member
              // which keeps the IOException
              defaultWriteException = null;
              // we call the actual write object method
              out.defaultWriteObject();
            } catch(IOException ex) {
              // oops, an exception occurred, remember the
              // exception object
              defaultWriteException = ex;
            }
          }
        });
        if (defaultWriteException != null) {
          // an exception occurred in the code above, throw it!
          throw defaultWriteException;
        }
      } catch(InterruptedException ex) {
        // I'm not quite sure what do here, perhaps:
        Thread.currentThread().interrupt();
        return;
      } catch(InvocationTargetException ex) {
        // This can actually only be a RuntimeException or an
        // Error - in either case we want to rethrow them
        Throwable target = ex.getTargetException();
        if (target instanceof RuntimeException) {
          throw (RuntimeException)target;
        } else if (target instanceof Error) {
          throw (Error)target;
        }
        ex.printStackTrace(); // this should not happen!
        throw new RuntimeException(ex.toString());
      }
    }
  }
}
This highlights again that we have to be quite careful with Java. In the big project that we work on, we have standardized on the JDK 1.3 compiler and ALL our classes have to be compiled with that. This happened only after much nailbiting because of slight compiler differences of various IDEs.

 created by Dr. Heinz M. Kabutz

Finding Lost Frames

Something that I have encountered in some almost-complete projects, were dialogs that were not bound to frames. When you construct a dialog, you should specify who the owner is, which is either a frame or another dialog. It is, however, also possible to specify "null" as the owner, which causes very irritating problems. In MS Windows, if a modal dialog does not have an owner, and you have moved away from it, for example if you quickly switched to Outlook while waiting for it to start up, the only way to get back to the dialog is by using ALT+Tab. If you click on the frame icon on the toolbar, you will get just the frame, not the dialog. Clicking on the frame will cause a beep and that's all.
A few months ago, I asked myself the question: Is there a way in which we can find a frame that has already been created?
One of my first newsletters showed a GlobalHotkeyManager that allowed you to install your own event queue into the AWT, letting you catch any events that occur, before ANY other components get hold of them. Why don't we use the concept to catch Window events and then use those events to remember which frames are available?
The problem with the GlobalHotkeyManager, was that it only allowed exactly one event catcher to be present at a time. It is therefore only safe to use if none of the libraries you use link in a similar event queue. An alternative, suggested by F.S., was to register ourselves as a listener to the main AWT event system by calling Toolkit.getDefaultToolkit().addAWTEventListener().
Each time a window is activated, we grap the handle and add it to our list of frames that we know about. We can then write a method called "lookupFrame(String title)" which goes through the list and returns the first frame that we find with the specified title.
//: FrameLookup.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class FrameLookup {
  private final Collection frames = new LinkedList();
  // Singleton Pattern
  private static final FrameLookup instance = new FrameLookup();
  public static FrameLookup getInstance() {
    return instance;
  }
  private FrameLookup() {
    Toolkit.getDefaultToolkit().addAWTEventListener(
      new AWTEventListener() {
        public void eventDispatched(AWTEvent event) {
          System.out.println("Event Dispatched : " + event);
          if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
            if (event.getSource() instanceof Frame) {
              synchronized(frames) {
                frames.add(event.getSource());
              }
            }
          }
        }
      }, AWTEvent.WINDOW_EVENT_MASK);
  }
  public Frame lookupFrame(String title) {
    synchronized(frames) {
      Iterator it = frames.iterator();
      while(it.hasNext()) {
        Frame frame = (Frame)it.next();
        if (frame.getTitle().equals(title)) return frame;
      }
    }
    return null;
  }
}
I can now write a test program that creates a frame, forgets the handle, and then creates a dialog and uses the FrameLookup class to find the correct frame to use as owner.
//: FrameLookupTest.java
import javax.swing.*;
import java.awt.*;

public class FrameLookupTest {
  private static final String SWING_FRAME_TITLE = "Swing Frame";
  private static final String AWT_FRAME_TITLE = "AWT Frame";
  private static final String SWING_DIALOG_TITLE = "Swing Dialog";

  public static void main(String[] args) {
    FrameLookup framer = FrameLookup.getInstance();
    makeFrame();

    // we can find visible frame by using our FrameLookup utility
    System.out.println("Frame is " +
      FrameLookup.getInstance().lookupFrame(SWING_FRAME_TITLE));

    makeDialog();
  }

  private static void makeFrame() {
    JFrame frame = new JFrame(SWING_FRAME_TITLE);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(300, 300);
    frame.show();
  }

  private static void makeDialog() {
    // we can bind our JDialog to the Frame
    JDialog dialog = new JDialog(
      FrameLookup.getInstance().lookupFrame(SWING_FRAME_TITLE),
      SWING_DIALOG_TITLE, true);
    dialog.setSize(400, 100);
    dialog.show();
  }
}
This is great; we can discover the correct frame as owner, just by knowing its title! There are some minor problems, such as the fact that there is a racing condition involved. It can happen that we request to find the frame before it has been activated or shown, thereby our lookupFrame() method would return "null". Big Oooops. Our situation is now worse than before, as such intermittent failings are MUCH harder to find. Let's also not forget that the title of a frame does not have to be unique.
When I was preparing this newsletter, I got distracted in that I tried to reproduce the Swing dealock problem (which I didn't manage :(. While I was looking through the source code of JFrame/Frame/Window/Container/Component, I happened upon the method Frame[] java.awt.Frame.getFrames(), which Sun added in the JDK 1.2, according to the @version tag. Is it just me, or have I been reading the wrong publications, that don't tell me about these features? Please, if you have heard of Frame.getFrames(), send me an email.
Anyway, with my newly acquired knowledge, I ran off and wrote a second version of the FrameLookup class that looks like this, and sorts out the racing condition problem:
//: FrameLookup.java take 2
import java.awt.*;
public class FrameLookup {
  // Singleton Pattern
  private static final FrameLookup instance = new FrameLookup();
  public static FrameLookup getInstance() {
    return instance;
  }
  private FrameLookup() { }
  public Frame lookupFrame(String title) {
    Frame[] frames = Frame.getFrames();
    for (int i=0; i<frames.length; i++) {
      if (frames[i].getTitle().equals(title))
        return frames[i];
    }
    return null;
  }
}
We still have the problem that titles alone could be ambigious, but the getFrames() method is definitely the correct way to find a Frame if you need to.
Until next week, and please remember that the more people read this newsletter each week, the more corporate time I can waste collectively, currently I'm wasting about 2 man-months of your development time each week. i.e. please keep on forwarding these newsletters to friends and colleagues who use Java ;-)

created by Dr. Heinz M. Kabutz

What do you Prefer?

The guys at Sun has seen the need for handling preferences in a somewhat better and easier to use way than using Properties or implementing a complex preference subsystem that saves it to a database or some other backing store. So they created the Preferences API.
Using this API starts with the Preferences class in the java.util.prefs package. This class represents a preference node in some kind of preference hierarchy. Such a node can have child nodes, as well as key-value pairs belonging to it (similar to Windows Registry). The 4 important static methods of Preferences are:

// return system preference-node for package that O belongs to
Preferences systemNodeForPackage(Object 0);
// return root node of system preferences
Preferences systemRoot();
// return system preference-node for package that O belongs to
Preferences userNodeForPackage(Object O);
// return root node of user preferences
Preferences userRoot();
Some explanation is probably needed. The preference data gets saved in two tree-like structures in an implementation specific way. The JDK for Windows version saves it in the Windows Registry, but it is possible to create one's own implementation that might for example use a database. The one tree is used to store user-specific preferences (each user on a system will have a seperate tree), and the other tree stores system preferences. (The definition of user and system depends on the preferences implementation. In the Windows JDK version it maps to Windows users and system-wide preferences.) Each node in this tree can be represented by a Preferences object. However, if you're like me you do not like theory too much (and that's what javadocs are for), so let us explore this API with an example: a "Cross-platform Registry Editor".

Cross-platform Registry Editor

The idea of this Java tool is to be able to view and edit preferences saved via the Preferences API, no matter on what platform it is executed (i.e. the backing store used is transparent to the user).

Preference Nodes

We implement the class PreferencesEditor as a JDialog, and it must contain a JTree to present the preferences trees (user and/or system), and a JTable to display and edit the actual preference values. We need the following inner classes: PrefTreeNode to represent a preference node in the JTree, PrefTableModel to handle the display and editing of preference values in the table, and PrefTreeSelectionListener to update the JTable with the currently selected preference node.
I list the code for PreferenceEditor with discussions in between the code.
//add all other necessary imports here
import java.util.prefs.Preferences;
import java.util.prefs.BackingStoreException;

public class PreferencesEditor extends JDialog {
  JTree prefTree;
  JTable editTable;

  /**
   * Creates PreferencesEditor dialog that show all System and
   * User preferences.
   * @param owner owner JFrame
   * @param title title of dialog
   */
  public PreferencesEditor(JFrame owner, String title){
    this(owner, title, null, true, null, true);
  }

  /**
   * @param owner owner JFrame
   * @param title title of dialog
   * @param userObj the package to which this object belongs is
   * used as the root-node of the User preferences tree (if
   * userObj is null, then the rootnode of all user preferences
   * will be used)
   * @boolean showUserPrefs if true, then show user preferences
   * @param systemObj the package to which this object belongs is
   * used as the root-node of the System preferences tree (if
   * systemObj is null, then the rootnode of all system
   * preferences will be used)
   * @param showSystemPrefs if true, then show system preferences
   */ 
  public PreferencesEditor(JFrame owner, String title,
      Object userObj, boolean showUserPrefs, Object systemObj,
      boolean showSystemPrefs) {
    super(owner, title);
    getContentPane().setLayout(new BorderLayout(5,5));
    setSize(640,480);
    createTree(userObj, showUserPrefs, systemObj, showSystemPrefs);
    editTable = new JTable();
    createSplitPane();
    createButtonPanel();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }
As mentioned in the code comments, there are two constructors: one give access to all the system and user preferences, and one can be used to only display/edit a specified subset of the preferences. Let's first look at the createTree(...), createUserNode(...) and createSystemRootNode(...) methods to see how this is done:
private void createTree(Object userObj, boolean showUserPrefs, Object systemObj, boolean showSystemPrefs){
    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Preferences");
    if (showUserPrefs) {
      rootNode.add(createUserRootNode(userObj));
    }
    if (showSystemPrefs) {
      rootNode.add(createSystemRootNode(systemObj));
    }
    DefaultTreeModel model = new DefaultTreeModel(rootNode);
    prefTree = new JTree(model);
    prefTree.addTreeSelectionListener(new PrefTreeSelectionListener());
  }
  private MutableTreeNode createSystemRootNode(Object obj) {
    try {
      PrefTreeNode systemRoot;
      if (obj==null) {
        systemRoot = new PrefTreeNode(Preferences.systemRoot());
      } else {
        systemRoot = new PrefTreeNode(Preferences.systemNodeForPackage(obj));
      }
      return systemRoot;
    } catch (BackingStoreException e) {
      e.printStackTrace();
      return new DefaultMutableTreeNode("No System Preferences!");
    }
  }
  private MutableTreeNode createUserRootNode(Object obj) {
    try {
      PrefTreeNode userRoot;
      if (obj==null) {
        userRoot = new PrefTreeNode(Preferences.userRoot());
      } else {
        userRoot = new PrefTreeNode(Preferences.userNodeForPackage(obj));
      }
      return userRoot;
    } catch (BackingStoreException e) {
      e.printStackTrace();
      return new DefaultMutableTreeNode("No User Preferences!");
    }
  }
If the user specify a userObj (and showUserPrefs=true), then Preferences.userNodeForPackage(userObj) gets called in creteUserRootNode. This will return a Preferences object that represents the preferences node that maps to the package structure of userObj. If this preference node does not yet exist in the backing store, it gets created. For example, if I call createUserNode(new com.polymorph.MyClass()), then the preference node "com/polymorph" will be returned, and its parent node will be "com" (in the user preference tree). If the user pass null as parameter, then Preferences.userRoot() gets called, which return the root node of the user preferences tree (for the current user). The same goes for createSystemRootNode and the system preferences. Of course we need a way of representing a preference node in a JTree, and this is what the PrefTreeNode inner class is for.
class PrefTreeNode extends DefaultMutableTreeNode {
    Preferences pref;
    String nodeName;
    String[] childrenNames;

    public PrefTreeNode(Preferences pref) throws BackingStoreException {
      this.pref = pref;
      childrenNames = pref.childrenNames();
    }
    public Preferences getPrefObject(){
      return pref;
    }
    public boolean isLeaf(){
      return ((childrenNames==null)||(childrenNames.length == 0));
    }
    public int getChildCount(){
      return childrenNames.length;
    }
    public TreeNode getChildAt(int childIndex){
      if(childIndex < childrenNames.length){
        try {
          PrefTreeNode child = new PrefTreeNode(pref.node(childrenNames[childIndex]));
          return child;
        } catch (BackingStoreException e) {
          e.printStackTrace();
          return new DefaultMutableTreeNode("Problem Child!");
        }
      }
      return null;
    }
    public String toString(){
      String name = pref.name();
      if ((name == null)||("".equals(name))){ //if root node
        name = "System Preferences";
        if (pref.isUserNode()) name = "User Preferences";
      }
      return name;
    }
  }
This inner class decorates a Preferences object to be used as a MutableTreeNode in a JTree. All the child preferences nodes of this object are accessed via the pref.childrenNames() call, and stored in a String array. This array is then used to calculate the number of children nodes, whether this is a leaf node, etc. getChildAt gets a specific child node, mainly via the pref.node(childrenNames[childIndex]) call. Preferences.node("nodeString") returns a Preferencesobject for the node specified by "nodeString". It can specify a node relative to the current one, ex. "child1", which will return a child preference node (as we use it in getChildAt), or an absolute path can be specified, ex. "/com/polymorph/UI" will return a node "UI", with parent node "polymorph".

Preference Key-Value pairs

OK, our editor is now able to handle the nodes in the user and/or system preferences hierarchy, but how to we actually access the preference values? Well, the Preferences API allows us to save preferences in our custom defined preferences structure in a very similar way as we would in a Hashmap: we put key-value pairs in the preferences node, where the key is a specific preference setting name, and the value can either be a String, int, long, boolean, float, double or byte[]. Once you have a Preferences object, you can just call put("keyStr", "valueStr"), or putLong("keyStr", 123l), etc. And you can retrieve these values via the get("keyStr", "defaultStr"), or getLong("keyStr", 233 /*defaultVal*/), etc. methods. Note that for every get method, a default value must be supplied. This forces you to think about default values for when the preferences cannot be loaded from the backing store, thus allowing your application to continue even though preferences could not be loaded.
In our editor example, we access these key-value pairs in a JTable, and we need the PrefTableModel to do this:
class PrefTableModel extends AbstractTableModel {
    Preferences pref;
    String[] keys;
    public PrefTableModel(Preferences pref){
      this.pref = pref;
      try {
        keys = pref.keys();
      } catch (BackingStoreException e) {
        System.out.println("Could not get keys for Preference node: "+pref.name());
        e.printStackTrace();
        keys = new String[0];
      }
    }
    public String getColumnName(int column) {
      switch(column){
      case 0: return "Key";
      case 1: return "Value";
      default: return "-";
      }
    }
    public boolean isCellEditable(int rowIndex, int columnIndex) {
      switch(columnIndex) {
      case 0: return false;
      case 1: return true;
      default: return false;
      }
    }
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
      pref.put(keys[rowIndex], aValue.toString());
      try {
        pref.sync(); //make sure the backing store is synchronized with latest update
      } catch (BackingStoreException e) {
        System.out.println("Error synchronizing backStore with updated value");
        e.printStackTrace();
      }
    }
    public Object getValueAt(int row, int column){
      String key = keys[row];
      if (column==0) return key;
      Object value = pref.get(key, "(Unknown)");
      return value;
    }
    public int getColumnCount(){
      return 2;
    }
    public int getRowCount(){
      return keys.length;
    }
  }
In the PrefTableModel constructor, pref.keys returns all the key-names stored in the pref node. These key-names are then used in getValueAt to get the value of either a key or value-column cell. If we want the value of a Value-column cell, we get it as a String via the pref.get(key, "Unknown") call (default value="Unknown"), as the Preferences API unfortunately does not seem to allow us to retrieve it as an Object. Thus all values are presented as String in the table, but this should not be a problem, as it seems that these values are saved as Strings anyway in the backing store. getLong, getBoolean, etc. tries and interpret the saved string-value as a long, boolean, etc. Only the Value-column cells are editable, and the setValueAt method uses pref.put(key-name, aValue) to update the edited value. It also calls pref.sync() that forces any updates to be synchronized with the backing store.
How do we connect this table model to the preference tree? Well, the PreferencesEditor constructor creates a JTable object (editTable), and then we use the PrefTreeSelectionListener inner class to update the table model of this table.
class PrefTreeSelectionListener implements TreeSelectionListener{
    public void valueChanged(TreeSelectionEvent e) {
      try {
        PrefTreeNode node = (PrefTreeNode)e.getPath().getLastPathComponent();
        Preferences pref = node.getPrefObject();
        editTable.setModel(new PrefTableModel(pref));
      } catch (ClassCastException ce) {
        System.out.println("Node not PrefTreeNode!");
        editTable.setModel(new DefaultTableModel());
      }
    }
  }
The createTree method adds an instance of PrefTreeSelectionListener to the JTree as a listener. All that now remains to be defined are the createSplitPane() and createButtonPanel methods, and none of them contains any surprises:
private void createSplitPane(){
    JSplitPane splitPane = new JSplitPane();
    splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
    splitPane.setOneTouchExpandable(true);
    splitPane.setLeftComponent(new JScrollPane(prefTree));
    splitPane.setRightComponent(new JScrollPane(editTable));
    getContentPane().add(splitPane, BorderLayout.CENTER);
  }
  private void createButtonPanel(){
    JPanel buttonPanel = new JPanel(new BorderLayout(5,5));
    JButton closeButton = new JButton("Close");
    closeButton.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    });
    buttonPanel.add(closeButton, BorderLayout.EAST);
    getContentPane().add(buttonPanel, BorderLayout.SOUTH);
  }
} //end of PreferencesEditor
And that's how easy it is to implement a simple, yet usable, cross-platform registry editor. Already my mind is spinning with ideas on how to improve on this, like adding functionality to be able to modify the preference trees and making use of the Preferences API export/import capabilities (yhep, you can actually export preferences to XML files, and also import these files). A whole new preferable world is opening up...

 created by Herman Lintvelt (Polymorph Systems)

Placing components on each other

Two weeks ago, I was presenting my course Design Patterns - The Timeless Way of Coding to some experienced Java developers, and we spent quite a bit of time arguing about the Composite pattern. To refresh your memory, the book on OO Design Patterns by Erich Gamma et al, contains the following classes in the structure section on Composite:
public abstract class Component {
  public void add(Component c) {}
  public void remove(Component c) {}
  public abstract void operation();
}
public class Leaf extends Component {
  public void operation() { /* do something */ }
}
public class Composite {
  private java.util.List children = new java.util.LinkedList();
  public void add(Component c) { children.add(c); }
  public void remove(Component c) { children.remove(c); }
  public void operation() {
    // or my cool VisitingIterator from last week ;-)
    java.util.Iterator it = children.iterator();
    while(it.hasNext()) {
      ((Component)it.next()).operation();
    }
  }
}
The question we were kicking around was: "Why are the add() and remove() methods in the top-level Component class?"
For an excellent discussion around this question, have a look at the Pattern Hatching column by John Vlissides in the September 2001 edition of Java Report.
Hoping to have whet your appetite, I will not pursue that discussion further here, but instead jump over to the JDK. A few years ago, I was looking at the Composite pattern and comparing it to the java.awt.Component. I found it interesting that java.awt.Component did not contain the add() and remove() methods, those were contained in the subclass java.awt.Container. However, when I looked at javax.swing.JComponent I noticed that it extended java.awt.Container and therefore contained the methods add() and remove().
The big question is: Why? Was it done like this because Java does not have multiple inheritance or was it done to more closely follow the Composite pattern? My bet is on multiple inheritance being the reason, but if you have reliable information (not speculation) I'd love to hear from you.
So, how do you put this to use?

Multi-lined button

Say for example you want to have a JButton with multiple lines of labels on it. One way is to use HTML text, but then the default font is different to the normal JButton font. An answer is to stick a few JLabels on top of a JButton (remember that JButton extends AbstractButton extends JComponent extends Container). You can make such a button by doing the following:
import javax.swing.*;
import java.awt.*;

public class MultilineButton {
  public static void main(String[] args) {
    JButton button = new JButton();
    // It is important to set the layout manager of the button
    button.setLayout(new GridLayout(0, 1));
    // Then we simply add components to it!
    button.add(new JLabel("This is a", JLabel.CENTER));
    button.add(new JLabel("multiline", JLabel.CENTER));
    button.add(new JLabel("button.", JLabel.CENTER));
    JFrame f = new JFrame("Multi-line Button");
    f.getContentPane().setLayout(new FlowLayout());
    f.getContentPane().add(button);
    f.setSize(100,100);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.show();
  }
}
This will produce a button with several lines on it. The problem with that example is that the focus of the button is not shown.

CheckBox on a Button

What we can also do is put a JCheckBox (or any Component for that matter) on top of a JButton. This can be used to make really confusing user interfaces. Please don't actually do this, I'm just illustrating something here...
import javax.swing.*;
import java.awt.*;

public class CheckBoxOnButton {
  public static void main(String[] args) {
    JButton records = new JButton();
    records.setLayout(new BorderLayout());
    records.add(new JLabel("Show Records"), BorderLayout.NORTH);
    records.add(new JCheckBox("autoscroll"), BorderLayout.CENTER);
    JFrame f = new JFrame("CheckBox on Button");
    f.getContentPane().setLayout(new FlowLayout());
    f.getContentPane().add(records);
    f.setSize(200,200);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.show();
  }
}
Not very useful? You've seen nothing yet!

Why not a Tree on a Button?

Of course, if we can add a JCheckBox to a JButton, why can we not add a JTree to a JButton? Here's an example of how you can do that:
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;

public class TreeOnButton {
  public static void main(String[] args) {
    JButton button = new JButton();
    button.setLayout(new BorderLayout());

    final JLabel buttonText = new JLabel("Press me",
      JLabel.CENTER);
    button.add(buttonText, BorderLayout.NORTH);

    JTree tree = new JTree();
    tree.addTreeSelectionListener(new TreeSelectionListener() {
      public void valueChanged(TreeSelectionEvent e) {
        buttonText.setText("Press for " +
          e.getPath().getLastPathComponent());
      }
    });
    button.add(tree, BorderLayout.CENTER);

    JFrame f = new JFrame("Tree on Button");
    f.getContentPane().setLayout(new FlowLayout());
    f.getContentPane().add(button);
    f.setSize(500,500);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.show();
  }
}
Well, there you go. I have not found any IDE's which support this functionality directly, and I would be surprised if there was such an IDE. You should not really add complex components to each other, as it will confuse your users. Imagine trying to write a user manual for buttons containing trees and check boxes!
This was again one of the funnest newsletters to write, I'm looking forward to your feedback :-)

created by Dr. Heinz M. Kabutz

Wait, Cursor, Wait! (Java)

What is the intent of wait cursors? To tell the user: "Hey, everywhere you see a wait cursor you can't do nothing." Of course having some progress indication while the application is busy with a long operation is also a good idea. (Law 1 concerning GUIs: The GUI should ALWAYS be responsive, even if it is only indicating how busy the application is.) I recently discovered a nice class to use for feedback on long operations: javax.swing.ProgressMonitor. However, most of the time we need a wait cursor (also known as an hourglass cursor).
Most of you probably already know how to use wait cursors in Swing, but let me go ahead and give an example of a useful CursorToolkitOne class, implementing an interface for the constants:
import java.awt.*;

public interface Cursors {
  Cursor WAIT_CURSOR = 
    Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
  Cursor DEFAULT_CURSOR = 
    Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);  
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** Basic CursorToolkit that still allows mouseclicks */
public class CursorToolkitOne implements Cursors {
  private CursorToolkitOne() { }

  /** Sets cursor for specified component to Wait cursor */
  public static void startWaitCursor(JComponent component) {
    RootPaneContainer root = 
      (RootPaneContainer)component.getTopLevelAncestor();
    root.getGlassPane().setCursor(WAIT_CURSOR);
    root.getGlassPane().setVisible(true);
  }

  /** Sets cursor for specified component to normal cursor */
  public static void stopWaitCursor(JComponent component) {
    RootPaneContainer root = 
      (RootPaneContainer)component.getTopLevelAncestor();
    root.getGlassPane().setCursor(DEFAULT_CURSOR);
    root.getGlassPane().setVisible(false);
  }

  public static void main(String[] args) {
    final JFrame frame = new JFrame("Test App");
    frame.getContentPane().add(
      new JLabel("I'm a Frame"), BorderLayout.NORTH);
    frame.getContentPane().add(
      new JButton(new AbstractAction("Wait Cursor") {
        public void actionPerformed(ActionEvent event) {
          System.out.println("Setting Wait cursor on frame");
          startWaitCursor(frame.getRootPane());
        }
      }));
    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.show();
  }
}
CursorToolkitOne only has two class methods: startWaitCursor(...) and stopWaitCursor(...). You pass a JComponent as parameter, and each method finds the RootPaneContainer (i.e. the uppermost container that contains the component) and then sets the Cursor of the GlassPane of this container to either the default or the wait cursor. It then sets this GlassPane's visibility to true or false. As easy as pie, or is it? What happens if we run the main method?
A JFrame is displayed, with a label and a single button. Pressing the button results in the wait cursor being set on the frame via the startWaitCursor method. This part is still fine, but what happens if you click on the button again? Unfortunately the button's action is performed again (you can see the extra System.out.println). So we have a wait cursor, but is does not actually stop the input.
So let's upgrade to CursorToolkitTwo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** Basic CursorToolkit that swallows mouseclicks */
public class CursorToolkitTwo implements Cursors {
  private final static MouseAdapter mouseAdapter = 
    new MouseAdapter() {};

  private CursorToolkitTwo() {}

  /** Sets cursor for specified component to Wait cursor */
  public static void startWaitCursor(JComponent component) { 
    RootPaneContainer root =
      ((RootPaneContainer) component.getTopLevelAncestor()); 
    root.getGlassPane().setCursor(WAIT_CURSOR);
    root.getGlassPane().addMouseListener(mouseAdapter);
    root.getGlassPane().setVisible(true);
  }

  /** Sets cursor for specified component to normal cursor */
  public static void stopWaitCursor(JComponent component) { 
    RootPaneContainer root =
      ((RootPaneContainer) component.getTopLevelAncestor()); 
    root.getGlassPane().setCursor(DEFAULT_CURSOR);
    root.getGlassPane().removeMouseListener(mouseAdapter);
    root.getGlassPane().setVisible(false);
  }

  public static void main(String[] args) {
    final JFrame frame = new JFrame("Test App");
    frame.getContentPane().add(
      new JLabel("I'm a Frame"), BorderLayout.NORTH);
    frame.getContentPane().add(
      new JButton(new AbstractAction("Wait Cursor") {
        public void actionPerformed(ActionEvent event) {
          System.out.println("Setting Wait cursor on frame");
          startWaitCursor(frame.getRootPane());
        }
      }));
    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.show();
  }
}
We added a MouseAdapter that does nothing to the GlassPane and this prevents any MouseEvents from getting through to the underlying components. Why does it work this way? Well, that's a topic for another discussion.

Wait Cursors and Modal dialogs...

However, the original question was not only about wait cursors, but their use with modal dialogs, and specifically the parent frame or window of the modal dialog.
The intent of a modal dialog is to deny access to any other part of the application GUI, while retaining access to the dialog. So what happens if the dialog initiates a background operation that takes long to execute (GUI Law 2: never execute a potential long operation from the main GUI thread), and you need to indicate with a wait cursor that the dialog GUI is off limits for the moment? Easy: use CursorToolkitTwo.startWaitCursor to set the wait cursor on the dialog.
And then one day when a client is playing with his mouse while waiting for the wait cursor to disappear (since there is no progress indication because of tight deadlines), he sees that the cursor changes back to the default cursor when he moves the mouse out of the dialog unto the main frame of the application. I can already see the Problem Report: "No wait cursor is shown while the application is busy; only the dialog has a wait cursor, but mouse-clicks have no effect even though there is no wait cursor."
How can we fix this gross enfringement of human rights?

Solution 1

Hey, this should be simple: I only have to get the parent of the modal dialog (in most cases a JFrame), and set the wait cursor on it:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** First attempt at a solution */
public class SolutionOne {
  private static JDialog createDialog(final JFrame frame) {
    final JDialog dialog =new JDialog(frame, "I'm Modal", true);
    dialog.getContentPane().add(
      new JLabel("I'm a busy modal dialog"));
    dialog.getContentPane().add(
      new JButton(new AbstractAction("Wait Cursor") {
        public void actionPerformed(ActionEvent event) {
          setWaitCursor(dialog);
        }
      }));
    dialog.setSize(300, 200);
    return dialog;
  }

  public static void setWaitCursor(JDialog dialog) {
    System.out.println("Setting Wait cursor on frame");
    CursorToolkitTwo.startWaitCursor(
        ((JFrame)dialog.getOwner()).getRootPane());
    System.out.println("Setting Wait cursor on dialog");
    CursorToolkitTwo.startWaitCursor(dialog.getRootPane());
  }

  public static void main(String[] args) {
    final JFrame frame = new JFrame("Solution One");
    frame.getContentPane().add(
      new JLabel("I'm a Frame"), BorderLayout.NORTH);
    frame.getContentPane().add(
      new JButton(new AbstractAction("Show Dialog") {
        public void actionPerformed(ActionEvent event) {
          System.out.println("Showing dialog");
          createDialog(frame).show();
        }
      }));
    frame.setSize(800, 600);
    frame.setVisible(true);
  }
}
I'm not going to discuss all the Swing code; basically a JFrame is created that contains a button. When pressed, this button will show a dialog that contains a button. If this dialog button is pressed, then the setWaitCursors method will be called, which attempts to set the wait cursor on both the dialog and it's parent frame by using our CursorToolkitTwo.
Run it. Press the buttons. It doesn't work :-(. Yes, the wait cursor is set on the dialog, but not on the frame behind it.
Why not?! Well, as soon as a modal dialog is displayed, the current AWT event pump (the mechanism that handles mouse, keyboard and other events) is blocked, and a new event pump is started. As soon as the modal dialog is closed, the previous event pump is unblocked. This means that if the modal dialog in SolutionOne is closed, the wait cursor will suddenly be set on the frame. "Betterlate than never" they say, but this is an example of "better never than late" :-)

Solution 2

There are a few ways around this. If you have access to the code that calls the dialog, but not to the dialog code, you can try to first set the wait cursor on the dialog's parent (the JFrame in our example) before displaying the dialog.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** 
 * This is a second attempt - but native code changes the 
 * cursor back to the default cursor.
 */
public class SolutionTwo {
  private static JDialog createDialog(final JFrame frame) {
    final JDialog dialog =new JDialog(frame, "I'm Modal", true);
    dialog.getContentPane().add(
      new JLabel("I'm a busy modal dialog"));
    dialog.getContentPane().add(
      new JButton(new AbstractAction("Wait Cursor"){
        public void actionPerformed(ActionEvent event) {
          setWaitCursor(dialog);
        }
      }));           
    dialog.setSize(300, 200);
    return dialog;
  }

  private static void setWaitCursor(final JDialog dialog) {
    System.out.println("Setting Wait cursor on dialog");
    CursorToolkitTwo.startWaitCursor(dialog.getRootPane());
  }

  public static void main(String[] args) {
    final JFrame frame = new JFrame("Solution Two");
    frame.getContentPane().add(
      new JLabel("I'm a Frame"), BorderLayout.NORTH);  
    frame.getContentPane().add(
      new JButton(new AbstractAction("Show Dialog") {
        public void actionPerformed(ActionEvent event){
          System.out.println("Setting Wait cursor on frame");
          CursorToolkitTwo.startWaitCursor(frame.getRootPane());
          System.out.println("Showing dialog");
          createDialog(frame).show();
        }
      }));
    frame.setSize(800, 600);
    frame.show();
  }
}
SolutionTwo is similar to SolutionOne, except for the changes listed above. The setWaitCursor method now only sets the wait cursor on the dialog, but in the showDialogAction.actionPerformed method, we set the wait cursor on the frame before showing the dialog.
Run it.
Another failure :-(
Why does this not work? We've set the wait cursor before the main event pump got blocked, and apparently Swing (or AWT) resets the cursor to the default cursor on the rest of the components (i.e. everything except the modal component). You can check the JDC Bug Parade (bug nr 4282540) for their reasons why this is so.

Solution 3

Well, the easy way to fix this (if you have access to the dialog code) is to make the dialog non-modal, but then set the wait cursor on the frame before showing the dialog. It prevents access to the frame, while allowing the wait cursor to be set, and also allows the wait cursor to be set on the dialog. You then basically have SolutionTwo, but a non-modal dialog is now created.
Here is a WaitEnabledDialog that automatically sets the JFrame cursor to an hourglass whenever the dialog is opened and resets it to the default cursor when the dialog closes:
import java.awt.event.*;
import javax.swing.*;

public class WaitEnabledDialog extends JDialog {
  public WaitEnabledDialog(final JFrame owner, String title) {
    super(owner, title, false);
    addWindowListener(new WindowAdapter() {
      public void windowOpened(WindowEvent e) {
        CursorToolkitTwo.startWaitCursor(owner.getRootPane());
      }
      public void windowClosing(WindowEvent e) {
        CursorToolkitTwo.stopWaitCursor(owner.getRootPane());
      }
    });
  }
}
Our third solution now looks like so:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Here is a solution where we make the modal dialog non-modal.
 * Since we disable mouse clicks on the frame, it is actually
 * the same as a modal dialog.
 */
public class SolutionThree {
  private static JDialog createDialog(final JFrame frame) {
    final WaitEnabledDialog dialog = 
      new WaitEnabledDialog(frame, "I'm not Modal");
    dialog.getContentPane().add(
      new JLabel("I'm a busy non-modal dialog"));
    dialog.getContentPane().add(
      new JButton(new AbstractAction("Wait Cursor") {
        public void actionPerformed(ActionEvent event){
          setWaitCursor(dialog);
        }
      }));
    dialog.setSize(300, 200);
    return dialog;
  }

  public static void setWaitCursor(final JDialog dialog) {
    System.out.println("Setting Wait cursor on dialog");
    CursorToolkitTwo.startWaitCursor(dialog.getRootPane());
  }

  public static void main(String[] args) {
    final JFrame frame = new JFrame("Solution Three");
    frame.getContentPane().add(
      new JLabel("I'm a Frame"), BorderLayout.NORTH);
    frame.getContentPane().add(
      new JButton(new AbstractAction("Show Dialog") {
        public void actionPerformed(ActionEvent event){
          JDialog dialog = createDialog(frame);
          System.out.println("Showing dialog");
          dialog.show();
        }
      }));
    frame.setSize(800, 600);
    frame.show();
  }
}

Semi-modal Dialogs

Talking of non-modal dialogs, I came across the idea of "semi-modal" dialogs on the Web. Basically it's a less strict version of a modal dialog, in that certain specified components of the parent frame/window are still accessible, even though the semi-modal dialog is displayed. I did not fully agree with the situation it was used in: basically the author wanted to give users the option of cancelling the current operation (the semi-modal dialog gets input parameters from the user) by selecting another function on the frame's toolbar. Why not just add a "Cancel" button to the dialog? (GUI Law 3: the GUI should be as simple and intuitive as possible). I actually cannot think of any scenario where you would want to use a semi-modal dialog, however that might be because I wanted to use it for our wait cursor problem, but could not find a way in which it will be easier to use than Solution 3.
I must admit, though, the idea of a semi-modal dialog is an interesting one. You can check out the code as well as a discussion of a class called Blocker at JavaWorld. Blocker extends the java.awt.EventQueue class that handles the queueing and dispatching of AWT events. It allows one to register components that should be "blockable", and then you can enable or disable the blocking (i.e. switch between semi-modal and normal mode).

Threading solutions

Aha! Why not use multiple threads and trick AWT into keeping the wait cursor on the frame, while showing a modal dialog? Because it's a very bad idea to have more than one event pump working, and even if you don't call dialog.setVisible(true) from the main Swing/AWT thread, it will still block the main EventDispatchThread. You can update SolutionTwo to set the wait cursor on the frame, and then start another thread that sleeps a few hundred milliseconds before displaying the dialog. The wait cursor will be visible on the frame while the specified number of milliseconds tick off, and then behold: it is once again a default cursor just as the dialog appears.

And the Keyboard?

You probably noticed that I did not mention keyboard input at all. Well, the above solutions only prevent mouse input. Go have a look at KeyboardFocusManager as an exercise (if you're using JDK 1.4+).
Happy waiting until next time :-)

created by Herman Lintvelt (Polymorph Systems)

 
This Theme Modified by Kapten Andre based on Structure Theme from MIT-style License by Jason J. Jaeger