Skip to content

Package: TableControlSWTRenderer$TableControlSWTRendererTableViewerCreator

TableControlSWTRenderer$TableControlSWTRendererTableViewerCreator

nameinstructionbranchcomplexitylinemethod
TableControlSWTRenderer.TableControlSWTRendererTableViewerCreator(TableControlSWTRenderer)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
createTableViewer(Composite)
M: 8 C: 54
87%
M: 2 C: 2
50%
M: 2 C: 1
33%
M: 1 C: 14
93%
M: 0 C: 1
100%
createTableViewerEditor(TableViewer)
M: 0 C: 21
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 10
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: * Johannes Faltermeier - refactorings
14: * Christian W. Damus - bugs 544116, 544537, 545686, 530314, 547271, 547787, 548592, 552385, 559267
15: ******************************************************************************/
16: package org.eclipse.emf.ecp.view.spi.table.swt;
17:
18: import static org.eclipse.emfforms.spi.swt.table.ViewerRefreshManager.getRefreshRunnable;
19:
20: import java.net.MalformedURLException;
21: import java.net.URL;
22: import java.text.MessageFormat;
23: import java.util.ArrayList;
24: import java.util.Collection;
25: import java.util.Collections;
26: import java.util.LinkedHashMap;
27: import java.util.LinkedHashSet;
28: import java.util.List;
29: import java.util.Map;
30: import java.util.Objects;
31: import java.util.Set;
32: import java.util.SortedSet;
33: import java.util.TreeSet;
34: import java.util.stream.Collectors;
35:
36: import javax.inject.Inject;
37:
38: import org.eclipse.core.databinding.Binding;
39: import org.eclipse.core.databinding.observable.IObserving;
40: import org.eclipse.core.databinding.observable.Observables;
41: import org.eclipse.core.databinding.observable.list.IListChangeListener;
42: import org.eclipse.core.databinding.observable.list.IObservableList;
43: import org.eclipse.core.databinding.observable.list.ListChangeEvent;
44: import org.eclipse.core.databinding.observable.map.IObservableMap;
45: import org.eclipse.core.databinding.observable.value.IObservableValue;
46: import org.eclipse.core.databinding.property.value.IValueProperty;
47: import org.eclipse.core.runtime.Assert;
48: import org.eclipse.core.runtime.IAdaptable;
49: import org.eclipse.core.runtime.IStatus;
50: import org.eclipse.core.runtime.Platform;
51: import org.eclipse.emf.common.command.Command;
52: import org.eclipse.emf.common.command.CompoundCommand;
53: import org.eclipse.emf.common.notify.Notification;
54: import org.eclipse.emf.common.util.Diagnostic;
55: import org.eclipse.emf.common.util.EList;
56: import org.eclipse.emf.common.util.Enumerator;
57: import org.eclipse.emf.databinding.EMFDataBindingContext;
58: import org.eclipse.emf.databinding.EMFProperties;
59: import org.eclipse.emf.ecore.EClass;
60: import org.eclipse.emf.ecore.EClassifier;
61: import org.eclipse.emf.ecore.EObject;
62: import org.eclipse.emf.ecore.EReference;
63: import org.eclipse.emf.ecore.EStructuralFeature;
64: import org.eclipse.emf.ecore.EStructuralFeature.Setting;
65: import org.eclipse.emf.ecore.InternalEObject;
66: import org.eclipse.emf.ecp.edit.spi.ConditionalDeleteService;
67: import org.eclipse.emf.ecp.edit.spi.DeleteService;
68: import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
69: import org.eclipse.emf.ecp.edit.spi.ReferenceService;
70: import org.eclipse.emf.ecp.edit.spi.swt.table.ECPCellEditor;
71: import org.eclipse.emf.ecp.edit.spi.swt.table.ECPCellEditorComparator;
72: import org.eclipse.emf.ecp.edit.spi.swt.table.ECPCustomUpdateCellEditor;
73: import org.eclipse.emf.ecp.edit.spi.swt.table.ECPElementAwareCellEditor;
74: import org.eclipse.emf.ecp.edit.spi.swt.table.ECPFilterableCell;
75: import org.eclipse.emf.ecp.edit.spi.swt.table.ECPViewerAwareCellEditor;
76: import org.eclipse.emf.ecp.edit.spi.swt.util.ECPDialogExecutor;
77: import org.eclipse.emf.ecp.view.internal.table.swt.Activator;
78: import org.eclipse.emf.ecp.view.internal.table.swt.CellReadOnlyTesterHelper;
79: import org.eclipse.emf.ecp.view.internal.table.swt.MessageKeys;
80: import org.eclipse.emf.ecp.view.internal.table.swt.RunnableManager;
81: import org.eclipse.emf.ecp.view.internal.table.swt.TableConfigurationHelper;
82: import org.eclipse.emf.ecp.view.model.common.util.RendererUtil;
83: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
84: import org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer;
85: import org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRendererUtil;
86: import org.eclipse.emf.ecp.view.spi.model.DiagnosticMessageExtractor;
87: import org.eclipse.emf.ecp.view.spi.model.LabelAlignment;
88: import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
89: import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
90: import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
91: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
92: import org.eclipse.emf.ecp.view.spi.model.VElement;
93: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
94: import org.eclipse.emf.ecp.view.spi.provider.ECPTooltipModifierHelper;
95: import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
96: import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
97: import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
98: import org.eclipse.emf.ecp.view.spi.table.model.VEnablementConfiguration;
99: import org.eclipse.emf.ecp.view.spi.table.model.VTableControl;
100: import org.eclipse.emf.ecp.view.spi.table.model.VTableDomainModelReference;
101: import org.eclipse.emf.ecp.view.spi.table.swt.action.AddRowAction;
102: import org.eclipse.emf.ecp.view.spi.table.swt.action.DuplicateRowAction;
103: import org.eclipse.emf.ecp.view.spi.table.swt.action.MoveRowDownAction;
104: import org.eclipse.emf.ecp.view.spi.table.swt.action.MoveRowUpAction;
105: import org.eclipse.emf.ecp.view.spi.table.swt.action.RemoveRowAction;
106: import org.eclipse.emf.ecp.view.spi.table.swt.action.TableActionIconButton;
107: import org.eclipse.emf.ecp.view.spi.table.swt.action.TableRendererActionBar;
108: import org.eclipse.emf.ecp.view.spi.table.swt.action.TableRendererViewerActionContext;
109: import org.eclipse.emf.ecp.view.spi.util.swt.ImageRegistryService;
110: import org.eclipse.emf.ecp.view.template.model.VTStyleProperty;
111: import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
112: import org.eclipse.emf.ecp.view.template.style.background.model.VTBackgroundFactory;
113: import org.eclipse.emf.ecp.view.template.style.background.model.VTBackgroundStyleProperty;
114: import org.eclipse.emf.ecp.view.template.style.fontProperties.model.VTFontPropertiesFactory;
115: import org.eclipse.emf.ecp.view.template.style.fontProperties.model.VTFontPropertiesStyleProperty;
116: import org.eclipse.emf.ecp.view.template.style.keybinding.model.VTKeyBinding;
117: import org.eclipse.emf.ecp.view.template.style.keybinding.model.VTKeyBindings;
118: import org.eclipse.emf.ecp.view.template.style.tableStyleProperty.model.RenderMode;
119: import org.eclipse.emf.ecp.view.template.style.tableStyleProperty.model.VTTableStyleProperty;
120: import org.eclipse.emf.ecp.view.template.style.tableStyleProperty.model.VTTableStylePropertyFactory;
121: import org.eclipse.emf.ecp.view.template.style.tableValidation.model.VTTableValidationFactory;
122: import org.eclipse.emf.ecp.view.template.style.tableValidation.model.VTTableValidationStyleProperty;
123: import org.eclipse.emf.edit.command.AddCommand;
124: import org.eclipse.emf.edit.command.MoveCommand;
125: import org.eclipse.emf.edit.command.RemoveCommand;
126: import org.eclipse.emf.edit.domain.EditingDomain;
127: import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
128: import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
129: import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
130: import org.eclipse.emfforms.common.Optional;
131: import org.eclipse.emfforms.spi.common.BundleResolver;
132: import org.eclipse.emfforms.spi.common.BundleResolver.NoBundleFoundException;
133: import org.eclipse.emfforms.spi.common.BundleResolverFactory;
134: import org.eclipse.emfforms.spi.common.report.AbstractReport;
135: import org.eclipse.emfforms.spi.common.report.ReportService;
136: import org.eclipse.emfforms.spi.common.sort.NumberAwareStringComparator;
137: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
138: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
139: import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
140: import org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF;
141: import org.eclipse.emfforms.spi.core.services.editsupport.EMFFormsEditSupport;
142: import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
143: import org.eclipse.emfforms.spi.core.services.label.NoLabelFoundException;
144: import org.eclipse.emfforms.spi.localization.EMFFormsLocalizationService;
145: import org.eclipse.emfforms.spi.localization.LocalizationServiceHelper;
146: import org.eclipse.emfforms.spi.swt.core.SWTDataElementIdHelper;
147: import org.eclipse.emfforms.spi.swt.core.layout.EMFFormsSWTLayoutUtil;
148: import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
149: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
150: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
151: import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
152: import org.eclipse.emfforms.spi.swt.table.AbstractTableViewerComposite;
153: import org.eclipse.emfforms.spi.swt.table.CellLabelProviderFactory;
154: import org.eclipse.emfforms.spi.swt.table.ColumnConfiguration;
155: import org.eclipse.emfforms.spi.swt.table.ColumnConfigurationBuilder;
156: import org.eclipse.emfforms.spi.swt.table.DNDProvider;
157: import org.eclipse.emfforms.spi.swt.table.EditingSupportCreator;
158: import org.eclipse.emfforms.spi.swt.table.TableConfiguration;
159: import org.eclipse.emfforms.spi.swt.table.TableConfigurationBuilder;
160: import org.eclipse.emfforms.spi.swt.table.TableControl;
161: import org.eclipse.emfforms.spi.swt.table.TableViewerComparator;
162: import org.eclipse.emfforms.spi.swt.table.TableViewerCompositeBuilder;
163: import org.eclipse.emfforms.spi.swt.table.TableViewerCreator;
164: import org.eclipse.emfforms.spi.swt.table.TableViewerFactory;
165: import org.eclipse.emfforms.spi.swt.table.TableViewerSWTBuilder;
166: import org.eclipse.emfforms.spi.swt.table.TableViewerSWTCustomization;
167: import org.eclipse.emfforms.spi.swt.table.ViewerRefreshManager;
168: import org.eclipse.emfforms.spi.swt.table.action.ActionBar;
169: import org.eclipse.emfforms.spi.swt.table.action.ActionConfiguration;
170: import org.eclipse.emfforms.spi.swt.table.action.ActionConfigurationBuilder;
171: import org.eclipse.emfforms.spi.swt.table.action.TableActionBar;
172: import org.eclipse.emfforms.spi.swt.table.action.ViewerActionContext;
173: import org.eclipse.emfforms.view.spi.multisegment.model.MultiSegmentUtil;
174: import org.eclipse.emfforms.view.spi.multisegment.model.VMultiDomainModelReferenceSegment;
175: import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
176: import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
177: import org.eclipse.jface.databinding.viewers.ObservableMapCellLabelProvider;
178: import org.eclipse.jface.dialogs.IDialogConstants;
179: import org.eclipse.jface.dialogs.IDialogLabelKeys;
180: import org.eclipse.jface.dialogs.MessageDialog;
181: import org.eclipse.jface.layout.GridDataFactory;
182: import org.eclipse.jface.layout.GridLayoutFactory;
183: import org.eclipse.jface.resource.JFaceResources;
184: import org.eclipse.jface.viewers.AbstractTableViewer;
185: import org.eclipse.jface.viewers.CellEditor;
186: import org.eclipse.jface.viewers.CellLabelProvider;
187: import org.eclipse.jface.viewers.ColumnViewer;
188: import org.eclipse.jface.viewers.ColumnViewerEditor;
189: import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
190: import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener;
191: import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
192: import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
193: import org.eclipse.jface.viewers.DoubleClickEvent;
194: import org.eclipse.jface.viewers.EditingSupport;
195: import org.eclipse.jface.viewers.IColorProvider;
196: import org.eclipse.jface.viewers.IDoubleClickListener;
197: import org.eclipse.jface.viewers.ISelection;
198: import org.eclipse.jface.viewers.ISelectionChangedListener;
199: import org.eclipse.jface.viewers.IStructuredSelection;
200: import org.eclipse.jface.viewers.SelectionChangedEvent;
201: import org.eclipse.jface.viewers.StructuredSelection;
202: import org.eclipse.jface.viewers.TableViewer;
203: import org.eclipse.jface.viewers.TableViewerEditor;
204: import org.eclipse.jface.viewers.TableViewerFocusCellManager;
205: import org.eclipse.jface.viewers.Viewer;
206: import org.eclipse.jface.viewers.ViewerCell;
207: import org.eclipse.jface.viewers.ViewerComparator;
208: import org.eclipse.swt.SWT;
209: import org.eclipse.swt.dnd.DND;
210: import org.eclipse.swt.dnd.DragSourceListener;
211: import org.eclipse.swt.dnd.DropTargetEvent;
212: import org.eclipse.swt.dnd.DropTargetListener;
213: import org.eclipse.swt.dnd.Transfer;
214: import org.eclipse.swt.events.ControlAdapter;
215: import org.eclipse.swt.events.ControlEvent;
216: import org.eclipse.swt.events.DisposeEvent;
217: import org.eclipse.swt.events.DisposeListener;
218: import org.eclipse.swt.graphics.Color;
219: import org.eclipse.swt.graphics.Image;
220: import org.eclipse.swt.graphics.Point;
221: import org.eclipse.swt.layout.GridData;
222: import org.eclipse.swt.widgets.Button;
223: import org.eclipse.swt.widgets.Composite;
224: import org.eclipse.swt.widgets.Control;
225: import org.eclipse.swt.widgets.Display;
226: import org.eclipse.swt.widgets.Item;
227: import org.eclipse.swt.widgets.Label;
228: import org.eclipse.swt.widgets.ScrollBar;
229: import org.eclipse.swt.widgets.Shell;
230: import org.eclipse.swt.widgets.Table;
231: import org.eclipse.swt.widgets.Widget;
232: import org.osgi.framework.Bundle;
233: import org.osgi.framework.FrameworkUtil;
234:
235: /**
236: * SWT Renderer for Table Control.
237: *
238: * @author Eugen Neufeld
239: * @author Johannes Faltermeier
240: *
241: */
242: public class TableControlSWTRenderer extends AbstractControlSWTRenderer<VTableControl> {
243:
244:         /**
245:          * @since 1.10
246:          */
247:         protected static final String FIXED_COLUMNS = "org.eclipse.rap.rwt.fixedColumns"; //$NON-NLS-1$
248:         /**
249:          * @since 1.10
250:          */
251:         protected static final String TABLE_CUSTOM_VARIANT = "org_eclipse_emf_ecp_control_table"; //$NON-NLS-1$
252:         /**
253:          * @since 1.17
254:          */
255:         protected static final Point VALIDATION_PREFERRED_SIZE = new Point(16, 17);
256:
257:         private final Map<Integer, ECPCellEditorComparator> columnIndexToComparatorMap = new LinkedHashMap<Integer, ECPCellEditorComparator>();
258:
259:         private final ImageRegistryService imageRegistryService;
260:         private final EMFDataBindingContext viewModelDBC;
261:         private final EMFFormsEditSupport emfFormsEditSupport;
262:
263:         private SWTGridDescription rendererGridDescription;
264:
265:         private AbstractTableViewer tableViewer;
266:
267:         private Label validationIcon;
268:         private boolean showValidationSummaryTooltip;
269:
270:         private Optional<Integer> minimumHeight;
271:         private Optional<Integer> maximumHeight;
272:         private Optional<Integer> visibleLines;
273:         private AbstractTableViewerComposite<? extends AbstractTableViewer> tableViewerComposite;
274:         private int regularColumnsStartIndex;
275:         private boolean isDisposing;
276:         private IObservableList<?> list;
277:         private final RunnableManager runnableManager = new RunnableManager(Display.getDefault());
278:
279:         private TableViewerSWTCustomization<?> customization;
280:
281:         /** The EReference describing the list shown by the table. */
282:         private EReference tableEReference;
283:         private EStructuralFeature[] columnFeatures;
284:         /** The feature of the column which is currently used for sorting. */
285:         private java.util.Optional<EStructuralFeature> sortColumnFeature = java.util.Optional.empty();
286:         private ModelChangeListener autoSortModelChangeListener;
287:         private final EMFFormsLocalizationService localizationService;
288:         private final BundleResolver bundleResolver = BundleResolverFactory.createBundleResolver();
289:         /** DO NOT USE DIRECTLY! Use {@link #getEnumeratorComparator()} instead. */
290:         private LocalizedEnumeratorComparator enumeratorComparator;
291:
292:         private EMFFormsLocalizationService l10n;
293:         private String referenceDisplayName;
294:
295:         /**
296:          * Legacy constructor for backwards compatibility.
297:          *
298:          * @param vElement the view model element to be rendered
299:          * @param viewContext the view context
300:          * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
301:          * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
302:          * @param reportService The {@link ReportService}
303:          * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
304:          * @param imageRegistryService The {@link ImageRegistryService}
305:          * @param emfFormsEditSupport The {@link EMFFormsEditSupport}
306:          * @since 1.8
307:          */
308:         @Deprecated
309:         // BEGIN COMPLEX CODE
310:         public TableControlSWTRenderer(
311:                 VTableControl vElement,
312:                 ViewModelContext viewContext,
313:                 ReportService reportService,
314:                 EMFFormsDatabindingEMF emfFormsDatabinding,
315:                 EMFFormsLabelProvider emfFormsLabelProvider,
316:                 VTViewTemplateProvider vtViewTemplateProvider,
317:                 ImageRegistryService imageRegistryService,
318:                 EMFFormsEditSupport emfFormsEditSupport) {
319:                 // END COMPLEX CODE
320:
321:                 this(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider,
322:                         imageRegistryService, emfFormsEditSupport, viewContext.getService(EMFFormsLocalizationService.class));
323:         }
324:
325:         /**
326:          * Default constructor.
327:          *
328:          * @param vElement the view model element to be rendered
329:          * @param viewContext the view context
330:          * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
331:          * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
332:          * @param reportService The {@link ReportService}
333:          * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
334:          * @param imageRegistryService The {@link ImageRegistryService}
335:          * @param emfFormsEditSupport The {@link EMFFormsEditSupport}
336:          * @param localizationService The {@link EMFFormsLocalizationService}
337:          * @since 1.22
338:          */
339:         @Inject
340:         // BEGIN COMPLEX CODE
341:         public TableControlSWTRenderer(
342:                 VTableControl vElement,
343:                 ViewModelContext viewContext,
344:                 ReportService reportService,
345:                 EMFFormsDatabindingEMF emfFormsDatabinding,
346:                 EMFFormsLabelProvider emfFormsLabelProvider,
347:                 VTViewTemplateProvider vtViewTemplateProvider,
348:                 ImageRegistryService imageRegistryService,
349:                 EMFFormsEditSupport emfFormsEditSupport,
350:                 EMFFormsLocalizationService localizationService) {
351:                 // END COMPLEX CODE
352:
353:                 super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider);
354:                 this.imageRegistryService = imageRegistryService;
355:                 this.emfFormsEditSupport = emfFormsEditSupport;
356:                 this.localizationService = localizationService;
357:                 viewModelDBC = new EMFDataBindingContext();
358:         }
359:
360:         @Override
361:         public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
362:                 if (rendererGridDescription == null) {
363:                         if (getTableStyleProperty().getRenderMode() == RenderMode.COMPACT_VERTICALLY) {
364:                                 final boolean includeLabel = getVElement().getLabelAlignment() != LabelAlignment.NONE;
365:                                 if (getVElement().getLabelAlignment() == LabelAlignment.TOP) {
366:                                         Activator.getInstance().log(IStatus.WARNING, MessageFormat.format(
367:                                                 Messages.TableControlSWTRenderer_LabelAlignmentTopNotSupportForRenderModeCompactVertically,
368:                                                 getVElement().getName()));
369:                                 }
370:                                 rendererGridDescription = GridDescriptionFactory.INSTANCE.createCompactGrid(includeLabel, true, this);
371:                         } else {
372:                                 rendererGridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
373:                         }
374:
375:                 }
376:                 return rendererGridDescription;
377:         }
378:
379:         @Override
380:         protected EMFFormsDatabindingEMF getEMFFormsDatabinding() {
381:                 return (EMFFormsDatabindingEMF) super.getEMFFormsDatabinding();
382:         }
383:
384:         @Override
385:         protected Control renderControl(SWTGridCell gridCell, final Composite parent) throws NoRendererFoundException,
386:                 NoPropertyDescriptorFoundExeption {
387:                 // compact label
388:                 if (gridCell.getColumn() == 0 && rendererGridDescription.getColumns() == 3) {
389:                         return createLabel(parent);
390:                 }
391:                 // compact validation
392:                 if (gridCell.getColumn() == 0 && rendererGridDescription.getColumns() == 2
393:                         || gridCell.getColumn() == 1 && rendererGridDescription.getColumns() == 3) {
394:                         validationIcon = createValidationIcon(parent);
395:                         return validationIcon;
396:                 }
397:                 // Default
398:                 return renderTableControl(gridCell, parent);
399:         }
400:
401:         @Override
402:         protected Control createLabel(final Composite parent) {
403:                 final VDomainModelReference dmrToCheck = getDMRToMultiReference();
404:                 final IObservableValue<?> labelText = getLabelText(dmrToCheck);
405:                 final IObservableValue<?> labelTooltipText = getLabelTooltipText(dmrToCheck);
406:
407:                 final Label titleLabel = new Label(parent, AbstractControlSWTRendererUtil
408:                         .getLabelStyleBits(getVTViewTemplateProvider(), getVElement(), getViewModelContext()));
409:                 titleLabel.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_label"); //$NON-NLS-1$
410:                 titleLabel.setBackground(parent.getBackground());
411:
412:                 viewModelDBC.bindValue(
413:                         WidgetProperties.text().observe(titleLabel),
414:                         labelText);
415:                 viewModelDBC.bindValue(
416:                         WidgetProperties.tooltipText().observe(titleLabel),
417:                         labelTooltipText);
418:
419:                 return titleLabel;
420:         }
421:
422:         /**
423:          * Renders the Table Control.
424:          *
425:          * Renders the Table Control including title and validation when {@link RenderMode} is set to
426:          * {@link RenderMode#DEFAULT}. Only renders the
427:          * Table Control without title and validation when renderMode is set to {@link RenderMode#COMPACT_VERTICALLY}.
428:          *
429:          * @param gridCell the {@link SWTGridCell}.
430:          * @param parent the {@link Composite}.
431:          * @return the rendered {@link Control}.
432:          * @throws NoRendererFoundException the {@link NoRendererFoundException}.
433:          * @throws NoPropertyDescriptorFoundExeption the {@link NoPropertyDescriptorFoundExeption}.
434:          * @since 1.14
435:          */
436:         protected Control renderTableControl(SWTGridCell gridCell, final Composite parent)
437:                 throws NoRendererFoundException,
438:                 NoPropertyDescriptorFoundExeption {
439:                 try {
440:                         /* get the list-setting which is displayed */
441:                         final VDomainModelReference dmrToCheck = getDMRToMultiReference();
442:
443:                         /* get the observable list */
444:                         list = getEMFFormsDatabinding().getObservableList(dmrToCheck,
445:                                 getViewModelContext().getDomainModel());
446:
447:                         /* get the EReference describing the list shown by the table */
448:                         tableEReference = (EReference) list.getElementType();
449:
450:                         final TableRendererViewerActionContext actionContext = createViewerActionContext();
451:                         final ActionConfiguration actionConfiguration = configureActions(actionContext);
452:                         final TableActionBar<? extends AbstractTableViewer> actionBar = createActionBar(actionContext,
453:                                 actionConfiguration);
454:
455:                         /* get the label text/tooltip */
456:                         final IObservableValue<?> labelText = getLabelText(dmrToCheck);
457:                         final IObservableValue<?> labelTooltipText = getLabelTooltipText(dmrToCheck);
458:
459:                         /* content provider */
460:                         final ObservableListContentProvider cp = new ObservableListContentProvider();
461:
462:                         final TableControlComparator comparator = getVElement().isMoveUpDownDisabled()
463:                                 ? createTableViewerComparator()
464:                                 : null;
465:
466:                         /* render */
467:                         final TableViewerCompositeBuilder compositeBuilder = createTableViewerCompositeBuilder();
468:
469:                         final TableViewerSWTBuilder tableViewerSWTBuilder = createTableViewerSWTBuilder(parent, list, labelText,
470:                                 labelTooltipText, compositeBuilder, cp, comparator, actionBar);
471:                         tableViewerSWTBuilder.customizeActionConfiguration(actionConfiguration);
472:
473:                         tableViewerSWTBuilder
474:                                 .configureTable(TableConfigurationBuilder.from(tableViewerSWTBuilder)
475:                                         .dataMapEntry(TableConfiguration.DMR, dmrToCheck)
476:                                         .dataMapEntry(ViewerRefreshManager.REFRESH_MANAGER,
477:                                                 (ViewerRefreshManager) this::postRefresh)
478:                                         .build());
479:
480:                         regularColumnsStartIndex = 0;
481:
482:                         /* validation column */
483:                         if (!getVElement().isEffectivelyReadonly()) {
484:                                 regularColumnsStartIndex++;
485:                                 createFixedValidationStatusColumn(tableViewerSWTBuilder);
486:                         }
487:                         regularColumnsStartIndex += addAdditionalColumns(tableViewerSWTBuilder);
488:                         addColumns(tableViewerSWTBuilder, EReference.class.cast(list.getElementType()).getEReferenceType(), cp);
489:
490:                         initCompositeHeight();
491:
492:                         tableViewerComposite = tableViewerSWTBuilder.build();
493:
494:                         /* setup selection changes listener */
495:                         tableViewerComposite.getTableViewer().addSelectionChangedListener(new ViewerSelectionChangedListener());
496:                         tableViewerComposite.getTableViewer().addDoubleClickListener(new DoubleClickListener());
497:
498:                         /* setup sorting via column selection */
499:                         if (getVElement().isMoveUpDownDisabled()) {
500:                                 setupSorting(comparator, regularColumnsStartIndex, tableViewerComposite);
501:                         }
502:
503:                         /* get validation icon */
504:                         setupValidation(tableViewerComposite);
505:
506:                         /* create the table viewer editor */
507:                         setTableViewer(tableViewerComposite.getTableViewer());
508:
509:                         SWTDataElementIdHelper.setElementIdDataForVControl(tableViewerComposite, getVElement(),
510:                                 getViewModelContext());
511:
512:                         // FIXME doesn't work with table with panel
513:                         // setLayoutData(compositeBuilder.getViewerComposite());
514:                         GridData.class
515:                                 .cast(compositeBuilder.getViewerComposite().getLayoutData()).heightHint = getTableHeightHint();
516:
517:                         addRelayoutListenerIfNeeded(list, compositeBuilder.getViewerComposite());
518:
519:                         addResizeListener(tableViewerComposite.getTableViewer().getControl(), regularColumnsStartIndex);
520:
521:                         customization = tableViewerSWTBuilder.getCustomization();
522:
523:                         autoSortModelChangeListener = new AutoSortModelChangeListener();
524:                         getViewModelContext().registerDomainChangeListener(autoSortModelChangeListener);
525:
526:                         return tableViewerComposite;
527:
528:                 } catch (final DatabindingFailedException ex) {
529:                         getReportService().report(new RenderingFailedReport(ex));
530:                         final Label errorLabel = new Label(parent, SWT.NONE);
531:                         errorLabel.setText(ex.getMessage());
532:                         return errorLabel;
533:                 }
534:         }
535:
536:         /**
537:          * Creates a TableControlComparator.
538:          *
539:          * @return TableControlComparator
540:          * @since 1.22
541:          */
542:         protected TableControlComparator createTableViewerComparator() {
543:                 return new ECPTableViewerComparator();
544:         }
545:
546:         /**
547:          * Create the {@link ViewerActionContext} for the table viewer.
548:          *
549:          * @return the {@link TableRendererViewerActionContext}
550:          * @throws DatabindingFailedException
551:          * @since 1.18
552:          */
553:         protected TableRendererViewerActionContext createViewerActionContext() {
554:                 return new TableRendererViewerActionContext() {
555:
556:                         @Override
557:                         public VTableControl getVElement() {
558:                                 return TableControlSWTRenderer.this.getVElement();
559:                         }
560:
561:                         @Override
562:                         public Setting getSetting() {
563:                                 try {
564:                                         return getEMFFormsDatabinding().getSetting(
565:                                                 getDMRToMultiReference(), getViewModelContext().getDomainModel());
566:                                 } catch (final DatabindingFailedException ex) {
567:                                         return null; // this should never happen
568:                                 }
569:                         }
570:
571:                         @Override
572:                         public EditingDomain getEditingDomain() {
573:                                 return TableControlSWTRenderer.this.getEditingDomain(getViewModelContext().getDomainModel());
574:                         }
575:
576:                         @Override
577:                         public AbstractTableViewer getViewer() {
578:                                 return TableControlSWTRenderer.this.getTableViewer();
579:                         }
580:                 };
581:         }
582:
583:         /**
584:          * Configure the actions applicable to this table viewer.
585:          *
586:          * @param actionContext the action context
587:          * @return an {@link ActionConfigurationImpl} built using the {@link ActionConfigurationBuilder}
588:          *
589:          * @since 1.18
590:          */
591:         protected ActionConfiguration configureActions(TableRendererViewerActionContext actionContext) {
592:                 final ActionConfigurationBuilder actionConfigBuilder = //
593:                         ActionConfigurationBuilder.usingDefaults();
594:
595:                 /**
596:                  * Note: EMF Forms distinguishes between read-only and enabled.
597:                  * Read-only is a declarative state defined by the view model and cannot
598:                  * be overwritten during runtime whereas the enabled state can.
599:                  * Therefore, if the view element is marked as read-only we must not configure any buttons.
600:                  *
601:                  * @see @{link TableRenderAction#isTableDisabled()}
602:                  */
603:                 if (getVElement().isEffectivelyReadonly()) {
604:                         return actionConfigBuilder.build();
605:                 }
606:
607:                 final Setting setting = actionContext.getSetting();
608:                 final EClass eClass = ((EReference) setting.getEStructuralFeature()).getEReferenceType();
609:
610:                 if (!getVElement().isMoveUpDownDisabled()) {
611:                         final MoveRowUpAction moveRowUpAction = new MoveRowUpAction(actionContext);
612:                         final MoveRowDownAction moveRowDownAction = new MoveRowDownAction(actionContext);
613:
614:                         actionConfigBuilder
615:                                 .addAction(moveRowUpAction)
616:                                 .addControlFor(moveRowUpAction, new TableActionIconButton(
617:                                         formatTooltipText(eClass, MessageKeys.TableControl_MoveUp), getImage("icons/move_up.png"))) //$NON-NLS-1$
618:                                 .addKeySequenceFor(moveRowUpAction,
619:                                         getKeyBindingsForAction(MoveRowUpAction.ACTION_ID))
620:                                 //
621:                                 .addAction(moveRowDownAction)
622:                                 .addControlFor(moveRowDownAction, new TableActionIconButton(
623:                                         formatTooltipText(eClass, MessageKeys.TableControl_MoveDown), getImage("icons/move_down.png"))) //$NON-NLS-1$
624:                                 .addKeySequenceFor(moveRowDownAction,
625:                                         getKeyBindingsForAction(MoveRowDownAction.ACTION_ID));
626:                 }
627:
628:                 if (!getVElement().isAddRemoveDisabled()) {
629:                         final AddRowAction addRowAction = new AddRowAction(actionContext) {
630:                                 @Override
631:                                 public void addRowLegacy(
632:                                         final EClass eClass, final EStructuralFeature eStructuralFeature, final EObject eObject) {
633:                                         addRow(eClass, eObject, eStructuralFeature);
634:                                 }
635:                         };
636:                         final RemoveRowAction removeRowAction = new RemoveRowAction(actionContext) {
637:                                 @Override
638:                                 public void removeRowLegacy(List<EObject> deletionList, EObject eObject,
639:                                         EStructuralFeature eStructuralFeature) {
640:                                         deleteRowUserConfirmDialog(deletionList, eObject, eStructuralFeature, getAddButton(),
641:                                                 getRemoveButton());
642:                                 }
643:
644:                                 @Override
645:                                 public boolean canExecute() {
646:                                         final Setting setting = getActionContext().getSetting();
647:                                         return super.canExecute() && ConditionalDeleteService.getDeleteService(getViewModelContext())
648:                                                 .canRemove(setting.getEObject(), setting.getEStructuralFeature(),
649:                                                         getActionContext().getViewer().getStructuredSelection().toList());
650:                                 }
651:                         };
652:
653:                         actionConfigBuilder
654:                                 .addAction(addRowAction)
655:                                 .addControlFor(addRowAction, new TableActionIconButton(
656:                                         formatTooltipText(eClass, MessageKeys.TableControl_AddInstanceOf), getImage("icons/add.png"))) //$NON-NLS-1$
657:                                 .addKeySequenceFor(addRowAction,
658:                                         getKeyBindingsForAction(AddRowAction.ACTION_ID));
659:                         actionConfigBuilder
660:                                 .addAction(removeRowAction)
661:                                 .addControlFor(removeRowAction, new TableActionIconButton(
662:                                         formatTooltipText(eClass, MessageKeys.TableControl_RemoveSelected), getImage("icons/delete.png"))) //$NON-NLS-1$
663:                                 .addKeySequenceFor(removeRowAction,
664:                                         getKeyBindingsForAction(RemoveRowAction.ACTION_ID));
665:                 }
666:
667:                 if (!getVElement().isDuplicateDisabled()) {
668:                         final DuplicateRowAction duplicateRow = new DuplicateRowAction(actionContext);
669:
670:                         actionConfigBuilder
671:                                 .addAction(duplicateRow)
672:                                 .addControlFor(duplicateRow, new TableActionIconButton(
673:                                         formatTooltipText(eClass, MessageKeys.TableControl_Duplicate), getImage("icons/duplicate.png"))) //$NON-NLS-1$
674:                                 .addKeySequenceFor(duplicateRow,
675:                                         getKeyBindingsForAction(DuplicateRowAction.ACTION_ID));
676:                 }
677:
678:                 return actionConfigBuilder.build();
679:         }
680:
681:         /**
682:          * Helper to extract the configured key bindings form the view template model.
683:          *
684:          * @param actionId the ID of the action to extract the key bindings for
685:          * @param defaultKeybindings the default key bindings to use in case there are no bindings configured
686:          * @return an array of key bindings
687:          *
688:          * @since 1.18
689:          */
690:         protected String[] getKeyBindingsForAction(String actionId, String... defaultKeybindings) {
691:                 final VTKeyBindings bindings = getStyleProperty(VTKeyBindings.class);
692:                 if (bindings == null) {
693:                         return defaultKeybindings;
694:                 }
695:                 final Set<String> ret = new LinkedHashSet<String>();
696:                 for (final VTKeyBinding binding : bindings.getBindings()) {
697:                         if (actionId.equals(binding.getId()) && binding.getKeySequence() != null
698:                                 && !binding.getKeySequence().isEmpty()) {
699:                                 ret.add(binding.getKeySequence());
700:                         }
701:                 }
702:                 return ret.toArray(new String[] {});
703:         }
704:
705:         /**
706:          * Creates an action bar.
707:          *
708:          * @param actionContext the {@link ViewerActionContext} to use
709:          * @param actionConfiguration the {@link ActionConfiguration} to use
710:          * @return a action bar builder
711:          *
712:          * @since 1.18
713:          */
714:         protected TableActionBar<? extends AbstractTableViewer> createActionBar(
715:                 TableRendererViewerActionContext actionContext, ActionConfiguration actionConfiguration) {
716:                 return new TableRendererActionBar(actionContext, actionConfiguration, getViewModelContext());
717:         }
718:
719:         /**
720:          * Creates the {@link TableViewerCompositeBuilder} used to get Composite hierarchy for this table renderer. This
721:          * method can be overwritten by sub classes to customize the layout.
722:          *
723:          * @return The {@link TableViewerCompositeBuilder}
724:          * @since 1.13
725:          */
726:         protected TableViewerCompositeBuilder createTableViewerCompositeBuilder() {
727:                 if (getTableStyleProperty().getRenderMode() == RenderMode.COMPACT_VERTICALLY) {
728:                         return new CompactVerticallyTableControlSWTRendererCompositeBuilder(false, false);
729:                 }
730:                 return new TableControlSWTRendererCompositeBuilder();
731:         }
732:
733:         /**
734:          * Creates a new {@link TableViewerSWTBuilder}.
735:          *
736:          * @param parent the parent {@link Composite}
737:          * @param list the input object
738:          * @param labelText the title
739:          * @param labelTooltipText the tooltip
740:          * @param compositeBuilder the {@link TableViewerCompositeBuilder}
741:          * @param cp the content provider
742:          * @param comparator the {@link ViewerComparator}; has no effect if move up/down
743:          * functionality is enabled
744:          * @param actionBar the {@link ActionBar}
745:          * @return the {@link TableViewerSWTBuilder}
746:          * @since 1.18
747:          * @deprecated Please use
748:          * {@link #createTableViewerSWTBuilder(Composite, IObservableList, IObservableValue, IObservableValue, TableViewerCompositeBuilder, ObservableListContentProvider, ViewerComparator, TableActionBar)}
749:          * instead
750:          */
751:         @Deprecated
752:         // CHECKSTYLE.OFF: ParameterNumber
753:         protected TableViewerSWTBuilder createTableViewerSWTBuilder(Composite parent,
754:                 @SuppressWarnings("rawtypes") IObservableList list,
755:                 @SuppressWarnings("rawtypes") IObservableValue labelText,
756:                 @SuppressWarnings("rawtypes") IObservableValue labelTooltipText, TableViewerCompositeBuilder compositeBuilder,
757:                 ObservableListContentProvider cp, ECPTableViewerComparator comparator,
758:                 TableActionBar<? extends AbstractTableViewer> actionBar) {
759:                 // CHECKSTYLE.ON: ParameterNumber
760:
761:                 return createTableViewerSWTBuilder(parent, list, labelText, labelTooltipText, compositeBuilder, cp,
762:                         (ViewerComparator) comparator,
763:                         actionBar);
764:         }
765:
766:         /**
767:          * Creates a new {@link TableViewerSWTBuilder}.
768:          *
769:          * @param parent the parent {@link Composite}
770:          * @param list the input object
771:          * @param labelText the title
772:          * @param labelTooltipText the tooltip
773:          * @param compositeBuilder the {@link TableViewerCompositeBuilder}
774:          * @param cp the content provider
775:          * @param comparator the {@link ViewerComparator}; has no effect if move up/down
776:          * functionality is enabled
777:          * @param actionBar the {@link ActionBar}
778:          * @return the {@link TableViewerSWTBuilder}
779:          * @since 1.22
780:          *
781:          */
782:         // CHECKSTYLE.OFF: ParameterNumber
783:         protected TableViewerSWTBuilder createTableViewerSWTBuilder(Composite parent,
784:                 @SuppressWarnings("rawtypes") IObservableList list,
785:                 @SuppressWarnings("rawtypes") IObservableValue labelText,
786:                 @SuppressWarnings("rawtypes") IObservableValue labelTooltipText, TableViewerCompositeBuilder compositeBuilder,
787:                 ObservableListContentProvider cp, ViewerComparator comparator,
788:                 TableActionBar<? extends AbstractTableViewer> actionBar) {
789:                 // CHECKSTYLE.ON: ParameterNumber
790:                 return TableViewerFactory.fillDefaults(parent, SWT.NONE, list, labelText, labelTooltipText)
791:                         .customizeCompositeStructure(compositeBuilder)
792:                         .customizeActionBar(actionBar)
793:                         .customizeTableViewerCreation(getTableViewerCreator())
794:                         .customizeContentProvider(cp)
795:                         .customizeComparator(comparator)
796:                         .customizeDragAndDrop(new TableControlSWTRendererDragAndDrop());
797:
798:         }
799:
800:         /**
801:          * Creates a new instance of the {@link TableViewerCreator} to be used.
802:          *
803:          * @return the {@link TableViewerCreator}
804:          * @since 1.10
805:          */
806:         protected TableViewerCreator<? extends AbstractTableViewer> getTableViewerCreator() {
807:                 return new TableControlSWTRendererTableViewerCreator();
808:         }
809:
810:         /**
811:          * Override this method to add additional static columns at the beginning of the table.
812:          *
813:          * @param tableViewerSWTBuilder the builder
814:          * @return the number of columns added
815:          * @since 1.9
816:          */
817:         protected int addAdditionalColumns(TableViewerSWTBuilder tableViewerSWTBuilder) {
818:                 return 0;
819:         }
820:
821:         /**
822:          * Returns the zero-relative index of the item which is currently selected in the receiver, or -1 if no item is
823:          * selected.
824:          *
825:          * @return the index of the selected item
826:          * @since 1.10
827:          */
828:         protected int getSelectionIndex() {
829:                 return ((TableViewer) tableViewer).getTable().getSelectionIndex();
830:         }
831:
832:         /**
833:          * Returns an array of {@link Item items} which are the columns in the table.
834:          *
835:          * @return the columns of the table
836:          * @since 1.10
837:          */
838:         protected Item[] getColumns() {
839:                 return ((TableViewer) tableViewer).getTable().getColumns();
840:         }
841:
842:         /**
843:          * Returns the receiver's horizontal scroll bar if it has one, and null if it does not.
844:          *
845:          * @return the horizontal scroll bar (or null)
846:          * @since 1.10
847:          */
848:         protected ScrollBar getHorizontalBar() {
849:                 return ((TableViewer) tableViewer).getTable().getHorizontalBar();
850:         }
851:
852:         /**
853:          * Returns the receiver's vertical scroll bar if it has one, and null if it does not.
854:          *
855:          * @return the vertical scroll bar (or null)
856:          * @since 1.10
857:          */
858:         protected ScrollBar getVerticalBar() {
859:                 return ((TableViewer) tableViewer).getTable().getVerticalBar();
860:         }
861:
862:         private void addResizeListener(final Control control, final int regularColumnsStartIndex) {
863:                 final ControlAdapter controlAdapter = new ControlAdapter() {
864:                         @Override
865:                         public void controlResized(ControlEvent e) {
866:                                 updateTableColumnWidths(control, regularColumnsStartIndex);
867:                         }
868:                 };
869:                 control.addControlListener(controlAdapter);
870:
871:                 tableViewerComposite.addColumnListener(controlAdapter);
872:         }
873:
874:         private void updateTableColumnWidths(Control table, int regularColumnsStartIndex) {
875:                 if (isDisposing) {
876:                         return;
877:                 }
878:                 final VTableControl tableControl = getVElement();
879:                 final Widget[] allColumns = tableViewerComposite.getColumns();
880:                 for (int i = regularColumnsStartIndex; i < allColumns.length; i++) {
881:                         final Widget tableColumn = allColumns[i];
882:                         final VDomainModelReference columnDMR = getColumnDomainModelReferences().get(i - regularColumnsStartIndex);
883:                         TableConfigurationHelper.updateWidthConfiguration(tableControl, columnDMR, tableColumn);
884:                 }
885:         }
886:
887:         private void initCompositeHeight() {
888:                 final VTTableStyleProperty styleProperty = getTableStyleProperty();
889:                 minimumHeight = styleProperty.isSetMinimumHeight() ? Optional.of(styleProperty.getMinimumHeight())
890:                         : Optional.<Integer> empty();
891:                 maximumHeight = styleProperty.isSetMaximumHeight() ? Optional.of(styleProperty.getMaximumHeight())
892:                         : Optional.<Integer> empty();
893:                 visibleLines = styleProperty.isSetVisibleLines() ? Optional.of(styleProperty.getVisibleLines())
894:                         : Optional.<Integer> empty();
895:         }
896:
897:         private <T> void addRelayoutListenerIfNeeded(IObservableList<T> list, final Composite composite) {
898:                 if (list == null) {
899:                         return;
900:                 }
901:
902:                 // relayout is only needed if min height != max height
903:                 if (!minimumHeight.isPresent() && !maximumHeight.isPresent()) {
904:                         return;
905:                 }
906:                 if (minimumHeight.isPresent() && maximumHeight.isPresent() && minimumHeight.get() == maximumHeight.get()) {
907:                         return;
908:                 }
909:
910:                 final GridData gridData = GridData.class.cast(composite.getLayoutData());
911:                 list.addListChangeListener(new IListChangeListener<T>() {
912:                         @Override
913:                         public void handleListChange(ListChangeEvent<? extends T> event) {
914:                                 gridData.heightHint = getTableHeightHint();
915:                                 EMFFormsSWTLayoutUtil.adjustParentSize(composite);
916:                         }
917:                 });
918:         }
919:
920:         /**
921:          * Adds the table columns to the {@link TableViewerSWTBuilder}.
922:          *
923:          * @param tableViewerSWTBuilder the builder
924:          * @param clazz the {@EClass} of the rendered object
925:          * @param cp the content provider
926:          * @param tableConfiguration
927:          *
928:          */
929:         private void addColumns(TableViewerSWTBuilder tableViewerSWTBuilder, EClass clazz,
930:                 ObservableListContentProvider cp) {
931:                 InternalEObject tempInstance = null;
932:                 if (!clazz.isAbstract() && !clazz.isInterface()) {
933:                         tempInstance = getInstanceOf(clazz);
934:                 }
935:
936:                 final List<VDomainModelReference> columns = getColumnDomainModelReferences().stream().filter(Objects::nonNull)
937:                         .collect(Collectors.toList());
938:                 /* regular columns */
939:                 columnFeatures = new EStructuralFeature[columns.size()];
940:                 for (int i = 0; i < columns.size(); i++) {
941:                         try {
942:                                 final VDomainModelReference dmr = columns.get(i);
943:                                 final IObservableValue<?> text = getLabelTextForColumn(dmr, clazz);
944:                                 final IObservableValue<?> tooltip = getLabelTooltipTextForColumn(dmr, clazz);
945:
946:                                 // Use the same editing domain for the columns as for the view's domain object
947:                                 final EditingDomain editingDomain = getEditingDomain(getViewModelContext().getDomainModel());
948:                                 final IValueProperty<?, ?> valueProperty = getEMFFormsDatabinding().getValueProperty(dmr, clazz,
949:                                         editingDomain);
950:                                 final EStructuralFeature eStructuralFeature = (EStructuralFeature) valueProperty.getValueType();
951:                                 columnFeatures[i] = eStructuralFeature;
952:
953:                                 @SuppressWarnings("unchecked")
954:                                 final IObservableMap<?, ?> observableMap = valueProperty.observeDetail(cp.getKnownElements());
955:
956:                                 final TableControlEditingSupportAndLabelProvider labelProvider = new TableControlEditingSupportAndLabelProvider(
957:                                         tempInstance, eStructuralFeature, dmr, valueProperty, observableMap,
958:                                         getColumnDomainModelReferences().indexOf(dmr));
959:                                 final EditingSupportCreator editingSupportCreator = TableConfigurationHelper
960:                                         .isReadOnly(getVElement(), dmr) ? null : labelProvider;
961:
962:                                 final Optional<Integer> weightConfig = TableConfigurationHelper.getColumnWeight(getVElement(), dmr);
963:                                 final Optional<Integer> widthConfig = TableConfigurationHelper.getColumnWidth(getVElement(), dmr);
964:
965:                                 int weight;
966:                                 int minWidth;
967:                                 if (weightConfig.isPresent()) {
968:                                         minWidth = widthConfig.get();
969:                                         weight = weightConfig.get();
970:                                 } else {
971:                                         // TODO ugly: we need this temporary cell editor so early just to get size information
972:                                         final Shell tempShell = new Shell();
973:                                         final CellEditor tempCellEditor = createCellEditor(tempInstance, eStructuralFeature,
974:                                                 new Table(tempShell, SWT.NONE));
975:                                         weight = ECPCellEditor.class.isInstance(tempCellEditor)
976:                                                 ? ECPCellEditor.class.cast(tempCellEditor).getColumnWidthWeight()
977:                                                 : 100;
978:                                         minWidth = ECPCellEditor.class.isInstance(tempCellEditor)
979:                                                 ? ECPCellEditor.class.cast(tempCellEditor).getMinWidth()
980:                                                 : 10;
981:                                         tempShell.dispose();
982:                                 }
983:
984:                                 tableViewerSWTBuilder.addColumn(
985:                                         ColumnConfigurationBuilder.from(tableViewerSWTBuilder)
986:                                                 .weight(weight)
987:                                                 .minWidth(minWidth)
988:                                                 .text(text)
989:                                                 .tooltip(tooltip)
990:                                                 .labelProviderFactory(labelProvider)
991:                                                 .editingSupportCreator(editingSupportCreator)
992:                                                 .dataMapEntry(ColumnConfiguration.DMR, dmr)
993:                                                 .build());
994:
995:                         } catch (final DatabindingFailedException ex) {
996:                                 getReportService().report(new RenderingFailedReport(ex));
997:                                 continue;
998:                         }
999:                 }
1000:
1001:         }
1002:
1003:         private void setupValidation(
1004:                 final AbstractTableViewerComposite<? extends AbstractTableViewer> tableViewerComposite) {
1005:                 if (tableViewerComposite.getValidationControls().isPresent()) {
1006:                         final List<Control> validationControls = tableViewerComposite.getValidationControls().get();
1007:                         if (validationControls.size() == 1 && Label.class.isInstance(validationControls.get(0))) {
1008:                                 validationIcon = (Label) validationControls.get(0);
1009:                         }
1010:
1011:                         final VTTableStyleProperty tableStyleProperty = getTableStyleProperty();
1012:                         showValidationSummaryTooltip = tableStyleProperty.isShowValidationSummaryTooltip();
1013:                 }
1014:         }
1015:
1016:         private void setupSorting(final TableViewerComparator comparator, int regularColumnsStartIndex,
1017:                 final AbstractTableViewerComposite<? extends AbstractTableViewer> tableViewerComposite) {
1018:
1019:                 final VTTableStyleProperty tableStyleProperty = getTableStyleProperty();
1020:                 if (!tableStyleProperty.isEnableSorting()) {
1021:                         return;
1022:                 }
1023:                 final int length = tableViewerComposite.getColumns().length;
1024:                 final List<Integer> sortableColumns = new ArrayList<Integer>();
1025:                 for (int i = 0; i < length; i++) {
1026:                         if (i >= regularColumnsStartIndex) {
1027:                                 sortableColumns.add(i);
1028:                         }
1029:                 }
1030:                 tableViewerComposite.setComparator(comparator, sortableColumns);
1031:         }
1032:
1033:         private IObservableValue<?> getLabelText(VDomainModelReference dmrToCheck) {
1034:                 switch (getVElement().getLabelAlignment()) {
1035:                 case NONE:
1036:                         return Observables.constantObservableValue("", String.class); //$NON-NLS-1$
1037:                 default:
1038:                         try {
1039:                                 return getEMFFormsLabelProvider().getDisplayName(dmrToCheck, getViewModelContext().getDomainModel());
1040:                         } catch (final NoLabelFoundException e) {
1041:                                 // FIXME Expectation?
1042:                                 getReportService().report(new RenderingFailedReport(e));
1043:                                 return Observables.constantObservableValue(e.getMessage(), String.class);
1044:                         }
1045:                 }
1046:         }
1047:
1048:         private IObservableValue<?> getLabelTextForColumn(VDomainModelReference dmrToCheck, EClass dmrRootEClass) {
1049:                 try {
1050:                         // See whether the view model specifies a label for the column
1051:                         final Optional<VEnablementConfiguration> config = TableConfigurationHelper
1052:                                 .findEnablementConfiguration(getVElement(), dmrToCheck);
1053:                         if (config.isPresent()) {
1054:                                 final String label = config.get().getLabel();
1055:                                 if (label != null && !label.isEmpty()) {
1056:                                         @SuppressWarnings("unchecked")
1057:                                         final IValueProperty<VElement, String> labelProperty = EMFProperties
1058:                                                 .value(VViewPackage.Literals.ELEMENT__LABEL);
1059:                                         return labelProperty.observe(config.get());
1060:                                 }
1061:                         }
1062:                         return getEMFFormsLabelProvider().getDisplayName(dmrToCheck, dmrRootEClass);
1063:                 } catch (final NoLabelFoundException e) {
1064:                         // FIXME Expectation?
1065:                         getReportService().report(new RenderingFailedReport(e));
1066:                         return Observables.constantObservableValue(e.getMessage(), String.class);
1067:                 }
1068:         }
1069:
1070:         private IObservableValue<?> getLabelTooltipText(VDomainModelReference dmrToCheck) {
1071:                 switch (getVElement().getLabelAlignment()) {
1072:                 case NONE:
1073:                         return Observables.constantObservableValue("", String.class); //$NON-NLS-1$
1074:                 default:
1075:                         try {
1076:                                 return getEMFFormsLabelProvider().getDescription(dmrToCheck, getViewModelContext().getDomainModel());
1077:                         } catch (final NoLabelFoundException e) {
1078:                                 // FIXME Expectation?
1079:                                 getReportService().report(new RenderingFailedReport(e));
1080:                                 return Observables.constantObservableValue(e.toString(), String.class);
1081:                         }
1082:                 }
1083:         }
1084:
1085:         private IObservableValue<?> getLabelTooltipTextForColumn(VDomainModelReference dmrToCheck, EClass dmrRootEClass) {
1086:                 try {
1087:                         return getEMFFormsLabelProvider().getDescription(dmrToCheck, dmrRootEClass);
1088:                 } catch (final NoLabelFoundException e) {
1089:                         // FIXME Expectation?
1090:                         getReportService().report(new RenderingFailedReport(e));
1091:                         return Observables.constantObservableValue(e.toString(), String.class);
1092:                 }
1093:         }
1094:
1095:         /**
1096:          * @return the {@link VDomainModelReference} which ends at the table setting
1097:          * @since 1.11
1098:          */
1099:         protected VDomainModelReference getDMRToMultiReference() {
1100:                 // A segment based view model uses the plain dmr
1101:                 if (getVElement().getDomainModelReference().eClass() == VViewPackage.Literals.DOMAIN_MODEL_REFERENCE) {
1102:                         return getVElement().getDomainModelReference();
1103:                 }
1104:
1105:                 final VTableDomainModelReference tableDomainModelReference = (VTableDomainModelReference) getVElement()
1106:                         .getDomainModelReference();
1107:                 final VDomainModelReference dmrToCheck = tableDomainModelReference.getDomainModelReference() == null
1108:                         ? tableDomainModelReference
1109:                         : tableDomainModelReference.getDomainModelReference();
1110:                 return dmrToCheck;
1111:         }
1112:
1113:         /**
1114:          * Allows to add additional buttons to the button bar of the table control.
1115:          * <p>
1116:          * The default implementation does not add additional buttons.
1117:          * </p>
1118:          *
1119:          * @param buttonComposite the composite where the buttons are added
1120:          * @return the total number of buttons added
1121:          */
1122:         protected int addButtonsToButtonBar(Composite buttonComposite) {
1123:                 return 0;
1124:         }
1125:
1126:         /**
1127:          * Creates and returns the composite which will be the parent for the table viewer.
1128:          *
1129:          * @param composite the parent composite including the title/button bar
1130:          * @return the parent for the table viewer
1131:          */
1132:         protected Composite createControlComposite(Composite composite) {
1133:                 final Composite controlComposite = new Composite(composite, SWT.NONE);
1134:                 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).hint(1, getTableHeightHint())
1135:                         .applyTo(controlComposite);
1136:                 GridLayoutFactory.fillDefaults().numColumns(1).applyTo(controlComposite);
1137:                 return controlComposite;
1138:         }
1139:
1140:         /**
1141:          * Returns the preferred height for the table. This will be passed to the layoutdata.
1142:          *
1143:          * @return the height in px
1144:          */
1145:         // BEGIN COMPLEX CODE
1146:         protected int getTableHeightHint() {
1147:                 /* if neither min nor max is set we use a fixed height */
1148:                 if (!minimumHeight.isPresent() && !maximumHeight.isPresent() && !visibleLines.isPresent()) {
1149:                         return 200;
1150:                 }
1151:                 // if the visible lines attribute is present, it takes precedence over the minimum & maximum height hints
1152:                 if (visibleLines.isPresent()) {
1153:                         return computeRequiredHeight(visibleLines.get());
1154:                 }
1155:                 if (minimumHeight.isPresent() && maximumHeight.isPresent() && minimumHeight.get() == maximumHeight.get()) {
1156:                         return minimumHeight.get();
1157:                 }
1158:
1159:                 final int requiredHeight = computeRequiredHeight(null);
1160:
1161:                 if (minimumHeight.isPresent() && !maximumHeight.isPresent()) {
1162:                         return requiredHeight < minimumHeight.get() ? minimumHeight.get() : requiredHeight;
1163:                 }
1164:
1165:                 if (!minimumHeight.isPresent() && maximumHeight.isPresent()) {
1166:                         return requiredHeight > maximumHeight.get() ? maximumHeight.get() : requiredHeight;
1167:                 }
1168:
1169:                 if (requiredHeight < minimumHeight.get()) {
1170:                         return minimumHeight.get();
1171:                 }
1172:
1173:                 if (requiredHeight > maximumHeight.get()) {
1174:                         return maximumHeight.get();
1175:                 }
1176:
1177:                 return requiredHeight;
1178:         }
1179:         // END COMPLEX CODE
1180:
1181:         /**
1182:          * Returns the height in pixels required to display the given number of table items. If the visible items are not
1183:          * specified, the height required to display all the table items is returned.
1184:          *
1185:          * @param visibleLines the number of visible table items
1186:          * @return the required height
1187:          * @since 1.13
1188:          */
1189:         protected int computeRequiredHeight(Integer visibleLines) {
1190:                 if (tableViewer == null) {
1191:                         return SWT.DEFAULT;
1192:                 }
1193:                 final TableControl table = tableViewerComposite.getTableControl();
1194:                 if (table == null) {
1195:                         return SWT.DEFAULT;
1196:                 }
1197:                 if (table.isDisposed()) {
1198:                         return SWT.DEFAULT;
1199:                 }
1200:                 final int itemHeight = table.getItemHeight();
1201:                 // show one empty row if table does not contain any items or visibleLines < 1
1202:                 int itemCount;
1203:                 if (visibleLines != null) {
1204:                         itemCount = Math.max(visibleLines, 1);
1205:                 } else {
1206:                         itemCount = Math.max(table.getItemCount(), 1);
1207:                 }
1208:                 final int headerHeight = table.getHeaderVisible() ? table.getHeaderHeight() : 0;
1209:                 // 4px needed as a buffer to avoid scrollbars
1210:                 final int tableHeight = itemHeight * itemCount + headerHeight + 4;
1211:                 return tableHeight;
1212:         }
1213:
1214:         /**
1215:          * Returns the table viewer.
1216:          *
1217:          * @return the viewer
1218:          * @since 1.10
1219:          */
1220:         protected AbstractTableViewer getTableViewer() {
1221:                 return tableViewer;
1222:         }
1223:
1224:         /**
1225:          * Returns the {@link AbstractTableViewerComposite}.
1226:          *
1227:          * @return the table viewer composite
1228:          * @since 1.13
1229:          */
1230:         @SuppressWarnings("rawtypes")
1231:         protected AbstractTableViewerComposite getTableViewerComposite() {
1232:                 return tableViewerComposite;
1233:         }
1234:
1235:         /**
1236:          * Sets the table viewer.
1237:          *
1238:          * @param tableViewer the viewer
1239:          * @since 1.10
1240:          */
1241:         protected void setTableViewer(AbstractTableViewer tableViewer) {
1242:                 this.tableViewer = tableViewer;
1243:         }
1244:
1245:         /**
1246:          * This method gets called when the selection on the {@link TableViewer} (see {@link #getTableViewer()}) has
1247:          * changed.
1248:          * <p>
1249:          * If you override this method make sure to call super.
1250:          * </p>
1251:          *
1252:          * @param event the {@link SelectionChangedEvent}
1253:          */
1254:         protected void viewerSelectionChanged(SelectionChangedEvent event) {
1255:                 // The default implementation does not need to do anything
1256:         }
1257:
1258:         private void createFixedValidationStatusColumn(TableViewerSWTBuilder tableViewerSWTBuilder) {
1259:                 final VTTableValidationStyleProperty tableValidationStyleProperty = getTableValidationStyleProperty();
1260:                 final int columnWidth = tableValidationStyleProperty.getColumnWidth();
1261:                 final String columnName = tableValidationStyleProperty.getColumnName();
1262:                 final String imagePath = tableValidationStyleProperty.getImagePath();
1263:                 Image image = null;
1264:                 if (imagePath != null && !imagePath.isEmpty()) {
1265:                         try {
1266:                                 image = getImage(new URL(imagePath));
1267:                         } catch (final MalformedURLException ex) {
1268:                                 getReportService().report(new AbstractReport(ex));
1269:                         }
1270:                 }
1271:
1272:                 tableViewerSWTBuilder.addColumn(
1273:                         ColumnConfigurationBuilder.usingDefaults()
1274:                                 .minWidth(columnWidth)
1275:                                 .text(columnName)
1276:                                 .tooltip(columnName)
1277:                                 .labelProvider(new ValidationStatusCellLabelProvider(getVElement()))
1278:                                 .image(image)
1279:                                 .build());
1280:
1281:         }
1282:
1283:         /**
1284:          * Retrieve images from the {@link ImageRegistryService} using an {@link URL}.
1285:          *
1286:          * @param url The {@link URL} pointing to the image
1287:          * @return The retrieved Image
1288:          * @since 1.6
1289:          */
1290:         protected Image getImage(URL url) {
1291:                 return imageRegistryService.getImage(url);
1292:         }
1293:
1294:         /**
1295:          * Retrieve images from the {@link ImageRegistryService} using a bundle relative path.
1296:          *
1297:          * @param path The bundle relative path pointing to the image
1298:          * @return The retrieved Image
1299:          * @since 1.6
1300:          */
1301:         protected Image getImage(String path) {
1302:                 Image result = imageRegistryService.getImage(FrameworkUtil.getBundle(getClass()), path);
1303:                 if (result == null && getClass() != TableControlSWTRenderer.class) {
1304:                         result = imageRegistryService.getImage(FrameworkUtil.getBundle(TableControlSWTRenderer.class), path);
1305:                 }
1306:                 return result;
1307:         }
1308:
1309:         /**
1310:          * Returns the {@link VTTableValidationStyleProperty}.
1311:          *
1312:          * @return the {@link VTTableValidationStyleProperty}
1313:          * @since 1.14
1314:          */
1315:         protected VTTableValidationStyleProperty getTableValidationStyleProperty() {
1316:                 VTTableValidationStyleProperty tableValidationStyleProperties = getStyleProperty(
1317:                         VTTableValidationStyleProperty.class);
1318:                 if (tableValidationStyleProperties == null) {
1319:                         tableValidationStyleProperties = createDefaultTableValidationStyleProperty();
1320:                 }
1321:                 return tableValidationStyleProperties;
1322:         }
1323:
1324:         /**
1325:          * Creates the default {@link VTTableValidationStyleProperty}.
1326:          *
1327:          * @return the default {@link VTTableValidationStyleProperty}
1328:          * @since 1.14
1329:          */
1330:         protected VTTableValidationStyleProperty createDefaultTableValidationStyleProperty() {
1331:                 final VTTableValidationStyleProperty tableValidationProp = VTTableValidationFactory.eINSTANCE
1332:                         .createTableValidationStyleProperty();
1333:                 tableValidationProp.setColumnWidth(80);
1334:                 tableValidationProp.setColumnName(LocalizationServiceHelper.getString(TableControlSWTRenderer.class,
1335:                         MessageKeys.TableControl_ValidationStatusColumn));
1336:                 tableValidationProp.setImagePath(null);
1337:                 return tableValidationProp;
1338:         }
1339:
1340:         /**
1341:          * Returns the {@link VTBackgroundStyleProperty}.
1342:          *
1343:          * @return the {@link VTBackgroundStyleProperty}
1344:          *
1345:          * @since 1.10
1346:          */
1347:         protected VTBackgroundStyleProperty getBackgroundStyleProperty() {
1348:                 VTBackgroundStyleProperty styleProperty = getStyleProperty(VTBackgroundStyleProperty.class);
1349:                 if (styleProperty == null) {
1350:                         styleProperty = createDefaultBackgroundStyleProperty();
1351:                 }
1352:                 return styleProperty;
1353:         }
1354:
1355:         /**
1356:          * Creates the default {@link VTBackgroundStyleProperty}.
1357:          *
1358:          * @return the default {@link VTBackgroundStyleProperty}
1359:          * @since 1.14
1360:          */
1361:         protected VTBackgroundStyleProperty createDefaultBackgroundStyleProperty() {
1362:                 return VTBackgroundFactory.eINSTANCE.createBackgroundStyleProperty();
1363:         }
1364:
1365:         /**
1366:          * Returns the {@link VTTableStyleProperty}.
1367:          *
1368:          * @return the {@link VTTableStyleProperty}
1369:          * @since 1.14
1370:          */
1371:         protected VTTableStyleProperty getTableStyleProperty() {
1372:                 VTTableStyleProperty styleProperty = getStyleProperty(VTTableStyleProperty.class);
1373:                 if (styleProperty == null) {
1374:                         styleProperty = createDefaultTableStyleProperty();
1375:                 }
1376:                 return styleProperty;
1377:         }
1378:
1379:         /**
1380:          * Creates the default {@link VTTableStyleProperty}.
1381:          *
1382:          * @return the default {@link VTTableStyleProperty}
1383:          * @since 1.14
1384:          */
1385:         protected VTTableStyleProperty createDefaultTableStyleProperty() {
1386:                 final VTTableStyleProperty tableStyleProperty = VTTableStylePropertyFactory.eINSTANCE
1387:                         .createTableStyleProperty();
1388:                 tableStyleProperty.setMaximumHeight(200);
1389:                 if (!getVElement().isEffectivelyReadonly()) {
1390:                         tableStyleProperty.setMinimumHeight(200);
1391:                 }
1392:                 return tableStyleProperty;
1393:         }
1394:
1395:         /**
1396:          * Returns the {@link VTFontPropertiesStyleProperty}.
1397:          *
1398:          * @return the {@link VTFontPropertiesStyleProperty}
1399:          * @since 1.10
1400:          */
1401:         protected VTFontPropertiesStyleProperty getFontPropertiesStyleProperty() {
1402:                 VTFontPropertiesStyleProperty styleProperty = getStyleProperty(VTFontPropertiesStyleProperty.class);
1403:                 if (styleProperty == null) {
1404:                         styleProperty = createDefaultFontPropertiesStyleProperty();
1405:                 }
1406:                 return styleProperty;
1407:         }
1408:
1409:         /**
1410:          * Creates the default {@link VTFontPropertiesStyleProperty}.
1411:          *
1412:          * @return the default {@link VTFontPropertiesStyleProperty}
1413:          * @since 1.14
1414:          */
1415:         protected VTFontPropertiesStyleProperty createDefaultFontPropertiesStyleProperty() {
1416:                 final VTFontPropertiesStyleProperty property = VTFontPropertiesFactory.eINSTANCE
1417:                         .createFontPropertiesStyleProperty();
1418:                 property.setColorHEX("000000"); //$NON-NLS-1$
1419:                 return property;
1420:         }
1421:
1422:         private <SP extends VTStyleProperty> SP getStyleProperty(Class<SP> stylePropertyClass) {
1423:                 return RendererUtil.getStyleProperty(getVTViewTemplateProvider(), getVElement(), getViewModelContext(),
1424:                         stylePropertyClass);
1425:         }
1426:
1427:         /**
1428:          * Returns the {@link Color} specified by the provided String.
1429:          *
1430:          * @param colorHex the Hex String describing the color
1431:          * @return the {@link Color}
1432:          * @since 1.10
1433:          *
1434:          */
1435:         protected Color getSWTColor(String colorHex) {
1436:                 final String redString = colorHex.substring(0, 2);
1437:                 final String greenString = colorHex.substring(2, 4);
1438:                 final String blueString = colorHex.substring(4, 6);
1439:                 final int red = Integer.parseInt(redString, 16);
1440:                 final int green = Integer.parseInt(greenString, 16);
1441:                 final int blue = Integer.parseInt(blueString, 16);
1442:                 return new Color(Display.getDefault(), red, green, blue);
1443:         }
1444:
1445:         /**
1446:          * Retrieves this table's column DMRs from the table DMR.
1447:          *
1448:          * @return The domain model references defining the columns of this table.
1449:          * @since 1.19
1450:          */
1451:         protected EList<VDomainModelReference> getColumnDomainModelReferences() {
1452:                 final VDomainModelReference dmr = getVElement().getDomainModelReference();
1453:                 final java.util.Optional<VMultiDomainModelReferenceSegment> multiSegment = MultiSegmentUtil
1454:                         .getMultiSegment(dmr);
1455:                 if (multiSegment.isPresent()) {
1456:                         return multiSegment.get().getChildDomainModelReferences();
1457:                 }
1458:                 return VTableDomainModelReference.class.cast(dmr).getColumnDomainModelReferences();
1459:         }
1460:
1461:         /**
1462:          * This is called in order to setup the editing support for a table column.
1463:          *
1464:          * @param tempInstance the temporary input instance of the table
1465:          * @param feature the feature of the column
1466:          * @param table the table/parent
1467:          * @return the cell editor
1468:          * @since 1.10
1469:          */
1470:         protected CellEditor createCellEditor(final EObject tempInstance, final EStructuralFeature feature,
1471:                 Composite table) {
1472:                 return CellEditorFactory.INSTANCE.getCellEditor(feature,
1473:                         tempInstance, table, getViewModelContext());
1474:         }
1475:
1476:         private InternalEObject getInstanceOf(EClass clazz) {
1477:                 return InternalEObject.class.cast(clazz.getEPackage().getEFactoryInstance().create(clazz));
1478:         }
1479:
1480:         /**
1481:          * This method shows a user confirmation dialog when the user attempts to delete a row in the table.
1482:          *
1483:          * @deprecated this method will be moved to {@link RemoveRowAction} in the future
1484:          * @param deletionList the list of selected EObjects to delete
1485:          * @param eObject The containment reference {@link EObject}
1486:          * @param structuralFeature The containment reference {@link EStructuralFeature}
1487:          * @param addButton the add button
1488:          * @param removeButton the remove button
1489:          * @since 1.6
1490:          */
1491:         @Deprecated
1492:         protected void deleteRowUserConfirmDialog(final List<EObject> deletionList, final EObject eObject,
1493:                 final EStructuralFeature structuralFeature, final Button addButton, final Button removeButton) {
1494:                 final MessageDialog dialog = new MessageDialog(addButton.getShell(),
1495:                         LocalizationServiceHelper.getString(TableControlSWTRenderer.class, MessageKeys.TableControl_Delete), null,
1496:                         LocalizationServiceHelper.getString(TableControlSWTRenderer.class,
1497:                                 MessageKeys.TableControl_DeleteAreYouSure),
1498:                         MessageDialog.CONFIRM, new String[] {
1499:                                 JFaceResources.getString(IDialogLabelKeys.YES_LABEL_KEY),
1500:                                 JFaceResources.getString(IDialogLabelKeys.NO_LABEL_KEY) },
1501:                         0);
1502:
1503:                 new ECPDialogExecutor(dialog) {
1504:
1505:                         @Override
1506:                         public void handleResult(int codeResult) {
1507:                                 if (codeResult == IDialogConstants.CANCEL_ID
1508:                                         || codeResult == SWT.DEFAULT) { // SWT.DEFAULT is return by closing a message dialog
1509:                                         return;
1510:                                 }
1511:
1512:                                 deleteRows(deletionList, eObject, structuralFeature);
1513:
1514:                                 final List<?> containments = (List<?>) eObject.eGet(structuralFeature, true);
1515:                                 if (containments.size() < structuralFeature.getUpperBound()) {
1516:                                         addButton.setEnabled(true);
1517:                                 }
1518:                                 if (containments.size() <= structuralFeature.getLowerBound()) {
1519:                                         removeButton.setEnabled(false);
1520:                                 }
1521:                         }
1522:                 }.execute();
1523:         }
1524:
1525:         /**
1526:          * This is called by {@link #deleteRowUserConfirmDialog(List)} after the user confirmed to delete the selected
1527:          * elements.
1528:          *
1529:          * @deprecated this method will be moved to {@link RemoveRowAction} in the future
1530:          * @param deletionList the list of {@link EObject EObjects} to delete
1531:          * @param eObject The containment reference {@link EObject}
1532:          * @param structuralFeature The containment reference {@link EStructuralFeature}
1533:          * @since 1.6
1534:          */
1535:         @Deprecated
1536:         protected void deleteRows(List<EObject> deletionList, final EObject eObject,
1537:                 final EStructuralFeature structuralFeature) {
1538:
1539:                 final EditingDomain editingDomain = getEditingDomain(eObject);
1540:
1541:                 /* assured by #isApplicable */
1542:                 final EReference reference = EReference.class.cast(structuralFeature);
1543:                 final List<Object> toDelete = new ArrayList<Object>(deletionList);
1544:                 if (reference.isContainment()) {
1545:                         DeleteService deleteService = getViewModelContext().getService(DeleteService.class);
1546:                         if (deleteService == null) {
1547:                                 /*
1548:                                  * #getService(Class<?>) will report to the reportservice if it could not be found
1549:                                  * Use Default
1550:                                  */
1551:                                 deleteService = new EMFDeleteServiceImpl();
1552:                         }
1553:                         deleteService.deleteElements(toDelete);
1554:                 } else {
1555:                         removeElements(editingDomain, eObject, reference, toDelete);
1556:                 }
1557:         }
1558:
1559:         private void removeElements(EditingDomain editingDomain, Object source, EStructuralFeature feature,
1560:                 Collection<Object> toRemove) {
1561:                 final Command removeCommand = RemoveCommand.create(editingDomain, source, feature, toRemove);
1562:                 if (removeCommand.canExecute()) {
1563:                         if (editingDomain.getCommandStack() == null) {
1564:                                 removeCommand.execute();
1565:                         } else {
1566:                                 editingDomain.getCommandStack().execute(removeCommand);
1567:                         }
1568:                 }
1569:         }
1570:
1571:         /**
1572:          * This method is called to add a new entry in the domain model and thus to create a new row in the table. The
1573:          * element to create is defined by the provided class.
1574:          * You can override this method but you have to call super nonetheless.
1575:          *
1576:          * @deprecated this method will be move to {@link AddRowAction} in the future
1577:          * @param clazz the {@link EClass} defining the EObject to create
1578:          * @param eObject The containment reference {@link EObject}
1579:          * @param structuralFeature The containment reference {@link EStructuralFeature}
1580:          * @since 1.6
1581:          */
1582:         @Deprecated
1583:         protected void addRow(EClass clazz, EObject eObject, EStructuralFeature structuralFeature) {
1584:                 Optional<EObject> eObjectToAdd = null;
1585:
1586:                 /* table service available => use specific behavior to create row */
1587:                 if (getViewModelContext().hasService(TableControlService.class)) {
1588:                         final TableControlService tableService = getViewModelContext()
1589:                                 .getService(TableControlService.class);
1590:                         eObjectToAdd = tableService.createNewElement(clazz, eObject, structuralFeature);
1591:                 }
1592:                 /* no table service available, fall back to default */
1593:                 if (eObjectToAdd == null) {
1594:                         final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
1595:                         eObjectToAdd = referenceService.addNewModelElements(eObject, EReference.class.cast(structuralFeature),
1596:                                 false);
1597:                 }
1598:
1599:                 if (!eObjectToAdd.isPresent()) {
1600:                         return;
1601:                 }
1602:
1603:                 final EObject instance = eObjectToAdd.get();
1604:                 final EditingDomain editingDomain = getEditingDomain(eObject);
1605:                 if (editingDomain == null) {
1606:                         return;
1607:                 }
1608:                 editingDomain.getCommandStack().execute(
1609:                         AddCommand.create(editingDomain, eObject, structuralFeature, instance));
1610:                 tableViewer.setSelection(new StructuredSelection(eObjectToAdd.get()), true);
1611:
1612:         }
1613:
1614:         @Override
1615:         protected void applyValidation(VDiagnostic oldDiagnostic, VDiagnostic newDiagnostic) {
1616:                 getRunnableManager().executeAsync(new ComputeValidationUpdate(oldDiagnostic, newDiagnostic));
1617:         }
1618:
1619:         @Override
1620:         protected void applyValidation() {
1621:                 if (!getRunnableManager().isRunning()) {
1622:                         getRunnableManager().executeAsync(new ApplyValidationRunnable());
1623:                 }
1624:         }
1625:
1626:         /**
1627:          * Obtain my runnable manager (visible for testability).
1628:          *
1629:          * @return my runnable manager
1630:          */
1631:         final RunnableManager getRunnableManager() {
1632:                 return runnableManager;
1633:         }
1634:
1635:         /**
1636:          * Post a request to refresh my {@linkplain #getTableViewer() viewer} on the
1637:          * asynchronous refresh manager.
1638:          *
1639:          * @since 1.21
1640:          * @see #getTableViewer()
1641:          * @see ViewerRefreshManager
1642:          */
1643:         protected void postRefresh() {
1644:                 postRefresh(getRefreshRunnable(getTableViewer()));
1645:         }
1646:
1647:         /**
1648:          * Post a refresh request on the asynchronous refresh manager.
1649:          *
1650:          * @param refreshRunnable the refresh operation to execute
1651:          * @since 1.21
1652:          * @see ViewerRefreshManager
1653:          */
1654:         protected void postRefresh(Runnable refreshRunnable) {
1655:                 final Viewer viewer = getTableViewer();
1656:                 if (viewer != null && !viewer.getControl().isDisposed()) {
1657:                         getRunnableManager().executeAsync(refreshRunnable);
1658:                 }
1659:         }
1660:
1661:         /**
1662:          * Returns the add button created by the framework.
1663:          *
1664:          * @deprecated use {@link #getControlForAction(String)} instead
1665:          * @return the addButton
1666:          * @since 1.6
1667:          */
1668:         @Deprecated
1669:         protected Button getAddButton() {
1670:                 final Optional<Control> control = getControlForAction(AddRowAction.ACTION_ID);
1671:                 if (control.isPresent()) {
1672:                         return (Button) control.get();
1673:                 }
1674:                 return null; // happens when add/remove has been disabled in the view model
1675:         }
1676:
1677:         /**
1678:          * Returns the remove button created by the framework.
1679:          *
1680:          * @deprecated use {@link #getControlForAction(String)} instead
1681:          * @return the removeButton
1682:          * @since 1.6
1683:          */
1684:         @Deprecated
1685:         protected Button getRemoveButton() {
1686:                 final Optional<Control> control = getControlForAction(RemoveRowAction.ACTION_ID);
1687:                 if (control.isPresent()) {
1688:                         return (Button) control.get();
1689:                 }
1690:                 return null; // happens when add/remove has been disabled in the view model
1691:         }
1692:
1693:         /**
1694:          * Returns the control created to trigger a certain action.
1695:          *
1696:          * @param actionId the action ID of the action that is bound to the control
1697:          * @return an optional for the control of the given action ID
1698:          * @since 1.18
1699:          */
1700:         public Optional<Control> getControlForAction(String actionId) {
1701:                 if (tableViewerComposite.getActionBar().isPresent()) {
1702:                         return tableViewerComposite.getActionBar().get().getControlById(actionId);
1703:                 }
1704:                 return Optional.empty();
1705:         }
1706:
1707:         /**
1708:          * Helper method which uses an EMFForms observable value to get the setting for the given
1709:          * {@link VDomainModelReference}.
1710:          *
1711:          * @param dmr the {@link VDomainModelReference}
1712:          * @param eObject the {@link EObject} to get the setting for
1713:          * @return an Optional<Setting>
1714:          *
1715:          * @since 1.17
1716:          */
1717:         protected Optional<Setting> getSettingFromObservable(VDomainModelReference dmr, EObject eObject) {
1718:                 @SuppressWarnings("rawtypes")
1719:                 IObservableValue observableValue = null;
1720:                 try {
1721:                         observableValue = getEMFFormsDatabinding().getObservableValue(dmr, eObject);
1722:
1723:                         final EStructuralFeature feature = (EStructuralFeature) observableValue.getValueType();
1724:                         final EObject observed = (EObject) ((IObserving) observableValue).getObserved();
1725:
1726:                         // Given EClass has no such feature
1727:                         if (observed == null || observed.eClass().getFeatureID(feature) == -1) {
1728:                                 return Optional.empty();
1729:                         }
1730:
1731:                         return Optional.of(((InternalEObject) observed).eSetting(feature));
1732:
1733:                 } catch (final DatabindingFailedException ex) {
1734:                         getReportService().report(new DatabindingFailedReport(ex));
1735:                         return Optional.empty();
1736:                 } finally {
1737:                         if (observableValue != null) {
1738:                                 observableValue.dispose();
1739:                         }
1740:                 }
1741:
1742:         }
1743:
1744:         @Override
1745:         protected void applyEnable() {
1746:                 updateActionBar();
1747:         }
1748:
1749:         @Override
1750:         protected void applyReadOnly() {
1751:                 updateActionBar();
1752:         }
1753:
1754:         @Override
1755:         protected void applyUnchangeableFeature() {
1756:                 getVElement().setAddRemoveDisabled(true);
1757:                 getVElement().setDuplicateDisabled(true);
1758:                 getVElement().setMoveUpDownDisabled(true);
1759:                 updateActionBar();
1760:         }
1761:
1762:         /**
1763:          * Update this table control's action bar if it is present.
1764:          *
1765:          * @since 1.20
1766:          */
1767:         protected void updateActionBar() {
1768:                 if (tableViewerComposite != null && tableViewerComposite.getActionBar().isPresent()) {
1769:                         tableViewerComposite.getActionBar().get().updateActionBar();
1770:                 }
1771:         }
1772:
1773:         @Override
1774:         protected void dispose() {
1775:                 isDisposing = true;
1776:                 rendererGridDescription = null;
1777:                 viewModelDBC.dispose();
1778:                 if (list != null) {
1779:                         list.dispose();
1780:                 }
1781:                 if (columnIndexToComparatorMap != null) {
1782:                         for (final ECPCellEditorComparator value : columnIndexToComparatorMap.values()) {
1783:                                 if (value instanceof CellEditor) {
1784:                                         ((CellEditor) value).dispose();
1785:                                 }
1786:                         }
1787:                         columnIndexToComparatorMap.clear();
1788:                 }
1789:                 if (tableViewerComposite != null) {
1790:                         tableViewerComposite.dispose();
1791:                         tableViewerComposite = null;
1792:                 }
1793:                 if (tableViewer != null) {
1794:                         tableViewer.getControl().dispose();
1795:                         tableViewer = null;
1796:                 }
1797:
1798:                 if (customization != null) {
1799:                         for (final ColumnConfiguration columnConfig : customization.getColumnConfigurations()) {
1800:                                 columnConfig.visible().dispose();
1801:                                 columnConfig.dispose();
1802:                         }
1803:                         customization.getTableConfiguration().dispose();
1804:                 }
1805:
1806:                 if (autoSortModelChangeListener != null) {
1807:                         getViewModelContext().unregisterDomainChangeListener(autoSortModelChangeListener);
1808:                         autoSortModelChangeListener = null;
1809:                 }
1810:                 super.dispose();
1811:         }
1812:
1813:         /**
1814:          * Get called by the {@link ECPTableViewerComparator} in order to compare the given objects.
1815:          *
1816:          * @param viewer the table viewer
1817:          * @param left the first object of the comparison
1818:          * @param right the second object of the comparison
1819:          * @param propertyIndex index of the selection column. the index is aligned with the index of the associated column
1820:          * domain model reference
1821:          * @param direction 0 (no sorting = insertion order := {@link SWT#NONE}), 1 (ascending := {@link SWT#DOWN}) or 2
1822:          * (descending := {@link SWT#UP}) according to the indication displayed at
1823:          * the table column.
1824:          * @return a negative number if the first element is less than the
1825:          * second element; the value <code>0</code> if the first element is
1826:          * equal to the second element; and a positive number if the first
1827:          * element is greater than the second element
1828:          * @since 1.8
1829:          */
1830:         // BEGIN COMPLEX CODE
1831:         @SuppressWarnings("unchecked")
1832:         protected int compare(Viewer viewer, Object left, Object right, int direction, int propertyIndex) {
1833:                 if (direction == 0) {
1834:                         return 0;
1835:                 }
1836:
1837:                 // We might have ignored columns at the beginning
1838:                 propertyIndex = propertyIndex - regularColumnsStartIndex;
1839:                 if (propertyIndex < 0) {
1840:                         return 0;
1841:                 }
1842:                 int rc = 0;
1843:
1844:                 final VDomainModelReference dmr = getColumnDomainModelReferences().get(propertyIndex);
1845:
1846:                 final Optional<Setting> leftSetting = getSettingFromObservable(dmr, (EObject) left);
1847:                 final Optional<Setting> rightSetting = getSettingFromObservable(dmr, (EObject) right);
1848:
1849:                 final Object leftValue = leftSetting.isPresent() ? leftSetting.get().get(true) : null;
1850:                 final Object rightValue = rightSetting.isPresent() ? rightSetting.get().get(true) : null;
1851:
1852:                 if (columnIndexToComparatorMap.containsKey(propertyIndex)) {
1853:                         return columnIndexToComparatorMap.get(propertyIndex).compare(leftValue, rightValue, direction);
1854:                 }
1855:
1856:                 if (leftValue == null) {
1857:                         if (rightValue == null) {
1858:                                 rc = 0;
1859:                         } else {
1860:                                 rc = 1;
1861:                         }
1862:                 } else if (rightValue == null) {
1863:                         rc = -1;
1864:                 } else {
1865:                         if (leftValue instanceof Enumerator) {
1866:                                 final EStructuralFeature feature = leftSetting.get().getEStructuralFeature();
1867:                                 rc = getEnumeratorComparator().compare(feature, (Enumerator) leftValue, (Enumerator) rightValue);
1868:                         } else if (!(leftValue instanceof String) && leftValue instanceof Comparable
1869:                                 && leftValue.getClass().isInstance(rightValue)) {
1870:                                 rc = Comparable.class.cast(leftValue).compareTo(rightValue);
1871:                         } else {
1872:                                 rc = NumberAwareStringComparator.getInstance().compare(leftValue.toString(), rightValue.toString());
1873:                         }
1874:                 }
1875:                 // If descending order, flip the direction
1876:                 if (direction == 2) {
1877:                         rc = -rc;
1878:                 }
1879:                 return rc;
1880:         }
1881:         // END COMPLEX CODE
1882:
1883:         @Override
1884:         protected void rootDomainModelChanged() throws DatabindingFailedException {
1885:
1886:                 final IObservableList<?> oldList = (IObservableList<?>) getTableViewer().getInput();
1887:                 oldList.dispose();
1888:
1889:                 final EObject domainModel = getViewModelContext().getDomainModel();
1890:                 final IObservableList<?> list = getEMFFormsDatabinding().getObservableList(getDMRToMultiReference(),
1891:                         domainModel);
1892:                 getTableViewer().setInput(list);
1893:
1894:                 if (tableViewerComposite.getActionBar().isPresent()) {
1895:                         tableViewerComposite.getActionBar().get().updateActionBar();
1896:                 }
1897:         }
1898:
1899:         /**
1900:          * Checks whether an element is editable or not.
1901:          *
1902:          * @param element The list entry to be checked
1903:          * @return True if the element can be edited, false otherwise
1904:          *
1905:          * @since 1.11
1906:          */
1907:         protected boolean canEditObject(Object element) {
1908:                 return true;
1909:         }
1910:
1911:         /**
1912:          * Defined whether a cell editor should be created or not.
1913:          *
1914:          * @param element The table entry to be checked
1915:          * @return True if a CellEditor should be created, false otherwise
1916:          * @since 1.12
1917:          */
1918:         protected boolean shouldCreateCellEditor(Object element) {
1919:                 final boolean isObjectEditable = canEditObject(element);
1920:                 if (!isObjectEditable) {
1921:                         return false;
1922:                 }
1923:                 final boolean editable = getVElement().isEffectivelyEnabled()
1924:                         && !getVElement().isEffectivelyReadonly();
1925:                 return editable;
1926:         }
1927:
1928:         private boolean isDisabled(EObject eObject, VDomainModelReference columnDmr) {
1929:
1930:                 final Optional<VEnablementConfiguration> enablmentConf = TableConfigurationHelper
1931:                         .findEnablementConfiguration(getVElement(), columnDmr);
1932:
1933:                 if (enablmentConf.isPresent()) {
1934:                         return !enablmentConf.get().isEnabled();
1935:                 }
1936:
1937:                 return false;
1938:         }
1939:
1940:         /**
1941:          * Called by the {@link TableControlEditingSupportAndLabelProvider}.
1942:          *
1943:          * @param feature the feature of the column
1944:          * @param cellEditor the cell editor for the column
1945:          * @param attributeMap the attribute map displayed in the table
1946:          * @param vTableControl the table view model element
1947:          * @param dmr the domain model reference of the column
1948:          * @param table the table control
1949:          * @return the {@link CellLabelProvider} of the column
1950:          * @since 1.12
1951:          */
1952:         protected CellLabelProvider createCellLabelProvider(
1953:                 EStructuralFeature feature,
1954:                 CellEditor cellEditor,
1955:                 @SuppressWarnings("rawtypes") IObservableMap attributeMap,
1956:                 VTableControl vTableControl,
1957:                 VDomainModelReference dmr,
1958:                 Control table) {
1959:                 return new ECPCellLabelProvider(
1960:                         feature,
1961:                         cellEditor,
1962:                         attributeMap,
1963:                         getVElement(),
1964:                         dmr,
1965:                         table);
1966:         }
1967:
1968:         /**
1969:          * @return The {@link FeatureAwareComparator} used to compare {@link Enumerator enum values} when sorting the table
1970:          * @since 1.22
1971:          */
1972:         protected FeatureAwareComparator<Enumerator> getEnumeratorComparator() {
1973:                 if (enumeratorComparator == null) {
1974:                         enumeratorComparator = new LocalizedEnumeratorComparator(localizationService, bundleResolver,
1975:                                 getReportService());
1976:                 }
1977:                 return enumeratorComparator;
1978:         }
1979:
1980:         /**
1981:          * Reeveal the given {@code object} in my table.
1982:          *
1983:          * @param object an object to reveal
1984:          * @return whether I succeeded in revealing it
1985:          *
1986:          * @since 1.22
1987:          */
1988:         public boolean reveal(Object object) {
1989:                 final AbstractTableViewer viewer = getTableViewer();
1990:                 final IObservableList<?> list = (IObservableList<?>) viewer.getInput();
1991:                 final boolean result = list.contains(object);
1992:
1993:                 if (result) {
1994:                         final IStructuredSelection selection = new StructuredSelection(object);
1995:                         if (!selection.equals(viewer.getSelection())) {
1996:                                 viewer.setSelection(selection, true);
1997:                         }
1998:                 } else {
1999:                         viewer.reveal(object);
2000:                 }
2001:
2002:                 return result;
2003:         }
2004:
2005:         /**
2006:          * The {@link DNDProvider} for this renderer.
2007:          *
2008:          * @author Johannes Faltermeier
2009:          *
2010:          */
2011:         private final class TableControlSWTRendererDragAndDrop implements DNDProvider {
2012:
2013:                 /**
2014:                  * The drop adapter.
2015:                  */
2016:                 private final class TableControlDropAdapter extends EditingDomainViewerDropAdapter {
2017:
2018:                         private EObject eObject;
2019:                         private EStructuralFeature eStructuralFeature;
2020:                         private List<Object> list;
2021:
2022:                         @SuppressWarnings("unchecked")
2023:                         TableControlDropAdapter(EditingDomain domain, Viewer viewer, AbstractTableViewer tableViewer) {
2024:                                 super(domain, viewer);
2025:                                 try {
2026:                                         final Setting setting = getEMFFormsDatabinding().getSetting(getDMRToMultiReference(),
2027:                                                 getViewModelContext().getDomainModel());
2028:                                         eObject = setting.getEObject();
2029:                                         eStructuralFeature = setting.getEStructuralFeature();
2030:                                         list = (List<Object>) setting.get(true);
2031:                                 } catch (final DatabindingFailedException ex) {
2032:                                         getReportService().report(new AbstractReport(ex));
2033:                                 }
2034:                         }
2035:
2036:                         @Override
2037:                         protected void helper(DropTargetEvent event) {
2038:                                 final Object target = extractDropTarget(event.item);
2039:                                 final Collection<?> dragSource = getDragSource(event);
2040:
2041:                                 if (dragSource == null) {
2042:                                         /* possible on non-win32 platforms */
2043:                                         /* in this case we will just wait until the data is available without setting a detail */
2044:                                         return;
2045:                                 }
2046:
2047:                                 if (target == null || dragSource.contains(target)) {
2048:                                         event.detail = DND.DROP_NONE;
2049:                                         return;
2050:                                 }
2051:
2052:                                 event.detail = DND.DROP_MOVE;
2053:                         }
2054:
2055:                         @Override
2056:                         public void drop(DropTargetEvent event) {
2057:
2058:                                 final Collection<?> dragSource = getDragSource(event);
2059:                                 final Object target = extractDropTarget(event.item);
2060:                                 final float location = getLocation(event);
2061:
2062:                                 final List<Command> commands = new ArrayList<Command>();
2063:                                 final boolean insertAfter = location >= 0.5;
2064:
2065:                                 for (final Object toMove : dragSource) {
2066:                                         final int indexTarget = list.indexOf(target);
2067:                                         final int indexToMove = list.indexOf(toMove);
2068:
2069:                                         if (indexTarget == -1 || indexToMove == -1) {
2070:                                                 return;
2071:                                         }
2072:
2073:                                         final boolean moveIsLocatedBeforeTarget = indexToMove < indexTarget;
2074:
2075:                                         int index;
2076:                                         if (insertAfter) {
2077:                                                 if (moveIsLocatedBeforeTarget) {
2078:                                                         index = indexTarget;
2079:                                                 } else {
2080:                                                         index = indexTarget + 1;
2081:                                                 }
2082:                                         } else {
2083:                                                 /* insert Before Target */
2084:                                                 if (moveIsLocatedBeforeTarget) {
2085:                                                         index = indexTarget - 1;
2086:                                                 } else {
2087:                                                         index = indexTarget;
2088:                                                 }
2089:                                         }
2090:
2091:                                         commands.add(MoveCommand.create(domain, eObject, eStructuralFeature, toMove, index));
2092:                                 }
2093:
2094:                                 final Command command = new CompoundCommand(commands);
2095:
2096:                                 if (!command.canExecute()) {
2097:                                         return;
2098:                                 }
2099:                                 domain.getCommandStack().execute(command);
2100:
2101:                                 postRefresh();
2102:                         }
2103:                 }
2104:
2105:                 @Override
2106:                 public int getDragOperations() {
2107:                         return getDNDOperations();
2108:                 }
2109:
2110:                 @Override
2111:                 public Transfer[] getDragTransferTypes() {
2112:                         return getDNDTransferTypes();
2113:                 }
2114:
2115:                 @Override
2116:                 public DragSourceListener getDragListener(AbstractTableViewer tableViewer) {
2117:                         return new ViewerDragAdapter(tableViewer);
2118:                 }
2119:
2120:                 @Override
2121:                 public int getDropOperations() {
2122:                         return getDNDOperations();
2123:                 }
2124:
2125:                 @Override
2126:                 public Transfer[] getDropTransferTypes() {
2127:                         return getDNDTransferTypes();
2128:                 }
2129:
2130:                 @Override
2131:                 public DropTargetListener getDropListener(final AbstractTableViewer tableViewer) {
2132:                         return new TableControlDropAdapter(getEditingDomain(getViewModelContext().getDomainModel()), tableViewer,
2133:                                 tableViewer);
2134:                 }
2135:
2136:                 private int getDNDOperations() {
2137:                         return DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
2138:                 }
2139:
2140:                 private Transfer[] getDNDTransferTypes() {
2141:                         return new Transfer[] { LocalTransfer.getInstance() };
2142:                 }
2143:
2144:                 @Override
2145:                 public boolean hasDND() {
2146:                         return true;
2147:                 }
2148:         }
2149:
2150:         /**
2151:          * Double click listener.
2152:          *
2153:          */
2154:         private final class DoubleClickListener implements IDoubleClickListener {
2155:                 @Override
2156:                 public void doubleClick(DoubleClickEvent event) {
2157:                         if (!getViewModelContext().hasService(TableControlService.class)) {
2158:                                 return;
2159:                         }
2160:                         final ISelection selection = event.getSelection();
2161:                         if (!StructuredSelection.class.isInstance(selection)) {
2162:                                 return;
2163:                         }
2164:                         final TableControlService tableService = getViewModelContext()
2165:                                 .getService(TableControlService.class);
2166:                         tableService.doubleClick(getVElement(),
2167:                                 (EObject) StructuredSelection.class.cast(selection).getFirstElement());
2168:                 }
2169:         }
2170:
2171:         /**
2172:          * The Listener which is set on the table viewer to inform the renderer about selection changes.
2173:          *
2174:          */
2175:         private final class ViewerSelectionChangedListener implements ISelectionChangedListener {
2176:                 @Override
2177:                 public void selectionChanged(SelectionChangedEvent event) {
2178:                         viewerSelectionChanged(event);
2179:                 }
2180:         }
2181:
2182:         /**
2183:          * A two-stage asynchronous task that first, in the background, computes the objects
2184:          * that have actually seen validation changes and then post the viewer update
2185:          * on the UI thread.
2186:          */
2187:         private final class ComputeValidationUpdate implements Runnable, RunnableManager.BackgroundStage {
2188:                 private final Set<Diagnostic> oldDiagnostics;
2189:                 private final Set<Diagnostic> newDiagnostics;
2190:
2191:                 private Runnable update;
2192:
2193:                 /**
2194:                  * Initializes me with the old and new diagnostics from which to compute the objects
2195:                  * that need to be updated in my viewer.
2196:                  *
2197:                  * @param oldDiagnostic the old validation state
2198:                  * @param newDiagnostic the new validation state
2199:                  */
2200:                 ComputeValidationUpdate(VDiagnostic oldDiagnostic, VDiagnostic newDiagnostic) {
2201:                         super();
2202:
2203:                         // These have to be extracted on the calling thread because the ELists
2204:                         // are not thread-safe
2205:                         oldDiagnostics = getDiagnostics(oldDiagnostic);
2206:                         newDiagnostics = getDiagnostics(newDiagnostic);
2207:                 }
2208:
2209:                 @Override
2210:                 public Runnable getNextStage() {
2211:                         return update;
2212:                 }
2213:
2214:                 @Override
2215:                 public void run() {
2216:                         // Compute the difference between the diagnostics to find what actually
2217:                         // are the logical changes in validation that will need updates in the UI
2218:                         final Set<Diagnostic> difference = difference(oldDiagnostics, newDiagnostics);
2219:                         final Set<Object> updates = difference.stream().map(this::getSubject).filter(Objects::nonNull)
2220:                                 .collect(Collectors.toSet());
2221:                         if (!updates.isEmpty()) {
2222:                                 update = new ApplyValidationRunnable(updates);
2223:                         } // Otherwise we don't need the UI update stage
2224:                 }
2225:
2226:                 private Set<Diagnostic> getDiagnostics(VDiagnostic container) {
2227:                         return container == null ? Collections.emptySet()
2228:                                 : container.getDiagnostics().stream()
2229:                                         .filter(Diagnostic.class::isInstance).map(Diagnostic.class::cast)
2230:                                         .collect(Collectors.toSet());
2231:                 }
2232:
2233:                 private Object getSubject(Diagnostic diagnostic) {
2234:                         final List<?> data = diagnostic.getData();
2235:                         return data.isEmpty() ? null : data.get(0);
2236:                 }
2237:
2238:                 private Set<Diagnostic> difference(Set<Diagnostic> set1, Set<Diagnostic> set2) {
2239:                         // Diagnostics do not implement equals(), so we have to do it for them.
2240:                         // Most straightforward approach is to use a tree set, which uses the
2241:                         // comparator's zero result to test equality
2242:                         final SortedSet<Diagnostic> sorted1 = new TreeSet<>(this::compare);
2243:                         sorted1.addAll(set1);
2244:                         final SortedSet<Diagnostic> sorted2 = new TreeSet<>(this::compare);
2245:                         sorted2.addAll(set2);
2246:
2247:                         // Difference each side
2248:                         sorted1.removeAll(set2);
2249:                         sorted2.removeAll(set1);
2250:
2251:                         // And the union of that
2252:                         sorted1.addAll(sorted2);
2253:
2254:                         return sorted1;
2255:                 }
2256:
2257:                 private int compare(Diagnostic d1, Diagnostic d2) {
2258:                         int result = d1.getSeverity() - d2.getSeverity();
2259:                         if (result != 0) {
2260:                                 return result;
2261:                         }
2262:
2263:                         result = d1.getCode() - d2.getCode();
2264:                         if (result != 0) {
2265:                                 return result;
2266:                         }
2267:
2268:                         result = compare(d1.getSource(), d2.getSource());
2269:                         if (result != 0) {
2270:                                 return result;
2271:                         }
2272:
2273:                         result = compare(d1.getMessage(), d2.getMessage());
2274:                         if (result != 0) {
2275:                                 return result;
2276:                         }
2277:
2278:                         result = compare(d1.getData(), d2.getData());
2279:
2280:                         // The diagnostics from validation should not have exceptions and
2281:                         // the children are immaterial because we've already flattened them
2282:
2283:                         return result;
2284:                 }
2285:
2286:                 private int compare(String s1, String s2) {
2287:                         if (s1 == null) {
2288:                                 return s2 == null ? 0 : -1;
2289:                         }
2290:                         if (s2 == null) {
2291:                                 return +1;
2292:                         }
2293:                         return s1.compareTo(s2);
2294:                 }
2295:
2296:                 private int compare(List<?> s1, List<?> s2) {
2297:                         if (s1 == null) {
2298:                                 return s2 == null ? 0 : -1;
2299:                         }
2300:                         if (s2 == null) {
2301:                                 return +1;
2302:                         }
2303:
2304:                         final int size = s1.size();
2305:                         int result = size - s2.size();
2306:                         if (result != 0) {
2307:                                 return result;
2308:                         }
2309:
2310:                         int i = 0;
2311:                         for (i = 0; i < size; i++) {
2312:                                 final Object e1 = s1.get(i);
2313:                                 final Object e2 = s2.get(i);
2314:                                 if (e1 != e2) {
2315:                                         // Arbitrary but stable order
2316:                                         result = System.identityHashCode(e1) - System.identityHashCode(e2);
2317:                                         break;
2318:                                 }
2319:                         }
2320:
2321:                         return result;
2322:                 }
2323:         }
2324:
2325:         /**
2326:          * Runnable which is called by {@link TableControlSWTRenderer#applyValidation() applyValidation}.
2327:          *
2328:          */
2329:         private final class ApplyValidationRunnable implements Runnable {
2330:                 private Collection<?> updates;
2331:
2332:                 ApplyValidationRunnable() {
2333:                         super();
2334:                 }
2335:
2336:                 ApplyValidationRunnable(Collection<?> updates) {
2337:                         super();
2338:
2339:                         this.updates = updates;
2340:                 }
2341:
2342:                 @Override
2343:                 public void run() {
2344:                         if (isDisposing) {
2345:                                 return;
2346:                         }
2347:                         // triggered due to another validation rule before this control is rendered
2348:                         // validation rule triggered after the control was disposed
2349:                         if (validationIcon == null || validationIcon.isDisposed()) {
2350:                                 return;
2351:                         }
2352:
2353:                         // no diagnostic set
2354:                         if (getVElement().getDiagnostic() == null) {
2355:                                 return;
2356:                         }
2357:
2358:                         final VDomainModelReference dmr = getDMRToMultiReference();
2359:                         final Optional<Setting> setting = getSettingFromObservable(dmr, getViewModelContext().getDomainModel());
2360:
2361:                         if (!setting.isPresent()) {
2362:                                 return;
2363:                         }
2364:
2365:                         validationIcon.setImage(getValidationIcon());
2366:                         showValidationSummaryTooltip(setting.get(), showValidationSummaryTooltip);
2367:
2368:                         if (updates != null) {
2369:                                 // Update these specific objects
2370:                                 getTableViewer().update(updates.toArray(), null);
2371:                         } else {
2372:                                 // Just refresh everything. We are already in the RunnableManager
2373:                                 // context, so don't post but do it directly
2374:                                 getTableViewer().refresh();
2375:                         }
2376:                 }
2377:
2378:                 // extracted in order to avoid checkstyle complexity warning
2379:                 private void showValidationSummaryTooltip(Setting tableSetting, boolean doShow) {
2380:                         if (doShow) {
2381:                                 validationIcon.setToolTipText(ECPTooltipModifierHelper.modifyString(getVElement().getDiagnostic()
2382:                                         .getMessage(), null));
2383:                                 return;
2384:                         }
2385:
2386:                         // Even if the display of a validation summary tooltip is disabled, we still show validation errors directly
2387:                         // related to the table feature (e.g. multiplicity errors)
2388:                         final StringBuilder builder = new StringBuilder();
2389:                         final List<Diagnostic> tableDiagnostics = getVElement().getDiagnostic()
2390:                                 .getDiagnostic(tableSetting.getEObject(), tableSetting.getEStructuralFeature());
2391:                         for (final Diagnostic diagnostic : tableDiagnostics) {
2392:                                 builder.append(diagnostic.getMessage());
2393:                                 builder.append("\n"); //$NON-NLS-1$
2394:                         }
2395:                         final String toolTipText = ECPTooltipModifierHelper.modifyString(builder.toString().trim(), null);
2396:                         validationIcon.setToolTipText(toolTipText);
2397:                 }
2398:         }
2399:
2400:         /**
2401:          * Implements {@link EditingSupportCreator} and {@link CellLabelProviderFactory} for the table control swt renderer.
2402:          *
2403:          * This allows us to access the actual cell editor from the cell label provider.
2404:          *
2405:          * @author Johannes Faltermeier
2406:          *
2407:          */
2408:         protected final class TableControlEditingSupportAndLabelProvider
2409:                 implements EditingSupportCreator, CellLabelProviderFactory {
2410:                 private final InternalEObject tempInstance;
2411:                 private final EStructuralFeature eStructuralFeature;
2412:                 private final VDomainModelReference dmr;
2413:                 private final IValueProperty<?, ?> valueProperty;
2414:                 private final IObservableMap<?, ?> observableMap;
2415:
2416:                 private CellEditor cellEditor;
2417:                 private ECPTableEditingSupport observableSupport;
2418:
2419:                 private boolean initialized;
2420:                 private final int indexOfColumn;
2421:
2422:                 private TableControlEditingSupportAndLabelProvider(InternalEObject tempInstance,
2423:                         EStructuralFeature eStructuralFeature, VDomainModelReference dmr,
2424:                         IValueProperty<?, ?> valueProperty, IObservableMap<?, ?> observableMap, int indexOfColumn) {
2425:                         this.tempInstance = tempInstance;
2426:                         this.eStructuralFeature = eStructuralFeature;
2427:                         this.dmr = dmr;
2428:                         this.valueProperty = valueProperty;
2429:                         this.observableMap = observableMap;
2430:                         this.indexOfColumn = indexOfColumn;
2431:                 }
2432:
2433:                 @Override
2434:                 public EditingSupport createEditingSupport(AbstractTableViewer tableViewer) {
2435:                         if (!initialized) {
2436:                                 init(tableViewer);
2437:                         }
2438:                         return observableSupport;
2439:                 }
2440:
2441:                 private void init(AbstractTableViewer tableViewer) {
2442:                         cellEditor = createCellEditor(tempInstance, eStructuralFeature,
2443:                                 (Composite) tableViewer.getControl());
2444:                         if (cellEditor instanceof ECPViewerAwareCellEditor) {
2445:                                 ((ECPViewerAwareCellEditor) cellEditor).setTableViewer(tableViewer);
2446:                                 ((ECPViewerAwareCellEditor) cellEditor).setTableFeature((EReference) list.getElementType());
2447:                         }
2448:                         tableViewer.getControl().addDisposeListener(new DisposeListener() {
2449:
2450:                                 @Override
2451:                                 public void widgetDisposed(DisposeEvent arg0) {
2452:                                         cellEditor.dispose();
2453:                                 }
2454:                         });
2455:                         if (ECPCellEditorComparator.class.isInstance(cellEditor)) {
2456:                                 columnIndexToComparatorMap.put(indexOfColumn, ECPCellEditorComparator.class.cast(cellEditor));
2457:                         }
2458:                         observableSupport = new ECPTableEditingSupport(tableViewer, cellEditor, dmr, valueProperty);
2459:                         initialized = true;
2460:                 }
2461:
2462:                 @Override
2463:                 public CellLabelProvider createCellLabelProvider(AbstractTableViewer table) {
2464:                         if (!initialized) {
2465:                                 init(table);
2466:                         }
2467:                         return TableControlSWTRenderer.this.createCellLabelProvider(eStructuralFeature, cellEditor, observableMap,
2468:                                 getVElement(), dmr, table.getControl());
2469:                 }
2470:         }
2471:
2472:         /**
2473:          * {@link TableViewerCreator} for the table control swt renderer. It will create a GridTableViewer with the expected
2474:          * custom variant data and the correct style properties as defined in the template model.
2475:          *
2476:          * @since 1.10
2477:          *
2478:          */
2479:         protected class TableControlSWTRendererTableViewerCreator implements TableViewerCreator<TableViewer> {
2480:
2481:                 @Override
2482:                 public TableViewer createTableViewer(Composite parent) {
2483:                         final TableViewer tableViewer = new TableViewer(parent,
2484:                                 SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
2485:                         tableViewer.getTable().setData(CUSTOM_VARIANT, TABLE_CUSTOM_VARIANT);
2486:                         tableViewer.getTable().setHeaderVisible(true);
2487:                         tableViewer.getTable().setLinesVisible(true);
2488:
2489:                         /* Set background color */
2490:                         final VTBackgroundStyleProperty backgroundStyleProperty = getBackgroundStyleProperty();
2491:•                        if (backgroundStyleProperty.getColor() != null) {
2492:                                 tableViewer.getTable().setBackground(getSWTColor(backgroundStyleProperty.getColor()));
2493:                         }
2494:
2495:                         /* Set foreground color */
2496:                         final VTFontPropertiesStyleProperty fontPropertiesStyleProperty = getFontPropertiesStyleProperty();
2497:•                        if (fontPropertiesStyleProperty.getColorHEX() != null) {
2498:                                 tableViewer.getTable()
2499:                                         .setForeground(getSWTColor(fontPropertiesStyleProperty.getColorHEX()));
2500:                         }
2501:
2502:                         tableViewer.getTable().setData(FIXED_COLUMNS, new Integer(1));
2503:
2504:                         /* manage editing support activation */
2505:                         createTableViewerEditor(tableViewer);
2506:                         return tableViewer;
2507:                 }
2508:
2509:                 /**
2510:                  * This method creates and initialises a {@link TableViewerEditor} for the given {@link TableViewer}.
2511:                  *
2512:                  * @param tableViewer the table viewer
2513:                  */
2514:                 protected void createTableViewerEditor(final TableViewer tableViewer) {
2515:                         final TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer,
2516:                                 new org.eclipse.emf.ecp.edit.internal.swt.controls.ECPFocusCellDrawHighlighter(tableViewer));
2517:                         final ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(
2518:                                 tableViewer) {
2519:                                 @Override
2520:                                 protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
2521:                                         return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
2522:                                                 || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION
2523:                                                 || event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR
2524:                                                 || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
2525:                                 }
2526:                         };
2527:                         TableViewerEditor.create(
2528:                                 tableViewer,
2529:                                 focusCellManager,
2530:                                 actSupport,
2531:                                 ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
2532:                                         | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);
2533:                 }
2534:         }
2535:
2536:         /**
2537:          * {@link org.eclipse.emfforms.spi.swt.table.TableViewerCompositeBuilder TableViewerCompositeBuilder} which calls
2538:          * the existing template method to create the validation label.
2539:          *
2540:          * @since 1.13
2541:          *
2542:          */
2543:         protected class TableControlSWTRendererCompositeBuilder
2544:                 extends org.eclipse.emfforms.spi.swt.table.DefaultTableViewerCompositeBuilder {
2545:                 @Override
2546:                 protected Label createValidationLabel(Composite topComposite) {
2547:                         final Label validationLabel = createValidationIcon(topComposite);
2548:                         GridDataFactory.fillDefaults().hint(16, 17).grab(false, false).applyTo(validationLabel);
2549:                         return validationLabel;
2550:                 }
2551:
2552:                 @Override
2553:                 protected Composite createViewerComposite(Composite composite) {
2554:                         return createControlComposite(composite);
2555:                 }
2556:         }
2557:
2558:         /**
2559:          * {@link org.eclipse.emfforms.spi.swt.table.TableViewerCompositeBuilder TableViewerCompositeBuilder} which calls
2560:          * the existing template method to create the validation label.
2561:          *
2562:          * @since 1.14
2563:          *
2564:          */
2565:         protected class CompactVerticallyTableControlSWTRendererCompositeBuilder
2566:                 extends org.eclipse.emfforms.spi.swt.table.CompactVerticallyTableViewerCompositeBuilder {
2567:                 /**
2568:                  * Constructor.
2569:                  *
2570:                  * @param createTitleLabel indicates whether to create a title label.
2571:                  * @param createValidationLabel indicates whether to create a validation label.
2572:                  */
2573:                 public CompactVerticallyTableControlSWTRendererCompositeBuilder(
2574:                         boolean createTitleLabel, boolean createValidationLabel) {
2575:                         super(createTitleLabel, createValidationLabel);
2576:                 }
2577:
2578:                 @Override
2579:                 protected Label createValidationLabel(Composite composite) {
2580:                         final Label validationLabel = createValidationIcon(composite);
2581:                         GridDataFactory.fillDefaults().hint(16, 17).grab(false, false).applyTo(validationLabel);
2582:                         return validationLabel;
2583:                 }
2584:
2585:                 @Override
2586:                 protected Composite createViewerComposite(Composite composite) {
2587:                         return createControlComposite(composite);
2588:                 }
2589:         }
2590:
2591:         /**
2592:          * This class combines the {@link ViewerComparator} with the {@link TableViewerComparator}.
2593:          * This is needed to allow customizations.
2594:          *
2595:          * @author Eugen Neufeld
2596:          * @since 1.22
2597:          */
2598:         protected abstract class TableControlComparator extends ViewerComparator implements TableViewerComparator {
2599:         }
2600:
2601:         /**
2602:          * The {@link ViewerComparator} for this table which allows 3 states for sort order:
2603:          * none, up and down.
2604:          *
2605:          * @author Eugen Neufeld
2606:          * @since 1.10
2607:          *
2608:          */
2609:         protected class ECPTableViewerComparator extends TableControlComparator {
2610:                 private int propertyIndex;
2611:                 private static final int NONE = 0;
2612:                 private int direction = NONE;
2613:
2614:                 /** Constructs a new instance. */
2615:                 ECPTableViewerComparator() {
2616:                         propertyIndex = 0;
2617:                         direction = NONE;
2618:                 }
2619:
2620:                 @Override
2621:                 public int getDirection() {
2622:                         switch (direction) {
2623:                         case 0:
2624:                                 return SWT.NONE;
2625:                         case 1:
2626:                                 return SWT.DOWN; // ascending
2627:                         case 2:
2628:                                 return SWT.UP; // descending
2629:                         default:
2630:                                 return SWT.NONE;
2631:                         }
2632:
2633:                 }
2634:
2635:                 @Override
2636:                 public void setColumn(int column) {
2637:                         if (column == propertyIndex) {
2638:                                 // Same column as last sort; toggle the direction
2639:                                 direction = (direction + 1) % 3;
2640:                         } else {
2641:                                 // New column; do an ascending sort
2642:                                 propertyIndex = column;
2643:                                 direction = 1;
2644:                         }
2645:                         // No sorting is the same as no column being selected for sorting
2646:                         if (direction == NONE) {
2647:                                 setSortColumnFeature(null);
2648:                         } else {
2649:                                 // columnFeatures starts at index 0 with the first regular column
2650:                                 setSortColumnFeature(getColumnFeature(propertyIndex));
2651:                         }
2652:                 }
2653:
2654:                 @Override
2655:                 public int compare(Viewer viewer, Object e1, Object e2) {
2656:                         return TableControlSWTRenderer.this.compare(viewer, e1, e2, direction, propertyIndex);
2657:                 }
2658:         }
2659:
2660:         /**
2661:          * Domain model change listener that re-sorts the table and reveals the added resp. changed object.
2662:          *
2663:          * @since 1.20
2664:          */
2665:         protected class AutoSortModelChangeListener implements ModelChangeListener {
2666:
2667:                 @Override
2668:                 public void notifyChange(ModelChangeNotification notification) {
2669:                         final int event = notification.getRawNotification().getEventType();
2670:                         if (notification.getStructuralFeature() == tableEReference
2671:                                 && getSortColumnFeature().isPresent()
2672:                                 && (event == Notification.ADD || event == Notification.ADD_MANY)) {
2673:                                 sortAndReveal(notification.getNewEObjects());
2674:                         } else if (getSortColumnFeature().isPresent()
2675:                                 && notification.getStructuralFeature() == getSortColumnFeature().get()) {
2676:                                 sortAndReveal(notification.getNotifier());
2677:                         }
2678:                 }
2679:
2680:                 private void sortAndReveal(Object toReveal) {
2681:                         final AbstractTableViewer viewer = getTableViewer();
2682:                         postRefresh(() -> {
2683:                                 viewer.refresh();
2684:                                 viewer.reveal(toReveal);
2685:                         });
2686:                 }
2687:         }
2688:
2689:         /**
2690:          * ECP specific cell label provider that does also implement {@link IColorProvider} in
2691:          * order to correctly.
2692:          *
2693:          * @author emueller
2694:          *
2695:          */
2696:         public class ECPCellLabelProvider extends ObservableMapCellLabelProvider implements IColorProvider, IAdaptable {
2697:
2698:                 private final EStructuralFeature feature;
2699:                 private final CellEditor cellEditor;
2700:                 private final VTableControl vTableControl;
2701:                 private final VDomainModelReference dmr;
2702:                 private final Control table;
2703:
2704:                 /**
2705:                  * Constructor.
2706:                  *
2707:                  * @param feature
2708:                  * the {@link EStructuralFeature} the cell is bound to
2709:                  * @param cellEditor
2710:                  * the {@link CellEditor} instance
2711:                  * @param attributeMap
2712:                  * an {@link IObservableMap} instance that is passed to the {@link ObservableMapCellLabelProvider}
2713:                  * @param vTableControl the {@link VTableControl}
2714:                  * @param dmr the {@link VDomainModelReference} for this cell
2715:                  * @param table the swt table
2716:                  * @since 1.10
2717:                  */
2718:                 public ECPCellLabelProvider(EStructuralFeature feature, CellEditor cellEditor,
2719:                         @SuppressWarnings("rawtypes") IObservableMap attributeMap,
2720:                         VTableControl vTableControl, VDomainModelReference dmr, Control table) {
2721:                         super(attributeMap);
2722:                         this.vTableControl = vTableControl;
2723:                         this.feature = feature;
2724:                         this.cellEditor = cellEditor;
2725:                         this.dmr = dmr;
2726:                         this.table = table;
2727:                 }
2728:
2729:                 @Override
2730:                 public String getToolTipText(Object element) {
2731:                         final EObject domainObject = (EObject) element;
2732:
2733:                         final Optional<Setting> setting = getSettingFromObservable(dmr, domainObject);
2734:                         if (!setting.isPresent()) {
2735:                                 return null;
2736:                         }
2737:
2738:                         final VDiagnostic vDiagnostic = vTableControl.getDiagnostic();
2739:                         if (vDiagnostic != null) {
2740:                                 final String message = DiagnosticMessageExtractor.getMessage(vDiagnostic.getDiagnostic(domainObject,
2741:                                         feature));
2742:                                 if (message != null && !message.isEmpty()) {
2743:                                         return ECPTooltipModifierHelper.modifyString(message, setting.get());
2744:                                 }
2745:                         }
2746:                         final Object value = setting.get().get(true);
2747:                         if (value == null) {
2748:                                 return null;
2749:                         }
2750:                         final String tooltip = ECPTooltipModifierHelper.modifyString(String.valueOf(value), setting.get());
2751:                         if (tooltip == null || tooltip.isEmpty()) {
2752:                                 return null;
2753:                         }
2754:                         return tooltip;
2755:
2756:                 }
2757:
2758:                 @Override
2759:                 public void update(ViewerCell cell) {
2760:                         final EObject element = (EObject) cell.getElement();
2761:                         final Object value = getValue(element);
2762:                         if (ECPCustomUpdateCellEditor.class.isInstance(cellEditor)) {
2763:                                 ((ECPCustomUpdateCellEditor) cellEditor).updateCell(cell, value);
2764:                         } else {
2765:                                 String text;
2766:                                 Image image = null;
2767:
2768:                                 if (ECPCellEditor.class.isInstance(cellEditor)) {
2769:                                         final ECPCellEditor ecpCellEditor = (ECPCellEditor) cellEditor;
2770:                                         text = Objects.toString(ecpCellEditor.getFormatedString(value), ""); //$NON-NLS-1$
2771:                                         image = ecpCellEditor.getImage(value);
2772:                                 } else {
2773:                                         text = Objects.toString(value, ""); //$NON-NLS-1$
2774:                                         cell.getControl().setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_edit_cellEditor_string"); //$NON-NLS-1$
2775:                                 }
2776:
2777:                                 if (!Objects.equals(text, cell.getText())) {
2778:                                         cell.setText(text);
2779:                                 }
2780:
2781:                                 // Don't try to compare images
2782:                                 if (image != null || cell.getImage() != null) {
2783:                                         cell.setImage(image);
2784:                                 }
2785:                         }
2786:
2787:                         final Color foreground = getForeground(element);
2788:                         if (!Objects.equals(cell.getForeground(), foreground)) {
2789:                                 cell.setForeground(foreground);
2790:                         }
2791:                         final Color background = getBackground(element);
2792:                         if (!Objects.equals(cell.getBackground(), background)) {
2793:                                 cell.setBackground(background);
2794:                         }
2795:                 }
2796:
2797:                 /**
2798:                  * Get the value for an {@code object} from my observable map.
2799:                  *
2800:                  * @param object an object to look up the value for
2801:                  * @return its value
2802:                  */
2803:                 Object getValue(Object object) {
2804:                         return attributeMaps[0].get(object);
2805:                 }
2806:
2807:                 @Override
2808:                 public Color getForeground(Object element) {
2809:                         return table.getForeground();
2810:                 }
2811:
2812:                 @Override
2813:                 public Color getBackground(Object element) {
2814:                         final VDiagnostic vDiagnostic = vTableControl.getDiagnostic();
2815:                         final SWTValidationUiService validationUiService = getViewModelContext()
2816:                                 .getService(SWTValidationUiService.class);
2817:                         if (vDiagnostic == null) {
2818:                                 return validationUiService.getValidationBackgroundColor(Diagnostic.OK_INSTANCE, vTableControl,
2819:                                         getViewModelContext());
2820:                         }
2821:                         final List<Diagnostic> diagnostic = vDiagnostic.getDiagnostic((EObject) element, feature);
2822:                         final Diagnostic iconDiagnostic = diagnostic.size() == 0 ? Diagnostic.OK_INSTANCE : diagnostic.get(0);
2823:                         return validationUiService.getValidationBackgroundColor(iconDiagnostic, vTableControl,
2824:                                 getViewModelContext());
2825:                 }
2826:
2827:                 /**
2828:                  * @return the cellEditor
2829:                  */
2830:                 protected CellEditor getCellEditor() {
2831:                         return cellEditor;
2832:                 }
2833:
2834:                 /**
2835:                  * @return the feature
2836:                  */
2837:                 protected EStructuralFeature getFeature() {
2838:                         return feature;
2839:                 }
2840:
2841:                 /**
2842:                  * @return the dmr
2843:                  */
2844:                 protected VDomainModelReference getDmr() {
2845:                         return dmr;
2846:                 }
2847:
2848:                 @Override
2849:                 public <T> T getAdapter(Class<T> adapter) {
2850:                         T result = null;
2851:
2852:                         // For custom cell update, we must ask the cell editor to render a string for filtering
2853:                         if (adapter == ECPFilterableCell.class && !(cellEditor instanceof ECPCustomUpdateCellEditor)) {
2854:                                 ECPFilterableCell filterable = null;
2855:
2856:                                 if (cellEditor instanceof ECPCellEditor) {
2857:                                         final ECPCellEditor ecpCellEditor = (ECPCellEditor) cellEditor;
2858:                                         filterable = object -> ecpCellEditor.getFormatedString(getValue(object));
2859:                                 } else {
2860:                                         filterable = object -> Objects.toString(object, ""); //$NON-NLS-1$
2861:                                 }
2862:
2863:                                 result = adapter.cast(filterable);
2864:                         } else {
2865:                                 result = Platform.getAdapterManager().getAdapter(this, adapter);
2866:                         }
2867:
2868:                         return result;
2869:                 }
2870:
2871:         }
2872:
2873:         /**
2874:          * Implementation of the {@link EditingSupport} for the generic ECP Table.
2875:          *
2876:          * @author Eugen Neufeld
2877:          *
2878:          */
2879:         class ECPTableEditingSupport extends EditingSupport {
2880:
2881:                 private final CellEditor cellEditor;
2882:
2883:                 @SuppressWarnings("rawtypes")
2884:                 private final IValueProperty valueProperty;
2885:
2886:                 private final VDomainModelReference domainModelReference;
2887:
2888:                 /**
2889:                  * Initializes me.
2890:                  *
2891:                  * @param viewer the viewer to edit
2892:                  * @param cellEditor the cell editor used to edit the {@code viewer}
2893:                  * @param domainModelReference the model reference that populates the {@code viewer}
2894:                  * @param valueProperty the value property that supplies the {@code viewer}
2895:                  */
2896:                 ECPTableEditingSupport(ColumnViewer viewer, CellEditor cellEditor, VDomainModelReference domainModelReference,
2897:                         IValueProperty<?, ?> valueProperty) {
2898:                         super(viewer);
2899:                         this.cellEditor = cellEditor;
2900:                         this.valueProperty = valueProperty;
2901:                         this.domainModelReference = domainModelReference;
2902:                 }
2903:
2904:                 private EditingState editingState;
2905:
2906:                 private final ColumnViewerEditorActivationListenerHelper activationListener = new ColumnViewerEditorActivationListenerHelper();
2907:
2908:                 /**
2909:                  * Default implementation always returns <code>true</code>.
2910:                  *
2911:                  * @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
2912:                  */
2913:                 @Override
2914:                 protected boolean canEdit(Object element) {
2915:                         if (!shouldCreateCellEditor(element)) {
2916:                                 return false;
2917:                         }
2918:
2919:                         // TODO: use getSettingFromObservable(dmr, eObject) instead?
2920:                         @SuppressWarnings("unchecked")
2921:                         final IObservableValue<?> observableValue = valueProperty.observe(element);
2922:                         final EObject eObject = (EObject) ((IObserving) observableValue).getObserved();
2923:
2924:                         final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
2925:
2926:                         // Given EClass has no such feature
2927:                         if (eObject == null || eObject.eClass().getFeatureID(structuralFeature) == -1) {
2928:                                 return false;
2929:                         }
2930:
2931:                         final Setting setting = ((InternalEObject) eObject).eSetting(structuralFeature);
2932:
2933:                         if (isDisabled(eObject, domainModelReference)
2934:                                 || CellReadOnlyTesterHelper.getInstance().isReadOnly(getVElement(), setting)) {
2935:                                 return false;
2936:                         }
2937:
2938:                         final boolean editable = emfFormsEditSupport.canSetProperty(domainModelReference, (EObject) element);
2939:
2940:                         if (ECPCellEditor.class.isInstance(cellEditor)) {
2941:                                 ECPCellEditor.class.cast(cellEditor).setEditable(editable);
2942:                                 return true;
2943:                         }
2944:                         return editable;
2945:                 }
2946:
2947:                 /**
2948:                  * Default implementation always returns <code>null</code> as this will be
2949:                  * handled by the Binding.
2950:                  *
2951:                  * @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
2952:                  */
2953:                 @Override
2954:                 protected Object getValue(Object element) {
2955:                         // no op
2956:                         return null;
2957:                 }
2958:
2959:                 /**
2960:                  * Default implementation does nothing as this will be handled by the
2961:                  * Binding.
2962:                  *
2963:                  * @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, java.lang.Object)
2964:                  */
2965:                 @Override
2966:                 protected void setValue(Object element, Object value) {
2967:                         // no op
2968:                 }
2969:
2970:                 /**
2971:                  * Creates a {@link Binding} between the editor and the element to be
2972:                  * edited. Invokes {@link #doCreateCellEditorObservable(CellEditor)},
2973:                  * {@link #doCreateElementObservable(Object, ViewerCell)}, and then
2974:                  * {@link #createBinding(IObservableValue, IObservableValue)}.
2975:                  */
2976:                 @Override
2977:                 protected void initializeCellEditorValue(CellEditor cellEditor, ViewerCell cell) {
2978:
2979:                         if (ECPElementAwareCellEditor.class.isInstance(cellEditor)) {
2980:                                 ECPElementAwareCellEditor.class.cast(cellEditor).updateRowElement(cell.getElement());
2981:                         }
2982:
2983:                         final IObservableValue<?> target = doCreateCellEditorObservable(cellEditor);
2984:                         Assert.isNotNull(target, "doCreateCellEditorObservable(...) did not return an observable"); //$NON-NLS-1$
2985:
2986:                         @SuppressWarnings("unchecked")
2987:                         final IObservableValue<?> model = valueProperty.observe(cell.getElement());
2988:                         Assert.isNotNull(model, "The databinding service did not return an observable"); //$NON-NLS-1$
2989:
2990:                         final Binding binding = createBinding(target, model);
2991:
2992:                         Assert.isNotNull(binding, "createBinding(...) did not return a binding"); //$NON-NLS-1$
2993:
2994:                         editingState = new EditingState(binding, target, model);
2995:
2996:                         getViewer().getColumnViewerEditor().addEditorActivationListener(activationListener);
2997:                 }
2998:
2999:                 @Override
3000:                 protected CellEditor getCellEditor(Object element) {
3001:                         return cellEditor;
3002:                 }
3003:
3004:                 /**
3005:                  * Bind a {@code model} value to a {@code target} value.
3006:                  *
3007:                  * @param target the binding target
3008:                  * @param model the binding source
3009:                  * @return the binding
3010:                  */
3011:                 @SuppressWarnings({ "rawtypes", "unchecked" })
3012:                 protected Binding createBinding(IObservableValue target, IObservableValue model) {
3013:                         if (ECPCellEditor.class.isInstance(cellEditor)) {
3014:                                 return getDataBindingContext().bindValue(target, model,
3015:                                         ((ECPCellEditor) cellEditor).getTargetToModelStrategy(getDataBindingContext()),
3016:                                         ((ECPCellEditor) cellEditor).getModelToTargetStrategy(getDataBindingContext()));
3017:                         }
3018:                         return getDataBindingContext().bindValue(target, model);
3019:                 }
3020:
3021:                 /**
3022:                  * Create an observable value to supply the cells edited by a cell editor.
3023:                  *
3024:                  * @param cellEditor a cell editor
3025:                  * @return the observable value to supply it with data to edit
3026:                  */
3027:                 @SuppressWarnings({ "rawtypes", "unchecked" })
3028:                 protected IObservableValue doCreateCellEditorObservable(CellEditor cellEditor) {
3029:                         if (ECPCellEditor.class.isInstance(cellEditor)) {
3030:                                 return ((ECPCellEditor) cellEditor).getValueProperty().observe(cellEditor);
3031:                         }
3032:                         return WidgetProperties.text(SWT.FocusOut).observe(cellEditor.getControl());
3033:                 }
3034:
3035:                 @Override
3036:                 protected final void saveCellEditorValue(CellEditor cellEditor, ViewerCell cell) {
3037:                         if (editingState.isUpdateNeeded()) {
3038:                                 editingState.binding.updateTargetToModel();
3039:                         }
3040:                 }
3041:
3042:                 /**
3043:                  * A ColumnViewerEditorActivationListener to reset the cells after focus lost.
3044:                  *
3045:                  * @author Eugen Neufeld
3046:                  *
3047:                  */
3048:                 private class ColumnViewerEditorActivationListenerHelper extends ColumnViewerEditorActivationListener {
3049:
3050:                         @Override
3051:                         public void afterEditorActivated(ColumnViewerEditorActivationEvent event) {
3052:                                 // set colors for cell editor
3053:                                 final Control control = cellEditor.getControl();
3054:                                 if (control == null || control.isDisposed()) {
3055:                                         return;
3056:                                 }
3057:                                 control.setBackground(getViewer().getControl().getBackground());
3058:                                 control.setForeground(getViewer().getControl().getForeground());
3059:                         }
3060:
3061:                         @Override
3062:                         public void afterEditorDeactivated(ColumnViewerEditorDeactivationEvent event) {
3063:                                 editingState.dispose();
3064:                                 editingState = null;
3065:
3066:                                 getViewer().getColumnViewerEditor().removeEditorActivationListener(this);
3067:                                 final ViewerCell focusCell = getViewer().getColumnViewerEditor().getFocusCell();
3068:                                 if (focusCell != null) {
3069:                                         getViewer().update(focusCell.getElement(), null);
3070:                                 }
3071:                         }
3072:
3073:                         @Override
3074:                         public void beforeEditorActivated(ColumnViewerEditorActivationEvent event) {
3075:                                 // do nothing
3076:                         }
3077:
3078:                         @Override
3079:                         public void beforeEditorDeactivated(ColumnViewerEditorDeactivationEvent event) {
3080:                                 // do nothing
3081:                         }
3082:                 }
3083:
3084:                 /**
3085:                  * Maintains references to objects that only live for the length of the edit
3086:                  * cycle.
3087:                  */
3088:                 class EditingState {
3089:
3090:                         private final IObservableValue<?> target;
3091:                         private final IObservableValue<?> model;
3092:                         private final Binding binding;
3093:
3094:                         /**
3095:                          * Initializes me with the data binding details involved in editing.
3096:                          *
3097:                          * @param binding the data binding
3098:                          * @param target the bound target value
3099:                          * @param model the bound model value
3100:                          */
3101:                         EditingState(Binding binding, IObservableValue<?> target, IObservableValue<?> model) {
3102:                                 this.binding = binding;
3103:                                 this.target = target;
3104:                                 this.model = model;
3105:                         }
3106:
3107:                         /**
3108:                          * Dispose the data binding that I maintain.
3109:                          */
3110:                         void dispose() {
3111:                                 binding.dispose();
3112:                                 target.dispose();
3113:                                 model.dispose();
3114:                         }
3115:
3116:                         /**
3117:                          * Checks if an update is really needed.
3118:                          *
3119:                          * @return <code>true</code> if update is really needed, <code>false</code> otherwise.
3120:                          */
3121:                         boolean isUpdateNeeded() {
3122:                                 final Object targetValue = target.getValue();
3123:                                 final Object modelValue = model.getValue();
3124:
3125:                                 if (targetValue == null) {
3126:                                         return modelValue != null;
3127:                                 }
3128:                                 return !targetValue.equals(modelValue);
3129:                         }
3130:                 }
3131:         }
3132:
3133:         /**
3134:          * The {@link CellLabelProvider} to update the validation status on the cells.
3135:          *
3136:          * @author Eugen Neufeld
3137:          *
3138:          */
3139:         private class ValidationStatusCellLabelProvider extends CellLabelProvider {
3140:                 private final VTableControl vTableControl;
3141:
3142:                 ValidationStatusCellLabelProvider(
3143:                         VTableControl vTableControl) {
3144:                         this.vTableControl = vTableControl;
3145:                 }
3146:
3147:                 @Override
3148:                 public void update(ViewerCell cell) {
3149:                         final VDiagnostic vDiagnostic = vTableControl.getDiagnostic();
3150:                         if (vDiagnostic == null) {
3151:                                 return;
3152:                         }
3153:                         final List<Diagnostic> diagnostics = vDiagnostic.getDiagnostics((EObject) cell.getElement());
3154:                         Diagnostic cellDiagnostic;
3155:                         if (diagnostics.size() != 0) {
3156:                                 cellDiagnostic = diagnostics.get(0);
3157:                         } else {
3158:                                 // If there is no diagnostic, we assume everything is ok
3159:                                 cellDiagnostic = Diagnostic.OK_INSTANCE;
3160:                         }
3161:                         final Image validationIcon = getViewModelContext().getService(SWTValidationUiService.class)
3162:                                 .getValidationIcon(cellDiagnostic, getVElement(), getViewModelContext());
3163:                         cell.setImage(validationIcon);
3164:                 }
3165:
3166:                 @Override
3167:                 public String getToolTipText(Object element) {
3168:                         final VDiagnostic vDiagnostic = vTableControl.getDiagnostic();
3169:                         if (vDiagnostic == null) {
3170:                                 return null;
3171:                         }
3172:                         final String message = DiagnosticMessageExtractor.getMessage(vDiagnostic.getDiagnostics((EObject) element));
3173:                         return ECPTooltipModifierHelper.modifyString(message, null);
3174:                 }
3175:         }
3176:
3177:         /**
3178:          * Localize the message.
3179:          *
3180:          * @param eClass The EClass of the table setting.
3181:          * @param messageKey key of the string
3182:          * @return tooltip.
3183:          * @since 1.26
3184:          */
3185:         protected String formatTooltipText(EClass eClass, String messageKey) {
3186:                 final String instanceName = getReferenceDisplayName(eClass);
3187:                 return String.format(LocalizationServiceHelper.getString(
3188:                         TableControlSWTRenderer.class, messageKey),
3189:                         instanceName);
3190:         }
3191:
3192:         /**
3193:          * Returns the current sort column feature.
3194:          *
3195:          * @return an {@link java.util.Optional} containing the feature used for sorting
3196:          * @since 1.22
3197:          */
3198:         protected java.util.Optional<EStructuralFeature> getSortColumnFeature() {
3199:                 return sortColumnFeature;
3200:         }
3201:
3202:         /**
3203:          * Set the column to use for sorting.
3204:          *
3205:          * @param sortColumnFeature an optional containing the feature to use for sorting
3206:          * @since 1.22
3207:          */
3208:         protected void setSortColumnFeature(EStructuralFeature sortColumnFeature) {
3209:                 this.sortColumnFeature = java.util.Optional.ofNullable(sortColumnFeature);
3210:         }
3211:
3212:         /**
3213:          * Find the feature for a specific index.
3214:          *
3215:          * @param propertyIndex The index to find the feature for
3216:          * @return The {@link EStructuralFeature} for the provided index
3217:          * @since 1.22
3218:          */
3219:         protected EStructuralFeature getColumnFeature(int propertyIndex) {
3220:                 return columnFeatures[propertyIndex - regularColumnsStartIndex];
3221:         }
3222:
3223:         /**
3224:          * Obtains a user-presentable name for the reference that I edit, to be used for example
3225:          * in button tool-tips.
3226:          *
3227:          * @return the reference display name
3228:          * @since 1.25
3229:          */
3230:         protected String getReferenceDisplayName(EClassifier type) {
3231:                 if (referenceDisplayName == null) {
3232:                         if (l10n == null) {
3233:                                 // Maybe the view-model context has one
3234:                                 l10n = getViewModelContext().getService(EMFFormsLocalizationService.class);
3235:                         }
3236:
3237:                         if (type != null && l10n != null) {
3238:                                 try {
3239:                                         final Bundle editBundle = bundleResolver.getEditBundle(type);
3240:                                         referenceDisplayName = l10n.getString(editBundle, String.format("_UI_%s_type", type.getName())); //$NON-NLS-1$
3241:                                 } catch (final NoBundleFoundException ex) {
3242:                                         referenceDisplayName = type.getName();
3243:                                 }
3244:                         }
3245:
3246:                         if (referenceDisplayName == null) {
3247:                                 referenceDisplayName = LocalizationServiceHelper.getString(TableControlSWTRenderer.class,
3248:                                         MessageKeys.TableControlSWTRenderer_defaultReferenceDisplayName);
3249:                         }
3250:                 }
3251:
3252:                 return referenceDisplayName;
3253:         }
3254: }