From 7e1371a36ce1150177d2a63f09f3ff7585e751d6 Mon Sep 17 00:00:00 2001 From: Max Schenkelberg Date: Tue, 7 Feb 2012 09:15:13 -0600 Subject: [PATCH] Issue #206 - Fixed loading and disposing bugs in tear off menus Issue #206 Fixed bugs in maps menu and other dynamically created menus Former-commit-id: eb2ecaaab4826ae3fbc6139a7dd7f1c56c2bbde8 [formerly 885f291b352bd9c285342746e601908a14d67aae] [formerly be37f19c90acc9d3b772b44dc0922237f1de021e] [formerly be37f19c90acc9d3b772b44dc0922237f1de021e [formerly 370e20f0ae7e7c3f99085c0219918e9a370dfabd]] [formerly eb2ecaaab4826ae3fbc6139a7dd7f1c56c2bbde8 [formerly 885f291b352bd9c285342746e601908a14d67aae] [formerly be37f19c90acc9d3b772b44dc0922237f1de021e] [formerly be37f19c90acc9d3b772b44dc0922237f1de021e [formerly 370e20f0ae7e7c3f99085c0219918e9a370dfabd]] [formerly a7ccea5edac03fc8b5addaba04916e12911ac994 [formerly be37f19c90acc9d3b772b44dc0922237f1de021e [formerly 370e20f0ae7e7c3f99085c0219918e9a370dfabd] [formerly a7ccea5edac03fc8b5addaba04916e12911ac994 [formerly 875c90104e80d95798956330e24b59d83ef52942]]]]] Former-commit-id: a7ccea5edac03fc8b5addaba04916e12911ac994 Former-commit-id: 215193f8d676315973e5fa0640dcfd4a817da54d [formerly 26b5acd957b5b2d7ec3dffe175e932218bd8de59] [formerly 825bd76f55160e9185531f0a5c10bd6e01388fa5] [formerly 7b710c402a49ea8adcdf21a1450bf5a45bb2188b [formerly ead10f07881d137ecc80314da8c248c4e77e5f1b] [formerly 825bd76f55160e9185531f0a5c10bd6e01388fa5 [formerly a6622153d798dd063338d5b0dcf0281437560266]]] Former-commit-id: 18e144f5bd9f49a3902d5b42b72311ccd899979d [formerly ab8f916eb71b9c4a01d3b1b2acdc2d21f22703d0] [formerly a6c4e04126f99f22f23bdf55734f22e23ff752f2 [formerly 4ba7705369ca29fbcbdf263b6dbc08cbaa9adab4]] Former-commit-id: a6c4e04126f99f22f23bdf55734f22e23ff752f2 Former-commit-id: 18662550ee4e217a56aee9be40354a420e89a92a --- .../uf/viz/core/maps/menus/MapsMenu.java | 42 +- ...ctTearOffableCompoundContributionItem.java | 82 ++++ .../widgets/tearoff/MenuItemComposite.java | 423 +++++++----------- .../widgets/tearoff/TearOffMenuDialog.java | 54 +-- .../widgets/tearoff/TearOffMenuListener.java | 104 +++-- .../tearoff/TearOffPreferencePage.java | 3 +- .../awips/VizWorkbenchWindowAdvisor.java | 7 +- 7 files changed, 365 insertions(+), 350 deletions(-) create mode 100644 cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/AbstractTearOffableCompoundContributionItem.java diff --git a/cave/com.raytheon.uf.viz.core.maps/src/com/raytheon/uf/viz/core/maps/menus/MapsMenu.java b/cave/com.raytheon.uf.viz.core.maps/src/com/raytheon/uf/viz/core/maps/menus/MapsMenu.java index c8f9936d7f..d50d0fc7de 100644 --- a/cave/com.raytheon.uf.viz.core.maps/src/com/raytheon/uf/viz/core/maps/menus/MapsMenu.java +++ b/cave/com.raytheon.uf.viz.core.maps/src/com/raytheon/uf/viz/core/maps/menus/MapsMenu.java @@ -22,16 +22,15 @@ package com.raytheon.uf.viz.core.maps.menus; import java.util.HashMap; import java.util.Map; -import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.actions.CompoundContributionItem; import org.eclipse.ui.menus.CommandContributionItem; import org.eclipse.ui.menus.CommandContributionItemParameter; import com.raytheon.uf.viz.core.maps.MapStore; import com.raytheon.uf.viz.core.maps.MapStore.MapNode; -import com.raytheon.uf.viz.ui.menus.widgets.tearoff.TearOffMenuListener; +import com.raytheon.uf.viz.ui.menus.widgets.AbstractTearOffableCompoundContributionItem; /** * TODO Add Description @@ -49,31 +48,33 @@ import com.raytheon.uf.viz.ui.menus.widgets.tearoff.TearOffMenuListener; * @version 1.0 */ -public class MapsMenu extends CompoundContributionItem { +public class MapsMenu extends AbstractTearOffableCompoundContributionItem { + + /** + * @param text + * @param id + */ + public MapsMenu() { + super("Maps", MapsMenu.class.getName()); + } boolean addTear = false; /* * (non-Javadoc) * - * @see - * org.eclipse.ui.actions.CompoundContributionItem#getContributionItems() + * @see com.raytheon.uf.viz.ui.menus.widgets. + * AbstractTearOffableCompoundContributionItem + * #addContributionItems(org.eclipse.jface.action.IMenuManager) */ @Override - protected IContributionItem[] getContributionItems() { + protected void addContributionItems(IMenuManager manager) { MapNode node = MapStore.getMapTree(); - return createMenu(node).getItems(); + createMenu(manager, node); } - private MenuManager createMenu(MapNode root) { - MenuManager menuMgr = new MenuManager(root.getName()); + private void createMenu(IMenuManager manager, MapNode root) { for (MapNode node : root.getSubTree()) { - if (addTear - && com.raytheon.uf.viz.core.Activator.getDefault() - .getPreferenceStore().getBoolean("tearoffmenus")) { - menuMgr.addMenuListener(new TearOffMenuListener(menuMgr)); - addTear = false; - } if (node.getSubTree() == null) { Map parms = new HashMap(); parms.put("mapName", node.getName()); @@ -87,12 +88,13 @@ public class MapsMenu extends CompoundContributionItem { parms, null, null, null, node.getName(), null, null, CommandContributionItem.STYLE_CHECK, null, true)); - menuMgr.add(item); + manager.add(item); } else { - addTear = true; - menuMgr.add(createMenu(node)); + IMenuManager subMenu = new MenuManager(node.getName(), + manager.getId() + "." + node.getName()); + createMenu(subMenu, node); + manager.add(subMenu); } } - return menuMgr; } } diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/AbstractTearOffableCompoundContributionItem.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/AbstractTearOffableCompoundContributionItem.java new file mode 100644 index 0000000000..39b1debe64 --- /dev/null +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/AbstractTearOffableCompoundContributionItem.java @@ -0,0 +1,82 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.viz.ui.menus.widgets; + +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.ui.actions.CompoundContributionItem; + +import com.raytheon.uf.viz.ui.menus.widgets.tearoff.TearOffMenuListener; + +/** + * TODO Add Description + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 6, 2012            mschenke     Initial creation
+ * 
+ * 
+ * + * @author mschenke + * @version 1.0 + */ + +public abstract class AbstractTearOffableCompoundContributionItem extends + CompoundContributionItem { + + private String text; + + private String id; + + private TearOffMenuListener listener; + + protected AbstractTearOffableCompoundContributionItem(String text, String id) { + this.text = text; + this.id = id; + listener = new TearOffMenuListener(); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.actions.CompoundContributionItem#getContributionItems() + */ + @Override + protected final IContributionItem[] getContributionItems() { + IMenuManager manager = new MenuManager(text, id); + addContributionItems(manager); + IContributionItem[] items = manager.getItems(); + TearOffMenuListener.register(items, listener); + return items; + } + + /** + * Internal method for retrieving contribution items to add to the menu. + * + * @param manager + */ + protected abstract void addContributionItems(IMenuManager manager); +} diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java index 8ce4f5bb56..2cb401a303 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java @@ -19,15 +19,19 @@ **/ package com.raytheon.uf.viz.ui.menus.widgets.tearoff; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; @@ -41,10 +45,6 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.ui.progress.UIJob; - -import com.raytheon.viz.ui.EditorUtil; -import com.raytheon.viz.ui.editor.AbstractEditor; /** * Holds the information for all the menu items in the dialog @@ -63,7 +63,9 @@ import com.raytheon.viz.ui.editor.AbstractEditor; * @version 1.0 */ -public class MenuItemComposite extends Composite { +public class MenuItemComposite extends Composite implements MenuListener { + + private boolean separator = false; private Control firstItem; @@ -78,15 +80,11 @@ public class MenuItemComposite extends Composite { private Listener updateListener = null; - private Listener showListener = null; + private SelectionListener radioListener = null; - private Menu menu = null; + private List myPath; - private Composite parent = null; - - private Menu topLevelMenu = null; - - private UIJob job = null; + private Menu topMostParent; /** * @param parent @@ -94,7 +92,6 @@ public class MenuItemComposite extends Composite { */ public MenuItemComposite(Composite parent, int style) { super(parent, style); - this.parent = parent; } // creates both labels and ties them together @@ -103,21 +100,27 @@ public class MenuItemComposite extends Composite { return; } - // going to hold the menu around so that if the cave menu gets opened - // again (when the items get disposed), we will be able to go back in - // and get the items from the menu and rebuild in the background - menu = it.getParent(); + myPath = new ArrayList(); - topLevelMenu = menu; - while (topLevelMenu.getParentMenu() != null - && topLevelMenu.getParentMenu().getParentMenu() != null) { - topLevelMenu = topLevelMenu.getParentMenu(); - } + // Build the menu path to the MenuItem from highest menu level + Menu parent = it.getParent(); + MenuItem toAdd = it; + do { + myPath.add(toAdd.getText()); + toAdd = parent.getParentItem(); + topMostParent = parent; + parent = parent.getParentMenu(); + } while (parent.getParentMenu() != null); + Collections.reverse(myPath); + + topMostParent.addMenuListener(this); item = it; + String[] labels = item.getText().split("\t"); // handle for a separator menu item if (item.getStyle() == SWT.SEPARATOR) { + separator = true; firstItem = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL); GridData gd = new GridData(SWT.FILL, SWT.DEFAULT, true, false); gd.horizontalSpan = 2; @@ -134,47 +137,8 @@ public class MenuItemComposite extends Composite { ((Label) secondItem).setText(labels[0]); gd = new GridData(SWT.LEFT, SWT.CENTER, true, true); secondItem.setLayoutData(gd); - item.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - if (e.widget instanceof MenuItem) { - // check that the radio groups match - for (Control comp : firstItem.getParent() - .getParent().getChildren()) { - MenuItemComposite composite = (MenuItemComposite) comp; - if (composite.item.getText().equals( - ((MenuItem) e.widget).getText())) { - if (composite.firstItem instanceof Button) { - ((Button) composite.firstItem) - .setSelection(composite.item - .getSelection()); - } - } else { - if (composite.firstItem instanceof Button) { - ((Button) composite.firstItem) - .setSelection(false); - } - } - } - } - } - }); - } - // check boxes - // else if (item.getStyle() == SWT.CHECK) { - // // if (item.getStyle() == SWT.CHECK) { - // firstItem = new Button(this, SWT.CHECK); - // ((Button) firstItem).setSelection(item.getSelection()); - // GridData gd = new GridData(18, 18); - // firstItem.setLayoutData(gd); - // - // secondItem = new Label(this, labelStyle); - // ((Label) secondItem).setText(labels[0]); - // gd = new GridData(SWT.LEFT, SWT.CENTER, true, true); - // secondItem.setLayoutData(gd); - // } - // submenus (with arrows) - else if (item.getStyle() == SWT.CASCADE) { + createRadioListener(); + } else if (item.getStyle() == SWT.CASCADE) { firstItem = new Label(this, SWT.PUSH); firstItem.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false)); @@ -195,108 +159,9 @@ public class MenuItemComposite extends Composite { ((Label) secondItem).setText(labels[1]); } - createUpdateListener(this); - item.addListener(SWT.Modify, updateListener); + // Create and add text update listener + createUpdateListener(); } - showListener = new Listener() { - @Override - public void handleEvent(final Event event) { - job = new UIJob(Display.getCurrent(), - "Regenerate Tear Off Menus") { - - @Override - public IStatus runInUIThread(IProgressMonitor monitor) { - if (parent == null || parent.isDisposed() - || parent.getShell() == null - || parent.getShell().isDisposed()) { - return Status.CANCEL_STATUS; - } - if (menu == null || menu.isDisposed()) { - for (MenuItem item : topLevelMenu.getItems()) { - - if (item.getMenu() != null) { - for (Listener list : item.getMenu() - .getListeners(SWT.Show)) { - try { - Event event = new Event(); - event.widget = topLevelMenu; - event.type = SWT.Show; - list.handleEvent(event); - } catch (Exception e) { - // do nothing - } - } - if (getShell().getText().equals( - item.getText().replaceAll("&", - ""))) { - menu = item.getMenu(); - break; - } - } - } - } - - int start = 0; - if (menu == null || menu.isDisposed() - || parent == null || parent.isDisposed() - || parent.getChildren() == null) { - return Status.CANCEL_STATUS; - } - if (menu.getItemCount() != parent.getChildren().length) { - start = (menu.getItemCount() - parent - .getChildren().length); - } - if (parent.getChildren().length > 0) { - for (int i = start; i < menu.getItemCount(); i++) { - final MenuItemComposite mic = (MenuItemComposite) parent - .getChildren()[i - start]; - if (mic.item.isDisposed()) { - mic.item = menu.getItem(i); - createUpdateListener(mic); - mic.item.addListener(SWT.Modify, - updateListener); - - for (Listener list : mic.item - .getListeners(SWT.Modify)) { - Event e = new Event(); - e.type = SWT.Modify; - e.data = mic.item; - list.handleEvent(e); - } - } - - mic.item.addSelectionListener(new SelectionAdapter() { - public void widgetSelected( - SelectionEvent e) { - if (e.widget instanceof MenuItem) { - if (mic.item.getText().equals( - ((MenuItem) e.widget) - .getText())) { - if (mic.firstItem instanceof Button) { - ((Button) mic.firstItem) - .setSelection(mic.item - .getSelection()); - } else { - if (mic.firstItem instanceof Button) { - ((Button) mic.firstItem) - .setSelection(false); - } - } - } - } - } - }); - } - } - - return Status.OK_STATUS; - } - }; - job.schedule(); - } - }; - item.getParent().addListener(SWT.Show, showListener); - topLevelMenu.addListener(SWT.Show, showListener); if (item.isEnabled()) { // add the listeners to both the first and the second @@ -316,21 +181,66 @@ public class MenuItemComposite extends Composite { SWT.COLOR_DARK_GRAY)); } } + + addItemListeners(); } - protected void createUpdateListener(final MenuItemComposite mic) { + /** + * + */ + private void addItemListeners() { + if (updateListener != null) { + item.addListener(SWT.Modify, updateListener); + } + if (radioListener != null) { + item.addSelectionListener(radioListener); + } + } + + /** + * + */ + private void createRadioListener() { + radioListener = new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (e.widget instanceof MenuItem) { + // check that the radio groups match + for (Control comp : firstItem.getParent().getParent() + .getChildren()) { + MenuItemComposite composite = (MenuItemComposite) comp; + if (composite.item.getText().equals( + ((MenuItem) e.widget).getText())) { + if (composite.firstItem instanceof Button) { + ((Button) composite.firstItem) + .setSelection(composite.item + .getSelection()); + } + } else { + if (composite.firstItem instanceof Button) { + ((Button) composite.firstItem) + .setSelection(false); + } + } + } + } + } + }; + } + + protected void createUpdateListener() { updateListener = new Listener() { @Override public void handleEvent(Event event) { - if (mic.secondItem != null && !mic.secondItem.isDisposed()) { - if (mic.item == event.data) { + if (secondItem != null && !secondItem.isDisposed()) { + if (item == event.data) { if (((MenuItem) event.data).getText().split("\t").length > 1) { - ((Label) mic.secondItem) + ((Label) secondItem) .setText(((MenuItem) event.data).getText() .split("\t")[1]); // don't want to make the times go off the // screen - mic.layout(); + layout(); } } } @@ -407,7 +317,7 @@ public class MenuItemComposite extends Composite { gc.fillPolygon(polyArray); } - private void addMenu(MenuItem item, int y) { + private void createSubMenu(MenuItem item, int y) { PopupMenu men = new PopupMenu(); men.addSubmenus(item, this.getShell(), y); } @@ -467,62 +377,16 @@ public class MenuItemComposite extends Composite { MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { - if (menu == null || menu.isDisposed()) { - for (MenuItem item : topLevelMenu.getItems()) { - if (item.getMenu() != null) { - for (Listener list : item.getMenu().getListeners( - SWT.Show)) { - Event event = new Event(); - event.widget = item; - event.type = SWT.Show; - list.handleEvent(event); - } - if (getShell().getText().equals( - item.getText().replaceAll("&", ""))) { - menu = item.getMenu(); - break; - } - } - } - } - // if the menu has been opened, then the items need to - // be regenerated if there is a submenu, then add the - // ability to show it - // TODO, actually need to keep them in sync - if (item == null || item.isDisposed()) { - int start = 0; - if (menu.getItemCount() != parent.getChildren().length) { - start = 1; - } - for (int i = start; i < menu.getItemCount(); i++) { - MenuItemComposite mic = (MenuItemComposite) parent - .getChildren()[i - start]; - if (mic.item.isDisposed()) { - mic.item = menu.getItem(i); - createUpdateListener(mic); - mic.item.addListener(SWT.Modify, updateListener); - - for (Listener list : mic.item - .getListeners(SWT.Modify)) { - Event ev = new Event(); - ev.type = SWT.Modify; - ev.data = mic.item; - list.handleEvent(ev); - } - } - } - } - if (item.getMenu() != null) { - // get the y offset based on the location of the - // click + // This is item opens a submenu, get the y offset based on + // the location of the click int y = 0; if (e.widget instanceof MenuItemComposite) { y = ((Control) e.widget).getLocation().y; } else { y = ((Control) e.widget).getParent().getLocation().y; } - addMenu(item, y); + createSubMenu(item, y); return; } @@ -536,12 +400,9 @@ public class MenuItemComposite extends Composite { list.handleEvent(event); } - // for commands that do not refresh the editor, menus worked - // since the display was covered by the editor and when the menu - // went away a refresh was forced... this doesn't happen with - // tear-offs that are in the main dialog, since nothing about - // the display changes, so we must force a refresh on the editor - ((AbstractEditor) EditorUtil.getActiveEditor()).refresh(); + if (isDisposed()) { + return; + } // handles the check boxes, if clicking the check box // need to not do this (because SWT does it already) @@ -554,10 +415,12 @@ public class MenuItemComposite extends Composite { } } - for (int i = 0; i < parent.getChildren().length; i++) { - final MenuItemComposite mic = (MenuItemComposite) parent - .getChildren()[i]; - if (mic.item.getStyle() == SWT.RADIO) { + // Handle radio selection changing... + Control[] siblings = getParent().getChildren(); + for (int i = 0; i < siblings.length; i++) { + final MenuItemComposite mic = (MenuItemComposite) siblings[i]; + if (mic.separator == false + && mic.item.getStyle() == SWT.RADIO) { try { MenuItemComposite parent = null; // check whether a Label is clicked or a @@ -595,7 +458,6 @@ public class MenuItemComposite extends Composite { } @Override - // TODO XXX make sure we don't leak anything here public void dispose() { if (arrow != null) { arrow.dispose(); @@ -604,27 +466,20 @@ public class MenuItemComposite extends Composite { highlightedArrow.dispose(); } - if (updateListener != null && !item.isDisposed()) { - item.removeListener(SWT.Modify, updateListener); + if (item != null) { + if (updateListener != null && !item.isDisposed()) { + item.removeListener(SWT.Modify, updateListener); + } + + if (radioListener != null && !item.isDisposed()) { + item.removeSelectionListener(radioListener); + } } - if (showListener != null && !item.isDisposed() - && !item.getParent().isDisposed()) { - item.getParent().removeListener(SWT.Show, showListener); - topLevelMenu.removeListener(SWT.Show, showListener); + + if (topMostParent != null) { + topMostParent.removeMenuListener(this); } - if (firstItem != null) { - firstItem.dispose(); - } - if (secondItem != null) { - secondItem.dispose(); - } - if (job != null) { - job.cancel(); - } - updateListener = null; - showListener = null; - parent.dispose(); - item.dispose(); + super.dispose(); } @@ -633,4 +488,68 @@ public class MenuItemComposite extends Composite { ((Button) firstItem).setSelection(selection); } } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events + * .MenuEvent) + */ + @Override + public void menuHidden(MenuEvent e) { + if (item.isDisposed() == false) { + return; + } + + // At some point we may need to check against the index as well but that + // is difficult because we don't know if the tear off menu item will/was + // in the menu when we were created/now so our index could be off by one + // very easily making it unreliable + Menu menu = topMostParent; + MenuItem myItem = null; + String last = myPath.get(myPath.size() - 1); + for (String path : myPath) { + if (menu != null) { + MenuItem[] items = menu.getItems(); + for (int i = 0; i < items.length; ++i) { + MenuItem item = items[i]; + if (path.equals(item.getText())) { + if (path == last) { + myItem = item; + } else { + menu = item.getMenu(); + + if (menu != null && menu.getItemCount() == 0) { + // Have to manually fill menu + for (Listener listener : menu + .getListeners(SWT.Show)) { + Event event = new Event(); + event.type = SWT.Show; + event.widget = menu; + listener.handleEvent(event); + } + } + } + break; + } + } + } + } + if (myItem != null && myItem.isDisposed() == false) { + item = myItem; + addItemListeners(); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events. + * MenuEvent) + */ + @Override + public void menuShown(MenuEvent e) { + } } \ No newline at end of file diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java index c8d895aa85..d93512e0a8 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java @@ -21,7 +21,7 @@ package com.raytheon.uf.viz.ui.menus.widgets.tearoff; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; @@ -31,7 +31,6 @@ import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.Monitor; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.services.IServiceLocator; @@ -59,11 +58,11 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog; public class TearOffMenuDialog extends CaveSWTDialog { - private MenuItem[] items = null; + private MenuItem[] items; - private ScrolledComposite scrolledComp = null; + private ScrolledComposite scrolledComp; - private Composite fullComp = null; + private Composite fullComp; /** * @param parentShell @@ -72,13 +71,10 @@ public class TearOffMenuDialog extends CaveSWTDialog { super(VizWorkbenchManager.getInstance().getCurrentWindow().getShell(), SWT.DIALOG_TRIM | SWT.RESIZE, CAVE.DO_NOT_BLOCK); String text = menu.getParentItem().getText(); + this.items = menu.getItems(); // handle for the & that makes key bindings - if (text.contains("&")) { - text = text.replace("&", ""); - } - setText(text); - this.items = menu.getItems(); + setText(text.replace("&", "")); } @Override @@ -97,31 +93,15 @@ public class TearOffMenuDialog extends CaveSWTDialog { gd = new GridData(SWT.FILL, SWT.FILL, true, true); fullComp.setLayoutData(gd); - // remove the first menu item which is the tear off item, so that it - // doesn't accidentally appear anywhere else - MenuItem[] preparedItems = new MenuItem[items.length - 1]; - for (int i = 1; i < items.length; i++) { - preparedItems[i - 1] = items[i]; - } - items = preparedItems; - - // TODO, handle radio items, probably in here to keep track of what - // radio items are selected so that they can be deselected - // go through menu items and build MenuItemComposite for each item, // which handles all the selection and color of the "MenuItem" in the // dialog int radioGroup = 0; - for (int i = 0; i < items.length; i++) { - int labelStyle = SWT.NONE; - if (items[i] == null) { - labelStyle = SWT.SEPARATOR | SWT.HORIZONTAL; - } + for (int i = 1; i < items.length; i++) { + MenuItem item = items[i]; + MenuItemComposite comp = new MenuItemComposite(fullComp, SWT.NONE); - final MenuItemComposite comp = new MenuItemComposite(fullComp, - SWT.NONE); - - if (items[i].getStyle() == SWT.RADIO) { + if (item.getStyle() == SWT.RADIO) { comp.setData("radioGroup", radioGroup); } else { radioGroup++; @@ -135,7 +115,7 @@ public class TearOffMenuDialog extends CaveSWTDialog { comp.setLayoutData(gd); // add the labels to the dialog with each of the MenuItems - comp.addLabels(items[i], labelStyle); + comp.addLabels(item, SWT.NONE); } scrolledComp.setContent(fullComp); scrolledComp.setExpandHorizontal(true); @@ -144,13 +124,10 @@ public class TearOffMenuDialog extends CaveSWTDialog { shell.setMinimumSize(150, fullComp.getSize().y); shell.pack(); - // sets the location based on the current shell size (after it is - // packed) - Monitor primary = Display.getCurrent().getPrimaryMonitor(); - Rectangle monitorBounds = primary.getBounds(); - Rectangle shellBounds = shell.getBounds(); - int x = (monitorBounds.width / 2) - (shellBounds.width / 2); - int y = (monitorBounds.height / 2) - (shellBounds.height / 2); + Point point = Display.getCurrent().getCursorLocation(); + int offset = shell.getBounds().width / 2; + int x = point.x - offset; + int y = point.y; shell.setLocation(x, y); // close the dialog on perspective change @@ -200,4 +177,5 @@ public class TearOffMenuDialog extends CaveSWTDialog { } super.disposed(); } + } diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java index 1f0be7b279..f24762681d 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java @@ -30,7 +30,9 @@ import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuListener2; import org.eclipse.jface.action.IMenuManager; -import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; @@ -38,7 +40,7 @@ import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; /** - * TODO Add Description + * Menu listener that adds item to menu which will open dialog which is the menu * *
  * 
@@ -56,9 +58,30 @@ import org.eclipse.swt.widgets.MenuItem;
 
 public class TearOffMenuListener implements IMenuListener2 {
 
-    private List openDialogs = new ArrayList();
+    private List openDialogs = new ArrayList();
 
-    private static final String ID = "tearOffMenuItem";
+    private static final String ID = "tearOffMenuContributionItem";
+
+    private static final String ACTION_ID = "tearOffMenuAction";
+
+    public static final String TEAROFF_PREFERENCE_ID = "tearoffmenus";
+
+    private static boolean enabled;
+    static {
+        final IPreferenceStore store = com.raytheon.uf.viz.core.Activator
+                .getDefault().getPreferenceStore();
+        enabled = store.getBoolean(TEAROFF_PREFERENCE_ID);
+        store.addPropertyChangeListener(new IPropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent event) {
+                enabled = store.getBoolean(TEAROFF_PREFERENCE_ID);
+            }
+        });
+    }
+
+    public TearOffMenuListener() {
+
+    }
 
     public TearOffMenuListener(IMenuManager mgr) {
         register(mgr.getItems(), this);
@@ -70,12 +93,16 @@ public class TearOffMenuListener implements IMenuListener2 {
      */
     @Override
     public void menuAboutToShow(final IMenuManager manager) {
-        // new Exception().printStackTrace();
         register(manager.getItems(), this);
-        if (openDialogs.contains(manager) == false) {
-            // No open dialog for this menu, add tear off button
-            MenuItem[] menuItems = ((MenuManager) manager).getMenu().getItems();
-            manager.add(new TearOffContributionItem(manager, menuItems));
+        if (openDialogs.contains(getKey(manager)) == false) {
+            // We need to add our item to be first so we need to remove others
+            // then add ourself
+            IContributionItem[] items = manager.getItems();
+            manager.removeAll();
+            manager.add(new TearOffContributionItem(manager));
+            for (IContributionItem item : items) {
+                manager.add(item);
+            }
         }
     }
 
@@ -85,8 +112,11 @@ public class TearOffMenuListener implements IMenuListener2 {
      */
     @Override
     public void menuAboutToHide(IMenuManager manager) {
-        manager.remove(ID);
-        unregister(manager.getItems(), this);
+        if (openDialogs.contains(getKey(manager)) == false) {
+            manager.remove(ID);
+            manager.remove(ACTION_ID);
+            unregister(manager.getItems(), this);
+        }
     }
 
     public static void register(IContributionItem[] items,
@@ -107,21 +137,24 @@ public class TearOffMenuListener implements IMenuListener2 {
         }
     }
 
+    private static Object getKey(IMenuManager manager) {
+        Object key = manager;
+        if (manager.getId() != null) {
+            key = manager.getId();
+        }
+        return key;
+    }
+
     private class TearOffContributionItem extends ContributionItem {
 
-        private Menu menu;
-
         private IMenuManager manager;
 
-        private MenuItem[] items;
-
         /**
          * @param action
          */
-        public TearOffContributionItem(IMenuManager manager, MenuItem[] items) {
+        public TearOffContributionItem(IMenuManager manager) {
             super(ID);
             this.manager = manager;
-            this.items = items;
         }
 
         /*
@@ -132,8 +165,7 @@ public class TearOffMenuListener implements IMenuListener2 {
          * swt.widgets.Menu, int)
          */
         @Override
-        public void fill(Menu parent, int index) {
-            this.menu = parent;
+        public void fill(Menu menu, int index) {
             String longest = "";
             for (MenuItem item : menu.getItems()) {
                 String check = item.getText();
@@ -145,19 +177,23 @@ public class TearOffMenuListener implements IMenuListener2 {
             Arrays.fill(bytes, (byte) '|');
             // String filled = new String(bytes);
             String filled = "- - - - - - TEAR-OFF : "
-                    + parent.getParentItem().getText() + " - - - - - -";
+                    + menu.getParentItem().getText() + " - - - - - -";
             // String filled = "-" * bytes.length
 
-            // safety, not wanting to be permanent, making sure only one shows
-            // up
-            for (MenuItem item : menu.getItems()) {
-                if (item.getText().contains("TEAR-OFF")) {
-                    return;
-                }
-            }
-            new ActionContributionItem(new TearOffAction(filled, manager,
-                    items, menu)).fill(parent, 0);
+            new ActionContributionItem(new TearOffAction(filled, manager, menu))
+                    .fill(menu, index);
         }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see org.eclipse.jface.action.ContributionItem#isVisible()
+         */
+        @Override
+        public boolean isVisible() {
+            return super.isVisible() && enabled;
+        }
+
     }
 
     private class TearOffAction extends Action {
@@ -166,8 +202,7 @@ public class TearOffMenuListener implements IMenuListener2 {
 
         private Menu menu;
 
-        private TearOffAction(String text, final IMenuManager manager,
-                final MenuItem[] items, Menu menu) {
+        private TearOffAction(String text, final IMenuManager manager, Menu menu) {
             super(text);
             this.manager = manager;
             this.menu = menu;
@@ -184,12 +219,13 @@ public class TearOffMenuListener implements IMenuListener2 {
             dialog.addListener(SWT.Dispose, new Listener() {
                 @Override
                 public void handleEvent(Event event) {
-                    openDialogs.remove(manager);
+                    openDialogs.remove(getKey(manager));
                     manager.remove(ID);
+                    manager.remove(ACTION_ID);
                     unregister(manager.getItems(), TearOffMenuListener.this);
                 }
             });
-            openDialogs.add(manager);
+            openDialogs.add(getKey(manager));
             register(manager.getItems(), TearOffMenuListener.this);
             dialog.open();
         }
@@ -201,7 +237,7 @@ public class TearOffMenuListener implements IMenuListener2 {
          */
         @Override
         public String getId() {
-            return ID;
+            return ACTION_ID;
         }
     }
 }
diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffPreferencePage.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffPreferencePage.java
index c6a16319b2..7c47d94557 100644
--- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffPreferencePage.java
+++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffPreferencePage.java
@@ -70,7 +70,8 @@ public class TearOffPreferencePage extends FieldEditorPreferencePage implements
      */
     @Override
     protected void createFieldEditors() {
-        BooleanFieldEditor editor = new BooleanFieldEditor("tearoffmenus",
+        BooleanFieldEditor editor = new BooleanFieldEditor(
+                TearOffMenuListener.TEAROFF_PREFERENCE_ID,
                 "Enable Tear-Off Menus (requires restart)",
                 getFieldEditorParent());
         addField(editor);
diff --git a/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/VizWorkbenchWindowAdvisor.java b/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/VizWorkbenchWindowAdvisor.java
index 26fa2782e9..f7b4755d1d 100644
--- a/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/VizWorkbenchWindowAdvisor.java
+++ b/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/VizWorkbenchWindowAdvisor.java
@@ -137,11 +137,8 @@ public class VizWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
             listener.perspectiveActivated(page, perspective);
         }
 
-        if (com.raytheon.uf.viz.core.Activator.getDefault()
-                .getPreferenceStore().getBoolean("tearoffmenus")) {
-            new TearOffMenuListener(VizActionBarAdvisor.getInstance(window)
-                    .getMenuManager());
-        }
+        new TearOffMenuListener(VizActionBarAdvisor.getInstance(window)
+                .getMenuManager());
     }
 
     /*