]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.views.swt/src/org/simantics/views/swt/SimanticsView.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.views.swt / src / org / simantics / views / swt / SimanticsView.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.views.swt;\r
13 \r
14 import java.util.Collection;\r
15 import java.util.Collections;\r
16 import java.util.HashSet;\r
17 import java.util.Map;\r
18 import java.util.Set;\r
19 \r
20 import org.eclipse.jface.action.IMenuManager;\r
21 import org.eclipse.jface.resource.JFaceResources;\r
22 import org.eclipse.jface.resource.LocalResourceManager;\r
23 import org.eclipse.jface.viewers.ISelectionProvider;\r
24 import org.eclipse.swt.SWT;\r
25 import org.eclipse.swt.dnd.DND;\r
26 import org.eclipse.swt.dnd.DragSource;\r
27 import org.eclipse.swt.dnd.DragSourceListener;\r
28 import org.eclipse.swt.dnd.FileTransfer;\r
29 import org.eclipse.swt.dnd.Transfer;\r
30 import org.eclipse.swt.widgets.Composite;\r
31 import org.eclipse.swt.widgets.Control;\r
32 import org.eclipse.ui.IMemento;\r
33 import org.eclipse.ui.ISelectionListener;\r
34 import org.eclipse.ui.IViewSite;\r
35 import org.eclipse.ui.IWorkbenchSite;\r
36 import org.eclipse.ui.PartInitException;\r
37 import org.eclipse.ui.contexts.IContextService;\r
38 import org.eclipse.ui.part.ViewPart;\r
39 import org.simantics.browsing.ui.BuiltinKeys;\r
40 import org.simantics.browsing.ui.Column;\r
41 import org.simantics.browsing.ui.GraphExplorer;\r
42 import org.simantics.browsing.ui.NodeContext;\r
43 import org.simantics.browsing.ui.common.ColumnKeys;\r
44 import org.simantics.browsing.ui.common.node.IDropTargetNode;\r
45 import org.simantics.browsing.ui.graph.impl.GraphInputSources;\r
46 import org.simantics.browsing.ui.graph.impl.SessionContextInputSource;\r
47 import org.simantics.browsing.ui.swt.ContextMenuInitializer;\r
48 import org.simantics.browsing.ui.swt.DefaultKeyListener;\r
49 import org.simantics.browsing.ui.swt.DefaultMouseListener;\r
50 import org.simantics.browsing.ui.swt.DefaultSelectionDataResolver;\r
51 import org.simantics.browsing.ui.swt.GraphExplorerFactory;\r
52 import org.simantics.browsing.ui.swt.IContextMenuInitializer;\r
53 import org.simantics.browsing.ui.swt.ViewArgumentUtils;\r
54 import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite;\r
55 import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport;\r
56 import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl;\r
57 import org.simantics.db.Disposable;\r
58 import org.simantics.db.management.ISessionContext;\r
59 import org.simantics.db.management.ISessionContextChangedListener;\r
60 import org.simantics.db.management.ISessionContextProvider;\r
61 import org.simantics.db.management.SessionContextChangedEvent;\r
62 import org.simantics.project.ProjectKeys;\r
63 import org.simantics.ui.SimanticsUI;\r
64 import org.simantics.ui.dnd.LocalObjectTransfer;\r
65 import org.simantics.ui.dnd.LocalSelectionDragSourceListener;\r
66 import org.simantics.ui.dnd.NoImageDragSourceEffect;\r
67 import org.simantics.ui.workbench.IPropertyPage;\r
68 import org.simantics.utils.ObjectUtils;\r
69 import org.simantics.utils.datastructures.Function;\r
70 import org.simantics.utils.datastructures.disposable.DisposeState;\r
71 import org.simantics.utils.datastructures.hints.HintContext;\r
72 import org.simantics.utils.datastructures.hints.HintListenerAdapter;\r
73 import org.simantics.utils.datastructures.hints.HintTracker;\r
74 import org.simantics.utils.datastructures.hints.IHintContext;\r
75 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
76 import org.simantics.utils.datastructures.hints.IHintListener;\r
77 import org.simantics.utils.datastructures.hints.IHintObservable;\r
78 import org.simantics.utils.datastructures.hints.IHintTracker;\r
79 import org.simantics.utils.ui.LayoutUtils;\r
80 \r
81 /**\r
82  * @author Antti Villberg\r
83  * @deprecated in favor of org.simantics.views.swt.ModelledView\r
84  */\r
85 public abstract class SimanticsView extends ViewPart {\r
86 \r
87     private final WidgetSupportImpl          widgetSupport = createSupport();\r
88 \r
89     protected IHintContext                   factoryHints = new HintContext();\r
90 \r
91     protected LocalResourceManager           resourceManager;\r
92 \r
93     protected ISelectionListener             workbenchSelectionListener;\r
94 \r
95     protected Composite                      parent;\r
96 \r
97     private Map<String, String>              args;\r
98 \r
99     private ISessionContextProvider          contextProvider;\r
100 \r
101     private ISessionContext                  sessionContext;\r
102 \r
103     protected IMemento                       memento;\r
104 \r
105     private IHintTracker                     sessionContextTracker = new SessionContextProjectTracker();\r
106 \r
107     private SessionContextInputSource        inputSource           = GraphInputSources.projectSource();\r
108 \r
109     private DisposeState                     disposeState          = DisposeState.Alive;\r
110 \r
111     protected ISessionContextChangedListener contextChangeListener = new ISessionContextChangedListener() {\r
112         @Override\r
113         public void sessionContextChanged(SessionContextChangedEvent event) {\r
114             sessionContext = event.getNewValue();\r
115             sessionContextTracker.track(sessionContext);\r
116         }\r
117     };\r
118 \r
119     protected Set<String> getBrowseContexts() {\r
120         return Collections.emptySet();\r
121     }\r
122 \r
123     protected WidgetSupportImpl createSupport() {\r
124         return new WidgetSupportImpl();\r
125     }\r
126     \r
127     abstract protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support);\r
128 \r
129     protected void activateUiContexts() {\r
130         Collection<String> contexts = getUiContexts();\r
131         if (!contexts.isEmpty()) {\r
132             IContextService cs = (IContextService) getSite().getService(IContextService.class);\r
133             for (String context : contexts)\r
134                 cs.activateContext(context);\r
135         }\r
136     }\r
137 \r
138     protected Transfer[] getAcceptedDataTypes() {\r
139         return new Transfer[] {  LocalObjectTransfer.getTransfer(), FileTransfer.getInstance() };\r
140     }\r
141 \r
142     protected void handleDrop(Object data, NodeContext target) {\r
143         if (target != null) {\r
144             Object input = target.getConstant(BuiltinKeys.INPUT);\r
145             //System.out.println("DROPPED " + data + " ON " + target);\r
146             if (input instanceof IDropTargetNode)\r
147                 ((IDropTargetNode) input).drop(data);\r
148         }\r
149     }\r
150 \r
151     protected Object createDragSource(GraphExplorer explorer) {\r
152         ISelectionProvider selectionProvider = (ISelectionProvider) explorer.getAdapter(ISelectionProvider.class);\r
153 \r
154         DragSourceListener listener = new LocalSelectionDragSourceListener(selectionProvider);\r
155 \r
156         Control control = explorer.getControl();\r
157         DragSource source = new DragSource(control, DND.DROP_LINK | DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);\r
158         source.setTransfer(new Transfer[] {LocalObjectTransfer.getTransfer()});\r
159         source.addDragListener(listener);\r
160         source.setDragSourceEffect(new NoImageDragSourceEffect(control));\r
161 \r
162         return listener;\r
163     }\r
164 \r
165     /**\r
166      * @return the set of <code>org.eclipse.ui.context</code> contexts to\r
167      *         activate for this view site\r
168      */\r
169     protected Set<String> getUiContexts() {\r
170         return Collections.emptySet();\r
171     }\r
172 \r
173     /**\r
174      * The default hint tracker that will be active if\r
175      * {@link SimanticsViewBase#setSessionContextTracker(IHintTracker) is\r
176      * not called.\r
177      */\r
178     public class SessionContextProjectTracker extends HintTracker {\r
179         public SessionContextProjectTracker() {\r
180             IHintListener activeProjectListener = new HintListenerAdapter() {\r
181                 @Override\r
182                 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
183                     applySessionContext(getSessionContext());\r
184                 }\r
185             };\r
186             addKeyHintListener(ProjectKeys.KEY_PROJECT, activeProjectListener);\r
187         }\r
188     }\r
189 \r
190     protected void setSessionContextTracker(IHintTracker tracker) {\r
191         this.sessionContextTracker = tracker;\r
192     }\r
193 \r
194     public void setInputSource(SessionContextInputSource source) {\r
195         this.inputSource = source;\r
196     }\r
197 \r
198     protected final SessionContextInputSource getInputSource() {\r
199         return inputSource;\r
200     }\r
201 \r
202     protected Map<String, String> getViewArguments() {\r
203         return args;\r
204     }\r
205 \r
206     protected DisposeState getDisposeState() {\r
207         return disposeState;\r
208     }\r
209 \r
210     public ISessionContext getSessionContext() {\r
211         return sessionContext;\r
212     }\r
213 \r
214     public ISessionContextProvider getSessionContextProvider() {\r
215         return contextProvider;\r
216     }\r
217 \r
218     @Override\r
219     public void createPartControl(Composite parent) {\r
220         this.parent = parent;\r
221         this.resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay()));\r
222 \r
223         contextProvider = SimanticsUI.getSessionContextProvider(getViewSite().getWorkbenchWindow());\r
224 \r
225         parent.setLayout(LayoutUtils.createNoBorderGridLayout(1, false));\r
226 \r
227         setWorkbenchListeners();\r
228         activateUiContexts();\r
229 \r
230         createControls(parent, getSite(), getSessionContext(), widgetSupport);\r
231 \r
232         attachToSession();\r
233 \r
234     }\r
235 \r
236     /**\r
237      * Invoked when this viewpart is disposed. Unhooks the view from its\r
238      * ISessionContextProvider. Overriding is allowed but super.dispose() must\r
239      * be called.\r
240      * \r
241      * @see org.eclipse.ui.part.WorkbenchPart#dispose()\r
242      */\r
243     @Override\r
244     public void dispose() {\r
245         removeWorkbenchListeners();\r
246         disposeState = DisposeState.Disposing;\r
247         try {\r
248             if (inputSource instanceof Disposable) {\r
249                 ((Disposable) inputSource).dispose();\r
250             }\r
251             //System.out.println(this + ".GraphExplorerViewBase.dispose()");\r
252             if (contextProvider != null) {\r
253                 contextProvider.removeContextChangedListener(contextChangeListener);\r
254                 contextProvider = null;\r
255             }\r
256             sessionContextTracker.untrack();\r
257             resourceManager.dispose();\r
258             if (widgetSupport instanceof Disposable)\r
259                 ((Disposable) widgetSupport).dispose();\r
260             resourceManager = null;\r
261             args = null;\r
262             sessionContext = null;\r
263             parent = null;\r
264             super.dispose();\r
265         } finally {\r
266             disposeState = DisposeState.Disposed;\r
267         }\r
268     }\r
269 \r
270     @Override\r
271     public void setFocus() {\r
272     }\r
273 \r
274     @Override\r
275     public void init(IViewSite site) throws PartInitException {\r
276         super.init(site);\r
277         this.args = ViewArgumentUtils.parseViewArguments(this);\r
278     }\r
279 \r
280     @Override\r
281     public void init(IViewSite site, IMemento memento) throws PartInitException {\r
282         super.init(site, memento);\r
283         this.args = ViewArgumentUtils.parseViewArguments(this);\r
284         this.memento = memento;\r
285     }\r
286 \r
287     @Override\r
288     public void saveState(IMemento memento) {\r
289         if (this.memento != null) {\r
290             memento.putMemento(this.memento);\r
291         }\r
292 //        if (explorer != null)\r
293 //            explorer.saveState(memento);\r
294     }\r
295 \r
296     protected void setWorkbenchListeners() {\r
297         if (workbenchSelectionListener == null) {\r
298 \r
299 //            ISelectionProvider selectionProvider = (ISelectionProvider)explorer.getAdapter(ISelectionProvider.class);\r
300 //            getSite().setSelectionProvider(selectionProvider);\r
301 \r
302             // Listen to the workbench selection also to propagate it to\r
303             // the explorer also.\r
304 //            workbenchSelectionListener = new DefaultExplorerSelectionListener(this, explorer);\r
305 //            getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(workbenchSelectionListener);\r
306         }\r
307     }\r
308 \r
309     protected void removeWorkbenchListeners() {\r
310         // Remember to remove the installed workbench selection listener\r
311         if (workbenchSelectionListener != null) {\r
312             getSite().getWorkbenchWindow().getSelectionService().removePostSelectionListener(workbenchSelectionListener);\r
313             workbenchSelectionListener = null;\r
314 \r
315             getSite().setSelectionProvider(null);\r
316         }\r
317     }\r
318 \r
319     protected final void attachToSession() {\r
320         // Track active ISessionContext changes\r
321         //contextProvider = SimanticsUI.getSessionContextProvider(getViewSite().getWorkbenchWindow());\r
322         contextProvider.addContextChangedListener(contextChangeListener);\r
323 \r
324         // Start tracking the current session context for input changes.\r
325         // This will/must cause applySessionContext to get called.\r
326         // Doing the applySessionContext initialization this way\r
327         // instead of directly calling it will also make sure that\r
328         // applySessionContext is only called once when first initialized,\r
329         // and not twice like with the direct invocation.\r
330         this.sessionContext = contextProvider.getSessionContext();\r
331         sessionContextTracker.track(sessionContext);\r
332     }\r
333 \r
334     // /////////////////////////////////////////////////////////////////////////\r
335     // Override / implement these:\r
336 \r
337     protected Column[] getColumns() {\r
338         return null;\r
339     }\r
340 \r
341     /**\r
342      * Override this method to add controls to the view part. This is invoked\r
343      * before attaching the view part to a database session.\r
344      * \r
345      * @param parent\r
346      */\r
347     protected void createControls(Composite parent) {\r
348 \r
349         parent.setLayout(LayoutUtils.createNoBorderGridLayout(1, false));\r
350 \r
351     }\r
352 \r
353     /**\r
354      * Override this method and provide a proper context menu initializer if you\r
355      * want to have this base class initialize one for you.\r
356      * \r
357      * @return the initializer to be used by {@link #createControls(Composite)}\r
358      */\r
359     protected IContextMenuInitializer getContextMenuInitializer() {\r
360         String contextMenuId = getContextMenuId();\r
361         if(contextMenuId != null) {\r
362             return new ContextMenuInitializer(contextMenuId);\r
363         } else {\r
364             return null;\r
365         }\r
366     }\r
367 \r
368     protected String getContextMenuId() {\r
369         return null;\r
370     }\r
371 \r
372     protected int getStyle() {\r
373         return SWT.MULTI;\r
374     }\r
375 \r
376     /**\r
377      * @param parent\r
378      * @return\r
379      */\r
380     protected GraphExplorer createExplorerControl(Composite parent) {\r
381         return GraphExplorerFactory.getInstance()\r
382         .selectionDataResolver(new DefaultSelectionDataResolver())\r
383         .create(parent, getStyle());\r
384     }\r
385 \r
386     /**\r
387      * Override to customize the addition of listeners a newly created\r
388      * GraphExplorer.\r
389      * \r
390      * @param explorer\r
391      */\r
392     protected void addListeners(GraphExplorer explorer, IMenuManager menuManager) {\r
393         addSelectionInputListeners(explorer, menuManager);\r
394     }\r
395 \r
396     protected void addSelectionInputListeners(GraphExplorer explorer, IMenuManager menuManager) {\r
397         // Consider ENTER presses to simulate mouse left button double clicks\r
398         explorer.addListener(new DefaultKeyListener(contextProvider, explorer, new Function<String[]>() {\r
399             @Override\r
400             public String[] execute(Object... obj) {\r
401                 return new String[] { getEditingColumn((NodeContext) obj[0]) };\r
402             }\r
403         }));\r
404         // Default double click handling\r
405         explorer.addListener(new DefaultMouseListener(explorer));\r
406     }\r
407 \r
408     protected String getEditingColumn(NodeContext context) {\r
409         return ColumnKeys.SINGLE;\r
410     }\r
411 \r
412     // Needed for preventing unnecessary re-initialization of the explorer with the same input.\r
413     private Object currentInput;\r
414 \r
415     protected boolean isImportantInput(Object previousInput, Object input) {\r
416         return !ObjectUtils.objectEquals(previousInput, input);\r
417     }\r
418 \r
419     /**\r
420      * Invoke this to reinitialize the explorer and reset its input. The input\r
421      * will be resolved from the specified ISessionContext based on the\r
422      * {@link SessionContextInputSource} that is currently in use. If the input\r
423      * is identical to the previous input, nothing will be done.\r
424      * \r
425      * @param context\r
426      */\r
427     protected final boolean applySessionContext(ISessionContext context) {\r
428         // If control is not alive anymore, do nothing.\r
429         //System.out.println(this + "(SimanticsView).applySessionContext(context=" + context + ")");\r
430         if (disposeState != DisposeState.Alive)\r
431             return false;\r
432         \r
433         if(this.sessionContext == null && context == null)\r
434             return false;\r
435 \r
436         this.sessionContext = context;\r
437         Object input = getInputSource().get(context);\r
438         //System.out.println(this + "(SimanticsView).applySessionContext(input=" + input + ")");\r
439         if (!isImportantInput(currentInput, input))\r
440             return false;\r
441 \r
442 //        System.out.println(this + ": initializeExplorer(" + explorer + ", " + context + ")");\r
443 //        initializeExplorer(explorer, context);\r
444 //        System.out.println(this + ": setRoot(" + input + ")");\r
445 //        explorer.setRoot(input);\r
446 \r
447         currentInput = input;\r
448         //System.out.println(this + "(SimanticsView).applySessionContext(new input=" + currentInput + ")");\r
449 \r
450         widgetSupport.fireInput(context, input);\r
451 \r
452         // Start tracking the session context.\r
453         //\r
454         // If this is not the same session that is currently tracked, it will\r
455         // cause IHintListeners of the sessionContextTracker to fire.\r
456         // For this we need the above input equality (identity) checking.\r
457         // This is here just to make sure that we are tracking the correct\r
458         // session context.\r
459         sessionContextTracker.track(sessionContext);\r
460 \r
461         return true;\r
462     }\r
463     \r
464     @SuppressWarnings("unchecked")\r
465     <T extends Control> void searchControl(Control control, Class<T> c, Set<T> result) {\r
466         if(c.isInstance(control))\r
467                 result.add((T)control);\r
468         if(control instanceof Composite) {\r
469                 for(Control child : ((Composite)control).getChildren()) {\r
470                         searchControl(child, c, result);\r
471                 }\r
472         }\r
473     }\r
474 \r
475     @SuppressWarnings("unchecked")\r
476     @Override\r
477     public <T> T getAdapter(Class<T> adapter) {\r
478 \r
479         if(ISessionContextProvider.class == adapter)\r
480             return (T) getSessionContextProvider();\r
481         else if(IPropertyPage.class == adapter)\r
482             return (T) getPropertyPage();\r
483         else if(GraphExplorer.class == adapter) {\r
484                 Set<GraphExplorerComposite> composites = new HashSet<>();\r
485                 searchControl(parent, GraphExplorerComposite.class, composites);\r
486                 if(composites.size() == 1) {\r
487                         GraphExplorerComposite gec = composites.iterator().next(); \r
488                         return (T) gec.getExplorer();\r
489                 }\r
490                 return null;\r
491         }\r
492 \r
493         return super.getAdapter(adapter);\r
494 \r
495     }\r
496 \r
497     abstract protected IPropertyPage getPropertyPage();\r
498     \r
499 //    protected IPropertyPage getPropertyPage() {\r
500 //        return new StandardPropertyPage(getSite(), getBrowseContexts());\r
501 //    }\r
502 \r
503 }\r