Skip to content

Package: TreeMasterDetailSWTRenderer$MasterTreeContextMenuListener

TreeMasterDetailSWTRenderer$MasterTreeContextMenuListener

nameinstructionbranchcomplexitylinemethod
TreeMasterDetailSWTRenderer.MasterTreeContextMenuListener(TreeMasterDetailSWTRenderer, EditingDomain, TreeViewer, ChildrenDescriptorCollector, List)
M: 0 C: 18
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
fillMenu(EObject, IMenuManager)
M: 0 C: 40
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
menuAboutToShow(IMenuManager)
M: 6 C: 105
95%
M: 7 C: 13
65%
M: 7 C: 4
36%
M: 3 C: 20
87%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * Anas Chakfeh - initial API and implementation
13: * Eugen Neufeld - Refactoring
14: * Alexandra Buzila - Refactoring
15: * Johannes Faltermeier - integration with validation service
16: * Christian W. Damus - bugs 543376, 545460, 527686, 548592, 552385
17: ******************************************************************************/
18: package org.eclipse.emf.ecp.view.spi.treemasterdetail.ui.swt;
19:
20: import java.util.ArrayList;
21: import java.util.Arrays;
22: import java.util.Collection;
23: import java.util.Collections;
24: import java.util.Iterator;
25: import java.util.LinkedHashSet;
26: import java.util.LinkedList;
27: import java.util.List;
28: import java.util.Set;
29:
30: import javax.inject.Inject;
31:
32: import org.eclipse.core.runtime.CoreException;
33: import org.eclipse.core.runtime.IConfigurationElement;
34: import org.eclipse.core.runtime.IExtensionRegistry;
35: import org.eclipse.core.runtime.IStatus;
36: import org.eclipse.core.runtime.Platform;
37: import org.eclipse.core.runtime.Status;
38: import org.eclipse.emf.common.command.Command;
39: import org.eclipse.emf.common.command.CompoundCommand;
40: import org.eclipse.emf.common.notify.AdapterFactory;
41: import org.eclipse.emf.common.notify.Notification;
42: import org.eclipse.emf.common.notify.Notifier;
43: import org.eclipse.emf.common.notify.impl.AdapterImpl;
44: import org.eclipse.emf.common.util.Diagnostic;
45: import org.eclipse.emf.ecore.EAttribute;
46: import org.eclipse.emf.ecore.EObject;
47: import org.eclipse.emf.ecore.EReference;
48: import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
49: import org.eclipse.emf.ecore.util.EcoreUtil;
50: import org.eclipse.emf.ecp.common.spi.ChildrenDescriptorCollector;
51: import org.eclipse.emf.ecp.edit.internal.swt.util.OverlayImageDescriptor;
52: import org.eclipse.emf.ecp.edit.spi.ConditionalDeleteService;
53: import org.eclipse.emf.ecp.edit.spi.DeleteService;
54: import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
55: import org.eclipse.emf.ecp.edit.spi.ReferenceService;
56: import org.eclipse.emf.ecp.ui.view.swt.ECPSWTViewRenderer;
57: import org.eclipse.emf.ecp.view.internal.swt.ContextMenuViewModelService;
58: import org.eclipse.emf.ecp.view.internal.treemasterdetail.ui.swt.Activator;
59: import org.eclipse.emf.ecp.view.model.common.edit.provider.CustomReflectiveItemProviderAdapterFactory;
60: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
61: import org.eclipse.emf.ecp.view.spi.context.ViewModelContextFactory;
62: import org.eclipse.emf.ecp.view.spi.model.ModelChangeAddRemoveListener;
63: import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
64: import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
65: import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
66: import org.eclipse.emf.ecp.view.spi.model.VView;
67: import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
68: import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
69: import org.eclipse.emf.ecp.view.spi.swt.masterdetail.DetailViewCache;
70: import org.eclipse.emf.ecp.view.spi.swt.masterdetail.DetailViewManager;
71: import org.eclipse.emf.ecp.view.spi.swt.selection.IMasterDetailSelectionProvider;
72: import org.eclipse.emf.ecp.view.spi.swt.services.ECPSelectionProviderService;
73: import org.eclipse.emf.ecp.view.treemasterdetail.model.VTreeMasterDetail;
74: import org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.internal.RootObject;
75: import org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.internal.TreeMasterDetailSelectionManipulatorHelper;
76: import org.eclipse.emf.edit.command.AddCommand;
77: import org.eclipse.emf.edit.command.CommandParameter;
78: import org.eclipse.emf.edit.command.DeleteCommand;
79: import org.eclipse.emf.edit.command.SetCommand;
80: import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
81: import org.eclipse.emf.edit.domain.EditingDomain;
82: import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
83: import org.eclipse.emf.edit.ui.action.ecp.CreateChildAction;
84: import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
85: import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
86: import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
87: import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
88: import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
89: import org.eclipse.emfforms.common.Optional;
90: import org.eclipse.emfforms.spi.common.report.ReportService;
91: import org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer;
92: import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
93: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
94: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
95: import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
96: import org.eclipse.jface.action.Action;
97: import org.eclipse.jface.action.IMenuListener;
98: import org.eclipse.jface.action.IMenuManager;
99: import org.eclipse.jface.action.MenuManager;
100: import org.eclipse.jface.action.Separator;
101: import org.eclipse.jface.action.ToolBarManager;
102: import org.eclipse.jface.layout.GridDataFactory;
103: import org.eclipse.jface.layout.GridLayoutFactory;
104: import org.eclipse.jface.resource.ImageDescriptor;
105: import org.eclipse.jface.viewers.ILabelProvider;
106: import org.eclipse.jface.viewers.ISelection;
107: import org.eclipse.jface.viewers.ISelectionChangedListener;
108: import org.eclipse.jface.viewers.IStructuredSelection;
109: import org.eclipse.jface.viewers.ITreeContentProvider;
110: import org.eclipse.jface.viewers.SelectionChangedEvent;
111: import org.eclipse.jface.viewers.StructuredSelection;
112: import org.eclipse.jface.viewers.TreePath;
113: import org.eclipse.jface.viewers.TreeSelection;
114: import org.eclipse.jface.viewers.TreeViewer;
115: import org.eclipse.swt.SWT;
116: import org.eclipse.swt.custom.SashForm;
117: import org.eclipse.swt.custom.ScrolledComposite;
118: import org.eclipse.swt.dnd.DND;
119: import org.eclipse.swt.dnd.Transfer;
120: import org.eclipse.swt.events.DisposeEvent;
121: import org.eclipse.swt.events.DisposeListener;
122: import org.eclipse.swt.graphics.Color;
123: import org.eclipse.swt.graphics.Font;
124: import org.eclipse.swt.graphics.Image;
125: import org.eclipse.swt.graphics.Point;
126: import org.eclipse.swt.graphics.RGB;
127: import org.eclipse.swt.layout.FormAttachment;
128: import org.eclipse.swt.layout.FormData;
129: import org.eclipse.swt.layout.FormLayout;
130: import org.eclipse.swt.layout.GridLayout;
131: import org.eclipse.swt.widgets.Composite;
132: import org.eclipse.swt.widgets.Control;
133: import org.eclipse.swt.widgets.Display;
134: import org.eclipse.swt.widgets.Label;
135: import org.eclipse.swt.widgets.Menu;
136: import org.eclipse.swt.widgets.ToolBar;
137: import org.osgi.framework.FrameworkUtil;
138:
139: /**
140: * SWT Renderer for a {@link VTreeMasterDetail} element.
141: *
142: * @author Anas Chakfeh
143: * @author Eugen Neufeld
144: * @since 1.5
145: *
146: */
147: @SuppressWarnings("deprecation")
148: public class TreeMasterDetailSWTRenderer extends AbstractSWTRenderer<VTreeMasterDetail> {
149:
150:         private static final String ENABLE_MULTI_EDIT = "enableMultiEdit"; //$NON-NLS-1$
151:
152:         /**
153:          * Default Constructor.
154:          *
155:          * @param vElement the view element to be rendered
156:          * @param viewContext The view model context
157:          * @param reportService the ReportService to use
158:          * @since 1.6
159:          */
160:         @Inject
161:         public TreeMasterDetailSWTRenderer(final VTreeMasterDetail vElement, final ViewModelContext viewContext,
162:                 ReportService reportService) {
163:
164:                 super(vElement, viewContext, reportService);
165:         }
166:
167:         /**
168:          * The detail key passed to the view model context.
169:          */
170:         public static final String DETAIL_KEY = DetailViewManager.DETAIL_PROPERTY;
171:
172:         /**
173:          * Menu separator ID for the group to which additional menu contributions are added in the
174:          * tree's context menu.
175:          */
176:         public static final String GLOBAL_ADDITIONS = "global_additions"; //$NON-NLS-1$
177:
178:         /**
179:          * Context key for the root.
180:          */
181:         public static final String ROOT_KEY = "root"; //$NON-NLS-1$
182:
183:         private SWTGridDescription rendererGridDescription;
184:
185:         private Font detailsFont;
186:         private Color titleColor;
187:         private Font titleFont;
188:         private Color headerBgColor;
189:         private TreeViewer treeViewer;
190:
191:         private ScrolledComposite rightPanel;
192:
193:         private Composite container;
194:
195:         private Composite rightPanelContainerComposite;
196:
197:         private ModelChangeListener domainModelListener;
198:         private ViewModelContext childContext;
199:         private DetailViewManager detailManager;
200:
201:         /**
202:          * @author jfaltermeier
203:          *
204:          */
205:         private final class MasterTreeContextMenuListener implements IMenuListener {
206:                 private final EditingDomain editingDomain;
207:                 private final TreeViewer treeViewer;
208:                 private final ChildrenDescriptorCollector childrenDescriptorCollector;
209:                 private final List<MasterDetailAction> menuActions;
210:
211:                 /**
212:                  * @param editingDomain
213:                  * @param treeViewer
214:                  * @param childrenDescriptorCollector
215:                  * @param menuActions
216:                  */
217:                 private MasterTreeContextMenuListener(EditingDomain editingDomain, TreeViewer treeViewer,
218:                         ChildrenDescriptorCollector childrenDescriptorCollector, List<MasterDetailAction> menuActions) {
219:                         this.editingDomain = editingDomain;
220:                         this.treeViewer = treeViewer;
221:                         this.childrenDescriptorCollector = childrenDescriptorCollector;
222:                         this.menuActions = menuActions;
223:                 }
224:
225:                 @Override
226:                 public void menuAboutToShow(IMenuManager manager) {
227:•                        if (getVElement().isEffectivelyReadonly() || !getVElement().isEffectivelyEnabled()) {
228:                                 return;
229:                         }
230:•                        if (treeViewer.getSelection().isEmpty()) {
231:                                 fillMenu(null, manager);
232:                                 return;
233:                         }
234:                         final EObject root = ((RootObject) treeViewer.getInput()).getRoot();
235:
236:•                        if (treeViewer.getSelection() instanceof IStructuredSelection) {
237:                                 final IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
238:
239:•                                if (selection.size() == 1 && EObject.class.isInstance(selection.getFirstElement())) {
240:                                         final EObject eObject = (EObject) selection.getFirstElement();
241:                                         final EditingDomain domain = AdapterFactoryEditingDomain.getEditingDomainFor(eObject);
242:•                                        if (domain == null) {
243:                                                 return;
244:                                         }
245:                                         final Collection<?> descriptors = childrenDescriptorCollector.getDescriptors(eObject);
246:                                         fillContextMenu(manager, descriptors, editingDomain, eObject);
247:                                 }
248:•                                if (!selection.toList().contains(root)) {
249:                                         manager.add(new Separator(GLOBAL_ADDITIONS));
250:                                         addDeleteActionToContextMenu(editingDomain, manager, selection);
251:                                 }
252:                                 manager.add(new Separator());
253:
254:•                                if (selection.getFirstElement() != null && EObject.class.isInstance(selection.getFirstElement())) {
255:                                         final EObject selectedObject = (EObject) selection.getFirstElement();
256:
257:                                         fillMenu(selectedObject, manager);
258:                                 }
259:                         }
260:                 }
261:
262:                 private void fillMenu(final EObject selectedObject, IMenuManager manager) {
263:•                        for (final MasterDetailAction menuAction : menuActions) {
264:•                                if (menuAction.shouldShow(selectedObject)) {
265:                                         final Action newAction = new Action() {
266:                                                 @Override
267:                                                 public void run() {
268:                                                         super.run();
269:                                                         menuAction.execute(selectedObject);
270:                                                 }
271:                                         };
272:
273:                                         newAction.setImageDescriptor(ImageDescriptor.createFromURL(FrameworkUtil.getBundle(
274:                                                 menuAction.getClass())
275:                                                 .getResource(menuAction.getImagePath())));
276:                                         newAction.setText(menuAction.getLabel());
277:
278:                                         manager.add(newAction);
279:                                 }
280:                         }
281:                 }
282:         }
283:
284:         @Override
285:         protected void dispose() {
286:                 rendererGridDescription = null;
287:                 if (detailManager != null) {
288:                         detailManager.dispose();
289:                 }
290:                 childContext = null;
291:                 if (getViewModelContext() != null && domainModelListener != null) {
292:                         getViewModelContext().unregisterDomainChangeListener(domainModelListener);
293:                 }
294:                 domainModelListener = null;
295:                 if (treeViewer != null) {
296:                         treeViewer.setInput(null);
297:                 }
298:                 super.dispose();
299:         }
300:
301:         /**
302:          * {@inheritDoc}
303:          *
304:          * @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#getGridDescription(SWTGridDescription)
305:          */
306:         @Override
307:         public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
308:                 if (rendererGridDescription == null) {
309:                         rendererGridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
310:                 }
311:                 return rendererGridDescription;
312:         }
313:
314:         /**
315:          * {@inheritDoc}
316:          *
317:          * @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#renderControl(org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell,
318:          * org.eclipse.swt.widgets.Composite)
319:          */
320:         @Override
321:         protected Control renderControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
322:                 NoPropertyDescriptorFoundExeption {
323:
324:                 /* The tree's composites */
325:                 final Composite form = createMasterDetailForm(parent);
326:
327:                 createHeader(form);
328:
329:                 final SashForm sash = createSash(form);
330:
331:                 final Composite masterPanel = createMasterPanel(sash);
332:
333:                 createRightPanelContent(sash);
334:
335:                 sash.setWeights(new int[] { 1, 3 });
336:
337:                 createMasterTree(masterPanel);
338:
339:                 if (hasContextMenu()) {
340:                         registerControlAsContextMenuReceiver();
341:                 }
342:                 form.layout(true);
343:                 return form;
344:         }
345:
346:         private void registerControlAsContextMenuReceiver() {
347:                 if (!getViewModelContext().hasService(ContextMenuViewModelService.class)) {
348:                         return;
349:                 }
350:                 final ContextMenuViewModelService service = getViewModelContext().getService(
351:                         ContextMenuViewModelService.class);
352:
353:                 if (service != null) {
354:                         service.setParentControl(treeViewer.getTree());
355:                         service.registerContextMenu();
356:                 }
357:         }
358:
359:         /**
360:          * Creates the sashform for the master detail colums.
361:          *
362:          * @param parent the parent
363:          * @return the sash
364:          */
365:         protected SashForm createSash(Composite parent) {
366:                 /* THe contents of the composite */
367:                 final Composite sashComposite = new Composite(parent, SWT.FILL);
368:                 final GridLayout sashLayout = GridLayoutFactory.fillDefaults().create();
369:                 sashLayout.marginWidth = 5;
370:                 sashComposite.setLayout(sashLayout);
371:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(sashComposite);
372:
373:                 final SashForm sash = new SashForm(sashComposite, SWT.HORIZONTAL);
374:
375:                 sash.setBackground(parent.getBackground());
376:                 GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).applyTo(sash);
377:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(sash);
378:                 sash.setSashWidth(5);
379:                 return sash;
380:         }
381:
382:         /**
383:          * Create the parent of the master detail form.
384:          *
385:          * @param parent the parent
386:          * @return the composite
387:          */
388:         protected Composite createMasterDetailForm(Composite parent) {
389:                 final Composite form = new Composite(parent, SWT.BORDER);
390:                 final GridLayout layout = GridLayoutFactory.fillDefaults().create();
391:
392:                 form.setLayout(layout);
393:                 form.setBackgroundMode(SWT.INHERIT_FORCE);
394:                 // form.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
395:                 return form;
396:         }
397:
398:         /**
399:          * Creates the tree viewer for the master.
400:          *
401:          * @param masterPanel the parent
402:          * @return the tree viewer
403:          */
404:         protected TreeViewer createMasterTree(final Composite masterPanel) {
405:                 final EObject modelElement = getViewModelContext().getDomainModel();
406:                 final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(modelElement);
407:
408:                 final ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
409:                         new CustomReflectiveItemProviderAdapterFactory(),
410:                         new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
411:
412:                 final AdapterFactoryContentProvider adapterFactoryContentProvider = new AdapterFactoryContentProvider(
413:                         adapterFactory) {
414:
415:                         @Override
416:                         public Object[] getElements(Object object) {
417:                                 return new Object[] { ((RootObject) object).getRoot() };
418:                         }
419:                 };
420:                 final AdapterFactoryLabelProvider labelProvider = new TreeMasterDetailLabelProvider(adapterFactory);
421:
422:                 treeViewer = new TreeViewer(masterPanel);
423:
424:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).hint(100, SWT.DEFAULT)
425:                         .applyTo(treeViewer.getTree());
426:
427:                 treeViewer.setContentProvider(adapterFactoryContentProvider);
428:                 treeViewer.setLabelProvider(getLabelProvider(labelProvider));
429:                 treeViewer.setAutoExpandLevel(2); // top level element is expanded, but not the children
430:                 treeViewer.setInput(new RootObject(modelElement));
431:
432:                 domainModelListener = new ModelChangeAddRemoveListener() {
433:
434:                         @Override
435:                         public void notifyChange(ModelChangeNotification notification) {
436:                                 // nothing to do here
437:                         }
438:
439:                         // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=27480
440:                         // the treeviewer doesn't autoexpand on refresh
441:                         @Override
442:                         public void notifyAdd(Notifier notifier) {
443:                                 if (isRenderingFinished()) {
444:                                         treeViewer.expandToLevel(notifier, 1);
445:                                 }
446:                         }
447:
448:                         @Override
449:                         public void notifyRemove(Notifier notifier) {
450:                                 // If an element is deleted, reset the selection to the root node
451:                                 if (childContext != null && notifier == childContext.getDomainModel()) {
452:                                         treeViewer.setSelection(new StructuredSelection(getViewModelContext().getDomainModel()));
453:                                 }
454:                         }
455:                 };
456:                 getViewModelContext().registerDomainChangeListener(domainModelListener);
457:
458:                 // Drag and Drop
459:                 if (hasDnDSupport()) {
460:                         addDragAndDropSupport(modelElement, treeViewer, editingDomain);
461:                 }
462:
463:                 // Selection Listener
464:                 final TreeMasterViewSelectionListener treeMasterViewSelectionListener = new TreeMasterViewSelectionListener();
465:                 treeViewer.addSelectionChangedListener(treeMasterViewSelectionListener);
466:                 treeViewer.setSelection(new StructuredSelection(modelElement));
467:                 if (hasContextMenu()) {
468:                         fillContextMenu(treeViewer, editingDomain);
469:                 }
470:
471:                 treeViewer.getTree().addDisposeListener(new DisposeListener() {
472:
473:                         @Override
474:                         public void widgetDisposed(DisposeEvent event) {
475:                                 adapterFactoryContentProvider.dispose();
476:                                 labelProvider.dispose();
477:                                 adapterFactory.dispose();
478:                                 if (titleFont != null) {
479:                                         titleFont.dispose();
480:                                 }
481:                                 if (detailsFont != null) {
482:                                         detailsFont.dispose();
483:                                 }
484:                                 if (titleColor != null) {
485:                                         titleColor.dispose();
486:                                 }
487:                                 if (headerBgColor != null) {
488:                                         headerBgColor.dispose();
489:                                 }
490:                                 treeViewer.removeSelectionChangedListener(treeMasterViewSelectionListener);
491:                         }
492:                 });
493:
494:                 // Register my tree viewer as the selection provider for my element
495:                 final ECPSelectionProviderService sps = getViewModelContext().getService(ECPSelectionProviderService.class);
496:                 final IMasterDetailSelectionProvider mdSelectionProvider = sps.createMasterDetailSelectionProvider(treeViewer,
497:                         () -> rightPanel);
498:                 sps.registerSelectionProvider(getVElement(), mdSelectionProvider);
499:
500:                 return treeViewer;
501:         }
502:
503:         /**
504:          * Return true if a context menu should be shown in the tree.
505:          *
506:          * @return true if a context menu should be shown, false otherwise
507:          */
508:         protected boolean hasContextMenu() {
509:                 return !getVElement().isEffectivelyReadonly();
510:         }
511:
512:         /**
513:          * Return true if the tree should support DnD.
514:          *
515:          * @return true if DnD should be supported , false otherwise
516:          */
517:         protected boolean hasDnDSupport() {
518:                 return !getVElement().isEffectivelyReadonly();
519:         }
520:
521:         /**
522:          * Returns the label provider.
523:          *
524:          * @param adapterFactoryLabelProvider the adaper factory label provider
525:          * @return the label provider to use for the tree
526:          */
527:         protected ILabelProvider getLabelProvider(final AdapterFactoryLabelProvider adapterFactoryLabelProvider) {
528:                 return adapterFactoryLabelProvider;
529:         }
530:
531:         /**
532:          * Creates the composite for the master panel.
533:          *
534:          * @param sash the parent
535:          * @return the composite
536:          */
537:         protected Composite createMasterPanel(final SashForm sash) {
538:                 final Composite leftPanel = new Composite(sash, SWT.NONE);
539:                 leftPanel.setLayout(GridLayoutFactory.fillDefaults().create());
540:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(leftPanel);
541:                 // leftPanel.setBackground(sash.getBackground());
542:                 leftPanel.setBackgroundMode(SWT.INHERIT_FORCE);
543:                 return leftPanel;
544:         }
545:
546:         /**
547:          * Adds the header to a parent composite.
548:          *
549:          * @param parent the parent
550:          */
551:         protected void createHeader(Composite parent) {
552:                 final Composite headerComposite = new Composite(parent, SWT.NONE);
553:                 final GridLayout headerLayout = GridLayoutFactory.fillDefaults().create();
554:                 headerComposite.setLayout(headerLayout);
555:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(headerComposite);
556:                 headerBgColor = new Color(parent.getDisplay(), new RGB(220, 240, 247));
557:                 headerComposite.setBackground(headerBgColor);
558:
559:                 final EObject modelElement = getViewModelContext().getDomainModel();
560:                 final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(modelElement);
561:
562:                 /* The header of the composite */
563:                 if (modelElement.eContainer() == null && !DynamicEObjectImpl.class.isInstance(modelElement)) {
564:
565:                         final Composite header = getPageHeader(headerComposite);
566:                         final List<Action> actions = readToolbarActions(modelElement, editingDomain);
567:
568:                         final ToolBar toolBar = new ToolBar(header, SWT.FLAT | SWT.RIGHT);
569:                         final FormData formData = new FormData();
570:                         formData.right = new FormAttachment(100, 0);
571:                         toolBar.setLayoutData(formData);
572:                         toolBar.layout();
573:                         final ToolBarManager toolBarManager = new ToolBarManager(toolBar);
574:
575:                         /* Add actions to header */
576:                         for (final Action action : actions) {
577:                                 toolBarManager.add(action);
578:                         }
579:                         toolBarManager.update(true);
580:                         header.layout();
581:
582:                 }
583:         }
584:
585:         private Composite getPageHeader(Composite parent) {
586:                 final Composite header = new Composite(parent, SWT.FILL);
587:                 final FormLayout layout = new FormLayout();
588:                 layout.marginHeight = 5;
589:                 layout.marginWidth = 5;
590:                 header.setLayout(layout);
591:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(header);
592:
593:                 header.setBackground(parent.getBackground());
594:
595:                 final Label titleImage = new Label(header, SWT.FILL);
596:                 final ImageDescriptor imageDescriptor = ImageDescriptor.createFromURL(Activator.getDefault()
597:                         .getBundle()
598:                         .getResource("icons/view.png")); //$NON-NLS-1$
599:                 titleImage.setImage(new Image(parent.getDisplay(), imageDescriptor.getImageData()));
600:                 final FormData titleImageData = new FormData();
601:                 final int imageOffset = -titleImage.computeSize(SWT.DEFAULT, SWT.DEFAULT).y / 2;
602:                 titleImageData.top = new FormAttachment(50, imageOffset);
603:                 titleImageData.left = new FormAttachment(0, 10);
604:                 titleImage.setLayoutData(titleImageData);
605:
606:                 final Label title = new Label(header, SWT.WRAP);
607:                 title.setText("View Editor"); //$NON-NLS-1$
608:                 titleFont = new Font(title.getDisplay(), getDefaultFontName(title), 12, SWT.BOLD);
609:                 title.setFont(titleFont);
610:                 title.setForeground(getTitleColor(parent));
611:                 final int titleHeight = title.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
612:                 final FormData titleData = new FormData();
613:                 titleData.left = new FormAttachment(titleImage, 5, SWT.DEFAULT);
614:                 titleData.top = new FormAttachment(50, -titleHeight / 2);
615:                 title.setLayoutData(titleData);
616:
617:                 return header;
618:
619:         }
620:
621:         private Color getTitleColor(Composite parent) {
622:                 if (titleColor == null) {
623:                         titleColor = new Color(parent.getDisplay(), new RGB(25, 76, 127));
624:                 }
625:                 return titleColor;
626:         }
627:
628:         /**
629:          * Creates the composite holding the details.
630:          *
631:          * @param parent the parent
632:          * @return the right panel/detail composite
633:          */
634:         protected ScrolledComposite createRightPanelContent(Composite parent) {
635:                 rightPanel = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
636:                 rightPanel.setShowFocusedControl(true);
637:                 rightPanel.setExpandVertical(true);
638:                 rightPanel.setExpandHorizontal(true);
639:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(rightPanel);
640:                 rightPanel.setLayout(GridLayoutFactory.fillDefaults().create());
641:                 rightPanel.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
642:
643:                 container = new Composite(rightPanel, SWT.FILL);
644:                 container.setLayout(GridLayoutFactory.fillDefaults().create());
645:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(container);
646:                 container.setBackground(rightPanel.getBackground());
647:
648:                 /* The header */
649:                 final Composite header = new Composite(container, SWT.FILL);
650:                 final GridLayout headerLayout = GridLayoutFactory.fillDefaults().create();
651:                 header.setLayout(headerLayout);
652:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(header);
653:                 header.setBackground(rightPanel.getBackground());
654:
655:                 final Label label = new Label(header, SWT.WRAP);
656:                 label.setText("Details"); //$NON-NLS-1$
657:                 detailsFont = new Font(label.getDisplay(), getDefaultFontName(label), 10, SWT.BOLD);
658:                 label.setFont(detailsFont);
659:                 label.setForeground(getTitleColor(parent));
660:                 label.setBackground(header.getBackground());
661:
662:                 rightPanelContainerComposite = new Composite(container, SWT.FILL);
663:                 rightPanelContainerComposite.setLayout(GridLayoutFactory.fillDefaults().create());
664:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true)
665:                         .applyTo(rightPanelContainerComposite);
666:                 rightPanelContainerComposite.setBackground(rightPanel.getBackground());
667:
668:                 rightPanel.setContent(container);
669:
670:                 rightPanel.layout();
671:                 container.layout();
672:
673:                 final Point point = container.computeSize(SWT.DEFAULT, SWT.DEFAULT);
674:                 rightPanel.setMinSize(point);
675:
676:                 detailManager = new DetailViewManager(rightPanelContainerComposite);
677:                 detailManager.setCache(DetailViewCache.createCache(getViewModelContext()));
678:                 detailManager.layoutDetailParent(rightPanelContainerComposite);
679:
680:                 return rightPanel;
681:         }
682:
683:         @Override
684:         protected String getDefaultFontName(Control control) {
685:                 return control.getDisplay().getSystemFont().getFontData()[0].getName();
686:         }
687:
688:         private List<Action> readToolbarActions(EObject modelElement, final EditingDomain editingDomain) {
689:                 final List<Action> actions = new ArrayList<Action>();
690:                 final IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
691:                 if (extensionRegistry == null) {
692:                         return actions;
693:                 }
694:                 if (!VView.class.isInstance(modelElement)) {
695:                         return actions;
696:                 }
697:                 final VView view = (VView) modelElement;
698:
699:                 final IConfigurationElement[] controls = extensionRegistry
700:                         .getConfigurationElementsFor("org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.masterDetailActions"); //$NON-NLS-1$
701:                 for (final IConfigurationElement e : controls) {
702:                         try {
703:                                 final String location = e.getAttribute("location"); //$NON-NLS-1$
704:                                 if (!location.equals("toolbar")) { //$NON-NLS-1$
705:                                         continue;
706:                                 }
707:
708:                                 final String label = e.getAttribute("label"); //$NON-NLS-1$
709:                                 final String imagePath = e.getAttribute("imagePath"); //$NON-NLS-1$
710:                                 final MasterDetailAction command = (MasterDetailAction) e.createExecutableExtension("command"); //$NON-NLS-1$
711:                                 final Action newAction = new Action() {
712:                                         @Override
713:                                         public void run() {
714:                                                 super.run();
715:                                                 command.execute(view);
716:                                         }
717:                                 };
718:
719:                                 newAction.setImageDescriptor(ImageDescriptor.createFromURL(FrameworkUtil.getBundle(command.getClass())
720:                                         .getResource(imagePath)));
721:                                 newAction.setText(label);
722:                                 actions.add(newAction);
723:                         } catch (final CoreException e1) {
724:                                 e1.printStackTrace();
725:                         }
726:                 }
727:                 return actions;
728:         }
729:
730:         private void addDragAndDropSupport(final EObject modelElement, final TreeViewer treeViewer,
731:                 EditingDomain editingDomain) {
732:
733:                 final int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
734:                 final Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance() };
735:                 treeViewer.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(treeViewer));
736:                 final EditingDomainViewerDropAdapter editingDomainViewerDropAdapter = new EditingDomainViewerDropAdapter(
737:                         editingDomain,
738:                         treeViewer);
739:                 treeViewer.addDropSupport(dndOperations, transfers, editingDomainViewerDropAdapter);
740:         }
741:
742:         /**
743:          * @param treeViewer
744:          * @param editingDomain
745:          */
746:         private void fillContextMenu(final TreeViewer treeViewer, final EditingDomain editingDomain) {
747:                 final ChildrenDescriptorCollector childrenDescriptorCollector = new ChildrenDescriptorCollector();
748:                 final List<MasterDetailAction> menuActions = readMasterDetailActions();
749:                 final MenuManager menuMgr = new MenuManager();
750:                 menuMgr.setRemoveAllWhenShown(true);
751:                 menuMgr.addMenuListener(new MasterTreeContextMenuListener(editingDomain, treeViewer,
752:                         childrenDescriptorCollector, menuActions));
753:                 final Menu menu = menuMgr.createContextMenu(treeViewer.getControl());
754:                 treeViewer.getControl().setMenu(menu);
755:         }
756:
757:         /**
758:          * Returns a list of all {@link MasterDetailAction MasterDetailActions} which shall be displayed in the context menu
759:          * of the master treeviewer.
760:          *
761:          * @return the actions
762:          */
763:         protected List<MasterDetailAction> readMasterDetailActions() {
764:                 final List<MasterDetailAction> commands = new ArrayList<MasterDetailAction>();
765:                 final IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
766:                 if (extensionRegistry == null) {
767:                         return commands;
768:                 }
769:
770:                 final IConfigurationElement[] controls = extensionRegistry
771:                         .getConfigurationElementsFor("org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.masterDetailActions"); //$NON-NLS-1$
772:                 for (final IConfigurationElement e : controls) {
773:                         try {
774:                                 final String location = e.getAttribute("location"); //$NON-NLS-1$
775:                                 if (!location.equals("menu")) { //$NON-NLS-1$
776:                                         continue;
777:                                 }
778:                                 final String label = e.getAttribute("label"); //$NON-NLS-1$
779:                                 final String imagePath = e.getAttribute("imagePath"); //$NON-NLS-1$
780:                                 final MasterDetailAction command = (MasterDetailAction) e.createExecutableExtension("command"); //$NON-NLS-1$
781:                                 command.setLabel(label);
782:                                 command.setImagePath(imagePath);
783:                                 command.setTreeViewer(treeViewer);
784:
785:                                 commands.add(command);
786:
787:                         } catch (final CoreException ex) {
788:                                 Activator.getDefault().getLog().log(
789:                                         new Status(IStatus.ERROR, Activator.getDefault().getBundle().getSymbolicName(),
790:                                                 ex.getMessage(), ex));
791:                         }
792:                 }
793:
794:                 return commands;
795:
796:         }
797:
798:         /**
799:          * @param manager The menu manager responsible for the context menu
800:          * @param descriptors The menu items to be added
801:          * @param domain The editing domain of the current EObject
802:          * @param eObject The model element
803:          */
804:         private void fillContextMenu(IMenuManager manager, Collection<?> descriptors, final EditingDomain domain,
805:                 final EObject eObject) {
806:                 for (final Object descriptor : descriptors) {
807:
808:                         final CommandParameter cp = (CommandParameter) descriptor;
809:                         if (!CommandParameter.class.isInstance(descriptor)) {
810:                                 continue;
811:                         }
812:                         if (cp.getEReference() == null) {
813:                                 continue;
814:                         }
815:                         if (!cp.getEReference().isMany() && eObject.eIsSet(cp.getEStructuralFeature())) {
816:                                 continue;
817:                         } else if (cp.getEReference().isMany() && cp.getEReference().getUpperBound() != -1
818:                                 && cp.getEReference().getUpperBound() <= ((List<?>) eObject.eGet(cp.getEReference())).size()) {
819:                                 continue;
820:                         }
821:
822:                         manager.add(new CreateChildAction(domain, new StructuredSelection(eObject), descriptor) {
823:                                 @Override
824:                                 public void run() {
825:                                         super.run();
826:
827:                                         final EReference reference = ((CommandParameter) descriptor).getEReference();
828:                                         // if (!reference.isContainment()) {
829:                                         // domain.getCommandStack().execute(
830:                                         // AddCommand.create(domain, eObject.eContainer(), null, cp.getEValue()));
831:                                         // }
832:
833:                                         domain.getCommandStack().execute(
834:                                                 AddCommand.create(domain, eObject, reference, cp.getEValue()));
835:                                 }
836:                         });
837:                 }
838:
839:         }
840:
841:         /**
842:          * @param editingDomain
843:          * @param manager
844:          * @param selection
845:          */
846:         private void addDeleteActionToContextMenu(final EditingDomain editingDomain, final IMenuManager manager,
847:                 final IStructuredSelection selection) {
848:
849:                 final Action deleteAction = new Action() {
850:                         @SuppressWarnings("unchecked")
851:                         @Override
852:                         public void run() {
853:                                 super.run();
854:                                 DeleteService deleteService = getViewModelContext().getService(DeleteService.class);
855:                                 if (deleteService == null) {
856:                                         /*
857:                                          * #getService(Class<?>) will report to the reportservice if it could not be found
858:                                          * Use Default
859:                                          */
860:                                         deleteService = new EMFDeleteServiceImpl();
861:                                 }
862:                                 deleteService.deleteElements(selection.toList());
863:                         }
864:                 };
865:                 final String deleteImagePath = "icons/delete.png";//$NON-NLS-1$
866:                 deleteAction.setImageDescriptor(ImageDescriptor.createFromURL(Activator.getDefault()
867:                         .getBundle()
868:                         .getResource(deleteImagePath)));
869:                 deleteAction.setText("Delete"); //$NON-NLS-1$
870:                 deleteAction.setEnabled(ConditionalDeleteService.getDeleteService(getViewModelContext()).canDelete(selection.toList()));
871:                 manager.add(deleteAction);
872:         }
873:
874:         /**
875:          * Allows to manipulate the view context for the selected element that is about to be rendered.
876:          *
877:          * @param viewContext the view context.
878:          */
879:         protected void manipulateViewContext(ViewModelContext viewContext) {
880:                 // do nothing
881:         }
882:
883:         /**
884:          *
885:          * @author Anas Chakfeh
886:          * This class is responsible for handling selection changed events which happen on the tree
887:          *
888:          */
889:         private class TreeMasterViewSelectionListener implements ISelectionChangedListener {
890:
891:                 /**
892:                  * Adapter which listens to changes and delegates the notification to other EObjects.
893:                  *
894:                  * @author Eugen Neufeld
895:                  *
896:                  */
897:                 private final class MultiEditAdapter extends AdapterImpl {
898:                         private final EObject dummy;
899:                         private final Set<EObject> selectedEObjects;
900:
901:                         private MultiEditAdapter(EObject dummy, Set<EObject> selectedEObjects) {
902:                                 this.dummy = dummy;
903:                                 this.selectedEObjects = selectedEObjects;
904:                         }
905:
906:                         @Override
907:                         public void notifyChanged(Notification notification) {
908:                                 final EditingDomain editingDomain = AdapterFactoryEditingDomain
909:                                         .getEditingDomainFor(getViewModelContext().getDomainModel());
910:                                 if (dummy.eClass().getEAllAttributes().contains(notification.getFeature())) {
911:                                         final CompoundCommand cc = new CompoundCommand();
912:                                         for (final EObject selected : selectedEObjects) {
913:                                                 Command command = null;
914:                                                 switch (notification.getEventType()) {
915:                                                 case Notification.SET:
916:                                                         command = SetCommand.create(editingDomain, selected,
917:                                                                 notification.getFeature(), notification.getNewValue());
918:                                                         break;
919:                                                 case Notification.UNSET:
920:                                                         command = SetCommand.create(editingDomain, selected,
921:                                                                 notification.getFeature(), SetCommand.UNSET_VALUE);
922:                                                         break;
923:                                                 case Notification.ADD:
924:                                                 case Notification.ADD_MANY:
925:                                                         command = AddCommand.create(editingDomain, selected,
926:                                                                 notification.getFeature(), notification.getNewValue());
927:                                                         break;
928:                                                 case Notification.REMOVE:
929:                                                 case Notification.REMOVE_MANY:
930:                                                         command = DeleteCommand.create(editingDomain, notification.getOldValue());
931:                                                         break;
932:                                                 default:
933:                                                         continue;
934:                                                 }
935:                                                 cc.append(command);
936:                                         }
937:                                         editingDomain.getCommandStack().execute(cc);
938:                                 }
939:                         }
940:                 }
941:
942:                 @Override
943:                 public void selectionChanged(SelectionChangedEvent event) {
944:                         final IStructuredSelection selection = (IStructuredSelection) event.getSelection();
945:                         final Object treeSelected = getSelection(selection);
946:
947:                         detailManager.cacheCurrentDetail();
948:                         cleanCustomOnSelectionChange();
949:
950:                         final Object selectedObject = treeSelected == null ? treeSelected : manipulateSelection(treeSelected);
951:                         if (selectedObject instanceof EObject) {
952:                                 final EObject selected = (EObject) selectedObject;
953:                                 final Object root = manipulateSelection(((RootObject) ((TreeViewer) event.getSource()).getInput())
954:                                         .getRoot());
955:
956:                                 final boolean rootSelected = selected == root;
957:                                 VView view = null;
958:                                 if (rootSelected) {
959:                                         view = getVElement().getDetailView();
960:                                 }
961:                                 if (view == null || view.getChildren().isEmpty()) {
962:                                         view = detailManager.getDetailView(getViewModelContext(), selected,
963:                                                 properties -> {
964:                                                         if (rootSelected) {
965:                                                                 properties.addNonInheritableProperty(ROOT_KEY, true);
966:                                                         }
967:                                                 });
968:                                 }
969:
970:                                 if (detailManager.isCached(selected)) {
971:                                         detailManager.activate(selected);
972:                                 } else {
973:                                         final ReferenceService referenceService = getViewModelContext().getService(
974:                                                 ReferenceService.class);
975:                                         // we have a multi selection, the multi edit is enabled and the multi selection is valid
976:                                         if (getViewModelContext().getContextValue(ENABLE_MULTI_EDIT) == Boolean.TRUE
977:                                                 && selection.size() > 1
978:                                                 && selected != getSelection(new StructuredSelection(selection.getFirstElement()))) {
979:                                                 childContext = ViewModelContextFactory.INSTANCE.createViewModelContext(view, selected,
980:                                                         new TreeMasterDetailReferenceService(referenceService));
981:                                         } else {
982:                                                 childContext = getViewModelContext().getChildContext(selected, getVElement(), view,
983:                                                         new TreeMasterDetailReferenceService(referenceService));
984:                                         }
985:
986:                                         manipulateViewContext(childContext);
987:
988:                                         detailManager.render(childContext, ECPSWTViewRenderer.INSTANCE::render);
989:                                 }
990:
991:                                 detailManager.setDetailReadOnly(!getVElement().isEffectivelyEnabled()
992:                                         || getVElement().isEffectivelyReadonly());
993:                         } else {
994:                                 // No selection
995:                                 childContext = null;
996:                         }
997:
998:                         relayoutDetail();
999:                 }
1000:
1001:                 private Object getSelection(IStructuredSelection selection) {
1002:                         Object treeSelected = selection != null ? selection.getFirstElement() : null;
1003:                         if (getViewModelContext().getContextValue(ENABLE_MULTI_EDIT) == Boolean.TRUE
1004:                                 && treeSelected instanceof EObject && selection.size() > 1) {
1005:                                 boolean allOfSameType = true;
1006:                                 final EObject dummy = EcoreUtil.create(((EObject) treeSelected).eClass());
1007:
1008:                                 final Iterator<?> iterator = selection.iterator();
1009:                                 final Set<EObject> selectedEObjects = new LinkedHashSet<EObject>();
1010:                                 while (iterator.hasNext()) {
1011:                                         final EObject eObject = (EObject) iterator.next();
1012:                                         allOfSameType &= eObject.eClass() == dummy.eClass();
1013:                                         if (allOfSameType) {
1014:                                                 for (final EAttribute attribute : dummy.eClass().getEAllAttributes()) {
1015:                                                         if (eObject == treeSelected) {
1016:                                                                 dummy.eSet(attribute, eObject.eGet(attribute));
1017:                                                         } else if (dummy.eGet(attribute) != null
1018:                                                                 && !dummy.eGet(attribute).equals(eObject.eGet(attribute))) {
1019:                                                                 dummy.eUnset(attribute);
1020:                                                         }
1021:                                                 }
1022:                                                 selectedEObjects.add(eObject);
1023:                                         } else {
1024:                                                 break;
1025:                                         }
1026:                                 }
1027:                                 if (allOfSameType) {
1028:                                         treeSelected = dummy;
1029:                                         dummy.eAdapters().add(new MultiEditAdapter(dummy, selectedEObjects));
1030:                                 }
1031:                         }
1032:                         return treeSelected;
1033:                 }
1034:         }
1035:
1036:         /**
1037:          * Returns the composite for the detail.
1038:          *
1039:          * @return the composite
1040:          */
1041:         protected Composite getDetailContainer() {
1042:                 return rightPanelContainerComposite;
1043:         }
1044:
1045:         /**
1046:          * Allows to manipulate the selection by returning a specific child.
1047:          *
1048:          * @param treeSelected the selected element in the tree
1049:          * @return the object that should be used as a selection
1050:          */
1051:         protected Object manipulateSelection(Object treeSelected) {
1052:                 return TreeMasterDetailSelectionManipulatorHelper.manipulateSelection(treeSelected);
1053:         }
1054:
1055:         /**
1056:          * Gets called after a detail composite was disposed. Allows for further cleanup.
1057:          */
1058:         protected void cleanCustomOnSelectionChange() {
1059:                 // do nothing
1060:         }
1061:
1062:         /**
1063:          * Relayouts the detail composite.
1064:          */
1065:         protected void relayoutDetail() {
1066:                 rightPanelContainerComposite.layout();
1067:                 final Point point = container.computeSize(SWT.DEFAULT, SWT.DEFAULT);
1068:                 rightPanel.setMinSize(point);
1069:         }
1070:
1071:         /**
1072:          * Reveal the given {@code object} in my tree.
1073:          *
1074:          * @param object an object to reveal
1075:          * @return whether I succeeded in revealing it
1076:          *
1077:          * @since 1.22
1078:          */
1079:         public boolean reveal(Object object) {
1080:                 final TreePath treePath = getTreePathFor(object);
1081:                 return reveal(treePath);
1082:         }
1083:
1084:         /**
1085:          * Get a path to an {@object} in my tree.
1086:          *
1087:          * @param object an object in my tree
1088:          * @return a path to it
1089:          *
1090:          * @since 1.22
1091:          */
1092:         public TreePath getTreePathFor(Object object) {
1093:                 final ITreeContentProvider content = (ITreeContentProvider) treeViewer.getContentProvider();
1094:                 final Collection<?> roots = Arrays.asList(content.getElements(treeViewer.getInput()));
1095:
1096:                 final List<Object> path = new LinkedList<Object>();
1097:                 path.add(object);
1098:                 for (Object parent = content.getParent(object); parent != null; parent = content.getParent(parent)) {
1099:                         path.add(0, parent);
1100:
1101:                         // Don't go above the root element
1102:                         if (roots.contains(parent)) {
1103:                                 break;
1104:                         }
1105:                 }
1106:
1107:                 return new TreePath(path.toArray());
1108:         }
1109:
1110:         /**
1111:          * Reveal the given {@code path} in my tree.
1112:          *
1113:          * @param path a tree path to reveal
1114:          * @return whether I succeeded in revealing it
1115:          *
1116:          * @since 1.22
1117:          */
1118:         public boolean reveal(TreePath path) {
1119:                 final ISelection newSelection = new TreeSelection(path);
1120:                 if (!newSelection.equals(treeViewer.getSelection())) {
1121:                         treeViewer.setSelection(new TreeSelection(path), true);
1122:                 }
1123:                 treeViewer.reveal(path);
1124:
1125:                 return treeViewer.getStructuredSelection().getFirstElement() == path.getLastSegment();
1126:         }
1127:
1128:         /**
1129:          * Query whether the given {@code path} exists in my tree.
1130:          *
1131:          * @param path a tree path
1132:          * @return whether the path locates an element that exists in my tree
1133:          *
1134:          * @since 1.22
1135:          */
1136:         public boolean hasPath(TreePath path) {
1137:                 if (path.equals(TreePath.EMPTY)) {
1138:                         return true;
1139:                 }
1140:
1141:                 final TreePath parentPath = path.getParentPath();
1142:                 if (!hasPath(parentPath)) {
1143:                         return false;
1144:                 }
1145:
1146:                 final ITreeContentProvider content = (ITreeContentProvider) treeViewer.getContentProvider();
1147:                 Collection<?> children;
1148:                 if (parentPath.equals(TreePath.EMPTY)) {
1149:                         children = Arrays.asList(content.getElements(treeViewer.getInput()));
1150:                 } else if (content.hasChildren(parentPath.getLastSegment())) {
1151:                         children = Arrays.asList(content.getChildren(parentPath.getLastSegment()));
1152:                 } else {
1153:                         children = Collections.EMPTY_SET;
1154:                 }
1155:
1156:                 return children.contains(path.getLastSegment());
1157:         }
1158:
1159:         /**
1160:          * Obtain the current detail context, if any.
1161:          *
1162:          * @return the view-model context of the details currently being presented,
1163:          * or {@code null} if none (usually because there is no selection in the tree)
1164:          *
1165:          * @since 1.22
1166:          */
1167:         public ViewModelContext getDetailContext() {
1168:                 return childContext;
1169:         }
1170:
1171:         /**
1172:          * The label provider used for the detail tree.
1173:          *
1174:          * @author jfaltermeier
1175:          *
1176:          */
1177:         private class TreeMasterDetailLabelProvider extends AdapterFactoryLabelProvider {
1178:
1179:                 TreeMasterDetailLabelProvider(AdapterFactory adapterFactory) {
1180:                         super(adapterFactory);
1181:                 }
1182:
1183:                 @Override
1184:                 public Image getImage(Object object) {
1185:                         final Image image = super.getImage(object);
1186:                         if (!EObject.class.isInstance(object)) {
1187:                                 return image;
1188:                         }
1189:                         return getValidationOverlay(image, (EObject) object);
1190:                 }
1191:
1192:                 protected Image getValidationOverlay(Image image, final EObject object) {
1193:                         // final Integer severity = validationResultCacheTree.getCachedValue(object);
1194:                         final VDiagnostic vDiagnostic = getVElement().getDiagnostic();
1195:                         int highestSeverity = Diagnostic.OK;
1196:                         if (vDiagnostic != null) {
1197:                                 for (final Diagnostic diagnostic : vDiagnostic.getDiagnostics(object)) {
1198:                                         if (diagnostic.getSeverity() > highestSeverity) {
1199:                                                 highestSeverity = diagnostic.getSeverity();
1200:                                         }
1201:                                 }
1202:                         }
1203:                         final ImageDescriptor overlay = SWTValidationHelper.INSTANCE.getValidationOverlayDescriptor(highestSeverity,
1204:                                 getVElement(), getViewModelContext());
1205:                         if (overlay == null) {
1206:                                 return image;
1207:                         }
1208:                         final OverlayImageDescriptor imageDescriptor = new OverlayImageDescriptor(image, overlay,
1209:                                 OverlayImageDescriptor.LOWER_RIGHT);
1210:                         final Image resultImage = imageDescriptor.createImage();
1211:                         return resultImage;
1212:                 }
1213:
1214:         }
1215:
1216:         @Override
1217:         protected void applyEnable() {
1218:                 // Re-select the current selection to enforce re-rendering the detail.
1219:                 treeViewer.setSelection(new StructuredSelection(treeViewer.getStructuredSelection().getFirstElement()));
1220:         }
1221:
1222:         /**
1223:          * {@inheritDoc}
1224:          *
1225:          * @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#applyValidation()
1226:          * @since 1.14
1227:          */
1228:         @Override
1229:         protected void applyValidation(final VDiagnostic oldDia, final VDiagnostic newDia) {
1230:                 super.applyValidation();
1231:
1232:                 if (treeViewer == null) {
1233:                         return;
1234:                 }
1235:                 Display.getDefault().asyncExec(new Runnable() {
1236:
1237:                         @Override
1238:                         public void run() {
1239:                                 if (treeViewer.getTree().isDisposed()) {
1240:                                         return;
1241:                                 }
1242:                                 updateTree(oldDia, newDia);
1243:                         }
1244:                 });
1245:         }
1246:
1247:         private void updateTree(VDiagnostic oldDia, VDiagnostic newDia) {
1248:                 final List<Object> diff = new ArrayList<Object>();
1249:                 if (newDia != null) {
1250:                         diff.addAll(newDia.getDiagnostics());
1251:                 }
1252:                 if (oldDia != null) {
1253:                         diff.removeAll(oldDia.getDiagnostics());
1254:                 }
1255:                 final List<Object> diff2 = new ArrayList<Object>();
1256:                 if (oldDia != null) {
1257:                         diff2.addAll(oldDia.getDiagnostics());
1258:                 }
1259:                 if (newDia != null) {
1260:                         diff2.removeAll(newDia.getDiagnostics());
1261:                 }
1262:                 diff.addAll(diff2);
1263:                 final Set<Object> toUpdate = new LinkedHashSet<Object>();
1264:                 final ITreeContentProvider provider = ITreeContentProvider.class.cast(treeViewer.getContentProvider());
1265:                 for (final Object o : diff) {
1266:                         final EObject toAdd = (EObject) Diagnostic.class.cast(o).getData().get(0);
1267:                         toUpdate.add(toAdd);
1268:
1269:                         Object parent = provider.getParent(toAdd);
1270:                         while (EObject.class.isInstance(parent)) {
1271:                                 toUpdate.add(parent);
1272:                                 parent = provider.getParent(parent);
1273:                         }
1274:                 }
1275:                 if (toUpdate.isEmpty() && !(oldDia.getDiagnostics().isEmpty() && newDia.getDiagnostics().isEmpty())) {
1276:                         treeViewer.refresh();
1277:                         return;
1278:                 }
1279:                 treeViewer.update(toUpdate.toArray(), null);
1280:         }
1281:
1282:         /**
1283:          * reference service for the detail pane of a tree master detail view.
1284:          * It delegates calls to a root reference service, but opens new element in the detail pane.
1285:          *
1286:          * @author Jonas
1287:          *
1288:          */
1289:         private class TreeMasterDetailReferenceService implements ReferenceService {
1290:
1291:                 private final ReferenceService delegate;
1292:
1293:                 TreeMasterDetailReferenceService(ReferenceService delegate) {
1294:                         this.delegate = delegate;
1295:                 }
1296:
1297:                 /**
1298:                  * {@inheritDoc}
1299:                  *
1300:                  * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#instantiate(org.eclipse.emf.ecp.view.spi.context.ViewModelContext)
1301:                  */
1302:                 @Override
1303:                 public void instantiate(ViewModelContext context) {
1304:                         // no op
1305:                 }
1306:
1307:                 /**
1308:                  * {@inheritDoc}
1309:                  *
1310:                  * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#dispose()
1311:                  */
1312:                 @Override
1313:                 public void dispose() {
1314:                         // no op
1315:                 }
1316:
1317:                 /**
1318:                  * {@inheritDoc}
1319:                  *
1320:                  * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#getPriority()
1321:                  */
1322:                 @Override
1323:                 public int getPriority() {
1324:                         if (delegate == null) {
1325:                                 return 0;
1326:                         }
1327:                         return delegate.getPriority() - 1;
1328:                 }
1329:
1330:                 /**
1331:                  * {@inheritDoc}
1332:                  *
1333:                  * @see org.eclipse.emf.ecp.edit.spi.ReferenceService#addNewModelElements(org.eclipse.emf.ecore.EObject,
1334:                  * org.eclipse.emf.ecore.EReference)
1335:                  */
1336:                 @Override
1337:                 public void addNewModelElements(EObject eObject, EReference eReference) {
1338:                         addNewModelElements(eObject, eReference, true);
1339:                 }
1340:
1341:                 /**
1342:                  * {@inheritDoc}
1343:                  *
1344:                  * @see org.eclipse.emf.ecp.edit.spi.ReferenceService#addNewModelElements(org.eclipse.emf.ecore.EObject,
1345:                  * org.eclipse.emf.ecore.EReference)
1346:                  */
1347:                 @Override
1348:                 public Optional<EObject> addNewModelElements(EObject eObject, EReference eReference, boolean openInNewContext) {
1349:                         if (delegate == null) {
1350:                                 return Optional.empty();
1351:                         }
1352:                         return delegate.addNewModelElements(eObject, eReference, openInNewContext);
1353:                 }
1354:
1355:                 /**
1356:                  * {@inheritDoc}
1357:                  *
1358:                  * @see org.eclipse.emf.ecp.edit.spi.ReferenceService#addExistingModelElements(org.eclipse.emf.ecore.EObject,
1359:                  * org.eclipse.emf.ecore.EReference)
1360:                  */
1361:                 @Override
1362:                 public void addExistingModelElements(EObject eObject, EReference eReference) {
1363:                         if (delegate == null) {
1364:                                 return;
1365:                         }
1366:                         delegate.addExistingModelElements(eObject, eReference);
1367:                 }
1368:
1369:                 /**
1370:                  * {@inheritDoc}
1371:                  *
1372:                  * @see org.eclipse.emf.ecp.edit.spi.ReferenceService#openInNewContext(org.eclipse.emf.ecore.EObject)
1373:                  */
1374:                 @Override
1375:                 public void openInNewContext(EObject eObject) {
1376:                         treeViewer.setSelection(new StructuredSelection(eObject), true);
1377:                         final ISelection selection = treeViewer.getSelection();
1378:                         if (!selection.isEmpty()) {
1379:                                 return;
1380:                         }
1381:                         if (delegate == null) {
1382:                                 return;
1383:                         }
1384:                         delegate.openInNewContext(eObject);
1385:                 }
1386:         }
1387:
1388: }