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: eb2ecaaab4 [formerly 885f291b35] [formerly be37f19c90] [formerly be37f19c90 [formerly 370e20f0ae]] [formerly eb2ecaaab4 [formerly 885f291b35] [formerly be37f19c90] [formerly be37f19c90 [formerly 370e20f0ae]] [formerly a7ccea5eda [formerly be37f19c90 [formerly 370e20f0ae] [formerly a7ccea5eda [formerly 875c90104e80d95798956330e24b59d83ef52942]]]]]
Former-commit-id: a7ccea5eda
Former-commit-id: 215193f8d6 [formerly 26b5acd957] [formerly 825bd76f55] [formerly 7b710c402a49ea8adcdf21a1450bf5a45bb2188b [formerly ead10f07881d137ecc80314da8c248c4e77e5f1b] [formerly 825bd76f55 [formerly a6622153d7]]]
Former-commit-id: 18e144f5bd9f49a3902d5b42b72311ccd899979d [formerly ab8f916eb71b9c4a01d3b1b2acdc2d21f22703d0] [formerly a6c4e04126 [formerly 4ba7705369]]
Former-commit-id: a6c4e04126
Former-commit-id: 18662550ee
This commit is contained in:
Max Schenkelberg 2012-02-07 09:15:13 -06:00
parent 1091d730f0
commit 7e1371a36c
7 changed files with 365 additions and 350 deletions

View file

@ -22,16 +22,15 @@ package com.raytheon.uf.viz.core.maps.menus;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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.jface.action.MenuManager;
import org.eclipse.ui.PlatformUI; import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.CompoundContributionItem;
import org.eclipse.ui.menus.CommandContributionItem; import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter; import org.eclipse.ui.menus.CommandContributionItemParameter;
import com.raytheon.uf.viz.core.maps.MapStore; import com.raytheon.uf.viz.core.maps.MapStore;
import com.raytheon.uf.viz.core.maps.MapStore.MapNode; 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 * TODO Add Description
@ -49,31 +48,33 @@ import com.raytheon.uf.viz.ui.menus.widgets.tearoff.TearOffMenuListener;
* @version 1.0 * @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; boolean addTear = false;
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see * @see com.raytheon.uf.viz.ui.menus.widgets.
* org.eclipse.ui.actions.CompoundContributionItem#getContributionItems() * AbstractTearOffableCompoundContributionItem
* #addContributionItems(org.eclipse.jface.action.IMenuManager)
*/ */
@Override @Override
protected IContributionItem[] getContributionItems() { protected void addContributionItems(IMenuManager manager) {
MapNode node = MapStore.getMapTree(); MapNode node = MapStore.getMapTree();
return createMenu(node).getItems(); createMenu(manager, node);
} }
private MenuManager createMenu(MapNode root) { private void createMenu(IMenuManager manager, MapNode root) {
MenuManager menuMgr = new MenuManager(root.getName());
for (MapNode node : root.getSubTree()) { 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) { if (node.getSubTree() == null) {
Map<String, String> parms = new HashMap<String, String>(); Map<String, String> parms = new HashMap<String, String>();
parms.put("mapName", node.getName()); parms.put("mapName", node.getName());
@ -87,12 +88,13 @@ public class MapsMenu extends CompoundContributionItem {
parms, null, null, null, node.getName(), null, parms, null, null, null, node.getName(), null,
null, CommandContributionItem.STYLE_CHECK, null, CommandContributionItem.STYLE_CHECK,
null, true)); null, true));
menuMgr.add(item); manager.add(item);
} else { } else {
addTear = true; IMenuManager subMenu = new MenuManager(node.getName(),
menuMgr.add(createMenu(node)); manager.getId() + "." + node.getName());
createMenu(subMenu, node);
manager.add(subMenu);
} }
} }
return menuMgr;
} }
} }

View file

@ -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
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 6, 2012 mschenke Initial creation
*
* </pre>
*
* @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);
}

View file

