Skip to content

Package: MultiReferenceSWTRenderer$6

MultiReferenceSWTRenderer$6

nameinstructionbranchcomplexitylinemethod
widgetSelected(SelectionEvent)
M: 0 C: 25
100%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 6
100%
M: 0 C: 1
100%
{...}
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2020 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: * Eugen Neufeld - initial API and implementation
13: * Lucas Koehler - use data binding services
14: * Martin Fleck - bug 487101
15: * Christian W. Damus - bugs 527736, 548592, 552385, 559267
16: ******************************************************************************/
17: package org.eclipse.emf.ecp.view.internal.control.multireference;
18:
19: import java.util.Collection;
20: import java.util.Collections;
21: import java.util.List;
22: import java.util.Optional;
23:
24: import javax.inject.Inject;
25:
26: import org.eclipse.core.databinding.observable.IObserving;
27: import org.eclipse.core.databinding.observable.list.IObservableList;
28: import org.eclipse.core.databinding.observable.value.IObservableValue;
29: import org.eclipse.emf.common.command.Command;
30: import org.eclipse.emf.common.notify.AdapterFactory;
31: import org.eclipse.emf.common.util.EList;
32: import org.eclipse.emf.databinding.EMFDataBindingContext;
33: import org.eclipse.emf.ecore.EClassifier;
34: import org.eclipse.emf.ecore.EObject;
35: import org.eclipse.emf.ecore.EReference;
36: import org.eclipse.emf.ecore.EStructuralFeature;
37: import org.eclipse.emf.ecp.edit.internal.swt.controls.TableViewerColumnBuilder;
38: import org.eclipse.emf.ecp.edit.spi.ConditionalDeleteService;
39: import org.eclipse.emf.ecp.edit.spi.DeleteService;
40: import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
41: import org.eclipse.emf.ecp.edit.spi.ReferenceService;
42: import org.eclipse.emf.ecp.view.model.common.edit.provider.CustomReflectiveItemProviderAdapterFactory;
43: import org.eclipse.emf.ecp.view.model.common.util.RendererUtil;
44: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
45: import org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer;
46: import org.eclipse.emf.ecp.view.spi.model.VControl;
47: import org.eclipse.emf.ecp.view.spi.provider.ECPTooltipModifierHelper;
48: import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
49: import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
50: import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
51: import org.eclipse.emf.ecp.view.spi.util.swt.ImageRegistryService;
52: import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
53: import org.eclipse.emf.ecp.view.template.style.reference.model.VTReferenceFactory;
54: import org.eclipse.emf.ecp.view.template.style.reference.model.VTReferenceStyleProperty;
55: import org.eclipse.emf.ecp.view.template.style.tableStyleProperty.model.RenderMode;
56: import org.eclipse.emf.ecp.view.template.style.tableStyleProperty.model.VTTableStyleProperty;
57: import org.eclipse.emf.ecp.view.template.style.tableStyleProperty.model.VTTableStylePropertyFactory;
58: import org.eclipse.emf.edit.command.MoveCommand;
59: import org.eclipse.emf.edit.command.RemoveCommand;
60: import org.eclipse.emf.edit.domain.EditingDomain;
61: import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
62: import org.eclipse.emf.edit.provider.IDisposable;
63: import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
64: import org.eclipse.emfforms.spi.common.BundleResolver;
65: import org.eclipse.emfforms.spi.common.BundleResolver.NoBundleFoundException;
66: import org.eclipse.emfforms.spi.common.BundleResolverFactory;
67: import org.eclipse.emfforms.spi.common.report.AbstractReport;
68: import org.eclipse.emfforms.spi.common.report.ReportService;
69: import org.eclipse.emfforms.spi.common.sort.NumberAwareStringComparator;
70: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
71: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
72: import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
73: import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
74: import org.eclipse.emfforms.spi.core.services.label.NoLabelFoundException;
75: import org.eclipse.emfforms.spi.localization.EMFFormsLocalizationService;
76: import org.eclipse.emfforms.spi.localization.LocalizationServiceHelper;
77: import org.eclipse.emfforms.spi.swt.core.SWTDataElementIdHelper;
78: import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
79: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
80: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
81: import org.eclipse.emfforms.spi.swt.core.ui.ObjectViewerComparator;
82: import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
83: import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
84: import org.eclipse.jface.layout.GridDataFactory;
85: import org.eclipse.jface.layout.GridLayoutFactory;
86: import org.eclipse.jface.layout.TableColumnLayout;
87: import org.eclipse.jface.viewers.ColumnViewerEditor;
88: import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
89: import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
90: import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
91: import org.eclipse.jface.viewers.ColumnWeightData;
92: import org.eclipse.jface.viewers.DoubleClickEvent;
93: import org.eclipse.jface.viewers.IDoubleClickListener;
94: import org.eclipse.jface.viewers.ILabelProvider;
95: import org.eclipse.jface.viewers.ISelection;
96: import org.eclipse.jface.viewers.ISelectionChangedListener;
97: import org.eclipse.jface.viewers.IStructuredSelection;
98: import org.eclipse.jface.viewers.SelectionChangedEvent;
99: import org.eclipse.jface.viewers.StructuredSelection;
100: import org.eclipse.jface.viewers.TableViewer;
101: import org.eclipse.jface.viewers.TableViewerColumn;
102: import org.eclipse.jface.viewers.TableViewerEditor;
103: import org.eclipse.osgi.util.NLS;
104: import org.eclipse.swt.SWT;
105: import org.eclipse.swt.events.SelectionAdapter;
106: import org.eclipse.swt.events.SelectionEvent;
107: import org.eclipse.swt.graphics.Image;
108: import org.eclipse.swt.layout.GridLayout;
109: import org.eclipse.swt.widgets.Button;
110: import org.eclipse.swt.widgets.Composite;
111: import org.eclipse.swt.widgets.Control;
112: import org.eclipse.swt.widgets.Display;
113: import org.eclipse.swt.widgets.Label;
114: import org.eclipse.swt.widgets.TableColumn;
115: import org.osgi.framework.Bundle;
116: import org.osgi.framework.FrameworkUtil;
117:
118: /**
119: * Renderer for MultiReferenceControl.
120: *
121: * @author Eugen Neufeld
122: *
123: */
124: @SuppressWarnings("restriction")
125: public class MultiReferenceSWTRenderer extends AbstractControlSWTRenderer<VControl> {
126:
127:         private static final String ICON_ADD_EXISTING = "icons/link.png"; //$NON-NLS-1$
128:         private static final String ICON_ADD_NEW = "icons/link_add.png"; //$NON-NLS-1$
129:         private static final String ICON_DELETE = "icons/unset_reference.png"; //$NON-NLS-1$
130:         private static final String ICON_MOVE_DOWN = "icons/move_down.png"; //$NON-NLS-1$
131:         private static final String ICON_MOVE_UP = "icons/move_up.png"; //$NON-NLS-1$
132:
133:         private final BundleResolver bundleResolver = BundleResolverFactory.createBundleResolver();
134:         private final ImageRegistryService imageRegistryService;
135:         private EMFFormsLocalizationService l10n;
136:
137:         /**
138:          * The {@link EObject} that contains the elements rendered in this multi reference.
139:          */
140:         private Optional<EObject> cachedContainer;
141:
142:         /**
143:          * The structural feature presented in this multi reference control.
144:          */
145:         private Optional<EStructuralFeature> cachedFeature;
146:
147:         /**
148:          * A user-presentable display name for the reference, used in tool-tips.
149:          */
150:         private String referenceDisplayName;
151:
152:         /**
153:          * Legacy constructor, initializing me without a localization service. When needed,
154:          * I will attempt to get it from the {@code viewContext}.
155:          *
156:          * @param vElement the view model element to be rendered
157:          * @param viewContext the view context
158:          * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
159:          * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
160:          * @param reportService The {@link ReportService}
161:          * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
162:          * @param imageRegistryService The {@link ImageRegistryService}
163:          */
164:         public MultiReferenceSWTRenderer(VControl vElement, ViewModelContext viewContext, ReportService reportService,
165:                 EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
166:                 VTViewTemplateProvider vtViewTemplateProvider, ImageRegistryService imageRegistryService) {
167:                 this(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider,
168:                         imageRegistryService, null);
169:         }
170:
171:         /**
172:          * Complete constructor, supplying all dependencies.
173:          *
174:          * @param vElement the view model element to be rendered
175:          * @param viewContext the view context
176:          * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
177:          * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
178:          * @param reportService The {@link ReportService}
179:          * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
180:          * @param imageRegistryService The {@link ImageRegistryService}
181:          * @param localizationService the localization service
182:          *
183:          * @since 1.16
184:          */
185:         // BEGIN COMPLEX CODE
186:         @Inject
187:         public MultiReferenceSWTRenderer(VControl vElement, ViewModelContext viewContext, ReportService reportService,
188:                 EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
189:                 VTViewTemplateProvider vtViewTemplateProvider, ImageRegistryService imageRegistryService,
190:                 EMFFormsLocalizationService localizationService) {
191:                 // END COMPLEX CODE
192:                 super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider);
193:                 this.imageRegistryService = imageRegistryService;
194:                 l10n = localizationService;
195:                 viewModelDBC = new EMFDataBindingContext();
196:         }
197:
198:         private Label validationIcon;
199:         private ILabelProvider labelProvider;
200:         private AdapterFactory adapterFactory;
201:         private TableViewer tableViewer;
202:         private final EMFDataBindingContext viewModelDBC;
203:         private IObservableList<?> tableViewerInputList;
204:         private Button btnAddExisting;
205:         private Button btnAddNew;
206:         private Button btnDelete;
207:         private Button btnMoveUp;
208:         private Button btnMoveDown;
209:         private SWTGridDescription rendererGridDescription;
210:
211:         @Override
212:         public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
213:                 if (rendererGridDescription == null) {
214:                         // create special grid for compact mode
215:                         if (getTableStyleProperty().getRenderMode() == RenderMode.COMPACT_VERTICALLY) {
216:                                 rendererGridDescription = GridDescriptionFactory.INSTANCE.createCompactGrid(false, true, this);
217:                         } else {
218:                                 rendererGridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
219:                         }
220:
221:                 }
222:                 return rendererGridDescription;
223:         }
224:
225:         /**
226:          * Updates the table viewer's input observable list. Call this method in sub classes if you detect a change to the
227:          * domain model which requires the re-resolvement of the input list.
228:          * <p>
229:          * <strong>Note:</strong> This method only updates the input list but does not trigger button enablement etc.
230:          *
231:          * @return returns the new input list; might be empty but never <code>null</code>
232:          * @throws DatabindingFailedException if the new list cannot be resolved
233:          */
234:         protected final IObservableList<?> updateTableViewerInputList() throws DatabindingFailedException {
235:                 // Rebind table content
236:                 if (tableViewerInputList != null) {
237:                         tableViewerInputList.dispose();
238:                 }
239:                 tableViewerInputList = getReferencedElementsList();
240:                 tableViewer.setInput(tableViewerInputList);
241:                 return tableViewerInputList;
242:         }
243:
244:         /**
245:          * Returns true if the 'AddExisting' button is shown, false otherwise.
246:          *
247:          * @return true if the 'AddExisting' button is shown, false otherwise
248:          */
249:         protected boolean showAddExistingButton() {
250:                 EReference eReference = null;
251:                 try {
252:                         eReference = (EReference) getEStructuralFeature();
253:                 } catch (final DatabindingFailedException ex) {
254:                         getReportService().report(new AbstractReport(ex));
255:                 }
256:
257:                 if (eReference != null) {
258:                         // Always show the add existing button for cross references
259:                         if (!eReference.isContainment()) {
260:                                 return true;
261:                         }
262:
263:                         VTReferenceStyleProperty referenceStyle = RendererUtil.getStyleProperty(
264:                                 getVTViewTemplateProvider(), getVElement(), getViewModelContext(), VTReferenceStyleProperty.class);
265:                         if (referenceStyle == null) {
266:                                 referenceStyle = getDefaultReferenceStyle();
267:                         }
268:                         return referenceStyle.isShowLinkButtonForContainmentReferences();
269:                 }
270:
271:                 return false;
272:         }
273:
274:         /**
275:          * Returns true if the 'AddNew' button is shown, false otherwise.
276:          *
277:          * @return true if the 'AddNew' button is shown, false otherwise
278:          */
279:         protected boolean showAddNewButton() {
280:                 EReference eReference = null;
281:                 try {
282:                         eReference = (EReference) getModelValue().getValueType();
283:                 } catch (final DatabindingFailedException ex) {
284:                         getReportService().report(new AbstractReport(ex));
285:                 }
286:
287:                 if (eReference != null) {
288:                         if (eReference.isContainment()) {
289:                                 return true;
290:                         }
291:
292:                         VTReferenceStyleProperty referenceStyle = RendererUtil.getStyleProperty(
293:                                 getVTViewTemplateProvider(), getVElement(), getViewModelContext(), VTReferenceStyleProperty.class);
294:                         if (referenceStyle == null) {
295:                                 referenceStyle = getDefaultReferenceStyle();
296:                         }
297:                         return referenceStyle.isShowCreateAndLinkButtonForCrossReferences();
298:                 }
299:
300:                 return false;
301:         }
302:
303:         /**
304:          * Creates and returns a default version of a {@link VTReferenceStyleProperty}.
305:          *
306:          * @return The default {@link VTReferenceStyleProperty}
307:          */
308:         protected VTReferenceStyleProperty getDefaultReferenceStyle() {
309:                 return VTReferenceFactory.eINSTANCE.createReferenceStyleProperty();
310:         }
311:
312:         /**
313:          * Returns true if the 'Delete' button is shown, false otherwise.
314:          *
315:          * @return true if the 'Delete' button is shown, false otherwise
316:          */
317:         protected boolean showDeleteButton() {
318:                 return true;
319:         }
320:
321:         /**
322:          * Returns true if the 'MoveUp' button is shown, false otherwise.
323:          * Returning true will disable any sorting behavior.
324:          *
325:          * @return true if the 'MoveUp' button is shown, false otherwise
326:          */
327:         protected boolean showMoveUpButton() {
328:                 return false;
329:         }
330:
331:         /**
332:          * Returns true if the 'MoveDown' button is shown, false otherwise.
333:          * Returning true will disable any sorting behavior.
334:          *
335:          * @return true if the 'MoveDown' button is shown, false otherwise
336:          */
337:         protected boolean showMoveDownButton() {
338:                 return false;
339:         }
340:
341:         /**
342:          * Returns the observed {@link EStructuralFeature}.
343:          *
344:          * @return the observed {@link EStructuralFeature}.
345:          * @throws DatabindingFailedException when databinding fails.
346:          */
347:         protected EStructuralFeature getEStructuralFeature() throws DatabindingFailedException {
348:                 return (EStructuralFeature) getModelValue().getValueType();
349:         }
350:
351:         /**
352:          * Returns the {@link EObject} that contains the elements rendered in this multi reference.
353:          *
354:          * @return The {@link EObject} containing the elements rendered in this multi reference or nothing if the container
355:          * couldn't be computed.
356:          */
357:         protected Optional<EObject> getContainer() {
358:                 if (cachedContainer == null || !cachedContainer.isPresent()) {
359:                         EObject eObject = null;
360:                         try {
361:                                 eObject = (EObject) IObserving.class.cast(getModelValue()).getObserved();
362:                         } catch (final DatabindingFailedException ex) {
363:                                 getReportService().report(new DatabindingFailedReport(ex));
364:                         }
365:                         cachedContainer = Optional.ofNullable(eObject);
366:                 }
367:                 return cachedContainer;
368:         }
369:
370:         /**
371:          * Creates the default {@link VTTableStyleProperty}.
372:          *
373:          * @return the default {@link VTTableStyleProperty}
374:          * @since 1.14
375:          */
376:         protected VTTableStyleProperty createDefaultTableStyleProperty() {
377:                 return VTTableStylePropertyFactory.eINSTANCE.createTableStyleProperty();
378:         }
379:
380:         /**
381:          * Returns the {@link VTTableStyleProperty}.
382:          *
383:          * @return the {@link VTTableStyleProperty}
384:          * @since 1.14
385:          */
386:         protected VTTableStyleProperty getTableStyleProperty() {
387:                 VTTableStyleProperty styleProperty = RendererUtil.getStyleProperty(getVTViewTemplateProvider(), getVElement(),
388:                         getViewModelContext(), VTTableStyleProperty.class);
389:                 if (styleProperty == null) {
390:                         styleProperty = createDefaultTableStyleProperty();
391:                 }
392:                 return styleProperty;
393:         }
394:
395:         @Override
396:         protected Control renderControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
397:                 NoPropertyDescriptorFoundExeption {
398:                 if (rendererGridDescription.getColumns() == 1) {
399:                         // Default
400:                         return renderMultiReferenceControl(cell, parent);
401:                 }
402:                 // Compact: render icon
403:                 if (cell.getColumn() == 0 && rendererGridDescription.getColumns() > 1) {
404:                         validationIcon = createValidationIcon(parent);
405:                         return validationIcon;
406:                 }
407:                 // Compact: render table and buttons next to each other
408:                 final Composite composite = new Composite(parent, SWT.NONE);
409:                 composite.setBackgroundMode(SWT.INHERIT_FORCE);
410:
411:                 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite);
412:                 final Control multiRefComposite = renderMultiReferenceControl(cell, composite);
413:                 GridDataFactory.fillDefaults().grab(true, true).applyTo(multiRefComposite);
414:                 try {
415:                         final Composite buttonComposite = createButtonComposite(composite);
416:                         GridDataFactory.fillDefaults().align(SWT.END, SWT.BEGINNING).applyTo(buttonComposite);
417:                 } catch (final DatabindingFailedException ex) {
418:                         getReportService().report(new RenderingFailedReport(ex));
419:                         return createErrorLabel(composite, ex);
420:                 }
421:                 updateButtons();
422:                 return composite;
423:         }
424:
425:         /**
426:          * Renders the MultiReference Control.
427:          *
428:          * Renders the MultiReference control including validation and buttons when {@link RenderMode} is set to
429:          * {@link RenderMode#DEFAULT}. Only renders the
430:          * MultiReference control without validation and buttons when renderMode is set to
431:          * {@link RenderMode#COMPACT_VERTICALLY}.
432:          *
433:          * @param cell the {@link SWTGridCell}.
434:          * @param parent the {@link Composite}.
435:          * @return the rendered {@link Control}
436:          * @throws NoRendererFoundException the {@link NoRendererFoundException}.
437:          * @throws NoPropertyDescriptorFoundExeption the {@link NoPropertyDescriptorFoundExeption}.
438:          * @since 1.14
439:          */
440:         protected Control renderMultiReferenceControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
441:                 NoPropertyDescriptorFoundExeption {
442:                 if (cell.getRow() != 0 || cell.getRenderer() != this) {
443:                         throw new IllegalArgumentException("Wrong parameter passed!"); //$NON-NLS-1$
444:                 }
445:
446:                 final Composite composite = new Composite(parent, SWT.NONE);
447:                 composite.setBackgroundMode(SWT.INHERIT_FORCE);
448:
449:                 if (getTableStyleProperty().getRenderMode() == RenderMode.COMPACT_VERTICALLY) {
450:                         // avoid the default margins set by new GridLayout()
451:                         GridLayoutFactory.fillDefaults().applyTo(composite);
452:                 } else {
453:                         composite.setLayout(new GridLayout(1, false));
454:                         try {
455:                                 createTitleComposite(composite);
456:                         } catch (final DatabindingFailedException ex) {
457:                                 getReportService().report(new RenderingFailedReport(ex));
458:                                 return createErrorLabel(parent, ex);
459:                         }
460:                 }
461:
462:                 adapterFactory = createAdapterFactory();
463:                 labelProvider = createLabelProvider();
464:
465:                 final Composite controlComposite = createControlComposite(composite);
466:                 try {
467:                         createContent(controlComposite);
468:                 } catch (final DatabindingFailedException ex) {
469:                         getReportService().report(new RenderingFailedReport(ex));
470:                         return createErrorLabel(parent, ex);
471:                 }
472:
473:                 if (getTableStyleProperty().getRenderMode() == RenderMode.DEFAULT) {
474:                         initButtons();
475:                         updateButtons();
476:                 }
477:
478:                 SWTDataElementIdHelper.setElementIdDataForVControl(composite, getVElement(), getViewModelContext());
479:
480:                 return composite;
481:         }
482:
483:         private void initButtons() {
484:                 getTableViewer().addSelectionChangedListener(new ISelectionChangedListener() {
485:                         @Override
486:                         public void selectionChanged(SelectionChangedEvent event) {
487:                                 updateButtonEnabling();
488:                         }
489:                 });
490:         }
491:
492:         /**
493:          * Creates the composite which will be the parent for the table.
494:          *
495:          * @param composite
496:          * the parent composite
497:          * @return the table composite
498:          */
499:         protected Composite createControlComposite(final Composite composite) {
500:                 final Composite controlComposite = new Composite(composite, SWT.NONE);
501:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL)
502:                         .hint(1, getTableHeightHint())
503:                         .applyTo(controlComposite);
504:                 GridLayoutFactory.fillDefaults().applyTo(controlComposite);
505:                 return controlComposite;
506:         }
507:
508:         /**
509:          * Returns the height for the table that will be created.
510:          *
511:          * @return the height hint
512:          */
513:         protected int getTableHeightHint() {
514:                 return 300;
515:         }
516:
517:         /**
518:          * Gives access to the tableViewer used to display the attributes.
519:          *
520:          * @return the viewer
521:          */
522:         protected TableViewer getTableViewer() {
523:                 return tableViewer;
524:         }
525:
526:         /**
527:          * Creates an error label for the given {@link Exception}.
528:          *
529:          * @param parent The parent of the {@link Label}
530:          * @param ex The {@link Exception} causing the error
531:          * @return The error {@link Label}
532:          */
533:         protected Control createErrorLabel(Composite parent, final Exception ex) {
534:                 final Label errorLabel = new Label(parent, SWT.NONE);
535:                 errorLabel.setText(ex.getMessage());
536:                 return errorLabel;
537:         }
538:
539:         /**
540:          * Creates a new {@link AdapterFactory}.
541:          *
542:          * @return the newly created {@link AdapterFactory}.
543:          */
544:         protected AdapterFactory createAdapterFactory() {
545:                 return new ComposedAdapterFactory(new AdapterFactory[] {
546:                         new CustomReflectiveItemProviderAdapterFactory(),
547:                         new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
548:         }
549:
550:         /**
551:          * Returns the {@link AdapterFactory} used by this renderer. To customize the used {@link AdapterFactory} override
552:          * {@link #createAdapterFactory()}.
553:          *
554:          * @return The {@link AdapterFactory} used by this renderer
555:          * @see #createAdapterFactory()
556:          */
557:         protected final AdapterFactory getAdapterFactory() {
558:                 return adapterFactory;
559:         }
560:
561:         /**
562:          * Creates a new {@link ILabelProvider} for the table viewer.
563:          *
564:          * @return the newly created {@link ILabelProvider}.
565:          */
566:         protected ILabelProvider createLabelProvider() {
567:                 final AdapterFactoryLabelProvider labelProvider = new AdapterFactoryLabelProvider(adapterFactory);
568:                 labelProvider.setFireLabelUpdateNotifications(true);
569:                 return labelProvider;
570:         }
571:
572:         /**
573:          * {@inheritDoc}
574:          *
575:          * @see org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer#dispose()
576:          */
577:         @Override
578:         protected void dispose() {
579:                 if (IDisposable.class.isInstance(adapterFactory)) {
580:                         IDisposable.class.cast(adapterFactory).dispose();
581:                 }
582:                 labelProvider.dispose();
583:                 viewModelDBC.dispose();
584:                 super.dispose();
585:         }
586:
587:         /**
588:          * Creates a button that enables reordering the references by the given {@link EStructuralFeature}.
589:          *
590:          * @param parent The parent of the created {@link Button}
591:          * @param structuralFeature The {@link EStructuralFeature} which's references are moved up.
592:          * @return The newly created {@link Button}
593:          */
594:         protected Button createMoveUpButton(Composite parent, final EStructuralFeature structuralFeature) {
595:                 final Button btnMoveUp = new Button(parent, SWT.PUSH);
596:                 SWTDataElementIdHelper.setElementIdDataWithSubId(btnMoveUp, getVElement(), "up", getViewModelContext()); //$NON-NLS-1$
597:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnMoveUp);
598:                 btnMoveUp.setImage(getImage(ICON_MOVE_UP));
599:                 btnMoveUp.setToolTipText(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
600:                         MessageKeys.MultiReferenceSWTRenderer_moveUpTooltip));
601:                 btnMoveUp.addSelectionListener(new SelectionAdapter() {
602:
603:                         @Override
604:                         public void widgetSelected(SelectionEvent e) {
605:                                 super.widgetSelected(e);
606:                                 final Optional<? extends EObject> container = getContainer();
607:                                 if (container.isPresent()) {
608:                                         handleMoveUp(tableViewer, container.get(), structuralFeature);
609:                                         updateButtons();
610:                                 }
611:                         }
612:
613:                 });
614:                 return btnMoveUp;
615:         }
616:
617:         /**
618:          * Creates a button that enables reordering the references by the given {@link EStructuralFeature}.
619:          *
620:          * @param parent The parent of the created {@link Button}
621:          * @param structuralFeature The {@link EStructuralFeature} which's references are moved down.
622:          * @return The newly created {@link Button}
623:          */
624:         protected Button createMoveDownButton(Composite parent, final EStructuralFeature structuralFeature) {
625:                 final Button btnMoveDown = new Button(parent, SWT.PUSH);
626:                 SWTDataElementIdHelper.setElementIdDataWithSubId(btnMoveDown, getVElement(), "down", getViewModelContext()); //$NON-NLS-1$
627:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnMoveDown);
628:                 btnMoveDown.setImage(getImage(ICON_MOVE_DOWN));
629:                 btnMoveDown.setToolTipText(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
630:                         MessageKeys.MultiReferenceSWTRenderer_moveDownTooltip));
631:                 btnMoveDown.addSelectionListener(new SelectionAdapter() {
632:
633:                         @Override
634:                         public void widgetSelected(SelectionEvent e) {
635:                                 super.widgetSelected(e);
636:                                 final Optional<EObject> container = getContainer();
637:                                 if (container.isPresent()) {
638:                                         handleMoveDown(tableViewer, container.get(), structuralFeature);
639:                                         updateButtons();
640:                                 }
641:                         }
642:
643:                 });
644:                 return btnMoveDown;
645:         }
646:
647:         /**
648:          * Creates a button that enables the addition of existing references to the given {@link EStructuralFeature}.
649:          *
650:          * @param parent The parent of the created {@link Button}
651:          * @param structuralFeature The {@link EStructuralFeature} to which references are added
652:          * @return The newly created {@link Button}
653:          */
654:         protected Button createAddExistingButton(Composite parent, final EStructuralFeature structuralFeature) {
655:                 final Button btnAddExisting = new Button(parent, SWT.PUSH);
656:                 SWTDataElementIdHelper.setElementIdDataWithSubId(btnAddExisting, getVElement(), "link", getViewModelContext()); //$NON-NLS-1$
657:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnAddExisting);
658:                 btnAddExisting.setImage(getImage(ICON_ADD_EXISTING));
659:                 btnAddExisting.setToolTipText(NLS.bind(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
660:                         MessageKeys.MultiReferenceSWTRenderer_addExistingTooltip), getReferenceDisplayName()));
661:                 btnAddExisting.addSelectionListener(new SelectionAdapter() {
662:
663:                         @Override
664:                         public void widgetSelected(SelectionEvent e) {
665:                                 super.widgetSelected(e);
666:                                 final Optional<EObject> container = getContainer();
667:                                 if (container.isPresent()) {
668:                                         handleAddExisting(tableViewer, container.get(), structuralFeature);
669:                                         updateButtons();
670:                                 }
671:                         }
672:
673:                 });
674:                 return btnAddExisting;
675:         }
676:
677:         /**
678:          * Creates a button that enables the addition of newly created references to the given {@link EStructuralFeature}.
679:          *
680:          * @param parent The parent of the created {@link Button}
681:          * @param structuralFeature The {@link EStructuralFeature} to which references are added
682:          * @return The newly created {@link Button}
683:          */
684:         protected Button createAddNewButton(Composite parent, final EStructuralFeature structuralFeature) {
685:                 final Button btnAddNew = new Button(parent, SWT.PUSH);
686:                 SWTDataElementIdHelper.setElementIdDataWithSubId(btnAddNew, getVElement(), "add", getViewModelContext()); //$NON-NLS-1$
687:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnAddNew);
688:                 btnAddNew.setImage(getImage(ICON_ADD_NEW));
689:                 btnAddNew.setToolTipText(NLS.bind(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
690:                         MessageKeys.MultiReferenceSWTRenderer_addNewTooltip), getReferenceDisplayName()));
691:                 btnAddNew.addSelectionListener(new SelectionAdapter() {
692:
693:                         @Override
694:                         public void widgetSelected(SelectionEvent e) {
695:                                 super.widgetSelected(e);
696:                                 final Optional<EObject> container = getContainer();
697:                                 if (container.isPresent()) {
698:                                         handleAddNew(tableViewer, container.get(), structuralFeature);
699:                                         updateButtons();
700:                                 }
701:                         }
702:
703:                 });
704:                 return btnAddNew;
705:         }
706:
707:         /**
708:          * Creates a button that enables the removal of existing references from the given {@link EStructuralFeature}.
709:          *
710:          * @param parent The parent of the newly created {@link Button}
711:          * @param structuralFeature The {@link EStructuralFeature} from which references are removed
712:          * @return The newly created {@link Button}
713:          */
714:         protected Button createDeleteButton(Composite parent, final EStructuralFeature structuralFeature) {
715:                 final Button btnDelete = new Button(parent, SWT.PUSH);
716:                 SWTDataElementIdHelper.setElementIdDataWithSubId(btnDelete, getVElement(), "delete", getViewModelContext()); //$NON-NLS-1$
717:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnDelete);
718:                 btnDelete.setImage(getImage(ICON_DELETE));
719:                 btnDelete.setToolTipText(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
720:                         MessageKeys.MultiReferenceSWTRenderer_deleteTooltip));
721:                 btnDelete.addSelectionListener(new SelectionAdapter() {
722:
723:                         @Override
724:                         public void widgetSelected(SelectionEvent e) {
725:                                 super.widgetSelected(e);
726:                                 final Optional<EObject> container = getContainer();
727:•                                if (container.isPresent()) {
728:                                         handleDelete(tableViewer, container.get(), structuralFeature);
729:                                         updateButtons();
730:                                 }
731:                         }
732:                 });
733:                 return btnDelete;
734:         }
735:
736:         /**
737:          * Updates button visibility and enablement.
738:          */
739:         protected void updateButtons() {
740:                 updateButtonVisibility();
741:                 updateButtonEnabling();
742:         }
743:
744:         /**
745:          * Updates the enablement of 'addExisting', 'addNew', 'delete', 'moveUp' and 'moveDown' buttons according to the
746:          * bound input.
747:          */
748:         protected void updateButtonEnabling() {
749:                 final boolean isEnable = getContainer().isPresent() && getVElement().isEffectivelyEnabled();
750:                 final int listSize = tableViewerInputList != null ? tableViewerInputList.size() : 0;
751:                 final int selectionIndex = tableViewer != null ? tableViewer.getTable().getSelectionIndex() : -1;
752:
753:                 enableUpButton(isEnable, listSize, selectionIndex);
754:                 enableDownButton(isEnable, listSize, selectionIndex);
755:                 enableAddExistingButton(isEnable, listSize, selectionIndex);
756:                 enableAddNewButton(isEnable, listSize, selectionIndex);
757:                 enableDeleteButton(isEnable, listSize, selectionIndex);
758:         }
759:
760:         private void enableUpButton(boolean baseEnable, int listSize, int selectionIndex) {
761:                 if (btnMoveUp != null && showMoveUpButton()) {
762:                         final boolean enabled = baseEnable && listSize > 1 && selectionIndex > 0;
763:                         btnMoveUp.setEnabled(enabled);
764:                 }
765:         }
766:
767:         private void enableDownButton(boolean baseEnable, int listSize, int selectionIndex) {
768:                 if (btnMoveDown != null && showMoveDownButton()) {
769:                         final boolean enabled = baseEnable && listSize > 1 && selectionIndex != -1 && selectionIndex < listSize - 1;
770:                         btnMoveDown.setEnabled(enabled);
771:                 }
772:         }
773:
774:         private void enableAddExistingButton(boolean baseEnable, int listSize, int selectionIndex) {
775:                 if (btnAddExisting != null && showAddExistingButton()) {
776:                         btnAddExisting.setEnabled(baseEnable);
777:                 }
778:         }
779:
780:         private void enableAddNewButton(boolean baseEnable, int listSize, int selectionIndex) {
781:                 if (btnAddNew != null && showAddNewButton()) {
782:                         btnAddNew.setEnabled(baseEnable);
783:                 }
784:         }
785:
786:         private void enableDeleteButton(boolean baseEnable, int listSize, int selectionIndex) {
787:                 if (btnDelete != null && showDeleteButton()) {
788:                         btnDelete.setEnabled(baseEnable && listSize > 0 && selectionIndex != -1
789:                                 && getContainer().isPresent() && cachedFeature.isPresent()
790:                                 && ConditionalDeleteService.getDeleteService(getViewModelContext())
791:                                         .canRemove(getContainer().get(), cachedFeature.get(),
792:                                                 tableViewer.getStructuredSelection().toList()));
793:                 }
794:         }
795:
796:         /**
797:          * Updates the visibility of 'addExisting', 'addNew', 'delete', 'moveUp' and 'moveDown' buttons according to the
798:          * bound input.
799:          */
800:         protected void updateButtonVisibility() {
801:                 final boolean isVisible = !getVElement().isEffectivelyReadonly();
802:
803:                 if (btnMoveUp != null) {
804:                         btnMoveUp.setVisible(showMoveUpButton() && isVisible);
805:                 }
806:                 if (btnMoveDown != null) {
807:                         btnMoveDown.setVisible(showMoveDownButton() && isVisible);
808:                 }
809:                 if (btnAddExisting != null) {
810:                         btnAddExisting.setVisible(showAddExistingButton() && isVisible);
811:                 }
812:                 if (btnAddNew != null) {
813:                         btnAddNew.setVisible(showAddNewButton() && isVisible);
814:                 }
815:                 if (btnDelete != null) {
816:                         btnDelete.setVisible(showDeleteButton() && isVisible);
817:                 }
818:         }
819:
820:         /***
821:          * Adds a composite with the buttons 'AddExisting', 'AddNew' and 'Delete' to the given {@link Composite} if
822:          * necessary.
823:          *
824:          * @param parent The parent of the created {@link Composite}
825:          * @return the created Composite
826:          * @throws DatabindingFailedException thrown if the databinding could not be executed successfully
827:          */
828:         protected Composite createButtonComposite(Composite parent) throws DatabindingFailedException {
829:                 final Composite buttonComposite = new Composite(parent, SWT.NONE);
830:
831:                 final EStructuralFeature structuralFeature = getEStructuralFeature();
832:                 cachedFeature = Optional.ofNullable(structuralFeature);
833:
834:                 int nrButtons = 0;
835:
836:                 if (showMoveUpButton()) {
837:                         btnMoveUp = createMoveUpButton(buttonComposite, structuralFeature);
838:                         nrButtons++;
839:                 }
840:                 if (showMoveDownButton()) {
841:                         btnMoveDown = createMoveDownButton(buttonComposite, structuralFeature);
842:                         nrButtons++;
843:                 }
844:                 if (showAddExistingButton()) {
845:                         btnAddExisting = createAddExistingButton(buttonComposite, structuralFeature);
846:                         nrButtons++;
847:                 }
848:                 if (showAddNewButton()) {
849:                         btnAddNew = createAddNewButton(buttonComposite, structuralFeature);
850:                         nrButtons++;
851:                 }
852:                 if (showDeleteButton()) {
853:                         btnDelete = createDeleteButton(buttonComposite, structuralFeature);
854:                         nrButtons++;
855:                 }
856:
857:                 GridLayoutFactory.fillDefaults().numColumns(nrButtons).equalWidth(true).applyTo(buttonComposite);
858:                 return buttonComposite;
859:         }
860:
861:         /**
862:          * Creates a composite with a label, a validation icon and a button composite.
863:          *
864:          * @param parent The parent of the created {@link Composite}
865:          * @throws DatabindingFailedException thrown if the databinding could not be executed successfully
866:          */
867:         protected void createTitleComposite(Composite parent)
868:                 throws DatabindingFailedException {
869:                 final Composite titleComposite = new Composite(parent, SWT.NONE);
870:                 titleComposite.setBackgroundMode(SWT.INHERIT_FORCE);
871:                 GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING)
872:                         .applyTo(titleComposite);
873:                 GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(titleComposite);
874:
875:                 final Label filler = new Label(titleComposite, SWT.NONE);
876:                 GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(filler);
877:
878:                 // VALIDATION
879:                 // // set the size of the label to the size of the image
880:                 validationIcon = createValidationIcon(titleComposite);
881:                 GridDataFactory.fillDefaults().hint(16, 17).grab(false, false).applyTo(validationIcon);
882:
883:                 final Composite buttonComposite = createButtonComposite(titleComposite);
884:                 GridDataFactory.fillDefaults().grab(true, false).align(SWT.END, SWT.FILL)
885:                         .applyTo(buttonComposite);
886:         }
887:
888:         /**
889:          * Returns an {@link Image} from the image registry.
890:          *
891:          * @param path
892:          * the path to the image
893:          * @return the image
894:          */
895:         protected Image getImage(String path) {
896:                 return imageRegistryService.getImage(FrameworkUtil.getBundle(MultiReferenceSWTRenderer.class), path);
897:         }
898:
899:         /**
900:          * Obtains a user-presentable name for the reference that I edit, to be used for example
901:          * in button tool-tips.
902:          *
903:          * @return the reference display name
904:          * @since 1.16
905:          */
906:         protected String getReferenceDisplayName() {
907:                 if (referenceDisplayName == null) {
908:                         try {
909:                                 if (l10n == null) {
910:                                         // Maybe the view-model context has one
911:                                         l10n = getViewModelContext().getService(EMFFormsLocalizationService.class);
912:                                 }
913:
914:                                 final EStructuralFeature feature = getEStructuralFeature();
915:
916:                                 if (feature != null && l10n != null) {
917:                                         // Use type of the reference so that we don't get a plural (Ecore models typically
918:                                         // have plural names for many-valued references)
919:                                         final EClassifier type = feature.getEType();
920:                                         try {
921:                                                 final Bundle editBundle = bundleResolver.getEditBundle(type);
922:                                                 referenceDisplayName = l10n.getString(editBundle, String.format("_UI_%s_type", type.getName())); //$NON-NLS-1$
923:                                         } catch (final NoBundleFoundException ex) {
924:                                                 referenceDisplayName = type.getName();
925:                                         }
926:                                 }
927:                         } catch (final DatabindingFailedException e) {
928:                                 // We'll default it, below
929:                         }
930:
931:                         if (referenceDisplayName == null) {
932:                                 referenceDisplayName = LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
933:                                         MessageKeys.MultiReferenceSWTRenderer_defaultReferenceDisplayName);
934:                         }
935:                 }
936:
937:                 return referenceDisplayName;
938:         }
939:
940:         private void createContent(Composite composite) throws DatabindingFailedException {
941:                 tableViewer = new TableViewer(composite, SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION
942:                         | SWT.BORDER);
943:                 tableViewer.getTable().setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_multireference"); //$NON-NLS-1$
944:                 tableViewer.getTable().setHeaderVisible(true);
945:                 tableViewer.getTable().setLinesVisible(true);
946:
947:                 final ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(tableViewer) {
948:                         @Override
949:                         protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
950:                                 if (getVElement().isEffectivelyReadonly()) {
951:                                         return false;
952:                                 }
953:                                 return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
954:                                         || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION
955:                                         || event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR
956:                                         || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
957:                         }
958:                 };
959:
960:                 TableViewerEditor.create(tableViewer, null, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL
961:                         | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL
962:                         | ColumnViewerEditor.KEYBOARD_ACTIVATION);
963:                 ColumnViewerToolTipSupport.enableFor(tableViewer);
964:
965:                 final ObjectViewerComparator comparator = new ObjectViewerComparator(this::compare);
966:                 final boolean isMoveDisabled = isMoveDisabled();
967:                 if (isMoveDisabled) {
968:                         tableViewer.setComparator(comparator);
969:                 }
970:
971:                 final ObservableListContentProvider cp = new ObservableListContentProvider();
972:
973:                 final EMFFormsLabelProvider labelService = getEMFFormsLabelProvider();
974:
975:                 final TableViewerColumn column = TableViewerColumnBuilder
976:                         .create()
977:                         .setResizable(false)
978:                         .setMoveable(false)
979:                         .setStyle(SWT.NONE)
980:                         .build(tableViewer);
981:
982:                 final IObservableValue textObservableValue = WidgetProperties.text().observe(column.getColumn());
983:                 final IObservableValue tooltipObservableValue = WidgetProperties.tooltipText().observe(column.getColumn());
984:                 try {
985:                         viewModelDBC.bindValue(textObservableValue,
986:                                 labelService.getDisplayName(getVElement().getDomainModelReference(), getViewModelContext()
987:                                         .getDomainModel()));
988:
989:                         viewModelDBC.bindValue(tooltipObservableValue,
990:                                 labelService.getDescription(getVElement().getDomainModelReference(), getViewModelContext()
991:                                         .getDomainModel()));
992:                 } catch (final NoLabelFoundException e) {
993:                         // FIXME Expectations?
994:                         getReportService().report(new RenderingFailedReport(e));
995:                 }
996:
997:                 // only enable column sorting if move is disabled
998:                 if (isMoveDisabled) {
999:                         column.getColumn().addSelectionListener(
1000:                                 getSelectionAdapter(tableViewer, isMoveDisabled ? comparator : null, column.getColumn()));
1001:                 }
1002:
1003:                 tableViewer.setLabelProvider(labelProvider);
1004:                 tableViewer.setContentProvider(cp);
1005:                 updateTableViewerInputList();
1006:
1007:                 final TableColumnLayout layout = new TableColumnLayout();
1008:                 composite.setLayout(layout);
1009:                 layout.setColumnData(column.getColumn(), new ColumnWeightData(1, false));
1010:
1011:                 tableViewer.addDoubleClickListener(new IDoubleClickListener() {
1012:
1013:                         @Override
1014:                         public void doubleClick(DoubleClickEvent event) {
1015:                                 if (!getVElement().isEffectivelyReadonly()) {
1016:                                         final EObject selectedObject = (EObject) IStructuredSelection.class.cast(event.getSelection())
1017:                                                 .getFirstElement();
1018:                                         handleDoubleClick(selectedObject);
1019:                                 }
1020:                         }
1021:
1022:                 });
1023:         }
1024:
1025:         private boolean isMoveDisabled() {
1026:                 return !showMoveUpButton() && !showMoveDownButton();
1027:         }
1028:
1029:         private SelectionAdapter getSelectionAdapter(final TableViewer tableViewer,
1030:                 final ObjectViewerComparator comparator, final TableColumn column) {
1031:                 final SelectionAdapter selectionAdapter = new SelectionAdapter() {
1032:                         @Override
1033:                         public void widgetSelected(SelectionEvent e) {
1034:                                 comparator.toggleDirection();
1035:                                 final int dir = comparator.getDirection();
1036:                                 tableViewer.getTable().setSortDirection(dir);
1037:                                 tableViewer.getTable().setSortColumn(column);
1038:                                 tableViewer.refresh();
1039:                         }
1040:                 };
1041:                 return selectionAdapter;
1042:         }
1043:
1044:         /**
1045:          * Method for handling a double click.
1046:          *
1047:          * @param selectedObject the selected {@link EObject}
1048:          */
1049:         protected void handleDoubleClick(EObject selectedObject) {
1050:                 final ReferenceService referenceService = getReferenceService();
1051:                 referenceService.openInNewContext(selectedObject);
1052:         }
1053:
1054:         /**
1055:          * Method for adding an existing element.
1056:          *
1057:          * @param tableViewer the {@link TableViewer}
1058:          * @param eObject The {@link EObject} to add to
1059:          * @param structuralFeature The corresponding {@link EStructuralFeature}
1060:          */
1061:         protected void handleAddExisting(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
1062:                 final ReferenceService referenceService = getReferenceService();
1063:                 referenceService.addExistingModelElements(eObject, (EReference) structuralFeature);
1064:         }
1065:
1066:         /**
1067:          * Method for adding a new element.
1068:          *
1069:          * @param tableViewer the {@link TableViewer}
1070:          * @param eObject The {@link EObject} to add to
1071:          * @param structuralFeature The corresponding {@link EStructuralFeature}
1072:          */
1073:         protected void handleAddNew(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
1074:                 final ReferenceService referenceService = getReferenceService();
1075:                 referenceService.addNewModelElements(eObject, (EReference) structuralFeature, true);
1076:         }
1077:
1078:         /**
1079:          * Override to customize linking and creation of EObjects in this renderer's EReference.
1080:          *
1081:          * @return The {@link ReferenceService} used to link and create new EObjects in this renderer's reference.
1082:          */
1083:         protected ReferenceService getReferenceService() {
1084:                 return getViewModelContext().getService(ReferenceService.class);
1085:         }
1086:
1087:         /**
1088:          * Method for deleting elements.
1089:          *
1090:          * @param tableViewer the {@link TableViewer}
1091:          * @param eObject The {@link EObject} to delete from
1092:          * @param structuralFeature The corresponding {@link EStructuralFeature}
1093:          */
1094:         protected void handleDelete(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
1095:
1096:                 @SuppressWarnings("unchecked")
1097:                 final List<Object> deletionList = IStructuredSelection.class.cast(tableViewer.getSelection()).toList();
1098:                 final EditingDomain editingDomain = getEditingDomain(eObject);
1099:
1100:                 /* assured by #isApplicable */
1101:                 final EReference reference = EReference.class.cast(structuralFeature);
1102:
1103:                 if (reference.isContainment()) {
1104:                         DeleteService deleteService = getViewModelContext().getService(DeleteService.class);
1105:                         if (deleteService == null) {
1106:                                 /*
1107:                                  * #getService(Class<?>) will report to the reportservice if it could not be found
1108:                                  * Use Default
1109:                                  */
1110:                                 deleteService = new EMFDeleteServiceImpl();
1111:                         }
1112:                         deleteService.deleteElements(deletionList);
1113:                 } else {
1114:                         removeElements(editingDomain, eObject, reference, deletionList);
1115:                 }
1116:         }
1117:
1118:         private void removeElements(EditingDomain editingDomain, Object source, EStructuralFeature feature,
1119:                 Collection<Object> toRemove) {
1120:                 final Command removeCommand = RemoveCommand.create(editingDomain, source, feature, toRemove);
1121:                 if (removeCommand.canExecute()) {
1122:                         if (editingDomain.getCommandStack() == null) {
1123:                                 removeCommand.execute();
1124:                         } else {
1125:                                 editingDomain.getCommandStack().execute(removeCommand);
1126:                         }
1127:                 }
1128:         }
1129:
1130:         /**
1131:          * Method for moving up elements.
1132:          *
1133:          * @param tableViewer the {@link TableViewer}
1134:          * @param eObject The {@link EObject} to delete from
1135:          * @param structuralFeature The corresponding {@link EStructuralFeature}
1136:          */
1137:         protected void handleMoveUp(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
1138:                 final List<?> moveUpList = IStructuredSelection.class.cast(tableViewer.getSelection()).toList();
1139:                 final EditingDomain editingDomain = getEditingDomain(eObject);
1140:
1141:                 for (final Object moveUpObject : moveUpList) {
1142:                         final int currentIndex = EList.class.cast(eObject.eGet(structuralFeature)).indexOf(moveUpObject);
1143:                         if (currentIndex <= 0) {
1144:                                 return;
1145:                         }
1146:                         editingDomain.getCommandStack()
1147:                                 .execute(
1148:                                         new MoveCommand(editingDomain, eObject, structuralFeature, currentIndex, currentIndex - 1));
1149:                 }
1150:         }
1151:
1152:         /**
1153:          * Method for moving down elements.
1154:          *
1155:          * @param tableViewer the {@link TableViewer}
1156:          * @param eObject The {@link EObject} to delete from
1157:          * @param structuralFeature The corresponding {@link EStructuralFeature}
1158:          */
1159:         protected void handleMoveDown(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
1160:                 final List<?> moveDownList = IStructuredSelection.class.cast(tableViewer.getSelection()).toList();
1161:                 final EditingDomain editingDomain = getEditingDomain(eObject);
1162:
1163:                 // need to reverse to avoid the moves interfering each other
1164:                 Collections.reverse(moveDownList);
1165:
1166:                 for (final Object moveDownObject : moveDownList) {
1167:                         final int maxIndex = EList.class.cast(eObject.eGet(structuralFeature)).size() - 1;
1168:                         final int currentIndex = EList.class.cast(eObject.eGet(structuralFeature)).indexOf(moveDownObject);
1169:                         if (currentIndex < 0 || currentIndex == maxIndex) {
1170:                                 return;
1171:
1172:                         }
1173:                         editingDomain.getCommandStack()
1174:                                 .execute(
1175:                                         new MoveCommand(editingDomain, eObject, structuralFeature, currentIndex, currentIndex + 1));
1176:                 }
1177:         }
1178:
1179:         @Override
1180:         protected void rootDomainModelChanged() throws DatabindingFailedException {
1181:                 // TODO rebinding of text and tooltip needed? If yes, complete!
1182:                 // if (textBinding != null) {
1183:                 // textBinding.dispose();
1184:                 // }
1185:                 // if (tooltipBinding != null) {
1186:                 // tooltipBinding.dispose();
1187:                 // }
1188:
1189:                 // Rebind table content to the new domain model
1190:                 updateTableViewerInputList();
1191:
1192:                 // Update cachedContainer to allow addition and removal of elements to/from the multi reference.
1193:                 cachedContainer = Optional.ofNullable((EObject) IObserving.class.cast(getModelValue()).getObserved());
1194:                 applyEnable();
1195:                 applyReadOnly();
1196:         }
1197:
1198:         /**
1199:          * Computes and returns the observable list of the referenced elements shown by this renderer.
1200:          *
1201:          * @return The {@link IObservableList} of the referenced elements
1202:          * @throws DatabindingFailedException If computing the list failed due to failed databinding
1203:          */
1204:         protected IObservableList<?> getReferencedElementsList() throws DatabindingFailedException {
1205:                 return getEMFFormsDatabinding().getObservableList(getVElement().getDomainModelReference(),
1206:                         getViewModelContext().getDomainModel());
1207:         }
1208:
1209:         @Override
1210:         protected boolean ignoreEnableOnReadOnly() {
1211:                 // always take the enable state into account (read only but enable let the user sort the table content for
1212:                 // example)
1213:                 return false;
1214:         }
1215:
1216:         @Override
1217:         protected void applyEnable() {
1218:                 super.applyEnable();
1219:                 // specific handling for buttons
1220:                 updateButtonEnabling();
1221:         }
1222:
1223:         @Override
1224:         protected void applyReadOnly() {
1225:                 // specific handling for buttons
1226:                 // do not let the super method disable the control, so the table is still enabled for sorting for example
1227:                 updateButtonVisibility();
1228:         }
1229:
1230:         @Override
1231:         protected void applyValidation() {
1232:                 Display.getDefault().asyncExec(new Runnable() {
1233:
1234:                         @Override
1235:                         public void run() {
1236:                                 if (validationIcon == null) {
1237:                                         return;
1238:                                 }
1239:                                 if (validationIcon.isDisposed()) {
1240:                                         return;
1241:                                 }
1242:                                 if (getVElement().getDiagnostic() == null) {
1243:                                         return;
1244:                                 }
1245:                                 validationIcon.setImage(getValidationIcon());
1246:                                 validationIcon.setToolTipText(ECPTooltipModifierHelper.modifyString(getVElement().getDiagnostic()
1247:                                         .getMessage(), null));
1248:                         }
1249:                 });
1250:         }
1251:
1252:         /**
1253:          * <p>
1254:          * Sorts two objects based on their labels and the given sorting direction.
1255:          * </p>
1256:          * <p>
1257:          * Override this method to provide a custom sorting algorithm for the objects shown in this control's table.
1258:          * </p>
1259:          *
1260:          * @param direction The sorting direction: 0 == NONE, 1 == UP, 2 == DOWN
1261:          * @param object1 The first object of the comparison
1262:          * @param object2 The second object of the comparison
1263:          * @return 0 if both objects are equal, -1 if object1 will appear higher in the table, 1 if object2 will appear
1264:          * higher in the table
1265:          */
1266:         protected int compare(int direction, Object object1, Object object2) {
1267:                 if (direction == 0) {
1268:                         return 0;
1269:                 }
1270:                 int rc = 0;
1271:
1272:                 final String label1 = labelProvider.getText(object1);
1273:                 final String label2 = labelProvider.getText(object2);
1274:                 if (label1 == null) {
1275:                         if (label2 == null) {
1276:                                 rc = 0;
1277:                         } else {
1278:                                 rc = 1;
1279:                         }
1280:                 } else if (label2 == null) {
1281:                         rc = -1;
1282:                 } else {
1283:                         rc = NumberAwareStringComparator.getInstance().compare(label1, label2);
1284:                 }
1285:                 // If descending order, flip the direction
1286:                 if (direction == 2) {
1287:                         rc = -rc;
1288:                 }
1289:                 return rc;
1290:         }
1291:
1292:         /**
1293:          * Returns the {@link ILabelProvider} used by this renderer.
1294:          *
1295:          * @return the {@link ILabelProvider}
1296:          */
1297:         protected ILabelProvider getLabelProvider() {
1298:                 return labelProvider;
1299:         }
1300:
1301:         /**
1302:          * Select and reveal an {@code object} in my table.
1303:          *
1304:          * @param object an object to reveal
1305:          *
1306:          * @since 1.22
1307:          */
1308:         void reveal(Object object) {
1309:                 checkRenderer();
1310:
1311:                 if (tableViewer != null) {
1312:                         final ISelection newSelection = new StructuredSelection(object);
1313:                         if (!newSelection.equals(tableViewer.getSelection())) {
1314:                                 tableViewer.setSelection(newSelection, true);
1315:                         } else {
1316:                                 tableViewer.reveal(object);
1317:                         }
1318:                 }
1319:         }
1320:
1321: }