The Source for Java Technology Collaboration
User: Password:



Hans Muller

Hans Muller's Blog

Dialog Diatribe

Posted by hansmuller on October 27, 2006 at 05:48 PM | Comments (23)

I've been writing the occasional small application recently and now and then I blunder into a problem with Java SE that's, uh..., well, annoying. I realize that I'm not the only one who's had this experience and I'm probably not the only one who seeks relief by writing a lengthy diatribe and then sending it to whomever might be guilty of creating the situation. Of course, in my case that's often me, and since relief usually doesn't come from berating oneself, I'm guilty of sending the occasional long crabby missive to the people who are currently responsible for maintaining things that I'm probably responsible for bollocksing up in the first place. It's not a particularly endearing habit.

I sent the following to Swing's technical lead, Shannon Hickey, and he confirmed that the details, though twisted with bile, are essentially correct. So in the interest of furthering my own therapy, and also to ensure that some record of this will be stored away in Google's indices till the end of time, I thought I'd share.

I would think that a fairly common idiom in a Swing application would be to popup a dialog in response to selecting a menu item. Given Matisse, we'll assume that the JDialog has been created with the IDE, rather than some JOptionPane convenience method, and given rudimentary aesthetics, assume the dialog should be centered over the menu item's frame. Accomplishing this seems to be much too difficult:

public void showMyDialog(ActionEvent e) {
    // How to find the Dialog's Frame owner?
    Window dialogOwner = null;
    JDialog dialog = new MyDialog(dialogOwner, true); // true => modal Dialog
    dialog.pack();
    // How to center the Dialog?
    dailog.setVisible(true);
}

The first problem to deal with is mapping from the menu item's ActionEvent to the frame that contains the menu item. The frame will be the dialog's owner as well as the component we're going to center the dialog relative to.

There seems to be an overabundance of SwingUtilities methods that address this trivial problem:

  • Window getWindowAncestor(Component c)
  • Window windowForComponent(Component c)
  • Component getRoot(Component c)
Sadly, none of them "work" for a JMenuItem. They all simply traipse up the parent chain and in our case they find a JPopupMenu and then null.

To find the Frame that owns a JMenuItem, we have to follow the JPopupMenu's "invoker" property, which gets us back into the component hierarchy. So to find the frame that corresponds to an ActionEvent one must write (!):

Frame frameForActionEvent(ActionEvent e) {
    if (e.getSource() instanceof Component) {
        Component c = (Component)e.getSource();
        while(c != null) {
            if (c instanceof Frame) {
	        return (Frame)c;
            }
            c = (c instanceof JPopupMenu) ? ((JPopupMenu)c).getInvoker() : c.getParent();
        }
    }
    return null;
}

It would more useful to have written windowForActionEvent but sadly support for creating Dialogs whose owner is a Window (the parent class for Frames and Dialogs) only appeared in Java SE 6, and I needed code that worked for Java SE 5. It's also worth noting that this works for Applets too, although you'd be forgiven for not guessing that this is true. Applets do have a Frame parent that's created by the Java plugin and whose bounds are the same as the Applet (Panel) itself.

But we're still not done, because we must also center the dialog over the frame. Naturally there are other useful positions for the dialog. Centering a dialog over its frame happens to be what started me on this quest.

I have it on good authority that Windows.setLocationRelativeTo() is the handy method for this job. The javadoc for this method isn't promising:

Sets the location of the window relative to the specified component.

OK so far. Except it sounds like I'm going to have to compute the relative origin of my dialog and deal with edge (of the screen) conditions. Yech.

If the component is not currently showing, or c is null, the window is placed at the center of the screen. The center point can be determined with GraphicsEnvironment.getCenterPoint [sic]

