DockingFrames 1.1.0: HierarchyListener in RootMenuPiece breaks MenuItems

Hi Ben,

I’m just switching from 1.0.7 to 1.1.0 and, unless I am missing something, it seems the new HierarchyListener on RootMenuPiece is trying to be too smart. It calls unbind() last, causing all ActionListeners to be removed, making the menu items for the dockables dysfunctional. See this minimal example:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.HierarchyListener;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.menu.SingleCDockableListMenuPiece;
import bibliothek.gui.dock.facile.menu.RootMenuPiece;


public class MenuPieceDemo extends JFrame {
	public static void main(String[] args) throws InterruptedException, InvocationTargetException {
		SwingUtilities.invokeAndWait(new Runnable() {
			@Override
			public void run() {
				new MenuPieceDemo().start();				
			}
		});
	}
    private final CControl dockingControl = new CControl(this);
	
	private static class ColorPanel extends JPanel {
		ColorPanel() {
			setBackground(new Color(new Random().nextInt()));
		}
	}
	private DefaultSingleCDockable createDockable(final String id)    {
        DefaultSingleCDockable tempdockable = new DefaultSingleCDockable(id, null,"Dock"+id, new ColorPanel());
        dockingControl.addDockable(tempdockable);
        tempdockable.setCloseable( true );
        tempdockable.setTitleToolTip("Description for "+id);
        return tempdockable;
        
    }

	private void start() {
		setTitle(getClass().getSimpleName());
		setSize(800, 600);
		setLayout(new BorderLayout());
		setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        add(dockingControl.getContentArea(), BorderLayout.CENTER);
        
        final DefaultSingleCDockable d1 = createDockable("One");
        final DefaultSingleCDockable d2 = createDockable("Two");
        final DefaultSingleCDockable d3 = createDockable("Three");
        CGrid grid = new CGrid(dockingControl);
        grid.add(0, 0, 1, 1, d1); 
        grid.add(0, 1, 1, 1, d2, d3);  
        dockingControl.getContentArea().deploy(grid);
        
        initMenu();
        setVisible(true);
	}

	private void initMenu() {
		final JMenu viewMenu = new JMenu("View"); //$NON-NLS-1$
        viewMenu.setMnemonic(KeyEvent.VK_A); // TODO localization
        final RootMenuPiece settings = new RootMenuPiece( "Subviews", true  );
        final SingleCDockableListMenuPiece piece = new SingleCDockableListMenuPiece( dockingControl );
        settings.add(piece);
//        without this, the menu doesn't work
//        for(final HierarchyListener l : settings.getMenu().getHierarchyListeners()) {
//        	settings.getMenu().removeHierarchyListener(l);
//        }
//        piece.bind();
        
        for(int c=0; c<settings.getChildrenCount(); c++) {
            viewMenu.add(settings.getChild(c).getMenu());
        }
        final JMenuBar menuBar = new JMenuBar();
        menuBar.add(viewMenu);
		setJMenuBar(menuBar);
	}
}

Peace to you!

Jakob

Unfortunately the RootMenuPiece must really be a root JMenu, meaning it must be a direct child of a JMenuBar. Like this:

    	final RootMenuPiece viewMenu = new RootMenuPiece( new JMenu( "View" ) );
    	viewMenu.getMenu().setMnemonic(KeyEvent.VK_A); // TODO localization
        final SubmenuPiece settings = new SubmenuPiece( "Subviews", true  );
        final SingleCDockableListMenuPiece piece = new SingleCDockableListMenuPiece( dockingControl );
        settings.getRoot().add(piece);
//        without this, the menu doesn't work
//        for(final HierarchyListener l : settings.getMenu().getHierarchyListeners()) {
//          settings.getMenu().removeHierarchyListener(l);
//        }
//        piece.bind();
       
        viewMenu.add(settings);
        
        final JMenuBar menuBar = new JMenuBar();
        menuBar.add(viewMenu.getMenu());
        setJMenuBar(menuBar);
    }```

If you need to add some custom JMenuItems between the menu-pieces, you can use a "FreeMenuPiece" to wrap them up.

Hm, that’s a pity and a deterioration in comparison to 1.0.7. Please consider fixing this. For now, my workaround helps me.

You are probably right, but the old solution was not good either. It introduced a memory leak. I’ll fix it in the next version and write an answer.

Alright, your old code should work again with 1.1.1p7a. I’ll upload the new version on the weekend.