Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra')
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/ServiceConfigAttributes.java132
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationAction.java62
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationConfiguration.java160
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/Activator.java60
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/widgets/LanguagesTable.java91
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelPainter.java119
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderImagePainter.java52
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderTextPainter.java77
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/ColumnHeaderHelper.java157
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/EventListObservableAdapter.java198
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/PapyrusGlazedListEventsLayer.java159
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/FormTable.java230
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/HyperlinkTable.java53
13 files changed, 1550 insertions, 0 deletions
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/ServiceConfigAttributes.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/ServiceConfigAttributes.java
new file mode 100644
index 00000000000..f9255e4d342
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/ServiceConfigAttributes.java
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+import org.eclipse.nebula.widgets.nattable.style.ConfigAttribute;
+
+/**
+ * A factory for unique NatTable configuration attributes for Papyrus services.
+ */
+public class ServiceConfigAttributes {
+ private static Map<Class<?>, ConfigAttribute<?>> configAttributes = new HashMap<>();
+
+ private ServiceConfigAttributes() {
+ super();
+ }
+
+ /**
+ * Obtains the (unique) configuration attribute for the given service type.
+ *
+ * @param serviceType
+ * a service type
+ *
+ * @return the corresponding configuration attribute
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> ConfigAttribute<T> get(Class<T> serviceType) {
+ return (ConfigAttribute<T>) configAttributes.computeIfAbsent(serviceType, key -> new ConfigAttribute<T>());
+ }
+
+ /**
+ * Obtains a builder-like configurator object that uses the given service accessor to obtain
+ * services to register.
+ *
+ * @param registry
+ * the NatTable configuration registry
+ * @param serviceAccessor
+ * a service accessor function
+ *
+ * @return the configurator
+ */
+ public static Configurator with(IConfigRegistry registry, Function<? super Class<?>, ?> serviceAccessor) {
+ return new Configurator(registry, serviceAccessor);
+ }
+
+ public static <T> void registerService(Class<T> serviceType, IConfigRegistry configRegistry, T service) {
+ configRegistry.registerConfigAttribute(get(serviceType), service);
+ }
+
+ public static <T> void registerService(Class<T> serviceType, IConfigRegistry configRegistry, T service, String targetDisplayMode) {
+ configRegistry.registerConfigAttribute(get(serviceType), service, targetDisplayMode);
+ }
+
+ public static <T> void registerService(Class<T> serviceType, IConfigRegistry configRegistry, T service, String targetDisplayMode, String label) {
+ configRegistry.registerConfigAttribute(get(serviceType), service, targetDisplayMode, label);
+ }
+
+ public static <T> T getService(Class<T> serviceType, IConfigRegistry config, String targetDisplayMode, String... labels) {
+ return config.getConfigAttribute(get(serviceType), targetDisplayMode, labels);
+ }
+
+ public static <T> T getService(Class<T> serviceType, IConfigRegistry config, String targetDisplayMode, List<String> labels) {
+ return config.getConfigAttribute(get(serviceType), targetDisplayMode, labels);
+ }
+
+ public static <T> T getService(Class<T> serviceType, IConfigRegistry config, String targetDisplayMode, LabelStack labels) {
+ return getService(serviceType, config, targetDisplayMode, labels.getLabels());
+ }
+
+ public static <T> T getService(Class<T> serviceType, IConfigRegistry config, ILayerCell cell) {
+ return getService(serviceType, config, cell.getDisplayMode(), cell.getConfigLabels().getLabels());
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A builder-like configurator object that automatically registers services in a particular
+ * NatTable configuration registry.
+ */
+ public static class Configurator {
+ private final IConfigRegistry configRegistry;
+ private final Function<? super Class<?>, ?> serviceAccessor;
+
+ Configurator(IConfigRegistry configRegistry, Function<? super Class<?>, ?> serviceAccessor) {
+ super();
+
+ this.configRegistry = configRegistry;
+ this.serviceAccessor = serviceAccessor;
+ }
+
+ <T> T getService(Class<T> serviceType) {
+ @SuppressWarnings("unchecked")
+ Function<? super Class<T>, T> accessor = (Function<? super Class<T>, T>) serviceAccessor;
+ return accessor.apply(serviceType);
+ }
+
+ public <T> Configurator register(Class<T> serviceType) {
+ registerService(serviceType, configRegistry, getService(serviceType));
+ return this;
+ }
+
+ public <T> Configurator register(Class<T> serviceType, String targetDisplayMode) {
+ registerService(serviceType, configRegistry, getService(serviceType), targetDisplayMode);
+ return this;
+ }
+
+ public <T> Configurator register(Class<T> serviceType, String targetDisplayMode, String label) {
+ registerService(serviceType, configRegistry, getService(serviceType), targetDisplayMode, label);
+ return this;
+ }
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationAction.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationAction.java
new file mode 100644
index 00000000000..b5236901c8d
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationAction.java
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.hyperlink;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.selection.action.AbstractMouseSelectionAction;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
+import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageManager;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.ServiceConfigAttributes;
+import org.eclipse.papyrus.infra.services.navigation.service.NavigationService;
+import org.eclipse.swt.events.MouseEvent;
+
+/**
+ * A NatTable action for navigation of a hyperlink cell.
+ */
+public class HyperlinkNavigationAction extends AbstractMouseSelectionAction {
+
+ public HyperlinkNavigationAction() {
+ super();
+ }
+
+ <T> T getService(NatTable natTable, Class<T> serviceType) {
+ return ServiceConfigAttributes.getService(serviceType, natTable.getConfigRegistry(), DisplayMode.NORMAL);
+ }
+
+ @Override
+ public void run(NatTable natTable, MouseEvent event) {
+ super.run(natTable, event);
+
+ Object data = natTable.getDataValueByPosition(getGridColumnPosition(), getGridRowPosition());
+ if (data instanceof IObservableValue<?>) {
+ data = ((IObservableValue<?>) data).getValue();
+ }
+
+ IPageManager pages = getService(natTable, IPageManager.class);
+ if (pages.allPages().contains(data)) {
+ if (pages.isOpen(data)) {
+ pages.selectPage(data);
+ } else {
+ pages.openPage(data);
+ }
+ } else if (data != null) {
+ getService(natTable, NavigationService.class).navigate(data);
+ getService(natTable, ISelectionProvider.class).setSelection(new StructuredSelection(data));
+ }
+ }
+
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationConfiguration.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationConfiguration.java
new file mode 100644
index 00000000000..3c795edc3aa
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/hyperlink/HyperlinkNavigationConfiguration.java
@@ -0,0 +1,160 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.hyperlink;
+
+import java.util.function.Predicate;
+
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
+import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.IMouseEventMatcher;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.painter.LabelPainter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * A NatTable configuration that installs hyperlink navigation cursor feed-back and
+ * actions on the cells of a table that have hyperlinks.
+ */
+public class HyperlinkNavigationConfiguration<E> extends AbstractUiBindingConfiguration {
+ private final Class<? extends E> elementType;
+ private final Predicate<? super E> isActiveHyperlink;
+
+ private Cursor defaultCursor;
+ private Cursor handCursor;
+
+ private int currentColumn = -1;
+ private int currentRow = -1;
+ private Rectangle currentCellTextBounds;
+
+ /**
+ * Initializes me.
+ *
+ * @param owner
+ * the control that owns the {@link NatTable} that I will configure
+ * @param elementType
+ * the type of object that is a potential hyperlink
+ * @param isActiveHyperlink
+ * a predicate matching valid, navigable hyperlink elements
+ */
+ public HyperlinkNavigationConfiguration(Control owner, Class<? extends E> elementType, Predicate<? super E> isActiveHyperlink) {
+ super();
+
+ this.elementType = elementType;
+ this.isActiveHyperlink = isActiveHyperlink;
+
+ this.defaultCursor = owner.getCursor();
+ this.handCursor = new Cursor(owner.getDisplay(), SWT.CURSOR_HAND);
+
+ owner.addDisposeListener(event -> handCursor.dispose());
+ }
+
+ @Override
+ public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
+ uiBindingRegistry.registerSingleClickBinding(
+ MouseEventMatcher.bodyLeftClick(SWT.NONE),
+ new HyperlinkNavigationAction());
+
+ // On moving the mouse into the body area, show a navigation cursor
+ uiBindingRegistry.registerFirstMouseMoveBinding(
+ new IMouseEventMatcher() {
+ @Override
+ public boolean matches(NatTable natTable, MouseEvent event, LabelStack regionLabels) {
+ int col = natTable.getColumnPositionByX(event.x);
+ int row = natTable.getRowPositionByY(event.y);
+
+ return (col >= 0) && (row >= 0);
+ }
+
+ }, this::updateCursor);
+
+ // On moving the mouse out of the body area, restore the usual cursor
+ uiBindingRegistry.registerMouseMoveBinding(new IMouseEventMatcher() {
+ @Override
+ public boolean matches(NatTable natTable, MouseEvent event, LabelStack regionLabels) {
+ return (natTable != null && regionLabels == null);
+ }
+
+ }, this::reset);
+
+ // And same if it is moved out of the table altogether
+ uiBindingRegistry.registerMouseExitBinding(new IMouseEventMatcher() {
+ @Override
+ public boolean matches(NatTable natTable, MouseEvent event, LabelStack regionLabels) {
+ return true;
+ }
+
+ }, this::reset);
+ }
+
+ void updateCursor(NatTable table, MouseEvent event) {
+ int col = table.getColumnPositionByX(event.x);
+ int row = table.getRowPositionByY(event.y);
+ Object data = (col >= 0) && (row >= 0) ? table.getDataValueByPosition(col, row) : null;
+
+ if (!elementType.isInstance(data) || !isActiveHyperlink.test(elementType.cast(data))) {
+ // not a havigable hyperlink
+ table.setCursor(defaultCursor);
+ } else {
+ // are we over text?
+ if ((currentCellTextBounds == null) || (col != currentColumn) || (row != currentRow)) {
+ updateCellTextBounds(table, col, row);
+ }
+
+ if (currentCellTextBounds != null && currentCellTextBounds.contains(event.x, event.y)) {
+ table.setCursor(handCursor);
+ } else {
+ table.setCursor(defaultCursor);
+ }
+ }
+ }
+
+ void reset(NatTable table, MouseEvent event) {
+ currentColumn = -1;
+ currentRow = -1;
+ currentCellTextBounds = null;
+
+ table.setCursor(defaultCursor);
+ }
+
+ void updateCellTextBounds(NatTable table, int column, int row) {
+ currentColumn = column;
+ currentRow = row;
+ currentCellTextBounds = null;
+
+ ILayerCell cell = table.getCellByPosition(column, row);
+ if (cell != null) {
+ ICellPainter painter = table.getCellPainter(column, row, cell, table.getConfigRegistry());
+ if (painter instanceof LabelPainter) {
+ GC gc = new GC(table.getDisplay());
+ try {
+ currentCellTextBounds = ((LabelPainter) painter).getTextBounds(cell, gc, table.getConfigRegistry());
+ } finally {
+ gc.dispose();
+ }
+ } else {
+ // Assume the whole cell is text, then
+ currentCellTextBounds = cell.getBounds();
+ }
+ }
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/Activator.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/Activator.java
new file mode 100644
index 00000000000..be2ea27bebf
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/Activator.java
@@ -0,0 +1,60 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.editor.welcome.nattable.internal;
+
+import org.eclipse.papyrus.infra.core.log.LogHelper;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.papyrus.infra.editor.welcome.nattable"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ public static LogHelper log;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ log = new LogHelper(this);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/widgets/LanguagesTable.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/widgets/LanguagesTable.java
new file mode 100644
index 00000000000..9b677ba672b
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/internal/widgets/LanguagesTable.java
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.internal.widgets;
+
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.papyrus.infra.editor.welcome.internal.modelelements.LanguageObservable;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.widgets.FormTable;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A table widget presenting languages in two columns for name and version.
+ */
+public class LanguagesTable extends FormTable<LanguageObservable> {
+
+ public LanguagesTable(Composite parent, int style) {
+ // No hyperlinks
+ super(parent, style, "Languages:", new LanguagesColumnAccessor(), "Name", "Version");
+ }
+
+ //
+ // Nested types
+ //
+
+ static class LanguagesColumnAccessor implements IColumnPropertyAccessor<LanguageObservable> {
+ static final String NAME = "name"; //$NON-NLS-1$
+ static final String VERSION = "version"; //$NON-NLS-1$
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public Object getDataValue(LanguageObservable rowObject, int columnIndex) {
+ Object result;
+
+ switch (columnIndex) {
+ case 0:
+ result = rowObject.getName();
+ break;
+ case 1:
+ result = rowObject.getVersion();
+ break;
+ default:
+ throw new IndexOutOfBoundsException(Integer.toString(columnIndex));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setDataValue(LanguageObservable rowObject, int columnIndex, Object newValue) {
+ throw new IllegalStateException("not editable"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String getColumnProperty(int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return NAME;
+ case 1:
+ return VERSION;
+ default:
+ throw new IndexOutOfBoundsException(Integer.toString(columnIndex));
+ }
+ }
+
+ @Override
+ public int getColumnIndex(String propertyName) {
+ switch (propertyName) {
+ case NAME:
+ return 0;
+ case VERSION:
+ return 1;
+ default:
+ throw new IllegalArgumentException(propertyName);
+ }
+ }
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelPainter.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelPainter.java
new file mode 100644
index 00000000000..cdc86d17ed0
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelPainter.java
@@ -0,0 +1,119 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.painter;
+
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
+import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.CellPainterDecorator;
+import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * @author damus
+ *
+ */
+public class LabelPainter extends CellPainterDecorator {
+
+ public LabelPainter(CellEdgeEnum cellEdge) {
+ this(new LabelProviderTextPainter(), cellEdge, new LabelProviderImagePainter());
+ }
+
+ public LabelPainter(ICellPainter textPainter, CellEdgeEnum cellEdge) {
+ this(textPainter, cellEdge, new LabelProviderImagePainter());
+ }
+
+ public LabelPainter(CellEdgeEnum cellEdge, ICellPainter imagePainter) {
+ this(new LabelProviderTextPainter(), cellEdge, imagePainter);
+ }
+
+ public LabelPainter(ICellPainter textPainter, CellEdgeEnum cellEdge, ICellPainter imagePainter) {
+ super(textPainter, cellEdge, imagePainter);
+ }
+
+ public LabelPainter(ICellPainter textPainter, CellEdgeEnum cellEdge, ICellPainter imagePainter, boolean paintDecorationDependent) {
+ super(textPainter, cellEdge, imagePainter, paintDecorationDependent);
+ }
+
+ public LabelPainter(ICellPainter textPainter, CellEdgeEnum cellEdge, int spacing, ICellPainter imagePainter) {
+ super(textPainter, cellEdge, spacing, imagePainter);
+ }
+
+ public LabelPainter(ICellPainter textPainter, CellEdgeEnum cellEdge, int spacing, ICellPainter imagePainter, boolean paintDecorationDependent) {
+ super(textPainter, cellEdge, spacing, imagePainter, paintDecorationDependent);
+ }
+
+ public LabelPainter(ICellPainter textPainter, CellEdgeEnum cellEdge, int spacing, ICellPainter imagePainter, boolean paintDecorationDependent, boolean paintBg) {
+ super(textPainter, cellEdge, spacing, imagePainter, paintDecorationDependent, paintBg);
+ }
+
+ public Rectangle getTextBounds(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {
+ Rectangle cellBounds = cell.getBounds();
+
+ int imageWidth = getDecoratorCellPainter().getPreferredWidth(cell, gc, configRegistry);
+ int imageHeight = getDecoratorCellPainter().getPreferredHeight(cell, gc, configRegistry);
+
+ // grab any extra space:
+ int preferredWidth = getBaseCellPainter().getPreferredWidth(cell, gc, configRegistry);
+ int preferredHeight = getBaseCellPainter().getPreferredHeight(cell, gc, configRegistry);
+
+ Rectangle result = null;
+
+ switch (getCellEdge()) {
+ case LEFT:
+ result = new Rectangle(cellBounds.x + imageWidth + getSpacing(),
+ cellBounds.y, preferredWidth, cellBounds.height);
+ break;
+ case RIGHT:
+ result = new Rectangle(cellBounds.x, cellBounds.y, preferredWidth, cellBounds.height);
+ break;
+ case TOP:
+ result = new Rectangle(cellBounds.x,
+ cellBounds.y + imageHeight + getSpacing(),
+ cellBounds.width, preferredHeight);
+ break;
+ case BOTTOM:
+ result = new Rectangle(cellBounds.x, cellBounds.y, cellBounds.width, preferredHeight);
+ break;
+ case TOP_LEFT:
+ result = new Rectangle(cellBounds.x + imageWidth + getSpacing(),
+ cellBounds.y + imageHeight + getSpacing(),
+ preferredWidth, preferredHeight);
+ break;
+ case TOP_RIGHT:
+ result = new Rectangle(cellBounds.x,
+ cellBounds.y + imageHeight + getSpacing(),
+ preferredWidth, preferredHeight);
+ break;
+ case BOTTOM_LEFT:
+ result = new Rectangle(cellBounds.x + imageWidth + getSpacing(),
+ cellBounds.y, preferredWidth, preferredHeight);
+ break;
+ case BOTTOM_RIGHT:
+ result = new Rectangle(cellBounds.x, cellBounds.y, preferredWidth, preferredHeight);
+ break;
+ case NONE:
+ break;
+ }
+
+ if (result != null) {
+ // Crop to the actual cell bounds
+ result = result.intersection(cellBounds);
+ }
+
+ return result;
+ }
+
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderImagePainter.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderImagePainter.java
new file mode 100644
index 00000000000..048820b9b29
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderImagePainter.java
@@ -0,0 +1,52 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.painter;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+import org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.ServiceConfigAttributes;
+import org.eclipse.papyrus.infra.services.labelprovider.service.LabelProviderService;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * An image painter that obtains images from the {@link LabelProviderService} registered
+ * in the NatTable configuration registry.
+ */
+public class LabelProviderImagePainter extends ImagePainter {
+
+ public LabelProviderImagePainter() {
+ super();
+ }
+
+ public LabelProviderImagePainter(boolean paintBg) {
+ super(null, paintBg);
+ }
+
+ @Override
+ protected Image getImage(ILayerCell cell, IConfigRegistry configRegistry) {
+ Object dataValue = cell.getDataValue();
+
+ if (dataValue instanceof IObservableValue<?>) {
+ dataValue = ((IObservableValue<?>) dataValue).getValue();
+ }
+
+ LabelProviderService labelService = ServiceConfigAttributes.getService(LabelProviderService.class, configRegistry, cell);
+ ILabelProvider labelProvider = (dataValue == null) ? labelService.getLabelProvider() : labelService.getLabelProvider(dataValue);
+
+ return labelProvider.getImage(dataValue);
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderTextPainter.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderTextPainter.java
new file mode 100644
index 00000000000..671337988b4
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/painter/LabelProviderTextPainter.java
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.painter;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.ServiceConfigAttributes;
+import org.eclipse.papyrus.infra.services.labelprovider.service.LabelProviderService;
+
+/**
+ * A text painter that obtains text labels from the {@link LabelProviderService} registered
+ * in the NatTable configuration registry.
+ */
+public class LabelProviderTextPainter extends TextPainter {
+
+ public LabelProviderTextPainter() {
+ super();
+ }
+
+ public LabelProviderTextPainter(boolean wrapText, boolean paintBg, boolean calculateByTextLength, boolean calculateByTextHeight) {
+ super(wrapText, paintBg, calculateByTextLength, calculateByTextHeight);
+ }
+
+ public LabelProviderTextPainter(boolean wrapText, boolean paintBg, boolean calculate) {
+ super(wrapText, paintBg, calculate);
+ }
+
+ public LabelProviderTextPainter(boolean wrapText, boolean paintBg, int spacing, boolean calculateByTextLength, boolean calculateByTextHeight) {
+ super(wrapText, paintBg, spacing, calculateByTextLength, calculateByTextHeight);
+ }
+
+ public LabelProviderTextPainter(boolean wrapText, boolean paintBg, int spacing, boolean calculate) {
+ super(wrapText, paintBg, spacing, calculate);
+ }
+
+ public LabelProviderTextPainter(boolean wrapText, boolean paintBg, int spacing) {
+ super(wrapText, paintBg, spacing);
+ }
+
+ public LabelProviderTextPainter(boolean wrapText, boolean paintBg) {
+ super(wrapText, paintBg);
+ }
+
+ @Override
+ protected String convertDataType(ILayerCell cell, IConfigRegistry configRegistry) {
+ String result;
+ Object dataValue = cell.getDataValue();
+ if (dataValue instanceof String) {
+ result = (String) dataValue;
+ } else {
+ if (dataValue instanceof IObservableValue<?>) {
+ dataValue = ((IObservableValue<?>) dataValue).getValue();
+ }
+
+ LabelProviderService labelService = ServiceConfigAttributes.getService(LabelProviderService.class, configRegistry, cell);
+ ILabelProvider labelProvider = (dataValue == null) ? labelService.getLabelProvider() : labelService.getLabelProvider(dataValue);
+
+ result = labelProvider.getText(dataValue);
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/ColumnHeaderHelper.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/ColumnHeaderHelper.java
new file mode 100644
index 00000000000..6f6072be724
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/ColumnHeaderHelper.java
@@ -0,0 +1,157 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.sorting;
+
+import java.text.Collator;
+import java.util.Comparator;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.sort.ISortModel;
+import org.eclipse.nebula.widgets.nattable.sort.SortConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
+import org.eclipse.nebula.widgets.nattable.sort.painter.SortableHeaderTextPainter;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
+import org.eclipse.nebula.widgets.nattable.style.IStyle;
+import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.ServiceConfigAttributes;
+import org.eclipse.papyrus.infra.services.labelprovider.service.LabelProviderService;
+
+import ca.odell.glazedlists.SortedList;
+
+/**
+ * A helper utility for the configuration of sorting a NatTable that renders a
+ * SWT Forms-compatible flat presentation.
+ */
+public class ColumnHeaderHelper<T> {
+ private final IConfigRegistry configRegistry;
+
+ private final ILayer viewportLayer;
+ private final SelectionLayer selectionLayer;
+
+ private IColumnPropertyAccessor<T> columnAccessor;
+ private DataLayer headerData;
+ private SortHeaderLayer<T> sortHeaders;
+
+ /**
+ * Initializes me.
+ *
+ * @param configRegistry
+ * the NatTable configuration registry
+ * @param viewportLayer
+ * the viewport layer to track for positioning of header cells
+ * @param selectionLayer
+ * the selection layer in the table body
+ */
+ public ColumnHeaderHelper(IConfigRegistry configRegistry, ILayer viewportLayer, SelectionLayer selectionLayer) {
+ super();
+
+ this.configRegistry = configRegistry;
+ this.viewportLayer = viewportLayer;
+ this.selectionLayer = selectionLayer;
+ }
+
+ /**
+ * Creates the header layer. <b>Note</b> that this must be done exactly once, as the returned
+ * layer and other parts of it are retained for follow-up work.
+ *
+ * @param data
+ * the sorted Glazed-list backing store of the table
+ * @param columnAccessor
+ * the column accessor describing the columns to be presented
+ * @param columnHeadings
+ * the localized heading titles corresponding to each column
+ *
+ * @return the column header layer
+ */
+ public ILayer createHeaderLayer(SortedList<T> data, IColumnPropertyAccessor<T> columnAccessor, String... columnHeadings) {
+ this.columnAccessor = columnAccessor;
+ IDataProvider headerProvider = new DefaultColumnHeaderDataProvider(columnHeadings);
+ headerData = new DataLayer(headerProvider);
+ ColumnHeaderLayer headers = new ColumnHeaderLayer(headerData, viewportLayer, selectionLayer);
+ ISortModel sortModel = new GlazedListsSortModel<>(data, columnAccessor, configRegistry, headerData);
+ sortHeaders = new SortHeaderLayer<>(headers, sortModel, false);
+ return sortHeaders;
+ }
+
+ /**
+ * Configures the header layer {@linkplain #createHeaderLayer previously created} for sorting
+ * control behavior.
+ *
+ * @see #createHeaderLayer(SortedList, IColumnPropertyAccessor, String...)
+ */
+ public void configureSorting() {
+ ColumnOverrideLabelAccumulator labelAccumulator = new ColumnOverrideLabelAccumulator(headerData);
+ headerData.setConfigLabelAccumulator(labelAccumulator);
+
+ for (int i = 0; i < columnAccessor.getColumnCount(); i++) {
+ String label = sortLabel(i);
+ labelAccumulator.registerColumnOverrides(i, label);
+ configRegistry.registerConfigAttribute(SortConfigAttributes.SORT_COMPARATOR, getByLabelOrdering(), DisplayMode.NORMAL, label);
+ }
+ }
+
+ /**
+ * Configures the visual styling of the header layer {@linkplain #createHeaderLayer previously created}
+ * for presentation in a SWT Forms UI with a flat appearance.
+ *
+ * @see #createHeaderLayer(SortedList, IColumnPropertyAccessor, String...)
+ */
+ public void configureHeaders(ICellPainter cellPainter, IStyle style) {
+ sortHeaders.addConfiguration(new SingleClickSortConfiguration(new SortableHeaderTextPainter(cellPainter, CellEdgeEnum.LEFT, true, 3, true)));
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, cellPainter, DisplayMode.NORMAL, GridRegion.COLUMN_HEADER);
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, style, DisplayMode.NORMAL, GridRegion.COLUMN_HEADER);
+ for (int i = 0; i < columnAccessor.getColumnCount(); i++) {
+ String label = sortLabel(i);
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, cellPainter, DisplayMode.NORMAL, label);
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, style, DisplayMode.NORMAL, label);
+ }
+ }
+
+ String sortLabel(int columnIndex) {
+ return sortLabel(columnAccessor.getColumnProperty(columnIndex));
+ }
+
+ static String sortLabel(String propertyName) {
+ return "sortBy:" + propertyName;
+ }
+
+ private Comparator<IObservableValue<?>> getByLabelOrdering() {
+ Collator collator = Collator.getInstance();
+ collator.setStrength(Collator.PRIMARY);
+ return (a, b) -> collator.compare(getLabel(a), getLabel(b));
+ }
+
+ private String getLabel(IObservableValue<?> observable) {
+ LabelProviderService labels = ServiceConfigAttributes.getService(LabelProviderService.class, configRegistry, DisplayMode.NORMAL);
+ Object value = (observable == null) ? null : observable.getValue();
+ ILabelProvider labelProvider = (value == null) ? null : labels.getLabelProvider(observable);
+ return (labelProvider == null) ? null : labelProvider.getText(value);
+ }
+
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/EventListObservableAdapter.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/EventListObservableAdapter.java
new file mode 100644
index 00000000000..5145cb4aeba
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/EventListObservableAdapter.java
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.sorting;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.list.IListChangeListener;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListChangeEvent;
+
+import ca.odell.glazedlists.AbstractEventList;
+import ca.odell.glazedlists.event.ListEventPublisher;
+import ca.odell.glazedlists.util.concurrent.LockFactory;
+
+/**
+ * @author damus
+ *
+ */
+public class EventListObservableAdapter<E> extends AbstractEventList<E> {
+ private final IChangeListener elementListener;
+ private final IListChangeListener<E> listener;
+ private IObservableList<E> delegate;
+
+ public EventListObservableAdapter() {
+ this(null);
+ }
+
+ public EventListObservableAdapter(ListEventPublisher publisher) {
+ super(publisher);
+
+ this.readWriteLock = LockFactory.DEFAULT.createReadWriteLock();
+
+ this.listener = new IListChangeListener<E>() {
+ @Override
+ public void handleListChange(ListChangeEvent<? extends E> event) {
+ readWriteLock.readLock().lock();
+ try {
+ updates.beginEvent();
+ try {
+ Stream.of(event.diff.getDifferences()).forEach(entry -> {
+ if (entry.isAddition()) {
+ updates.elementInserted(entry.getPosition(), entry.getElement());
+ connect(entry.getElement());
+ } else {
+ disconnect(entry.getElement());
+ updates.elementDeleted(entry.getPosition(), entry.getElement());
+ }
+ });
+ } finally {
+ updates.commitEvent();
+ }
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+ };
+
+ this.elementListener = new IChangeListener() {
+
+ @Override
+ public void handleChange(ChangeEvent event) {
+ readWriteLock.readLock().lock();
+ try {
+ updates.beginEvent();
+ try {
+ @SuppressWarnings("unchecked")
+ E element = (E) event.getObservable();
+ updates.elementUpdated(indexOf(element), element, element);
+ } finally {
+ updates.commitEvent();
+ }
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+ };
+ }
+
+ public synchronized void setDelegate(IObservableList<E> newDelegate) {
+ IObservableList<E> oldDelegate = this.delegate;
+ this.delegate = newDelegate;
+
+ if (oldDelegate != null) {
+ oldDelegate.removeListChangeListener(listener);
+ }
+ if (newDelegate != null) {
+ newDelegate.addListChangeListener(listener);
+ }
+
+ readWriteLock.readLock().lock();
+ try {
+ updates.beginEvent();
+ try {
+ AtomicInteger i = new AtomicInteger();
+ if (oldDelegate != null) {
+ // Report updates for elements that have correspondents in the new list
+ oldDelegate.subList(0, (newDelegate == null) ? 0 : newDelegate.size()).forEach(e -> {
+ E newE = newDelegate.get(i.get());
+ disconnect(e);
+ updates.elementUpdated(i.getAndIncrement(), e, newE);
+ connect(newE);
+ });
+
+ // Report deletions for any further elements
+ if (oldDelegate.size() < i.get()) {
+ oldDelegate.forEach(e -> {
+ disconnect(e);
+ updates.elementDeleted(i.getAndIncrement(), e);
+ });
+ }
+ }
+
+ if (newDelegate != null) {
+ if ((newDelegate.size() > i.get())) {
+ // Report insertions for any further new elements
+ newDelegate.listIterator(i.get()).forEachRemaining(e -> {
+ updates.elementInserted(i.getAndIncrement(), e);
+ connect(e);
+ });
+ }
+ }
+ } finally {
+ updates.commitEvent();
+ }
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (delegate != null) {
+ delegate.removeListChangeListener(listener);
+ }
+ }
+
+ @Override
+ public int size() {
+ return (delegate == null) ? 0 : delegate.size();
+ }
+
+ @Override
+ public E get(int index) {
+ checkDelegate();
+ return delegate.get(index);
+ }
+
+ private void checkDelegate() {
+ if (delegate == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ public void add(int index, E value) {
+ checkDelegate();
+ delegate.add(index, value);
+ }
+
+ @Override
+ public E set(int index, E value) {
+ checkDelegate();
+ return delegate.set(index, value);
+ }
+
+ @Override
+ public E remove(int index) {
+ checkDelegate();
+ return delegate.remove(index);
+ }
+
+ private void connect(E newElement) {
+ if (newElement instanceof IObservable) {
+ ((IObservable) newElement).addChangeListener(elementListener);
+ }
+ }
+
+ private void disconnect(E newElement) {
+ if (newElement instanceof IObservable) {
+ ((IObservable) newElement).removeChangeListener(elementListener);
+ }
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/PapyrusGlazedListEventsLayer.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/PapyrusGlazedListEventsLayer.java
new file mode 100644
index 00000000000..a8bb13c1439
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/sorting/PapyrusGlazedListEventsLayer.java
@@ -0,0 +1,159 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.sorting;
+
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
+import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
+import org.eclipse.nebula.widgets.nattable.layer.event.RowStructuralRefreshEvent;
+import org.eclipse.nebula.widgets.nattable.layer.event.VisualRefreshEvent;
+import org.eclipse.swt.widgets.Display;
+
+import ca.odell.glazedlists.EventList;
+import ca.odell.glazedlists.event.ListEvent;
+import ca.odell.glazedlists.event.ListEventListener;
+
+/**
+ * An alternative to the {@link GlazedListsEventLayer} that doesn't run a thread
+ * continuously checking for updates every 100 milliseconds, but instead posts
+ * 100-millisecond deferred updates that coalesce events in the interim.
+ */
+public class PapyrusGlazedListEventsLayer<E> extends AbstractLayerTransform implements IUniqueIndexLayer, ListEventListener<E> {
+
+ private final IUniqueIndexLayer underlyingLayer;
+ private EventList<? extends E> eventList;
+
+ private volatile boolean active;
+ private volatile Update pendingUpdate;
+
+ /**
+ * Initializes me.
+ *
+ * @param underlyingLayer
+ * the layer to which I post refresh events
+ * @param eventList
+ * the event list from which the {@code underlyingLayer} obtains its data
+ */
+ public PapyrusGlazedListEventsLayer(IUniqueIndexLayer underlyingLayer, EventList<? extends E> eventList) {
+ super(underlyingLayer);
+ this.underlyingLayer = underlyingLayer;
+ this.eventList = eventList;
+
+ this.eventList.addListEventListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ if (eventList != null) {
+ eventList.removeListEventListener(this);
+ eventList = null;
+ }
+ }
+
+ /**
+ * Queries whether I am currently reactiving to changes in the list that I am observing.
+ *
+ * @return whether I am active
+ */
+ public boolean isActive() {
+ return active;
+ }
+
+ /**
+ * Sets I am currently reactiving to changes in the list that I am observing.
+ * It is useful to deactivate me when initiating bulk operations on the list
+ * to avoid useless work when a full table refresh will follow, anyways.
+ *
+ * @param active
+ * whether I am active
+ */
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ //
+ // Event handling
+ //
+
+ @Override
+ public void listChanged(ListEvent<E> event) {
+ // Check whether we even want to process anything
+ if (!isActive()) {
+ return;
+ }
+
+ boolean structure = false;
+
+ out: while (event.next()) {
+ int eventType = event.getType();
+ switch (eventType) {
+ case ListEvent.DELETE:
+ case ListEvent.INSERT:
+ structure = true;
+ Update update = pendingUpdate;
+ if (update != null) {
+ update.structuralUpdate = structure;
+ }
+ break out;
+ }
+ }
+
+ if (pendingUpdate == null) {
+ // May have missed the boat
+ pendingUpdate = new Update();
+ pendingUpdate.structuralUpdate = structure;
+ Display.getDefault().timerExec(100, pendingUpdate);
+ }
+ }
+
+ //
+ // IUniqueIndexLayer protocol
+ //
+
+ @Override
+ public int getColumnPositionByIndex(int columnIndex) {
+ return this.underlyingLayer.getColumnPositionByIndex(columnIndex);
+ }
+
+ @Override
+ public int getRowPositionByIndex(int rowIndex) {
+ return this.underlyingLayer.getRowPositionByIndex(rowIndex);
+ }
+
+ //
+ // Nested types
+ //
+
+ private class Update implements Runnable {
+ volatile boolean structuralUpdate;
+
+ @Override
+ public void run() {
+ if (pendingUpdate == this) {
+ pendingUpdate = null;
+
+ // Check that we haven't been disposed since posting the update
+ if (eventList != null) {
+ if (structuralUpdate) {
+ fireLayerEvent(new RowStructuralRefreshEvent(getUnderlyingLayer()));
+ } else {
+ fireLayerEvent(new VisualRefreshEvent(getUnderlyingLayer()));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/FormTable.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/FormTable.java
new file mode 100644
index 00000000000..615f360c423
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/FormTable.java
@@ -0,0 +1,230 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.widgets;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.layer.CompositeLayer;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
+import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
+import org.eclipse.nebula.widgets.nattable.style.Style;
+import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum;
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageManager;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.ServiceConfigAttributes;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.painter.LabelPainter;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.painter.LabelProviderImagePainter;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.painter.LabelProviderTextPainter;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.sorting.ColumnHeaderHelper;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.sorting.EventListObservableAdapter;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.sorting.PapyrusGlazedListEventsLayer;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForEObject;
+import org.eclipse.papyrus.infra.services.labelprovider.service.LabelProviderService;
+import org.eclipse.papyrus.infra.services.navigation.service.NavigationService;
+import org.eclipse.papyrus.views.properties.modelelement.DataSource;
+import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+import ca.odell.glazedlists.SortedList;
+
+/**
+ * A {@link NatTable} composite with a "flat" presentation suitable for use in an SWT Forms context.
+ */
+public abstract class FormTable<E> extends Composite {
+ private final IConfigRegistry configRegistry;
+ private final NatTable table;
+ private final EventListObservableAdapter<E> backingList;
+ private final PapyrusGlazedListEventsLayer<E> eventLayer;
+
+ private String propertyPath;
+ private DataSource input;
+
+ public FormTable(Composite parent, int style, IColumnPropertyAccessor<E> columnAccessor, String... columnTitle) {
+ this(parent, style, null, columnAccessor, columnTitle);
+ }
+
+ public FormTable(Composite parent, int style, String label, IColumnPropertyAccessor<E> columnAccessor, String... columnTitle) {
+ super(parent, style);
+
+ setBackground(parent.getBackground());
+ setBackgroundMode(SWT.INHERIT_DEFAULT);
+
+ setLayout(new GridLayout());
+
+ configRegistry = new ConfigRegistry();
+
+ // Optional label for the table
+ if ((label != null) && !label.isEmpty()) {
+ Label _label = new Label(parent, SWT.LEFT);
+ _label.setText(label);
+ }
+
+ // Body of the table
+ backingList = new EventListObservableAdapter<>();
+ SortedList<E> sortedDiagrams = new SortedList<>(backingList, null);
+ IDataProvider dataProvider = new GlazedListsDataProvider<>(sortedDiagrams, columnAccessor);
+ DataLayer bodyDataLayer = new DataLayer(dataProvider);
+ eventLayer = new PapyrusGlazedListEventsLayer<>(bodyDataLayer, backingList);
+ SelectionLayer selectionLayer = new SelectionLayer(eventLayer, false);
+ addConfigurations(selectionLayer);
+ ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
+ viewportLayer.setRegionName(GridRegion.BODY);
+
+ // Column headers
+ ColumnHeaderHelper<E> headerHelper = new ColumnHeaderHelper<>(configRegistry, viewportLayer, selectionLayer);
+ ILayer headers = headerHelper.createHeaderLayer(sortedDiagrams, columnAccessor, columnTitle);
+
+ // Arrange the headers and body together
+ CompositeLayer composition = new CompositeLayer(1, 2);
+ composition.setChildLayer(GridRegion.COLUMN_HEADER, headers, 0, 0);
+ composition.setChildLayer(GridRegion.BODY, viewportLayer, 0, 1);
+
+ // Sorting configuration
+ headerHelper.configureSorting();
+
+ //
+ // Presentation configuration
+ //
+ ICellPainter commonPainter = new LabelProviderTextPainter(false, true, 3, true);
+ ICellPainter iconAndTextPainter = new LabelPainter(commonPainter, CellEdgeEnum.LEFT, new LabelProviderImagePainter());
+
+ // Headers
+ Style headerStyle = new Style();
+ headerStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, parent.getBackground());
+ headerStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.LEFT);
+ headerStyle.setAttributeValue(CellStyleAttributes.FONT, JFaceResources.getFont(JFaceResources.BANNER_FONT));
+ headerHelper.configureHeaders(commonPainter, headerStyle);
+
+ // Body
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, iconAndTextPainter, DisplayMode.NORMAL, GridRegion.BODY);
+ Style bodyStyle = new Style();
+ bodyStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, parent.getBackground());
+ bodyStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.LEFT);
+ configureBodyStyle(bodyStyle);
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, bodyStyle, DisplayMode.NORMAL, GridRegion.BODY);
+ configRegistry.registerConfigAttribute(CellConfigAttributes.RENDER_GRID_LINES, false);
+
+ table = new NatTable(parent, composition, false);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(table);
+ table.setConfigRegistry(configRegistry);
+ table.setBackground(parent.getBackground());
+ table.addConfiguration(new DefaultNatTableStyleConfiguration());
+ table.configure();
+ }
+
+ protected void addConfigurations(SelectionLayer selectionLayer) {
+ // Pass
+ }
+
+ protected void configureBodyStyle(Style bodyStyle) {
+ // Pass
+ }
+
+ /**
+ * Sets the qualified property name (including model element path) of the
+ * {@linkplain DataSource data-source} property to access. The referenced
+ * property must be an {@linkplain IObservableList observable list}.
+ *
+ * @param propertyPath
+ * the property path
+ */
+ public void setProperty(String propertyPath) {
+ this.propertyPath = propertyPath;
+ checkInput();
+ }
+
+ public String getProperty() {
+ return propertyPath;
+ }
+
+ /**
+ * Sets the input DataSource for this Property editor.
+ *
+ * @param input
+ */
+ public void setInput(DataSource input) {
+ this.input = input;
+ checkInput();
+ }
+
+ protected void checkInput() {
+ String propertyPath = getProperty();
+
+ if ((input != null) && (propertyPath != null)) {
+ String elementPath = propertyPath.substring(0, propertyPath.lastIndexOf(':'));
+ String propertyName = propertyPath.substring(elementPath.length() + 1);
+
+ ModelElement element = input.getModelElement(elementPath);
+
+ ServiceConfigAttributes.with(configRegistry, this::getService)
+ .register(LabelProviderService.class)
+ .register(NavigationService.class)
+ .register(IPageManager.class);
+ ServiceConfigAttributes.registerService(ISelectionProvider.class, configRegistry,
+ getService(IMultiDiagramEditor.class).getEditorSite().getSelectionProvider());
+
+ eventLayer.setActive(false);
+ try {
+ @SuppressWarnings("unchecked")
+ IObservableList<E> diagrams = (IObservableList<E>) element.getObservable(propertyName);
+ this.backingList.setDelegate(diagrams);
+ } finally {
+ eventLayer.setActive(true);
+ table.refresh();
+ }
+ }
+ }
+
+ /**
+ * @return the input DataSource for this Property editor
+ */
+ public DataSource getInput() {
+ return input;
+ }
+
+ protected EObject getModelElement() {
+ return EMFHelper.getEObject(input.getSelection().getFirstElement());
+ }
+
+ protected <S> S getService(Class<? extends S> serviceType) {
+ try {
+ return ServiceUtilsForEObject.getInstance().getService(serviceType, getModelElement());
+ } catch (ServiceException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/HyperlinkTable.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/HyperlinkTable.java
new file mode 100644
index 00000000000..f268f4d4495
--- /dev/null
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.nattable/src/org/eclipse/papyrus/infra/editor/welcome/nattable/widgets/HyperlinkTable.java
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.editor.welcome.nattable.widgets;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
+import org.eclipse.nebula.widgets.nattable.style.Style;
+import org.eclipse.nebula.widgets.nattable.style.TextDecorationEnum;
+import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
+import org.eclipse.papyrus.infra.editor.welcome.nattable.hyperlink.HyperlinkNavigationConfiguration;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A table of hyperlinks for objects. One or more columns may render as hyperlinks to
+ * diagrams, views, etc. Label-based sorting of columns is provided.
+ */
+public abstract class HyperlinkTable<E> extends FormTable<E> {
+ public HyperlinkTable(Composite parent, int style, IColumnPropertyAccessor<E> columnAccessor, String... columnTitle) {
+ this(parent, style, null, columnAccessor, columnTitle);
+ }
+
+ public HyperlinkTable(Composite parent, int style, String label, IColumnPropertyAccessor<E> columnAccessor, String... columnTitle) {
+ super(parent, style, label, columnAccessor, columnTitle);
+ }
+
+ @Override
+ protected void addConfigurations(SelectionLayer selectionLayer) {
+ selectionLayer.addConfiguration(new HyperlinkNavigationConfiguration<>(getParent(), IObservableValue.class, this::isActiveHyperlink));
+ }
+
+ @Override
+ protected void configureBodyStyle(Style bodyStyle) {
+ bodyStyle.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR, GUIHelper.COLOR_BLUE);
+ bodyStyle.setAttributeValue(CellStyleAttributes.TEXT_DECORATION, TextDecorationEnum.UNDERLINE);
+ }
+
+ protected boolean isActiveHyperlink(IObservableValue<?> value) {
+ return (value != null) && (value.getValue() != null);
+ }
+}

Back to the top