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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.browsing.ui.platform;
\r
14 import org.eclipse.jface.resource.ImageDescriptor;
\r
15 import org.eclipse.jface.resource.JFaceResources;
\r
16 import org.eclipse.jface.resource.LocalResourceManager;
\r
17 import org.eclipse.jface.resource.ResourceManager;
\r
18 import org.eclipse.jface.viewers.ISelection;
\r
19 import org.eclipse.swt.widgets.Composite;
\r
20 import org.eclipse.swt.widgets.Display;
\r
21 import org.eclipse.ui.IEditorPart;
\r
22 import org.eclipse.ui.IMemento;
\r
23 import org.eclipse.ui.IPropertyListener;
\r
24 import org.eclipse.ui.ISelectionListener;
\r
25 import org.eclipse.ui.IViewPart;
\r
26 import org.eclipse.ui.IViewSite;
\r
27 import org.eclipse.ui.IWorkbenchPage;
\r
28 import org.eclipse.ui.IWorkbenchPart;
\r
29 import org.eclipse.ui.PartInitException;
\r
30 import org.eclipse.ui.contexts.IContextService;
\r
31 import org.eclipse.ui.part.IContributedContentsView;
\r
32 import org.eclipse.ui.part.IPage;
\r
33 import org.eclipse.ui.part.PageBook;
\r
34 import org.eclipse.ui.part.PageBookView;
\r
35 import org.simantics.browsing.ui.swt.IVariablesPage;
\r
36 import org.simantics.db.management.ISessionContextProvider;
\r
37 import org.simantics.selectionview.PropertyPage;
\r
38 import org.simantics.ui.SimanticsUI;
\r
39 import org.simantics.ui.workbench.IPropertyPage;
\r
40 import org.simantics.ui.workbench.ResourceInput;
\r
41 import org.simantics.utils.ui.BundleUtils;
\r
44 * This is a version of the standard eclipse <code>PropertySheet</code> view a
\r
45 * graph database access twist. It presents a property view based the active
\r
46 * workbench part and the active part's current selection.
\r
49 * To get a property page for your own view or editor part you can do one of the
\r
53 * <li>Implement getAdapter for your view or editor part as follows:
\r
56 * Object getAdapter(Class c) {
\r
57 * if (c == ISessionContextProvider.class)
\r
58 * // Get ISessionContextProvider from somewhere, i.e. SimanticsUI
\r
59 * if (c == IPropertyPage.class)
\r
60 * return new PropertyPage(getSite(), this);
\r
64 * This method also allows customization of the actual property page control
\r
65 * that gets created. <code>PropertyPage</code> serves as a good starting
\r
66 * point for your own version.</li>
\r
67 * <li>Make the workbench part implement the marker interface
\r
68 * <code>IStandardPropertyPage</code> which will make this view do the above
\r
69 * automatically without implementing <code>getAdapter</code>.</li>
\r
72 * @author Tuukka Lehtonen
\r
74 * @see IStandardPropertyPage
\r
75 * @see IPropertyPage
\r
78 public class VariablesPageView extends PageBookView implements ISelectionListener {
\r
80 private static final String VARIABLES_VIEW_CONTEXT = "org.simantics.browsing.ui.variables";
\r
82 private static final String PROP_PINNED = "pinned";
\r
84 protected static final long SELECTION_CHANGE_THRESHOLD = 500;
\r
86 private ISessionContextProvider contextProvider;
\r
89 * The initial selection when the property sheet opens
\r
91 private ISelection bootstrapSelection;
\r
94 * A flag for indicating whether or not this view will only use the
\r
95 * bootstrap selection and and IPropertyPage source instead of listening to
\r
96 * the input constantly.
\r
98 private final boolean bootstrapOnly = false;
\r
100 private IMemento memento;
\r
102 private boolean pinSelection = false;
\r
104 private IWorkbenchPart lastPart;
\r
105 private ISelection lastSelection;
\r
107 private ResourceManager resourceManager;
\r
109 private ImageDescriptor notPinned;
\r
110 private ImageDescriptor pinned;
\r
113 public void createPartControl(Composite parent) {
\r
114 super.createPartControl(parent);
\r
116 this.resourceManager = new LocalResourceManager(JFaceResources.getResources());
\r
117 notPinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple.png");
\r
118 pinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple_pinned.png");
\r
120 IContextService cs = (IContextService) getSite().getService(IContextService.class);
\r
121 cs.activateContext(VARIABLES_VIEW_CONTEXT);
\r
127 * @see org.eclipse.ui.part.PageBookView#getAdapter(java.lang.Class)
\r
129 @SuppressWarnings({ "rawtypes" })
\r
131 public Object getAdapter(Class adapter) {
\r
132 if (adapter == IContributedContentsView.class) {
\r
133 // This makes it possible to duplicate a PropertyPageView with another
\r
134 // secondary ID and make it show the same property page that was showing
\r
135 // in the original property page view.
\r
136 return new IContributedContentsView() {
\r
138 public IWorkbenchPart getContributingPart() {
\r
139 return getContributingEditor();
\r
143 if (adapter == ISessionContextProvider.class)
\r
144 return contextProvider;
\r
145 return super.getAdapter(adapter);
\r
149 * Returns the editor which contributed the current
\r
150 * page to this view.
\r
152 * @return the editor which contributed the current page
\r
153 * or <code>null</code> if no editor contributed the current page
\r
155 private IWorkbenchPart getContributingEditor() {
\r
156 return getCurrentContributingPart();
\r
160 * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
\r
163 public void init(IViewSite site, IMemento memento) throws PartInitException {
\r
164 this.memento = memento;
\r
171 * @see org.eclipse.ui.part.PageBookView#init(org.eclipse.ui.IViewSite)
\r
174 public void init(IViewSite site) throws PartInitException {
\r
175 String secondaryId = site.getSecondaryId();
\r
176 if (secondaryId != null) {
\r
177 ResourceInput input = ResourceInput.unmarshall(secondaryId);
\r
178 if (input != null) {
\r
179 //bootstrapOnly = true;
\r
183 //System.out.println("PPV init: " + this);
\r
186 // This prevents the Properties view from providing a selection to other
\r
187 // workbench parts, thus making them lose their selections which is not
\r
189 site.setSelectionProvider(null);
\r
191 contextProvider = SimanticsUI.getSessionContextProvider();
\r
193 if (!bootstrapOnly) {
\r
194 site.getPage().addSelectionListener(immediateSelectionListener);
\r
195 site.getPage().addPostSelectionListener(this);
\r
200 public void saveState(IMemento memento) {
\r
201 if (this.memento != null) {
\r
202 memento.putMemento(this.memento);
\r
207 * Method declared on IWorkbenchPart.
\r
210 public void dispose() {
\r
211 //System.out.println("PPV dispose: " + this);
\r
212 // Dispose of this before nullifying contextProvider because this
\r
213 // dispose may just need the context provider - at least PropertyPage
\r
217 if (lastPart != null)
\r
218 lastPart.removePropertyListener(partPropertyListener);
\r
220 contextProvider = null;
\r
222 // Remove ourselves as a workbench selection listener.
\r
223 if (!bootstrapOnly) {
\r
224 getSite().getPage().removePostSelectionListener(this);
\r
225 getSite().getPage().removeSelectionListener(immediateSelectionListener);
\r
228 if (resourceManager != null) {
\r
229 resourceManager.dispose();
\r
230 resourceManager = null;
\r
235 protected IPage createDefaultPage(PageBook book) {
\r
237 MessagePage page = new MessagePage();
\r
239 page.createControl(book);
\r
240 page.setMessage(Messages.PropertyPageView_noPropertiesAvailable);
\r
244 VariablesPage page = new VariablesPage(getSite(), this);
\r
245 page.setAdapter(this);
\r
247 page.createControl(book);
\r
248 //System.out.println("PPV create default page: " + page);
\r
254 protected PageRec doCreatePage(IWorkbenchPart part) {
\r
258 // // NOTE: If the default page should be shown, this method must return null.
\r
259 // if (part == null)
\r
262 // //System.out.println("PPV try to create page for part: " + (part != null ? part.getTitle() : null));
\r
264 // VariablesPage page = new VariablesPage(getSite(), this);
\r
266 // // Make sure that the adaptation is provided by this class
\r
267 // // in order not to leave the ISessionContextProvider adaptability
\r
268 // // up to clients to implement.
\r
269 // page.setAdapter(this);
\r
270 // //System.out.println("PPV created page: " + page);
\r
271 // if (page instanceof IPageBookViewPage) {
\r
272 // initPage((IPageBookViewPage) page);
\r
274 // page.createControl(getPageBook());
\r
275 // //System.out.println("PPV created page control: " + page.getControl());
\r
276 // return new PageRec(part, page);
\r
281 protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
\r
282 //System.out.println("PPV destroy page for part: " + part.getTitle());
\r
284 IPropertyPage page = (IPropertyPage) pageRecord.page;
\r
286 pageRecord.dispose();
\r
290 protected IWorkbenchPart getBootstrapPart() {
\r
291 IWorkbenchPage page = getSite().getPage();
\r
292 if (page != null) {
\r
293 bootstrapSelection = page.getSelection();
\r
294 return page.getActivePart();
\r
300 protected boolean isImportant(IWorkbenchPart part) {
\r
301 // If selection is pinned, part activations are not important.
\r
304 // Ignore self, try all others.
\r
305 return part != this;
\r
309 * The <code>PropertySheet</code> implementation of this
\r
310 * <code>IPartListener</code> method first sees if the active part is an
\r
311 * <code>IContributedContentsView</code> adapter and if so, asks it for
\r
312 * its contributing part.
\r
315 public void partActivated(IWorkbenchPart part) {
\r
316 // if (bootstrapSelection == null && bootstrapOnly)
\r
319 // Look for a declaratively-contributed adapter - including not yet
\r
320 // loaded adapter factories.
\r
321 // See bug 86362 [PropertiesView] Can not access AdapterFactory, when
\r
322 // plugin is not loaded.
\r
323 IWorkbenchPart source = getSourcePart(part);
\r
324 //System.out.println("PPV part activated: " + part + ",src " + source + ",view " + this + " bss: " + bootstrapSelection + " pin " + pinSelection);
\r
325 super.partActivated(source);
\r
327 // When the view is first opened, pass the selection to the page
\r
328 if (bootstrapSelection != null) {
\r
329 IPage page = getCurrentPage();
\r
330 if (page instanceof IPropertyPage) {
\r
331 IPropertyPage ppage = (IPropertyPage) page;
\r
332 // FIXME: should this pass source or part ??
\r
333 ppage.selectionChanged(part, bootstrapSelection);
\r
335 bootstrapSelection = null;
\r
341 protected void partHidden(IWorkbenchPart part) {
\r
342 // Make sure that pinned view is not hidden when the editor is hidden
\r
344 super.partHidden(part);
\r
347 long lastSelectionChangeTime = -1000;
\r
349 ISelectionListener immediateSelectionListener = new ISelectionListener() {
\r
351 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
\r
352 // Check that enough time has passed since the last selection change.
\r
353 long time = System.currentTimeMillis();
\r
354 long delta = time - lastSelectionChangeTime;
\r
355 lastSelectionChangeTime = time;
\r
357 //System.out.println("time delta: " + delta + " : " + selection);
\r
359 if (delta > SELECTION_CHANGE_THRESHOLD) {
\r
360 VariablesPageView.this.selectionChanged(part, selection);
\r
365 public ISelection getLastSelection() {
\r
366 return lastSelection;
\r
372 * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,
\r
373 * org.eclipse.jface.viewers.ISelection)
\r
376 public void selectionChanged(IWorkbenchPart part, ISelection sel) {
\r
377 // we ignore our own selection or null selection
\r
378 if (part == this || sel == null) {
\r
381 // ignore workbench selections when pinned also
\r
385 // pass the selection change to the page
\r
386 //System.out.println("PPV selection changed: " + sel + " " + this);
\r
387 IPage page = getCurrentPage();
\r
388 if (page instanceof IVariablesPage) {
\r
389 IVariablesPage ppage = (IVariablesPage) page;
\r
390 ppage.selectionChanged(part, sel);
\r
392 // Make sure that the part name is not updated unnecessarily because
\r
393 // of immediate and post selection listeners.
\r
394 boolean sameSelection = false;
\r
395 if (part == lastPart) {
\r
396 if (sel != null && sel.equals(lastSelection))
\r
397 sameSelection = true;
\r
400 if (lastPart != null) {
\r
401 lastPart.removePropertyListener(partPropertyListener);
\r
404 lastSelection = sel;
\r
405 if (lastPart != null) {
\r
406 lastPart.addPropertyListener(partPropertyListener);
\r
409 if (!sameSelection) {
\r
410 final Display d = getSite().getShell().getDisplay();
\r
411 ppage.updatePartName(sel, parameter -> {
\r
412 if (!d.isDisposed())
\r
413 d.asyncExec(() -> doSetPartName(parameter));
\r
419 void doSetPartName(String partName) {
\r
420 // Is the page view disposed ??
\r
421 if (contextProvider == null)
\r
423 if (partName == null) {
\r
424 // Return to default
\r
425 partName = "Selection";
\r
427 setPartName(partName);
\r
430 public boolean isWorkbenchSelectionPinned() {
\r
431 return pinSelection;
\r
434 public void pinWorkbenchSelection(boolean pin) {
\r
435 if (pin == pinSelection)
\r
438 pinSelection = pin;
\r
439 setPartProperty(PROP_PINNED, Boolean.toString(pin));
\r
442 setTitleImage(resourceManager.createImage(pinned));
\r
444 setTitleImage(resourceManager.createImage(notPinned));
\r
446 updateContentDescription(pin, lastPart);
\r
447 // Since lastPart is another PropertyView, we do not want to listen it's changes (At least current implementation is done so)
\r
448 if (lastPart != null) {
\r
449 lastPart.removePropertyListener(partPropertyListener);
\r
455 IWorkbenchPart getSourcePart(IWorkbenchPart part) {
\r
456 IContributedContentsView view = (IContributedContentsView) part.getAdapter(IContributedContentsView.class);
\r
457 if (view != null) {
\r
458 IWorkbenchPart source = view.getContributingPart();
\r
459 if (source != null)
\r
465 private void updateContentDescription(boolean selectionPinned, IWorkbenchPart sourcePart) {
\r
466 if (selectionPinned) {
\r
467 if (sourcePart == null) {
\r
468 setContentDescription("No selection");
\r
470 sourcePart = getSourcePart(sourcePart);
\r
472 StringBuilder desc = new StringBuilder("Selection from ");
\r
473 if (sourcePart instanceof IEditorPart)
\r
474 desc.append("editor ");
\r
475 if (sourcePart instanceof IViewPart)
\r
476 desc.append("view ");
\r
478 desc.append(sourcePart.getTitle());
\r
481 setContentDescription(desc.toString());
\r
484 setContentDescription("");
\r
488 IPropertyListener partPropertyListener = new IPropertyListener() {
\r
490 public void propertyChanged(Object source, int propId) {
\r
491 if (propId == IWorkbenchPart.PROP_TITLE) {
\r
492 updateContentDescription(pinSelection, lastPart);
\r