Huh? What does "the component" refer to in this sentence? I assume they're not referring to this Window and I have to wonder what "showing" means in this context. Is is the same thing as getVisible() being true? If not, do I have to hope that my menu item is still "showing" when this method is called? And what's this advice about getCenterPoint (and where's the period)? In my case the Window and its menu item are visible, so I'm hoping none of this stuff applies. Because I don't really understand it.

If the bottom of the component is offscreen, the window is placed to the side of the Component that is closest to the center of the screen. So if the Component is on the right part of the screen, the Window is placed to its left, and visa versa.

This, no doubt, means that the method will endeavor to find a location for my dialog that respects the relative location I've specified, without making part of the dialog appear off-screen. Good, I think.

So I still appear to be stuck with computing an origin for my dialog that centers it relative to its owner. Before I code that, I try leaving the origin of the new dialog at 0,0, which is the default:

public void showMyDialog(ActionEvent e) {
    Window dialogOwner = frameForActionEvent(e);
    JDialog dialog = new MyDialog(dialogOwner, true);
    dialog.pack();
    aboutBox.setLocationRelativeTo(dialogOwner);
    dialog.setVisible(true);
}

Miraculously, this works. The dialog appears centered over the dialogOwner unless that would cause the dialog to appear off-screen. I have no idea why it works, since according to the "spec" (and the name of the method) I should have had to compute an appropriate relative origin for the dialog. But I guess I don't.

Frankly, I think this whole mess is a mini-travesty. If I'm going to show a dialog, I should be able to do so without writing code that digs around the component hierarchy and without experimentally determining what something as simple (and not terribly useful) as Window.setLocationRelativeTo() does.

There, that feels a little better.

A seven year old bug that covers the menu item to frame lookup problem is still open. Given the fact that it's accumulated exactly 0 votes in that time, perhaps no one has ever cared about the problem quite as much as I do at this moment. I would think that a cleaner way to handle this case would be some static methods that handled the entire idiom, for example:

public void showMyDialog(AWTEvent event) {
    Window dialogOwner = Window.eventToWindow(event);
    JDialog dialog = new MyDialog(dialogOwner, true);
    Window.showModalDialog(dialog); // Center dialog relative to its owner
}

And Shannon suggested that the method name might be rationalized as implying that the Window is be moved to a location that makes its relationship to the component parameter obvious. Typically that means centering the Window relative to the component. In return for that tortured explanation, I had to agree to file an RFE about the Window.setLocationRelativeTo() javadoc. I haven't done so yet. But I will.

Thanks for listening.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • How about Window.showModalDialog(window, location, effect) where location is north, south, east, west, or center. Center is obvious. North gives you mac-like dialog on the top. 'Effect' could be something like a window slide-in (again like on a mac), or alpha fade-in.

    (just dreamin')

    jeff

    Posted by: jdinkins on October 27, 2006 at 06:11 PM

  • "Given the fact that it's accumulated exactly 0 votes in that time, perhaps no one has ever cared about the problem quite as much as I do at this moment."

    Or, more likely, they just have three other things they care about more.

    Posted by: coxcu on October 27, 2006 at 08:09 PM

  • > "Given the fact that it's accumulated exactly 0 votes in that time"

    I wonder when, if ever, Sun is going to understand that the voting systems doesn't work. It has been said again and again and for instance on every JavaOne alumni meeting I've attended. The Bug Charade and everything around it doesn't work. Period. It's a usability disaster and basically gives nothing back to the developer, so why bother?

    OK, I haven't gotten my coffee yet. Sorry.. ;-)

    Cheers,
    Mikael.

    Posted by: mikaelgrev on October 28, 2006 at 01:23 AM

  • I ran into this quite painfully ;-) I just replaced the SwingUtilities.windowForComponent() calls with my own utility class. In this class I made the assumption that the only time this method won't find a window is when the main window is involved (I don't have popups in my dialogs) so I just used Frame.getFrames() found the first visible frame (only one visible top level frame at any time) and displayed on that... Terrible hack but it worked for me.
    I would have submitted a bug or looked for one but finding a bug like that is very hard within the bug database especially when you are cramped for time on a release... Its a bit of a problem to find a bug in such a huge database that has such "odd" categories (not that bugzilla is better).

    Posted by: vprise on October 28, 2006 at 03:15 AM

  • Dear Hans,

    In case you do any changes to the JOptionPane API, please consider implementing support for document modal dialogs.

    In J2SE6, we finally got support for this in AWT class Dialog. But without some higher level support in Swing, I fear this API will be underused.

    I implemented an API for document modal dialogs and file choosers specifically for Mac OS X some time ago: JSheet.

    Best, Werner

    Posted by: wrandelshofer on October 29, 2006 at 07:29 AM

  • > Huh? What does "the component" refer to in this sentence?
    This "component" is the component passed as a parameter to the method setLocationRelativeTo()

    > I have to wonder what "showing" means in this context
    isShowing() is a method in java.awt.Component class. It is used to verify that a screen location for the component can be queries, see getLocationOnScreen() method in java.awt.Component class

    Posted by: ixmal on October 30, 2006 at 05:48 AM

  • good article......solved a whole lot of probs.....

    Jaji

    Posted by: lukman_jaji on October 30, 2006 at 09:46 AM

  • I always delegated the action item to a mediator class.

    This allowed me the option of creating a static dialog (preferred) or creating a new one on each event call.

    My mediator always had a static method for getting the Frame which you had to have laying around.

    The version of TFrame on the java.net site is pretty old. It actually had a slew of Action Items and they were created with a reference back to the main Frame object.

    So I would create the Action Items when I created the Toolbar and MenuBar.

    Then after creating all of that I would reference these with the mediator.

    The hardest thing in a large Swing application is order of contruction and an easy way to get to something.

    I've never been a big fan of putting business logic in a listener tied to a component unless it was very simple. Anything complex I always registered the component and delegated listeners to a mediator class.

    I would keep a static hash of ActionItems and JComponents.

    Then I could from anywhere update, change or notify what ever I needed.

    There is NOTHING worse than being in a very large Swing application and someone wanting you to disable or enable components based on an event that is 3 dialogs/panels away.

    Let me know if you need more info but the problem you mention is why everyone doing serious Java Swing applications wrote their own custom framework. ;-)


    This problem is old news. ;-) My biggest gripe these days is how to easily deal with multiple monitor displays. The video driver for my Matrox G450 tends to give interesting values back to the JVM.


    Regards,
    cupofjoe

    Posted by: cupofjoe on October 31, 2006 at 08:53 AM


  • I've been out for a few days and haven't been able to respond to any
    of the comments. Will do so now; hopefully someone will take a look.

    Posted by: hansmuller on November 01, 2006 at 10:35 AM

  • [response to Jeff Dinkins], per:
    Window.showModalDialog(window, location, effect)
    What you're suggesting would definitely be an improvement. The
    overall goal has to be making the common cases trivially, as well as
    make it easy to "do the right thing". I've often shuddered to see a
    slapped together app popup a dialog in the upper left hand corner of
    the screen. I wish that was a little more difficult :-).

    Posted by: hansmuller on November 01, 2006 at 10:36 AM

  • [response to Mikael Grev] per:


    ... The Bug Charade and everything around it doesn't
    work. Period. It's a usability disaster and basically gives nothing
    back to the developer, so why bother?


    OK, I haven't gotten my coffee yet. Sorry.. ;-)

    It's been a few days, no doubt you've had a cup of coffee now. I
    doubt that anyone thinks that the Bug Parade is the apotheosis
    of bug/rfe management. How would you improve it?

    Posted by: hansmuller on November 01, 2006 at 10:37 AM

  • [response to vprise] Sorry to hear that you had to hack your
    way through this little thicket too. I agree that finding an existing
    bug that matches a problem that you've run into is a difficult, I have
    to admit that Google is often a more direct path to developers who've
    had similar problems and who may have left a solution behind.

    Posted by: hansmuller on November 01, 2006 at 10:38 AM

  • [response to Werner Randelshofer]
    I'd be interested to hear what sort of higher level support for
    (document) modal dialogs you think would be worth adding to Swing. As
    far (just a quick look) JSheet provides roughly the same sort of
    conveneince methods as JOptionPane.

    Posted by: hansmuller on November 01, 2006 at 10:38 AM

  • [response to cupofjoe]
    The point of my blog was just that the common idiom I'd coded
    shouldn't have required so much effort, e.g. one shouldn't need a
    custom application framework for something so simple. Of the many
    valid points you made, I particularly liked this one:

    The hardest thing in a large Swing application is order of contruction and an easy way to get to something.

    I've found this to be true too, and the difficulty is almost always
    self-inflicted. I hope we'll find a way to for most developers to
    get this kind of thing right with JSR-296.

    Posted by: hansmuller on November 01, 2006 at 10:39 AM

  • Hans,
    I'm of course talking about the version that we as users see, you might have another system on the inside? Basically everything with it is bad. And yes, I have gotten plenty of coffee.

    The thing is that there is no way to communicate around a bug in a decent manner. The bug forum software is buggy and has no threads (you can't see who is responding to what). You don't know who the evaluator is (xxxxxx@xxxxx.xxx). The voting system is very inflexible and it takes no consideration regarding how much time and effort you actually is spending. A newbie and a really really helpful contributor all have three votes, in all. If it is like Sun say, that the voting has a deep effect on your priority for a fix, I am scared.

    There is no formal way to reopen a bug. Everything feels 1997 in there. There are only "open" and "closed" categories, several more should be added. Search is a joke (and a bad one at that).

    Anyone with even a little bit of usability training can find things to improve when it comes to UI.

    I've filed a few bugs in the past but the process was so time consuming and gave nothing back so I work around bugs in silent nowadays.

    Here is some more I've written before (under mgrev):
    http://today.java.net/pub/pq/24

    Cheers,
    Mikael Grev

    Posted by: mikaelgrev on November 01, 2006 at 01:42 PM

  • Here is some more info regarding the bug parade: http://www.javalobby.org/java/forums/m91817273.html#91817273

    Posted by: mikaelgrev on November 01, 2006 at 01:45 PM

  • More discussion about what is wrong with bug parade:
    http://weblogs.java.net/blog/robogeek/archive/2006/09/bug_tracking_sy.html

    Posted by: coxcu on November 02, 2006 at 08:31 AM

  • [response to Mikael Grev] per improving the bug parade.
    I've looked at the bugtraq comments that you

    referred to and also at

    Dave Herron's blog on this topic. I hope you can appreciate that
    I didn't intend to inspire a debate about bug tracking by writing this
    blog. It was really all about a small missing link in Swing that had
    to do with popping up a dialog from a menu item's action.
    Nevertheless, I did ask for some suggestions about improving the bug parade,
    and you are on record with some sensible ideas (I'm paraphrasing):



    Provide a rich web started client for interacting with the bug
    database. You mentioned this three times.

    We use such a client within Sun and I wish there was a public
    version for everyone else to use instead of the clunky browser
    bug parade client.


    Give people a way to weigh their votes, e.g. a scale from -5
    (least important) to +5 (most important), and don't limit
    the number of bugs a developer can vote on.

    This sounds reasonable however given the large community that
    uses the bug database, I suspect that it would be difficult to
    ensure that everyone used a more flexible system consistently.


    Automatically detect voters who are trying to game the
    system.

    No doubt by peering into their souls with a web cam :-).
    Seriously: I agree that heuristic could be applied that might
    appear to balance competing interests as reflected by voting.
    I think it's more likely that what you'd really be doing is
    just complicating the rules of a game.


    Thread comments properly: it should be possible to reply to
    a specific comment/author.

    I completely agree.


    Identify users by role, e.g. Sun developer/manager/politician
    or "senior poster", "poster", "newbee" etc.

    I'm the last person you'd want to ask about how to set up
    a system for assigning such roles however I agree that it
    might help one interpret bug descriptions and comments and
    so on.


    Fine grained access controls by role. For example only
    some kinds of voting would be limited to users with specific roles.

    There's a little of this implicit in the existing system.
    I suspect that trying to institute a more elaborate system of
    access controls would be a wee bit controversial.


    Polling: give users an opporunity to take the pulse of everyone
    who's interested in a particular bug.

    If used responsibly I think "custom polling" would make it much
    easier to summarize developers' feedback. It could also become
    a tool for rabble rousing.

    Posted by: hansmuller on November 02, 2006 at 10:18 AM

  • [response to hansmuller] [...]I'd be interested to hear what sort of higher level support for (document) modal dialogs you think would be worth adding to Swing [...] JSheet provides roughly the same sort of conveneince methods as JOptionPane.

    JSheet adds a SheetListener to the convenience methods of JOptionPane and of JFileChooser. The JOptionPane pane methods return after the user has approved or dismissed the dialog. Whereas the JSheet methods return immediately, and later the SheetListener is notified when the user has approved or dismissed the dialog.

    Before using this approach, I tried Foxtrot, but for document modal dialogs, the approach that Foxtrot uses does not work at all.

    Thanks, Werner.

    Posted by: wrandelshofer on November 02, 2006 at 10:31 PM

  • Re. centering dialogs, don't forget that a naive implementation can look bad on a multiheaded display (Xinerama etc.) - you don't want the dialog split across two displays even if the owner frame is. Currently the NB IDE uses: http://www.netbeans.org/source/browse/~checkout~/openide/util/src/org/openide/util/Utilities.java#findCenterBounds

    Posted by: jglick on November 06, 2006 at 08:02 AM

  • exactly! Hopefully the new JSR will address that. Things like this have been driving me crazy for years. In response, we've ended up developing our own abstractions around the UI : SystemShell, Workspace, etc., b/c Swing shouldn't be that hard, and developing swing apps in a corporate environment is like trying to get a Lotus Notes developer these days, the ones that can be productive day one are hard to find (b/c the cost of being that good is too high in Swing). Personally, I can't wait for the app framework to gain traction. The desktop, is still IMO, a neglected area for Java. It's just been waiting for something better.

    Posted by: bcchun on November 08, 2006 at 05:10 AM

  • Sorry to intrude with an out of topic, but I'm starting to develop a new swing app and I would like to use the classes described in the JSR 296... if only these were available...
    Any hope to see them out soon?

    Posted by: aaime on November 18, 2006 at 09:01 AM

  • Hans listed these three methods that don't work on a JMenuItem:


    * Window getWindowAncestor(Component c)
    * Window windowForComponent(Component c)
    * Component getRoot(Component c)

    To these, I would add this one:
    * SwingUtilities.getRootPane(Component c)
    All of these use a loop based on a variation of parent = parent.getParent(). They all trip up when "parent' is a JPopupMenu. I believe all of these could be fixed if Sun override JPopupMenu.getParent() to call getInvoker(). Does anybody know if there's a downside to a fix like this?

    Posted by: miguelm on December 06, 2006 at 02:17 PM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds