From 32d4848ef28e3c0b1f78a9ce3821124de34c6baa Mon Sep 17 00:00:00 2001 From: Ben Steffensmeier Date: Thu, 12 Jul 2012 13:35:52 -0500 Subject: [PATCH] Issue #808 create API for managing floating views and made CaveDetachedWindow more robust. Former-commit-id: 45a1cc1997609b399d6bd0115538764b587b25e1 [formerly ea0c7046af014d8771776310b88e97edf4296595] [formerly 8984b35ff82d156050e93f1ca95b718bfe3493e1 [formerly 1f9b6d2a0b7d45f68bda57c6fcaeec493cbb835a]] Former-commit-id: 8984b35ff82d156050e93f1ca95b718bfe3493e1 Former-commit-id: 1c68f4e01e65c588c50adb454b54480d3da75d4d --- .../viz/ui/views/CaveDetachedWindow.java | 218 ++++++++----- .../viz/ui/views/CaveFloatingView.java | 97 ++---- .../ui/views/CaveWorkbenchPageManager.java | 306 ++++++++++++++++++ 3 files changed, 476 insertions(+), 145 deletions(-) create mode 100644 cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveWorkbenchPageManager.java diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveDetachedWindow.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveDetachedWindow.java index 52c248577d..cec4396e69 100644 --- a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveDetachedWindow.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveDetachedWindow.java @@ -37,13 +37,13 @@ import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.ISaveablePart; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewReference; -import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPartConstants; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.internal.DetachedWindow; import org.eclipse.ui.internal.EditorManager; +import org.eclipse.ui.internal.ILayoutContainer; import org.eclipse.ui.internal.IWorkbenchConstants; import org.eclipse.ui.internal.IWorkbenchHelpContextIds; import org.eclipse.ui.internal.LayoutPart; @@ -56,6 +56,7 @@ import org.eclipse.ui.internal.dnd.DragUtil; import org.eclipse.ui.internal.dnd.IDragOverListener; import org.eclipse.ui.internal.dnd.IDropTarget; import org.eclipse.ui.internal.presentations.PresentationFactoryUtil; +import org.eclipse.ui.presentations.IPresentablePart; import org.eclipse.ui.presentations.StackDropResult; /** @@ -84,6 +85,13 @@ public class CaveDetachedWindow extends DetachedWindow implements // wasn't canceled e.doit = handleClose(); } + + @Override + public void shellActivated(ShellEvent e) { + page.getPartService().setActivePart( + folder.getSelection().getPartReference()); + } + }; private Listener resizeListener = new Listener() { @@ -119,7 +127,111 @@ public class CaveDetachedWindow extends DetachedWindow implements this.page = workbenchPage; folder = new ViewStack(page, false, PresentationFactoryUtil.ROLE_VIEW, - null); + null) { + + /** + * Override close because super.close expects to find the view in + * the page. + */ + @Override + protected void close(IPresentablePart part) { + if (!presentationSite.isCloseable(part)) { + return; + } + + LayoutPart layoutPart = getPaneFor(part); + if (layoutPart != null && layoutPart instanceof PartPane) { + PartPane viewPane = (PartPane) layoutPart; + IViewReference ref = (IViewReference) viewPane + .getPartReference(); + CaveWorkbenchPageManager.getInstance(page).hideView(ref); + } + } + + /** + * Override paneDragStart so we can check if the stack is empty and + * close the window + */ + @Override + public void paneDragStart(LayoutPart pane, Point initialLocation, + boolean keyboard) { + super.paneDragStart(pane, initialLocation, keyboard); + if (pane == null) { + // This means the whole stack is getting dragged: + for (LayoutPart lpart : getChildren()) { + checkChild(lpart); + } + } else { + checkChild(pane); + } + } + + /** + * Use this after dragging to check if I still only my children, + * normally the page removes them but that doesn't work for + * CaveDetachedWindows so this removes it from the stack when + * someone else gets it. + * + * @param pane + */ + private void checkChild(LayoutPart pane) { + if (pane.getContainer() != this) { + // the part does nto get properly dereferenced since it is + // not part of the page + ILayoutContainer newContainer = pane.getContainer(); + newContainer.remove(pane); + remove(pane); + newContainer.add(pane); + if (this.getItemCount() == 0 && windowShell != null) { + windowShell.dispose(); + } + } + } + + /** + * Override add so we can change floating status + */ + @Override + public void add(LayoutPart child, Object cookie) { + super.add(child, cookie); + setFloating(child, true); + + } + + /** + * Override remove so we can change floating status + */ + @Override + public void remove(LayoutPart child) { + super.remove(child); + setFloating(child, false); + } + + private void setFloating(LayoutPart child, boolean floating) { + if (child instanceof ViewPane) { + ViewPane pane = (ViewPane) child; + IViewPart view = pane.getViewReference().getView(false); + if (view instanceof CaveFloatingView) { + ((CaveFloatingView) view).setFloating(floating); + + } + } + } + + /** + * Override set selection so we can fire appropriate activation + * listeners. + */ + @Override + public void setSelection(LayoutPart part) { + super.setSelection(part); + if (part instanceof PartPane) { + page.getPartService().setActivePart( + ((PartPane) part).getPartReference()); + } + } + + }; folder.addListener(propertyListener); } @@ -180,6 +292,7 @@ public class CaveDetachedWindow extends DetachedWindow implements public void create() { windowShell = new Shell(Display.getCurrent(), SWT.SHELL_TRIM); + windowShell.addShellListener(shellListener); windowShell.setData(this); windowShell.setText("Detached Window"); DragUtil.addDragTarget(windowShell, this); @@ -214,33 +327,6 @@ public class CaveDetachedWindow extends DetachedWindow implements folder.setBounds(windowShell.getClientArea()); } - /** - * Adds a visual part to this window. Supports reparenting. - */ - public void add(ViewPane part) { - - Shell shell = getShell(); - if (shell != null) { - part.reparent(shell); - } - folder.add(part); - updateMinimumSize(); - } - - public boolean belongsToWorkbenchPage(IWorkbenchPage workbenchPage) { - return (this.page == workbenchPage); - } - - public boolean close() { - hideViewsOnClose = false; - Shell shell = getShell(); - if (shell != null && !shell.isDisposed()) { - shell.removeShellListener(shellListener); - shell.close(); - } - return true; - } - /** * Closes this window and disposes its shell. */ @@ -262,7 +348,8 @@ public class CaveDetachedWindow extends DetachedWindow implements // Only close if closable... if (child.isCloseable()) { - page.hideView(child.getViewReference()); + CaveWorkbenchPageManager.getInstance(page).hideView( + child.getViewReference()); // Was the close cancelled? if (child.getContainer() != null) @@ -352,34 +439,36 @@ public class CaveDetachedWindow extends DetachedWindow implements public IDropTarget drag(Control currentControl, Object draggedObject, Point position, Rectangle dragRectangle) { - if (!(draggedObject instanceof PartPane)) { + if (!(draggedObject instanceof ViewPane)) { return null; } - final PartPane sourcePart = (PartPane) draggedObject; + ViewPane sourcePart = (ViewPane) draggedObject; if (sourcePart.getWorkbenchWindow() != page.getWorkbenchWindow()) { return null; } - // Only handle the event if the source part is acceptable to the - // particular PartStack - IDropTarget target = null; - if (sourcePart instanceof ViewPane) { - target = folder.getDropTarget(draggedObject, position); - - if (target == null) { - Rectangle displayBounds = DragUtil.getDisplayBounds(folder - .getControl()); - if (displayBounds.contains(position)) { - target = folder.createDropTarget(sourcePart, - new StackDropResult(displayBounds, null)); - } else { - return null; - } - } + // If you put views in a CaveDetachedWindow then you can't find them + // using the workbenchPage, this is expected for FLoatingViews but + // puttiing other views in the detached window would cause API problems + // by unexpectedly losing views. + if (!(sourcePart.getViewReference().getPart(false) instanceof CaveFloatingView)) { + return null; } + IDropTarget target = folder.getDropTarget(draggedObject, position); + + if (target == null) { + Rectangle displayBounds = DragUtil.getDisplayBounds(folder + .getControl()); + if (displayBounds.contains(position)) { + target = folder.createDropTarget(sourcePart, + new StackDropResult(displayBounds, null)); + } else { + return null; + } + } return target; } @@ -441,35 +530,6 @@ public class CaveDetachedWindow extends DetachedWindow implements return this.page; } - /** - * @see IPersistablePart - */ - public void restoreState(IMemento memento) { - // Read the bounds. - Integer bigInt; - bigInt = memento.getInteger(IWorkbenchConstants.TAG_X); - int x = bigInt.intValue(); - bigInt = memento.getInteger(IWorkbenchConstants.TAG_Y); - int y = bigInt.intValue(); - bigInt = memento.getInteger(IWorkbenchConstants.TAG_WIDTH); - int width = bigInt.intValue(); - bigInt = memento.getInteger(IWorkbenchConstants.TAG_HEIGHT); - int height = bigInt.intValue(); - bigInt = memento.getInteger(IWorkbenchConstants.TAG_FLOAT); - - // Set the bounds. - bounds = new Rectangle(x, y, width, height); - if (getShell() != null) { - getShell().setBounds(bounds); - } - - // Create the folder. - IMemento childMem = memento.getChild(IWorkbenchConstants.TAG_FOLDER); - if (childMem != null) { - folder.restoreState(childMem); - } - } - /** * @see IPersistablePart */ @@ -517,4 +577,8 @@ public class CaveDetachedWindow extends DetachedWindow implements return Window.OK; } + public void setBounds(Rectangle bounds) { + this.bounds = bounds; + } + } diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveFloatingView.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveFloatingView.java index 14a7e10ded..4eb09d53e7 100644 --- a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveFloatingView.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveFloatingView.java @@ -24,19 +24,7 @@ import java.io.File; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.ui.IViewPart; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.internal.ILayoutContainer; -import org.eclipse.ui.internal.PartPane; -import org.eclipse.ui.internal.ViewPane; -import org.eclipse.ui.internal.ViewSite; -import org.eclipse.ui.internal.WorkbenchPage; -import org.eclipse.ui.internal.dnd.DragUtil; import org.eclipse.ui.part.ViewPart; import com.raytheon.viz.ui.UiPlugin; @@ -61,13 +49,7 @@ import com.raytheon.viz.ui.UiPlugin; public abstract class CaveFloatingView extends ViewPart { - private boolean detached; - - private CaveDetachedWindow window; - - private ILayoutContainer container; - - private Rectangle bounds; + protected FloatAction floatAction; /** * Constructs the view @@ -81,8 +63,13 @@ public abstract class CaveFloatingView extends ViewPart { createToolbarButton(); } - public IViewPart getPart() { - return this; + public void setFloating(boolean floating) { + floatAction.setChecked(floating); + if (floating) { + floatAction.setToolTipText("Dock"); + } else { + floatAction.setToolTipText("Float"); + } } /** @@ -90,57 +77,31 @@ public abstract class CaveFloatingView extends ViewPart { */ protected void createToolbarButton() { IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager(); - final Action floatAction = new Action("Float", SWT.TOGGLE) { - @Override - public void run() { - // should only run when the user is currently attached to cave - // (or detached, but not a floating dialog) - this.setToolTipText("Dock"); - setChecked(true); - detached = true; - WorkbenchPage page = (WorkbenchPage) PlatformUI.getWorkbench() - .getActiveWorkbenchWindow().getActivePage(); - bounds = ((ViewSite) ((ViewPart) getPart()).getViewSite()) - .getPane().getBounds(); + floatAction = new FloatAction(); + mgr.add(floatAction); - container = ((ViewSite) ((ViewPart) getPart()).getViewSite()) - .getPane().getStack(); - window = new CaveDetachedWindow(page); - window.create(); - window.open(); + } - bounds.x += PlatformUI.getWorkbench() - .getActiveWorkbenchWindow().getShell().getMonitor() - .getBounds().x; - ViewPane pane = (ViewPane) ((ViewSite) getPart().getViewSite()) - .getPane(); - container = pane.getContainer(); - window.drop(pane); - window.getShell().setBounds(bounds); - }; + private class FloatAction extends Action { - @Override - public void runWithEvent(Event event) { - if (!detached) { - run(); - return; - } - setToolTipText("Float"); - setChecked(false); - detached = false; - WorkbenchPage page = (WorkbenchPage) PlatformUI.getWorkbench() - .getActiveWorkbenchWindow().getActivePage(); - PartPane layoutPart = ((ViewSite) getPart().getViewSite()) - .getPane(); - Point point = new Point(bounds.x + bounds.width / 2, bounds.y - + bounds.height / 2); - window.getShell().setVisible(false); - window = null; - DragUtil.dragTo(Display.getCurrent(), layoutPart, point, bounds); + public FloatAction() { + super("Float", SWT.TOGGLE); + setImageDescriptor(UiPlugin.getImageDescriptor("icons" + + File.separator + "float.gif")); + } + + @Override + public void run() { + if (isChecked()) { + CaveWorkbenchPageManager manager = CaveWorkbenchPageManager + .getInstance(getSite().getPage()); + manager.floatView(CaveFloatingView.this); + } else { + CaveWorkbenchPageManager manager = CaveWorkbenchPageManager + .getInstance(getSite().getPage()); + manager.dockView(CaveFloatingView.this); } }; - floatAction.setImageDescriptor(UiPlugin.getImageDescriptor("icons" - + File.separator + "float.gif")); - mgr.add(floatAction); + } } diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveWorkbenchPageManager.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveWorkbenchPageManager.java new file mode 100644 index 0000000000..1be090d156 --- /dev/null +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/views/CaveWorkbenchPageManager.java @@ -0,0 +1,306 @@ +/** + * 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.viz.ui.views; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IPageListener; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.internal.ILayoutContainer; +import org.eclipse.ui.internal.LayoutPart; +import org.eclipse.ui.internal.PartPane; +import org.eclipse.ui.internal.PartStack; +import org.eclipse.ui.internal.ViewPane; +import org.eclipse.ui.internal.ViewSite; +import org.eclipse.ui.internal.WorkbenchPage; + +/** + * The Eclipse WorkbenchPage has lots of nifty functions for dealing with views, + * but unfortunately it fails to take into account CaveFloatingViews that are in + * CaveDetachedWindows, the goal of this class is to provide an API of equal + * niftosity that works for CaveFloatingViews. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jul 11, 2012            bsteffen     Initial creation
+ * 
+ * 
+ * + * @author bsteffen + * @version 1.0 + */ + +public class CaveWorkbenchPageManager { + + private static Map instanceMap = new HashMap(); + + public static CaveWorkbenchPageManager getInstance(IWorkbenchPage page) { + synchronized (instanceMap) { + CaveWorkbenchPageManager instance = instanceMap.get(page); + if (instance == null) { + instance = new CaveWorkbenchPageManager(page); + instanceMap.put(page, instance); + } + return instance; + } + } + + public static CaveWorkbenchPageManager getActiveInstance() { + synchronized (instanceMap) { + return getInstance(PlatformUI.getWorkbench() + .getActiveWorkbenchWindow().getActivePage()); + } + } + + private final IWorkbenchPage workbenchPage; + + private List detachedWindows = new ArrayList(); + + private CaveWorkbenchPageManager(IWorkbenchPage workbenchPage) { + this.workbenchPage = workbenchPage; + workbenchPage.getWorkbenchWindow().addPageListener( + new PageListener(this)); + } + + public IWorkbenchPage getWorkbenchPage() { + return workbenchPage; + } + + public IViewReference[] getViewReferences() { + if (detachedWindows.isEmpty()) { + return workbenchPage.getViewReferences(); + } + List result = Arrays.asList(workbenchPage + .getViewReferences()); + result = new ArrayList(result); + for (CaveDetachedWindow window : detachedWindows) { + for (LayoutPart part : window.getChildren()) { + if (part instanceof ViewPane) { + result.add(((ViewPane) part).getViewReference()); + } + } + } + return result.toArray(new IViewReference[0]); + } + + public IViewPart showView(String viewID) throws PartInitException { + return showView(viewID, null, IWorkbenchPage.VIEW_ACTIVATE); + } + + public IViewPart showView(String viewId, String secondaryId, int mode) + throws PartInitException { + IViewReference ref = findDetachedViewReference(viewId, secondaryId); + if (ref != null) { + IViewPart view = ref.getView(true); + if (mode == IWorkbenchPage.VIEW_ACTIVATE) { + activate(view); + } else if (mode == IWorkbenchPage.VIEW_VISIBLE) { + workbenchPage.bringToTop(view); + } + return view; + } + return workbenchPage.showView(viewId, secondaryId, mode); + } + + public void activate(IViewPart view) { + for (CaveDetachedWindow window : detachedWindows) { + for (LayoutPart part : window.getChildren()) { + if (((ViewPane) part).getPartReference().getPart(false) == view) { + window.getShell().forceActive(); + ILayoutContainer container = part.getContainer(); + if (container != null && container instanceof PartStack) { + PartStack folder = (PartStack) container; + if (folder.getSelection() != part) { + folder.setSelection(part); + } + } + return; + } + } + } + workbenchPage.activate(view); + } + + public void bringToTop(IViewPart view) { + for (CaveDetachedWindow window : detachedWindows) { + for (LayoutPart part : window.getChildren()) { + if (((ViewPane) part).getPartReference().getPart(false) == view) { + window.getShell().moveAbove(null); + ILayoutContainer container = part.getContainer(); + if (container != null && container instanceof PartStack) { + PartStack folder = (PartStack) container; + if (folder.getSelection() != part) { + folder.setSelection(part); + } + } + return; + } + } + } + workbenchPage.bringToTop(view); + } + + public IViewReference findViewReference(String viewId) { + return findViewReference(viewId, null); + } + + public IViewReference findViewReference(String viewId, String secondaryId) { + IViewReference ref = findDetachedViewReference(viewId, secondaryId); + if (ref == null) { + ref = workbenchPage.findViewReference(viewId, secondaryId); + } + return ref; + } + + private IViewReference findDetachedViewReference(String viewId, + String secondaryId) { + for (CaveDetachedWindow window : detachedWindows) { + for (LayoutPart part : window.getChildren()) { + if (part instanceof ViewPane) { + IViewReference ref = ((ViewPane) part).getViewReference(); + if (ref.getId().equals(viewId) + && ((secondaryId == null && ref.getSecondaryId() == null) || secondaryId + .equals(ref.getSecondaryId()))) { + return ref; + } + } + } + } + return null; + } + + public void hideView(IViewReference view) { + for (CaveDetachedWindow window : detachedWindows) { + for (LayoutPart part : window.getChildren()) { + if (part instanceof ViewPane) { + if (((ViewPane) part).getPartReference() == view) { + WorkbenchPage page = (WorkbenchPage) workbenchPage; + if (page.getActivePartReference() == view) { + // You cannot hide the active part, normally the + // page changes the active part but since we bypass + // the page we need to choose a new active part + for (IWorkbenchPartReference wbRef : page + .getSortedParts()) { + if (wbRef != view) { + page.activate(wbRef.getPart(false)); + break; + } + } + } + page.getActivePerspective().hideView(view); + return; + } + } + } + } + workbenchPage.hideView(view); + } + + public void floatView(IViewPart part) { + ViewSite site = ((ViewSite) part.getViewSite()); + WorkbenchPage page = (WorkbenchPage) site.getPage(); + PartPane pane = site.getPane(); + Control control = pane.getStack().getControl(); + Rectangle bounds = control.getBounds(); + Point corner = control.getParent().toDisplay(bounds.x, bounds.y); + bounds.x = corner.x; + bounds.y = corner.y; + CaveDetachedWindow window = new CaveDetachedWindow(page); + window.setBounds(bounds); + window.create(); + window.drop(pane); + window.getShell().setBounds(bounds); + window.getShell().addShellListener(new ShellListener(this, window)); + window.open(); + window.getShell().forceActive(); + detachedWindows.add(window); + } + + public void dockView(IViewPart part) { + WorkbenchPage page = (WorkbenchPage) part.getSite().getPage(); + IViewReference ref = (IViewReference) page.getReference(part); + page.attachView(ref); + } + + private static class ShellListener extends ShellAdapter { + + private final CaveWorkbenchPageManager manager; + + private final CaveDetachedWindow window; + + public ShellListener(CaveWorkbenchPageManager manager, + CaveDetachedWindow window) { + this.manager = manager; + this.window = window; + } + + @Override + public void shellClosed(ShellEvent e) { + manager.detachedWindows.remove(window); + } + + } + + private static class PageListener implements IPageListener { + + private final CaveWorkbenchPageManager manager; + + public PageListener(CaveWorkbenchPageManager manager) { + this.manager = manager; + } + + @Override + public void pageActivated(IWorkbenchPage page) { + + } + + @Override + public void pageClosed(IWorkbenchPage page) { + if (page == manager.getWorkbenchPage()) { + page.getWorkbenchWindow().removePageListener(this); + } + + } + + @Override + public void pageOpened(IWorkbenchPage page) { + + } + + } +}