@ -19,15 +19,19 @@
**/ **/
package com.raytheon.uf.viz.ui.menus.widgets.tearoff; package com.raytheon.uf.viz.ui.menus.widgets.tearoff;
import org.eclipse.core.runtime.IProgressMonitor; import java.util.ArrayList;
import org.eclipse.core.runtime.IStatus; import java.util.Collections;
import org.eclipse.core.runtime.Status; import java.util.List;
import org.eclipse.swt.SWT; 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.MouseAdapter;
import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image; 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.Listener;
import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem; 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 * 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 * @version 1.0
*/ */
public class MenuItemComposite extends Composite { public class MenuItemComposite extends Composite implements MenuListener {
private boolean separator = false;
private Control firstItem; private Control firstItem;
@ -78,15 +80,11 @@ public class MenuItemComposite extends Composite {
private Listener updateListener = null; private Listener updateListener = null;
private Listener showListener = null; private SelectionListener radioListener = null;
private Menu menu = null; private List<String> myPath;
private Composite parent = null; private Menu topMostParent;
private Menu topLevelMenu = null;
private UIJob job = null;
/** /**
* @param parent * @param parent
@ -94,7 +92,6 @@ public class MenuItemComposite extends Composite {
*/ */
public MenuItemComposite(Composite parent, int style) { public MenuItemComposite(Composite parent, int style) {
super(parent, style); super(parent, style);
this.parent = parent;
} }
// creates both labels and ties them together // creates both labels and ties them together
@ -103,21 +100,27 @@ public class MenuItemComposite extends Composite {
return; return;
} }
// going to hold the menu around so that if the cave menu gets opened myPath = new ArrayList<String>();
// 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();
topLevelMenu = menu; // Build the menu path to the MenuItem from highest menu level
while (topLevelMenu.getParentMenu() != null Menu parent = it.getParent();
&& topLevelMenu.getParentMenu().getParentMenu() != null) { MenuItem toAdd = it;
topLevelMenu = topLevelMenu.getParentMenu(); 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; item = it;
String[] labels = item.getText().split("\t"); String[] labels = item.getText().split("\t");
// handle for a separator menu item // handle for a separator menu item
if (item.getStyle() == SWT.SEPARATOR) { if (item.getStyle() == SWT.SEPARATOR) {
separator = true;
firstItem = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL); firstItem = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL);
GridData gd = new GridData(SWT.FILL, SWT.DEFAULT, true, false); GridData gd = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
gd.horizontalSpan = 2; gd.horizontalSpan = 2;
@ -134,47 +137,8 @@ public class MenuItemComposite extends Composite {
((Label) secondItem).setText(labels[0]); ((Label) secondItem).setText(labels[0]);
gd = new GridData(SWT.LEFT, SWT.CENTER, true, true); gd = new GridData(SWT.LEFT, SWT.CENTER, true, true);
secondItem.setLayoutData(gd); secondItem.setLayoutData(gd);
item.addSelectionListener(new SelectionAdapter() { createRadioListener();
@Override } else if (item.getStyle() == SWT.CASCADE) {
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) {
firstItem = new Label(this, SWT.PUSH); firstItem = new Label(this, SWT.PUSH);
firstItem.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, firstItem.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT,
true, false)); true, false));
@ -195,108 +159,9 @@ public class MenuItemComposite extends Composite {
((Label) secondItem).setText(labels[1]); ((Label) secondItem).setText(labels[1]);
} }
createUpdateListener(this); // Create and add text update listener
item.addListener(SWT.Modify, updateListener); 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()) { if (item.isEnabled()) {
// add the listeners to both the first and the second // add the listeners to both the first and the second
@ -316,21 +181,66 @@ public class MenuItemComposite extends Composite {
SWT.COLOR_DARK_GRAY)); 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() { updateListener = new Listener() {
@Override @Override
public void handleEvent(Event event) { public void handleEvent(Event event) {
if (mic.secondItem != null && !mic.secondItem.isDisposed()) { if (secondItem != null && !secondItem.isDisposed()) {
if (mic.item == event.data) { if (item == event.data) {
if (((MenuItem) event.data).getText().split("\t").length > 1) { if (((MenuItem) event.data).getText().split("\t").length > 1) {
((Label) mic.secondItem) ((Label) secondItem)
.setText(((MenuItem) event.data).getText() .setText(((MenuItem) event.data).getText()
.split("\t")[1]); .split("\t")[1]);
// don't want to make the times go off the // don't want to make the times go off the
// screen // screen
mic.layout(); layout();
} }
} }
} }
@ -407,7 +317,7 @@ public class MenuItemComposite extends Composite {
gc.fillPolygon(polyArray); gc.fillPolygon(polyArray);
} }
private void addMenu(MenuItem item, int y) { private void createSubMenu(MenuItem item, int y) {
PopupMenu men = new PopupMenu(); PopupMenu men = new PopupMenu();
men.addSubmenus(item, this.getShell(), y); men.addSubmenus(item, this.getShell(), y);
} }
@ -467,62 +377,16 @@ public class MenuItemComposite extends Composite {
MouseAdapter mouseAdapter = new MouseAdapter() { MouseAdapter mouseAdapter = new MouseAdapter() {
@Override @Override
public void mouseDown(MouseEvent e) { 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) { if (item.getMenu() != null) {
// get the y offset based on the location of the // This is item opens a submenu, get the y offset based on
// click // the location of the click
int y = 0; int y = 0;
if (e.widget instanceof MenuItemComposite) { if (e.widget instanceof MenuItemComposite) {
y = ((Control) e.widget).getLocation().y; y = ((Control) e.widget).getLocation().y;
} else { } else {
y = ((Control) e.widget).getParent().getLocation().y; y = ((Control) e.widget).getParent().getLocation().y;
} }
addMenu(item, y); createSubMenu(item, y);
return; return;
} }
@ -536,12 +400,9 @@ public class MenuItemComposite extends Composite {
list.handleEvent(event); list.handleEvent(event);
} }
// for commands that do not refresh the editor, menus worked if (isDisposed()) {
// since the display was covered by the editor and when the menu return;
// 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();
// handles the check boxes, if clicking the check box // handles the check boxes, if clicking the check box
// need to not do this (because SWT does it already) // 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++) { // Handle radio selection changing...
final MenuItemComposite mic = (MenuItemComposite) parent Control[] siblings = getParent().getChildren();
.getChildren()[i]; for (int i = 0; i < siblings.length; i++) {
if (mic.item.getStyle() == SWT.RADIO) { final MenuItemComposite mic = (MenuItemComposite) siblings[i];
if (mic.separator == false
&& mic.item.getStyle() == SWT.RADIO) {
try { try {
MenuItemComposite parent = null; MenuItemComposite parent = null;
// check whether a Label is clicked or a // check whether a Label is clicked or a
@ -595,7 +458,6 @@ public class MenuItemComposite extends Composite {
} }
@Override @Override
// TODO XXX make sure we don't leak anything here
public void dispose() { public void dispose() {
if (arrow != null) { if (arrow != null) {
arrow.dispose(); arrow.dispose();
@ -604,27 +466,20 @@ public class MenuItemComposite extends Composite {
highlightedArrow.dispose(); highlightedArrow.dispose();
} }
if (updateListener != null && !item.isDisposed()) { if (item != null) {
item.removeListener(SWT.Modify, updateListener); 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()) { if (topMostParent != null) {
item.getParent().removeListener(SWT.Show, showListener); topMostParent.removeMenuListener(this);
topLevelMenu.removeListener(SWT.Show, showListener);
} }
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(); super.dispose();
} }
@ -633,4 +488,68 @@ public class MenuItemComposite extends Composite {
((Button) firstItem).setSelection(selection); ((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) {
}
} }

View file

@ -21,7 +21,7 @@ package com.raytheon.uf.viz.ui.menus.widgets.tearoff;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite; 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.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; 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.Listener;
import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI; import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.services.IServiceLocator; import org.eclipse.ui.services.IServiceLocator;
@ -59,11 +58,11 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
public class TearOffMenuDialog extends 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 * @param parentShell
@ -72,13 +71,10 @@ public class TearOffMenuDialog extends CaveSWTDialog {
super(VizWorkbenchManager.getInstance().getCurrentWindow().getShell(), super(VizWorkbenchManager.getInstance().getCurrentWindow().getShell(),
SWT.DIALOG_TRIM | SWT.RESIZE, CAVE.DO_NOT_BLOCK); SWT.DIALOG_TRIM | SWT.RESIZE, CAVE.DO_NOT_BLOCK);
String text = menu.getParentItem().getText(); String text = menu.getParentItem().getText();
this.items = menu.getItems();
// handle for the & that makes key bindings // handle for the & that makes key bindings
if (text.contains("&")) { setText(text.replace("&", ""));
text = text.replace("&", "");
}
setText(text);
this.items = menu.getItems();
} }
@Override @Override
@ -97,31 +93,15 @@ public class TearOffMenuDialog extends CaveSWTDialog {
gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd = new GridData(SWT.FILL, SWT.FILL, true, true);
fullComp.setLayoutData(gd); 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, // go through menu items and build MenuItemComposite for each item,
// which handles all the selection and color of the "MenuItem" in the // which handles all the selection and color of the "MenuItem" in the
// dialog // dialog
int radioGroup = 0; int radioGroup = 0;
for (int i = 0; i < items.length; i++) { for (int i = 1; i < items.length; i++) {
int labelStyle = SWT.NONE; MenuItem item = items[i];
if (items[i] == null) { MenuItemComposite comp = new MenuItemComposite(fullComp, SWT.NONE);
labelStyle = SWT.SEPARATOR | SWT.HORIZONTAL;
}
final MenuItemComposite comp = new MenuItemComposite(fullComp, if (item.getStyle() == SWT.RADIO) {
SWT.NONE);
if (items[i].getStyle() == SWT.RADIO) {
comp.setData("radioGroup", radioGroup); comp.setData("radioGroup", radioGroup);
} else { } else {
radioGroup++; radioGroup++;
@ -135,7 +115,7 @@ public class TearOffMenuDialog extends CaveSWTDialog {
comp.setLayoutData(gd); comp.setLayoutData(gd);
// add the labels to the dialog with each of the MenuItems // 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.setContent(fullComp);
scrolledComp.setExpandHorizontal(true); scrolledComp.setExpandHorizontal(true);
@ -144,13 +124,10 @@ public class TearOffMenuDialog extends CaveSWTDialog {
shell.setMinimumSize(150, fullComp.getSize().y); shell.setMinimumSize(150, fullComp.getSize().y);
shell.pack(); shell.pack();
// sets the location based on the current shell size (after it is Point point = Display.getCurrent().getCursorLocation();
// packed) int offset = shell.getBounds().width / 2;
Monitor primary = Display.getCurrent().getPrimaryMonitor(); int x = point.x - offset;
Rectangle monitorBounds = primary.getBounds(); int y = point.y;
Rectangle shellBounds = shell.getBounds();
int x = (monitorBounds.width / 2) - (shellBounds.width / 2);
int y = (monitorBounds.height / 2) - (shellBounds.height / 2);
shell.setLocation(x, y); shell.setLocation(x, y);
// close the dialog on perspective change // close the dialog on perspective change
@ -200,4 +177,5 @@ public class TearOffMenuDialog extends CaveSWTDialog {
} }
super.disposed(); super.disposed();
} }
} }

View file

@ -30,7 +30,9 @@ import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuListener2; import org.eclipse.jface.action.IMenuListener2;
import org.eclipse.jface.action.IMenuManager; 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.SWT;
import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Listener;
@ -38,7 +40,7 @@ import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MenuItem;
/** /**
* TODO Add Description * Menu listener that adds item to menu which will open dialog which is the menu
* *
* <pre> * <pre>
* *
@ -56,9 +58,30 @@ import org.eclipse.swt.widgets.MenuItem;
public class TearOffMenuListener implements IMenuListener2 { public class TearOffMenuListener implements IMenuListener2 {
private List<IMenuManager> openDialogs = new ArrayList<IMenuManager>(); private List<Object> openDialogs = new ArrayList<Object>();
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) { public TearOffMenuListener(IMenuManager mgr) {
register(mgr.getItems(), this); register(mgr.getItems(), this);
@ -70,12 +93,16 @@ public class TearOffMenuListener implements IMenuListener2 {
*/ */
@Override @Override
public void menuAboutToShow(final IMenuManager manager) { public void menuAboutToShow(final IMenuManager manager) {
// new Exception().printStackTrace();
register(manager.getItems(), this); register(manager.getItems(), this);
if (openDialogs.contains(manager) == false) { if (openDialogs.contains(getKey(manager)) == false) {
// No open dialog for this menu, add tear off button // We need to add our item to be first so we need to remove others
MenuItem[] menuItems = ((MenuManager) manager).getMenu().getItems(); // then add ourself
manager.add(new TearOffContributionItem(manager, menuItems)); 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 @Override
public void menuAboutToHide(IMenuManager manager) { public void menuAboutToHide(IMenuManager manager) {
manager.remove(ID); if (openDialogs.contains(getKey(manager)) == false) {
unregister(manager.getItems(), this); manager.remove(ID);
manager.remove(ACTION_ID);
unregister(manager.getItems(), this);
}
} }
public static void register(IContributionItem[] items, 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 class TearOffContributionItem extends ContributionItem {
private Menu menu;
private IMenuManager manager; private IMenuManager manager;
private MenuItem[] items;
/** /**
* @param action * @param action
*/ */
public TearOffContributionItem(IMenuManager manager, MenuItem[] items) { public TearOffContributionItem(IMenuManager manager) {
super(ID); super(ID);
this.manager = manager; this.manager = manager;
this.items = items;
} }
/* /*
@ -132,8 +165,7 @@ public class TearOffMenuListener implements IMenuListener2 {
* swt.widgets.Menu, int) * swt.widgets.Menu, int)
*/ */
@Override @Override
public void fill(Menu parent, int index) { public void fill(Menu menu, int index) {
this.menu = parent;
String longest = ""; String longest = "";
for (MenuItem item : menu.getItems()) { for (MenuItem item : menu.getItems()) {
String check = item.getText(); String check = item.getText();
@ -145,19 +177,23 @@ public class TearOffMenuListener implements IMenuListener2 {
Arrays.fill(bytes, (byte) '|'); Arrays.fill(bytes, (byte) '|');
// String filled = new String(bytes); // String filled = new String(bytes);
String filled = "- - - - - - TEAR-OFF : " String filled = "- - - - - - TEAR-OFF : "
+ parent.getParentItem().getText() + " - - - - - -"; + menu.getParentItem().getText() + " - - - - - -";
// String filled = "-" * bytes.length // String filled = "-" * bytes.length
// safety, not wanting to be permanent, making sure only one shows new ActionContributionItem(new TearOffAction(filled, manager, menu))
// up .fill(menu, index);
for (MenuItem item : menu.getItems()) {
if (item.getText().contains("TEAR-OFF")) {
return;
}
}
new ActionContributionItem(new TearOffAction(filled, manager,
items, menu)).fill(parent, 0);
} }
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.ContributionItem#isVisible()
*/
@Override
public boolean isVisible() {
return super.isVisible() && enabled;
}
} }
private class TearOffAction extends Action { private class TearOffAction extends Action {
@ -166,8 +202,7 @@ public class TearOffMenuListener implements IMenuListener2 {
private Menu menu; private Menu menu;
private TearOffAction(String text, final IMenuManager manager, private TearOffAction(String text, final IMenuManager manager, Menu menu) {
final MenuItem[] items, Menu menu) {
super(text); super(text);
this.manager = manager; this.manager = manager;
this.menu = menu; this.menu = menu;
@ -184,12 +219,13 @@ public class TearOffMenuListener implements IMenuListener2 {
dialog.addListener(SWT.Dispose, new Listener() { dialog.addListener(SWT.Dispose, new Listener() {
@Override @Override
public void handleEvent(Event event) { public void handleEvent(Event event) {
openDialogs.remove(manager); openDialogs.remove(getKey(manager));
manager.remove(ID); manager.remove(ID);
manager.remove(ACTION_ID);
unregister(manager.getItems(), TearOffMenuListener.this); unregister(manager.getItems(), TearOffMenuListener.this);
} }
}); });
openDialogs.add(manager); openDialogs.add(getKey(manager));
register(manager.getItems(), TearOffMenuListener.this); register(manager.getItems(), TearOffMenuListener.this);
dialog.open(); dialog.open();
} }
@ -201,7 +237,7 @@ public class TearOffMenuListener implements IMenuListener2 {
*/ */
@Override @Override
public String getId() { public String getId() {
return ID; return ACTION_ID;
} }
} }
} }

View file

@ -70,7 +70,8 @@ public class TearOffPreferencePage extends FieldEditorPreferencePage implements
*/ */
@Override @Override
protected void createFieldEditors() { protected void createFieldEditors() {
BooleanFieldEditor editor = new BooleanFieldEditor("tearoffmenus", BooleanFieldEditor editor = new BooleanFieldEditor(
TearOffMenuListener.TEAROFF_PREFERENCE_ID,
"Enable Tear-Off Menus (requires restart)", "Enable Tear-Off Menus (requires restart)",
getFieldEditorParent()); getFieldEditorParent());
addField(editor); addField(editor);

View file

@ -137,11 +137,8 @@ public class VizWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
listener.perspectiveActivated(page, perspective); listener.perspectiveActivated(page, perspective);
} }
if (com.raytheon.uf.viz.core.Activator.getDefault() new TearOffMenuListener(VizActionBarAdvisor.getInstance(window)
.getPreferenceStore().getBoolean("tearoffmenus")) { .getMenuManager());
new TearOffMenuListener(VizActionBarAdvisor.getInstance(window)
.getMenuManager());
}
} }
/* /*