Good catch Johannes! I miss you and the rest of the gang too. Remember I'll always be around until you've closed all the tickets I ever filed. :) Happy holidays and best wishes to the ImageJ2 team.
On Fri, Dec 13, 2013 at 11:12 PM, Johannes Schindelin < [email protected]> wrote: > Hi all, > > I dedicate this mail to Barry because he reported the issue first, and > besides, I miss him ;-) > > The issue Barry pointed out earlier, on Ubuntu, was that ImageJ2 started > behaving strangely with regards to the menu: as soon as the mouse was > hovering over the tool bar, the menu would go away. > > Back then, I could not reproduce the issue, but today I could, so I went > on a hunt, and here is the write-up for everybody to enjoy: > > First of all, I figured that I should find out whether some strange AWT > events were processed, so I fired up Eclipse -- because I wanted to use > Eclipse's debugger to investigate the issue -- and added this code to the > end of AbstractSwingUI#createUI(): > > -- snip -- > Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { > > @Override > public void eventDispatched(AWTEvent event) { > System.err.println("event: " + event); > } > }, -1); > -- snap -- > > Basically, this code asks Java to output all the UI events as they > happen. > > Then, I set a breakpoint on the first line and started ImageJ2 in debug > mode. A little-known gem is that Eclipse's Console window not only shows > the standard output and standard error stream of the debugged program, > but also accepts the standard input. I used this fact by adding some > empty lines (simply focusing the Console window and hitting the Return > key a couple of times) as soon as the main window was visible because > the events that had been shown so far could not be responsible (or so I > thought). > > But after moving the mouse over the toolbar (and the menu bar > vanishing), really only the obvious events were shown: mouse enter, > mouse moves (and then mouse leave). > > So I imagined that another way to debug the issue might be to look > whether there actually *was* a menu bar still when the tool bar was > painted after the mouse entered the main window. > > To this end, I patched in a paintComponent method into the SwingToolBar > class: > > -- snip -- > @Override > public void paintComponent(final Graphics g) { > Container c = getParent(); > c = c.getParent(); > c = c.getParent(); > c = c.getParent(); > System.err.println(c); > System.err.println(((JFrame)c).getJMenuBar()); > super.paintComponent(g); > } > -- snap -- > > Of course, that was not the initial version. The initial version assumed > that already the direct parent container of the tool bar would be the > JFrame, and in hindsight, I'd better have written something like: > > Container c = getParent(); > while (c != null && !(c instanceof JFrame)) { > c = c.getParent(); > } > > but hindsight is 20/20 and I did not have the luxury of that yet. > However, I had the luxury of incremental compilation (as performed by > Eclipse, one of the few things I really like about Eclipse): I could set > a breakpoint on the "System.err.println(c);" call, launch ImageJ2 in debug > mode, and then insert the "c = c.getParent();" lines one by one, saving > them, which would trigger Eclipse's incremental compiler and move > execution back to the first line of the method, until I had the right > amount. > > And sure enough, the first time round, there was a menu bar, but after > moving the mouse over the main window the menu bar was null! > > Therefore the next step was to find out when it was set to null. To find > out, I edited the SwingApplicationFrame class (which I found out from > inspecting the "c" variable in the SwingToolBar#paintComponent(Graphics) > method above) like so: > > -- snip -- > @Override > public void setJMenuBar(final JMenuBar bar) { > super.setJMenuBar(bar); > } > -- snap -- > > and -- you probably guessed it by now -- added a breakpoint on the > "super.setJMenuBar(bar);" line. > > It turns out that it is called the first time from > AbstractSwingUI#createUI(), as expected, but then -- unexpectedly -- > from MacOSXPlatform#onEvent(WinActivatedEvent)! On Linux, no less... > > There we have the culprit. As I found out by inserting > > -- snip -- > if (c.getName().endsWith("WinActivatedEvent")) { > System.err.println(); > } > -- snap -- > > into the DefaultEventService#subscribe(Class, EventSubscriber) method > and -- you probably guessed that again -- setting a breakpoint on the > "System.err.println();" line (after importing scijava-common into the > Eclipse workspace and adjusting ImageJ2's pom.xml to set the > scijava-common.version property to point to the checked out version), > MacOSXPlatform is registered as an event listener. > > This is the consequence of some subtle, recent change that at the same > time a Context is injected, the class is also automatically registered > as event listener if it has @EventHandler-annotated methods). > > Now, MacOSXPlatform's onEvent() method does not verify that the current > platform is actually MacOSX, nor does the EventService "forget" the > MacOSXPlatform as one would expect when the MacOSXPlatform is > garbage-collected after it has been determined that it does not match > the current platform because the PlatformService is now a > SingletonService that does not let go of its instances... > > So we actually need two changes to fix this: > > 1) MacOSXPlatform#onEvent(WinActivatedEvent) needs to be safe, and > > 2) the DefaultPlatformService must not keep references to Platform > instances that are not applicable to the currently-running platform. > > https://github.com/imagej/imagej/commit/c814f4b0 addresses 1) while > https://github.com/scijava/scijava-common/commit/46f64db2 and > https://github.com/scijava/scijava-common/commit/522f524 address 2) > > Ciao, > Dscho >
_______________________________________________ ImageJ-devel mailing list [email protected] http://imagej.net/mailman/listinfo/imagej-devel
