Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2014-06-18 15:57:18 +0000
committerChristian W. Damus2014-07-24 20:15:37 +0000
commitdfde88b139d345b628415dda0769aa87dd7733c6 (patch)
tree6eea3b88d5bbf1f5979a37770dd836407b68a50b /plugins
parent660eb9df792ce11992342dfa712e659811d1da11 (diff)
downloadorg.eclipse.papyrus-dfde88b139d345b628415dda0769aa87dd7733c6.tar.gz
org.eclipse.papyrus-dfde88b139d345b628415dda0769aa87dd7733c6.tar.xz
org.eclipse.papyrus-dfde88b139d345b628415dda0769aa87dd7733c6.zip
437217: [Editors] In-place reloading of model resources in the editors
https://bugs.eclipse.org/bugs/show_bug.cgi?id=437217 In situ editor reloading. Introduces an IReloadableEditor adapter protocol with an implementation in the CoreMultiDiagramEditor that implements internal destruction of the ServicesRegistry and nested editors. Some refactoring of the initialization and disposal code in the editor class hierarchy and dependencies facilitates reuse of init/dispose code in the reload scenario. The re-loading of an editor is deferred until it is next activated, unless it is already the active editor (can happen when "Save All" is invoked). Editor re-load notifications to dependent views like Model Explorer and Outline. A new listener protocol informs dependents before and after reload so that they may properly dispose of obsolete state and re-initialize when the editor is reloaded. Also ensure that an editor is only reloaded once when some resource that it depends on has changed, not once for each resource. State restoration tokens. Re-load listeners can insert tokens into the re-load event that capture state to be restored after the re-load. Listeners retrieve and apply these tokens after the editor re-loads itself. Current state restoration includes: - tree node expansion and selection state in the Model Explorer view - diagram outline view: which presentation (tree or overview thumbnail) is active - which workbench part is active, such that the correct selection is reflected in views such as Model Explorer, Outline, and Properties - current active diagram in the re-loaded editor - edit-part selections in all diagrams - selection (columns and rows, not individual cells) in table editors - palettes in each diagram (or palette pages when the Palette View is open): * active tool * pinnable stack tool selection * drawer expansion state * drawer scroll position The Palette View support incidentally fixes loss of palette state when switching between Papyrus editors, caused by the PapyrusPaletteSynchronizer. JUnit regression tests for various aspects of editor re-load. Includes a fix for an NPE in the Validation View's content provider that occurs in several tests when an editor is closed or re-loaded. Also support for tests that need to load more than one test-fixture model and/or open more than one editor. Change-Id: Ic0f654ab138d3e091f81f1e9159bcca80d6bb0a5
Diffstat (limited to 'plugins')
-rw-r--r--plugins/editor/org.eclipse.papyrus.editor/META-INF/MANIFEST.MF3
-rw-r--r--plugins/editor/org.eclipse.papyrus.editor/src/org/eclipse/papyrus/editor/PapyrusPaletteSynchronizer.java57
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/editor/AbstractMultiPageSashEditor.java47
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/internal/SashWindowsContainer.java12
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF3
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/contentoutline/NestedEditorDelegatedOutlinePage.java152
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/CoreMultiDiagramEditor.java178
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/IReloadableEditor.java106
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/MultiDiagramEditorSelectionContext.java218
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/CompositeReloadContext.java86
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/DelegatingReloadContext.java74
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFSelectionContext.java52
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFTreeViewerContext.java52
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadAdapter.java36
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadEvent.java189
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IDisposableReloadContext.java23
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IEditorReloadListener.java44
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IInternalEMFSelectionContext.java106
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IReloadContextProvider.java38
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/SelectionContext.java81
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/TreeViewerContext.java62
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ResourceUpdateService.java80
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/plugin.xml7
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/DiagramReloadContextProvider.java149
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/PaletteViewerReloadContextProvider.java117
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/SynchronizableGmfDiagramEditor.java162
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineAdapterFactory.java39
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineReloadContextProvider.java135
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.outline/src/org/eclipse/papyrus/infra/gmfdiag/outline/DiagramOutline.java56
-rw-r--r--plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/META-INF/MANIFEST.MF3
-rw-r--r--plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/AbstractEMFNattableEditor.java6
-rw-r--r--plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/NattableReloadContextProvider.java90
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/LocalMemento.java284
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/UIUtil.java11
-rw-r--r--plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerTreeViewerContext.java141
-rw-r--r--plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerView.java173
-rw-r--r--plugins/views/validation/org.eclipse.papyrus.views.validation/src/org/eclipse/papyrus/views/validation/internal/providers/ProblemsContentProvider.java95
37 files changed, 2930 insertions, 237 deletions
diff --git a/plugins/editor/org.eclipse.papyrus.editor/META-INF/MANIFEST.MF b/plugins/editor/org.eclipse.papyrus.editor/META-INF/MANIFEST.MF
index 4995983e4c5..7e0bbf7ac34 100644
--- a/plugins/editor/org.eclipse.papyrus.editor/META-INF/MANIFEST.MF
+++ b/plugins/editor/org.eclipse.papyrus.editor/META-INF/MANIFEST.MF
@@ -6,7 +6,8 @@ Require-Bundle: org.eclipse.papyrus.infra.core;bundle-version="1.0.0",
org.eclipse.gmf.runtime.diagram.ui;bundle-version="1.5.0",
org.eclipse.papyrus.infra.core.sasheditor;bundle-version="1.0.0",
org.eclipse.ui.ide;bundle-version="3.8.0",
- org.eclipse.papyrus.infra.core.log;bundle-version="1.0.0"
+ org.eclipse.papyrus.infra.core.log;bundle-version="1.0.0",
+ com.google.guava;bundle-version="11.0.0"
Bundle-Vendor: %providerName
Bundle-ActivationPolicy: lazy
Bundle-Version: 1.0.0.qualifier
diff --git a/plugins/editor/org.eclipse.papyrus.editor/src/org/eclipse/papyrus/editor/PapyrusPaletteSynchronizer.java b/plugins/editor/org.eclipse.papyrus.editor/src/org/eclipse/papyrus/editor/PapyrusPaletteSynchronizer.java
index ad19bc2d895..f655bfc175a 100644
--- a/plugins/editor/org.eclipse.papyrus.editor/src/org/eclipse/papyrus/editor/PapyrusPaletteSynchronizer.java
+++ b/plugins/editor/org.eclipse.papyrus.editor/src/org/eclipse/papyrus/editor/PapyrusPaletteSynchronizer.java
@@ -1,3 +1,16 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Montages AG, CEA, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Montages AG - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437217
+ *
+ *****************************************************************************/
package org.eclipse.papyrus.editor;
import org.eclipse.gef.ui.views.palette.PaletteView;
@@ -20,27 +33,15 @@ import org.eclipse.ui.IWorkbenchPage;
public PapyrusPaletteSynchronizer(PapyrusMultiDiagramEditor multiEditor) {
myMultiDiagramEditor = multiEditor;
+
+ // Handle the initial page selection
+ synchronizePaletteView(multiEditor.getISashWindowsContainer().getActiveSashWindowsPage());
}
- /*
- * (non-Javadoc)
- *
- * @see
- * org.eclipse.papyrus.infra.core.sasheditor.internal.ActivePageTracker.IActiveEditorChangedListener
- * #activeEditorChanged(org.eclipse.papyrus.infra.core.sasheditor.internal.PagePart,
- * org.eclipse.papyrus.infra.core.sasheditor.internal.PagePart)
- */
public void activeEditorChanged(PagePart oldEditor, PagePart newEditor) {
synchronizePaletteView(newEditor);
}
- /*
- * (non-Javadoc)
- *
- * @see
- * org.eclipse.papyrus.infra.core.sasheditor.editor.IPageChangedListener#pageChanged(org.eclipse
- * .papyrus.infra.core.sasheditor.editor.IPage)
- */
public void pageChanged(IPage newPage) {
synchronizePaletteView(newPage);
}
@@ -50,21 +51,21 @@ import org.eclipse.ui.IWorkbenchPage;
* inner page
*
* @param activePage
- * inner page to synchronize palette view with
+ * inner page to synchronize palette view with
*/
private void synchronizePaletteView(IPage activePage) {
PaletteView paletteView = findPaletteView();
- if (paletteView == null) {
+ if(paletteView == null) {
return;
}
// IEditorPage is not granted, it may be, e.g ErrorComponentPart
- IEditorPart activePart = activePage instanceof IEditorPage ? ((IEditorPage) activePage).getIEditorPart() : null;
- if (activePart == myLastActivePart) {
+ IEditorPart activePart = activePage instanceof IEditorPage ? ((IEditorPage)activePage).getIEditorPart() : null;
+ if(activePart == myLastActivePart) {
return;
}
-
- if (activePart == null) {
+
+ if(activePart == null) {
paletteView.partClosed(myLastActivePart);
} else {
// multi-editor may be activated outside of this code
@@ -80,15 +81,15 @@ import org.eclipse.ui.IWorkbenchPage;
* Called when host editor is disposed, cleans up
*/
public void dispose() {
- if (myLastActivePart == null) {
- // nothing to do
- return;
- }
PaletteView paletteView = findPaletteView();
- if (paletteView == null) {
+ if(paletteView == null) {
return;
}
- paletteView.partClosed(myLastActivePart);
+ if(myLastActivePart != null) {
+ paletteView.partClosed(myLastActivePart);
+ } else {
+ paletteView.partClosed(myMultiDiagramEditor);
+ }
}
/**
@@ -98,7 +99,7 @@ import org.eclipse.ui.IWorkbenchPage;
*/
private PaletteView findPaletteView() {
IWorkbenchPage samePage = myMultiDiagramEditor.getSite().getPage();
- return (PaletteView) samePage.findView(PaletteView.ID);
+ return (PaletteView)samePage.findView(PaletteView.ID);
}
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/editor/AbstractMultiPageSashEditor.java b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/editor/AbstractMultiPageSashEditor.java
index 69e50222664..c33a59e41df 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/editor/AbstractMultiPageSashEditor.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/editor/AbstractMultiPageSashEditor.java
@@ -9,6 +9,7 @@
* Contributors:
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 431953 (pre-requisite refactoring of ModelSet service start-up)
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.sasheditor.editor;
@@ -33,6 +34,9 @@ import org.eclipse.ui.part.EditorPart;
*/
public abstract class AbstractMultiPageSashEditor extends EditorPart implements IMultiPageEditorPart, IMultiEditorManager {
+ /** The parent composite of my sash container. */
+ private Composite parentComposite;
+
/** The pageProvider */
private ISashWindowsContentProvider pageProvider;
@@ -116,7 +120,6 @@ public abstract class AbstractMultiPageSashEditor extends EditorPart implements
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
setSite(site);
setInput(input);
- site.setSelectionProvider(new MultiPageSelectionProvider(this));
}
/**
@@ -124,15 +127,10 @@ public abstract class AbstractMultiPageSashEditor extends EditorPart implements
*/
@Override
public void createPartControl(Composite parent) {
-
- // Create and intialize sash windows
- sashContainer = new SashWindowsContainer(this);
- sashContainer.setContentProvider(getContentProvider());
- sashContainer.createPartControl(parent);
-
- // Add double click menu
- tabMouseEventListener = new TabMouseEventListener(sashContainer, getSite());
-
+ parentComposite = parent;
+
+ getSite().setSelectionProvider(new MultiPageSelectionProvider(this));
+
activate();
}
@@ -143,6 +141,14 @@ public abstract class AbstractMultiPageSashEditor extends EditorPart implements
*/
protected void activate() {
+ // Create and initialize sash windows
+ sashContainer = new SashWindowsContainer(this);
+ sashContainer.setContentProvider(getContentProvider());
+ sashContainer.createPartControl(parentComposite);
+
+ // Add double click menu
+ tabMouseEventListener = new TabMouseEventListener(sashContainer, getSite());
+
tabsSynchronizer = new SashTabDecorationSynchronizer(sashContainer);
}
@@ -160,6 +166,16 @@ public abstract class AbstractMultiPageSashEditor extends EditorPart implements
}
tabsSynchronizer.dispose();
tabsSynchronizer = null;
+
+ if(tabMouseEventListener != null) {
+ tabMouseEventListener.dispose(sashContainer);
+ tabMouseEventListener = null;
+ }
+
+ if(sashContainer != null) {
+ sashContainer.dispose();
+ }
+ pageProvider = null;
}
/**
@@ -171,18 +187,9 @@ public abstract class AbstractMultiPageSashEditor extends EditorPart implements
@Override
public void dispose() {
deactivate();
-
- if(tabMouseEventListener != null) {
- tabMouseEventListener.dispose(sashContainer);
- tabMouseEventListener = null;
- }
-
+
//The selection provider keeps a reference to "this". It is not disposed.
getSite().setSelectionProvider(null);
- if(sashContainer != null) {
- sashContainer.dispose();
- }
- pageProvider = null;
super.dispose();
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/internal/SashWindowsContainer.java b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/internal/SashWindowsContainer.java
index 35c7316ff8e..926883e994d 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/internal/SashWindowsContainer.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/src/org/eclipse/papyrus/infra/core/sasheditor/internal/SashWindowsContainer.java
@@ -1,6 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2009 CEA LIST & LIFL
- *
+ * Copyright (c) 2009, 2014 LIFL, CEA LIST, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,6 +8,7 @@
*
* Contributors:
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.sasheditor.internal;
@@ -284,7 +284,13 @@ public class SashWindowsContainer implements ISashWindowsContainer {
// End disposing children's SWT controls.
// It is possible to recall the dispose() method on a Widget, even if we are called by the dispose event.
// Recalling the dispose method will continue disposing SWT children's.
- container.dispose();
+
+ // DO NOT dispose the container composite, as we did not create it!
+ if(container != null) {
+ for(Control next : container.getChildren()) {
+ next.dispose();
+ }
+ }
// dispose part children
if(rootPart!=null) {
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF
index 72fe68e603a..b60b29d15a7 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF
@@ -1,8 +1,9 @@
Manifest-Version: 1.0
Export-Package: org.eclipse.papyrus.infra.core,
- org.eclipse.papyrus.infra.core.clipboard,
+ org.eclipse.papyrus.infra.core.clipboard,
org.eclipse.papyrus.infra.core.contentoutline,
org.eclipse.papyrus.infra.core.editor,
+ org.eclipse.papyrus.infra.core.editor.reload,
org.eclipse.papyrus.infra.core.editorsfactory,
org.eclipse.papyrus.infra.core.extension,
org.eclipse.papyrus.infra.core.extension.commands,
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/contentoutline/NestedEditorDelegatedOutlinePage.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/contentoutline/NestedEditorDelegatedOutlinePage.java
index 491760a70d4..3e75bfbff41 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/contentoutline/NestedEditorDelegatedOutlinePage.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/contentoutline/NestedEditorDelegatedOutlinePage.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST and other.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
*
* Contributors:
* Remi Schnekenburger (CEA LIST) - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.contentoutline;
@@ -17,6 +18,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -24,15 +26,25 @@ import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.papyrus.infra.core.Activator;
import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.editor.IReloadableEditor;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadEvent;
+import org.eclipse.papyrus.infra.core.editor.reload.IEditorReloadListener;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IEditorPage;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IPage;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageLifeCycleEventsListener;
import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
@@ -50,10 +62,15 @@ import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.PageSite;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+import com.google.common.collect.Lists;
+
/**
* Page for Papyrus outline when active nested editor is a GMF editor
*/
-public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusContentOutlinePage, IPageLifeCycleEventsListener {
+public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusContentOutlinePage, IPageLifeCycleEventsListener, IEditorReloadListener {
+
+ /** The editor for which I am a slave. */
+ private IMultiDiagramEditor multiEditor;
/** Sash window container to listen for page changes inside the same editor */
private ISashWindowsContainer sashWindowsContainer;
@@ -82,7 +99,16 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
* {@inheritDoc}
*/
public void init(IMultiDiagramEditor multiEditor) {
+ this.multiEditor = multiEditor;
+
+ internalInit(multiEditor);
+
+ IReloadableEditor.Adapter.getAdapter(multiEditor).addEditorReloadListener(this);
+ }
+
+ private void internalInit(IMultiDiagramEditor multiEditor) {
sashWindowsContainer = (ISashWindowsContainer)multiEditor.getAdapter(ISashWindowsContainer.class);
+ sashWindowsContainer.addPageLifeCycleListener(this);
}
/**
@@ -103,6 +129,19 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
*/
@Override
public void dispose() {
+ if(multiEditor != null) {
+ IReloadableEditor.Adapter.getAdapter(multiEditor).removeEditorReloadListener(this);
+ }
+
+ internalDispose();
+
+ multiEditor = null;
+
+ // Run super.
+ super.dispose();
+ }
+
+ private void internalDispose() {
// Deref all of the pages.
activeRec = null;
if(defaultPageRec != null) {
@@ -122,9 +161,6 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
// remove listener and all refs to editor
sashWindowsContainer.removePageLifeCycleListener(this);
-
- // Run super.
- super.dispose();
}
/**
@@ -182,10 +218,12 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
*/
@Override
public void createControl(Composite parent) {
- sashWindowsContainer.addPageLifeCycleListener(this);
-
sashEditorPageBook = new PageBook(parent, SWT.BORDER);
+ createContents();
+ }
+
+ protected void createContents() {
// Create the default page rec.
IContentOutlinePage defaultPage = createDefaultPage(sashEditorPageBook);
defaultPageRec = new OutlinePageRec(null, defaultPage);
@@ -274,11 +312,8 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
*/
public void pageActivated(IPage page) {
// Activator.log.debug("Activated");
- // Create a page for the part.
- OutlinePageRec rec = getOutlinePageRec(page);
- if(rec == null) {
- rec = createPage(page);
- }
+ // Create a page for the partm, if necessary.
+ OutlinePageRec rec = getOutlinePageRec(page, true);
// Show the page, if it was successfully created
if(rec != null) {
@@ -309,6 +344,21 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
//throw new UnsupportedOperationException("pageAboutToBeClosed not implemented " + page);
}
+ @Override
+ public void editorAboutToReload(EditorReloadEvent event) {
+ event.putContext(new OutlineContext());
+
+ internalDispose();
+ }
+
+ @Override
+ public void editorReloaded(EditorReloadEvent event) {
+ internalInit(event.getEditor());
+ createContents();
+
+ ((OutlineContext)event.getContext()).restore();
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MAINLY INSPIRED FROM PAGE BOOK VIEW
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -537,6 +587,14 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
return mapIPapyrusPageToOutlineRec.get(papyrusPage);
}
+ OutlinePageRec getOutlinePageRec(IPage papyrusPage, boolean create) {
+ OutlinePageRec result = getOutlinePageRec(papyrusPage);
+ if(result == null) {
+ result = createPage(papyrusPage);
+ }
+ return result;
+ }
+
/**
* Returns the page record for the given page of this view.
*
@@ -572,14 +630,15 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
control.dispose();
}
- // free the page
- doDestroyPage(rec.papyrusPage, rec);
-
+ // Do this before destroying the page, otherwise we won't be able to retrieve the page site (it will be null)
IPageSite site = rec.getPageSite();
if(site instanceof PageSite) { // test null pointer and PageSite
((SubActionBars)((PageSite)site).getActionBars()).deactivate();
((SubActionBars)((PageSite)site).getActionBars()).dispose();
}
+
+ // Free the page
+ doDestroyPage(rec.papyrusPage, rec);
}
/*
@@ -952,4 +1011,67 @@ public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusCo
}
}
+
+ private class OutlineContext {
+
+ private List<PageContext> pages = Lists.newArrayListWithCapacity(mapIPapyrusPageToOutlineRec.size());
+
+ OutlineContext() {
+ for(OutlinePageRec next : mapIPapyrusPageToOutlineRec.values()) {
+ pages.add(new PageContext(next));
+ }
+ }
+
+ public void restore() {
+ for(PageContext next : pages) {
+ next.restore();
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ private class PageContext {
+
+ final URI diagramToken;
+
+ final Object context;
+
+ PageContext(OutlinePageRec outlinePage) {
+ Object diagram = outlinePage.papyrusPage.getRawModel();
+ diagramToken = (diagram instanceof EObject) ? EcoreUtil.getURI((EObject)diagram) : null;
+
+ // Can only sensibly manage restoring the state of the page if we can find it again
+ if(diagramToken == null) {
+ context = null;
+ } else {
+ IReloadContextProvider provider = AdapterUtils.adapt(outlinePage.contentOutlinePage, IReloadContextProvider.class, null);
+ context = (provider == null) ? null : provider.createReloadContext();
+ }
+ }
+
+ void restore() {
+ if(diagramToken != null) {
+ try {
+ ModelSet modelSet = multiEditor.getServicesRegistry().getService(ModelSet.class);
+
+ Object diagram = modelSet.getEObject(diagramToken, true);
+ if(diagram != null) {
+ IPage page = sashWindowsContainer.lookupModelPage(diagram);
+ if(page != null) {
+ OutlinePageRec outlinePage = getOutlinePageRec(page, true);
+ if((outlinePage != null) && (context != null)) {
+ // Restore it. We know it adapts if it provided the reload state in the first place
+ AdapterUtils.adapt(outlinePage.contentOutlinePage, IReloadContextProvider.class, null).restore(context);
+ }
+ }
+ }
+ } catch (ServiceException e) {
+ Activator.log.error(e);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/CoreMultiDiagramEditor.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/CoreMultiDiagramEditor.java
index 70800f02d49..50644e87186 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/CoreMultiDiagramEditor.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/CoreMultiDiagramEditor.java
@@ -12,6 +12,7 @@
* Christian W. Damus (CEA) - manage models by URI, not IFile (CDO)
* Christian W. Damus (CEA) - bug 410346
* Christian W. Damus (CEA) - bug 431953 (pre-requisite refactoring of ModelSet service start-up)
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
@@ -22,10 +23,14 @@ import static org.eclipse.papyrus.infra.core.Activator.log;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.common.util.URI;
@@ -41,10 +46,13 @@ import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.papyrus.infra.core.Activator;
import org.eclipse.papyrus.infra.core.contentoutline.ContentOutlineRegistry;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadEvent;
+import org.eclipse.papyrus.infra.core.editor.reload.IEditorReloadListener;
import org.eclipse.papyrus.infra.core.lifecycleevents.DoSaveEvent;
import org.eclipse.papyrus.infra.core.lifecycleevents.IEditorInputChangedListener;
import org.eclipse.papyrus.infra.core.lifecycleevents.ISaveAndDirtyService;
@@ -86,11 +94,15 @@ import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
+import com.google.common.collect.ImmutableList;
+
/**
* Multi diagram editor allowing to plug various kind of editors. Editors are
* registered with the help of the Eclipse extension mechanism. This
@@ -194,7 +206,7 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
}
}
- protected EditorInputChangedListener editorInputChangedListener = new EditorInputChangedListener(this);
+ protected EditorInputChangedListener editorInputChangedListener;
private TransactionalEditingDomain transactionalEditingDomain;
@@ -222,7 +234,7 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
/**
* A listener on model change events.
*/
- private ContentChangedListener contentChangedListener = new ContentChangedListener();
+ private ContentChangedListener contentChangedListener;
/**
* Undo context used to have the same undo context in all Papyrus related
@@ -233,6 +245,22 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
private IUndoContext undoContext;
/**
+ * Editor reload listeners.
+ */
+ private CopyOnWriteArrayList<IEditorReloadListener> reloadListeners = new CopyOnWriteArrayList<IEditorReloadListener>();
+
+ /**
+ * Whether a re-load is currently pending (awaiting next activation of the editor).
+ */
+ private boolean reloadPending;
+
+ public CoreMultiDiagramEditor() {
+ super();
+
+ addSelfReloadListener();
+ }
+
+ /**
* Get the contentOutlineRegistry. Create it if needed.
*
* @return the contentOutlineRegistry
@@ -431,6 +459,10 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
return getSite().getSelectionProvider().getSelection();
}
+ if(adapter == IReloadableEditor.class) {
+ return createReloadAdapter();
+ }
+
return super.getAdapter(adapter);
}
@@ -445,8 +477,7 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
// Set editor name
setPartName(input.getName());
- loadModelAndServices();
- loadNestedEditors();
+ initContents();
}
@Override
@@ -610,6 +641,7 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
// Listen on input changed from the ISaveAndDirtyService
+ editorInputChangedListener = new EditorInputChangedListener(this);
saveAndDirtyService.addInputChangedListener(editorInputChangedListener);
getLifecycleManager().firePostInit(this);
}
@@ -642,6 +674,9 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
setContentProvider(contentProvider);
// Listen on contentProvider changes
+ if(contentChangedListener == null) {
+ contentChangedListener = new ContentChangedListener();
+ }
sashModelMngr.getSashModelContentChangedProvider().addListener(contentChangedListener);
IEditorInput input = getEditorInput();
@@ -680,8 +715,8 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
*/
@Override
protected void activate() {
- // TODO Auto-generated method stub
super.activate();
+
initFolderTabMenus();
try {
@@ -739,17 +774,77 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
*/
@Override
public void dispose() {
+ for(IPropertySheetPage propertiesPage : this.propertiesPages) {
+ propertiesPage.dispose();
+ }
+ propertiesPages.clear();
+
+ super.dispose();
+ }
+
+ private IReloadableEditor createReloadAdapter() {
+ return new IReloadableEditor() {
+
+ @Override
+ public void reloadEditor(boolean save) throws CoreException {
+ reloadPending = true;
+
+ if(save) {
+ try {
+ ((IWorkbenchSiteProgressService)getSite().getService(IWorkbenchSiteProgressService.class)).busyCursorWhile(new IRunnableWithProgress() {
+
+ @Override
+ public void run(IProgressMonitor monitor) {
+ doSave(monitor);
+ }
+ });
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Save before re-load failed.", e)); //$NON-NLS-1$
+ }
+ }
+
+ // If I am already active, then re-load now
+ IWorkbenchPage page = getSite().getPage();
+ if(page.getActiveEditor() == CoreMultiDiagramEditor.this) {
+ doReload();
+ }
+ }
+
+ @Override
+ public void addEditorReloadListener(IEditorReloadListener listener) {
+ reloadListeners.addIfAbsent(listener);
+ }
+
+ @Override
+ public void removeEditorReloadListener(IEditorReloadListener listener) {
+ reloadListeners.remove(listener);
+ }
+ };
+ }
+
+ private void addSelfReloadListener() {
+ createReloadAdapter().addEditorReloadListener(new IEditorReloadListener() {
+
+ @Override
+ public void editorAboutToReload(EditorReloadEvent event) {
+ event.putContext(new MultiDiagramEditorSelectionContext(event.getEditor()));
+ }
+
+ @Override
+ public void editorReloaded(EditorReloadEvent event) {
+ ((MultiDiagramEditorSelectionContext)event.getContext()).restore(event.getEditor());
+ }
+ });
+ }
+
+ @Override
+ protected void deactivate() {
getLifecycleManager().fireBeforeClose(this);
if(sashModelMngr != null) {
sashModelMngr.getSashModelContentChangedProvider().removeListener(contentChangedListener);
}
- // Avoid memory leak
- // This call is done from the ServicesRegistry when it is disposed.
- // Don't need to do it there.
- // if(resourceSet != null) {
- // resourceSet.unload();
- // }
+ super.deactivate();
// dispose available service
if(servicesRegistry != null) {
@@ -781,13 +876,66 @@ public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implemen
undoContext = null;
saveAndDirtyService = null;
sashModelMngr = null;
+ }
- for(IPropertySheetPage propertiesPage : this.propertiesPages) {
- propertiesPage.dispose();
+ void initContents() throws PartInitException {
+ loadModelAndServices();
+ loadNestedEditors();
+ }
+
+ @Override
+ public void setFocus() {
+ super.setFocus();
+
+ if(isReloadPending()) {
+ doReload();
+ }
+ }
+
+ boolean isReloadPending() {
+ return reloadPending;
+ }
+
+ private void doReload() {
+ reloadPending = false;
+
+ final IWorkbenchPage page = getSite().getPage();
+ final IWorkbenchPart activePart = page.getActivePart();
+ final IEditorPart activeEditor = page.getActiveEditor();
+
+ final Iterable<? extends IEditorReloadListener> listeners = ImmutableList.copyOf(reloadListeners);
+ final EditorReloadEvent event = new EditorReloadEvent(CoreMultiDiagramEditor.this);
+
+ try {
+ event.dispatchEditorAboutToReload(listeners);
+
+ deactivate();
+
+ initContents();
+
+ activate();
+
+ // My self-listener will be first, to ensure that the pages are all restored before dependents run
+ event.dispatchEditorReloaded(listeners);
+ } catch (CoreException e) {
+ // Failed to properly unload/load in place, so just close
+ page.closeEditor(CoreMultiDiagramEditor.this, false);
+
+ StatusManager.getManager().handle(e.getStatus(), StatusManager.LOG | StatusManager.SHOW);
+ } finally {
+ event.dispose();
+
+ // Ensure that the editor previously active is active again (if it still exists)
+ if((activeEditor != null) && page.isPartVisible(activeEditor)) {
+ page.activate(activeEditor);
+ }
+
+ // Ensure that the part previously active is active again (if it still exists and is not the active editor)
+ if((activePart != null) && (activePart != activeEditor) && page.isPartVisible(activePart)) {
+ page.activate(activePart);
+ }
}
- propertiesPages.clear();
- super.dispose();
}
/**
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/IReloadableEditor.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/IReloadableEditor.java
new file mode 100644
index 00000000000..be3a7c7aca4
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/IReloadableEditor.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.papyrus.infra.core.Activator;
+import org.eclipse.papyrus.infra.core.editor.reload.IEditorReloadListener;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+
+
+/**
+ * An {@linkplain IAdaptable adapter protocol} for editors that know how to internally
+ * reload themselves without disturbing the workbench window's perspective layout.
+ */
+public interface IReloadableEditor {
+
+ /**
+ * Reloads me in-place in the perspective layout.
+ *
+ * @param save
+ * whether to save before re-loading
+ *
+ * @throws CoreException
+ * on any failure to unload, reload, or whatever
+ */
+ void reloadEditor(boolean save) throws CoreException;
+
+ void addEditorReloadListener(IEditorReloadListener listener);
+
+ void removeEditorReloadListener(IEditorReloadListener listener);
+
+ /**
+ * A convenience adapter for editors that don't actually know how to reload themselves in place.
+ * It simply closes the editor and then opens it again on the original input.
+ */
+ class Adapter implements IReloadableEditor {
+
+ private final IEditorPart editor;
+
+ public Adapter(IEditorPart editor) {
+ super();
+
+ this.editor = editor;
+ }
+
+ public static IReloadableEditor getAdapter(IMultiDiagramEditor editor) {
+ return AdapterUtils.adapt(editor, IReloadableEditor.class, new Adapter(editor));
+ }
+
+ public void reloadEditor(boolean save) throws CoreException {
+ final IWorkbenchPage page = editor.getSite().getPage();
+ final IEditorInput currentInput = editor.getEditorInput();
+
+ final Display display = editor.getSite().getShell().getDisplay();
+
+ final String editorId = editor.getSite().getId();
+
+ if(save) {
+ editor.doSave(new NullProgressMonitor());
+ }
+
+ page.closeEditor(editor, save);
+
+ display.asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ IDE.openEditor(page, currentInput, editorId);
+ } catch (PartInitException ex) {
+ Activator.log.error(ex);
+ }
+ }
+ });
+
+ }
+
+ @Override
+ public void addEditorReloadListener(IEditorReloadListener listener) {
+ // Don't need to track these listeners because I never properly reload an editor
+ }
+
+ @Override
+ public void removeEditorReloadListener(IEditorReloadListener listener) {
+ // Don't need to track these listeners because I never properly reload an editor
+ }
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/MultiDiagramEditorSelectionContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/MultiDiagramEditorSelectionContext.java
new file mode 100644
index 00000000000..366b636e7c1
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/MultiDiagramEditorSelectionContext.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor;
+
+import java.util.List;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.papyrus.infra.core.editor.reload.CompositeReloadContext;
+import org.eclipse.papyrus.infra.core.editor.reload.DelegatingReloadContext;
+import org.eclipse.papyrus.infra.core.editor.reload.EMFSelectionContext;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadEvent;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IComponentPage;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IEditorPage;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IPage;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageVisitor;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+
+import com.google.common.collect.Lists;
+
+
+/**
+ * A {@linkplain EditorReloadEvent reload event} context that restores the selection of which editor page
+ * is active in an {@link IMultiDiagramEditor} that is reloaded and delegates to its pages to capture
+ * re-load context for each to restore whatever they need to restore (internal selections etc.).
+ */
+class MultiDiagramEditorSelectionContext extends CompositeReloadContext {
+
+ private ISashWindowsContainer sashContainer;
+
+ MultiDiagramEditorSelectionContext(IMultiDiagramEditor editor) {
+ super();
+
+ init(editor);
+
+ ActivePageSelectionProvider activePageSelectionProvider = new ActivePageSelectionProvider();
+ IPage active = sashContainer.getActiveSashWindowsPage();
+ DiagramPageContext activePage = null;
+
+ List<IPage> visible = getAllPages(sashContainer);
+
+ for(IPage page : visible) {
+ final DelegatingReloadContext delegator = (page instanceof IEditorPage) ? add(new DelegatingReloadContext(((IEditorPage)page).getIEditorPart())) : null;
+ DiagramPageContext context;
+
+ if(page == active) {
+ // This one will have the selection of the active page
+ context = new DiagramPageContext(activePageSelectionProvider, page, delegator);
+ activePage = context;
+ } else {
+ // We make sure always to restore the active page last
+ // so that its selection is certain to be set properly
+ context = new DiagramPageContext(EmptySelectionProvider.INSTANCE, page, delegator);
+ add(context);
+ }
+ }
+
+ if(activePage != null) {
+ // Restore this one last
+ add(activePage);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ sashContainer = null;
+ super.dispose();
+ }
+
+ private List<IPage> getAllPages(ISashWindowsContainer container) {
+ final List<IPage> result = Lists.newArrayList();
+
+ container.visit(new IPageVisitor() {
+
+ @Override
+ public void accept(IEditorPage page) {
+ result.add(page);
+ }
+
+ @Override
+ public void accept(IComponentPage page) {
+ result.add(page);
+ }
+ });
+
+ return result;
+ }
+
+ private void init(IMultiDiagramEditor editor) {
+ sashContainer = AdapterUtils.adapt(editor, ISashWindowsContainer.class, null);
+ }
+
+ void restore(IMultiDiagramEditor editor) {
+ init(editor);
+
+ ISelectionProvider selectionProvider = new ActivePageSelectionProvider();
+ for(DiagramPageContext next : getReloadContexts(DiagramPageContext.class)) {
+ next.restore(selectionProvider);
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ private class DiagramPageContext extends EMFSelectionContext {
+
+ private URI pageRef;
+
+ private DelegatingReloadContext pageContext;
+
+ DiagramPageContext(ISelectionProvider structuredSelectionProvider, IPage page, DelegatingReloadContext pageContext) {
+ super(structuredSelectionProvider);
+
+ this.pageContext = pageContext;
+ this.pageRef = getToken(page.getRawModel());
+ }
+
+ @Override
+ public void restore(ISelectionProvider structuredSelectionProvider) {
+ IPage page = sashContainer.lookupModelPage(resolveToken(pageRef));
+
+ if((pageContext != null) && (page instanceof IEditorPage)) {
+ pageContext.restore(((IEditorPage)page).getIEditorPart());
+ }
+
+ super.restore(structuredSelectionProvider);
+ }
+
+ @Override
+ protected Object deresolveSelectableElement(Object selectableElement) {
+ return (selectableElement instanceof IPage) ? ((IPage)selectableElement).getRawModel() : super.deresolveSelectableElement(selectableElement);
+ }
+
+ @Override
+ protected Object resolveSelectableElement(Object deresolved) {
+ return sashContainer.lookupModelPage(deresolved);
+ }
+ }
+
+ private static class EmptySelectionProvider implements ISelectionProvider {
+
+ static final EmptySelectionProvider INSTANCE = new EmptySelectionProvider();
+
+ EmptySelectionProvider() {
+ super();
+ }
+
+ @Override
+ public ISelection getSelection() {
+ return StructuredSelection.EMPTY;
+ }
+
+ @Override
+ public void setSelection(ISelection selection) {
+ // Pass
+ }
+
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ // Not needed because the selection is always empty
+ }
+
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ // Not needed because the selection is always empty
+ }
+
+ }
+
+ private class ActivePageSelectionProvider implements ISelectionProvider {
+
+ ActivePageSelectionProvider() {
+ super();
+ }
+
+ @Override
+ public ISelection getSelection() {
+ IPage active = sashContainer.getActiveSashWindowsPage();
+
+ return (active == null) ? StructuredSelection.EMPTY : new StructuredSelection(active);
+ }
+
+ @Override
+ public void setSelection(ISelection selection) {
+ if(!selection.isEmpty()) {
+ IPage page = (IPage)((IStructuredSelection)selection).getFirstElement();
+ sashContainer.selectPage(page);
+ }
+ }
+
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ // Not needed
+ }
+
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ // Not needed
+ }
+
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/CompositeReloadContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/CompositeReloadContext.java
new file mode 100644
index 00000000000..57d99c9fd03
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/CompositeReloadContext.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+
+/**
+ * An {@linkplain EditorReloadEvent#putContext(Object) editor reload context} that composes other reload contexts.
+ * This should be used whenever a {@linkplain IReloadContextProvider reload context provider} supplies multiple
+ * reload contexts, to ensure that they are properly initialized by the reload system.
+ */
+public class CompositeReloadContext implements IDisposableReloadContext, IAdaptable {
+
+ private final Collection<Object> reloadContexts;
+
+ public CompositeReloadContext() {
+ this(Collections.EMPTY_LIST);
+ }
+
+ public CompositeReloadContext(Iterable<?> reloadContexts) {
+ super();
+
+ this.reloadContexts = Lists.newArrayList(reloadContexts);
+ }
+
+ public <T> T add(T reloadContext) {
+ reloadContexts.add(reloadContext);
+ return reloadContext;
+ }
+
+ public Iterable<?> getReloadContexts() {
+ return Collections.unmodifiableCollection(reloadContexts);
+ }
+
+ public <T> Iterable<T> getReloadContexts(Class<T> type) {
+ return Iterables.filter(getReloadContexts(), type);
+ }
+
+ @Override
+ public void dispose() {
+ for(Object next : reloadContexts) {
+ if(next instanceof IDisposableReloadContext) {
+ ((IDisposableReloadContext)next).dispose();
+ }
+ }
+
+ reloadContexts.clear();
+ }
+
+ @Override
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null;
+ }
+
+ private IInternalEMFSelectionContext getEMFContext() {
+ IInternalEMFSelectionContext result = null;
+
+ for(Object next : reloadContexts) {
+ if(AdapterUtils.adapt(next, IInternalEMFSelectionContext.class, null) != null) {
+ // We need the adapter
+ result = new IInternalEMFSelectionContext.Composite(this);
+ break;
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/DelegatingReloadContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/DelegatingReloadContext.java
new file mode 100644
index 00000000000..405c7c9977d
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/DelegatingReloadContext.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+
+
+/**
+ * An {@linkplain EditorReloadEvent#putContext(Object) editor reload context} that delegates to another reload context.
+ * This should be used whenever a {@linkplain IReloadContextProvider reload context provider} is needed to get a reload
+ * context to delegate to.
+ */
+public class DelegatingReloadContext implements IDisposableReloadContext, IAdaptable {
+
+ private Object delegate;
+
+ public DelegatingReloadContext(Object reloadContextProvider) {
+ super();
+
+ IReloadContextProvider provider = AdapterUtils.adapt(reloadContextProvider, IReloadContextProvider.class, null);
+ if(provider != null) {
+ delegate = provider.createReloadContext();
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if(delegate instanceof IDisposableReloadContext) {
+ ((IDisposableReloadContext)delegate).dispose();
+ }
+
+ delegate = null;
+ }
+
+ public Object getDelegate() {
+ return delegate;
+ }
+
+ public void restore(Object reloadContextProvider) {
+ if(delegate != null) {
+ IReloadContextProvider provider = AdapterUtils.adapt(reloadContextProvider, IReloadContextProvider.class, null);
+ if(provider != null) {
+ provider.restore(delegate);
+ }
+ }
+ }
+
+ @Override
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null;
+ }
+
+ private IInternalEMFSelectionContext getEMFContext() {
+ IInternalEMFSelectionContext result = null;
+
+ if((delegate != null) && (AdapterUtils.adapt(delegate, IInternalEMFSelectionContext.class, null) != null)) {
+ // We need the adapter
+ result = new IInternalEMFSelectionContext.Delegating(this);
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFSelectionContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFSelectionContext.java
new file mode 100644
index 00000000000..e625a97aad7
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFSelectionContext.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+
+/**
+ * A convenient selection re-load context for UIs that present EMF-based content.
+ */
+public class EMFSelectionContext extends SelectionContext<ISelectionProvider, URI> implements IAdaptable {
+
+ private IInternalEMFSelectionContext emfContext;
+
+ public EMFSelectionContext(ISelectionProvider structuredSelectionProvider) {
+ super(structuredSelectionProvider);
+ }
+
+ @Override
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null;
+ }
+
+ final IInternalEMFSelectionContext getEMFContext() {
+ if(emfContext == null) {
+ emfContext = new IInternalEMFSelectionContext.Default();
+ }
+ return emfContext;
+ }
+
+ @Override
+ protected URI getToken(Object object) {
+ return getEMFContext().getToken(object);
+ }
+
+ @Override
+ protected Object resolveToken(URI token) {
+ return getEMFContext().resolveToken(token);
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFTreeViewerContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFTreeViewerContext.java
new file mode 100644
index 00000000000..91b01e98d15
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EMFTreeViewerContext.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+
+/**
+ * A convenient context object for {@link IEditorReloadListener}s to store in an {@link EditorReloadEvent} to capture and restore
+ * the expansion and selection state of nodes in an EMF-based tree viewer.
+ */
+public class EMFTreeViewerContext extends TreeViewerContext<URI> implements IAdaptable {
+
+ private IInternalEMFSelectionContext emfContext;
+
+ public EMFTreeViewerContext(AbstractTreeViewer viewer) {
+ super(viewer);
+ }
+
+ @Override
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null;
+ }
+
+ final IInternalEMFSelectionContext getEMFContext() {
+ if(emfContext == null) {
+ emfContext = new IInternalEMFSelectionContext.Default();
+ }
+ return emfContext;
+ }
+
+ @Override
+ protected URI getToken(Object object) {
+ return getEMFContext().getToken(object);
+ }
+
+ @Override
+ protected Object resolveToken(URI token) {
+ return getEMFContext().resolveToken(token);
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadAdapter.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadAdapter.java
new file mode 100644
index 00000000000..64a8445081c
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+
+
+/**
+ * Convenience superclass for selective implementation of editor reload call-backs.
+ */
+public class EditorReloadAdapter implements IEditorReloadListener {
+
+ public EditorReloadAdapter() {
+ super();
+ }
+
+ @Override
+ public void editorAboutToReload(EditorReloadEvent event) {
+ // Pass
+ }
+
+ @Override
+ public void editorReloaded(EditorReloadEvent event) {
+ // Pass
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadEvent.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadEvent.java
new file mode 100644
index 00000000000..20416bc86d8
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/EditorReloadEvent.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import java.util.EventObject;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.papyrus.infra.core.Activator;
+import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.editor.IReloadableEditor;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+
+
+/**
+ * The event object for notifications of each phase in the reloading of a {@linkplain IReloadableEditor reloadable editor}.
+ */
+public class EditorReloadEvent extends EventObject {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int ABOUT_TO_RELOAD = 1;
+
+ public static final int RELOADED = 2;
+
+ private int type;
+
+ private transient Map<IEditorReloadListener, Object> context;
+
+ private transient IEditorReloadListener currentListener;
+
+ public EditorReloadEvent(IMultiDiagramEditor editor) {
+ super(editor);
+ }
+
+ public final IMultiDiagramEditor getEditor() {
+ return (IMultiDiagramEditor)getSource();
+ }
+
+ public final int getEventType() {
+ return type;
+ }
+
+ /**
+ * Puts some opaque representation of contextual state for the caller to retrieve later when an editor is
+ * {@linkplain IEditorReloadListener#editorReloaded(EditorReloadEvent) reloaded}.
+ * The canonical example of this usage is storing state such as selection, expanded tree nodes, etc. to restore after re-building a UI that
+ * depends on the reloaded editor. After the editor re-load completes and all listeners are notified of that, then all context objects are
+ * released. Any that implement the {@link IDisposableReloadContext} interface are disposed according to that protocol. This is done even if it
+ * happens that the editor re-load procedure cannot complete normally and the editor is forced to close without notifying the post-reload
+ * listeners.
+ *
+ * @param object
+ * some state to stash for later retrieval following the re-loading of the editor
+ *
+ * @return the previous context object, if any (there normally wouldn't be as each listener that is invoked has its own context storage
+ *
+ * @throws IllegalStateException
+ * on any attempt to invoke this method except during an {@link IEditorReloadListener#editorAboutToReload(EditorReloadEvent)} call-back
+ */
+ public Object putContext(Object object) {
+ checkContext(ABOUT_TO_RELOAD);
+
+ IInternalEMFSelectionContext emfContext = AdapterUtils.adapt(object, IInternalEMFSelectionContext.class, null);
+ if(emfContext != null) {
+ initContext(emfContext);
+ }
+
+ return context.put(currentListener, object);
+ }
+
+ /**
+ * Retrieves an opaque representation of contextual state that was previously {@linkplain #putContext(Object) put} by the caller.
+ *
+ * @return the previously stashed object, or {@code null} if none
+ *
+ * @throws IllegalStateException
+ * on any attempt to invoke this method except during an {@link IEditorReloadListener#editorReloaded(EditorReloadEvent)} call-back
+ */
+ public Object getContext() {
+ checkContext(RELOADED);
+ return context.get(currentListener);
+ }
+
+ private void initContext(IInternalEMFSelectionContext context) {
+ Supplier<ResourceSet> resourceSetSupplier = new Supplier<ResourceSet>() {
+
+ @Override
+ public ResourceSet get() {
+ try {
+ return getEditor().getServicesRegistry().getService(ModelSet.class);
+ } catch (ServiceException e) {
+ Activator.log.error(e);
+ throw new IllegalStateException("Invalid service registry in editor"); //$NON-NLS-1$
+ }
+ }
+ };
+
+ context.setResourceSetSupplier(resourceSetSupplier);
+ }
+
+ protected final void checkContext(int phase) {
+ if(currentListener == null) {
+ throw new IllegalStateException("Not in an IEditorReloadListener call-back"); //$NON-NLS-1$
+ }
+
+ if(phase != this.type) {
+ throw new IllegalStateException(String.format("Not in '%s' listener call-back", (phase == ABOUT_TO_RELOAD) ? "editorAboutToReload" : "editorReloaded"));
+ }
+ }
+
+ public final void dispatchEditorAboutToReload(Iterable<? extends IEditorReloadListener> listeners) {
+ context = Maps.newHashMap();
+ type = ABOUT_TO_RELOAD;
+
+ for(Iterator<? extends IEditorReloadListener> iter = listeners.iterator(); iter.hasNext();) {
+ currentListener = iter.next();
+
+ try {
+ currentListener.editorAboutToReload(this);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in editor reload listener.", e); //$NON-NLS-1$
+ } finally {
+ currentListener = null;
+ }
+ }
+ }
+
+ public final void dispatchEditorReloaded(Iterable<? extends IEditorReloadListener> listeners) {
+ type = RELOADED;
+
+ for(Iterator<? extends IEditorReloadListener> iter = listeners.iterator(); iter.hasNext();) {
+ currentListener = iter.next();
+
+ try {
+ currentListener.editorReloaded(this);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in editor reload listener.", e); //$NON-NLS-1$
+ } finally {
+ currentListener = null;
+ }
+ }
+ }
+
+ public void dispose() {
+ if(context != null) {
+ Error error = null;
+
+ try {
+ for(IDisposableReloadContext next : Iterables.filter(context.values(), IDisposableReloadContext.class)) {
+ try {
+ next.dispose();
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in editor reload context disposal.", e); //$NON-NLS-1$
+ } catch (Error e) {
+ if(error == null) {
+ error = e;
+ }
+ Activator.log.error("Uncaught exception in editor reload context disposal.", e); //$NON-NLS-1$
+ }
+ }
+ } finally {
+ context = null;
+ }
+
+ if(error != null) {
+ throw error;
+ }
+ }
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IDisposableReloadContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IDisposableReloadContext.java
new file mode 100644
index 00000000000..2e44d932fa6
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IDisposableReloadContext.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+/**
+ * Protocol implemented by {@link EditorReloadEvent} context objects that must be disposed when they are no longer needed.
+ *
+ * @see EditorReloadEvent#dispose()
+ */
+public interface IDisposableReloadContext {
+
+ void dispose();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IEditorReloadListener.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IEditorReloadListener.java
new file mode 100644
index 00000000000..27387a89c7c
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IEditorReloadListener.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import java.util.EventListener;
+
+import org.eclipse.papyrus.infra.core.editor.IReloadableEditor;
+
+
+/**
+ * A protocol for notification of the phases of re-loading of an {@link IReloadableEditor}.
+ */
+public interface IEditorReloadListener extends EventListener {
+
+ /**
+ * Notifies that an editor is about to reload. Implementors may put stuff into the {@code event}'s {@link EditorReloadEvent#putContext(Object)
+ * context} to retrieve in an eventual {@linkplain #editorReloaded(EditorReloadEvent) }re-load} notification. The canonical example of this
+ * usage is storing state such as selection, expanded tree nodes, etc. to restore after re-building a UI that depends on the reloaded
+ * editor.
+ *
+ * @param event
+ * notification that an editor is going to re-load itself
+ */
+ void editorAboutToReload(EditorReloadEvent event);
+
+ /**
+ * Notifies that an editor has reloaded. Implementors may retrieve from the {@code event} any {@link EditorReloadEvent#getContext()
+ * context} that they put in {@linkplain #editorAboutToReload(EditorReloadEvent) before} the re-load.
+ *
+ * @param event
+ * notification that an editor has reloaded
+ */
+ void editorReloaded(EditorReloadEvent event);
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IInternalEMFSelectionContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IInternalEMFSelectionContext.java
new file mode 100644
index 00000000000..c0c031a0378
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IInternalEMFSelectionContext.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+
+
+/**
+ * An internal interface provided (usually as an {@linkplain IAdaptable#getAdapter(Class) adapter}) by EMF-based {@linkplain SelectionContext
+ * selection contexts}.
+ */
+interface IInternalEMFSelectionContext {
+
+ void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier);
+
+ URI getToken(Object object);
+
+ Object resolveToken(URI token);
+
+ class Default implements IInternalEMFSelectionContext {
+
+ Supplier<? extends ResourceSet> resourceSetSupplier;
+
+ Default() {
+ super();
+ }
+
+ public void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier) {
+ this.resourceSetSupplier = Suppliers.memoize(resourceSetSupplier);
+ }
+
+ public URI getToken(Object object) {
+ return (object instanceof EObject) ? EcoreUtil.getURI((EObject)object) : null;
+ }
+
+ public Object resolveToken(URI token) {
+ ResourceSet rset = (resourceSetSupplier == null) ? null : resourceSetSupplier.get();
+ return (rset == null) ? null : rset.getEObject(token, true);
+ }
+
+ }
+
+ class Composite extends Default {
+
+ private final CompositeReloadContext composite;
+
+ Composite(CompositeReloadContext composite) {
+ super();
+
+ this.composite = composite;
+ }
+
+ @Override
+ public void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier) {
+ super.setResourceSetSupplier(resourceSetSupplier);
+
+ for(Object next : composite.getReloadContexts()) {
+ IInternalEMFSelectionContext emfContext = AdapterUtils.adapt(next, IInternalEMFSelectionContext.class, null);
+ if(emfContext != null) {
+ // Pass along the memoizer so that we can all share it
+ emfContext.setResourceSetSupplier(this.resourceSetSupplier);
+ }
+ }
+ }
+ }
+
+ class Delegating extends Default {
+
+ private final DelegatingReloadContext delegating;
+
+ Delegating(DelegatingReloadContext delegating) {
+ super();
+
+ this.delegating = delegating;
+ }
+
+ @Override
+ public void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier) {
+ super.setResourceSetSupplier(resourceSetSupplier);
+
+ IInternalEMFSelectionContext emfContext = AdapterUtils.adapt(delegating.getDelegate(), IInternalEMFSelectionContext.class, null);
+ if(emfContext != null) {
+ // Pass along the memoizer so that we can all share it
+ emfContext.setResourceSetSupplier(this.resourceSetSupplier);
+ }
+ }
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IReloadContextProvider.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IReloadContextProvider.java
new file mode 100644
index 00000000000..281c629e023
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/IReloadContextProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+
+/**
+ * An adapter protocol for objects that can provide {@code context}s to be included in the
+ * re-load state of dependent parts in an {@link EditorReloadEvent}, for the purpose of
+ * restoring the state of those objects after re-load has completed.
+ */
+public interface IReloadContextProvider {
+
+ /**
+ * Creates an opaque token from which the receiver can be {@linkplain #restore(Object) restored} after the editor has reloaded.
+ *
+ * @return an opaque editor re-load context, or {@code null} if none is needed on this occasion (for example because the receiver
+ * is in its default state)
+ */
+ Object createReloadContext();
+
+ /**
+ * Reloads the receiver's state from a token previously {@linkplain #createReloadContext() provided}.
+ *
+ * @param reloadContext
+ * the opaque re-load context token
+ */
+ void restore(Object reloadContext);
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/SelectionContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/SelectionContext.java
new file mode 100644
index 00000000000..cfe1e3be89a
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/SelectionContext.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import java.util.List;
+
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+import com.google.common.collect.Lists;
+
+/**
+ * A convenient context object for {@link IEditorReloadListener}s to store in an {@link EditorReloadEvent} to capture and restore
+ * the selection state a selection provider;
+ *
+ * @param <V>
+ * the type of selection provider
+ * @param <T>
+ * the type of token used to restore the selection state
+ */
+public abstract class SelectionContext<V extends ISelectionProvider, T> {
+
+ private List<T> selection = Lists.newArrayList();
+
+ public SelectionContext(V structuredSelectionProvider) {
+ for(Object next : ((IStructuredSelection)structuredSelectionProvider.getSelection()).toList()) {
+ T token = token(next);
+ if(token != null) {
+ selection.add(token);
+ }
+ }
+ }
+
+ public void restore(V structuredSelectionProvider) {
+ List<Object> select = Lists.newArrayListWithCapacity(selection.size());
+ for(T next : selection) {
+ Object resolved = resolve(next);
+ if(resolved != null) {
+ select.add(resolved);
+ }
+ }
+ setSelection(structuredSelectionProvider, select);
+ }
+
+ T token(Object selectableElement) {
+ Object deresolved = deresolveSelectableElement(selectableElement);
+ return (deresolved == null) ? null : getToken(deresolved);
+ }
+
+ protected Object deresolveSelectableElement(Object selectableElement) {
+ return selectableElement;
+ }
+
+ protected abstract T getToken(Object object);
+
+ Object resolve(T token) {
+ Object deresolved = resolveToken(token);
+ return (deresolved == null) ? null : resolveSelectableElement(deresolved);
+ }
+
+ protected Object resolveSelectableElement(Object deresolved) {
+ return deresolved;
+ }
+
+ protected abstract Object resolveToken(T token);
+
+ protected void setSelection(V structuredSelectionProvider, List<?> selection) {
+ structuredSelectionProvider.setSelection(new StructuredSelection(selection));
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/TreeViewerContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/TreeViewerContext.java
new file mode 100644
index 00000000000..2c2ec8520cf
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/reload/TreeViewerContext.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.core.editor.reload;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+import com.google.common.collect.Lists;
+
+/**
+ * A convenient context object for {@link IEditorReloadListener}s to store in an {@link EditorReloadEvent} to capture and restore
+ * the expansion and selection state of nodes in a tree viewer.
+ */
+public abstract class TreeViewerContext<T> extends SelectionContext<AbstractTreeViewer, T> {
+
+ private List<T> expandedNodes = Lists.newArrayList();
+
+ public TreeViewerContext(AbstractTreeViewer viewer) {
+ super(viewer);
+
+ for(Object next : viewer.getExpandedElements()) {
+ T token = token(next);
+ if(token != null) {
+ expandedNodes.add(token);
+ }
+ }
+ }
+
+ public void restore(AbstractTreeViewer viewer) {
+ List<Object> expand = Lists.newArrayListWithCapacity(expandedNodes.size());
+ for(T next : expandedNodes) {
+ Object resolved = resolve(next);
+ if(resolved != null) {
+ expand.add(resolved);
+ }
+ }
+ setExpandedElements(viewer, expand);
+
+ super.restore(viewer);
+ }
+
+ protected void setSelection(AbstractTreeViewer viewer, List<?> selection) {
+ viewer.setSelection(new StructuredSelection(selection), true);
+ }
+
+ protected void setExpandedElements(AbstractTreeViewer viewer, Collection<?> toExpand) {
+ viewer.setExpandedElements(toExpand.toArray());
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ResourceUpdateService.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ResourceUpdateService.java
index 3cf5783c524..74ac57d45c2 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ResourceUpdateService.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ResourceUpdateService.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,12 +9,14 @@
*
* Contributors:
* Camille Letavernier (camille.letavernier@cea.fr) - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.concurrent.ConcurrentMap;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
@@ -23,14 +25,21 @@ import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.core.Activator;
import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.editor.IReloadableEditor;
import org.eclipse.papyrus.infra.core.lifecycleevents.DoSaveEvent;
import org.eclipse.papyrus.infra.core.lifecycleevents.ILifeCycleEventsProvider;
import org.eclipse.papyrus.infra.core.lifecycleevents.ISaveAndDirtyService;
@@ -38,12 +47,14 @@ import org.eclipse.papyrus.infra.core.lifecycleevents.ISaveEventListener;
import org.eclipse.papyrus.infra.core.resource.ModelSet;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+import org.eclipse.ui.progress.UIJob;
+
+import com.google.common.collect.Maps;
/**
* A Service to check workspace modifications on current resources
@@ -61,6 +72,8 @@ public class ResourceUpdateService implements IService, IPartListener {
protected boolean isSaving;
+ protected ConcurrentMap<IMultiDiagramEditor, Job> pendingEditorCloseJobs = Maps.newConcurrentMap();
+
/**
* Update isSaving flag asynchronously to avoid race conditions, see bug 411574
*/
@@ -129,38 +142,49 @@ public class ResourceUpdateService implements IService, IPartListener {
}
final IMultiDiagramEditor editor = registry.getService(IMultiDiagramEditor.class);
if(editor != null) {
- Runnable closeEditorRunnable = new Runnable() {
+ final IWorkbenchPartSite site = editor.getSite();
+ UIJob closeEditorJob = new UIJob(site.getShell().getDisplay(), NLS.bind("Reload editor {0}", editor.getTitle())) {
@Override
- public void run() {
- final IWorkbenchPage page = editor.getSite().getPage();
- final IEditorInput currentInput = editor.getEditorInput();
-
-
- final String editorId = editor.getSite().getId();
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ // Remove the pending job
+ pendingEditorCloseJobs.remove(editor);
+
+ IStatus result = Status.OK_STATUS;
+ monitor = SubMonitor.convert(monitor, IProgressMonitor.UNKNOWN);
+
+ try {
+ if(reopen) {
+ try {
+ IReloadableEditor.Adapter.getAdapter(editor).reloadEditor(save);
+ } catch (CoreException e) {
+ result = e.getStatus();
+ }
+ } else {
+ // Just close it
- if(save) {
- editor.doSave(new NullProgressMonitor());
- }
- page.closeEditor(editor, save);
- if(reopen) {
- Display.getCurrent().asyncExec(new Runnable() {
-
- @Override
- public void run() {
- try {
- IDE.openEditor(page, currentInput, editorId);
- } catch (PartInitException ex) {
- Activator.log.error(ex);
- }
+ if(save) {
+ editor.doSave(new NullProgressMonitor());
}
- });
+
+ final IWorkbenchPage page = editor.getSite().getPage();
+ page.closeEditor(editor, save);
+ }
+ } finally {
+ monitor.done();
}
+
+ return result;
}
};
- //Async execution to avoid lock conflicts on the Workspace (Probably owned by this thread, and not the UI thread)
- editor.getSite().getShell().getDisplay().asyncExec(closeEditorRunnable);
+ // We are notified usually of at least three resources (*.di, *.notation, *.uml) that are unloaded, but
+ // there's no need to close and re-open the same editor three times
+ if(pendingEditorCloseJobs.putIfAbsent(editor, closeEditorJob) == null) {
+ //Async execution to avoid lock conflicts on the Workspace (Probably owned by this thread, and not the UI thread)
+ IWorkbenchSiteProgressService progressService = (IWorkbenchSiteProgressService)site.getService(IWorkbenchSiteProgressService.class);
+ progressService.schedule(closeEditorJob);
+ }
}
} catch (ServiceException ex) {
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/plugin.xml b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/plugin.xml
index ec5c2816412..d24fc3ff8eb 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/plugin.xml
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/plugin.xml
@@ -133,6 +133,13 @@
type="org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.IOpenable">
</adapter>
</factory>
+ <factory
+ adaptableType="org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor$DiagramOutlinePage"
+ class="org.eclipse.papyrus.infra.gmfdiag.common.adapter.DiagramOutlineAdapterFactory">
+ <adapter
+ type="org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider">
+ </adapter>
+ </factory>
</extension>
<!-- ElementType bindings for diagram duplication with paste command -->
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/DiagramReloadContextProvider.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/DiagramReloadContextProvider.java
new file mode 100644
index 00000000000..0250f075318
--- /dev/null
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/DiagramReloadContextProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.gmfdiag.common;
+
+import java.util.Collection;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.gef.EditDomain;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.ui.palette.PaletteViewer;
+import org.eclipse.gef.ui.views.palette.PalettePage;
+import org.eclipse.papyrus.infra.core.editor.reload.EMFSelectionContext;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadEvent;
+import org.eclipse.papyrus.infra.core.editor.reload.IDisposableReloadContext;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+
+import com.google.common.collect.Iterables;
+
+
+/**
+ * An {@linkplain EditorReloadEvent editor reload} context provider for nested diagram editors, providing
+ * a reload context that captures and restores the edit-part selection (tracked by URI of the selected
+ * edit parts' notation views) and palette state (delegated to a {@link PaletteViewerReloadContextProvider}).
+ */
+class DiagramReloadContextProvider implements IReloadContextProvider {
+
+ private final SynchronizableGmfDiagramEditor editor;
+
+ public DiagramReloadContextProvider(SynchronizableGmfDiagramEditor editor) {
+ this.editor = editor;
+ }
+
+ @Override
+ public Object createReloadContext() {
+ return new DiagramSelectionContext(editor);
+ }
+
+ @Override
+ public void restore(Object reloadContext) {
+ ((DiagramSelectionContext)reloadContext).restore(editor);
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class DiagramSelectionContext extends EMFSelectionContext implements IDisposableReloadContext {
+
+ private SynchronizableGmfDiagramEditor editor;
+
+ private Object embeddedPaletteContext;
+
+ private Object palettePageContext;
+
+ DiagramSelectionContext(SynchronizableGmfDiagramEditor editor) {
+ super(editor.getDiagramGraphicalViewer());
+
+ PaletteViewer embedded = getEmbeddedPalette(editor);
+ if(embedded != null) {
+ embeddedPaletteContext = PaletteViewerReloadContextProvider.getInstance(embedded).createReloadContext();
+ }
+
+ PalettePage page = Iterables.getFirst(editor.getPalettePages(), null);
+ if(page != null) {
+ // Get one context for all the pages (there should be only one Palette view!)
+ IReloadContextProvider pageProvider = AdapterUtils.adapt(page, IReloadContextProvider.class, null);
+ if(pageProvider != null) {
+ palettePageContext = pageProvider.createReloadContext();
+ }
+ }
+ }
+
+ @Override
+ public void dispose() {
+ editor = null;
+ embeddedPaletteContext = null;
+ palettePageContext = null;
+ }
+
+ void restore(SynchronizableGmfDiagramEditor editor) {
+ this.editor = editor;
+
+ restore(editor.getDiagramGraphicalViewer());
+
+ if(embeddedPaletteContext != null) {
+ PaletteViewer palette = getEmbeddedPalette(editor);
+ if(palette != null) {
+ PaletteViewerReloadContextProvider.getInstance(palette).restore(embeddedPaletteContext);
+ }
+ }
+
+ if(palettePageContext != null) {
+ Collection<? extends PalettePage> pages = editor.getPalettePages();
+ if(pages.isEmpty()) {
+ // Defer until the page is created (which we assume it eventually will be, since evidently it
+ // was there when we captured the reload context)
+ editor.setDeferredPalettePageReloadContext(palettePageContext);
+ } else {
+ for(PalettePage page : pages) {
+ IReloadContextProvider pageProvider = AdapterUtils.adapt(page, IReloadContextProvider.class, null);
+ if(pageProvider != null) {
+ pageProvider.restore(palettePageContext);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Object deresolveSelectableElement(Object selectableElement) {
+ Object result = null;
+
+ if(selectableElement instanceof EditPart) {
+ Object model = ((EditPart)selectableElement).getModel();
+ if(model instanceof EObject) {
+ result = model;
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ protected Object resolveSelectableElement(Object deresolved) {
+ return editor.getDiagramGraphicalViewer().getEditPartRegistry().get(deresolved);
+ }
+
+ PaletteViewer getEmbeddedPalette(SynchronizableGmfDiagramEditor editor) {
+ PaletteViewer result = null;
+
+ if(editor.getDiagramEditDomain() instanceof EditDomain) {
+ result = ((EditDomain)editor.getDiagramEditDomain()).getPaletteViewer();
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/PaletteViewerReloadContextProvider.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/PaletteViewerReloadContextProvider.java
new file mode 100644
index 00000000000..630f4994d46
--- /dev/null
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/PaletteViewerReloadContextProvider.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.gmfdiag.common;
+
+import java.util.List;
+
+import org.eclipse.gef.palette.PaletteContainer;
+import org.eclipse.gef.palette.PaletteEntry;
+import org.eclipse.gef.palette.PaletteStack;
+import org.eclipse.gef.ui.palette.PaletteViewer;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadEvent;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+import org.eclipse.papyrus.infra.tools.util.UIUtil;
+import org.eclipse.ui.IMemento;
+
+
+/**
+ * An {@linkplain EditorReloadEvent editor reload} context provider for palette viewers, providing
+ * a reload context that captures and restores the expansion states of drawers and selection states
+ * of stacks and tools.
+ */
+class PaletteViewerReloadContextProvider implements IReloadContextProvider {
+
+ private static final String MEMENTO_ACTIVE_ENTRY = "activeEntry"; //$NON-NLS-1$
+
+ private PaletteViewer palette;
+
+ public PaletteViewerReloadContextProvider(PaletteViewer palette) {
+ super();
+
+ this.palette = palette;
+ }
+
+ public static IReloadContextProvider getInstance(PaletteViewer palette) {
+ IReloadContextProvider result = AdapterUtils.adapt(palette, IReloadContextProvider.class, null);
+
+ if(result == null) {
+ result = new PaletteViewerReloadContextProvider(palette);
+ }
+
+ return result;
+ }
+
+ @Override
+ public Object createReloadContext() {
+ IMemento result = UIUtil.createLocalMemento();
+ palette.saveState(result);
+ saveMoreState(palette.getPaletteRoot(), result);
+ return result;
+ }
+
+ @Override
+ public void restore(Object reloadContext) {
+ IMemento memento = (IMemento)reloadContext;
+ palette.restoreState(memento);
+ restoreMoreState(palette.getPaletteRoot(), memento);
+ }
+
+ /**
+ * The palette's own memento doesn't record which tool of a stack is the stack's active tool.
+ */
+ void saveMoreState(PaletteEntry entry, IMemento memento) {
+ if(entry instanceof PaletteStack) {
+ PaletteStack stack = (PaletteStack)entry;
+ memento.putInteger(MEMENTO_ACTIVE_ENTRY, stack.getChildren().indexOf(stack.getActiveEntry()));
+ }
+
+ if(entry instanceof PaletteContainer) {
+ PaletteContainer container = (PaletteContainer)entry;
+ List<?> children = container.getChildren();
+ IMemento[] mementos = memento.getChildren();
+
+ int max = Math.min(children.size(), mementos.length);
+ for(int i = 0; i < max; i++) {
+ saveMoreState((PaletteEntry)children.get(i), mementos[i]);
+ }
+ }
+ }
+
+ /**
+ * @see #saveMoreState(PaletteEntry, IMemento)
+ */
+ void restoreMoreState(PaletteEntry entry, IMemento memento) {
+ if(entry instanceof PaletteStack) {
+ PaletteStack stack = (PaletteStack)entry;
+ List<?> children = stack.getChildren();
+
+ int activeIndex = memento.getInteger(MEMENTO_ACTIVE_ENTRY);
+ if((activeIndex >= 0) && (activeIndex < children.size())) {
+ stack.setActiveEntry((PaletteEntry)children.get(activeIndex));
+ }
+ }
+
+ if(entry instanceof PaletteContainer) {
+ PaletteContainer container = (PaletteContainer)entry;
+ List<?> children = container.getChildren();
+ IMemento[] mementos = memento.getChildren();
+
+ int max = Math.min(children.size(), mementos.length);
+ for(int i = 0; i < max; i++) {
+ restoreMoreState((PaletteEntry)children.get(i), mementos[i]);
+ }
+ }
+ }
+
+}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/SynchronizableGmfDiagramEditor.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/SynchronizableGmfDiagramEditor.java
index f1a007ac59d..42aa9c6f1bb 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/SynchronizableGmfDiagramEditor.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/SynchronizableGmfDiagramEditor.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,6 +9,7 @@
*
* Contributors:
* Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.common;
@@ -21,11 +22,14 @@ import java.util.Map;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.emf.transaction.RollbackException;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.commands.CommandStack;
+import org.eclipse.gef.ui.palette.PaletteViewer;
+import org.eclipse.gef.ui.views.palette.PalettePage;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
@@ -41,6 +45,7 @@ import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.papyrus.commands.CheckedDiagramCommandStack;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
import org.eclipse.papyrus.infra.gmfdiag.common.preferences.PreferencesConstantsHelper;
import org.eclipse.papyrus.infra.gmfdiag.common.reconciler.DiagramReconciler;
import org.eclipse.papyrus.infra.gmfdiag.common.reconciler.DiagramReconcilersReader;
@@ -50,9 +55,18 @@ import org.eclipse.papyrus.infra.gmfdiag.common.utils.GMFUnsafe;
import org.eclipse.papyrus.infra.tools.util.EclipseCommandUtils;
import org.eclipse.papyrus.infra.widgets.util.IRevealSemanticElement;
import org.eclipse.papyrus.infra.widgets.util.NavigationTarget;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IActionBars;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.part.IPageSite;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
/**
*
@@ -66,6 +80,10 @@ import org.eclipse.ui.commands.ICommandService;
//suppress the warning for WorkspaceViewerProperties
public class SynchronizableGmfDiagramEditor extends DiagramDocumentEditor implements IRevealSemanticElement, NavigationTarget {
+ private Collection<PalettePageWrapper> palettePages;
+
+ private Object palettePageState;
+
public SynchronizableGmfDiagramEditor(boolean hasFlyoutPalette) {
super(hasFlyoutPalette);
}
@@ -151,9 +169,48 @@ public class SynchronizableGmfDiagramEditor extends DiagramDocumentEditor implem
if(type == Diagram.class) {
return getDiagram();
}
+ if(type == IReloadContextProvider.class) {
+ return new DiagramReloadContextProvider(this);
+ }
+ if(type == PalettePage.class) {
+ if(palettePages == null) {
+ palettePages = Lists.newArrayListWithExpectedSize(1);
+ } else {
+ cleanUpPalettePages();
+ if(!palettePages.isEmpty()) {
+ // Make the new page look just like the last one (for continuity of the UI when the
+ // PapyrusPaletteSynchronizer causes a new page to be created)
+ Iterables.getLast(palettePages, null).saveState();
+ }
+ }
+ PalettePageWrapper result = new PalettePageWrapper((CustomPalettePage)super.getAdapter(type));
+ palettePages.add(result);
+ return result;
+ }
return super.getAdapter(type);
}
+ Collection<? extends PalettePage> getPalettePages() {
+ if(palettePages == null) {
+ return Collections.emptyList();
+ } else {
+ cleanUpPalettePages();
+ return Collections.unmodifiableCollection(palettePages);
+ }
+ }
+
+ void setDeferredPalettePageReloadContext(Object reloadContext) {
+ palettePageState = reloadContext;
+ }
+
+ private void cleanUpPalettePages() {
+ for(Iterator<PalettePageWrapper> iter = palettePages.iterator(); iter.hasNext();) {
+ if(iter.next().isDisposed()) {
+ iter.remove();
+ }
+ }
+ }
+
/**
* Configures my diagram edit domain with its command stack.
* This method has been completely overridden in order to use a proxy stack.
@@ -357,6 +414,109 @@ public class SynchronizableGmfDiagramEditor extends DiagramDocumentEditor implem
}
}
+ protected class PalettePageWrapper implements PalettePage, IAdaptable {
+
+ private final CustomPalettePage delegate;
+
+ private boolean disposed;
+
+ protected PalettePageWrapper(CustomPalettePage delegate) {
+ this.delegate = delegate;
+ }
+
+ public void createControl(Composite parent) {
+ Control existing = getControl();
+ if((existing != null) && !existing.isDisposed()) {
+ // Attempting to creating the page controls again? Bail
+ return;
+ }
+
+ delegate.createControl(parent);
+
+ delegate.getControl().addDisposeListener(new DisposeListener() {
+
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ disposed = true;
+
+ SynchronizableGmfDiagramEditor.this.palettePages.remove(PalettePageWrapper.this);
+ }
+ });
+
+ if(palettePageState != null) {
+ // We're re-creating the palette page after having closed it, either for editor re-load
+ // or the PapyrusPaletteSynchronizer forcing a palette refresh. Reinitialize from the
+ // last saved state
+ PaletteViewerReloadContextProvider.getInstance(getPaletteViewer()).restore(palettePageState);
+ palettePageState = null;
+ }
+ }
+
+ public void dispose() {
+ // Save current state for potential re-opening later
+ saveState();
+ delegate.dispose();
+ }
+
+ public boolean isDisposed() {
+ return disposed;
+ }
+
+ void saveState() {
+ PaletteViewer palette = getPaletteViewer();
+ if(palette != null) {
+ palettePageState = PaletteViewerReloadContextProvider.getInstance(palette).createReloadContext();
+ }
+ }
+
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ if(adapter == IReloadContextProvider.class) {
+ return new IReloadContextProvider() {
+
+ @Override
+ public Object createReloadContext() {
+ return (getPaletteViewer() != null) ? PaletteViewerReloadContextProvider.getInstance(getPaletteViewer()).createReloadContext() : null;
+ }
+
+ @Override
+ public void restore(Object reloadContext) {
+ if(getPaletteViewer() != null) {
+ PaletteViewerReloadContextProvider.getInstance(getPaletteViewer()).restore(reloadContext);
+ } else {
+ // We'll defer this until the page control is created
+ palettePageState = reloadContext;
+ }
+ }
+ };
+ }
+ return delegate.getAdapter(adapter);
+ }
+
+ public Control getControl() {
+ // CustomPalettePage will NPE if asked for the control before the PaletteViewer is created
+ return (delegate.getPaletteViewer() == null) ? null : delegate.getControl();
+ }
+
+ public void setFocus() {
+ delegate.setFocus();
+ }
+
+ public void setActionBars(IActionBars actionBars) {
+ delegate.setActionBars(actionBars);
+ }
+ public void init(IPageSite pageSite) {
+ delegate.init(pageSite);
+ }
+
+ public IPageSite getSite() {
+ return delegate.getSite();
+ }
+
+ public PaletteViewer getPaletteViewer() {
+ return delegate.getPaletteViewer();
+ }
+
+ }
}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineAdapterFactory.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineAdapterFactory.java
new file mode 100644
index 00000000000..acf4fde39ae
--- /dev/null
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineAdapterFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.gmfdiag.common.adapter;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
+
+/**
+ * An adapter factory for the outline page contributed by nested diagram editors.
+ */
+public class DiagramOutlineAdapterFactory implements IAdapterFactory {
+
+ private static final Class<?>[] ADAPTERS = { IReloadContextProvider.class };
+
+ @SuppressWarnings("rawtypes")
+ public Class[] getAdapterList() {
+ return ADAPTERS;
+ }
+
+ public Object getAdapter(Object adaptableObject, @SuppressWarnings("rawtypes") Class adapterType) {
+ if(adapterType == IReloadContextProvider.class) {
+ if(DiagramOutlineReloadContextProvider.isDiagramOutline(adaptableObject)) {
+ return new DiagramOutlineReloadContextProvider(adaptableObject);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineReloadContextProvider.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineReloadContextProvider.java
new file mode 100644
index 00000000000..2e9d4f381c6
--- /dev/null
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/adapter/DiagramOutlineReloadContextProvider.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.gmfdiag.common.adapter;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
+import org.eclipse.papyrus.infra.gmfdiag.common.Activator;
+
+
+/**
+ * A {@linkplain IReloadContextProvider reload context provider} for nested diagram outline pages that records and restores the selection of
+ * which presentation mode (tree outline or thumbnail overview) is active for each outline page. This implementation uses reflection to access
+ * internals (non API) of the outline page. On any problem in initializing this reflective access, the adapter basically disables itself and
+ * never participates in editor re-load, so that the system continues normally but with reduced functionality.
+ */
+class DiagramOutlineReloadContextProvider implements IReloadContextProvider {
+
+ private static final int ID_OUTLINE = 0;
+
+ private static final int ID_OVERVIEW = 1;
+
+ private static final Class<?> diagramOutlinePage;
+
+ private static final Method showPage;
+
+ private static final Field showOutlineAction;
+
+ static {
+ Class<?> diagramOutlinePageClass = null;
+ Method showPageMethod = null;
+ Field showOutlineActionField = null;
+
+ for(Class<?> next : DiagramEditor.class.getDeclaredClasses()) {
+ if("DiagramOutlinePage".equals(next.getSimpleName())) { //$NON-NLS-1$
+ diagramOutlinePageClass = next;
+
+ try {
+ showPageMethod = diagramOutlinePageClass.getDeclaredMethod("showPage", int.class); //$NON-NLS-1$
+ showPageMethod.setAccessible(true);
+
+ showOutlineActionField = diagramOutlinePageClass.getDeclaredField("showOutlineAction"); //$NON-NLS-1$
+ showOutlineActionField.setAccessible(true);
+ } catch (Exception e) {
+ // Can't reflect? Then abandon all hope
+ Activator.log.error(e);
+ diagramOutlinePageClass = null;
+ }
+
+ break;
+ }
+ }
+
+ diagramOutlinePage = diagramOutlinePageClass;
+ showPage = showPageMethod;
+ showOutlineAction = showOutlineActionField;
+ }
+
+ private final Object diagramOutline;
+
+ public DiagramOutlineReloadContextProvider(Object diagramOutline) {
+ super();
+
+ this.diagramOutline = diagramOutline;
+ }
+
+ @Override
+ public Object createReloadContext() {
+ return new ReloadContext(diagramOutline);
+ }
+
+ @Override
+ public void restore(Object reloadContext) {
+ ((ReloadContext)reloadContext).restore(diagramOutline);
+ }
+
+ static boolean isDiagramOutline(Object o) {
+ // If we couldn't reflect on DiagramOutline class, then we will never detect a diagram outline
+ // instance, so we will never try to create an adapter, and all is safe. The only consequence
+ // will be that we don't get diagram outline state properly restored. That's fine
+ return (diagramOutlinePage != null) && diagramOutlinePage.isInstance(o);
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class ReloadContext {
+
+ private final int pageID;
+
+ public ReloadContext(Object diagramOutline) {
+ super();
+
+ try {
+ this.pageID = ((IAction)showOutlineAction.get(diagramOutline)).isChecked() ? ID_OUTLINE : ID_OVERVIEW;
+ } catch (IllegalAccessException e) {
+ // We wouldn't be here if we couldn't make it accessible. Something is very wrong
+ throw new Error(e);
+ }
+ }
+
+ void restore(Object diagramOutline) {
+ try {
+ showPage.invoke(diagramOutline, pageID);
+ } catch (IllegalAccessException e) {
+ // We wouldn't be here if we couldn't make it accessible. Something is very wrong
+ throw new Error(e);
+ } catch (InvocationTargetException e) {
+ Throwable target = e.getTargetException();
+ if(target instanceof RuntimeException) {
+ throw (RuntimeException)target;
+ } else if(target instanceof Error) {
+ throw (Error)target;
+ } else {
+ Activator.log.error(target);
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.outline/src/org/eclipse/papyrus/infra/gmfdiag/outline/DiagramOutline.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.outline/src/org/eclipse/papyrus/infra/gmfdiag/outline/DiagramOutline.java
index a9b1798347c..dfe7cebb9b1 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.outline/src/org/eclipse/papyrus/infra/gmfdiag/outline/DiagramOutline.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.outline/src/org/eclipse/papyrus/infra/gmfdiag/outline/DiagramOutline.java
@@ -1,5 +1,5 @@
/***********************************************************************
- * Copyright (c) 2008, 2009 Anyware Technologies, Obeo, CEA LIST
+ * Copyright (c) 2008, 2014 Anyware Technologies, Obeo, CEA LIST, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -10,10 +10,13 @@
* Anyware Technologies - initial API and implementation
* Obeo
* CEA LIST - synchronization between selection and outline content
+ * Christian W. Damus (CEA) - bug 437217
*
**********************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.outline;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.GraphicalViewer;
@@ -31,6 +34,7 @@ import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.papyrus.infra.core.contentoutline.IPapyrusContentOutlinePage;
import org.eclipse.papyrus.infra.core.editor.BackboneException;
import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.gmfdiag.outline.internal.Activator;
import org.eclipse.papyrus.infra.gmfdiag.outline.internal.Messages;
@@ -56,7 +60,7 @@ import org.eclipse.ui.plugin.AbstractUIPlugin;
*/
//FIXME: The outline is broken in Eclipse 4.2. #createControl(Composite) is never called.
//See #refresh()
-public class DiagramOutline extends Page implements IPapyrusContentOutlinePage, ISelectionListener {
+public class DiagramOutline extends Page implements IPapyrusContentOutlinePage, ISelectionListener, IAdaptable {
private final class ShowAllAction extends Action {
@@ -451,4 +455,52 @@ public class DiagramOutline extends Page implements IPapyrusContentOutlinePage,
return showActionMode;
}
+
+ private void setShowActionMode(int showAction) {
+ switch(showAction) {
+ case SHOW_TREE:
+ showTreeItem.getAction().setChecked(true);
+ break;
+ case SHOW_OVERVIEW:
+ showOverviewItem.getAction().setChecked(true);
+ break;
+ case SHOW_BOTH:
+ showAllItem.getAction().setChecked(true);
+ break;
+ default:
+ throw new IllegalArgumentException("showAction"); //$NON-NLS-1$
+ }
+
+ performShowAction();
+ }
+
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ if(adapter == IReloadContextProvider.class) {
+ return new IReloadContextProvider() {
+
+ public Object createReloadContext() {
+ return new ReloadContext(DiagramOutline.this);
+ }
+
+ public void restore(Object reloadContext) {
+ ((ReloadContext)reloadContext).restore(DiagramOutline.this);
+ }
+ };
+ }
+
+ return Platform.getAdapterManager().getAdapter(this, adapter);
+ }
+
+ private static class ReloadContext {
+
+ private final int showAction;
+
+ ReloadContext(DiagramOutline outline) {
+ this.showAction = outline.getShowActionMode();
+ }
+
+ void restore(DiagramOutline outline) {
+ outline.setShowActionMode(showAction);
+ }
+ }
}
diff --git a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/META-INF/MANIFEST.MF b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/META-INF/MANIFEST.MF
index f087767b49f..c47c1807a79 100644
--- a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/META-INF/MANIFEST.MF
+++ b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/META-INF/MANIFEST.MF
@@ -22,7 +22,8 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.e4.core.contexts,
org.eclipse.core.expressions,
org.eclipse.emf.edit.ui,
- org.eclipse.papyrus.infra.viewpoints.policy;bundle-version="1.0.0"
+ org.eclipse.papyrus.infra.viewpoints.policy;bundle-version="1.0.0",
+ com.google.guava;bundle-version="11.0.0"
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-Version: 1.0.0.qualifier
diff --git a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/AbstractEMFNattableEditor.java b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/AbstractEMFNattableEditor.java
index 21a651b8585..f3d56f24321 100644
--- a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/AbstractEMFNattableEditor.java
+++ b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/AbstractEMFNattableEditor.java
@@ -11,6 +11,7 @@
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
* Vincent Lorenzo (CEA-LIST) vincent.lorenzo@cea.fr
* Christian W. Damus (CEA) - bug 430880
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.infra.nattable.common.editor;
@@ -30,6 +31,7 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
@@ -228,6 +230,10 @@ public abstract class AbstractEMFNattableEditor extends EditorPart {
return this.tableManager.getTable();
}
}
+
+ if(adapter == IReloadContextProvider.class) {
+ return new NattableReloadContextProvider(this);
+ }
return super.getAdapter(adapter);
}
diff --git a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/NattableReloadContextProvider.java b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/NattableReloadContextProvider.java
new file mode 100644
index 00000000000..a3123d9add2
--- /dev/null
+++ b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.common/src/org/eclipse/papyrus/infra/nattable/common/editor/NattableReloadContextProvider.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.nattable.common.editor;
+
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.selection.command.SelectColumnCommand;
+import org.eclipse.nebula.widgets.nattable.selection.command.SelectRowsCommand;
+import org.eclipse.papyrus.infra.core.editor.reload.EMFSelectionContext;
+import org.eclipse.papyrus.infra.core.editor.reload.IReloadContextProvider;
+import org.eclipse.papyrus.infra.core.utils.AdapterUtils;
+import org.eclipse.papyrus.infra.nattable.manager.table.INattableModelManager;
+
+
+/**
+ * A {@linkplain IReloadContextProvider reload context provider} for {@link NatTable} selection state.
+ */
+class NattableReloadContextProvider implements IReloadContextProvider {
+
+ private final AbstractEMFNattableEditor editor;
+
+ NattableReloadContextProvider(AbstractEMFNattableEditor editor) {
+ super();
+
+ this.editor = editor;
+ }
+
+ @Override
+ public Object createReloadContext() {
+ return new NatTableSelectionContext(getSelectionProvider(), getSelectionLayer());
+ }
+
+ @Override
+ public void restore(Object reloadContext) {
+ ((NatTableSelectionContext)reloadContext).restore(getSelectionProvider(), getSelectionLayer());
+ }
+
+ private ISelectionProvider getSelectionProvider() {
+ // The table editor registers its table-selection provider in its site
+ return editor.getSite().getSelectionProvider();
+ }
+
+ private SelectionLayer getSelectionLayer() {
+ INattableModelManager mgr = AdapterUtils.adapt(editor, INattableModelManager.class, null);
+ return mgr.getBodyLayerStack().getSelectionLayer();
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class NatTableSelectionContext extends EMFSelectionContext {
+
+ private final int[] selectedRows;
+
+ private final int[] selectedColumns;
+
+ NatTableSelectionContext(ISelectionProvider structuredSelectionProvider, SelectionLayer selectionLayer) {
+ super(structuredSelectionProvider);
+
+ selectedRows = selectionLayer.getFullySelectedRowPositions();
+ selectedColumns = selectionLayer.getFullySelectedColumnPositions();
+ }
+
+ void restore(ISelectionProvider structuredSelectionProvider, SelectionLayer selectionLayer) {
+ selectionLayer.clear();
+ if(selectedColumns.length > 0) {
+ for(int i = 0; i < selectedColumns.length; i++) {
+ selectionLayer.doCommand(new SelectColumnCommand(selectionLayer, selectedColumns[i], Integer.MAX_VALUE, false, true));
+ }
+ }
+ if(selectedRows.length > 0) {
+ selectionLayer.doCommand(new SelectRowsCommand(selectionLayer, Integer.MAX_VALUE, selectedRows, false, true, selectedRows[selectedRows.length - 1]));
+ }
+
+ restore(structuredSelectionProvider);
+ }
+ }
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/LocalMemento.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/LocalMemento.java
new file mode 100644
index 00000000000..c456b15a49f
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/LocalMemento.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.ui.IMemento;
+
+
+/**
+ * Invocation handler for the dynamic local memento implementation.
+ */
+class LocalMemento implements InvocationHandler {
+
+ private static final Class<?>[] INTERFACES = { IMemento.class };
+
+ private static final Map<Method, Method> delegates = createDelegates();
+
+ private final String type;
+
+ private final String id;
+
+ private final List<IMemento> children = new ArrayList<IMemento>();
+
+ private final Map<String, Object> attributes = new HashMap<String, Object>();
+
+ private String textData;
+
+ LocalMemento(String type, String id) {
+ super();
+
+ this.type = type;
+ this.id = id;
+ }
+
+ static IMemento createMemento(String type, String id) {
+ LocalMemento handler = new LocalMemento(type, id);
+ return (IMemento)Proxy.newProxyInstance(LocalMemento.class.getClassLoader(), INTERFACES, handler);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ Object result = null;
+
+ Method implementation = delegates.get(method);
+ if(implementation == null) {
+ throw new UnsupportedOperationException("dynamic proxy handler does not understand " + method.getName()); //$NON-NLS-1$
+ } else {
+ result = implementation.invoke(this, args);
+ }
+
+ return result;
+ }
+
+ @API
+ String getType() {
+ return type;
+ }
+
+ @API
+ String getID() {
+ return id;
+ }
+
+ @API
+ IMemento createChild(String type) {
+ return createChild(type, null);
+ }
+
+ @API
+ IMemento createChild(String type, String id) {
+ IMemento result = createMemento(type, id);
+ children.add(result);
+ return result;
+ }
+
+ @API
+ IMemento getChild(String type) {
+ IMemento result = null;
+ for(IMemento next : children) {
+ if(type.equals(next.getType())) {
+ result = next;
+ break;
+ }
+ }
+ return result;
+ }
+
+ @API
+ IMemento[] getChildren() {
+ return children.toArray(new IMemento[children.size()]);
+ }
+
+ @API
+ IMemento[] getChildren(String type) {
+ List<IMemento> result = new ArrayList<IMemento>(children.size());
+ for(IMemento next : children) {
+ if(type.equals(next.getType())) {
+ result.add(next);
+ }
+ }
+ return result.toArray(new IMemento[result.size()]);
+ }
+
+ @API
+ Float getFloat(String key) {
+ return coerce(attributes.get(key), Float.class);
+ }
+
+ private <T> T coerce(Object value, Class<T> type) {
+ Object result;
+
+ if(value == null) {
+ result = value;
+ } else if(type.isInstance(value)) {
+ result = value;
+ } else if(Number.class.isAssignableFrom(type) && (value instanceof Number)) {
+ Number number = (Number)value;
+ if(type == Integer.class) {
+ result = number.intValue();
+ } else if(type == Float.class) {
+ result = number.floatValue();
+ } else {
+ throw new IllegalArgumentException("unsupported numeric type: " + type.getSimpleName()); //$NON-NLS-1$
+ }
+ } else if(Number.class.isAssignableFrom(type) && (value instanceof String)) {
+ String string = (String)value;
+ if(type == Integer.class) {
+ result = Integer.valueOf(string);
+ } else if(type == Float.class) {
+ result = Float.valueOf(string);
+ } else {
+ throw new IllegalArgumentException("unsupported numeric type: " + type.getSimpleName()); //$NON-NLS-1$
+ }
+ } else if(type == Boolean.class) {
+ // We know the value isn't a Boolean, otherwise we would have handled it already
+ if(value instanceof String) {
+ result = Boolean.valueOf((String)value);
+ } else {
+ throw new IllegalArgumentException("unsupported boolean conversion from type: " + ((value == null) ? "null" : value.getClass().getSimpleName())); //$NON-NLS-1$
+ }
+ } else if(type == String.class) {
+ result = String.valueOf(value);
+ } else {
+ throw new IllegalArgumentException("unsupported attribute type: " + type.getSimpleName()); //$NON-NLS-1$
+ }
+
+ return type.cast(result);
+ }
+
+ @API
+ Integer getInteger(String key) {
+ return coerce(attributes.get(key), Integer.class);
+ }
+
+ @API
+ String getString(String key) {
+ return coerce(attributes.get(key), String.class);
+ }
+
+ @API
+ Boolean getBoolean(String key) {
+ return coerce(attributes.get(key), Boolean.class);
+ }
+
+ @API
+ String getTextData() {
+ return textData;
+ }
+
+ @API
+ String[] getAttributeKeys() {
+ return attributes.keySet().toArray(new String[attributes.size()]);
+ }
+
+ @API
+ void putFloat(String key, float value) {
+ attributes.put(key, value);
+ }
+
+ @API
+ void putInteger(String key, int value) {
+ attributes.put(key, value);
+ }
+
+ @API
+ void putString(String key, String value) {
+ attributes.put(key, value);
+ }
+
+ @API
+ void putBoolean(String key, boolean value) {
+ attributes.put(key, value);
+ }
+
+ @API
+ void putTextData(String data) {
+ textData = data;
+ }
+
+ private boolean isLocalMemento(IMemento memento) {
+ return (memento != null) && Proxy.isProxyClass(memento.getClass()) && ((Proxy.getInvocationHandler(memento) instanceof LocalMemento));
+ }
+
+ @API
+ void putMemento(IMemento memento) {
+ if(!isLocalMemento(memento)) {
+ throw new IllegalArgumentException("memento is not a local memento"); //$NON-NLS-1$
+ }
+ children.add(memento);
+ }
+
+ @API(owner = Object.class)
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+
+ append(result, 0);
+
+ return result.toString();
+ }
+
+ private void append(StringBuilder buf, int depth) {
+ // Indent
+ for(int i = 0; i < depth; i++) {
+ buf.append(" "); //$NON-NLS-1$
+ }
+
+ buf.append("LocalMemento(");//$NON-NLS-1$
+ buf.append(type);
+ if(id != null) {
+ buf.append('[').append(id).append(']');
+ }
+ buf.append(") ").append(attributes); //$NON-NLS-1$
+ buf.append('\n');
+
+ final int nextDepth = depth + 1;
+ for(IMemento next : children) {
+ ((LocalMemento)Proxy.getInvocationHandler(next)).append(buf, nextDepth);
+ }
+ }
+
+ private static Map<Method, Method> createDelegates() {
+ Map<Method, Method> result = new HashMap<Method, Method>();
+
+ for(Method implementation : LocalMemento.class.getDeclaredMethods()) {
+ if(implementation.isAnnotationPresent(API.class)) {
+ try {
+ Method api = implementation.getAnnotation(API.class).owner().getMethod(implementation.getName(), implementation.getParameterTypes());
+ result.put(api, implementation);
+ } catch (NoSuchMethodException e) {
+ throw new LinkageError("Incompatible IMemento API change: " + implementation.getName());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface API {
+
+ Class<?> owner() default IMemento.class;
+ }
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/UIUtil.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/UIUtil.java
index 051bb4dba11..10d4ea7054f 100644
--- a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/UIUtil.java
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/UIUtil.java
@@ -27,6 +27,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IMemento;
/**
@@ -54,6 +55,16 @@ public class UIUtil {
return new UIExecutorService(display);
}
+ /**
+ * Creates a local memento that is not persistable and is not based on an XML document. This is useful for capturing the
+ * state of UI elements locally in cases where persistence of the memento is not required.
+ *
+ * @return the memento
+ */
+ public static IMemento createLocalMemento() {
+ return LocalMemento.createMemento("__anonymous__", null); //$NON-NLS-1$
+ }
+
//
// Nested types
//
diff --git a/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerTreeViewerContext.java b/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerTreeViewerContext.java
new file mode 100644
index 00000000000..e81398f494a
--- /dev/null
+++ b/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerTreeViewerContext.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2010, 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus (CEA) - adapted from ModelExplorerView::reveal(...) API
+ *
+ */
+package org.eclipse.papyrus.views.modelexplorer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.papyrus.infra.core.editor.reload.EMFTreeViewerContext;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.resource.additional.AdditionalResourcesModel;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.views.modelexplorer.matching.IMatchingItem;
+import org.eclipse.papyrus.views.modelexplorer.matching.LinkItemMatchingItem;
+import org.eclipse.papyrus.views.modelexplorer.matching.ModelElementItemMatchingItem;
+import org.eclipse.papyrus.views.modelexplorer.matching.ReferencableMatchingItem;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+
+/**
+ * A specialization of the editor re-load tree viewer context that knows how to expand and select nodes
+ * in the EMF Facet-based Model Explorer view.
+ */
+class ModelExplorerTreeViewerContext extends EMFTreeViewerContext {
+
+ public ModelExplorerTreeViewerContext(AbstractTreeViewer viewer) {
+ super(viewer);
+ }
+
+ public Object deresolveSelectableElement(Object selectableElement) {
+ return EMFHelper.getEObject(selectableElement);
+ }
+
+ public Object resolveSelectableElement(Object object) {
+ return new ModelElementItemMatchingItemWithElement((EObject)object);
+ }
+
+ @Override
+ protected void setExpandedElements(AbstractTreeViewer viewer, Collection<?> toExpand) {
+ // EMF Facet makes expanding tree elements very complicated
+ if(viewer.getContentProvider() != null) {
+ for(ModelElementItemMatchingItemWithElement next : Iterables.filter(toExpand, ModelElementItemMatchingItemWithElement.class)) {
+
+ // retrieve the ancestors to reveal them
+ // and allow the selection of the object
+ EObject currentEObject = next.element();
+ ArrayList<EObject> parents = new ArrayList<EObject>();
+ EObject tmp = currentEObject.eContainer();
+ while(tmp != null) {
+ parents.add(tmp);
+ tmp = tmp.eContainer();
+ }
+
+ Iterable<EObject> reverseParents = Lists.reverse(parents);
+
+ // reveal the resource if necessary
+ Resource r = null;
+ if(!parents.isEmpty()) {
+ r = parents.get(parents.size() - 1).eResource();
+ } else {
+ r = currentEObject.eResource();
+ }
+
+ if(r != null) {
+ final ResourceSet rs = r.getResourceSet();
+ final Resource resource = r;
+ if(rs instanceof ModelSet && AdditionalResourcesModel.isAdditionalResource((ModelSet)rs, r.getURI())) {
+ viewer.expandToLevel(new ReferencableMatchingItem(rs), 1);
+ viewer.expandToLevel(new ReferencableMatchingItem(resource), 1);
+ }
+ }
+
+ /*
+ * reveal the ancestors tree using expandToLevel on each of them
+ * in the good order. This is a lot faster than going through the whole tree
+ * using getChildren of the ContentProvider since our Viewer uses a Hashtable
+ * to keep track of the revealed elements.
+ *
+ * However we need to use a dedicated MatchingItem to do the matching,
+ * and a specific comparer in our viewer so than the equals of MatchingItem is
+ * used in priority.
+ *
+ * Please refer to MatchingItem for more infos.
+ */
+ EObject previousParent = null;
+ for(EObject parent : reverseParents) {
+ if(parent.eContainingFeature() != null && previousParent != null) {
+ viewer.expandToLevel(new LinkItemMatchingItem(previousParent, parent.eContainmentFeature()), 1);
+ }
+
+ final IMatchingItem itemToExpand = new ModelElementItemMatchingItem(parent);
+ viewer.expandToLevel(itemToExpand, 1);
+
+ previousParent = parent;
+ }
+
+ // expand a reference-link item, if necessary
+ final IMatchingItem linkItem = new LinkItemMatchingItem(currentEObject.eContainer(), currentEObject.eContainmentFeature());
+ viewer.expandToLevel(linkItem, 1);
+
+ // and the actual element
+ viewer.expandToLevel(next, 1);
+ }
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ static class ModelElementItemMatchingItemWithElement extends ModelElementItemMatchingItem {
+
+ private EObject element;
+
+ ModelElementItemMatchingItemWithElement(EObject element) {
+ super(element);
+
+ this.element = element;
+ }
+
+ EObject element() {
+ return element;
+ }
+ }
+}
diff --git a/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerView.java b/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerView.java
index 7f2a6262cf1..b7f71045b88 100644
--- a/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerView.java
+++ b/plugins/views/modelexplorer/org.eclipse.papyrus.views.modelexplorer/src/org/eclipse/papyrus/views/modelexplorer/ModelExplorerView.java
@@ -12,6 +12,7 @@
* Christian W. Damus (CEA) - post refreshes for transaction commit asynchronously (CDO)
* Christian W. Damus (CEA) - bug 429826
* Christian W. Damus (CEA) - bug 434635
+ * Christian W. Damus (CEA) - bug 437217
*
*****************************************************************************/
package org.eclipse.papyrus.views.modelexplorer;
@@ -53,6 +54,10 @@ import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerColumn;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.editor.IReloadableEditor;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadAdapter;
+import org.eclipse.papyrus.infra.core.editor.reload.EditorReloadEvent;
+import org.eclipse.papyrus.infra.core.editor.reload.TreeViewerContext;
import org.eclipse.papyrus.infra.core.lifecycleevents.IEditorInputChangedListener;
import org.eclipse.papyrus.infra.core.lifecycleevents.ISaveAndDirtyService;
import org.eclipse.papyrus.infra.core.resource.IReadOnlyHandler2;
@@ -127,7 +132,7 @@ import com.google.common.collect.Lists;
public class ModelExplorerView extends CommonNavigator implements IRevealSemanticElement, IEditingDomainProvider, IPageLifeCycleEventsListener {
private SharedModelExplorerState sharedState;
-
+
private SharedModelExplorerState.StateChangedListener sharedStateListener;
/**
@@ -138,16 +143,10 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
public static final String LABEL_PROVIDER_SERVICE_CONTEXT = "org.eclipse.papyrus.views.modelexplorer.labelProvider.context";
/**
- * The associated EditorPart
- * The View is associated to the ServicesRegistry rather than to an editor.
- * */
- // private IMultiDiagramEditor editorPart;
-
- /**
* The {@link ServicesRegistry} associated to the Editor. This view is associated to the
* ServicesRegistry rather than to the EditorPart.
*/
- private final ServicesRegistry serviceRegistry;
+ private ServicesRegistry serviceRegistry;
/** The save aservice associated to the editor. */
private ISaveAndDirtyService saveAndDirtyService;
@@ -217,10 +216,37 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
throw new IllegalArgumentException("A part should be provided.");
}
+ init(part);
+
+ IReloadableEditor.Adapter.getAdapter(part).addEditorReloadListener(new EditorReloadAdapter() {
+
+ @Override
+ public void editorAboutToReload(EditorReloadEvent event) {
+ // Stash expansion and selection state of the common viewer
+ event.putContext(new ModelExplorerTreeViewerContext(getCommonViewer()));
+
+ deactivate();
+ }
+
+ @Override
+ public void editorReloaded(EditorReloadEvent event) {
+ init(event.getEditor());
+
+ activate();
+
+ initCommonViewer(getCommonViewer());
+
+ // Restore expansion and selection state of the common viewer
+ ((TreeViewerContext<?>)event.getContext()).restore(getCommonViewer());
+ }
+ });
+ }
+
+ private void init(IMultiDiagramEditor editor) {
// Try to get the ServicesRegistry
- serviceRegistry = part.getServicesRegistry();
+ serviceRegistry = editor.getServicesRegistry();
if(serviceRegistry == null) {
- throw new IllegalArgumentException("The part should have a ServiceRegistry.");
+ throw new IllegalArgumentException("The editor should have a ServiceRegistry.");
}
// Get required services from ServicesRegistry
@@ -228,7 +254,7 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
saveAndDirtyService = serviceRegistry.getService(ISaveAndDirtyService.class);
undoContext = serviceRegistry.getService(IUndoContext.class);
} catch (ServiceException e) {
- e.printStackTrace();
+ Activator.log.error(e);
}
}
@@ -341,6 +367,41 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
@Override
protected CommonViewer createCommonViewerObject(Composite aParent) {
CommonViewer viewer = new CustomCommonViewer(getViewSite().getId(), aParent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+
+ initCommonViewer(viewer);
+
+ viewer.getNavigatorContentService().getActivationService().addExtensionActivationListener(new IExtensionActivationListener() {
+
+ public void onExtensionActivation(String aViewerId, String[] theNavigatorExtensionIds, boolean isActive) {
+ sharedState.updateNavigatorContentExtensions(theNavigatorExtensionIds, isActive);
+ }
+ });
+
+ ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
+
+ return viewer;
+ }
+
+ private void installEMFFacetTreePainter(Tree tree) {
+ // Install the EMFFacet Custom Tree Painter
+ //org.eclipse.papyrus.infra.emf.Activator.getDefault().getCustomizationManager().installCustomPainter(tree);
+
+ // The EMF Facet MeasureItem Listener is incompatible with the NavigatorDecoratingLabelProvider. Remove it.
+ // Symptoms: ModelElementItems with an EMF Facet Overlay have a small selection size
+ // Removal also fixes bug 400012: no scrollbar although tree is larger than visible area
+ Collection<Listener> listenersToRemove = new LinkedList<Listener>();
+ for(Listener listener : tree.getListeners(SWT.MeasureItem)) {
+ if(listener.getClass().getName().contains("org.eclipse.papyrus.emf.facet.infra.browser.uicore.internal.CustomTreePainter")) {
+ listenersToRemove.add(listener);
+ }
+ }
+
+ for(Listener listener : listenersToRemove) {
+ tree.removeListener(SWT.MeasureItem, listener);
+ }
+ }
+
+ private void initCommonViewer(CommonViewer viewer) {
// enable tool-tips
// workaround for bug 311827: the Common Viewer always uses NavigatorDecoratingLabelProvider
// as a wrapper for the LabelProvider provided by the application. The NavigatorDecoratingLabelProvider
@@ -375,36 +436,6 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
}
}
contentService.dispose(); // No longer need this
-
- viewer.getNavigatorContentService().getActivationService().addExtensionActivationListener(new IExtensionActivationListener() {
-
- public void onExtensionActivation(String aViewerId, String[] theNavigatorExtensionIds, boolean isActive) {
- sharedState.updateNavigatorContentExtensions(theNavigatorExtensionIds, isActive);
- }
- });
-
- ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
-
- return viewer;
- }
-
- private void installEMFFacetTreePainter(Tree tree) {
- // Install the EMFFacet Custom Tree Painter
- //org.eclipse.papyrus.infra.emf.Activator.getDefault().getCustomizationManager().installCustomPainter(tree);
-
- // The EMF Facet MeasureItem Listener is incompatible with the NavigatorDecoratingLabelProvider. Remove it.
- // Symptoms: ModelElementItems with an EMF Facet Overlay have a small selection size
- // Removal also fixes bug 400012: no scrollbar although tree is larger than visible area
- Collection<Listener> listenersToRemove = new LinkedList<Listener>();
- for(Listener listener : tree.getListeners(SWT.MeasureItem)) {
- if(listener.getClass().getName().contains("org.eclipse.papyrus.emf.facet.infra.browser.uicore.internal.CustomTreePainter")) {
- listenersToRemove.add(listener);
- }
- }
-
- for(Listener listener : listenersToRemove) {
- tree.removeListener(SWT.MeasureItem, listener);
- }
}
@Override
@@ -625,6 +656,19 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
super.init(site, aMemento);
activate();
+
+ // Self-listen for property changes
+ addPropertyListener(new IPropertyListener() {
+
+ public void propertyChanged(Object source, int propId) {
+ switch(propId) {
+ case IS_LINKING_ENABLED_PROPERTY:
+ // Propagate to other instances
+ sharedState.setLinkingEnabled(isLinkingEnabled());
+ break;
+ }
+ }
+ });
}
/**
@@ -753,8 +797,6 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
* Activate specified Part.
*/
private void activate() {
-
-
try {
this.editingDomain = ServiceUtils.getInstance().getTransactionalEditingDomain(serviceRegistry);
@@ -777,19 +819,6 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
if(this.getCommonViewer() != null) {
syncRefresh();
}
-
- // Self-listen for property changes
- addPropertyListener(new IPropertyListener() {
-
- public void propertyChanged(Object source, int propId) {
- switch(propId) {
- case IS_LINKING_ENABLED_PROPERTY:
- // Propagate to other instances
- sharedState.setLinkingEnabled(isLinkingEnabled());
- break;
- }
- }
- });
}
/**
@@ -801,6 +830,15 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
Activator.log.debug("deactivate ModelExplorerView"); //$NON-NLS-1$
}
+ try {
+ ISashWindowsContainer sashWindowsContainer = serviceRegistry.getService(ISashWindowsContainer.class);
+ if(sashWindowsContainer != null) {
+ sashWindowsContainer.removePageLifeCycleListener(this);
+ }
+ } catch (ServiceException ex) {
+ //Ignore
+ }
+
// Stop listening on change events
getSite().getPage().removeSelectionListener(pageSelectionListener);
// Stop Listening to isDirty flag
@@ -811,6 +849,11 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
editingDomain = null;
}
+ saveAndDirtyService = null;
+ undoContext = null;
+ editingDomain = null;
+ editingDomain = null;
+ lastTrans = null;
}
/**
@@ -828,31 +871,19 @@ public class ModelExplorerView extends CommonNavigator implements IRevealSemanti
sharedState.removeListener(sharedStateListener);
}
- try {
- ISashWindowsContainer sashWindowsContainer = serviceRegistry.getService(ISashWindowsContainer.class);
- if(sashWindowsContainer != null) {
- sashWindowsContainer.removePageLifeCycleListener(this);
- }
- } catch (ServiceException ex) {
- //Ignore
+ if(getSite() != null) {
+ getSite().getPage().removeSelectionListener(pageSelectionListener);
}
deactivate();
- saveAndDirtyService = null;
- undoContext = null;
- editingDomain = null;
- pageSelectionListener = null;
- editingDomain = null;
- lastTrans = null;
-
for(IPropertySheetPage propertySheetPage : this.propertySheetPages) {
propertySheetPage.dispose();
}
propertySheetPages.clear();
-
+ pageSelectionListener = null;
super.dispose();
diff --git a/plugins/views/validation/org.eclipse.papyrus.views.validation/src/org/eclipse/papyrus/views/validation/internal/providers/ProblemsContentProvider.java b/plugins/views/validation/org.eclipse.papyrus.views.validation/src/org/eclipse/papyrus/views/validation/internal/providers/ProblemsContentProvider.java
index 23b5f5c347c..c47c5d83a33 100644
--- a/plugins/views/validation/org.eclipse.papyrus.views.validation/src/org/eclipse/papyrus/views/validation/internal/providers/ProblemsContentProvider.java
+++ b/plugins/views/validation/org.eclipse.papyrus.views.validation/src/org/eclipse/papyrus/views/validation/internal/providers/ProblemsContentProvider.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* CEA LIST - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437217
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.validation.internal.providers;
@@ -31,8 +33,7 @@ import com.google.common.collect.Iterables;
/**
* This is the ProblemsContentProvider type. Enjoy.
*/
-public class ProblemsContentProvider
- implements IStructuredContentProvider {
+public class ProblemsContentProvider implements IStructuredContentProvider {
private static final Object[] NONE = {};
@@ -53,31 +54,30 @@ public class ProblemsContentProvider
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- this.viewer = (AbstractTableViewer) viewer;
+ this.viewer = (AbstractTableViewer)viewer;
- if (oldInput instanceof ValidationMarkersService) {
- ValidationMarkersService service = (ValidationMarkersService) oldInput;
+ if(oldInput instanceof ValidationMarkersService) {
+ ValidationMarkersService service = (ValidationMarkersService)oldInput;
unhookMarkers(service);
- unhookResourceSet(service.getModelSet()
- .getTransactionalEditingDomain());
+
+ // The old service may have been disposed if its editor was closed
+ if(service.getModelSet() != null) {
+ unhookResourceSet(service.getModelSet().getTransactionalEditingDomain());
+ }
+
this.service = null;
}
- if (newInput instanceof ValidationMarkersService) {
- ValidationMarkersService service = (ValidationMarkersService) newInput;
+ if(newInput instanceof ValidationMarkersService) {
+ ValidationMarkersService service = (ValidationMarkersService)newInput;
this.service = service;
hookMarkers(service);
- hookResourceSet(service.getModelSet()
- .getTransactionalEditingDomain());
+ hookResourceSet(service.getModelSet().getTransactionalEditingDomain());
}
}
public Object[] getElements(Object inputElement) {
- return (inputElement instanceof ValidationMarkersService)
- ? Iterables.toArray(
- ((ValidationMarkersService) inputElement).getMarkers(),
- IPapyrusMarker.class)
- : NONE;
+ return (inputElement instanceof ValidationMarkersService) ? Iterables.toArray(((ValidationMarkersService)inputElement).getMarkers(), IPapyrusMarker.class) : NONE;
}
protected void hookMarkers(ValidationMarkersService service) {
@@ -89,19 +89,18 @@ public class ProblemsContentProvider
}
private IValidationMarkerListener getValidationMarkerListener() {
- if (listener == null) {
+ if(listener == null) {
listener = new IValidationMarkerListener() {
- public void notifyMarkerChange(IPapyrusMarker marker,
- MarkerChangeKind kind) {
- if (viewer != null) {
- switch (kind) {
- case ADDED :
- viewer.add(marker);
- break;
- case REMOVED :
- viewer.remove(marker);
- break;
+ public void notifyMarkerChange(IPapyrusMarker marker, MarkerChangeKind kind) {
+ if(viewer != null) {
+ switch(kind) {
+ case ADDED:
+ viewer.add(marker);
+ break;
+ case REMOVED:
+ viewer.remove(marker);
+ break;
}
}
}
@@ -120,42 +119,36 @@ public class ProblemsContentProvider
}
private ResourceSetListener getResourceSetListener() {
- if (resourceSetListener == null) {
+ if(resourceSetListener == null) {
resourceSetListener = new DemultiplexingListener() {
@Override
- protected void handleNotification(
- TransactionalEditingDomain domain,
- Notification notification) {
+ protected void handleNotification(TransactionalEditingDomain domain, Notification notification) {
// handle containment changes of problem elements to update
// labels
Object feature = notification.getFeature();
- if ((feature instanceof EReference)
- && ((EReference) feature).isContainment()) {
-
- switch (notification.getEventType()) {
- case Notification.ADD :
- handleContainment((EObject) notification
- .getNewValue());
- break;
- case Notification.ADD_MANY :
- for (Object next : (Collection<?>) notification
- .getNewValue()) {
- handleContainment((EObject) next);
- }
- break;
- case Notification.SET :
- handleContainment((EObject) notification
- .getNewValue());
- break;
+ if((feature instanceof EReference) && ((EReference)feature).isContainment()) {
+
+ switch(notification.getEventType()) {
+ case Notification.ADD:
+ handleContainment((EObject)notification.getNewValue());
+ break;
+ case Notification.ADD_MANY:
+ for(Object next : (Collection<?>)notification.getNewValue()) {
+ handleContainment((EObject)next);
+ }
+ break;
+ case Notification.SET:
+ handleContainment((EObject)notification.getNewValue());
+ break;
}
}
}
private void handleContainment(EObject object) {
Object[] markers = service.getMarkers(object).toArray();
- if (markers.length > 0) {
+ if(markers.length > 0) {
viewer.update(markers, null);
}
}

Back to the top