\r
import java.util.Map;\r
\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Event;\r
import org.simantics.browsing.ui.GraphExplorer.ModificationContext;\r
+import org.simantics.browsing.ui.NodeContext;\r
import org.simantics.browsing.ui.content.Labeler;\r
\r
/**\r
* \r
* @author Tuukka Lehtonen\r
*/\r
+/**\r
+ * @author Jani Simomaa\r
+ *\r
+ */\r
public class LabelerStub implements Labeler {\r
\r
protected LabelerContent content = LabelerContent.NO_CONTENT;\r
public void setListener(LabelerListener listener) {\r
}\r
\r
+ /**\r
+ * @param event\r
+ * @param parent\r
+ * @param nodeContext\r
+ * @return\r
+ */\r
+ public Composite createToolTipContentArea(Event event, Composite parent, NodeContext nodeContext) {\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @param event\r
+ * @param nodeContext\r
+ * @return\r
+ */\r
+ public boolean shouldCreateToolTip(Event event, NodeContext nodeContext) {\r
+ return false;\r
+ }\r
+ \r
}\r
import java.util.Map;\r
\r
import org.simantics.browsing.ui.BuiltinKeys.LabelerKey;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.simantics.Simantics;\r
import org.simantics.browsing.ui.NodeContext;\r
import org.simantics.browsing.ui.PrimitiveQueryUpdater;\r
import org.simantics.browsing.ui.graph.impl.contribution.LabelerContributionImpl;\r
import org.simantics.browsing.ui.model.browsecontexts.BrowseContext;\r
+import org.simantics.browsing.ui.model.tooltips.TooltipContribution;\r
import org.simantics.db.ReadGraph;\r
import org.simantics.db.UndoContext;\r
+import org.simantics.db.common.request.UniqueRead;\r
+import org.simantics.db.common.utils.RequestUtil;\r
import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.ui.SimanticsUI;\r
\r
public class EvaluatorLabeler extends LabelerContributionImpl {\r
\r
final BrowseContext browseContext;\r
final boolean useNodeBrowseContexts;\r
\r
+ private TooltipContribution currentTooltipContribution;\r
+ \r
public EvaluatorLabeler(PrimitiveQueryUpdater updater, NodeContext context,\r
LabelerKey key,\r
BrowseContext browseContext, boolean useNodeBrowseContexts) {\r
return "EvaluatorLabeler[" + browseContext + "] " + context;\r
}\r
\r
+ \r
+ @Override\r
+ public boolean shouldCreateToolTip(Event event, NodeContext context) {\r
+ try {\r
+ currentTooltipContribution = RequestUtil.trySyncRequest(\r
+ Simantics.getSession(),\r
+ SimanticsUI.UI_THREAD_REQUEST_START_TIMEOUT,\r
+ SimanticsUI.UI_THREAD_REQUEST_EXECUTION_TIMEOUT,\r
+ null,\r
+ new UniqueRead<TooltipContribution>() {\r
+ @Override\r
+ public TooltipContribution perform(ReadGraph graph) throws DatabaseException {\r
+ return BrowseContext.get(graph,context,browseContext,useNodeBrowseContexts).shouldCreateToolTip(graph, event, context);\r
+ }\r
+ });\r
+ if (currentTooltipContribution != null)\r
+ return true;\r
+ } catch (DatabaseException | InterruptedException e) {\r
+ e.printStackTrace();\r
+ }\r
+ return false;\r
+ }\r
+ \r
+ @Override\r
+ public Composite createToolTipContentArea(Event event, Composite parent, NodeContext nodeContext) {\r
+ return (Composite) currentTooltipContribution.getTooltip(event, parent, nodeContext);\r
+ }\r
\r
}\r
org.simantics.browsing.ui.model.queries,
org.simantics.browsing.ui.model.sorters,
org.simantics.browsing.ui.model.tests,
+ org.simantics.browsing.ui.model.tooltips,
org.simantics.browsing.ui.model.visuals
Bundle-Vendor: VTT Technical Research Centre of Finland
uri="http://www.simantics.org/Viewpoint-0.0/PassThruSorterRule"\r
class="org.simantics.browsing.ui.model.sorters.PassThruSorterRule"/>\r
<resource\r uri="http://www.simantics.org/Viewpoint-0.0/LinkedListSorterRule"\r class="org.simantics.browsing.ui.model.sorters.LinkedListSorterRule"/>\r\r
- <baseType uri="http://www.simantics.org/Viewpoint-0.0/ImageDecorationRule" />\r
+ <baseType uri="http://www.simantics.org/Viewpoint-0.0/ImageDecorationRule" />\r \r <resource\r uri="http://www.simantics.org/Viewpoint-0.0/DescriptionTooltipRule"\r class="org.simantics.browsing.ui.model.tooltips.DescriptionTooltipRule" />\r
</target>\r
\r
<target interface="org.simantics.browsing.ui.model.nodetypes.NodeType">\r
\r
import org.eclipse.jface.resource.ImageDescriptor;\r
import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.Event;\r
import org.eclipse.swt.widgets.Shell;\r
import org.simantics.browsing.ui.BuiltinKeys;\r
import org.simantics.browsing.ui.CheckedState;\r
import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;\r
import org.simantics.browsing.ui.model.sorters.Sorter;\r
import org.simantics.browsing.ui.model.sorters.SorterContribution;\r
+import org.simantics.browsing.ui.model.tooltips.TooltipContribution;\r
import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;\r
import org.simantics.browsing.ui.model.visuals.VisualsContribution;\r
import org.simantics.db.ReadGraph;\r
OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();\r
OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();\r
OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();\r
+ OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions = new OrderedNodeTypeMultiMap<>();\r
\r
private final String[] uris; \r
\r
browseContext.imageDecorationContributions,\r
browseContext.modifierContributions,\r
browseContext.sorterContributions,\r
- browseContext.flatNodeContributions\r
+ browseContext.flatNodeContributions,\r
+ browseContext.tooltipContributions\r
);\r
}\r
}\r
return null;\r
}\r
\r
+ public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {\r
+ NodeType nodeType = getNodeType(graph, context);\r
+ if(nodeType != null)\r
+ for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) { \r
+ if (contribution.shouldCreateToolTip(graph, context))\r
+ return contribution;\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {\r
+ Object tooltip = contribution.getTooltip(event, parent, context);\r
+ if (tooltip != null)\r
+ return tooltip;\r
+ return null;\r
+ }\r
+ \r
private Graph toGraph() {\r
Graph graph = new Graph();\r
new Node(graph, "Foo");\r
--- /dev/null
+package org.simantics.browsing.ui.model.tooltips;\r
+\r
+import java.util.Map;\r
+\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.graphics.FontMetrics;\r
+import org.eclipse.swt.graphics.GC;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.simantics.browsing.ui.BuiltinKeys;\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.layer0.Layer0;\r
+\r
+public class DescriptionTooltipRule implements TooltipRule {\r
+ \r
+ public static final DescriptionTooltipRule INSTANCE = new DescriptionTooltipRule();\r
+ \r
+ public DescriptionTooltipRule() {\r
+ }\r
+\r
+ @Override\r
+ public Object createTooltip(Object event, Object parentComponent, NodeContext context, Map<Object, Object> auxiliary) {\r
+ Composite parent = (Composite)parentComponent;\r
+ Composite composite = new Composite(parent, SWT.NONE);\r
+ //ScrolledComposite composite = new ScrolledComposite(parent, SWT.NONE);\r
+ \r
+ composite.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
+ GridLayout layout = new GridLayout(1, false);\r
+ composite.setLayout(layout);\r
+ Text text = new Text(composite, SWT.NONE | SWT.READ_ONLY | SWT.WRAP);\r
+ text.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
+ \r
+ String toolTipContent = (String) auxiliary.get("content");\r
+ text.setText(toolTipContent);\r
+ \r
+ GC gc = new GC(text);\r
+ FontMetrics fm = gc.getFontMetrics();\r
+ int width = toolTipContent.length() * fm.getAverageCharWidth();\r
+ gc.dispose();\r
+ GridData gridData = new GridData();\r
+ if (width < 500)\r
+ gridData.widthHint = width;\r
+ else\r
+ gridData.widthHint = 500;\r
+ \r
+ text.setLayoutData(gridData);\r
+ \r
+ return composite;\r
+ }\r
+\r
+ @Override\r
+ public boolean isCompatible(Class<?> contentType) {\r
+ return (contentType == Resource.class || contentType == Variable.class);\r
+ }\r
+ \r
+ private static String getToolTipContent(ReadGraph graph, NodeContext nodeContext) throws DatabaseException {\r
+ Object input = nodeContext.getConstant(BuiltinKeys.INPUT);\r
+ String content = null;\r
+ if (input instanceof Variable) {\r
+ Variable var = (Variable) input;\r
+ Resource res = var.getPredicateResource(graph);\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ String description = graph.getPossibleRelatedValue2(res, L0.HasDescription);\r
+ return description;\r
+ } else if (input instanceof Resource) {\r
+ Resource res = (Resource) input;\r
+\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ String description = graph.getPossibleRelatedValue2(res, L0.HasDescription);\r
+ return description;\r
+ }\r
+ return content;\r
+ }\r
+\r
+ @Override\r
+ public boolean shouldCreateToolTip(ReadGraph graph , NodeContext context, Map<Object, Object> auxiliary) throws DatabaseException {\r
+ String content = getToolTipContent(graph, context);\r
+ if (content == null || content.isEmpty())\r
+ return false;\r
+ auxiliary.put("content", content);\r
+ return true;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.model.tooltips;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.simantics.browsing.ui.BuiltinKeys;\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.browsing.ui.model.InvalidContribution;\r
+import org.simantics.browsing.ui.model.nodetypes.NodeType;\r
+import org.simantics.browsing.ui.model.tests.Test;\r
+import org.simantics.browsing.ui.model.visuals.VisualsContribution;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.exception.DatabaseException;\r
+\r
+public class TooltipContribution extends VisualsContribution {\r
+ TooltipRule tooltipRule;\r
+ \r
+ private Map<Object, Object> auxiliary = new HashMap<>();\r
+ \r
+ public TooltipContribution(NodeType nodeType, Test test, TooltipRule tooltipRule, double priority) throws InvalidContribution {\r
+ super(nodeType, test, priority);\r
+ if(!tooltipRule.isCompatible(nodeType.getContentType()))\r
+ throw new InvalidContribution("Tooltip rule is not compatible with the content type.");\r
+ this.tooltipRule = tooltipRule;\r
+ }\r
+\r
+ public Object getTooltip(Object event, Object parent, NodeContext context) {\r
+ try {\r
+ return tooltipRule.createTooltip(event, parent, context, auxiliary);\r
+ } finally {\r
+ auxiliary.clear();\r
+ }\r
+ \r
+ } \r
+ \r
+ public boolean shouldCreateToolTip(ReadGraph graph, NodeContext context) throws DatabaseException {\r
+ Object content = context.getConstant(BuiltinKeys.INPUT);\r
+ if(test != null && !test.test(graph, content))\r
+ return false;\r
+ \r
+ return tooltipRule.shouldCreateToolTip(graph, context, auxiliary);\r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.model.tooltips;\r
+\r
+import java.util.Map;\r
+\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.browsing.ui.model.visuals.VisualsRule;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.exception.DatabaseException;\r
+\r
+public interface TooltipRule extends VisualsRule {\r
+\r
+ boolean shouldCreateToolTip(ReadGraph graph, NodeContext context, Map<Object, Object> auxiliary) throws DatabaseException;\r
+\r
+ Object createTooltip(Object event, Object parentComponent, NodeContext context, Map<Object, Object> auxiliary);\r
+\r
+}\r
import org.simantics.browsing.ui.model.sorters.SorterContribution;\r
import org.simantics.browsing.ui.model.sorters.SorterRule;\r
import org.simantics.browsing.ui.model.tests.Test;\r
+import org.simantics.browsing.ui.model.tooltips.TooltipContribution;\r
+import org.simantics.browsing.ui.model.tooltips.TooltipRule;\r
import org.simantics.db.ReadGraph;\r
import org.simantics.db.Resource;\r
import org.simantics.db.exception.AdaptionException;\r
OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions,\r
OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions,\r
OrderedNodeTypeMultiMap<SorterContribution> sorterContributions,\r
- OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions) \r
+ OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions,\r
+ OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions)\r
throws DatabaseException, InvalidContribution {\r
ViewpointResource vr = ViewpointResource.getInstance(g);\r
\r
imageDecorationContributions.put(nodeType, new ImageDecorationContribution(nodeType, test, (ImageDecorationRule)rule, priority));\r
if(rule instanceof SorterRule)\r
sorterContributions.put(nodeType, new SorterContribution(nodeType, test, (SorterRule)rule, priority));\r
+ if(rule instanceof TooltipRule)\r
+ tooltipContributions.put(nodeType, new TooltipContribution(nodeType, test, (TooltipRule)rule, priority));\r
} catch(InvalidContribution e) {\r
e.printStackTrace();\r
continue;\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.browsing.ui.nattable</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\r
+org.eclipse.jdt.core.compiler.compliance=1.8\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.8\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Nattable
+Bundle-SymbolicName: org.simantics.browsing.ui.nattable
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.simantics.browsing.ui.nattable.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.simantics;bundle-version="1.0.0",
+ org.simantics.ui;bundle-version="1.0.0",
+ org.simantics.utils.ui;bundle-version="1.1.0",
+ org.simantics.utils.thread.swt;bundle-version="1.1.0",
+ org.simantics.browsing.ui.swt;bundle-version="1.1.0",
+ org.eclipse.nebula.widgets.nattable.core;bundle-version="1.4.0",
+ it.unimi.dsi.fastutil;bundle-version="7.0.6"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Export-Package: org.simantics.browsing.ui.nattable
--- /dev/null
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.ui.plugin.AbstractUIPlugin;\r
+import org.osgi.framework.BundleContext;\r
+\r
+/**\r
+ * The activator class controls the plug-in life cycle\r
+ */\r
+public class Activator extends AbstractUIPlugin {\r
+\r
+ // The plug-in ID\r
+ public static final String PLUGIN_ID = "og.simantics.browsing.ui.nattable"; //$NON-NLS-1$\r
+\r
+ // The shared instance\r
+ private static Activator plugin;\r
+ \r
+ /**\r
+ * The constructor\r
+ */\r
+ public Activator() {\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)\r
+ */\r
+ public void start(BundleContext context) throws Exception {\r
+ super.start(context);\r
+ plugin = this;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)\r
+ */\r
+ public void stop(BundleContext context) throws Exception {\r
+ plugin = null;\r
+ super.stop(context);\r
+ }\r
+\r
+ /**\r
+ * Returns the shared instance\r
+ *\r
+ * @return the shared instance\r
+ */\r
+ public static Activator getDefault() {\r
+ return plugin;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;\r
+import org.simantics.browsing.ui.Column;\r
+import org.simantics.browsing.ui.content.Labeler.Modifier;\r
+\r
+\r
+public class GEColumnAccessor implements IColumnPropertyAccessor<TreeNode> {\r
+ NatTableGraphExplorer ge;\r
+ \r
+ public GEColumnAccessor(NatTableGraphExplorer ge) {\r
+ this.ge = ge;\r
+ }\r
+ \r
+ @Override\r
+ public int getColumnCount() {\r
+ return ge.getColumns().length;\r
+ }\r
+ \r
+ @Override\r
+ public Object getDataValue(TreeNode rowObject, int columnIndex) {\r
+ \r
+ if (columnIndex > 0)\r
+ return rowObject.getValueString(columnIndex);\r
+ else {\r
+ String val = "";\r
+ for (int i = 0 ; i <rowObject.getDepth(); i++)\r
+ val += " ";\r
+ return val + rowObject.getValueString(columnIndex);\r
+ }\r
+ \r
+ \r
+ }\r
+ \r
+ \r
+ @Override\r
+ public void setDataValue(TreeNode rowObject, int columnIndex, Object newValue) {\r
+ Modifier modifier = ge.getModifier(rowObject, columnIndex);\r
+ if (modifier == null)\r
+ throw new IllegalArgumentException("Items is not modifiable");\r
+ modifier.modify(newValue.toString());\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public String getColumnProperty(int columnIndex) {\r
+ return ge.getColumns()[columnIndex].getKey();\r
+ }\r
+ \r
+ @Override\r
+ public int getColumnIndex(String propertyName) {\r
+ Column columns[] = ge.getColumns();\r
+ for (int i = 0; i < columns.length; i++) {\r
+ if (columns[i].getKey().equals(propertyName))\r
+ return i;\r
+ }\r
+ return -1;\r
+ }\r
+ \r
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;\r
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;\r
+\r
+public class GEColumnHeaderDataProvider implements IDataProvider {\r
+ \r
+ NatTableGraphExplorer ge;\r
+ DataLayer dataLayer;\r
+ \r
+ public GEColumnHeaderDataProvider(NatTableGraphExplorer ge, DataLayer dataLayer) {\r
+ this.ge = ge;\r
+ this.dataLayer = dataLayer;\r
+ }\r
+ \r
+ @Override\r
+ public int getColumnCount() {\r
+ return ge.getColumns().length;\r
+ }\r
+ \r
+ @Override\r
+ public int getRowCount() {\r
+ return 1;\r
+ }\r
+ \r
+ @Override\r
+ public Object getDataValue(int columnIndex, int rowIndex) {\r
+ if (columnIndex < 0 || rowIndex < 0)\r
+ return null;\r
+ return ge.getColumns()[columnIndex].getLabel();\r
+ }\r
+ \r
+ @Override\r
+ public void setDataValue(int columnIndex, int rowIndex, Object newValue) {\r
+ throw new java.lang.UnsupportedOperationException();\r
+ }\r
+ \r
+ public void updateColumnSizes() {\r
+ for (int i = 0; i < getColumnCount(); i++) {\r
+ int w = ge.getColumns()[i].getWidth();\r
+ if (w > 0)\r
+ dataLayer.setColumnWidthByPosition(i, w);\r
+ }\r
+ }\r
+ \r
+ public DataLayer getDataLayer() {\r
+ return dataLayer;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.edit.action.CellEditDragMode;\r
+import org.eclipse.nebula.widgets.nattable.edit.action.KeyEditAction;\r
+import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;\r
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;\r
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;\r
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;\r
+import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;\r
+import org.eclipse.nebula.widgets.nattable.ui.matcher.CellPainterMouseEventMatcher;\r
+import org.eclipse.nebula.widgets.nattable.ui.matcher.KeyEventMatcher;\r
+import org.eclipse.nebula.widgets.nattable.ui.matcher.LetterOrDigitKeyEventMatcher;\r
+import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;\r
+import org.eclipse.swt.SWT;\r
+\r
+public class GEEditBindings extends AbstractUiBindingConfiguration {\r
+ \r
+ @Override\r
+ public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {\r
+ // configure the space key to activate a cell editor via keyboard\r
+ // this is especially useful for changing the value for a checkbox\r
+ uiBindingRegistry.registerKeyBinding(\r
+ new KeyEventMatcher(SWT.NONE, 32),\r
+ new KeyEditAction());\r
+ uiBindingRegistry.registerKeyBinding(\r
+ new KeyEventMatcher(SWT.NONE, SWT.F2),\r
+ new KeyEditAction());\r
+ uiBindingRegistry.registerKeyBinding(\r
+ new LetterOrDigitKeyEventMatcher(),\r
+ new KeyEditAction());\r
+ uiBindingRegistry.registerKeyBinding(\r
+ new LetterOrDigitKeyEventMatcher(SWT.MOD2),\r
+ new KeyEditAction());\r
+\r
+ uiBindingRegistry.registerSingleClickBinding(\r
+ new SelectedCellEditorMatcher(GridRegion.BODY),\r
+ new MouseEditAction());\r
+\r
+ uiBindingRegistry.registerMouseDragMode(\r
+ new CellEditorMouseEventMatcher(GridRegion.BODY),\r
+ new CellEditDragMode());\r
+\r
+ uiBindingRegistry.registerFirstSingleClickBinding(\r
+ new CellPainterMouseEventMatcher(GridRegion.BODY, MouseEventMatcher.LEFT_BUTTON, CheckBoxPainter.class),\r
+ new MouseEditAction());\r
+\r
+ uiBindingRegistry.registerFirstMouseDragMode(\r
+ new CellPainterMouseEventMatcher(GridRegion.BODY, MouseEventMatcher.LEFT_BUTTON, CheckBoxPainter.class),\r
+ new CellEditDragMode());\r
+\r
+ }\r
+\r
+ \r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;\r
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter;\r
+import org.eclipse.nebula.widgets.nattable.resize.command.ColumnResizeCommand;\r
+import org.eclipse.nebula.widgets.nattable.resize.command.RowResizeCommand;\r
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;\r
+import org.eclipse.nebula.widgets.nattable.style.CellStyleUtil;\r
+import org.eclipse.nebula.widgets.nattable.style.IStyle;\r
+import org.eclipse.swt.graphics.GC;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+\r
+/**\r
+ * Modified org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter, which does not allow setting wrapped painter\r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class GEIconPainter extends BackgroundPainter {\r
+ \r
+ protected boolean calculateByWidth;\r
+ protected boolean calculateByHeight;\r
+ \r
+ public GEIconPainter(ICellPainter painter) {\r
+ super(painter);\r
+ }\r
+ \r
+ @Override\r
+ public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {\r
+ Image image = getImage(cell, configRegistry);\r
+ if (image != null) {\r
+ return image.getBounds().width;\r
+ } else {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {\r
+ Image image = getImage(cell, configRegistry);\r
+ if (image != null) {\r
+ return image.getBounds().height;\r
+ } else {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public ICellPainter getCellPainterAt(int x, int y, ILayerCell cell, GC gc,\r
+ Rectangle bounds, IConfigRegistry configRegistry) {\r
+\r
+ Image image = getImage(cell, configRegistry);\r
+ if (image != null) {\r
+ Rectangle imageBounds = image.getBounds();\r
+ IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry);\r
+ int x0 = bounds.x\r
+ + CellStyleUtil.getHorizontalAlignmentPadding(\r
+ cellStyle, bounds, imageBounds.width);\r
+ int y0 = bounds.y\r
+ + CellStyleUtil.getVerticalAlignmentPadding(\r
+ cellStyle, bounds, imageBounds.height);\r
+ if (x >= x0 && x < x0 + imageBounds.width\r
+ && y >= y0 && y < y0 + imageBounds.height) {\r
+ return super.getCellPainterAt(x, y, cell, gc, bounds, configRegistry);\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public void paintCell(ILayerCell cell, GC gc, Rectangle bounds, IConfigRegistry configRegistry) {\r
+ \r
+ \r
+ Image image = getImage(cell, configRegistry);\r
+ if (image != null) {\r
+ Rectangle imageBounds = image.getBounds();\r
+ IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry);\r
+\r
+ int contentHeight = imageBounds.height;\r
+ if (this.calculateByHeight && (contentHeight > bounds.height)) {\r
+ int contentToCellDiff = (cell.getBounds().height - bounds.height);\r
+ ILayer layer = cell.getLayer();\r
+ layer.doCommand(new RowResizeCommand(\r
+ layer,\r
+ cell.getRowPosition(),\r
+ contentHeight + contentToCellDiff));\r
+ }\r
+\r
+ int contentWidth = imageBounds.width;\r
+ if (this.calculateByWidth && (contentWidth > bounds.width)) {\r
+ int contentToCellDiff = (cell.getBounds().width - bounds.width);\r
+ ILayer layer = cell.getLayer();\r
+ layer.doCommand(new ColumnResizeCommand(\r
+ layer,\r
+ cell.getColumnPosition(),\r
+ contentWidth + contentToCellDiff));\r
+ }\r
+ int px = CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, bounds, imageBounds.width);\r
+ int py = CellStyleUtil.getVerticalAlignmentPadding(cellStyle, bounds, imageBounds.height);\r
+ Rectangle b = new Rectangle(bounds.x + px + imageBounds.width, bounds.y, bounds.width - px - imageBounds.width, bounds.height);\r
+ super.paintCell(cell, gc, b, configRegistry);\r
+ gc.drawImage(\r
+ image,\r
+ bounds.x + px,\r
+ bounds.y + py);\r
+ } else {\r
+ super.paintCell(cell, gc, bounds, configRegistry);\r
+ }\r
+ }\r
+ \r
+// @Override\r
+// public Rectangle getWrappedPainterBounds(ILayerCell cell, GC gc, Rectangle bounds, IConfigRegistry configRegistry) {\r
+// Image image = getImage(cell, configRegistry);\r
+// if (image != null) {\r
+// Rectangle imageBounds = image.getBounds();\r
+// IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry);\r
+// int px = CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, bounds, imageBounds.width);\r
+// int py = CellStyleUtil.getVerticalAlignmentPadding(cellStyle, bounds, imageBounds.height);\r
+// Rectangle b = new Rectangle(bounds.x + px + imageBounds.width, bounds.y, bounds.width - px - imageBounds.width, bounds.height);\r
+// return b;\r
+// \r
+// }\r
+// return super.getWrappedPainterBounds(cell, gc, bounds, configRegistry);\r
+// }\r
+\r
+ /**\r
+ *\r
+ * @param cell\r
+ * The {@link ILayerCell} for which this {@link ImagePainter} is\r
+ * called.\r
+ * @param configRegistry\r
+ * The current {@link IConfigRegistry} to retrieve the cell style\r
+ * information from.\r
+ * @return The {@link Image} that should be painted by this\r
+ * {@link ImagePainter}.\r
+ */\r
+ protected Image getImage(ILayerCell cell, IConfigRegistry configRegistry) {\r
+ return CellStyleUtil.getCellStyle(cell, configRegistry).getAttributeValue(CellStyleAttributes.IMAGE);\r
+ }\r
+\r
+ /**\r
+ * @return <code>true</code> if this {@link ImagePainter} is resizing the\r
+ * cell width to show the whole configured image, <code>false</code>\r
+ * if the cell width is not touched by this painter.\r
+ */\r
+ public boolean isCalculateByWidth() {\r
+ return this.calculateByWidth;\r
+ }\r
+\r
+ /**\r
+ * Configure whether the {@link ImagePainter} should calculate the cell\r
+ * dimensions by containing image width. This means the <b>width</b> of the\r
+ * cell is calculated by image width.\r
+ *\r
+ * @param calculateByWidth\r
+ * <code>true</code> to calculate and modify the cell dimension\r
+ * according to the image width, <code>false</code> to not\r
+ * modifying the cell dimensions.\r
+ */\r
+ public void setCalculateByWidth(boolean calculateByWidth) {\r
+ this.calculateByWidth = calculateByWidth;\r
+ }\r
+\r
+ /**\r
+ * @return <code>true</code> if this {@link ImagePainter} is resizing the\r
+ * cell height to show the whole configured image,\r
+ * <code>false</code> if the cell height is not touched by this\r
+ * painter.\r
+ */\r
+ public boolean isCalculateByHeight() {\r
+ return this.calculateByHeight;\r
+ }\r
+\r
+ /**\r
+ * Configure whether the {@link ImagePainter} should calculate the cell\r
+ * dimensions by containing image height. This means the <b>height</b> of\r
+ * the cell is calculated by image height.\r
+ *\r
+ * @param calculateByHeight\r
+ * <code>true</code> to calculate and modify the cell dimension\r
+ * according to the image height, <code>false</code> to not\r
+ * modifying the cell dimensions.\r
+ */\r
+ public void setCalculateByHeight(boolean calculateByHeight) {\r
+ this.calculateByHeight = calculateByHeight;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.PaddingDecorator;\r
+import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.util.GUIHelper;\r
+\r
+public class GENatTableThemeConfiguration extends ModernNatTableThemeConfiguration{\r
+ \r
+ public GENatTableThemeConfiguration(GETreeData treeData) {\r
+ super();\r
+ this.oddRowBgColor = GUIHelper.getColor(250, 250, 250);\r
+ this.defaultCellPainter =\r
+ new GEStyler(treeData,\r
+ new GEIconPainter(\r
+ new PaddingDecorator(\r
+ new TextPainter(),\r
+ 0,\r
+ 5,\r
+ 0,\r
+ 5,\r
+ false)));\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;\r
+import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;\r
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;\r
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;\r
+import org.eclipse.nebula.widgets.nattable.style.ConfigAttribute;\r
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;\r
+import org.eclipse.nebula.widgets.nattable.style.IDisplayModeOrdering;\r
+import org.eclipse.nebula.widgets.nattable.style.Style;\r
+import org.eclipse.swt.graphics.GC;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+\r
+public class GEStyler extends CellPainterWrapper{\r
+ \r
+ private GETreeData treeData;\r
+ \r
+ public GEStyler(GETreeData treeData, ICellPainter painter) {\r
+ super(painter);\r
+ this.treeData = treeData;\r
+ }\r
+ \r
+ private ConfigRegistryWrapper wrapper = new ConfigRegistryWrapper();\r
+\r
+ @Override\r
+ public void paintCell(ILayerCell cell, GC gc, Rectangle rectangle, IConfigRegistry configRegistry) {\r
+ wrapper.clear();\r
+ wrapper.wrappedRegistry = configRegistry;\r
+ TreeNode node = treeData.getDataAtIndex(cell.getRowIndex());\r
+ Style style = new Style();\r
+ node.getStyle(cell.getColumnIndex(), style);\r
+ Image image = node.getImage(cell.getColumnIndex());\r
+ if (image != null)\r
+ style.setAttributeValue(CellStyleAttributes.IMAGE, image);\r
+ \r
+ wrapper.setSpecificConfigAttribute(CellConfigAttributes.CELL_STYLE, DisplayMode.NORMAL, "BODY", style);\r
+// wrapper.setSpecificConfigAttribute(CellStyleAttributes.FOREGROUND_COLOR, DisplayMode.NORMAL, "BODY", style.getAttributeValue(CellStyleAttributes.FOREGROUND_COLOR));\r
+// wrapper.setSpecificConfigAttribute(CellStyleAttributes.BACKGROUND_COLOR, DisplayMode.NORMAL, "BODY", style.getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR));\r
+// wrapper.setSpecificConfigAttribute(CellStyleAttributes.FONT, DisplayMode.NORMAL, "BODY", style.getAttributeValue(CellStyleAttributes.FONT));\r
+ super.paintCell(cell, gc, rectangle, wrapper);\r
+ }\r
+ \r
+ private class ConfigRegistryWrapper extends ConfigRegistry {\r
+ IConfigRegistry wrappedRegistry;\r
+ Map<ConfigAttribute<?>, Map<String, Map<String, ?>>> configRegistry = new HashMap<ConfigAttribute<?>, Map<String, Map<String, ?>>>();\r
+ \r
+ public void clear() {\r
+ configRegistry.clear();\r
+ }\r
+\r
+ @Override\r
+ public <T> T getConfigAttribute(ConfigAttribute<T> configAttribute, String targetDisplayMode,\r
+ String... configLabels) {\r
+ return wrappedRegistry.getConfigAttribute(configAttribute, targetDisplayMode, configLabels);\r
+ }\r
+\r
+ @Override\r
+ public <T> T getConfigAttribute(ConfigAttribute<T> configAttribute, String targetDisplayMode,\r
+ List<String> configLabels) {\r
+ return wrappedRegistry.getConfigAttribute(configAttribute, targetDisplayMode, configLabels);\r
+ }\r
+\r
+ @Override\r
+ public <T> T getSpecificConfigAttribute(ConfigAttribute<T> configAttribute, String displayMode,\r
+ String configLabel) {\r
+ T value = _getSpecificConfigAttribute(configAttribute, displayMode, configLabel);\r
+ if (value != null)\r
+ return value;\r
+ return wrappedRegistry.getSpecificConfigAttribute(configAttribute, displayMode, configLabel);\r
+ }\r
+ \r
+ public <T> T _getSpecificConfigAttribute(ConfigAttribute<T> configAttribute,\r
+ String displayMode, String configLabel) {\r
+ T attributeValue = null;\r
+\r
+ Map<String, Map<String, ?>> displayModeConfigAttributeMap = this.configRegistry\r
+ .get(configAttribute);\r
+ if (displayModeConfigAttributeMap != null) {\r
+ Map<String, T> configAttributeMap = (Map<String, T>) displayModeConfigAttributeMap.get(displayMode);\r
+ if (configAttributeMap != null) {\r
+ attributeValue = configAttributeMap.get(configLabel);\r
+ if (attributeValue != null) {\r
+ return attributeValue;\r
+ }\r
+ }\r
+ }\r
+\r
+ return attributeValue;\r
+ }\r
+ \r
+ public <T> void setSpecificConfigAttribute(ConfigAttribute<T> configAttribute, String displayMode,\r
+ String configLabel, T attributeValue) {\r
+ Map<String, Map<String, ?>> displayModeConfigAttributeMap = this.configRegistry\r
+ .get(configAttribute);\r
+ if (displayModeConfigAttributeMap == null) {\r
+ displayModeConfigAttributeMap = new HashMap<String, Map<String, ?>>();\r
+ this.configRegistry.put(configAttribute, displayModeConfigAttributeMap);\r
+ }\r
+\r
+ Map<String, T> configAttributeMap = (Map<String, T>) displayModeConfigAttributeMap.get(displayMode);\r
+ if (configAttributeMap == null) {\r
+ configAttributeMap = new HashMap<String, T>();\r
+ displayModeConfigAttributeMap.put(displayMode, configAttributeMap);\r
+ }\r
+\r
+ configAttributeMap.put(configLabel, attributeValue);\r
+ }\r
+\r
+ @Override\r
+ public <T> void registerConfigAttribute(ConfigAttribute<T> configAttribute, T attributeValue) {\r
+ wrappedRegistry.registerConfigAttribute(configAttribute, attributeValue);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public <T> void registerConfigAttribute(ConfigAttribute<T> configAttribute, T attributeValue,\r
+ String targetDisplayMode) {\r
+ wrappedRegistry.registerConfigAttribute(configAttribute, attributeValue, targetDisplayMode);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public <T> void registerConfigAttribute(ConfigAttribute<T> configAttribute, T attributeValue,\r
+ String targetDisplayMode, String configLabel) {\r
+ wrappedRegistry.registerConfigAttribute(configAttribute, attributeValue, targetDisplayMode, configLabel);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public <T> void unregisterConfigAttribute(ConfigAttribute<T> configAttributeType) {\r
+ wrappedRegistry.unregisterConfigAttribute(configAttributeType);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public <T> void unregisterConfigAttribute(ConfigAttribute<T> configAttributeType, String displayMode) {\r
+ wrappedRegistry.unregisterConfigAttribute(configAttributeType, displayMode);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public <T> void unregisterConfigAttribute(ConfigAttribute<T> configAttributeType, String displayMode,\r
+ String configLabel) {\r
+ wrappedRegistry.unregisterConfigAttribute(configAttributeType, displayMode, configLabel);\r
+ }\r
+\r
+ @Override\r
+ public IDisplayModeOrdering getDisplayModeOrdering() {\r
+ return wrappedRegistry.getDisplayModeOrdering();\r
+ }\r
+ \r
+ }\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.eclipse.nebula.widgets.nattable.tree.ITreeData;\r
+\r
+public class GETreeData implements ITreeData<TreeNode> {\r
+ List<TreeNode> list;\r
+ \r
+ public GETreeData(List<TreeNode> list) {\r
+ this.list = list;\r
+ }\r
+ \r
+ @Override\r
+ public String formatDataForDepth(int depth, TreeNode object) {\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public List<TreeNode> getChildren(TreeNode object) {\r
+ return (List<TreeNode>)object.getChildren();\r
+ }\r
+ \r
+ @Override\r
+ public TreeNode getDataAtIndex(int index) {\r
+ if (index < 0 || index >= list.size() )\r
+ return null;\r
+ return list.get(index);\r
+ }\r
+ \r
+ @Override\r
+ public int getDepthOfData(TreeNode object) {\r
+ int count = object.getDepth()-1; // -1 removes invisible root.\r
+ return count;\r
+ }\r
+ \r
+ @Override\r
+ public boolean hasChildren(TreeNode object) {\r
+ return object.getChildren().size() > 0;\r
+ }\r
+ \r
+ @Override\r
+ public int indexOf(TreeNode child) {\r
+ return child.getListIndex();\r
+ }\r
+ \r
+ @Override\r
+ public boolean hasChildren(int index) {\r
+ return hasChildren(list.get(index));\r
+ }\r
+ \r
+ @Override\r
+ public String formatDataForDepth(int depth, int index) {\r
+ return formatDataForDepth(depth, list.get(index));\r
+ }\r
+ \r
+ @Override\r
+ public List<TreeNode> getChildren(int index) {\r
+ return getChildren(list.get(index));\r
+ }\r
+ \r
+ @Override\r
+ public List<TreeNode> getChildren(TreeNode object, boolean fullDepth) {\r
+ if (!fullDepth) {\r
+ return getChildren(object);\r
+ } else {\r
+ List<TreeNode> list = new ArrayList<TreeNode>();\r
+ _convertToList(list, object);\r
+ return list;\r
+ }\r
+ \r
+ }\r
+ private void _convertToList(List<TreeNode> list, TreeNode task) {\r
+ list.add(task);\r
+ for (TreeNode t : task.getChildren()) {\r
+ _convertToList(list, t);\r
+ }\r
+ }\r
+ @Override\r
+ public int getDepthOfData(int index) {\r
+ return getDepthOfData(list.get(index));\r
+ }\r
+ \r
+ @Override\r
+ public boolean isValidIndex(int index) {\r
+ if (index < 0)\r
+ return false;\r
+ if (index >= list.size())\r
+ return false;\r
+ return true;\r
+ }\r
+ \r
+ @Override\r
+ public int getElementCount() {\r
+ return list.size();\r
+ }\r
+ \r
+ public boolean isRoot(TreeNode object) {\r
+ TreeNode parent = object.getParent();\r
+ parent = parent.getParent();\r
+ return (parent == null);\r
+ }\r
+\r
+ \r
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;\r
+import org.simantics.browsing.ui.nattable.override.TreeLayer2;\r
+import org.simantics.databoard.util.IdentityHashSet;\r
+\r
+import it.unimi.dsi.fastutil.ints.IntRBTreeSet;\r
+/**\r
+ * NatTable TreeLayer for IEcoReportTask tree.\r
+ * \r
+ * Keeps track of collapsed nodes so that current sorting mechanism works.\r
+ * \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class GETreeLayer extends TreeLayer2 {\r
+\r
+ //Set<IEcoReportTask> collapsed = new HashSet<IEcoReportTask>();\r
+ Set<TreeNode> collapsed = new IdentityHashSet<TreeNode>();\r
+ GETreeData treeData;\r
+ Comparator<int[]> comparator = new FirstElementComparator();\r
+ \r
+ public GETreeLayer(IUniqueIndexLayer underlyingLayer, GETreeRowModel<TreeNode> treeRowModel,boolean useDefaultConfiguration) {\r
+ super(underlyingLayer, treeRowModel, useDefaultConfiguration);\r
+ \r
+ if (underlyingLayer instanceof AbstractRowHideShowLayer) {\r
+ throw new IllegalArgumentException("Cannot use treelayer above row hide layer");\r
+ }\r
+ \r
+ this.treeData = (GETreeData)treeRowModel.getTreeData();\r
+ hiddenPos = new ArrayList<int[]>();\r
+ hiddenPos.add(new int[]{0,0});\r
+ }\r
+\r
+ \r
+ @Override\r
+ public void collapseTreeRow(int parentIndex) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ collapsed.add(task);\r
+ task.setExpanded(false);\r
+ super.collapseTreeRow(parentIndex);\r
+ }\r
+ \r
+ public void fullCollapseTreeRow(int parentIndex) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ List<Integer> indices = new ArrayList<Integer>();\r
+ \r
+ Stack<TreeNode> stack = new Stack<TreeNode>();\r
+ stack.add(task);\r
+ while (!stack.isEmpty()) {\r
+ TreeNode t = stack.pop();\r
+ indices.add(treeData.indexOf(t));\r
+ stack.addAll(t.getChildren());\r
+ }\r
+ collapseTreeRow(indices);\r
+ }\r
+ \r
+ @Override\r
+ public void expandTreeRow(int parentIndex) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ collapsed.remove(task);\r
+ task.setExpanded(true);\r
+ super.expandTreeRow(parentIndex);\r
+ }\r
+ \r
+ public void expandToTreeRow(int parentIndex) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ List<TreeNode> ancestors = new ArrayList<TreeNode>();\r
+ while (true) {\r
+ task = task.getParent();\r
+ if (task == null)\r
+ break;\r
+ else\r
+ ancestors.add(0, task);\r
+ }\r
+ for (TreeNode t : ancestors) {\r
+ if (treeData.getDepthOfData(t) >= 0)\r
+ expandTreeRow(treeData.indexOf(t));\r
+ }\r
+ }\r
+ \r
+ public void fullExpandTreeRow(int parentIndex) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ List<Integer> indices = new ArrayList<Integer>();\r
+ \r
+ Stack<TreeNode> stack = new Stack<TreeNode>();\r
+ stack.add(task);\r
+ while (!stack.isEmpty()) {\r
+ TreeNode t = stack.pop();\r
+ indices.add(treeData.indexOf(t));\r
+ stack.addAll(t.getChildren());\r
+ }\r
+ expandTreeRow(indices);\r
+ }\r
+ \r
+ public void collapseTreeRow(int parentIndices[]) {\r
+ List<Integer> rowPositions = new ArrayList<Integer>();\r
+ List<Integer> rowIndexes = new ArrayList<Integer>();\r
+ // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set.\r
+ for (int parentIndex : parentIndices) {\r
+ if (parentIndex >= 0) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ if (task != null) {\r
+ task.setExpanded(false);\r
+ collapsed.add(task);\r
+ }\r
+ rowIndexes.addAll(getModel().collapse(parentIndex));\r
+ }\r
+ }\r
+ for (Integer rowIndex : rowIndexes) {\r
+ int rowPos = getRowPositionByIndex(rowIndex);\r
+ //if the rowPos is negative, it is not visible because of hidden state in an underlying layer\r
+ if (rowPos >= 0) {\r
+ rowPositions.add(rowPos);\r
+ }\r
+ }\r
+ //this.getHiddenRowIndexes().addAll(rowIndexes);\r
+ for (int i = 0; i < rowIndexes.size(); i++) {\r
+ this.getHiddenRowIndexes().add(rowIndexes.get(i));\r
+ }\r
+ invalidateCache();\r
+ fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));\r
+ }\r
+ \r
+ public void collapseTreeRow(List<Integer> parentIndices) {\r
+ List<Integer> rowPositions = new ArrayList<Integer>();\r
+ List<Integer> rowIndexes = new ArrayList<Integer>();\r
+ // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set.\r
+ for (int parentIndex : parentIndices) {\r
+ if (parentIndex >= 0) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ task.setExpanded(false);\r
+ collapsed.add(task);\r
+ rowIndexes.addAll(getModel().collapse(parentIndex));\r
+ }\r
+ }\r
+ for (Integer rowIndex : rowIndexes) {\r
+ int rowPos = getRowPositionByIndex(rowIndex);\r
+ //if the rowPos is negative, it is not visible because of hidden state in an underlying layer\r
+ if (rowPos >= 0) {\r
+ rowPositions.add(rowPos);\r
+ }\r
+ }\r
+ //this.getHiddenRowIndexes().addAll(rowIndexes);\r
+ for (int i = 0; i < rowIndexes.size(); i++) {\r
+ this.getHiddenRowIndexes().add(rowIndexes.get(i));\r
+ }\r
+ invalidateCache();\r
+ fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));\r
+ }\r
+ \r
+ public void collapseAllRows() {\r
+ int count = treeData.getElementCount();\r
+ List <Integer> rowIndexes = new ArrayList<Integer>(count);\r
+ for (int i = 0; i < count; i++) {\r
+ TreeNode t = treeData.getDataAtIndex(i);\r
+ // we don't want to hide the roots of the tree\r
+ if (!treeData.isRoot(t)) { \r
+ rowIndexes.add(i);\r
+ \r
+ } \r
+ t.setExpanded(false);\r
+ collapsed.add(t);\r
+ getModel().collapse(i);\r
+ \r
+ }\r
+ this.getHiddenRowIndexes().addAll(rowIndexes);\r
+ invalidateCache();\r
+ fireLayerEvent(new HideRowPositionsEvent(this, rowIndexes));\r
+ }\r
+ \r
+ public void expandTreeRow(int parentIndices[]) {\r
+ List<Integer> rowIndexes = new ArrayList<Integer>();\r
+ for (int parentIndex : parentIndices) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ task.setExpanded(true);\r
+ collapsed.remove(task);\r
+ rowIndexes.addAll(getModel().expand(parentIndex));\r
+ }\r
+ \r
+ //Implementation uses tree set, so removing in reverse order is faster.\r
+ for (int i = rowIndexes.size() -1 ; i >= 0; i--) {\r
+ this.getHiddenRowIndexes().remove(rowIndexes.get(i));\r
+ }\r
+ //this.getHiddenRowIndexes().removeAll(rowIndexes);\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+ \r
+ public void expandTreeRow(List<Integer> parentIndices) {\r
+ List<Integer> rowIndexes = new ArrayList<Integer>();\r
+ for (int parentIndex : parentIndices) {\r
+ TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+ task.setExpanded(true);\r
+ collapsed.remove(task);\r
+ rowIndexes.addAll(getModel().expand(parentIndex));\r
+ }\r
+ \r
+ //Implementation uses tree set, so removing in reverse order is faster.\r
+ for (int i = rowIndexes.size() -1 ; i >= 0; i--) {\r
+ this.getHiddenRowIndexes().remove(rowIndexes.get(i));\r
+ }\r
+ //this.getHiddenRowIndexes().removeAll(rowIndexes);\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+ \r
+ public void expandAllRows() {\r
+ Collection<Integer> parentIndices = getHiddenRowIndexes();\r
+ List<Integer> rowIndexes = new ArrayList<Integer>();\r
+ for (int parentIndex : parentIndices) {\r
+ rowIndexes.addAll(getModel().expand(parentIndex));\r
+ }\r
+ for (TreeNode t : collapsed)\r
+ t.setExpanded(true);\r
+ collapsed.clear();\r
+ getHiddenRowIndexes().clear();\r
+ ((GETreeRowModel)getModel()).clear();\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+ \r
+ @Override\r
+ protected void invalidateCache() {\r
+ super.invalidateCache();\r
+ hiddenPos.clear();\r
+ hiddenPos.add(new int[]{0,0});\r
+ }\r
+ \r
+ @Override\r
+ public void handleLayerEvent(ILayerEvent event) {\r
+ // Currently sorting is implemented by sorting the underlaying list.\r
+ // Since all layers are storing just indices, we have to keep track the indices after sorting,\r
+ // and refresh the layers accordingly.\r
+ \r
+ // Another option would use some sort of sorting layers, so that the original data is kept intact, and\r
+ // sorting layer would map the row indexes to sorted row positions.\r
+ \r
+ // preserve collapsed nodes \r
+ Set<TreeNode> coll = null;\r
+ if (event instanceof IStructuralChangeEvent) {\r
+ IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;\r
+ if (structuralChangeEvent.isVerticalStructureChanged()) {\r
+ // expand old indices\r
+ ((GETreeRowModel)getModel()).clear();\r
+ getHiddenRowIndexes().clear();\r
+ coll = collapsed;\r
+ }\r
+ }\r
+ super.handleLayerEvent(event);\r
+ if (coll != null) {\r
+ // collapse new indices\r
+ int ind[] = new int[coll.size()];\r
+ Iterator<TreeNode> iter = coll.iterator();\r
+ for (int i = 0; i < ind.length; i++) {\r
+ ind[i] = treeData.indexOf(iter.next());\r
+ }\r
+ collapseTreeRow(ind);\r
+ }\r
+ }\r
+ \r
+ public Set<TreeNode> getCollapsed() {\r
+ return collapsed;\r
+ }\r
+ \r
+ List<int[]> hiddenPos;\r
+ \r
+ @Override\r
+ public int getStartYOfRowPosition(int localRowPosition) {\r
+ Integer cachedStartY = startYCache.get(Integer.valueOf(localRowPosition));\r
+ if (cachedStartY != null) {\r
+ return cachedStartY.intValue();\r
+ }\r
+ \r
+ IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();\r
+ int underlyingPosition = localToUnderlyingRowPosition(localRowPosition);\r
+ int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition);\r
+ if (underlyingStartY < 0) {\r
+ return -1;\r
+ }\r
+\r
+ int h = 0;\r
+ int start = 0;\r
+ \r
+ if (hiddenPos.size() < 2) { // is cache empty? (hiddenPos contains {0,0} element by default) \r
+ if (getHiddenRowIndexes().size() > 0) // check if there are hidden rows.\r
+ start = getHiddenRowIndexes().iterator().next();\r
+ } else {\r
+ int[] d = hiddenPos.get(hiddenPos.size()-1); // take the last element of the cache.\r
+ start = d[0]+1; // set to search from the next element.\r
+ h = d[1];\r
+ }\r
+ if (start < underlyingPosition) { // check if we can find the amount of hidden space from the cache.\r
+ // cache positions of hidden nodes and hidden space.\r
+ //for (Integer hiddenIndex : ((TreeSet<Integer>)getHiddenRowIndexes()).tailSet(start)) {\r
+ for (int hiddenIndex : ((IntRBTreeSet)getHiddenRowIndexes()).tailSet(start)) {\r
+ // safety check (could be disabled, but this does not seem to cause considerable performance hit)\r
+ int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);//.intValue());\r
+ if (hiddenPosition != hiddenIndex)//.intValue())\r
+ throw new RuntimeException("Underlying layer is swithing indices");\r
+ if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) {\r
+ h += underlyingLayer.getRowHeightByPosition(hiddenPosition); \r
+ hiddenPos.add(new int[]{hiddenPosition,h});\r
+ } else if (hiddenPosition > underlyingPosition) {\r
+ break;\r
+ }\r
+ }\r
+ } else {\r
+ // use binary search to find hidden space.\r
+ h = 0;\r
+ int index = Collections.binarySearch(hiddenPos, new int[]{underlyingPosition,0}, comparator);\r
+ if (index < 0) { // exact element is not cached, but we can use the closest match.\r
+ index = -index-2;\r
+ } \r
+ h = hiddenPos.get(index)[1];\r
+ }\r
+ underlyingStartY -= h;\r
+ startYCache.put(Integer.valueOf(localRowPosition), Integer.valueOf(underlyingStartY));\r
+ return underlyingStartY;\r
+ }\r
+ \r
+ \r
+ private static class FirstElementComparator implements Comparator<int[]> {\r
+ @Override\r
+ public int compare(int[] o1, int[] o2) {\r
+ return o1[0]-o2[0];\r
+ }\r
+ }\r
+ \r
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+\r
+import org.eclipse.nebula.widgets.nattable.tree.ITreeData;\r
+import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;\r
+import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModelListener;\r
+\r
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;\r
+\r
+/**\r
+ * ITreeRowModel that does not automatically expand all child nodes (as TreeRowModel does). \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ * @param <T>\r
+ */\r
+public class GETreeRowModel<T> implements ITreeRowModel<T>{\r
+ //private final HashSet<Integer> parentIndexes = new HashSet<Integer>();\r
+ //private final TIntHashSet parentIndexes = new TIntHashSet(1000, 0.8f);\r
+ private final IntOpenHashSet parentIndexes = new IntOpenHashSet();\r
+\r
+ private final Collection<ITreeRowModelListener> listeners = new HashSet<ITreeRowModelListener>();\r
+\r
+ private final ITreeData<T> treeData;\r
+\r
+ public GETreeRowModel(ITreeData<T> treeData) {\r
+ this.treeData = treeData;\r
+ }\r
+\r
+ public void registerRowGroupModelListener(ITreeRowModelListener listener) {\r
+ this.listeners.add(listener);\r
+ }\r
+\r
+ public void notifyListeners() {\r
+ for (ITreeRowModelListener listener : this.listeners) {\r
+ listener.treeRowModelChanged();\r
+ }\r
+ }\r
+\r
+ public int depth(int index) {\r
+ return this.treeData.getDepthOfData(this.treeData.getDataAtIndex(index));\r
+ }\r
+\r
+ public boolean isLeaf(int index) {\r
+ return !hasChildren(index);\r
+ }\r
+\r
+ public String getObjectAtIndexAndDepth(int index, int depth) {\r
+ return this.treeData.formatDataForDepth(depth,this.treeData.getDataAtIndex(index));\r
+ }\r
+\r
+ public boolean hasChildren(int index) {\r
+ return this.treeData.hasChildren(this.treeData.getDataAtIndex(index));\r
+ }\r
+ \r
+ public boolean isCollapsed(int index) {\r
+ return this.parentIndexes.contains(index);\r
+ }\r
+\r
+ public void clear() {\r
+ this.parentIndexes.clear();\r
+ }\r
+ \r
+ @Override\r
+ public boolean isCollapsible(int index) {\r
+ return hasChildren(index);\r
+ }\r
+\r
+ @Override\r
+ public List<Integer> collapse(int index) {\r
+ this.parentIndexes.add(index);\r
+ notifyListeners();\r
+ return getChildIndexes(index);\r
+ }\r
+ \r
+ \r
+\r
+ @Override\r
+ public List<Integer> expand(int index) {\r
+ this.parentIndexes.remove(index);\r
+ notifyListeners();\r
+ List<Integer> children = getExpandedChildIndexes(index);\r
+ return children;\r
+ }\r
+ \r
+ @Override\r
+ public List<Integer> collapseAll() {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public List<Integer> expandToLevel(int level) {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public List<Integer> expandToLevel(T object, int level) {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public List<Integer> expandAll() {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public List<Integer> expandToLevel(int parentIndex, int level) {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public List<T> getChildren(int parentIndex) {\r
+ T t = treeData.getDataAtIndex(parentIndex);\r
+ return treeData.getChildren(t,true);\r
+ }\r
+ \r
+ @Override\r
+ public List<T> getDirectChildren(int parentIndex) {\r
+ return treeData.getChildren(parentIndex);\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public List<Integer> collapse(T object) {\r
+ int index = treeData.indexOf(object);\r
+ return collapse(index);\r
+ }\r
+ \r
+ @Override\r
+ public List<Integer> expand(T object) {\r
+ int index = treeData.indexOf(object);\r
+ return expand(index);\r
+ }\r
+ \r
+ @Override\r
+ public boolean isCollapsed(T object) {\r
+ int index = treeData.indexOf(object);\r
+ return isCollapsed(index);\r
+ }\r
+ \r
+\r
+ @SuppressWarnings("unchecked")\r
+ public List<Integer> getChildIndexes(int parentIndex) {\r
+ List<Integer> result = new ArrayList<Integer>();\r
+ T t = this.treeData.getDataAtIndex(parentIndex);\r
+ if (t == null)\r
+ return Collections.EMPTY_LIST;\r
+ List<T> children = this.treeData.getChildren(t);\r
+ for (T child : children) {\r
+ int index = this.treeData.indexOf(child);\r
+ if (index >= 0) {\r
+ result.add(index);\r
+ result.addAll(getChildIndexes(index));\r
+ } else {\r
+ result.addAll(getChildIndexes(child));\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ public List<Integer> getChildIndexes(T t) {\r
+ List<Integer> result = new ArrayList<Integer>();\r
+ List<T> children = this.treeData.getChildren(t);\r
+ for (T child : children) {\r
+ int index = this.treeData.indexOf(child);\r
+ if (index >= 0) {\r
+ result.add(index);\r
+ result.addAll(getChildIndexes(index));\r
+ } else {\r
+ result.addAll(getChildIndexes(child));\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ public List<Integer> getExpandedChildIndexes(int parentIndex) {\r
+ List<Integer> result = new ArrayList<Integer>();\r
+ T t = this.treeData.getDataAtIndex(parentIndex);\r
+ if (t == null)\r
+ return Collections.EMPTY_LIST;\r
+ List<T> children = this.treeData.getChildren(t);\r
+ for (T child : children) {\r
+ int index = this.treeData.indexOf(child);\r
+ if (index >= 0) {\r
+ result.add(index);\r
+ if (!parentIndexes.contains(index))\r
+ result.addAll(getExpandedChildIndexes(index));\r
+ } else {\r
+ result.addAll(getExpandedChildIndexes(child));\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ public List<Integer> getExpandedChildIndexes(T t) {\r
+ List<Integer> result = new ArrayList<Integer>();\r
+ List<T> children = this.treeData.getChildren(t);\r
+ for (T child : children) {\r
+ int index = this.treeData.indexOf(child);\r
+ if (index >= 0) {\r
+ result.add(index);\r
+ if (!parentIndexes.contains(index))\r
+ result.addAll(getExpandedChildIndexes(index));\r
+ } else {\r
+ result.addAll(getExpandedChildIndexes(child));\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ @Override\r
+ public List<Integer> getDirectChildIndexes(int parentIndex) {\r
+ List<Integer> result = new ArrayList<Integer>();\r
+ T t = this.treeData.getDataAtIndex(parentIndex);\r
+ if (t == null)\r
+ return Collections.EMPTY_LIST;\r
+ List<T> children = this.treeData.getChildren(t);\r
+ for (T child : children) {\r
+ int index = this.treeData.indexOf(child);\r
+ if (index >= 0) {\r
+ result.add(index);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ public ITreeData<T> getTreeData() {\r
+ return treeData;\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+public class ImageTask {\r
+ TreeNode node;\r
+ Object descsOrImage;\r
+ public ImageTask(TreeNode node, Object descsOrImage) {\r
+ this.node = node;\r
+ this.descsOrImage = descsOrImage;\r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.eclipse.core.runtime.Assert;\r
+import org.eclipse.jface.util.Util;\r
+import org.eclipse.jface.viewers.ColumnLayoutData;\r
+import org.eclipse.jface.viewers.ColumnPixelData;\r
+import org.eclipse.jface.viewers.ColumnWeightData;\r
+import org.eclipse.nebula.widgets.nattable.NatTable;\r
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ColumnDeleteEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ColumnInsertEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEvent;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Layout;\r
+import org.eclipse.swt.widgets.Scrollable;\r
+\r
+\r
+/**\r
+ * Modified org.eclipse.jface.layout.AbstractColumnLayout and TreeColumnLayout to NatTable compatible.\r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class NatTableColumnLayout extends Layout implements ILayerListener{\r
+ private static int COLUMN_TRIM;\r
+ static {\r
+ if (Util.isWindows()) {\r
+ COLUMN_TRIM = 4;\r
+ } else if (Util.isMac()) {\r
+ COLUMN_TRIM = 24;\r
+ } else {\r
+ COLUMN_TRIM = 3;\r
+ }\r
+ }\r
+ \r
+ NatTable natTable;\r
+ GEColumnHeaderDataProvider columnHeaderDataProvider;\r
+ DefaultRowHeaderDataLayer rowHeaderDataLayer;\r
+ Map<Integer, ColumnLayoutData> layoutDatas = new HashMap<>();\r
+ \r
+ private boolean inupdateMode = false;\r
+\r
+ private boolean relayout = true;\r
+ \r
+ public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider) {\r
+ this.natTable = natTable;\r
+ this.columnHeaderDataProvider = columnHeaderDataProvider;\r
+ this.natTable.addLayerListener(this);\r
+ }\r
+ \r
+ public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider, DefaultRowHeaderDataLayer rowHeaderDataLayer) {\r
+ this.natTable = natTable;\r
+ this.columnHeaderDataProvider = columnHeaderDataProvider;\r
+ this.natTable.addLayerListener(this);\r
+ this.rowHeaderDataLayer = rowHeaderDataLayer;\r
+ }\r
+ \r
+ public void setColumnData(int column, ColumnLayoutData data) {\r
+ layoutDatas.put(column, data);\r
+ }\r
+ \r
+ protected void setColumnWidths(Scrollable tree, int[] widths) {\r
+ for (int i=0; i < widths.length; i++) {\r
+ columnHeaderDataProvider.getDataLayer().setColumnWidthByPosition(i, widths[i]);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void handleLayerEvent(ILayerEvent event) {\r
+ if (inupdateMode)\r
+ return;\r
+ if (event instanceof ColumnResizeEvent) {\r
+ ColumnResizeEvent evt = (ColumnResizeEvent)event;\r
+ for (Range r : evt.getColumnPositionRanges()) {\r
+ int colIndex = evt.getLayer().getColumnIndexByPosition(r.start);\r
+ int w = columnHeaderDataProvider.getDataLayer().getColumnWidthByPosition(colIndex);\r
+ setColumnData(colIndex, new ColumnPixelData(w));\r
+ }\r
+ update();\r
+ } else if (event instanceof ColumnInsertEvent ||\r
+ event instanceof ColumnDeleteEvent) {\r
+ update();\r
+ } \r
+ }\r
+ \r
+ boolean updateCalled = false;\r
+ \r
+ private void update() {\r
+ if (updateCalled)\r
+ return;\r
+ updateCalled = true;\r
+ natTable.getDisplay().asyncExec(new Runnable() {\r
+ \r
+ @Override\r
+ public void run() {\r
+ if (!natTable.isDisposed()) {\r
+ natTable.update();\r
+ natTable.getParent().layout();\r
+ }\r
+ updateCalled = false;\r
+ }\r
+ });\r
+ }\r
+\r
+ \r
+ @Override\r
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {\r
+ return computeTableTreeSize(getControl(composite), wHint, hHint);\r
+ }\r
+ \r
+ Scrollable getControl(Composite composite) {\r
+ return natTable;\r
+ }\r
+ \r
+ \r
+ @Override\r
+ protected void layout(Composite composite, boolean flushCache) {\r
+ Rectangle area = composite.getClientArea();\r
+ Scrollable table = getControl(composite);\r
+ int tableWidth = table.getSize().x;\r
+ int trim = computeTrim(area, table, tableWidth);\r
+ int width = Math.max(0, area.width - trim);\r
+ if (rowHeaderDataLayer != null)\r
+ width -= rowHeaderDataLayer.getWidth();\r
+\r
+ if (width > 1)\r
+ layoutTableTree(table, width, area, tableWidth < area.width);\r
+\r
+ // For the first time we need to relayout because Scrollbars are not\r
+ // calculate appropriately\r
+ if (relayout) {\r
+ relayout = false;\r
+ composite.layout();\r
+ }\r
+ \r
+ }\r
+ \r
+ protected ColumnLayoutData getLayoutData(Scrollable tableTree,\r
+ int columnIndex) {\r
+ return layoutDatas.get(columnIndex);\r
+ }\r
+ \r
+ protected int getColumnCount(Scrollable tableTree) {\r
+ return columnHeaderDataProvider.getColumnCount();\r
+ }\r
+ \r
+ private Point computeTableTreeSize(Scrollable scrollable, int wHint,\r
+ int hHint) {\r
+ Point result = scrollable.computeSize(wHint, hHint);\r
+\r
+ int width = 0;\r
+ int size = getColumnCount(scrollable);\r
+ if (rowHeaderDataLayer != null)\r
+ width += rowHeaderDataLayer.getWidth();\r
+ for (int i = 0; i < size; ++i) {\r
+ ColumnLayoutData layoutData = getLayoutData(scrollable, i);\r
+ if (layoutData instanceof ColumnPixelData) {\r
+ ColumnPixelData col = (ColumnPixelData) layoutData;\r
+ width += col.width;\r
+ if (col.addTrim) {\r
+ width += getColumnTrim();\r
+ }\r
+ } else if (layoutData instanceof ColumnWeightData) {\r
+ ColumnWeightData col = (ColumnWeightData) layoutData;\r
+ width += col.minimumWidth;\r
+ } else {\r
+ Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$\r
+ }\r
+ }\r
+ if (width > result.x)\r
+ result.x = width;\r
+\r
+ return result;\r
+ }\r
+ \r
+ private void layoutTableTree(final Scrollable scrollable, final int width,\r
+ final Rectangle area, final boolean increase) {\r
+ final int numberOfColumns = getColumnCount(scrollable);\r
+ final int[] widths = new int[numberOfColumns];\r
+\r
+ final int[] weightColumnIndices = new int[numberOfColumns];\r
+ int numberOfWeightColumns = 0;\r
+\r
+ int fixedWidth = 0;\r
+ int totalWeight = 0;\r
+\r
+ // First calc space occupied by fixed columns\r
+ for (int i = 0; i < numberOfColumns; i++) {\r
+ ColumnLayoutData col = getLayoutData(scrollable, i);\r
+ if (col instanceof ColumnPixelData) {\r
+ ColumnPixelData cpd = (ColumnPixelData) col;\r
+ int pixels = cpd.width;\r
+ if (cpd.addTrim) {\r
+ pixels += getColumnTrim();\r
+ }\r
+ widths[i] = pixels;\r
+ fixedWidth += pixels;\r
+ } else if (col instanceof ColumnWeightData) {\r
+ ColumnWeightData cw = (ColumnWeightData) col;\r
+ weightColumnIndices[numberOfWeightColumns] = i;\r
+ numberOfWeightColumns++;\r
+ totalWeight += cw.weight;\r
+ } else {\r
+ Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$\r
+ }\r
+ }\r
+\r
+ boolean recalculate;\r
+ do {\r
+ recalculate = false;\r
+ for (int i = 0; i < numberOfWeightColumns; i++) {\r
+ int colIndex = weightColumnIndices[i];\r
+ ColumnWeightData cw = (ColumnWeightData) getLayoutData(\r
+ scrollable, colIndex);\r
+ final int minWidth = cw.minimumWidth;\r
+ final int allowedWidth = totalWeight == 0 ? 0\r
+ : (width - fixedWidth) * cw.weight / totalWeight;\r
+ if (allowedWidth < minWidth) {\r
+ /*\r
+ * if the width assigned by weight is less than the minimum,\r
+ * then treat this column as fixed, remove it from weight\r
+ * calculations, and recalculate other weights.\r
+ */\r
+ numberOfWeightColumns--;\r
+ totalWeight -= cw.weight;\r
+ fixedWidth += minWidth;\r
+ widths[colIndex] = minWidth;\r
+ System.arraycopy(weightColumnIndices, i + 1,\r
+ weightColumnIndices, i, numberOfWeightColumns - i);\r
+ recalculate = true;\r
+ break;\r
+ }\r
+ widths[colIndex] = allowedWidth;\r
+ }\r
+ } while (recalculate);\r
+\r
+ if (increase) {\r
+ scrollable.setSize(area.width, area.height);\r
+ }\r
+\r
+ inupdateMode = true;\r
+ setColumnWidths(scrollable, widths);\r
+ scrollable.update();\r
+ inupdateMode = false;\r
+\r
+ if (!increase) {\r
+ scrollable.setSize(area.width, area.height);\r
+ }\r
+ }\r
+ \r
+ private int computeTrim(Rectangle area, Scrollable scrollable,\r
+ int currentWidth) {\r
+ int trim;\r
+\r
+ if (currentWidth > 1) {\r
+ trim = currentWidth - scrollable.getClientArea().width;\r
+ } else {\r
+ // initially, the table has no extend and no client area - use the\r
+ // border with\r
+ // plus some padding as educated guess\r
+ trim = 2 * scrollable.getBorderWidth() + 1;\r
+ }\r
+\r
+ return trim;\r
+ }\r
+ \r
+ protected int getColumnTrim() {\r
+ return COLUMN_TRIM;\r
+ }\r
+ \r
+ \r
+ \r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Deque;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.WeakHashMap;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.ScheduledExecutorService;\r
+import java.util.concurrent.Semaphore;\r
+import java.util.concurrent.TimeUnit;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.util.concurrent.atomic.AtomicReference;\r
+import java.util.function.Consumer;\r
+\r
+import org.eclipse.core.runtime.Assert;\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.MultiStatus;\r
+import org.eclipse.core.runtime.Platform;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.core.runtime.jobs.Job;\r
+import org.eclipse.jface.layout.GridDataFactory;\r
+import org.eclipse.jface.layout.TreeColumnLayout;\r
+import org.eclipse.jface.resource.ColorDescriptor;\r
+import org.eclipse.jface.resource.DeviceResourceException;\r
+import org.eclipse.jface.resource.DeviceResourceManager;\r
+import org.eclipse.jface.resource.FontDescriptor;\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.viewers.ColumnWeightData;\r
+import org.eclipse.jface.viewers.ICellEditorValidator;\r
+import org.eclipse.jface.viewers.IPostSelectionProvider;\r
+import org.eclipse.jface.viewers.ISelection;\r
+import org.eclipse.jface.viewers.ISelectionChangedListener;\r
+import org.eclipse.jface.viewers.ISelectionProvider;\r
+import org.eclipse.jface.viewers.SelectionChangedEvent;\r
+import org.eclipse.jface.viewers.StructuredSelection;\r
+import org.eclipse.jface.window.Window;\r
+import org.eclipse.nebula.widgets.nattable.NatTable;\r
+import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;\r
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;\r
+import org.eclipse.nebula.widgets.nattable.config.IEditableRule;\r
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;\r
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;\r
+import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;\r
+import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDisplayConverter;\r
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;\r
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigHelper;\r
+import org.eclipse.nebula.widgets.nattable.edit.ICellEditHandler;\r
+import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditBindings;\r
+import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.edit.editor.AbstractCellEditor;\r
+import org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor;\r
+import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;\r
+import org.eclipse.nebula.widgets.nattable.edit.editor.IEditErrorHandler;\r
+import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;\r
+import org.eclipse.nebula.widgets.nattable.edit.gui.AbstractDialogCellEditor;\r
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;\r
+import org.eclipse.nebula.widgets.nattable.grid.cell.AlternatingRowConfigLabelAccumulator;\r
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;\r
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;\r
+import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;\r
+import org.eclipse.nebula.widgets.nattable.layer.LabelStack;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;\r
+import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;\r
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;\r
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;\r
+import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;\r
+import org.eclipse.nebula.widgets.nattable.ui.menu.AbstractHeaderMenuConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;\r
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;\r
+import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.DisposeEvent;\r
+import org.eclipse.swt.events.DisposeListener;\r
+import org.eclipse.swt.events.FocusEvent;\r
+import org.eclipse.swt.events.FocusListener;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.KeyListener;\r
+import org.eclipse.swt.events.MouseEvent;\r
+import org.eclipse.swt.events.MouseListener;\r
+import org.eclipse.swt.events.SelectionListener;\r
+import org.eclipse.swt.graphics.Color;\r
+import org.eclipse.swt.graphics.GC;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.graphics.RGB;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Control;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.eclipse.swt.widgets.Listener;\r
+import org.eclipse.swt.widgets.ScrollBar;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.eclipse.ui.contexts.IContextActivation;\r
+import org.eclipse.ui.contexts.IContextService;\r
+import org.eclipse.ui.services.IServiceLocator;\r
+import org.eclipse.ui.swt.IFocusService;\r
+import org.simantics.browsing.ui.BuiltinKeys;\r
+import org.simantics.browsing.ui.Column;\r
+import org.simantics.browsing.ui.Column.Align;\r
+import org.simantics.browsing.ui.DataSource;\r
+import org.simantics.browsing.ui.ExplorerState;\r
+import org.simantics.browsing.ui.GraphExplorer;\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.browsing.ui.NodeContext.CacheKey;\r
+import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey;\r
+import org.simantics.browsing.ui.NodeContext.QueryKey;\r
+import org.simantics.browsing.ui.NodeQueryManager;\r
+import org.simantics.browsing.ui.NodeQueryProcessor;\r
+import org.simantics.browsing.ui.PrimitiveQueryProcessor;\r
+import org.simantics.browsing.ui.PrimitiveQueryUpdater;\r
+import org.simantics.browsing.ui.SelectionDataResolver;\r
+import org.simantics.browsing.ui.SelectionFilter;\r
+import org.simantics.browsing.ui.StatePersistor;\r
+import org.simantics.browsing.ui.common.ColumnKeys;\r
+import org.simantics.browsing.ui.common.ErrorLogger;\r
+import org.simantics.browsing.ui.common.NodeContextBuilder;\r
+import org.simantics.browsing.ui.common.NodeContextUtil;\r
+import org.simantics.browsing.ui.common.internal.GENodeQueryManager;\r
+import org.simantics.browsing.ui.common.internal.IGECache;\r
+import org.simantics.browsing.ui.common.internal.IGraphExplorerContext;\r
+import org.simantics.browsing.ui.common.internal.UIElementReference;\r
+import org.simantics.browsing.ui.common.processors.AbstractPrimitiveQueryProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultCheckedStateProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultComparableChildrenProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultFinalChildrenProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultImageDecoratorProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultImagerFactoriesProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultImagerProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultLabelDecoratorProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultLabelerFactoriesProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultLabelerProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultPrunedChildrenProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultSelectedImageDecoratorFactoriesProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultSelectedLabelDecoratorFactoriesProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultSelectedLabelerProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultSelectedViewpointFactoryProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultSelectedViewpointProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultViewpointContributionProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultViewpointContributionsProcessor;\r
+import org.simantics.browsing.ui.common.processors.DefaultViewpointProcessor;\r
+import org.simantics.browsing.ui.common.processors.IsExpandedProcessor;\r
+import org.simantics.browsing.ui.common.processors.NoSelectionRequestProcessor;\r
+import org.simantics.browsing.ui.common.processors.ProcessorLifecycle;\r
+import org.simantics.browsing.ui.content.Labeler;\r
+import org.simantics.browsing.ui.content.Labeler.CustomModifier;\r
+import org.simantics.browsing.ui.content.Labeler.DialogModifier;\r
+import org.simantics.browsing.ui.content.Labeler.EnumerationModifier;\r
+import org.simantics.browsing.ui.content.Labeler.Modifier;\r
+import org.simantics.browsing.ui.nattable.override.DefaultTreeLayerConfiguration2;\r
+import org.simantics.browsing.ui.swt.Activator;\r
+import org.simantics.browsing.ui.swt.AdaptableHintContext;\r
+import org.simantics.browsing.ui.swt.DefaultImageDecoratorsProcessor;\r
+import org.simantics.browsing.ui.swt.DefaultIsExpandedProcessor;\r
+import org.simantics.browsing.ui.swt.DefaultLabelDecoratorsProcessor;\r
+import org.simantics.browsing.ui.swt.DefaultSelectedImagerProcessor;\r
+import org.simantics.browsing.ui.swt.DefaultShowMaxChildrenProcessor;\r
+import org.simantics.browsing.ui.swt.GraphExplorerImplBase;\r
+import org.simantics.browsing.ui.swt.ImageLoaderJob;\r
+import org.simantics.browsing.ui.swt.ViewerCellReference;\r
+import org.simantics.browsing.ui.swt.ViewerRowReference;\r
+import org.simantics.browsing.ui.swt.internal.Threads;\r
+import org.simantics.db.layer0.SelectionHints;\r
+import org.simantics.utils.datastructures.BinaryFunction;\r
+import org.simantics.utils.datastructures.MapList;\r
+import org.simantics.utils.datastructures.disposable.AbstractDisposable;\r
+import org.simantics.utils.datastructures.hints.IHintContext;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.SWTThread;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+import org.simantics.utils.ui.AdaptionUtils;\r
+import org.simantics.utils.ui.ISelectionUtils;\r
+import org.simantics.utils.ui.jface.BasePostSelectionProvider;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+/**\r
+ * NatTable base GraphExplorer\r
+ * \r
+ * \r
+ * FIXME : asynchronous node loading does not work properly + check expanded/collapsed sate handling\r
+ * TODO: InputValidators + input errors\r
+ * TODO: ability to hide headers\r
+ * TODO: code cleanup (copied from GraphExplorerImpl2) \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class NatTableGraphExplorer extends GraphExplorerImplBase implements GraphExplorer{\r
+ public static final int DEFAULT_MAX_CHILDREN = 1000;\r
+ private static final boolean DEBUG_SELECTION_LISTENERS = false;\r
+ private static final boolean DEBUG = false;\r
+ \r
+ private Composite composite;\r
+ private NatTable natTable;\r
+ \r
+ private GETreeLayer treeLayer;\r
+ private DataLayer dataLayer;\r
+ private ViewportLayer viewportLayer;\r
+ private SelectionLayer selectionLayer;\r
+ private GEColumnHeaderDataProvider columnHeaderDataProvider;\r
+ private GEColumnAccessor columnAccessor;\r
+ private DefaultRowHeaderDataLayer rowHeaderDataLayer;\r
+ private DataLayer columnHeaderDataLayer;\r
+ private DataLayer cornerDataLayer;\r
+ \r
+ private List<TreeNode> list = new ArrayList<>();\r
+ \r
+ private NatTableSelectionAdaptor selectionAdaptor;\r
+ private NatTableColumnLayout layout;\r
+ \r
+ LocalResourceManager localResourceManager;\r
+ DeviceResourceManager resourceManager;\r
+ \r
+ \r
+ private IThreadWorkQueue thread;\r
+ \r
+ @SuppressWarnings({ "rawtypes" })\r
+ final HashMap<CacheKey<?>, NodeQueryProcessor> processors = new HashMap<CacheKey<?>, NodeQueryProcessor>();\r
+ @SuppressWarnings({ "rawtypes" })\r
+ final HashMap<Object, PrimitiveQueryProcessor> primitiveProcessors = new HashMap<Object, PrimitiveQueryProcessor>();\r
+ @SuppressWarnings({ "rawtypes" })\r
+ final HashMap<Class, DataSource> dataSources = new HashMap<Class, DataSource>();\r
+\r
+ FontDescriptor originalFont;\r
+ protected ColorDescriptor originalForeground;\r
+ protected ColorDescriptor originalBackground;\r
+ private Color invalidModificationColor;\r
+ \r
+ private Column[] columns;\r
+ private Map<String,Integer> columnKeyToIndex;\r
+ private boolean columnsAreVisible = true;\r
+ \r
+ private NodeContext rootContext;\r
+ private TreeNode rootNode;\r
+ private StatePersistor persistor = null;\r
+\r
+ private boolean editable = true;\r
+ \r
+ private boolean disposed = false;\r
+ \r
+ private final CopyOnWriteArrayList<FocusListener> focusListeners = new CopyOnWriteArrayList<FocusListener>();\r
+ private final CopyOnWriteArrayList<MouseListener> mouseListeners = new CopyOnWriteArrayList<MouseListener>();\r
+ private final CopyOnWriteArrayList<KeyListener> keyListeners = new CopyOnWriteArrayList<KeyListener>();\r
+ \r
+ private int autoExpandLevel = 0;\r
+ private IServiceLocator serviceLocator;\r
+ private IContextService contextService = null;\r
+ private IFocusService focusService = null;\r
+ private IContextActivation editingContext = null;\r
+ \r
+ GeViewerContext explorerContext = new GeViewerContext(this);\r
+ \r
+ private GraphExplorerPostSelectionProvider postSelectionProvider = new GraphExplorerPostSelectionProvider(this);\r
+ private BasePostSelectionProvider selectionProvider = new BasePostSelectionProvider();\r
+ private SelectionDataResolver selectionDataResolver;\r
+ private SelectionFilter selectionFilter;\r
+ \r
+ private MapList<NodeContext, TreeNode> contextToNodeMap;\r
+ \r
+ private ModificationContext modificationContext = null;\r
+ \r
+ private boolean filterSelectionEdit = true;\r
+ \r
+ private boolean expand;\r
+ private boolean verticalBarVisible = false;\r
+ \r
+ private BinaryFunction<Object[], GraphExplorer, Object[]> selectionTransformation = new BinaryFunction<Object[], GraphExplorer, Object[]>() {\r
+\r
+ @Override\r
+ public Object[] call(GraphExplorer explorer, Object[] objects) {\r
+ Object[] result = new Object[objects.length];\r
+ for (int i = 0; i < objects.length; i++) {\r
+ IHintContext context = new AdaptableHintContext(SelectionHints.KEY_MAIN);\r
+ context.setHint(SelectionHints.KEY_MAIN, objects[i]);\r
+ result[i] = context;\r
+ }\r
+ return result;\r
+ }\r
+\r
+ };\r
+ \r
+ static class TransientStateImpl implements TransientExplorerState {\r
+\r
+ private Integer activeColumn = null;\r
+ \r
+ @Override\r
+ public synchronized Integer getActiveColumn() {\r
+ return activeColumn;\r
+ }\r
+ \r
+ public synchronized void setActiveColumn(Integer column) {\r
+ activeColumn = column;\r
+ }\r
+ \r
+ }\r
+ \r
+ private TransientStateImpl transientState = new TransientStateImpl();\r
+ \r
+ public NatTableGraphExplorer(Composite parent) {\r
+ this(parent, SWT.BORDER | SWT.MULTI );\r
+ }\r
+ \r
+ public NatTableGraphExplorer(Composite parent, int style) {\r
+ this.composite = parent;\r
+ \r
+ \r
+ this.localResourceManager = new LocalResourceManager(JFaceResources.getResources());\r
+ this.resourceManager = new DeviceResourceManager(parent.getDisplay());\r
+\r
+ this.imageLoaderJob = new ImageLoaderJob(this);\r
+ this.imageLoaderJob.setPriority(Job.DECORATE);\r
+ contextToNodeMap = new MapList<NodeContext, TreeNode>();\r
+ \r
+ invalidModificationColor = (Color) localResourceManager.get(ColorDescriptor.createFrom(new RGB(255, 128, 128)));\r
+\r
+ this.thread = SWTThread.getThreadAccess(parent);\r
+\r
+ for (int i = 0; i < 10; i++)\r
+ explorerContext.activity.push(0);\r
+ \r
+ originalFont = JFaceResources.getDefaultFontDescriptor();\r
+\r
+ columns = new Column[0];\r
+ createNatTable();\r
+ layout = new NatTableColumnLayout(natTable, columnHeaderDataProvider, rowHeaderDataLayer);\r
+ this.composite.setLayout(layout);\r
+ \r
+ setBasicListeners();\r
+ setDefaultProcessors();\r
+ \r
+ natTable.addDisposeListener(new DisposeListener() {\r
+ \r
+ @Override\r
+ public void widgetDisposed(DisposeEvent e) {\r
+ doDispose();\r
+ \r
+ }\r
+ });\r
+ \r
+ Listener listener = new Listener() {\r
+ \r
+ @Override\r
+ public void handleEvent(Event event) {\r
+ \r
+ switch (event.type) {\r
+ case SWT.Activate:\r
+ case SWT.Show:\r
+ case SWT.Paint:\r
+ {\r
+ visible = true;\r
+ activate();\r
+ break;\r
+ }\r
+ case SWT.Deactivate:\r
+ case SWT.Hide:\r
+ visible = false;\r
+ }\r
+ }\r
+ };\r
+ \r
+ natTable.addListener(SWT.Activate, listener);\r
+ natTable.addListener(SWT.Deactivate, listener);\r
+ natTable.addListener(SWT.Show, listener);\r
+ natTable.addListener(SWT.Hide, listener);\r
+ natTable.addListener(SWT.Paint,listener);\r
+ \r
+ setColumns( new Column[] { new Column(ColumnKeys.SINGLE) });\r
+ \r
+ }\r
+ \r
+ private long focusGainedAt = 0L;\r
+ private boolean visible = false;\r
+ private Collection<TreeNode> selectedNodes = new ArrayList<TreeNode>();\r
+ \r
+ protected void setBasicListeners() {\r
+ \r
+ natTable.addFocusListener(new FocusListener() {\r
+ @Override\r
+ public void focusGained(FocusEvent e) {\r
+ focusGainedAt = ((long) e.time) & 0xFFFFFFFFL;\r
+ for (FocusListener listener : focusListeners)\r
+ listener.focusGained(e);\r
+ }\r
+ @Override\r
+ public void focusLost(FocusEvent e) {\r
+ for (FocusListener listener : focusListeners)\r
+ listener.focusLost(e);\r
+ }\r
+ });\r
+ natTable.addMouseListener(new MouseListener() {\r
+ @Override\r
+ public void mouseDoubleClick(MouseEvent e) {\r
+ for (MouseListener listener : mouseListeners) {\r
+ listener.mouseDoubleClick(e);\r
+ }\r
+ }\r
+ @Override\r
+ public void mouseDown(MouseEvent e) {\r
+ for (MouseListener listener : mouseListeners) {\r
+ listener.mouseDown(e);\r
+ }\r
+ }\r
+ @Override\r
+ public void mouseUp(MouseEvent e) {\r
+ for (MouseListener listener : mouseListeners) {\r
+ listener.mouseUp(e);\r
+ }\r
+ }\r
+ });\r
+ natTable.addKeyListener(new KeyListener() {\r
+ @Override\r
+ public void keyPressed(KeyEvent e) {\r
+ for (KeyListener listener : keyListeners) {\r
+ listener.keyPressed(e);\r
+ }\r
+ }\r
+ @Override\r
+ public void keyReleased(KeyEvent e) {\r
+ for (KeyListener listener : keyListeners) {\r
+ listener.keyReleased(e);\r
+ }\r
+ }\r
+ });\r
+ \r
+ selectionAdaptor.addSelectionChangedListener(new ISelectionChangedListener() {\r
+ \r
+ @Override\r
+ public void selectionChanged(SelectionChangedEvent event) {\r
+ //System.out.println("GraphExplorerImpl2.fireSelection");\r
+ selectedNodes = AdaptionUtils.adaptToCollection(event.getSelection(), TreeNode.class);\r
+ Collection<NodeContext> selectedContexts = AdaptionUtils.adaptToCollection(event.getSelection(), NodeContext.class);\r
+ selectionProvider.setAndFireSelection(constructSelection(selectedContexts.toArray(new NodeContext[selectedContexts.size()])));\r
+ }\r
+ });\r
+ \r
+ selectionAdaptor.addPostSelectionChangedListener(new ISelectionChangedListener() {\r
+ \r
+ @Override\r
+ public void selectionChanged(SelectionChangedEvent event) {\r
+ //System.out.println("GraphExplorerImpl2.firePostSelection");\r
+ Collection<NodeContext> selectedContexts = AdaptionUtils.adaptToCollection(event.getSelection(), NodeContext.class);\r
+ selectionProvider.firePostSelection(constructSelection(selectedContexts.toArray(new NodeContext[selectedContexts.size()])));\r
+ \r
+ }\r
+ });\r
+\r
+ }\r
+ \r
+ private NodeContext pendingRoot;\r
+ \r
+ private void activate() {\r
+ if (pendingRoot != null && !expand) {\r
+ doSetRoot(pendingRoot);\r
+ pendingRoot = null;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Invoke only from SWT thread to reset the root of the graph explorer tree.\r
+ * \r
+ * @param root\r
+ */\r
+ private void doSetRoot(NodeContext root) {\r
+ Display display = composite.getDisplay();\r
+ if (display.getThread() != Thread.currentThread()) {\r
+ throw new RuntimeException("Invoke from SWT thread only");\r
+ }\r
+// System.out.println("doSetRoot " + root);\r
+ if (isDisposed())\r
+ return;\r
+ if (natTable.isDisposed())\r
+ return;\r
+ if (root.getConstant(BuiltinKeys.INPUT) == null) {\r
+ ErrorLogger.defaultLogError("root node context does not contain BuiltinKeys.INPUT key. Node = " + root, new Exception("trace"));\r
+ return;\r
+ }\r
+ \r
+ \r
+\r
+ // Empty caches, release queries.\r
+ if (rootNode != null) {\r
+ rootNode.dispose();\r
+ } \r
+ GeViewerContext oldContext = explorerContext;\r
+ GeViewerContext newContext = new GeViewerContext(this);\r
+ this.explorerContext = newContext;\r
+ oldContext.safeDispose();\r
+\r
+ // Need to empty these or otherwise they won't be emptied until the\r
+ // explorer is disposed which would mean that many unwanted references\r
+ // will be held by this map.\r
+ clearPrimitiveProcessors();\r
+\r
+ this.rootContext = root.getConstant(BuiltinKeys.IS_ROOT) != null ? root\r
+ : NodeContextUtil.withConstant(root, BuiltinKeys.IS_ROOT, Boolean.TRUE);\r
+\r
+ explorerContext.getCache().incRef(this.rootContext);\r
+\r
+ initializeState();\r
+ \r
+ \r
+ select(rootContext);\r
+ //refreshColumnSizes();\r
+ rootNode = new TreeNode(rootContext, explorerContext);\r
+ if (DEBUG) System.out.println("setRoot " + rootNode);\r
+ \r
+ // viewer.setInput(rootNode);\r
+ \r
+ // Delay content reading.\r
+ \r
+ // This is required for cases when GEImpl2 is attached to selection view. Reading content\r
+ // instantly could stagnate SWT thread under rapid changes in selection. By delaying the \r
+ // content reading we give the system a change to dispose the GEImpl2 before the content is read.\r
+ display.asyncExec(new Runnable() {\r
+ \r
+ @Override\r
+ public void run() {\r
+ if (rootNode != null) {\r
+ rootNode.updateChildren();\r
+ listReIndex();\r
+ natTable.refresh(true);\r
+ }\r
+ }\r
+ });\r
+ \r
+ }\r
+ \r
+ private synchronized void listReIndex() {\r
+ list.clear();\r
+ for (TreeNode c : rootNode.getChildren())\r
+ _insertToList(c);\r
+ }\r
+ \r
+ private void _insertToList(TreeNode n) {\r
+ n.setListIndex(list.size());\r
+ list.add(n);\r
+ for (TreeNode c : n.getChildren()) {\r
+ _insertToList(c);\r
+ }\r
+ }\r
+ \r
+ private void initializeState() {\r
+ if (persistor == null)\r
+ return;\r
+\r
+ ExplorerState state = persistor.deserialize(\r
+ Platform.getStateLocation(Activator.getDefault().getBundle()).toFile(),\r
+ getRoot());\r
+\r
+\r
+ Object processor = getPrimitiveProcessor(BuiltinKeys.IS_EXPANDED);\r
+ if (processor instanceof DefaultIsExpandedProcessor) {\r
+ DefaultIsExpandedProcessor isExpandedProcessor = (DefaultIsExpandedProcessor)processor;\r
+ for(NodeContext expanded : state.expandedNodes) {\r
+ isExpandedProcessor.setExpanded(expanded, true);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public NodeContext getRoot() {\r
+ return rootContext;\r
+ }\r
+ \r
+ @Override\r
+ public IThreadWorkQueue getThread() {\r
+ return thread;\r
+ }\r
+\r
+ @Override\r
+ public NodeContext getParentContext(NodeContext context) {\r
+ if (disposed)\r
+ throw new IllegalStateException("disposed");\r
+ if (!thread.currentThreadAccess())\r
+ throw new IllegalStateException("not in SWT display thread " + thread.getThread());\r
+\r
+ List<TreeNode> nodes = contextToNodeMap.getValuesUnsafe(context);\r
+ for (int i = 0; i < nodes.size(); i++) {\r
+ if (nodes.get(i).getParent() != null)\r
+ return nodes.get(i).getParent().getContext();\r
+ }\r
+ return null;\r
+ \r
+ }\r
+ \r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getAdapter(Class<T> adapter) {\r
+ if(ISelectionProvider.class == adapter) return (T) postSelectionProvider;\r
+ else if(IPostSelectionProvider.class == adapter) return (T) postSelectionProvider;\r
+ return null;\r
+ }\r
+\r
+ \r
+ protected void setDefaultProcessors() {\r
+ // Add a simple IMAGER query processor that always returns null.\r
+ // With this processor no images will ever be shown.\r
+ // setPrimitiveProcessor(new StaticImagerProcessor(null));\r
+\r
+ setProcessor(new DefaultComparableChildrenProcessor());\r
+ setProcessor(new DefaultLabelDecoratorsProcessor());\r
+ setProcessor(new DefaultImageDecoratorsProcessor());\r
+ setProcessor(new DefaultSelectedLabelerProcessor());\r
+ setProcessor(new DefaultLabelerFactoriesProcessor());\r
+ setProcessor(new DefaultSelectedImagerProcessor());\r
+ setProcessor(new DefaultImagerFactoriesProcessor());\r
+ setPrimitiveProcessor(new DefaultLabelerProcessor());\r
+ setPrimitiveProcessor(new DefaultCheckedStateProcessor());\r
+ setPrimitiveProcessor(new DefaultImagerProcessor());\r
+ setPrimitiveProcessor(new DefaultLabelDecoratorProcessor());\r
+ setPrimitiveProcessor(new DefaultImageDecoratorProcessor());\r
+ setPrimitiveProcessor(new NoSelectionRequestProcessor());\r
+\r
+ setProcessor(new DefaultFinalChildrenProcessor(this));\r
+\r
+ setProcessor(new DefaultPrunedChildrenProcessor());\r
+ setProcessor(new DefaultSelectedViewpointProcessor());\r
+ setProcessor(new DefaultSelectedLabelDecoratorFactoriesProcessor());\r
+ setProcessor(new DefaultSelectedImageDecoratorFactoriesProcessor());\r
+ setProcessor(new DefaultViewpointContributionsProcessor());\r
+\r
+ setPrimitiveProcessor(new DefaultViewpointProcessor());\r
+ setPrimitiveProcessor(new DefaultViewpointContributionProcessor());\r
+ setPrimitiveProcessor(new DefaultSelectedViewpointFactoryProcessor());\r
+ setPrimitiveProcessor(new TreeNodeIsExpandedProcessor());\r
+ setPrimitiveProcessor(new DefaultShowMaxChildrenProcessor());\r
+ }\r
+ \r
+ @Override\r
+ public Column[] getColumns() {\r
+ return Arrays.copyOf(columns, columns.length);\r
+ }\r
+ \r
+ @Override\r
+ public void setColumnsVisible(boolean visible) {\r
+ columnsAreVisible = visible;\r
+ //FIXME if(natTable != null) this.columnHeaderDataLayer.setHeaderVisible(columnsAreVisible);\r
+ }\r
+\r
+ @Override\r
+ public void setColumns(final Column[] columns) {\r
+ setColumns(columns, null);\r
+ }\r
+\r
+ @Override\r
+ public void setColumns(final Column[] columns, Consumer<Map<Column, Object>> callback) {\r
+ assertNotDisposed();\r
+ checkUniqueColumnKeys(columns);\r
+\r
+ Display d = composite.getDisplay();\r
+ if (d.getThread() == Thread.currentThread()) {\r
+ doSetColumns(columns, callback);\r
+ natTable.refresh(true);\r
+ }else\r
+ d.asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (natTable == null)\r
+ return;\r
+ if (natTable.isDisposed())\r
+ return;\r
+ doSetColumns(columns, callback);\r
+ natTable.refresh(true);\r
+ natTable.getParent().layout();\r
+ }\r
+ });\r
+ }\r
+ \r
+ private void checkUniqueColumnKeys(Column[] cols) {\r
+ Set<String> usedColumnKeys = new HashSet<String>();\r
+ List<Column> duplicateColumns = new ArrayList<Column>();\r
+ for (Column c : cols) {\r
+ if (!usedColumnKeys.add(c.getKey()))\r
+ duplicateColumns.add(c);\r
+ }\r
+ if (!duplicateColumns.isEmpty()) {\r
+ throw new IllegalArgumentException("All columns do not have unique keys: " + cols + ", overlapping: " + duplicateColumns);\r
+ }\r
+ }\r
+ \r
+ private void doSetColumns(Column[] cols, Consumer<Map<Column, Object>> callback) {\r
+\r
+ HashMap<String, Integer> keyToIndex = new HashMap<String, Integer>();\r
+ for (int i = 0; i < cols.length; ++i) {\r
+ keyToIndex.put(cols[i].getKey(), i);\r
+ }\r
+\r
+ this.columns = Arrays.copyOf(cols, cols.length);\r
+ //this.columns[cols.length] = FILLER_COLUMN;\r
+ this.columnKeyToIndex = keyToIndex;\r
+ \r
+ columnHeaderDataProvider.updateColumnSizes();\r
+\r
+ Map<Column, Object> map = new HashMap<Column, Object>();\r
+\r
+ // FIXME : temporary workaround for ModelBrowser.\r
+// natTable.setHeaderVisible(columns.length == 1 ? false : columnsAreVisible);\r
+ \r
+ int columnIndex = 0;\r
+\r
+ for (Column column : columns) {\r
+ int width = column.getWidth();\r
+ if(column.hasGrab()) {\r
+ if (width < 0)\r
+ width = 1;\r
+ layout.setColumnData(columnIndex, new ColumnWeightData(column.getWeight(), width));\r
+\r
+ } else {\r
+ if (width < 0)\r
+ width = 50;\r
+ layout.setColumnData(columnIndex, new ColumnWeightData(columns.length > 1 ? 0 : 1, width));\r
+\r
+ }\r
+ columnIndex++;\r
+ }\r
+ \r
+ \r
+\r
+ if(callback != null) callback.accept(map);\r
+ }\r
+ \r
+ int toSWT(Align alignment) {\r
+ switch (alignment) {\r
+ case LEFT: return SWT.LEFT;\r
+ case CENTER: return SWT.CENTER;\r
+ case RIGHT: return SWT.RIGHT;\r
+ default: throw new Error("unhandled alignment: " + alignment);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> void setProcessor(NodeQueryProcessor<T> processor) {\r
+ assertNotDisposed();\r
+ if (processor == null)\r
+ throw new IllegalArgumentException("null processor");\r
+\r
+ processors.put(processor.getIdentifier(), processor);\r
+ }\r
+\r
+ @Override\r
+ public <T> void setPrimitiveProcessor(PrimitiveQueryProcessor<T> processor) {\r
+ assertNotDisposed();\r
+ if (processor == null)\r
+ throw new IllegalArgumentException("null processor");\r
+\r
+ PrimitiveQueryProcessor<?> oldProcessor = primitiveProcessors.put(\r
+ processor.getIdentifier(), processor);\r
+\r
+ if (oldProcessor instanceof ProcessorLifecycle)\r
+ ((ProcessorLifecycle) oldProcessor).detached(this);\r
+ if (processor instanceof ProcessorLifecycle)\r
+ ((ProcessorLifecycle) processor).attached(this);\r
+ }\r
+\r
+ @Override\r
+ public <T> void setDataSource(DataSource<T> provider) {\r
+ assertNotDisposed();\r
+ if (provider == null)\r
+ throw new IllegalArgumentException("null provider");\r
+ dataSources.put(provider.getProvidedClass(), provider);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> DataSource<T> removeDataSource(Class<T> forProvidedClass) {\r
+ assertNotDisposed();\r
+ if (forProvidedClass == null)\r
+ throw new IllegalArgumentException("null class");\r
+ return dataSources.remove(forProvidedClass);\r
+ }\r
+\r
+ @Override\r
+ public void setPersistor(StatePersistor persistor) {\r
+ this.persistor = persistor;\r
+ }\r
+\r
+ @Override\r
+ public SelectionDataResolver getSelectionDataResolver() {\r
+ return selectionDataResolver;\r
+ }\r
+\r
+ @Override\r
+ public void setSelectionDataResolver(SelectionDataResolver r) {\r
+ this.selectionDataResolver = r;\r
+ }\r
+\r
+ @Override\r
+ public SelectionFilter getSelectionFilter() {\r
+ return selectionFilter;\r
+ }\r
+\r
+ @Override\r
+ public void setSelectionFilter(SelectionFilter f) {\r
+ this.selectionFilter = f;\r
+ // TODO: re-filter current selection?\r
+ }\r
+ \r
+ protected ISelection constructSelection(NodeContext... contexts) {\r
+ if (contexts == null)\r
+ throw new IllegalArgumentException("null contexts");\r
+ if (contexts.length == 0)\r
+ return StructuredSelection.EMPTY;\r
+ if (selectionFilter == null)\r
+ return new StructuredSelection(transformSelection(contexts));\r
+ return new StructuredSelection( transformSelection(filter(selectionFilter, contexts)) );\r
+ }\r
+ \r
+ protected Object[] transformSelection(Object[] objects) {\r
+ return selectionTransformation.call(this, objects);\r
+ }\r
+ \r
+ protected static Object[] filter(SelectionFilter filter, NodeContext[] contexts) {\r
+ int len = contexts.length;\r
+ Object[] objects = new Object[len];\r
+ for (int i = 0; i < len; ++i)\r
+ objects[i] = filter.filter(contexts[i]);\r
+ return objects;\r
+ }\r
+\r
+ @Override\r
+ public void setSelectionTransformation(\r
+ BinaryFunction<Object[], GraphExplorer, Object[]> f) {\r
+ this.selectionTransformation = f;\r
+ }\r
+ \r
+ public ISelection getWidgetSelection() {\r
+ return selectionAdaptor.getSelection();\r
+ }\r
+\r
+ @Override\r
+ public <T> void addListener(T listener) {\r
+ if (listener instanceof FocusListener) {\r
+ focusListeners.add((FocusListener) listener);\r
+ } else if (listener instanceof MouseListener) {\r
+ mouseListeners.add((MouseListener) listener);\r
+ } else if (listener instanceof KeyListener) {\r
+ keyListeners.add((KeyListener) listener);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> void removeListener(T listener) {\r
+ if (listener instanceof FocusListener) {\r
+ focusListeners.remove(listener);\r
+ } else if (listener instanceof MouseListener) {\r
+ mouseListeners.remove(listener);\r
+ } else if (listener instanceof KeyListener) {\r
+ keyListeners.remove(listener);\r
+ }\r
+ }\r
+\r
+ public void addSelectionListener(SelectionListener listener) {\r
+ selectionAdaptor.addSelectionListener(listener);\r
+ }\r
+\r
+ public void removeSelectionListener(SelectionListener listener) {\r
+ selectionAdaptor.removeSelectionListener(listener);\r
+ }\r
+\r
+ private Set<String> uiContexts;\r
+ \r
+ @Override\r
+ public void setUIContexts(Set<String> contexts) {\r
+ this.uiContexts = contexts;\r
+ }\r
+ \r
+ @Override\r
+ public void setRoot(final Object root) {\r
+ if(uiContexts != null && uiContexts.size() == 1)\r
+ setRootContext0(NodeContextBuilder.buildWithData(BuiltinKeys.INPUT, root, BuiltinKeys.UI_CONTEXT, uiContexts.iterator().next()));\r
+ else\r
+ setRootContext0(NodeContextBuilder.buildWithData(BuiltinKeys.INPUT, root));\r
+ }\r
+\r
+ @Override\r
+ public void setRootContext(final NodeContext context) {\r
+ setRootContext0(context);\r
+ }\r
+ \r
+ private void setRoot(NodeContext context) {\r
+ if (!visible) {\r
+ pendingRoot = context;\r
+ Display.getDefault().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (natTable!= null && !natTable.isDisposed())\r
+ natTable.redraw();\r
+ }\r
+ });\r
+ return;\r
+ }\r
+ doSetRoot(context);\r
+ }\r
+\r
+ private void setRootContext0(final NodeContext context) {\r
+ Assert.isNotNull(context, "root must not be null");\r
+ if (isDisposed() || natTable.isDisposed())\r
+ return;\r
+ Display display = natTable.getDisplay();\r
+ if (display.getThread() == Thread.currentThread()) {\r
+ setRoot(context);\r
+ } else {\r
+ display.asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ setRoot(context);\r
+ }\r
+ });\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void setFocus() {\r
+ natTable.setFocus();\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getControl() {\r
+ return (T)natTable;\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public boolean isDisposed() {\r
+ return disposed;\r
+ }\r
+\r
+ protected void assertNotDisposed() {\r
+ if (isDisposed())\r
+ throw new IllegalStateException("disposed");\r
+ }\r
+ \r
+ @Override\r
+ public boolean isEditable() {\r
+ return editable;\r
+ }\r
+\r
+ @Override\r
+ public void setEditable(boolean editable) {\r
+ if (!thread.currentThreadAccess())\r
+ throw new IllegalStateException("not in SWT display thread " + thread.getThread());\r
+\r
+ this.editable = editable;\r
+ Display display = natTable.getDisplay();\r
+ natTable.setBackground(editable ? null : display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));\r
+ }\r
+ \r
+ private void doDispose() {\r
+ if (disposed)\r
+ return;\r
+ disposed = true;\r
+ // TODO: Since GENodeQueryManager is cached in QueryChache and it refers to this class\r
+ // we have to remove all references here to reduce memory consumption.\r
+ // \r
+ // Proper fix would be to remove references between QueryCache and GENodeQueryManagers.\r
+ if (rootNode != null) {\r
+ rootNode.dispose();\r
+ rootNode = null; \r
+ } \r
+ explorerContext.dispose();\r
+ explorerContext = null;\r
+ processors.clear();\r
+ detachPrimitiveProcessors();\r
+ primitiveProcessors.clear();\r
+ dataSources.clear(); \r
+ pendingItems.clear();\r
+ rootContext = null;\r
+ mouseListeners.clear();\r
+ selectionProvider.clearListeners();\r
+ selectionProvider = null;\r
+ selectionDataResolver = null;\r
+ selectedNodes.clear();\r
+ selectedNodes = null;\r
+ selectionTransformation = null;\r
+ originalFont = null;\r
+ localResourceManager.dispose();\r
+ localResourceManager = null;\r
+ // Must shutdown image loader job before disposing its ResourceManager\r
+ imageLoaderJob.dispose();\r
+ imageLoaderJob.cancel();\r
+ try {\r
+ imageLoaderJob.join();\r
+ imageLoaderJob = null;\r
+ } catch (InterruptedException e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ }\r
+ resourceManager.dispose();\r
+ resourceManager = null;\r
+ \r
+ contextToNodeMap.clear(); // should be empty at this point.\r
+ contextToNodeMap = null;\r
+ if (postSelectionProvider != null) {\r
+ postSelectionProvider.dispose();\r
+ postSelectionProvider = null;\r
+ }\r
+ imageTasks = null;\r
+ modificationContext = null;\r
+ focusService = null;\r
+ contextService = null;\r
+ serviceLocator = null;\r
+ columns = null;\r
+ columnKeyToIndex.clear();\r
+ columnKeyToIndex = null;\r
+// if (natTable != null) {\r
+// natTable.dispose();\r
+// natTable = null;\r
+// }\r
+ treeLayer = null;\r
+ dataLayer = null;\r
+ viewportLayer = null;\r
+ selectionLayer = null;\r
+ columnHeaderDataProvider = null;\r
+ columnAccessor = null;\r
+ rowHeaderDataLayer = null;\r
+ columnHeaderDataLayer = null;\r
+ cornerDataLayer = null;\r
+\r
+ }\r
+ \r
+ @Override\r
+ public boolean select(NodeContext context) {\r
+\r
+ assertNotDisposed();\r
+\r
+ if (context == null || context.equals(rootContext) || contextToNodeMap.getValuesUnsafe(context).size() == 0) {\r
+ StructuredSelection s = new StructuredSelection();\r
+ selectionAdaptor.setSelection(s);\r
+ selectionProvider.setAndFireNonEqualSelection(s);\r
+ return true;\r
+ }\r
+\r
+ selectionAdaptor.setSelection(new StructuredSelection(contextToNodeMap.getValuesUnsafe(context).get(0)));\r
+ \r
+ return false;\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public boolean selectPath(Collection<NodeContext> contexts) {\r
+ \r
+ if(contexts == null) throw new IllegalArgumentException("Null list is not allowed");\r
+ if(contexts.isEmpty()) throw new IllegalArgumentException("Empty list is not allowed");\r
+ \r
+ return selectPathInternal(contexts.toArray(new NodeContext[contexts.size()]), 0);\r
+ \r
+ }\r
+ \r
+ private boolean selectPathInternal(NodeContext[] contexts, int position) {\r
+\r
+ NodeContext head = contexts[position];\r
+\r
+ if(position == contexts.length-1) {\r
+ return select(head);\r
+ \r
+ }\r
+\r
+ setExpanded(head, true);\r
+ if(!waitVisible(contexts[position+1])) return false;\r
+ \r
+ return selectPathInternal(contexts, position+1);\r
+ \r
+ }\r
+ \r
+ private boolean waitVisible(NodeContext context) {\r
+ long start = System.nanoTime();\r
+ while(!isVisible(context)) {\r
+ Display.getCurrent().readAndDispatch();\r
+ long duration = System.nanoTime() - start;\r
+ if(duration > 10e9) return false;\r
+ }\r
+ return true;\r
+ }\r
+ \r
+ @Override\r
+ public boolean isVisible(NodeContext context) {\r
+ if (contextToNodeMap.getValuesUnsafe(context).size() == 0)\r
+ return false;\r
+ \r
+ return true; //FIXME\r
+// Object elements[] = viewer.getVisibleExpandedElements();\r
+// return org.simantics.utils.datastructures.Arrays.contains(elements, contextToNodeMap.getValuesUnsafe(context).get(0));\r
+ \r
+ \r
+ }\r
+ \r
+ @Override\r
+ public TransientExplorerState getTransientState() {\r
+ if (!thread.currentThreadAccess())\r
+ throw new AssertionError(getClass().getSimpleName() + ".getActiveColumn called from non SWT-thread: " + Thread.currentThread());\r
+ return transientState;\r
+ }\r
+ \r
+ @Override\r
+ public <T> T query(NodeContext context, CacheKey<T> key) {\r
+ return this.explorerContext.cache.get(context, key);\r
+ }\r
+ \r
+ /**\r
+ * For setting a more local service locator for the explorer than the global\r
+ * workbench service locator. Sometimes required to give this implementation\r
+ * access to local workbench services like IFocusService.\r
+ * \r
+ * <p>\r
+ * Must be invoked during right after construction.\r
+ * \r
+ * @param serviceLocator\r
+ * a specific service locator or <code>null</code> to use the\r
+ * workbench global service locator\r
+ */\r
+ public void setServiceLocator(IServiceLocator serviceLocator) {\r
+ if (serviceLocator == null && PlatformUI.isWorkbenchRunning())\r
+ serviceLocator = PlatformUI.getWorkbench();\r
+ this.serviceLocator = serviceLocator;\r
+ if (serviceLocator != null) {\r
+ this.contextService = (IContextService) serviceLocator.getService(IContextService.class);\r
+ this.focusService = (IFocusService) serviceLocator.getService(IFocusService.class);\r
+ }\r
+ }\r
+ \r
+ private void detachPrimitiveProcessors() {\r
+ for (PrimitiveQueryProcessor<?> p : primitiveProcessors.values()) {\r
+ if (p instanceof ProcessorLifecycle) {\r
+ ((ProcessorLifecycle) p).detached(this);\r
+ }\r
+ }\r
+ }\r
+\r
+ private void clearPrimitiveProcessors() {\r
+ for (PrimitiveQueryProcessor<?> p : primitiveProcessors.values()) {\r
+ if (p instanceof ProcessorLifecycle) {\r
+ ((ProcessorLifecycle) p).clear();\r
+ }\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void setExpanded(NodeContext context, boolean expanded) {\r
+ for (TreeNode n : contextToNodeMap.getValues(context)) {\r
+ if (expanded)\r
+ treeLayer.expandTreeRow(n.getListIndex());\r
+ else\r
+ treeLayer.collapseTreeRow(n.getListIndex());\r
+ }\r
+ //viewer.setExpandedState(context, expanded);\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void setAutoExpandLevel(int level) {\r
+ this.autoExpandLevel = level;\r
+ treeLayer.expandAllToLevel(level);\r
+ //viewer.setAutoExpandLevel(level);\r
+ }\r
+ \r
+ int maxChildren = DEFAULT_MAX_CHILDREN;\r
+ \r
+ @Override\r
+ public int getMaxChildren() {\r
+ return maxChildren;\r
+ }\r
+ \r
+ @Override\r
+ public void setMaxChildren(int maxChildren) {\r
+ this.maxChildren = maxChildren;\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public int getMaxChildren(NodeQueryManager manager, NodeContext context) {\r
+ Integer result = manager.query(context, BuiltinKeys.SHOW_MAX_CHILDREN);\r
+ //System.out.println("getMaxChildren(" + manager + ", " + context + "): " + result);\r
+ if (result != null) {\r
+ if (result < 0)\r
+ throw new AssertionError("BuiltinKeys.SHOW_MAX_CHILDREN query must never return < 0, got " + result);\r
+ return result;\r
+ }\r
+ return maxChildren;\r
+ }\r
+ \r
+ @Override\r
+ public <T> NodeQueryProcessor<T> getProcessor(QueryKey<T> key) {\r
+ return explorerContext.getProcessor(key);\r
+ }\r
+\r
+ @Override\r
+ public <T> PrimitiveQueryProcessor<T> getPrimitiveProcessor(PrimitiveQueryKey<T> key) {\r
+ return explorerContext.getPrimitiveProcessor(key);\r
+ }\r
+ \r
+ private HashSet<UpdateItem> pendingItems = new HashSet<UpdateItem>();\r
+ private boolean updating = false;\r
+ private int updateCounter = 0;\r
+ final ScheduledExecutorService uiUpdateScheduler = ThreadUtils.getNonBlockingWorkExecutor();\r
+ \r
+ private class UpdateItem {\r
+ TreeNode element;\r
+ int columnIndex;\r
+ \r
+ public UpdateItem(TreeNode element) {\r
+ this(element,-1);\r
+ }\r
+ \r
+ public UpdateItem(TreeNode element, int columnIndex) {\r
+ this.element = element;\r
+ this.columnIndex = columnIndex;\r
+ if (element != null && element.isDisposed()) {\r
+ throw new IllegalArgumentException("Node is disposed. " + element);\r
+ }\r
+ }\r
+ \r
+ public void update(NatTable natTable) {\r
+ if (element != null) {\r
+\r
+ if (element.isDisposed()) {\r
+ return;\r
+ }\r
+ if (((TreeNode)element).updateChildren()) {\r
+ listReIndex();\r
+ natTable.refresh(true);\r
+ //viewer.refresh(element,true);\r
+ } else {\r
+ if (columnIndex >= 0) {\r
+ natTable.redraw();\r
+ //viewer.update(element, new String[]{columns[columnIndex].getKey()});\r
+ } else {\r
+ natTable.redraw();\r
+ //viewer.refresh(element,true);\r
+ }\r
+ }\r
+ \r
+ if (!element.isDisposed() && autoExpandLevel > 1 && !element.isExpanded() && element.getDepth() <= autoExpandLevel) {\r
+ expand = true;\r
+ treeLayer.expandTreeRow(element.getListIndex());\r
+ //viewer.setExpandedState(element, true);\r
+ expand = false;\r
+ }\r
+ } else {\r
+ if (rootNode.updateChildren()) {\r
+ listReIndex();\r
+ natTable.refresh(true);\r
+ //viewer.refresh(rootNode,true);\r
+ }\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj == null)\r
+ return false;\r
+ if (obj.getClass() != getClass())\r
+ return false;\r
+ UpdateItem other = (UpdateItem)obj;\r
+ if (columnIndex != other.columnIndex)\r
+ return false;\r
+ if (element != null)\r
+ return element.equals(other.element);\r
+ return other.element == null;\r
+ }\r
+ \r
+ @Override\r
+ public int hashCode() {\r
+ if (element != null)\r
+ return element.hashCode() + columnIndex;\r
+ return 0;\r
+ }\r
+ }\r
+ \r
+ private void update(final TreeNode element, final int columnIndex) {\r
+ if (DEBUG)System.out.println("update " + element + " " + columnIndex);\r
+ if (natTable.isDisposed())\r
+ return;\r
+ synchronized (pendingItems) {\r
+ pendingItems.add(new UpdateItem(element, columnIndex));\r
+ if (updating) return;\r
+ updateCounter++;\r
+ scheduleUpdater();\r
+ }\r
+ }\r
+\r
+ private void update(final TreeNode element) {\r
+ if (DEBUG)System.out.println("update " + element);\r
+ if (natTable.isDisposed())\r
+ return;\r
+ if (element != null && element.isDisposed())\r
+ return;\r
+ synchronized (pendingItems) {\r
+ \r
+ pendingItems.add(new UpdateItem(element));\r
+ if (updating) return;\r
+ updateCounter++;\r
+ scheduleUpdater();\r
+ }\r
+ }\r
+ \r
+ boolean scheduleUpdater() {\r
+\r
+ if (natTable.isDisposed())\r
+ return false;\r
+\r
+ if (!pendingItems.isEmpty()) {\r
+ \r
+ int activity = explorerContext.activityInt;\r
+ long delay = 30;\r
+ if (activity < 100) {\r
+ //System.out.println("Scheduling update immediately.");\r
+ } else if (activity < 1000) {\r
+ //System.out.println("Scheduling update after 500ms.");\r
+ delay = 500;\r
+ } else {\r
+ //System.out.println("Scheduling update after 3000ms.");\r
+ delay = 3000;\r
+ }\r
+\r
+ updateCounter = 0;\r
+ \r
+ //System.out.println("Scheduling UI update after " + delay + " ms.");\r
+ uiUpdateScheduler.schedule(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ \r
+ if (natTable == null || natTable.isDisposed())\r
+ return;\r
+ \r
+ if (updateCounter > 0) {\r
+ updateCounter = 0;\r
+ uiUpdateScheduler.schedule(this, 50, TimeUnit.MILLISECONDS);\r
+ } else {\r
+ natTable.getDisplay().asyncExec(new UpdateRunner(NatTableGraphExplorer.this, NatTableGraphExplorer.this.explorerContext));\r
+ }\r
+ \r
+ }\r
+ }, delay, TimeUnit.MILLISECONDS);\r
+\r
+ updating = true;\r
+ return true;\r
+ }\r
+\r
+ return false;\r
+ }\r
+ \r
+ @Override\r
+ public String startEditing(NodeContext context, String columnKey) {\r
+ assertNotDisposed();\r
+ if (!thread.currentThreadAccess())\r
+ throw new IllegalStateException("not in SWT display thread " + thread.getThread());\r
+\r
+ if(columnKey.startsWith("#")) {\r
+ columnKey = columnKey.substring(1);\r
+ }\r
+\r
+ Integer columnIndex = columnKeyToIndex.get(columnKey);\r
+ if (columnIndex == null)\r
+ return "Rename not supported for selection";\r
+// FIXME:\r
+// viewer.editElement(context, columnIndex);\r
+// if(viewer.isCellEditorActive()) return null;\r
+ return "Rename not supported for selection";\r
+ }\r
+\r
+ @Override\r
+ public String startEditing(String columnKey) {\r
+ ISelection selection = postSelectionProvider.getSelection();\r
+ if(selection == null) return "Rename not supported for selection";\r
+ NodeContext context = ISelectionUtils.filterSingleSelection(selection, NodeContext.class);\r
+ if(context == null) return "Rename not supported for selection";\r
+\r
+ return startEditing(context, columnKey);\r
+\r
+ }\r
+ \r
+ public void setSelection(final ISelection selection, boolean forceControlUpdate) {\r
+ assertNotDisposed();\r
+ boolean equalsOld = selectionProvider.selectionEquals(selection);\r
+ if (equalsOld && !forceControlUpdate) {\r
+ // Just set the selection object instance, fire no events nor update\r
+ // the viewer selection.\r
+ selectionProvider.setSelection(selection);\r
+ } else {\r
+ Collection<NodeContext> coll = AdaptionUtils.adaptToCollection(selection, NodeContext.class);\r
+ Collection<TreeNode> nodes = new ArrayList<TreeNode>();\r
+ for (NodeContext c : coll) {\r
+ List<TreeNode> match = contextToNodeMap.getValuesUnsafe(c);\r
+ if(match.size() > 0)\r
+ nodes.add(match.get(0));\r
+ }\r
+ final ISelection sel = new StructuredSelection(nodes.toArray());\r
+ if (coll.size() == 0)\r
+ return;\r
+ // Schedule viewer and selection update if necessary.\r
+ if (natTable.isDisposed())\r
+ return;\r
+ Display d = natTable.getDisplay();\r
+ if (d.getThread() == Thread.currentThread()) {\r
+ selectionAdaptor.setSelection(sel);\r
+ } else {\r
+ d.asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (natTable.isDisposed())\r
+ return;\r
+ selectionAdaptor.setSelection(sel);\r
+ }\r
+ });\r
+ }\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void setModificationContext(ModificationContext modificationContext) {\r
+ this.modificationContext = modificationContext;\r
+ \r
+ }\r
+ \r
+ final ExecutorService queryUpdateScheduler = Threads.getExecutor();\r
+ \r
+ \r
+ private double getDisplayScale() {\r
+ Point dpi = Display.getCurrent().getDPI();\r
+ return (double)dpi.x/96.0;\r
+ }\r
+ \r
+ private void createNatTable() {\r
+ GETreeData treeData = new GETreeData(list);\r
+ GETreeRowModel<TreeNode> treeRowModel = new GETreeRowModel<TreeNode>(treeData);\r
+ columnAccessor = new GEColumnAccessor(this);\r
+ \r
+ IDataProvider dataProvider = new ListDataProvider<TreeNode>(list, columnAccessor);\r
+ \r
+ int defaultFontSize = 12;\r
+ int height = (int)Math.ceil(((double)(defaultFontSize))*getDisplayScale()) + DataLayer.DEFAULT_ROW_HEIGHT-defaultFontSize;\r
+ dataLayer = new DataLayer(dataProvider, DataLayer.DEFAULT_COLUMN_WIDTH, height);\r
+ \r
+ // resizable rows are unnecessary in Sulca report.\r
+ dataLayer.setRowsResizableByDefault(false);\r
+ \r
+ // Row header layer\r
+ DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(dataProvider);\r
+ rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider);\r
+ \r
+ // adjust row header column width so that row numbers fit into the column. \r
+ //adjustRowHeaderWidth(list.size());\r
+ \r
+ // Column header layer\r
+ columnHeaderDataProvider = new GEColumnHeaderDataProvider(this, dataLayer); \r
+ columnHeaderDataLayer = new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);\r
+ columnHeaderDataLayer.setDefaultRowHeight(height);\r
+ columnHeaderDataProvider.updateColumnSizes();\r
+ \r
+ //ISortModel sortModel = new EcoSortModel(this, generator,dataLayer);\r
+ \r
+ // Column re-order + hide\r
+ ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(dataLayer);\r
+ ColumnHideShowLayer columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer);\r
+ \r
+ \r
+ treeLayer = new GETreeLayer(columnHideShowLayer, treeRowModel, false);\r
+ \r
+ selectionLayer = new SelectionLayer(treeLayer);\r
+ \r
+ viewportLayer = new ViewportLayer(selectionLayer);\r
+ \r
+ ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, viewportLayer, selectionLayer);\r
+ // Note: The column header layer is wrapped in a filter row composite.\r
+ // This plugs in the filter row functionality\r
+ \r
+ ColumnOverrideLabelAccumulator labelAccumulator = new ColumnOverrideLabelAccumulator(columnHeaderDataLayer);\r
+ columnHeaderDataLayer.setConfigLabelAccumulator(labelAccumulator);\r
+ \r
+ // Register labels\r
+ //SortHeaderLayer<TreeNode> sortHeaderLayer = new SortHeaderLayer<TreeNode>(columnHeaderLayer, sortModel, false);\r
+\r
+ RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, viewportLayer, selectionLayer);\r
+\r
+ // Corner layer\r
+ DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider);\r
+ cornerDataLayer = new DataLayer(cornerDataProvider);\r
+ //CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);\r
+ CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnHeaderLayer);\r
+\r
+ // Grid\r
+ //GridLayer gridLayer = new GridLayer(viewportLayer,sortHeaderLayer,rowHeaderLayer, cornerLayer);\r
+ GridLayer gridLayer = new GridLayer(viewportLayer, columnHeaderLayer,rowHeaderLayer, cornerLayer, false);\r
+ \r
+ /* Since 1.4.0, alternative row rendering uses row indexes in the original data list. \r
+ When combined with collapsed tree rows, rows with odd or even index may end up next to each other,\r
+ which defeats the purpose of alternating colors. This overrides that and returns the functionality\r
+ that we had with 1.0.1. */\r
+ gridLayer.setConfigLabelAccumulatorForRegion(GridRegion.BODY, new RelativeAlternatingRowConfigLabelAccumulator());\r
+ gridLayer.addConfiguration(new DefaultEditConfiguration());\r
+ //gridLayer.addConfiguration(new DefaultEditBindings());\r
+ gridLayer.addConfiguration(new GEEditBindings());\r
+ \r
+ natTable = new NatTable(composite,gridLayer,false);\r
+ \r
+ //selectionLayer.registerCommandHandler(new EcoCopyDataCommandHandler(selectionLayer,columnHeaderDataLayer,columnAccessor, columnHeaderDataProvider));\r
+ \r
+ natTable.addConfiguration(new NatTableHeaderMenuConfiguration(natTable));\r
+ natTable.addConfiguration(new DefaultTreeLayerConfiguration2(treeLayer));\r
+ natTable.addConfiguration(new SingleClickSortConfiguration());\r
+ //natTable.addLayerListener(this);\r
+ \r
+ natTable.addConfiguration(new GENatTableThemeConfiguration(treeData));\r
+ natTable.addConfiguration(new NatTableHeaderMenuConfiguration(natTable));\r
+ \r
+ natTable.addConfiguration(new AbstractRegistryConfiguration() {\r
+ \r
+ @Override\r
+ public void configureRegistry(IConfigRegistry configRegistry) {\r
+ configRegistry.registerConfigAttribute(\r
+ EditConfigAttributes.CELL_EDITABLE_RULE,\r
+ new IEditableRule() {\r
+\r
+ @Override\r
+ public boolean isEditable(ILayerCell cell,\r
+ IConfigRegistry configRegistry) {\r
+ int col = cell.getColumnIndex();\r
+ int row = cell.getRowIndex();\r
+ TreeNode node = list.get(row);\r
+ Modifier modifier = getModifier(node,col);\r
+ return modifier != null;\r
+ \r
+ }\r
+\r
+ @Override\r
+ public boolean isEditable(int columnIndex, int rowIndex) {\r
+ // there are no callers?\r
+ return false;\r
+ }\r
+\r
+ });\r
+ configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new AdaptableCellEditor());\r
+ configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, new DefaultDisplayConverter(),DisplayMode.EDIT);\r
+ // configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, new GECellPainter(),DisplayMode.NORMAL);\r
+\r
+ \r
+ }\r
+ });\r
+ \r
+ natTable.configure();\r
+ \r
+// natTable.addListener(SWT.MenuDetect, new NatTableMenuListener());\r
+ \r
+// DefaultToolTip toolTip = new EcoCellToolTip(natTable, columnAccessor);\r
+// toolTip.setBackgroundColor(natTable.getDisplay().getSystemColor(SWT.COLOR_WHITE));\r
+// toolTip.setPopupDelay(500);\r
+// toolTip.activate();\r
+// toolTip.setShift(new Point(10, 10));\r
+\r
+ \r
+// menuManager.createContextMenu(composite);\r
+// natTable.setMenu(getMenuManager().getMenu());\r
+ \r
+ selectionAdaptor = new NatTableSelectionAdaptor(natTable, selectionLayer, treeData);\r
+ }\r
+ \r
+ Modifier getModifier(TreeNode element, int columnIndex) {\r
+ GENodeQueryManager manager = element.getManager();\r
+ final NodeContext context = element.getContext();\r
+ Labeler labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER);\r
+ if (labeler == null)\r
+ return null;\r
+ Column column = columns[columnIndex];\r
+\r
+ return labeler.getModifier(modificationContext, column.getKey());\r
+\r
+ }\r
+ \r
+ private class AdaptableCellEditor implements ICellEditor {\r
+ ICellEditor editor;\r
+\r
+ @Override\r
+ public Control activateCell(Composite parent, Object originalCanonicalValue, EditModeEnum editMode,\r
+ ICellEditHandler editHandler, ILayerCell cell, IConfigRegistry configRegistry) {\r
+ int col = cell.getColumnIndex();\r
+ int row = cell.getRowIndex();\r
+ TreeNode node = list.get(row);\r
+ Modifier modifier = getModifier(node, col);\r
+ if (modifier == null)\r
+ return null;\r
+ \r
+ editor = null;\r
+ if (modifier instanceof DialogModifier) {\r
+ DialogModifier mod = (DialogModifier)modifier;\r
+ editor = new DialogCellEditor(node, col, mod);\r
+ } else if (modifier instanceof CustomModifier) {\r
+ CustomModifier mod = (CustomModifier)modifier;\r
+ editor = new CustomCellEditor(node, col, mod);\r
+ } else if (modifier instanceof EnumerationModifier) {\r
+ EnumerationModifier mod = (EnumerationModifier)modifier;\r
+ editor = new ComboBoxCellEditor(mod.getValues());\r
+ } else {\r
+ editor = new TextCellEditor();\r
+ }\r
+ \r
+ return editor.activateCell(parent, originalCanonicalValue, editMode, editHandler, cell, configRegistry);\r
+ }\r
+\r
+ @Override\r
+ public int getColumnIndex() {\r
+ return editor.getColumnIndex();\r
+ }\r
+\r
+ @Override\r
+ public int getRowIndex() {\r
+ return editor.getRowIndex();\r
+ }\r
+\r
+ @Override\r
+ public int getColumnPosition() {\r
+ return editor.getColumnPosition();\r
+ }\r
+\r
+ @Override\r
+ public int getRowPosition() {\r
+ return editor.getRowPosition();\r
+ }\r
+\r
+ @Override\r
+ public Object getEditorValue() {\r
+ return editor.getEditorValue();\r
+ }\r
+\r
+ @Override\r
+ public void setEditorValue(Object value) {\r
+ editor.setEditorValue(value);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public Object getCanonicalValue() {\r
+ return editor.getCanonicalValue();\r
+ }\r
+\r
+ @Override\r
+ public Object getCanonicalValue(IEditErrorHandler conversionErrorHandler) {\r
+ return editor.getCanonicalValue();\r
+ }\r
+\r
+ @Override\r
+ public void setCanonicalValue(Object canonicalValue) {\r
+ editor.setCanonicalValue(canonicalValue);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public boolean validateCanonicalValue(Object canonicalValue) {\r
+ return editor.validateCanonicalValue(canonicalValue);\r
+ }\r
+\r
+ @Override\r
+ public boolean validateCanonicalValue(Object canonicalValue, IEditErrorHandler validationErrorHandler) {\r
+ return editor.validateCanonicalValue(canonicalValue, validationErrorHandler);\r
+ }\r
+\r
+ @Override\r
+ public boolean commit(MoveDirectionEnum direction) {\r
+ return editor.commit(direction);\r
+ }\r
+\r
+ @Override\r
+ public boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit) {\r
+ return editor.commit(direction, closeAfterCommit);\r
+ }\r
+\r
+ @Override\r
+ public boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit, boolean skipValidation) {\r
+ return editor.commit(direction, closeAfterCommit, skipValidation);\r
+ }\r
+\r
+ @Override\r
+ public void close() {\r
+ editor.close();\r
+ \r
+ }\r
+\r
+ @Override\r
+ public boolean isClosed() {\r
+ return editor.isClosed();\r
+ }\r
+\r
+ @Override\r
+ public Control getEditorControl() {\r
+ return editor.getEditorControl();\r
+ }\r
+\r
+ @Override\r
+ public Control createEditorControl(Composite parent) {\r
+ return editor.createEditorControl(parent);\r
+ }\r
+\r
+ @Override\r
+ public boolean openInline(IConfigRegistry configRegistry, List<String> configLabels) {\r
+ return EditConfigHelper.openInline(configRegistry, configLabels);\r
+ }\r
+\r
+ @Override\r
+ public boolean supportMultiEdit(IConfigRegistry configRegistry, List<String> configLabels) {\r
+ return editor.supportMultiEdit(configRegistry, configLabels);\r
+ }\r
+\r
+ @Override\r
+ public boolean openMultiEditDialog() {\r
+ return editor.openMultiEditDialog();\r
+ }\r
+\r
+ @Override\r
+ public boolean openAdjacentEditor() {\r
+ return editor.openAdjacentEditor();\r
+ }\r
+\r
+ @Override\r
+ public boolean activateAtAnyPosition() {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public boolean activateOnTraversal(IConfigRegistry configRegistry, List<String> configLabels) {\r
+ return editor.activateOnTraversal(configRegistry, configLabels);\r
+ }\r
+\r
+ @Override\r
+ public void addEditorControlListeners() {\r
+ editor.addEditorControlListeners();\r
+ \r
+ }\r
+\r
+ @Override\r
+ public void removeEditorControlListeners() {\r
+ editor.removeEditorControlListeners();\r
+ \r
+ }\r
+\r
+ @Override\r
+ public Rectangle calculateControlBounds(Rectangle cellBounds) {\r
+ return editor.calculateControlBounds(cellBounds);\r
+ }\r
+ \r
+ \r
+ }\r
+ \r
+ private class CustomCellEditor extends AbstractCellEditor {\r
+ TreeNode node;\r
+ CustomModifier customModifier;\r
+ Control control;\r
+ int column;\r
+ \r
+ public CustomCellEditor(TreeNode node, int column, CustomModifier customModifier) {\r
+ this.customModifier = customModifier;\r
+ this.node = node;\r
+ this.column = column;\r
+ }\r
+\r
+ @Override\r
+ public Object getEditorValue() {\r
+ return customModifier.getValue();\r
+ }\r
+\r
+ @Override\r
+ public void setEditorValue(Object value) {\r
+ customModifier.modify(value.toString());\r
+ \r
+ }\r
+\r
+ @Override\r
+ public Control getEditorControl() {\r
+ return control;\r
+ }\r
+\r
+ @Override\r
+ public Control createEditorControl(Composite parent) {\r
+ return (Control)customModifier.createControl(parent, null, column, node.getContext());\r
+ \r
+ }\r
+\r
+ @Override\r
+ protected Control activateCell(Composite parent, Object originalCanonicalValue) {\r
+ this.control = createEditorControl(parent);\r
+ return control;\r
+ }\r
+ \r
+ \r
+ }\r
+ \r
+ private class DialogCellEditor extends AbstractDialogCellEditor {\r
+ TreeNode node;\r
+ DialogModifier dialogModifier;\r
+ int column;\r
+ \r
+ String res = null;\r
+ Semaphore sem;\r
+ \r
+ boolean closed = false;\r
+ \r
+ public DialogCellEditor(TreeNode node, int column, DialogModifier dialogModifier) {\r
+ this.dialogModifier = dialogModifier;\r
+ this.node = node;\r
+ this.column = column;\r
+ }\r
+ \r
+ @Override\r
+ public int open() {\r
+ sem = new Semaphore(1);\r
+ Consumer<String> callback = result -> {\r
+ res = result;\r
+ sem.release();\r
+ };\r
+ String status = dialogModifier.query(this.parent.getShell(), null, column, node.getContext(), callback);\r
+ if (status != null) {\r
+ closed = true;\r
+ return Window.CANCEL;\r
+ }\r
+ \r
+ try {\r
+ sem.acquire();\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ }\r
+ closed = true;\r
+ return Window.OK;\r
+ }\r
+ \r
+ @Override\r
+ public DialogModifier createDialogInstance() {\r
+ closed = false;\r
+ return dialogModifier;\r
+ }\r
+ \r
+ @Override\r
+ public Object getDialogInstance() {\r
+ return (DialogModifier)this.dialog;\r
+ }\r
+ \r
+ @Override\r
+ public void close() {\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public Object getEditorValue() {\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public void setEditorValue(Object value) {\r
+ // dialog modifier handles this internally\r
+ }\r
+ \r
+ @Override\r
+ public boolean isClosed() {\r
+ return closed;\r
+ }\r
+ \r
+ }\r
+ \r
+\r
+ /**\r
+ * The job that is used for off-loading image loading tasks (see\r
+ * {@link ImageTask} to a worker thread from the main UI thread.\r
+ */\r
+ ImageLoaderJob imageLoaderJob;\r
+ \r
+ // Map<NodeContext, ImageTask> imageTasks = new THashMap<NodeContext, ImageTask>();\r
+ Map<TreeNode, ImageTask> imageTasks = new THashMap<TreeNode, ImageTask>();\r
+ \r
+ void queueImageTask(TreeNode node, ImageTask task) {\r
+ synchronized (imageTasks) {\r
+ imageTasks.put(node, task);\r
+ }\r
+ imageLoaderJob.scheduleIfNecessary(100);\r
+ }\r
+ \r
+ /**\r
+ * Invoked in a job worker thread.\r
+ * \r
+ * @param monitor\r
+ */\r
+ @Override\r
+ protected IStatus setPendingImages(IProgressMonitor monitor) {\r
+ ImageTask[] tasks = null;\r
+ synchronized (imageTasks) {\r
+ tasks = imageTasks.values().toArray(new ImageTask[imageTasks.size()]);\r
+ imageTasks.clear();\r
+ }\r
+\r
+ MultiStatus status = null;\r
+\r
+ // Load missing images\r
+ for (ImageTask task : tasks) {\r
+ Object desc = task.descsOrImage;\r
+ if (desc instanceof ImageDescriptor) {\r
+ try {\r
+ desc = resourceManager.get((ImageDescriptor) desc);\r
+ task.descsOrImage = desc;\r
+ } catch (DeviceResourceException e) {\r
+ if (status == null)\r
+ status = new MultiStatus(Activator.PLUGIN_ID, 0, "Problems loading images:", null);\r
+ status.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Image descriptor loading failed: " + desc, e));\r
+ }\r
+ }\r
+ \r
+ }\r
+\r
+ // Perform final UI updates in the UI thread.\r
+ final ImageTask[] _tasks = tasks;\r
+ thread.asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ setImages(_tasks);\r
+ }\r
+ });\r
+\r
+ return status != null ? status : Status.OK_STATUS;\r
+ }\r
+ \r
+\r
+ void setImages(ImageTask[] tasks) {\r
+ for (ImageTask task : tasks)\r
+ if (task != null)\r
+ setImage(task);\r
+ }\r
+ \r
+ void setImage(ImageTask task) {\r
+ if (!task.node.isDisposed())\r
+ update(task.node, 0);\r
+ }\r
+ \r
+ private static class GraphExplorerPostSelectionProvider implements IPostSelectionProvider {\r
+ \r
+ private NatTableGraphExplorer ge;\r
+ \r
+ GraphExplorerPostSelectionProvider(NatTableGraphExplorer ge) {\r
+ this.ge = ge;\r
+ }\r
+ \r
+ void dispose() {\r
+ ge = null;\r
+ }\r
+ \r
+ @Override\r
+ public void setSelection(final ISelection selection) {\r
+ if(ge == null) return;\r
+ ge.setSelection(selection, false);\r
+ \r
+ }\r
+ \r
+\r
+ @Override\r
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {\r
+ if(ge == null) return;\r
+ if(ge.isDisposed()) {\r
+ if (DEBUG_SELECTION_LISTENERS)\r
+ System.out.println("GraphExplorerImpl is disposed in removeSelectionChangedListener: " + listener);\r
+ return;\r
+ }\r
+ ge.selectionProvider.removeSelectionChangedListener(listener);\r
+ }\r
+ \r
+ @Override\r
+ public void addPostSelectionChangedListener(ISelectionChangedListener listener) {\r
+ if(ge == null) return;\r
+ if (!ge.thread.currentThreadAccess())\r
+ throw new AssertionError(getClass().getSimpleName() + ".addPostSelectionChangedListener called from non SWT-thread: " + Thread.currentThread());\r
+ if(ge.isDisposed()) {\r
+ System.out.println("Client BUG: GraphExplorerImpl is disposed in addPostSelectionChangedListener: " + listener);\r
+ return;\r
+ }\r
+ ge.selectionProvider.addPostSelectionChangedListener(listener);\r
+ }\r
+\r
+ @Override\r
+ public void removePostSelectionChangedListener(ISelectionChangedListener listener) {\r
+ if(ge == null) return;\r
+ if(ge.isDisposed()) {\r
+ if (DEBUG_SELECTION_LISTENERS)\r
+ System.out.println("GraphExplorerImpl is disposed in removePostSelectionChangedListener: " + listener);\r
+ return;\r
+ }\r
+ ge.selectionProvider.removePostSelectionChangedListener(listener);\r
+ }\r
+ \r
+\r
+ @Override\r
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {\r
+ if(ge == null) return;\r
+ if (!ge.thread.currentThreadAccess())\r
+ throw new AssertionError(getClass().getSimpleName() + ".addSelectionChangedListener called from non SWT-thread: " + Thread.currentThread());\r
+ if (ge.natTable.isDisposed() || ge.selectionProvider == null) {\r
+ System.out.println("Client BUG: GraphExplorerImpl is disposed in addSelectionChangedListener: " + listener);\r
+ return;\r
+ }\r
+\r
+ ge.selectionProvider.addSelectionChangedListener(listener);\r
+ }\r
+\r
+ \r
+ @Override\r
+ public ISelection getSelection() {\r
+ if(ge == null) return StructuredSelection.EMPTY;\r
+ if (!ge.thread.currentThreadAccess())\r
+ throw new AssertionError(getClass().getSimpleName() + ".getSelection called from non SWT-thread: " + Thread.currentThread());\r
+ if (ge.natTable.isDisposed() || ge.selectionProvider == null)\r
+ return StructuredSelection.EMPTY;\r
+ return ge.selectionProvider.getSelection();\r
+ }\r
+ \r
+ }\r
+ \r
+ static class ModifierValidator implements ICellEditorValidator {\r
+ private Modifier modifier;\r
+ public ModifierValidator(Modifier modifier) {\r
+ this.modifier = modifier;\r
+ }\r
+ \r
+ @Override\r
+ public String isValid(Object value) {\r
+ return modifier.isValid((String)value);\r
+ }\r
+ }\r
+ \r
+ static class UpdateRunner implements Runnable {\r
+\r
+ final NatTableGraphExplorer ge;\r
+\r
+ UpdateRunner(NatTableGraphExplorer ge, IGraphExplorerContext geContext) {\r
+ this.ge = ge;\r
+ }\r
+\r
+ public void run() {\r
+ try {\r
+ doRun();\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void doRun() {\r
+ \r
+ if (ge.isDisposed())\r
+ return;\r
+\r
+ HashSet<UpdateItem> items;\r
+\r
+ ScrollBar verticalBar = ge.natTable.getVerticalBar();\r
+ \r
+ \r
+ synchronized (ge.pendingItems) {\r
+ items = ge.pendingItems;\r
+ ge.pendingItems = new HashSet<UpdateItem>();\r
+ }\r
+ if (DEBUG) System.out.println("UpdateRunner.doRun() " + items.size());\r
+\r
+ ge.natTable.setRedraw(false);\r
+ for (UpdateItem item : items) {\r
+ item.update(ge.natTable);\r
+ }\r
+ \r
+ // check if vertical scroll bar has become visible and refresh layout.\r
+ boolean currentlyVerticalBarVisible = verticalBar.isVisible();\r
+ if (ge.verticalBarVisible != currentlyVerticalBarVisible) {\r
+ ge.verticalBarVisible = currentlyVerticalBarVisible;\r
+ ge.natTable.getParent().layout();\r
+ }\r
+ \r
+ ge.natTable.setRedraw(true);\r
+ \r
+ synchronized (ge.pendingItems) {\r
+ if (!ge.scheduleUpdater()) {\r
+ ge.updating = false;\r
+ }\r
+ }\r
+ if (DEBUG) {\r
+ if (!ge.updating) {\r
+ ge.printTree(ge.rootNode, 0);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+ \r
+ \r
+ \r
+ public static class GeViewerContext extends AbstractDisposable implements IGraphExplorerContext {\r
+ // This is for query debugging only.\r
+ \r
+ private NatTableGraphExplorer ge;\r
+ int queryIndent = 0;\r
+\r
+ GECache2 cache = new GECache2();\r
+ AtomicBoolean propagating = new AtomicBoolean(false);\r
+ Object propagateList = new Object();\r
+ Object propagate = new Object();\r
+ List<Runnable> scheduleList = new ArrayList<Runnable>();\r
+ final Deque<Integer> activity = new LinkedList<Integer>();\r
+ int activityInt = 0;\r
+ \r
+ AtomicReference<Runnable> currentQueryUpdater = new AtomicReference<Runnable>();\r
+\r
+ /**\r
+ * Keeps track of nodes that have already been auto-expanded. After\r
+ * being inserted into this set, nodes will not be forced to stay in an\r
+ * expanded state after that. This makes it possible for the user to\r
+ * close auto-expanded nodes.\r
+ */\r
+ Map<NodeContext, Boolean> autoExpanded = new WeakHashMap<NodeContext, Boolean>();\r
+\r
+ public GeViewerContext(NatTableGraphExplorer ge) {\r
+ this.ge = ge;\r
+ }\r
+ \r
+ public MapList<NodeContext,TreeNode> getContextToNodeMap() {\r
+ if (ge == null)\r
+ return null;\r
+ return ge.contextToNodeMap;\r
+ }\r
+ \r
+ public NatTableGraphExplorer getGe() {\r
+ return ge;\r
+ }\r
+ \r
+ @Override\r
+ protected void doDispose() {\r
+ //saveState();\r
+ autoExpanded.clear();\r
+ }\r
+\r
+ @Override\r
+ public IGECache getCache() {\r
+ return cache;\r
+ }\r
+\r
+ @Override\r
+ public int queryIndent() {\r
+ return queryIndent;\r
+ }\r
+\r
+ @Override\r
+ public int queryIndent(int offset) {\r
+ queryIndent += offset;\r
+ return queryIndent;\r
+ }\r
+\r
+ @Override\r
+ @SuppressWarnings("unchecked")\r
+ public <T> NodeQueryProcessor<T> getProcessor(Object o) {\r
+ if (ge == null)\r
+ return null;\r
+ return ge.processors.get(o);\r
+ }\r
+\r
+ @Override\r
+ @SuppressWarnings("unchecked")\r
+ public <T> PrimitiveQueryProcessor<T> getPrimitiveProcessor(Object o) {\r
+ return ge.primitiveProcessors.get(o);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> DataSource<T> getDataSource(Class<T> clazz) {\r
+ return ge.dataSources.get(clazz);\r
+ }\r
+\r
+ @Override\r
+ public void update(UIElementReference ref) {\r
+ if (ref instanceof ViewerCellReference) {\r
+ ViewerCellReference tiref = (ViewerCellReference) ref;\r
+ Object element = tiref.getElement();\r
+ int columnIndex = tiref.getColumn();\r
+ // NOTE: must be called regardless of the the item value.\r
+ // A null item is currently used to indicate a tree root update.\r
+ ge.update((TreeNode)element,columnIndex);\r
+ } else if (ref instanceof ViewerRowReference) {\r
+ ViewerRowReference rref = (ViewerRowReference)ref;\r
+ Object element = rref.getElement();\r
+ ge.update((TreeNode)element);\r
+ } else {\r
+ throw new IllegalArgumentException("Ui Reference is unknkown " + ref);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Object getPropagateLock() {\r
+ return propagate;\r
+ }\r
+\r
+ @Override\r
+ public Object getPropagateListLock() {\r
+ return propagateList;\r
+ }\r
+\r
+ @Override\r
+ public boolean isPropagating() {\r
+ return propagating.get();\r
+ }\r
+\r
+ @Override\r
+ public void setPropagating(boolean b) {\r
+ this.propagating.set(b);\r
+ }\r
+\r
+ @Override\r
+ public List<Runnable> getScheduleList() {\r
+ return scheduleList;\r
+ }\r
+\r
+ @Override\r
+ public void setScheduleList(List<Runnable> list) {\r
+ this.scheduleList = list;\r
+ }\r
+\r
+ @Override\r
+ public Deque<Integer> getActivity() {\r
+ return activity;\r
+ }\r
+\r
+ @Override\r
+ public void setActivityInt(int i) {\r
+ this.activityInt = i;\r
+ }\r
+\r
+ @Override\r
+ public int getActivityInt() {\r
+ return activityInt;\r
+ }\r
+\r
+ @Override\r
+ public void scheduleQueryUpdate(Runnable r) {\r
+ if (ge == null)\r
+ return;\r
+ if (ge.isDisposed())\r
+ return;\r
+ if (currentQueryUpdater.compareAndSet(null, r)) {\r
+ ge.queryUpdateScheduler.execute(QUERY_UPDATE_SCHEDULER);\r
+ }\r
+ }\r
+\r
+ Runnable QUERY_UPDATE_SCHEDULER = new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Runnable r = currentQueryUpdater.getAndSet(null);\r
+ if (r != null) {\r
+ r.run();\r
+ }\r
+ }\r
+ };\r
+ \r
+ @Override\r
+ public void dispose() {\r
+ cache.dispose();\r
+ cache = new DummyCache();\r
+ scheduleList.clear();\r
+ autoExpanded.clear();\r
+ autoExpanded = null;\r
+ ge = null;\r
+ \r
+ }\r
+ }\r
+ \r
+ private class TreeNodeIsExpandedProcessor extends AbstractPrimitiveQueryProcessor<Boolean> implements\r
+ IsExpandedProcessor, ProcessorLifecycle {\r
+ /**\r
+ * The set of currently expanded node contexts.\r
+ */\r
+ private final HashSet<NodeContext> expanded = new HashSet<NodeContext>();\r
+ private final HashMap<NodeContext, PrimitiveQueryUpdater> expandedQueries = new HashMap<NodeContext, PrimitiveQueryUpdater>();\r
+\r
+ private NatTable natTable;\r
+ private List<TreeNode> list;\r
+\r
+ public TreeNodeIsExpandedProcessor() {\r
+ }\r
+\r
+ @Override\r
+ public Object getIdentifier() {\r
+ return BuiltinKeys.IS_EXPANDED;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "IsExpandedProcessor";\r
+ }\r
+\r
+ @Override\r
+ public Boolean query(PrimitiveQueryUpdater updater, NodeContext context, PrimitiveQueryKey<Boolean> key) {\r
+ boolean isExpanded = expanded.contains(context);\r
+ expandedQueries.put(context, updater);\r
+ return Boolean.valueOf(isExpanded);\r
+ }\r
+\r
+ @Override\r
+ public Collection<NodeContext> getExpanded() {\r
+ return new HashSet<NodeContext>(expanded);\r
+ }\r
+\r
+ @Override\r
+ public boolean getExpanded(NodeContext context) {\r
+ return this.expanded.contains(context);\r
+ }\r
+\r
+ @Override\r
+ public boolean setExpanded(NodeContext context, boolean expanded) {\r
+ return _setExpanded(context, expanded);\r
+ }\r
+\r
+ @Override\r
+ public boolean replaceExpanded(NodeContext context, boolean expanded) {\r
+ return nodeStatusChanged(context, expanded);\r
+ }\r
+\r
+ private boolean _setExpanded(NodeContext context, boolean expanded) {\r
+ if (expanded) {\r
+ return this.expanded.add(context);\r
+ } else {\r
+ return this.expanded.remove(context);\r
+ }\r
+ }\r
+\r
+ ILayerListener treeListener = new ILayerListener() {\r
+ \r
+ @Override\r
+ public void handleLayerEvent(ILayerEvent event) {\r
+ // TODO Auto-generated method stub\r
+ if (event instanceof ShowRowPositionsEvent) {\r
+ ShowRowPositionsEvent e = (ShowRowPositionsEvent)event;\r
+ for (Range r : e.getRowPositionRanges()) {\r
+ int expanded = viewportLayer.getRowIndexByPosition(r.start-2)+1;\r
+ //System.out.println("ex " + expanded);\r
+ if (expanded < 0) {\r
+ return;\r
+ }\r
+ nodeStatusChanged(list.get(expanded).getContext(), false);\r
+ }\r
+ } else if (event instanceof HideRowPositionsEvent) {\r
+ HideRowPositionsEvent e = (HideRowPositionsEvent)event;\r
+ for (Range r : e.getRowPositionRanges()) {\r
+ int collapsed = viewportLayer.getRowIndexByPosition(r.start-2)+1;\r
+ //System.out.println("col " + collapsed);\r
+ if (collapsed < 0) {\r
+ return;\r
+ }\r
+ nodeStatusChanged(list.get(collapsed).getContext(), false);\r
+ }\r
+ }\r
+ \r
+ }\r
+ };\r
+\r
+ protected boolean nodeStatusChanged(NodeContext context, boolean expanded) {\r
+ boolean result = _setExpanded(context, expanded);\r
+ PrimitiveQueryUpdater updater = expandedQueries.get(context);\r
+ if (updater != null)\r
+ updater.scheduleReplace(context, BuiltinKeys.IS_EXPANDED, expanded);\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public void attached(GraphExplorer explorer) {\r
+ Object control = explorer.getControl();\r
+ if (control instanceof NatTable) {\r
+ this.natTable = (NatTable) control;\r
+ this.list = ((NatTableGraphExplorer)explorer).list;\r
+ natTable.addLayerListener(treeListener);\r
+ \r
+ } else {\r
+ System.out.println("WARNING: " + getClass().getSimpleName() + " attached to unsupported control: " + control);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void clear() {\r
+ expanded.clear();\r
+ expandedQueries.clear();\r
+ }\r
+\r
+ @Override\r
+ public void detached(GraphExplorer explorer) {\r
+ clear();\r
+ if (natTable != null) {\r
+ natTable.removeLayerListener(treeListener);\r
+// natTable.removeListener(SWT.Expand, treeListener);\r
+// natTable.removeListener(SWT.Collapse, treeListener);\r
+ natTable = null;\r
+ }\r
+ }\r
+ }\r
+ \r
+ private void printTree(TreeNode node, int depth) {\r
+ String s = "";\r
+ for (int i = 0; i < depth; i++) {\r
+ s += " ";\r
+ }\r
+ s += node;\r
+ System.out.println(s);\r
+ int d = depth+1;\r
+ for (TreeNode n : node.getChildren()) {\r
+ printTree(n, d);\r
+ }\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Copy-paste of org.simantics.browsing.ui.common.internal.GECache.GECacheKey (internal class that cannot be used)\r
+ */\r
+ final private static class GECacheKey {\r
+\r
+ private NodeContext context;\r
+ private CacheKey<?> key;\r
+\r
+ GECacheKey(NodeContext context, CacheKey<?> key) {\r
+ this.context = context;\r
+ this.key = key;\r
+ if (context == null || key == null)\r
+ throw new IllegalArgumentException("Null context or key is not accepted");\r
+ }\r
+\r
+ GECacheKey(GECacheKey other) {\r
+ this.context = other.context;\r
+ this.key = other.key;\r
+ if (context == null || key == null)\r
+ throw new IllegalArgumentException("Null context or key is not accepted");\r
+ }\r
+\r
+ void setValues(NodeContext context, CacheKey<?> key) {\r
+ this.context = context;\r
+ this.key = key;\r
+ if (context == null || key == null)\r
+ throw new IllegalArgumentException("Null context or key is not accepted");\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return context.hashCode() | key.hashCode();\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object object) {\r
+\r
+ if (this == object)\r
+ return true;\r
+ else if (object == null)\r
+ return false;\r
+\r
+ GECacheKey i = (GECacheKey) object;\r
+\r
+ return key.equals(i.key) && context.equals(i.context);\r
+\r
+ }\r
+\r
+ };\r
+ \r
+ /**\r
+ * Copy-paste of org.simantics.browsing.ui.common.internal.GECache with added capability of purging all NodeContext related data.\r
+ */\r
+ public static class GECache2 implements IGECache {\r
+ \r
+ final HashMap<GECacheKey, IGECacheEntry> entries = new HashMap<GECacheKey, IGECacheEntry>();\r
+ final HashMap<GECacheKey, Set<UIElementReference>> treeReferences = new HashMap<GECacheKey, Set<UIElementReference>>();\r
+ final HashMap<NodeContext, Set<GECacheKey>> keyRefs = new HashMap<NodeContext, Set<GECacheKey>>();\r
+ \r
+ /**\r
+ * This single instance is used for all get operations from the cache. This\r
+ * should work since the GE cache is meant to be single-threaded within the\r
+ * current UI thread, what ever that thread is. For put operations which\r
+ * store the key, this is not used.\r
+ */\r
+ NodeContext getNC = new NodeContext() {\r
+ @SuppressWarnings("rawtypes")\r
+ @Override\r
+ public Object getAdapter(Class adapter) {\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public <T> T getConstant(ConstantKey<T> key) {\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public Set<ConstantKey<?>> getKeys() {\r
+ return Collections.emptySet();\r
+ }\r
+ };\r
+ CacheKey<?> getCK = new CacheKey<Object>() {\r
+ @Override\r
+ public Object processorIdenfitier() {\r
+ return this;\r
+ }\r
+ };\r
+ GECacheKey getKey = new GECacheKey(getNC, getCK);\r
+ \r
+ \r
+ private void addKey(GECacheKey key) {\r
+ Set<GECacheKey> refs = keyRefs.get(key.context);\r
+ if (refs != null) {\r
+ refs.add(key);\r
+ } else {\r
+ refs = new HashSet<GECacheKey>();\r
+ refs.add(key);\r
+ keyRefs.put(key.context, refs);\r
+ }\r
+ }\r
+ \r
+ private void removeKey(GECacheKey key) {\r
+ Set<GECacheKey> refs = keyRefs.get(key.context);\r
+ if (refs != null) {\r
+ refs.remove(key);\r
+ } \r
+ }\r
+\r
+ public <T> IGECacheEntry put(NodeContext context, CacheKey<T> key, T value) {\r
+// if (DEBUG) System.out.println("Add entry " + context + " " + key);\r
+ IGECacheEntry entry = new GECacheEntry(context, key, value);\r
+ GECacheKey gekey = new GECacheKey(context, key);\r
+ entries.put(gekey, entry);\r
+ addKey(gekey);\r
+ return entry;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public <T> T get(NodeContext context, CacheKey<T> key) {\r
+ getKey.setValues(context, key);\r
+ IGECacheEntry entry = entries.get(getKey);\r
+ if (entry == null)\r
+ return null;\r
+ return (T) entry.getValue();\r
+ }\r
+\r
+ @Override\r
+ public <T> IGECacheEntry getEntry(NodeContext context, CacheKey<T> key) {\r
+ assert(context != null);\r
+ assert(key != null);\r
+ getKey.setValues(context, key);\r
+ return entries.get(getKey);\r
+ }\r
+\r
+ @Override\r
+ public <T> void remove(NodeContext context, CacheKey<T> key) {\r
+// if (DEBUG) System.out.println("Remove entry " + context + " " + key);\r
+ getKey.setValues(context, key);\r
+ entries.remove(getKey);\r
+ removeKey(getKey);\r
+ }\r
+\r
+ @Override\r
+ public <T> Set<UIElementReference> getTreeReference(NodeContext context, CacheKey<T> key) {\r
+ assert(context != null);\r
+ assert(key != null);\r
+ getKey.setValues(context, key);\r
+ return treeReferences.get(getKey);\r
+ }\r
+\r
+ @Override\r
+ public <T> void putTreeReference(NodeContext context, CacheKey<T> key, UIElementReference reference) {\r
+ assert(context != null);\r
+ assert(key != null);\r
+ //if (DEBUG) System.out.println("Add tree reference " + context + " " + key);\r
+ getKey.setValues(context, key);\r
+ Set<UIElementReference> refs = treeReferences.get(getKey);\r
+ if (refs != null) {\r
+ refs.add(reference);\r
+ } else {\r
+ refs = new HashSet<UIElementReference>(4);\r
+ refs.add(reference);\r
+ GECacheKey gekey = new GECacheKey(getKey);\r
+ treeReferences.put(gekey, refs);\r
+ addKey(gekey);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> Set<UIElementReference> removeTreeReference(NodeContext context, CacheKey<T> key) {\r
+ assert(context != null);\r
+ assert(key != null);\r
+ //if (DEBUG) System.out.println("Remove tree reference " + context + " " + key);\r
+ getKey.setValues(context, key);\r
+ removeKey(getKey);\r
+ return treeReferences.remove(getKey);\r
+ }\r
+ \r
+ @Override\r
+ public boolean isShown(NodeContext context) {\r
+ return references.get(context) > 0;\r
+ }\r
+\r
+ private TObjectIntHashMap<NodeContext> references = new TObjectIntHashMap<NodeContext>();\r
+ \r
+ @Override\r
+ public void incRef(NodeContext context) {\r
+ int exist = references.get(context);\r
+ references.put(context, exist+1);\r
+ }\r
+ \r
+ @Override\r
+ public void decRef(NodeContext context) {\r
+ int exist = references.get(context);\r
+ references.put(context, exist-1);\r
+ if(exist == 1) {\r
+ references.remove(context);\r
+ }\r
+ }\r
+ \r
+ public void dispose() {\r
+ references.clear();\r
+ entries.clear();\r
+ treeReferences.clear();\r
+ keyRefs.clear();\r
+ }\r
+ \r
+ public void dispose(NodeContext context) {\r
+ Set<GECacheKey> keys = keyRefs.remove(context);\r
+ if (keys != null) {\r
+ for (GECacheKey key : keys) {\r
+ entries.remove(key);\r
+ treeReferences.remove(key);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Non-functional cache to replace actual cache when GEContext is disposed.\r
+ * \r
+ * @author mlmarko\r
+ *\r
+ */\r
+ private static class DummyCache extends GECache2 {\r
+\r
+ @Override\r
+ public <T> IGECacheEntry getEntry(NodeContext context, CacheKey<T> key) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public <T> IGECacheEntry put(NodeContext context, CacheKey<T> key,\r
+ T value) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public <T> void putTreeReference(NodeContext context, CacheKey<T> key,\r
+ UIElementReference reference) {\r
+ }\r
+\r
+ @Override\r
+ public <T> T get(NodeContext context, CacheKey<T> key) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public <T> Set<UIElementReference> getTreeReference(\r
+ NodeContext context, CacheKey<T> key) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public <T> void remove(NodeContext context, CacheKey<T> key) {\r
+ \r
+ }\r
+\r
+ @Override\r
+ public <T> Set<UIElementReference> removeTreeReference(\r
+ NodeContext context, CacheKey<T> key) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public boolean isShown(NodeContext context) {\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public void incRef(NodeContext context) {\r
+ \r
+ }\r
+\r
+ @Override\r
+ public void decRef(NodeContext context) {\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void dispose() {\r
+ super.dispose();\r
+ }\r
+ }\r
+ \r
+private class NatTableHeaderMenuConfiguration extends AbstractHeaderMenuConfiguration {\r
+ \r
+ \r
+ public NatTableHeaderMenuConfiguration(NatTable natTable) {\r
+ super(natTable);\r
+ }\r
+\r
+ @Override\r
+ protected PopupMenuBuilder createColumnHeaderMenu(NatTable natTable) {\r
+ return super.createColumnHeaderMenu(natTable)\r
+ .withHideColumnMenuItem()\r
+ .withShowAllColumnsMenuItem()\r
+ .withAutoResizeSelectedColumnsMenuItem();\r
+ }\r
+ \r
+ @Override\r
+ protected PopupMenuBuilder createCornerMenu(NatTable natTable) {\r
+ return super.createCornerMenu(natTable)\r
+ .withShowAllColumnsMenuItem();\r
+ }\r
+ @Override\r
+ protected PopupMenuBuilder createRowHeaderMenu(NatTable natTable) {\r
+ return super.createRowHeaderMenu(natTable);\r
+ }\r
+ }\r
+ \r
+ private static class RelativeAlternatingRowConfigLabelAccumulator extends AlternatingRowConfigLabelAccumulator {\r
+ \r
+ @Override\r
+ public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {\r
+ configLabels.addLabel((rowPosition % 2 == 0 ? EVEN_ROW_CONFIG_TYPE : ODD_ROW_CONFIG_TYPE));\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public Object getClicked(Object event) {\r
+ MouseEvent e = (MouseEvent)event;\r
+ final NatTable tree = (NatTable) e.getSource();\r
+ Point point = new Point(e.x, e.y);\r
+ int y = natTable.getRowPositionByY(point.y);\r
+ int x = natTable.getColumnPositionByX(point.x);\r
+ if (x < 0 | y < 0)\r
+ return null;\r
+ return list.get(y); \r
+ }\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.eclipse.jface.viewers.IPostSelectionProvider;\r
+import org.eclipse.jface.viewers.ISelection;\r
+import org.eclipse.jface.viewers.ISelectionChangedListener;\r
+import org.eclipse.jface.viewers.ISelectionProvider;\r
+import org.eclipse.jface.viewers.SelectionChangedEvent;\r
+import org.eclipse.jface.viewers.StructuredSelection;\r
+import org.eclipse.nebula.widgets.nattable.NatTable;\r
+import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate;\r
+import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;\r
+import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.events.SelectionListener;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.simantics.utils.datastructures.MapList;\r
+\r
+public class NatTableSelectionAdaptor implements ISelectionProvider, IPostSelectionProvider, ILayerListener {\r
+ NatTable natTable;\r
+ SelectionLayer selectionLayer;\r
+ GETreeData treeData;\r
+ StructuredSelection selection;\r
+ \r
+ private List<ISelectionChangedListener> selectionListeners = new ArrayList<>();\r
+ private List<ISelectionChangedListener> postSelectionListeners = new ArrayList<>();\r
+ private List<SelectionListener> selListeners = new ArrayList<>();\r
+ \r
+ public NatTableSelectionAdaptor(NatTable natTable, SelectionLayer selectionLayer, GETreeData treeData) {\r
+ this.natTable = natTable;\r
+ this.selectionLayer = selectionLayer;\r
+ this.natTable.addLayerListener(this);\r
+ this.treeData = treeData;\r
+ }\r
+ \r
+ @Override\r
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {\r
+ selectionListeners.add(listener);\r
+ }\r
+ \r
+ @Override\r
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {\r
+ selectionListeners.remove(listener);\r
+ }\r
+ \r
+ @Override\r
+ public void addPostSelectionChangedListener(ISelectionChangedListener listener) {\r
+ postSelectionListeners.add(listener);\r
+ }\r
+ \r
+ @Override\r
+ public void removePostSelectionChangedListener(ISelectionChangedListener listener) {\r
+ postSelectionListeners.remove(listener);\r
+ }\r
+ \r
+ public void addSelectionListener(SelectionListener listener) {\r
+ selListeners.add(listener);\r
+ }\r
+ \r
+ public void removeSelectionListener(SelectionListener listener) {\r
+ selListeners.remove(listener);\r
+ }\r
+ \r
+ @Override\r
+ public ISelection getSelection() {\r
+ return selection;\r
+ }\r
+ \r
+ @Override\r
+ public void setSelection(ISelection selection) {\r
+ if (!(selection instanceof StructuredSelection))\r
+ throw new IllegalArgumentException("Selection must be structured selection");\r
+ \r
+ }\r
+ \r
+ \r
+ \r
+ private List<Point> selectedCells = new ArrayList<Point>();\r
+ \r
+ @Override\r
+ public void handleLayerEvent(ILayerEvent event) {\r
+ if (event instanceof CellSelectionEvent) {\r
+ evaluateSeletedCells();\r
+ } \r
+ }\r
+ \r
+ /**\r
+ * Processes current selection to data indices.\r
+ */\r
+ private void evaluateSeletedCells() {\r
+ selectedCells.clear();\r
+ for (PositionCoordinate pc : selectionLayer.getSelectedCellPositions()) {\r
+ ILayerCell cell = pc.getLayer().getCellByPosition(pc.columnPosition, pc.rowPosition);\r
+ selectedCells.add(new Point(cell.getColumnIndex(), cell.getRowIndex()));\r
+ }\r
+ MapList<Integer, Integer> rowMap = new MapList<>();\r
+ for (Point p : selectedCells) {\r
+ rowMap.add(p.y, p.x);\r
+ }\r
+ List<RowSelectionItem> selectionItems = new ArrayList<>(rowMap.getKeySize());\r
+ for (Integer row : rowMap.getKeys()) {\r
+ List<Integer> cols = rowMap.getValues(row);\r
+ int col[] = new int[cols.size()];\r
+ for (int i = 0; i < col.length; i++)\r
+ col[i] = cols.get(i);\r
+ selectionItems.add(new RowSelectionItem(treeData.getDataAtIndex(row), row, col));\r
+ }\r
+ this.selection = new StructuredSelection(selectionItems);\r
+ fireEvents();\r
+ }\r
+ \r
+ private void fireEvents() {\r
+ for (ISelectionChangedListener l : selectionListeners) {\r
+ l.selectionChanged(new SelectionChangedEvent(this, selection));\r
+ }\r
+ for (ISelectionChangedListener l : postSelectionListeners) {\r
+ l.selectionChanged(new SelectionChangedEvent(this, selection));\r
+ }\r
+ Event evt = new Event();\r
+ evt.widget = natTable;\r
+ evt.display = natTable.getDisplay();\r
+ evt.data = selection;\r
+ for (SelectionListener l : selListeners) {\r
+ SelectionEvent sel = new SelectionEvent(evt);\r
+ l.widgetSelected(sel);\r
+ }\r
+ }\r
+ \r
+ public List<Point> getSelectedCells() {\r
+ return selectedCells;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.simantics.browsing.ui.swt.AdaptableHintContext;\r
+import org.simantics.db.layer0.SelectionHints;\r
+\r
+public class RowSelectionItem extends AdaptableHintContext {\r
+ int rowIndex;\r
+ int columnIndex[];\r
+ TreeNode item;\r
+ \r
+ public RowSelectionItem(TreeNode item, int rowIndex, int columnIndex[]) {\r
+ super(SelectionHints.KEY_MAIN);\r
+ this.item = item;\r
+ this.rowIndex = rowIndex;\r
+ this.columnIndex = columnIndex;\r
+ setHint(SelectionHints.KEY_MAIN,item);\r
+ }\r
+ \r
+ public TreeNode getItem() {\r
+ return item;\r
+ }\r
+ public int getRowIndex() {\r
+ return rowIndex;\r
+ }\r
+ \r
+ public int[] getColumnIndex() {\r
+ return columnIndex;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import org.eclipse.nebula.widgets.nattable.NatTable;\r
+import org.eclipse.nebula.widgets.nattable.layer.LabelStack;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;\r
+import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;\r
+import org.eclipse.swt.events.MouseEvent;\r
+\r
+public class SelectedCellEditorMatcher extends CellEditorMouseEventMatcher{\r
+\r
+ public SelectedCellEditorMatcher( String regionLabel) {\r
+ super(regionLabel);\r
+ }\r
+ \r
+ ILayerCell previous;\r
+ int previousTime = 0;\r
+ @Override\r
+ public boolean matches(NatTable natTable, MouseEvent event, LabelStack regionLabels) {\r
+ if (super.matches(natTable, event, regionLabels)) {\r
+ int px = natTable.getColumnPositionByX(event.x);\r
+ int py = natTable.getRowPositionByY(event.y);\r
+ ILayerCell cell = natTable.getCellByPosition(px,py);\r
+ int time = event.time;\r
+ if (previous != null &&\r
+ cell.getColumnIndex() == previous.getColumnIndex() &&\r
+ cell.getRowIndex() == previous.getRowIndex() &&\r
+ time - previousTime > event.display.getDoubleClickTime())\r
+ return true;\r
+ previous = cell;\r
+ previousTime = time;\r
+ }\r
+ return false;\r
+ }\r
+ \r
+ \r
+ \r
+ \r
+\r
+}\r
--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.IAdaptable;\r
+import org.eclipse.jface.resource.ColorDescriptor;\r
+import org.eclipse.jface.resource.FontDescriptor;\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;\r
+import org.eclipse.nebula.widgets.nattable.style.Style;\r
+import org.eclipse.swt.graphics.Color;\r
+import org.eclipse.swt.graphics.Font;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.simantics.browsing.ui.BuiltinKeys;\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.browsing.ui.common.internal.GENodeQueryManager;\r
+import org.simantics.browsing.ui.content.ImageDecorator;\r
+import org.simantics.browsing.ui.content.Imager;\r
+import org.simantics.browsing.ui.content.LabelDecorator;\r
+import org.simantics.browsing.ui.content.Labeler;\r
+import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GECache2;\r
+import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GeViewerContext;\r
+import org.simantics.browsing.ui.swt.ViewerRowReference;\r
+import org.simantics.utils.datastructures.BijectionMap;\r
+\r
+public class TreeNode implements IAdaptable {\r
+ private static boolean DEBUG = false;\r
+ \r
+ private NodeContext context;\r
+ GENodeQueryManager manager;\r
+ GeViewerContext explorerContext;\r
+ \r
+ TreeNode parent;\r
+ List<TreeNode> children = new ArrayList<TreeNode>();\r
+ boolean expanded;\r
+ \r
+ public TreeNode(NodeContext context, GeViewerContext explorerContext) {\r
+ this.context = context;\r
+ this.explorerContext = explorerContext;\r
+ this.expanded = false;\r
+ manager = new GENodeQueryManager(explorerContext, null, null, ViewerRowReference.create(this));\r
+ explorerContext.getContextToNodeMap().add(context, this);\r
+ }\r
+ \r
+ int getDepth() {\r
+ if (parent == null)\r
+ return 0;\r
+ return parent.getDepth() + 1;\r
+ }\r
+ \r
+ int listIndex;\r
+ \r
+ public int getListIndex() {\r
+ return listIndex;\r
+ }\r
+ \r
+ public void setListIndex(int listIndex) {\r
+ this.listIndex = listIndex;\r
+ }\r
+ \r
+ List<TreeNode> getChildren() {\r
+ return children;\r
+ }\r
+ \r
+ public TreeNode getParent() {\r
+ return parent;\r
+ }\r
+ \r
+ public void setExpanded(boolean expanded) {\r
+ this.expanded = expanded;\r
+ }\r
+ \r
+ public boolean isExpanded() {\r
+ return expanded;\r
+ }\r
+ \r
+ public NodeContext getContext() {\r
+ return context;\r
+ }\r
+ \r
+ private Labeler labeler;\r
+ private Imager imager;\r
+ Collection<LabelDecorator> labelDecorators;\r
+ Collection<ImageDecorator> imageDecorators;\r
+ \r
+ Map<String, String> labels;\r
+ Map<String, String> runtimeLabels;\r
+ \r
+ public String getValueString(int column) {\r
+ if (column == 0) {\r
+ initData();\r
+ }\r
+ if (labeler != null) {\r
+ String key = explorerContext.getGe().getColumns()[column].getKey();\r
+ String s = null;\r
+ if (runtimeLabels != null)\r
+ s = runtimeLabels.get(key);\r
+ if (s == null)\r
+ s = labels.get(key);\r
+ if (labelDecorators != null && !labelDecorators.isEmpty()) {\r
+ int index = 0;\r
+ for (LabelDecorator ld : labelDecorators) {\r
+ String ds = ld.decorateLabel(s, key, index);\r
+ if (ds != null)\r
+ s = ds;\r
+ }\r
+ }\r
+ return s;\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ public Image getImage(int column) {\r
+ String key = explorerContext.getGe().getColumns()[column].getKey();\r
+ if (imager != null) {\r
+ Object descOrImage = null;\r
+ boolean hasUncachedImages = false;\r
+\r
+ ImageDescriptor desc = imager.getImage(key);\r
+ if (desc != null) {\r
+ int index = 0;\r
+ // Attempt to decorate the label\r
+ if (!imageDecorators.isEmpty()) {\r
+ for (ImageDecorator id : imageDecorators) {\r
+ ImageDescriptor ds = id.decorateImage(desc, key, index);\r
+ if (ds != null)\r
+ desc = ds;\r
+ }\r
+ }\r
+\r
+ // Try resolving only cached images here and now\r
+ Object img = explorerContext.getGe().localResourceManager.find(desc);\r
+ if (img == null)\r
+ img = explorerContext.getGe().resourceManager.find(desc);\r
+\r
+ descOrImage = img != null ? img : desc;\r
+ hasUncachedImages |= img == null;\r
+ }\r
+\r
+ if (!hasUncachedImages) {\r
+ return (Image) descOrImage;\r
+ } else {\r
+ // Schedule loading to another thread to refrain from\r
+ // blocking\r
+ // the UI with database operations.\r
+ explorerContext.getGe().queueImageTask(this, new ImageTask(this, descOrImage));\r
+ return null;\r
+ }\r
+ } else {\r
+ return null;\r
+ }\r
+ }\r
+ \r
+ public void getStyle(int column, Style style) {\r
+ String key = explorerContext.getGe().getColumns()[column].getKey();\r
+ FontDescriptor font = explorerContext.getGe().originalFont;\r
+ ColorDescriptor bg = explorerContext.getGe().originalBackground;\r
+ ColorDescriptor fg = explorerContext.getGe().originalForeground;\r
+ \r
+ // Attempt to decorate the label\r
+ if (labelDecorators != null && !labelDecorators.isEmpty()) {\r
+ int index = 0;\r
+ for (LabelDecorator ld : labelDecorators) {\r
+\r
+ FontDescriptor dfont = ld.decorateFont(font, key, index);\r
+ if (dfont != null)\r
+ font = dfont;\r
+\r
+ ColorDescriptor dbg = ld.decorateBackground(bg, key, index);\r
+ if (dbg != null)\r
+ bg = dbg;\r
+\r
+ ColorDescriptor dfg = ld.decorateForeground(fg, key, index);\r
+ if (dfg != null)\r
+ fg = dfg;\r
+ }\r
+ }\r
+\r
+ if (font != explorerContext.getGe().originalFont) {\r
+ // System.out.println("set font: " + index + ": " +\r
+ // font);\r
+ style.setAttributeValue(CellStyleAttributes.FONT,(Font) explorerContext.getGe().localResourceManager.get(font));\r
+ } else {\r
+ style.setAttributeValue(CellStyleAttributes.FONT,(Font) (explorerContext.getGe().originalFont != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalFont) : null));\r
+ }\r
+ if (bg != explorerContext.getGe().originalBackground)\r
+ style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(bg));\r
+ else\r
+ style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) (explorerContext.getGe().originalBackground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalBackground) : null));\r
+ if (fg != explorerContext.getGe().originalForeground)\r
+ style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(fg));\r
+ else\r
+ style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) (explorerContext.getGe().originalForeground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalForeground) : null));\r
+\r
+ }\r
+ \r
+ private void initData() {\r
+ labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER);\r
+ imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER);\r
+ labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS);\r
+ imageDecorators = manager.query(context, BuiltinKeys.IMAGE_DECORATORS);\r
+ \r
+ if (labeler != null) {\r
+ labels = labeler.getLabels();\r
+ runtimeLabels = labeler.getRuntimeLabels();\r
+ } else {\r
+ labels = null;\r
+ runtimeLabels = null;\r
+ }\r
+ }\r
+ \r
+ public TreeNode addChild(NodeContext context, GeViewerContext explorerContext) {\r
+ TreeNode child = new TreeNode(context, explorerContext);\r
+ child.parent = this;\r
+ children.add(child);\r
+ if (DEBUG) System.out.println("Add " + this + " -> " + child);\r
+ return child;\r
+ }\r
+ \r
+ public TreeNode addChild(int index, NodeContext context,GeViewerContext explorerContext) {\r
+ \r
+ TreeNode child = new TreeNode(context, explorerContext);\r
+ child.parent = this;\r
+ children.add(index,child);\r
+ if (DEBUG) System.out.println("Add " + this + " -> " + child + " at " + index);\r
+ return child;\r
+ }\r
+ \r
+ public TreeNode setChild(int index, NodeContext context, GeViewerContext explorerContext) {\r
+ \r
+ TreeNode child = new TreeNode(context, explorerContext);\r
+ child.parent = this;\r
+ children.set(index,child);\r
+ if (DEBUG) System.out.println("Set " + this + " -> " + child + " at " + index);\r
+ return child;\r
+ }\r
+ \r
+ public void dispose() {\r
+ if (parent != null)\r
+ parent.children.remove(this);\r
+ dispose2();\r
+ }\r
+ \r
+ public void dispose2() {\r
+ if (DEBUG) System.out.println("dispose " + this);\r
+ parent = null;\r
+ for (TreeNode n : children) {\r
+ n.dispose2();\r
+ }\r
+ clearCache();\r
+ children.clear();\r
+ explorerContext.getContextToNodeMap().remove(context, this);\r
+ context = null;\r
+ explorerContext = null;\r
+ manager.dispose();\r
+ manager = null; \r
+ }\r
+ \r
+ private void clearCache() {\r
+ if (explorerContext != null) {\r
+ GECache2 cache = explorerContext.cache;\r
+ \r
+ if (cache != null) {\r
+ cache.dispose(context);\r
+ }\r
+ }\r
+ }\r
+ \r
+ public boolean updateChildren() {\r
+ if (context == null)\r
+ throw new IllegalStateException("Node is disposed.");\r
+ \r
+ NodeContext[] childContexts = manager.query(context, BuiltinKeys.FINAL_CHILDREN);\r
+ \r
+ if (DEBUG) System.out.println("updateChildren " + childContexts.length + " " + this);\r
+ \r
+ \r
+ boolean modified = false;\r
+ synchronized (children) {\r
+ \r
+ int oldCount = children.size();\r
+ BijectionMap<Integer, Integer> indexes = new BijectionMap<Integer, Integer>();\r
+ Set<Integer> mapped = new HashSet<Integer>();\r
+ boolean reorder = false;\r
+ // locate matching pairs form old and new children\r
+ for (int i = 0; i < oldCount; i++) {\r
+ NodeContext oldCtx = children.get(i).context;\r
+ for (int j = 0; j <childContexts.length; j++) {\r
+ if (mapped.contains(j))\r
+ continue;\r
+ if (oldCtx.equals(childContexts[j])) {\r
+ indexes.map(i, j);\r
+ mapped.add(j);\r
+ if (i != j)\r
+ reorder = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ // update children if required\r
+ if (childContexts.length != oldCount || reorder || childContexts.length != indexes.size()) {\r
+ modified = true;\r
+ List<TreeNode> oldChildren = new ArrayList<TreeNode>(oldCount);\r
+ oldChildren.addAll(children);\r
+ if (childContexts.length >= oldCount) {\r
+ for (int i = 0; i < oldCount; i++) {\r
+ Integer oldIndex = indexes.getLeft(i);\r
+ if (oldIndex == null) {\r
+ setChild(i, childContexts[i], explorerContext);\r
+ } else {\r
+ TreeNode n = oldChildren.get(oldIndex);\r
+ children.set(i, n);\r
+ }\r
+ \r
+ }\r
+ for (int i = oldCount; i < childContexts.length; i++) {\r
+ addChild(childContexts[i], explorerContext);\r
+ }\r
+ } else {\r
+ for (int i = 0; i < childContexts.length; i++) {\r
+ Integer oldIndex = indexes.getLeft(i);\r
+ if (oldIndex == null) {\r
+ setChild(i, childContexts[i], explorerContext);\r
+ } else {\r
+ TreeNode n = oldChildren.get(oldIndex);\r
+ children.set(i, n);\r
+ }\r
+ }\r
+ for (int i = oldCount -1; i >= childContexts.length; i--) {\r
+ children.remove(i);\r
+ }\r
+ }\r
+ for (int i = 0; i < oldChildren.size(); i++) {\r
+ if (!indexes.containsLeft(i)) {\r
+ oldChildren.get(i).dispose2();\r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+ }\r
+ return modified;\r
+ }\r
+ \r
+ public boolean isDisposed() {\r
+ return context == null;\r
+ }\r
+ \r
+ public GENodeQueryManager getManager() {\r
+ return manager;\r
+ }\r
+ \r
+ @SuppressWarnings("rawtypes")\r
+ @Override\r
+ public Object getAdapter(Class adapter) {\r
+ if (adapter == NodeContext.class)\r
+ return context;\r
+ \r
+ return context.getAdapter(adapter);\r
+ }\r
+\r
+}\r
--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2012 Original authors and others.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ * \r
+ * Contributors:\r
+ * Original authors and others - initial API and implementation\r
+ ******************************************************************************/\r
+package org.simantics.browsing.ui.nattable.override;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+\r
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;\r
+import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;\r
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.VisualRefreshEvent;\r
+\r
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;\r
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;\r
+\r
+/**\r
+ * AbstractRowHideShowLayer implementation with FastUtils hashmaps.\r
+ * \r
+ * @see org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer\r
+ * \r
+ * @author MLMARKO\r
+ *\r
+ */\r
+public abstract class AbstractRowHideShowLayer2 extends AbstractLayerTransform implements IUniqueIndexLayer {\r
+\r
+ private Int2IntOpenHashMap cachedVisibleRowIndexOrder;\r
+ private Int2IntOpenHashMap cachedVisibleRowPositionOrder;\r
+ \r
+ private Int2IntOpenHashMap cachedHiddenRowIndexToPositionMap;\r
+\r
+ protected final Int2IntOpenHashMap startYCache = new Int2IntOpenHashMap(); \r
+ \r
+ \r
+ public AbstractRowHideShowLayer2(IUniqueIndexLayer underlyingLayer) {\r
+ super(underlyingLayer);\r
+ }\r
+ \r
+ @Override\r
+ public void handleLayerEvent(ILayerEvent event) {\r
+ if (event instanceof IStructuralChangeEvent) {\r
+ IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;\r
+ if (structuralChangeEvent.isVerticalStructureChanged()) {\r
+ // vertical structure has changed, update cached row information\r
+ invalidateCache();\r
+ }\r
+ } else if (event instanceof VisualRefreshEvent) {\r
+ // visual change, e.g. font change, the startYCache needs to be\r
+ // cleared in order to re-render correctly\r
+ this.startYCache.clear();\r
+ }\r
+ super.handleLayerEvent(event);\r
+ }\r
+\r
+ // Horizontal features\r
+\r
+ // Columns\r
+\r
+ @Override\r
+ public int getColumnPositionByIndex(int columnIndex) {\r
+ return ((IUniqueIndexLayer) getUnderlyingLayer()).getColumnPositionByIndex(columnIndex);\r
+ }\r
+\r
+ // Vertical features\r
+\r
+ // Rows\r
+\r
+ @Override\r
+ public int getRowCount() {\r
+ return getCachedVisibleRowIndexes().size();\r
+ }\r
+\r
+ @Override\r
+ public int getRowIndexByPosition(int rowPosition) {\r
+ if (rowPosition < 0 || rowPosition >= getRowCount()) {\r
+ return -1;\r
+ }\r
+ return getCachedVisibleRowPositons().get(rowPosition);\r
+ }\r
+\r
+ @Override\r
+ public int getRowPositionByIndex(int rowIndex) {\r
+ return getCachedVisibleRowIndexes().get(rowIndex);\r
+ }\r
+\r
+ public Collection<Integer> getRowPositionsByIndexes(Collection<Integer> rowIndexes) {\r
+ IntOpenHashSet rowPositions = new IntOpenHashSet();\r
+ for (int rowIndex : rowIndexes) {\r
+ rowPositions.add(getRowPositionByIndex(rowIndex));\r
+ }\r
+ return rowPositions;\r
+ }\r
+\r
+ @Override\r
+ public int localToUnderlyingRowPosition(int localRowPosition) {\r
+ int rowIndex = getRowIndexByPosition(localRowPosition);\r
+ return ((IUniqueIndexLayer) getUnderlyingLayer()).getRowPositionByIndex(rowIndex);\r
+ }\r
+\r
+ @Override\r
+ public int underlyingToLocalRowPosition(ILayer sourceUnderlyingLayer, int underlyingRowPosition) {\r
+ int rowIndex = getUnderlyingLayer().getRowIndexByPosition(underlyingRowPosition);\r
+ int rowPosition = getRowPositionByIndex(rowIndex);\r
+ if (rowPosition >= 0) {\r
+ return rowPosition;\r
+ } else {\r
+ if (this.cachedHiddenRowIndexToPositionMap.containsKey(rowIndex)) {\r
+ return this.cachedHiddenRowIndexToPositionMap.get(rowIndex);\r
+ } else {\r
+ return -1;\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Collection<Range> underlyingToLocalRowPositions(\r
+ ILayer sourceUnderlyingLayer, Collection<Range> underlyingRowPositionRanges) {\r
+ Collection<Range> localRowPositionRanges = new ArrayList<Range>();\r
+\r
+ for (Range underlyingRowPositionRange : underlyingRowPositionRanges) {\r
+ int startRowPosition = getAdjustedUnderlyingToLocalStartPosition(\r
+ sourceUnderlyingLayer,\r
+ underlyingRowPositionRange.start,\r
+ underlyingRowPositionRange.end);\r
+ int endRowPosition = getAdjustedUnderlyingToLocalEndPosition(\r
+ sourceUnderlyingLayer,\r
+ underlyingRowPositionRange.end,\r
+ underlyingRowPositionRange.start);\r
+\r
+ // teichstaedt: fixes the problem that ranges where added even if\r
+ // the corresponding startPosition weren't found in the underlying\r
+ // layer. Without that fix a bunch of ranges of kind Range [-1, 180]\r
+ // which causes strange behaviour in Freeze- and other Layers were\r
+ // returned.\r
+ if (startRowPosition > -1) {\r
+ localRowPositionRanges.add(new Range(startRowPosition, endRowPosition));\r
+ }\r
+ }\r
+\r
+ return localRowPositionRanges;\r
+ }\r
+\r
+ private int getAdjustedUnderlyingToLocalStartPosition(\r
+ ILayer sourceUnderlyingLayer,\r
+ int startUnderlyingPosition,\r
+ int endUnderlyingPosition) {\r
+ int localStartRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, startUnderlyingPosition);\r
+ int offset = 0;\r
+ while (localStartRowPosition < 0\r
+ && (startUnderlyingPosition + offset < endUnderlyingPosition)) {\r
+ localStartRowPosition =\r
+ underlyingToLocalRowPosition(sourceUnderlyingLayer, startUnderlyingPosition + offset++);\r
+ }\r
+ return localStartRowPosition;\r
+ }\r
+\r
+ private int getAdjustedUnderlyingToLocalEndPosition(\r
+ ILayer sourceUnderlyingLayer,\r
+ int endUnderlyingPosition,\r
+ int startUnderlyingPosition) {\r
+ int localEndRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, endUnderlyingPosition - 1);\r
+ int offset = 0;\r
+ while (localEndRowPosition < 0\r
+ && (endUnderlyingPosition - offset > startUnderlyingPosition)) {\r
+ localEndRowPosition =\r
+ underlyingToLocalRowPosition(sourceUnderlyingLayer, endUnderlyingPosition - offset++);\r
+ }\r
+ return localEndRowPosition + 1;\r
+ }\r
+\r
+ // Height\r
+\r
+ @Override\r
+ public int getHeight() {\r
+ int lastRowPosition = getRowCount() - 1;\r
+ return getStartYOfRowPosition(lastRowPosition) + getRowHeightByPosition(lastRowPosition);\r
+ }\r
+\r
+ // Y\r
+\r
+ @Override\r
+ public int getRowPositionByY(int y) {\r
+ return LayerUtil.getRowPositionByY(this, y);\r
+ }\r
+\r
+ @Override\r
+ public int getStartYOfRowPosition(int localRowPosition) {\r
+ int index = this.startYCache.get(localRowPosition);\r
+ if (index >= 0)\r
+ return index;\r
+\r
+ IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();\r
+ int underlyingPosition = localToUnderlyingRowPosition(localRowPosition);\r
+ if (underlyingPosition < 0) {\r
+ return -1;\r
+ }\r
+ int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition);\r
+ if (underlyingStartY < 0) {\r
+ return -1;\r
+ }\r
+\r
+ for (Integer hiddenIndex : getHiddenRowIndexes()) {\r
+ int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);\r
+ // if the hidden position is -1, it is hidden in the underlying\r
+ // layertherefore the underlying layer should handle the positioning\r
+ if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) {\r
+ underlyingStartY -= underlyingLayer.getRowHeightByPosition(hiddenPosition);\r
+ }\r
+ }\r
+\r
+ this.startYCache.put(localRowPosition, underlyingStartY);\r
+ return underlyingStartY;\r
+ }\r
+\r
+ // Hide/show\r
+\r
+ /**\r
+ * Will check if the row at the specified index is hidden or not. Checks\r
+ * this layer and also the sublayers for the visibility.\r
+ *\r
+ * @param rowIndex\r
+ * The row index of the row whose visibility state should be\r
+ * checked.\r
+ * @return <code>true</code> if the row at the specified index is hidden,\r
+ * <code>false</code> if it is visible.\r
+ */\r
+ public abstract boolean isRowIndexHidden(int rowIndex);\r
+\r
+ /**\r
+ * Will collect and return all indexes of the rows that are hidden in this\r
+ * layer. Note: It is not intended that it also collects the row indexes of\r
+ * underlying layers. This would cause issues on calculating positions as\r
+ * every layer is responsible for those calculations itself.\r
+ *\r
+ * @return Collection of all row indexes that are hidden in this layer.\r
+ */\r
+ public abstract Collection<Integer> getHiddenRowIndexes();\r
+\r
+ // Cache\r
+\r
+ /**\r
+ * Invalidate the cache to ensure that information is rebuild.\r
+ */\r
+ protected void invalidateCache() {\r
+ this.cachedVisibleRowIndexOrder = null;\r
+ this.cachedVisibleRowPositionOrder = null;\r
+ this.cachedHiddenRowIndexToPositionMap = null;\r
+ this.startYCache.clear();\r
+ }\r
+\r
+ private Int2IntOpenHashMap getCachedVisibleRowIndexes() {\r
+ if (this.cachedVisibleRowIndexOrder == null) {\r
+ cacheVisibleRowIndexes();\r
+ }\r
+ return this.cachedVisibleRowIndexOrder;\r
+ }\r
+\r
+ private Int2IntOpenHashMap getCachedVisibleRowPositons() {\r
+ if (this.cachedVisibleRowPositionOrder == null) {\r
+ cacheVisibleRowIndexes();\r
+ }\r
+ return this.cachedVisibleRowPositionOrder;\r
+ }\r
+\r
+ protected void cacheVisibleRowIndexes() {\r
+ this.cachedVisibleRowIndexOrder = new Int2IntOpenHashMap();\r
+ this.cachedVisibleRowPositionOrder = new Int2IntOpenHashMap();\r
+ this.cachedHiddenRowIndexToPositionMap = new Int2IntOpenHashMap();\r
+ this.startYCache.clear();\r
+ \r
+ cachedVisibleRowPositionOrder.defaultReturnValue(-1);\r
+ cachedVisibleRowIndexOrder.defaultReturnValue(-1);\r
+ cachedHiddenRowIndexToPositionMap.defaultReturnValue(-1);\r
+ startYCache.defaultReturnValue(-1);\r
+\r
+ ILayer underlyingLayer = getUnderlyingLayer();\r
+ int rowPosition = 0;\r
+ for (int parentRowPosition = 0; parentRowPosition < underlyingLayer.getRowCount(); parentRowPosition++) {\r
+ int rowIndex = underlyingLayer.getRowIndexByPosition(parentRowPosition);\r
+\r
+ if (!isRowIndexHidden(rowIndex)) {\r
+ this.cachedVisibleRowIndexOrder.put(rowIndex, rowPosition);\r
+ this.cachedVisibleRowPositionOrder.put(rowPosition, rowIndex);\r
+ rowPosition++;\r
+ } else {\r
+ this.cachedHiddenRowIndexToPositionMap.put(rowIndex, rowPosition);\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 Original authors 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:
+ * Original authors and others - initial API and implementation
+ ******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataToClipboardCommand;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+import org.eclipse.nebula.widgets.nattable.serializing.ISerializer;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * CopyDataToClipboardSerializer implementation that limits the amount of data that is copied.
+ * Copying too much data will result in OutOfMemoryError.
+ *
+ * @see org.eclipse.nebula.widgets.nattable.copy.serializing.CopyDataToClipboardSerializer
+ *
+ * @author MLMARKO
+ *
+ */
+public class CopyDataToClipboardSerializer implements ISerializer {
+
+ public static final int COPY_THRESHOLD = 500000; // threshold for preventing OOM.
+
+ private final ILayerCell[][] copiedCells;
+ private final CopyDataToClipboardCommand command;
+
+ public CopyDataToClipboardSerializer(ILayerCell[][] copiedCells,
+ CopyDataToClipboardCommand command) {
+ this.copiedCells = copiedCells;
+ this.command = command;
+ }
+
+ @Override
+ public void serialize() {
+ final String cellDelimeter = this.command.getCellDelimeter();
+ final String rowDelimeter = this.command.getRowDelimeter();
+
+ final TextTransfer textTransfer = TextTransfer.getInstance();
+ final StringBuilder textData = new StringBuilder();
+ int count = 0;
+ for (ILayerCell[] cells : copiedCells) {
+ count+= cells.length;
+ }
+ if (count <= COPY_THRESHOLD) {
+ int currentRow = 0;
+ for (ILayerCell[] cells : this.copiedCells) {
+ int currentCell = 0;
+ for (ILayerCell cell : cells) {
+ final String delimeter = ++currentCell < cells.length ? cellDelimeter
+ : ""; //$NON-NLS-1$
+ if (cell != null) {
+ textData.append(getTextForCell(cell) + delimeter);
+ } else {
+ textData.append(delimeter);
+ }
+ }
+ if (++currentRow < this.copiedCells.length) {
+ textData.append(rowDelimeter);
+ }
+ }
+ } else {
+ textData.append("Too many cells copied (" + count + ")");
+ }
+ if (textData.length() > 0) {
+ final Clipboard clipboard = new Clipboard(Display.getDefault());
+ try {
+ clipboard.setContents(new Object[] { textData.toString() },
+ new Transfer[] { textTransfer });
+ } finally {
+ clipboard.dispose();
+ }
+ }
+ }
+
+ protected String getTextForCell(ILayerCell cell) {
+ return String.valueOf(cell.getDataValue());
+ }
+
+ final protected CopyDataToClipboardCommand getCommand() {
+ return this.command;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 Original authors 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:
+ * Original authors and others - initial API and implementation
+ ******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataToClipboardCommand;
+import org.eclipse.nebula.widgets.nattable.layer.cell.CellDisplayConversionUtils;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+
+public class CopyFormattedTextToClipboardSerializer extends
+ CopyDataToClipboardSerializer {
+
+ public CopyFormattedTextToClipboardSerializer(ILayerCell[][] copiedCells,
+ CopyDataToClipboardCommand command) {
+ super(copiedCells, command);
+ }
+
+ @Override
+ protected String getTextForCell(ILayerCell cell) {
+ return CellDisplayConversionUtils.convertDataType(cell, getCommand()
+ .getConfigRegistry());
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) Sep 7, 2012 Edwin Park 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:
+ * Edwin Park - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.IConfiguration;
+import org.eclipse.nebula.widgets.nattable.export.ExportConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+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.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.tree.action.TreeExpandCollapseAction;
+import org.eclipse.nebula.widgets.nattable.tree.config.TreeExportFormatter;
+import org.eclipse.nebula.widgets.nattable.tree.painter.TreeImagePainter;
+import org.eclipse.nebula.widgets.nattable.ui.action.NoOpMouseAction;
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.CellPainterMouseEventMatcher;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
+
+/**
+ * @author Edwin Park
+ *
+ */
+public class DefaultTreeLayerConfiguration2 implements IConfiguration {
+
+
+ private TreeLayer2 treeLayer;
+
+ /**
+ *
+ */
+ public DefaultTreeLayerConfiguration2(TreeLayer2 treeLayer) {
+ this.treeLayer = treeLayer;
+ }
+
+ @Override
+ public void configureLayer(ILayer layer) {}
+
+ @Override
+ public void configureRegistry(IConfigRegistry configRegistry) {
+ configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE,
+ new Style() {
+ {
+ setAttributeValue(
+ CellStyleAttributes.HORIZONTAL_ALIGNMENT,
+ HorizontalAlignmentEnum.LEFT);
+ }
+ }, DisplayMode.NORMAL, TreeLayer.TREE_COLUMN_CELL);
+ configRegistry.registerConfigAttribute(
+ ExportConfigAttributes.EXPORT_FORMATTER,
+ new TreeExportFormatter(this.treeLayer.getModel()),
+ DisplayMode.NORMAL, TreeLayer.TREE_COLUMN_CELL);
+ }
+
+ @Override
+ public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
+ TreeExpandCollapseAction treeExpandCollapseAction = new TreeExpandCollapseAction();
+ CellPainterMouseEventMatcher treeImagePainterMouseEventMatcher = new CellPainterMouseEventMatcher(
+ GridRegion.BODY, MouseEventMatcher.LEFT_BUTTON,
+ TreeImagePainter.class);
+
+ uiBindingRegistry.registerFirstSingleClickBinding(
+ treeImagePainterMouseEventMatcher, treeExpandCollapseAction);
+
+ // Obscure any mouse down bindings for this image painter
+ uiBindingRegistry.registerFirstMouseDownBinding(
+ treeImagePainterMouseEventMatcher, new NoOpMouseAction());
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2013 Dirk Fauth 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:
+ * Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeCollapseAllCommand;
+
+/**
+ * Command handler for the TreeCollapseAllCommand.
+ * <p>
+ * Will search over the whole tree structure in the associated TreeLayer to
+ * identify collapsible nodes and collapse them one after the other.
+ *
+ * @author Dirk Fauth
+ *
+ * @see TreeLayer
+ * @see TreeCollapseAllCommand
+ */
+public class TreeCollapseAllCommandHandler implements
+ ILayerCommandHandler<TreeCollapseAllCommand> {
+
+ /**
+ * The TreeLayer to which this command handler is connected.
+ */
+ private final TreeLayer2 treeLayer;
+
+ /**
+ *
+ * @param treeLayer
+ * The TreeLayer to which this command handler should be
+ * connected.
+ */
+ public TreeCollapseAllCommandHandler(TreeLayer2 treeLayer) {
+ this.treeLayer = treeLayer;
+ }
+
+ @Override
+ public boolean doCommand(ILayer targetLayer, TreeCollapseAllCommand command) {
+ this.treeLayer.collapseAll();
+ return true;
+ }
+
+ @Override
+ public Class<TreeCollapseAllCommand> getCommandClass() {
+ return TreeCollapseAllCommand.class;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2013 Dirk Fauth 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:
+ * Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeExpandAllCommand;
+
+/**
+ * Command handler for the TreeExpandAllCommand.
+ * <p>
+ * Will search over the whole tree structure in the associated TreeLayer to
+ * identify expandable nodes and expand them one after the other.
+ *
+ * @author Dirk Fauth
+ *
+ * @see TreeLayer
+ * @see TreeExpandAllCommand
+ *
+ */
+public class TreeExpandAllCommandHandler implements
+ ILayerCommandHandler<TreeExpandAllCommand> {
+
+ /**
+ * The TreeLayer to which this command handler is connected.
+ */
+ private final TreeLayer2 treeLayer;
+
+ /**
+ *
+ * @param treeLayer
+ * The TreeLayer to which this command handler should be
+ * connected.
+ */
+ public TreeExpandAllCommandHandler(TreeLayer2 treeLayer) {
+ this.treeLayer = treeLayer;
+ }
+
+ @Override
+ public boolean doCommand(ILayer targetLayer, TreeExpandAllCommand command) {
+ this.treeLayer.expandAll();
+ return true;
+ }
+
+ @Override
+ public Class<TreeExpandAllCommand> getCommandClass() {
+ return TreeExpandAllCommand.class;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 Original authors 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:
+ * Original authors and others - initial API and implementation
+ ******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeExpandCollapseCommand;
+
+public class TreeExpandCollapseCommandHandler extends
+ AbstractLayerCommandHandler<TreeExpandCollapseCommand> {
+
+ private final TreeLayer2 treeLayer;
+
+ public TreeExpandCollapseCommandHandler(TreeLayer2 treeLayer) {
+ this.treeLayer = treeLayer;
+ }
+
+ @Override
+ public Class<TreeExpandCollapseCommand> getCommandClass() {
+ return TreeExpandCollapseCommand.class;
+ }
+
+ @Override
+ protected boolean doCommand(TreeExpandCollapseCommand command) {
+ int parentIndex = command.getParentIndex();
+ this.treeLayer.expandOrCollapseIndex(parentIndex);
+ return true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2014 Dirk Fauth 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:
+ * Dirk Fauth <dirk.fauth@ggooglemail.com> - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.nattable.override;
+
+import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
+import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeExpandToLevelCommand;
+
+/**
+ * Command handler for the TreeExpandLevelCommand.
+ * <p>
+ * Will search over the whole tree structure in the associated TreeLayer to
+ * identify expandable nodes and expand them one after the other.
+ *
+ * @see TreeLayer
+ * @see TreeExpandToLevelCommand
+ *
+ */
+public class TreeExpandToLevelCommandHandler extends AbstractLayerCommandHandler<TreeExpandToLevelCommand> {
+
+ /**
+ * The TreeLayer to which this command handler is connected.
+ */
+ private final TreeLayer2 treeLayer;
+
+ /**
+ *
+ * @param treeLayer
+ * The TreeLayer to which this command handler should be
+ * connected.
+ */
+ public TreeExpandToLevelCommandHandler(TreeLayer2 treeLayer) {
+ this.treeLayer = treeLayer;
+ }
+
+ @Override
+ public boolean doCommand(TreeExpandToLevelCommand command) {
+ if (command.getParentIndex() == null) {
+ this.treeLayer.expandAllToLevel(command.getLevel());
+ }
+ else {
+ this.treeLayer.expandTreeRowToLevel(command.getParentIndex(), command.getLevel());
+ }
+ return true;
+ }
+
+ @Override
+ public Class<TreeExpandToLevelCommand> getCommandClass() {
+ return TreeExpandToLevelCommand.class;
+ }
+
+}
--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2012 Original authors and others.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ * \r
+ * Contributors:\r
+ * Original authors and others - initial API and implementation\r
+ ******************************************************************************/\r
+package org.simantics.browsing.ui.nattable.override;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+\r
+import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;\r
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowHideCommand;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.LabelStack;\r
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper;\r
+import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;\r
+import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;\r
+import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;\r
+import org.eclipse.nebula.widgets.nattable.tree.config.DefaultTreeLayerConfiguration;\r
+import org.eclipse.nebula.widgets.nattable.tree.config.TreeConfigAttributes;\r
+import org.eclipse.nebula.widgets.nattable.tree.painter.IndentedTreeImagePainter;\r
+\r
+import it.unimi.dsi.fastutil.ints.IntRBTreeSet;\r
+\r
+public class TreeLayer2 extends AbstractRowHideShowLayer2 {\r
+\r
+ //private static final Log log = LogFactory.getLog(TreeLayer.class);\r
+\r
+ public static final String TREE_COLUMN_CELL = "TREE_COLUMN_CELL"; //$NON-NLS-1$\r
+\r
+ public static final int TREE_COLUMN_NUMBER = 0;\r
+\r
+ /**\r
+ * Flag to configure whether the tree column should be identified by\r
+ * position or by index. Default is position.\r
+ */\r
+ private boolean useTreeColumnIndex = false;\r
+\r
+ /**\r
+ * The ITreeRowModelListener that is used to get information about the tree\r
+ * structure.\r
+ */\r
+ private final ITreeRowModel<?> treeRowModel;\r
+\r
+ /**\r
+ * Collection of all row indexes that are hidden if tree nodes are\r
+ * collapsed.\r
+ * <p>\r
+ * Note: This collection is only in use if the used {@link ITreeRowModel}\r
+ * implementation is returning the row indexes of affected rows on\r
+ * expand/collapse. There are also implementations that use another approach\r
+ * where the hide/show approach is not used (e.g. GlazedListTreeRowModel)\r
+ * </p>\r
+ */\r
+ private final IntRBTreeSet hiddenRowIndexes = new IntRBTreeSet();\r
+\r
+ /**\r
+ * The IndentedTreeImagePainter that paints indentation to the left of the\r
+ * configured base painter and icons for expand/collapse if possible, to\r
+ * render tree structure accordingly.\r
+ */\r
+ private IndentedTreeImagePainter indentedTreeImagePainter;\r
+\r
+ /**\r
+ * Creates a TreeLayer instance based on the given information. Will use a\r
+ * default IndentedTreeImagePainter that uses 10 pixels for indentation and\r
+ * simple + and - icons for expand/collapse icons. It also uses the\r
+ * DefaultTreeLayerConfiguration.\r
+ *\r
+ * @param underlyingLayer\r
+ * The underlying layer on whose top this layer will be set.\r
+ * @param treeRowModel\r
+ * The ITreeRowModelListener that is used to get information\r
+ * about the tree structure.\r
+ */\r
+ public TreeLayer2(IUniqueIndexLayer underlyingLayer, ITreeRowModel<?> treeRowModel) {\r
+ this(underlyingLayer, treeRowModel, new IndentedTreeImagePainter());\r
+ }\r
+\r
+ /**\r
+ * Creates a TreeLayer instance based on the given information. Allows to\r
+ * specify the IndentedTreeImagePainter while using the\r
+ * DefaultTreeLayerConfiguration.\r
+ *\r
+ * @param underlyingLayer\r
+ * The underlying layer on whose top this layer will be set.\r
+ * @param treeRowModel\r
+ * The ITreeRowModelListener that is used to get information\r
+ * about the tree structure.\r
+ * @param indentedTreeImagePainter\r
+ * The IndentedTreeImagePainter that paints indentation to the\r
+ * left of the configured base painter and icons for\r
+ * expand/collapse if possible, to render tree structure\r
+ * accordingly.\r
+ */\r
+ public TreeLayer2(\r
+ IUniqueIndexLayer underlyingLayer,\r
+ ITreeRowModel<?> treeRowModel,\r
+ IndentedTreeImagePainter indentedTreeImagePainter) {\r
+ this(underlyingLayer, treeRowModel, indentedTreeImagePainter, true);\r
+ }\r
+\r
+ /**\r
+ * Creates a TreeLayer instance based on the given information. Will use a\r
+ * default IndentedTreeImagePainter that uses 10 pixels for indentation and\r
+ * simple + and - icons for expand/collapse icons.\r
+ *\r
+ * @param underlyingLayer\r
+ * The underlying layer on whose top this layer will be set.\r
+ * @param treeRowModel\r
+ * The ITreeRowModelListener that is used to get information\r
+ * about the tree structure.\r
+ * @param useDefaultConfiguration\r
+ * <code>true</code> to use the DefaultTreeLayerConfiguration,\r
+ * <code>false</code> if you want to specify your own\r
+ * configuration.\r
+ */\r
+ public TreeLayer2(\r
+ IUniqueIndexLayer underlyingLayer,\r
+ ITreeRowModel<?> treeRowModel,\r
+ boolean useDefaultConfiguration) {\r
+ this(underlyingLayer,\r
+ treeRowModel,\r
+ new IndentedTreeImagePainter(),\r
+ useDefaultConfiguration);\r
+ }\r
+\r
+ /**\r
+ * Creates a TreeLayer instance based on the given information.\r
+ *\r
+ * @param underlyingLayer\r
+ * The underlying layer on whose top this layer will be set.\r
+ * @param treeRowModel\r
+ * The ITreeRowModelListener that is used to get information\r
+ * about the tree structure.\r
+ * @param indentedTreeImagePainter\r
+ * The IndentedTreeImagePainter that paints indentation to the\r
+ * left of the configured base painter and icons for\r
+ * expand/collapse if possible, to render tree structure\r
+ * accordingly.\r
+ * @param useDefaultConfiguration\r
+ * <code>true</code> to use the DefaultTreeLayerConfiguration,\r
+ * <code>false</code> if you want to specify your own\r
+ * configuration.\r
+ */\r
+ public TreeLayer2(\r
+ IUniqueIndexLayer underlyingLayer,\r
+ ITreeRowModel<?> treeRowModel,\r
+ IndentedTreeImagePainter indentedTreeImagePainter,\r
+ boolean useDefaultConfiguration) {\r
+\r
+ super(underlyingLayer);\r
+ this.treeRowModel = treeRowModel;\r
+\r
+ if (useDefaultConfiguration) {\r
+ addConfiguration(new DefaultTreeLayerConfiguration2(this));\r
+ }\r
+\r
+ this.indentedTreeImagePainter = indentedTreeImagePainter;\r
+\r
+ registerCommandHandler(new TreeExpandCollapseCommandHandler(this));\r
+ registerCommandHandler(new TreeCollapseAllCommandHandler(this));\r
+ registerCommandHandler(new TreeExpandAllCommandHandler(this));\r
+ registerCommandHandler(new TreeExpandToLevelCommandHandler(this));\r
+ }\r
+\r
+ @Override\r
+ public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) {\r
+ LabelStack configLabels = super.getConfigLabelsByPosition(columnPosition, rowPosition);\r
+\r
+ if (isTreeColumn(columnPosition)) {\r
+ configLabels.addLabelOnTop(TREE_COLUMN_CELL);\r
+\r
+ int rowIndex = getRowIndexByPosition(rowPosition);\r
+ configLabels.addLabelOnTop(\r
+ DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + this.treeRowModel.depth(rowIndex));\r
+ if (!this.treeRowModel.hasChildren(rowIndex)) {\r
+ configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE);\r
+ } else {\r
+ if (this.treeRowModel.isCollapsed(rowIndex)) {\r
+ configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE);\r
+ } else {\r
+ configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE);\r
+ }\r
+ }\r
+ }\r
+ return configLabels;\r
+ }\r
+\r
+ /**\r
+ * @return The ITreeRowModelListener that is used to get information about\r
+ * the tree structure.\r
+ */\r
+ public ITreeRowModel<?> getModel() {\r
+ return this.treeRowModel;\r
+ }\r
+\r
+ /**\r
+ * @return The IndentedTreeImagePainter that paints indentation to the left\r
+ * of the configured base painter and icons for expand/collapse if\r
+ * possible, to render tree structure accordingly.\r
+ *\r
+ * @deprecated since 1.1 the configured TreeImagePainter should be used\r
+ * instead of the hard referenced one\r
+ */\r
+ @Deprecated\r
+ public IndentedTreeImagePainter getIndentedTreeImagePainter() {\r
+ return this.indentedTreeImagePainter;\r
+ }\r
+\r
+ /**\r
+ * @return The ICellPainter that is used to paint the images in the tree by\r
+ * the IndentedTreeImagePainter. Usually it is some type of\r
+ * TreeImagePainter that paints expand/collapse/leaf icons regarding\r
+ * the node state.<br>\r
+ * Can be <code>null</code> if set explicitly to the\r
+ * IndentedTreeImagePainter!\r
+ *\r
+ * @deprecated since 1.1 the configured TreeImagePainter should be used\r
+ * instead of the hard referenced one\r
+ */\r
+ @Deprecated\r
+ public ICellPainter getTreeImagePainter() {\r
+ return this.indentedTreeImagePainter != null ? this.indentedTreeImagePainter\r
+ .getTreeImagePainter() : null;\r
+ }\r
+\r
+ /**\r
+ * @param columnPosition\r
+ * The column position to check.\r
+ * @return <code>true</code> if the given column position is the tree\r
+ * column, <code>false</code> if not.\r
+ */\r
+ private boolean isTreeColumn(int columnPosition) {\r
+ if (this.useTreeColumnIndex)\r
+ return getColumnIndexByPosition(columnPosition) == TREE_COLUMN_NUMBER;\r
+\r
+ return columnPosition == TREE_COLUMN_NUMBER;\r
+ }\r
+\r
+ @Override\r
+ public ICellPainter getCellPainter(\r
+ int columnPosition, int rowPosition,\r
+ ILayerCell cell, IConfigRegistry configRegistry) {\r
+ ICellPainter cellPainter = super.getCellPainter(\r
+ columnPosition, rowPosition, cell, configRegistry);\r
+\r
+ if (cell.getConfigLabels().hasLabel(TREE_COLUMN_CELL)) {\r
+\r
+ ICellPainter treeCellPainter = configRegistry.getConfigAttribute(\r
+ TreeConfigAttributes.TREE_STRUCTURE_PAINTER,\r
+ cell.getDisplayMode(),\r
+ cell.getConfigLabels().getLabels());\r
+\r
+ if (treeCellPainter != null) {\r
+ ICellPainter innerWrapper = treeCellPainter;\r
+ IndentedTreeImagePainter treePainter = null;\r
+ if (innerWrapper instanceof IndentedTreeImagePainter) {\r
+ treePainter = (IndentedTreeImagePainter) innerWrapper;\r
+ } else {\r
+ while (treePainter == null\r
+ && innerWrapper != null\r
+ && innerWrapper instanceof CellPainterWrapper\r
+ && ((CellPainterWrapper) innerWrapper).getWrappedPainter() != null) {\r
+\r
+ innerWrapper = ((CellPainterWrapper) innerWrapper).getWrappedPainter();\r
+ if (innerWrapper instanceof IndentedTreeImagePainter) {\r
+ treePainter = (IndentedTreeImagePainter) innerWrapper;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (treePainter != null) {\r
+ treePainter.setBaseCellPainter(cellPainter);\r
+ cellPainter = treeCellPainter;\r
+ } else {\r
+ // log error\r
+// log.warn("There is no IndentedTreeImagePainter found for TREE_STRUCTURE_PAINTER, " //$NON-NLS-1$\r
+// + "using local configured IndentedTreeImagePainter as fallback"); //$NON-NLS-1$\r
+ // fallback\r
+ this.indentedTreeImagePainter.setBaseCellPainter(cellPainter);\r
+ cellPainter = new BackgroundPainter(this.indentedTreeImagePainter);\r
+ }\r
+ } else {\r
+ // backwards compatibility fallback\r
+ this.indentedTreeImagePainter.setBaseCellPainter(cellPainter);\r
+ cellPainter = new BackgroundPainter(this.indentedTreeImagePainter);\r
+ }\r
+ }\r
+\r
+ return cellPainter;\r
+ }\r
+\r
+ @Override\r
+ public boolean isRowIndexHidden(int rowIndex) {\r
+ return this.hiddenRowIndexes.contains(rowIndex)\r
+ || isHiddenInUnderlyingLayer(rowIndex);\r
+ }\r
+\r
+ @Override\r
+ public Collection<Integer> getHiddenRowIndexes() {\r
+ return this.hiddenRowIndexes;\r
+ }\r
+\r
+ /**\r
+ * Performs an expand/collapse action dependent on the current state of the\r
+ * tree node at the given row index.\r
+ *\r
+ * @param parentIndex\r
+ * The index of the row that shows the tree node for which the\r
+ * expand/collapse action should be performed.\r
+ */\r
+ public void expandOrCollapseIndex(int parentIndex) {\r
+ if (this.treeRowModel.isCollapsed(parentIndex)) {\r
+ expandTreeRow(parentIndex);\r
+ } else {\r
+ collapseTreeRow(parentIndex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Collapses the tree node for the given row index.\r
+ *\r
+ * @param parentIndex\r
+ * The index of the row that shows the node that should be\r
+ * collapsed\r
+ */\r
+ public void collapseTreeRow(int parentIndex) {\r
+ List<Integer> rowIndexes = this.treeRowModel.collapse(parentIndex);\r
+ List<Integer> rowPositions = new ArrayList<Integer>();\r
+ for (Integer rowIndex : rowIndexes) {\r
+ int rowPos = getRowPositionByIndex(rowIndex);\r
+ // if the rowPos is negative, it is not visible because of hidden\r
+ // state in an underlying layer\r
+ if (rowPos >= 0) {\r
+ rowPositions.add(rowPos);\r
+ }\r
+ }\r
+ this.hiddenRowIndexes.addAll(rowIndexes);\r
+ invalidateCache();\r
+ fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));\r
+ }\r
+\r
+ /**\r
+ * Collapses all tree nodes in the tree.\r
+ */\r
+ public void collapseAll() {\r
+ List<Integer> rowIndexes = this.treeRowModel.collapseAll();\r
+ List<Integer> rowPositions = new ArrayList<Integer>();\r
+ for (Integer rowIndex : rowIndexes) {\r
+ int rowPos = getRowPositionByIndex(rowIndex);\r
+ // if the rowPos is negative, it is not visible because of hidden\r
+ // state in an underlying layer\r
+ if (rowPos >= 0) {\r
+ rowPositions.add(rowPos);\r
+ }\r
+ }\r
+ this.hiddenRowIndexes.addAll(rowIndexes);\r
+ invalidateCache();\r
+ fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));\r
+ }\r
+\r
+ /**\r
+ * Expands the tree node for the given row index.\r
+ *\r
+ * @param parentIndex\r
+ * The index of the row that shows the node that should be\r
+ * expanded\r
+ */\r
+ public void expandTreeRow(int parentIndex) {\r
+ List<Integer> rowIndexes = this.treeRowModel.expand(parentIndex);\r
+ // Bug 432865: iterating and removing every single item is faster than\r
+ // removeAll()\r
+ for (final Integer expandedChildRowIndex : rowIndexes) {\r
+ this.hiddenRowIndexes.remove(expandedChildRowIndex);\r
+ }\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+\r
+ /**\r
+ * Expands the tree node for the given row index in the tree to a certain\r
+ * level.\r
+ *\r
+ * @param parentIndex\r
+ * The index of the row that shows the node that should be\r
+ * expanded\r
+ * @param level\r
+ * The level to which the tree node should be expanded.\r
+ */\r
+ public void expandTreeRowToLevel(int parentIndex, int level) {\r
+ List<Integer> rowIndexes = this.treeRowModel.expandToLevel(parentIndex, level);\r
+ // Bug 432865: iterating and removing every single item is faster than\r
+ // removeAll()\r
+ for (final Integer expandedChildRowIndex : rowIndexes) {\r
+ this.hiddenRowIndexes.remove(expandedChildRowIndex);\r
+ }\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+\r
+ /**\r
+ * Expands all tree nodes in the tree.\r
+ */\r
+ public void expandAll() {\r
+ List<Integer> rowIndexes = this.treeRowModel.expandAll();\r
+ this.hiddenRowIndexes.clear();\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+\r
+ /**\r
+ * Expands all tree nodes in the tree to a certain level.\r
+ *\r
+ * @param level\r
+ * The level to which the tree node should be expanded.\r
+ */\r
+ public void expandAllToLevel(int level) {\r
+ List<Integer> rowIndexes = this.treeRowModel.expandToLevel(level);\r
+ // Bug 432865: iterating and removing every single item is faster than\r
+ // removeAll()\r
+// for (final Integer expandedChildRowIndex : rowIndexes) {\r
+// this.hiddenRowIndexes.remove(expandedChildRowIndex);\r
+// }\r
+ if (rowIndexes == null)\r
+ return;\r
+ for (int i = rowIndexes.size()-1; i>=0; i--) {\r
+ this.hiddenRowIndexes.remove(rowIndexes.get(i));\r
+ }\r
+ invalidateCache();\r
+ fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+ }\r
+\r
+ /**\r
+ * Checks the underlying layer if the row is hidden by another layer.\r
+ *\r
+ * @param rowIndex\r
+ * The index of the row whose hidden state should be checked\r
+ * @return <code>true</code> if the row at the given index is hidden in the\r
+ * underlying layer <code>false</code> if not.\r
+ */\r
+ private boolean isHiddenInUnderlyingLayer(int rowIndex) {\r
+ IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();\r
+ return (underlyingLayer.getRowPositionByIndex(rowIndex) == -1);\r
+ }\r
+\r
+ @Override\r
+ public boolean doCommand(ILayerCommand command) {\r
+ // special command transformations are needed to hide also child nodes\r
+ if (command instanceof RowHideCommand) {\r
+ return handleRowHideCommand((RowHideCommand) command);\r
+ } else if (command instanceof MultiRowHideCommand) {\r
+ return handleMultiRowHideCommand((MultiRowHideCommand) command);\r
+ }\r
+ return super.doCommand(command);\r
+ }\r
+\r
+ /**\r
+ * Checks if the given command tries to hide a row that is a node that is\r
+ * not collapsed and has children. In that case also the child rows need to\r
+ * be hidden.\r
+ *\r
+ * @param command\r
+ * The {@link RowHideCommand} to process\r
+ * @return <code>true</code> if the command has been handled,\r
+ * <code>false</code> otherwise\r
+ */\r
+ protected boolean handleRowHideCommand(RowHideCommand command) {\r
+ // transform position to index\r
+ if (command.convertToTargetLayer(this)) {\r
+ int rowIndex = getRowIndexByPosition(command.getRowPosition());\r
+ if (this.treeRowModel.hasChildren(rowIndex)\r
+ && !this.treeRowModel.isCollapsed(rowIndex)) {\r
+ List<Integer> childIndexes = this.treeRowModel.getChildIndexes(rowIndex);\r
+ int[] childPositions = new int[childIndexes.size() + 1];\r
+ childPositions[0] = command.getRowPosition();\r
+ for (int i = 1; i < childIndexes.size() + 1; i++) {\r
+ int childPos = getRowPositionByIndex(childIndexes.get(i - 1));\r
+ childPositions[i] = childPos;\r
+ }\r
+ return super.doCommand(new MultiRowHideCommand(this, childPositions));\r
+ }\r
+ }\r
+ return super.doCommand(command);\r
+ }\r
+\r
+ /**\r
+ * Checks if the given command tries to hide rows that are nodes that are\r
+ * not collapsed and have children. In that case also the child rows need to\r
+ * be hidden.\r
+ *\r
+ * @param command\r
+ * The {@link MultiRowHideCommand} to process\r
+ * @return <code>true</code> if the command has been handled,\r
+ * <code>false</code> otherwise\r
+ */\r
+ protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) {\r
+ // transform position to index\r
+ if (command.convertToTargetLayer(this)) {\r
+ List<Integer> rowPositionsToHide = new ArrayList<Integer>();\r
+ for (Integer rowPos : command.getRowPositions()) {\r
+ rowPositionsToHide.add(rowPos);\r
+ int rowIndex = getRowIndexByPosition(rowPos);\r
+ if (this.treeRowModel.hasChildren(rowIndex)\r
+ && !this.treeRowModel.isCollapsed(rowIndex)) {\r
+ List<Integer> childIndexes = this.treeRowModel.getChildIndexes(rowIndex);\r
+ for (Integer childIndex : childIndexes) {\r
+ rowPositionsToHide.add(getRowPositionByIndex(childIndex));\r
+ }\r
+ }\r
+ }\r
+\r
+ int[] childPositions = new int[rowPositionsToHide.size()];\r
+ for (int i = 0; i < rowPositionsToHide.size(); i++) {\r
+ childPositions[i] = rowPositionsToHide.get(i);\r
+ }\r
+ return super.doCommand(new MultiRowHideCommand(this, childPositions));\r
+ }\r
+ return super.doCommand(command);\r
+ }\r
+\r
+ /**\r
+ * @return <code>true</code> if the column index is used to determine the\r
+ * tree column, <code>false</code> if the column position is used.\r
+ * Default is <code>false</code>.\r
+ */\r
+ public boolean isUseTreeColumnIndex() {\r
+ return this.useTreeColumnIndex;\r
+ }\r
+\r
+ /**\r
+ * Configure whether (column index == 0) or (column position == 0) should be\r
+ * performed to identify the tree column.\r
+ *\r
+ * @param useTreeColumnIndex\r
+ * <code>true</code> if the column index should be used to\r
+ * determine the tree column, <code>false</code> if the column\r
+ * position should be used.\r
+ */\r
+ public void setUseTreeColumnIndex(boolean useTreeColumnIndex) {\r
+ this.useTreeColumnIndex = useTreeColumnIndex;\r
+ }\r
+\r
+ /**\r
+ * @since 1.4\r
+ */\r
+ @Override\r
+ public Collection<String> getProvidedLabels() {\r
+ Collection<String> result = super.getProvidedLabels();\r
+\r
+ result.add(TreeLayer.TREE_COLUMN_CELL);\r
+ result.add(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE);\r
+ result.add(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE);\r
+ result.add(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE);\r
+ // configure 5 levels to be configurable via CSS\r
+ // if you need more you need to override this method\r
+ result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "0"); //$NON-NLS-1$\r
+ result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "1"); //$NON-NLS-1$\r
+ result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "2"); //$NON-NLS-1$\r
+ result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "3"); //$NON-NLS-1$\r
+ result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "4"); //$NON-NLS-1$\r
+\r
+ return result;\r
+ }\r
+}\r
}\r
\r
protected void createAuxiliaryControls(Composite parent, Control explorerControl) {\r
- parent.setLayout(LayoutUtils.createNoBorderGridLayout(3, false));\r
+ if (explorerControl instanceof Tree)\r
+ parent.setLayout(LayoutUtils.createNoBorderGridLayout(3, false));\r
\r
if (!hideComparatorSelector) {\r
ComparatorSelector comparatorSelector = new ComparatorSelector(explorer, userSelectedComparableFactoryQueryProcessor, parent, SWT.READ_ONLY);\r
Export-Package: org.simantics.browsing.ui.swt,
org.simantics.browsing.ui.swt.contentassist,
org.simantics.browsing.ui.swt.inputs,
+ org.simantics.browsing.ui.swt.internal,
org.simantics.browsing.ui.swt.widgets,
org.simantics.browsing.ui.swt.widgets.impl
Bundle-Vendor: VTT Technical Research Centre of Finland
package org.simantics.browsing.ui.swt;\r
\r
import org.eclipse.jface.viewers.ISelection;\r
-import org.eclipse.swt.widgets.Tree;\r
+import org.eclipse.swt.widgets.Control;\r
import org.simantics.browsing.ui.GraphExplorer;\r
import org.simantics.browsing.ui.common.node.IDoubleClickableNode;\r
import org.simantics.db.common.procedure.adapter.ProcedureAdapter;\r
}\r
\r
@Override\r
- protected void handleContextDoubleClick(Tree tree, ISelection selection) {\r
+ protected void handleContextDoubleClick(Control tree, ISelection selection) {\r
// First see if node is an IDoubleClickableNode\r
IDoubleClickableNode doubleClickable = AdaptionUtils.adaptToSingle(selection, IDoubleClickableNode.class);\r
if (doubleClickable != null) {\r
package org.simantics.browsing.ui.swt;\r
\r
import org.eclipse.jface.viewers.ISelection;\r
-import org.eclipse.swt.widgets.Tree;\r
+import org.eclipse.swt.widgets.Control;\r
import org.simantics.browsing.ui.GraphExplorer;\r
import org.simantics.browsing.ui.common.node.IDoubleClickableNode;\r
import org.simantics.utils.ui.AdaptionUtils;\r
}\r
\r
@Override\r
- protected void handleContextDoubleClick(Tree tree, ISelection context) {\r
+ protected void handleContextDoubleClick(Control tree, ISelection context) {\r
IDoubleClickableNode doubleClickable = AdaptionUtils.adaptToSingle(context, IDoubleClickableNode.class);\r
if (doubleClickable != null) {\r
doubleClickable.handleDoubleClick();\r
*******************************************************************************/\r
package org.simantics.browsing.ui.swt;\r
\r
+import java.lang.reflect.Method;\r
+\r
+import org.eclipse.core.runtime.Platform;\r
import org.eclipse.swt.SWT;\r
import org.eclipse.swt.widgets.Composite;\r
import org.eclipse.ui.services.IServiceLocator;\r
+import org.osgi.framework.Bundle;\r
import org.simantics.Simantics;\r
import org.simantics.browsing.ui.BuiltinKeys;\r
import org.simantics.browsing.ui.GraphExplorer;\r
import org.simantics.simulation.ontology.SimulationResource;\r
import org.simantics.utils.datastructures.BinaryFunction;\r
import org.simantics.utils.datastructures.hints.IHintContext;\r
+import org.simantics.utils.ui.ExceptionUtils;\r
\r
/**\r
* @author Tuukka Lehtonen\r
explorer.setServiceLocator(serviceLocator);\r
return explorer;\r
}\r
+ \r
+ public GraphExplorer create3(Composite parent, int style) {\r
+ //GraphExplorerImpl2 explorer = new GraphExplorerImpl2(parent, style);\r
+ try {\r
+ Bundle bundle = Platform.getBundle("org.simantics.browsing.ui.nattable");\r
+ Class<GraphExplorer> clazz = (Class<GraphExplorer>)bundle.loadClass("org.simantics.browsing.ui.nattable.NatTableGraphExplorer");\r
+ //Class<GraphExplorer> clazz = (Class<GraphExplorer>)bundle.getClass().getClassLoader().loadClass("org.simantics.browsing.ui.nattable.NatTableGraphExplorer");\r
+ GraphExplorer explorer = clazz.getConstructor(Composite.class, int.class).newInstance(parent,style);\r
+ explorer.setSelectionDataResolver(selectionDataResolver);\r
+ explorer.setSelectionFilter(selectionFilter);\r
+ explorer.setSelectionTransformation(selectionTransformation);\r
+ Method m = clazz.getMethod("setServiceLocator", IServiceLocator.class);\r
+ m.invoke(explorer, serviceLocator);\r
+ //explorer.setServiceLocator(serviceLocator);\r
+ return explorer;\r
+ } catch (Throwable t) {\r
+ ExceptionUtils.logAndShowError(t);\r
+ return null;\r
+ }\r
+ }\r
\r
// void hookActions(IWorkbenchSite site) {\r
// IActionBars actionBars = null;\r
import org.eclipse.swt.widgets.Event;\r
import org.eclipse.swt.widgets.Listener;\r
import org.eclipse.swt.widgets.ScrollBar;\r
+import org.eclipse.swt.widgets.Shell;\r
import org.eclipse.swt.widgets.Text;\r
import org.eclipse.swt.widgets.Tree;\r
import org.eclipse.swt.widgets.TreeColumn;\r
deactivateEditingContext();\r
}\r
});\r
- editor.setEditor(control, item, columnIndex);\r
+ \r
+ if (!(control instanceof Shell)) {\r
+ editor.setEditor(control, item, columnIndex);\r
+ }\r
+ \r
\r
control.setFocus();\r
\r
\r
setBasicListeners();\r
setDefaultProcessors();\r
+ \r
+ this.toolTip = new GraphExplorerToolTip(explorerContext, tree);\r
}\r
\r
@Override\r
TreeItem previousSingleSelection = null;\r
long focusGainedAt = Long.MIN_VALUE;\r
\r
+ protected GraphExplorerToolTip toolTip;\r
+\r
protected void setBasicListeners() {\r
// Keep track of the previous single selection to help\r
// decide whether to start editing a tree node on mouse\r
GENodeQueryManager manager = new GENodeQueryManager(newContext, null, null, TreeItemReference.create(null));\r
this.explorerContext = newContext;\r
oldContext.safeDispose();\r
+ toolTip.setGraphExplorerContext(explorerContext);\r
\r
// Need to empty these or otherwise they won't be emptied until the\r
// explorer is disposed which would mean that many unwanted references\r
this.focusService = (IFocusService) serviceLocator.getService(IFocusService.class);\r
}\r
}\r
+ \r
+ @Override\r
+ public Object getClicked(Object event) {\r
+ MouseEvent e = (MouseEvent)event;\r
+ final Tree tree = (Tree) e.getSource();\r
+ Point point = new Point(e.x, e.y);\r
+ TreeItem item = tree.getItem(point);\r
+\r
+ // No selectable item at point?\r
+ if (item == null)\r
+ return null;\r
+\r
+ Object data = item.getData();\r
+ return data;\r
+ }\r
\r
}\r
import org.eclipse.swt.graphics.Color;\r
import org.eclipse.swt.graphics.Font;\r
import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.graphics.Point;\r
import org.eclipse.swt.graphics.RGB;\r
import org.eclipse.swt.layout.FillLayout;\r
import org.eclipse.swt.widgets.Composite;\r
import org.eclipse.swt.widgets.ScrollBar;\r
import org.eclipse.swt.widgets.Tree;\r
import org.eclipse.swt.widgets.TreeColumn;\r
+import org.eclipse.swt.widgets.TreeItem;\r
import org.eclipse.ui.PlatformUI;\r
import org.eclipse.ui.contexts.IContextActivation;\r
import org.eclipse.ui.contexts.IContextService;\r
super.dispose();\r
}\r
}\r
+ \r
+ @Override\r
+ public Object getClicked(Object event) {\r
+ MouseEvent e = (MouseEvent)event;\r
+ final Tree tree = (Tree) e.getSource();\r
+ Point point = new Point(e.x, e.y);\r
+ TreeItem item = tree.getItem(point);\r
+\r
+ // No selectable item at point?\r
+ if (item == null)\r
+ return null;\r
+\r
+ Object data = item.getData();\r
+ return data;\r
+ }\r
}\r
import org.eclipse.jface.viewers.StructuredSelection;\r
import org.eclipse.swt.events.MouseAdapter;\r
import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.widgets.Tree;\r
-import org.eclipse.swt.widgets.TreeItem;\r
+import org.eclipse.swt.widgets.Control;\r
import org.simantics.browsing.ui.GraphExplorer;\r
import org.simantics.browsing.ui.NodeContext;\r
import org.simantics.utils.ui.AdaptionUtils;\r
}\r
\r
protected ISelection getClickedContext(MouseEvent e) {\r
- final Tree tree = (Tree) e.getSource();\r
- Point point = new Point(e.x, e.y);\r
- TreeItem item = tree.getItem(point);\r
-\r
- // No selectable item at point?\r
- if (item == null)\r
- return null;\r
-\r
- Object data = item.getData();\r
+// final Tree tree = (Tree) e.getSource();\r
+// Point point = new Point(e.x, e.y);\r
+// TreeItem item = tree.getItem(point);\r
+//\r
+// // No selectable item at point?\r
+// if (item == null)\r
+// return null;\r
+//\r
+// Object data = item.getData();\r
+ Object data = ge.getClicked(e);\r
+ if (data == null)\r
+ return null;\r
+ \r
NodeContext context = AdaptionUtils.adaptToSingle(data, NodeContext.class);\r
if (context == null)\r
return null;\r
if (context == null)\r
return;\r
\r
- Tree tree = (Tree) e.getSource();\r
+ Control tree = (Control)e.getSource();\r
handleContextDoubleClick(tree, context);\r
}\r
\r
- protected void handleContextDoubleClick(Tree tree, ISelection selection) {\r
+ protected void handleContextDoubleClick(Control tree, ISelection selection) {\r
}\r
\r
}\r
--- /dev/null
+package org.simantics.browsing.ui.swt;\r
+\r
+import org.eclipse.jface.window.ToolTip;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.eclipse.swt.widgets.Tree;\r
+import org.eclipse.swt.widgets.TreeItem;\r
+import org.simantics.browsing.ui.BuiltinKeys;\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.browsing.ui.common.internal.GENodeQueryManager;\r
+import org.simantics.browsing.ui.common.labelers.LabelerStub;\r
+import org.simantics.browsing.ui.content.Labeler;\r
+import org.simantics.browsing.ui.swt.GraphExplorerImpl.GraphExplorerContext;\r
+\r
+public class GraphExplorerToolTip extends ToolTip {\r
+\r
+ private boolean DEBUG = false;\r
+ \r
+ private Tree parent;\r
+ private NodeContext nodeContext;\r
+ private Labeler labeler;\r
+\r
+ private GraphExplorerContext explorerContext;\r
+ \r
+ public GraphExplorerToolTip(GraphExplorerContext explorerContext, Tree parent) {\r
+ super(parent, NO_RECREATE, false);\r
+ setHideOnMouseDown(false);\r
+ setPopupDelay(400);\r
+ this.explorerContext = explorerContext;\r
+ this.parent = parent;\r
+ this.nodeContext = null;\r
+ if (DEBUG)\r
+ System.out.println("GraphExplorerToolTip constructor called for parent : " + parent + ", class : " + parent.getClass().toString());\r
+ }\r
+\r
+ @Override\r
+ protected Composite createToolTipContentArea(Event event, Composite parent) {\r
+ return ((LabelerStub) labeler).createToolTipContentArea(event, parent, nodeContext);\r
+ }\r
+ \r
+ @Override\r
+ protected boolean shouldCreateToolTip(Event event) {\r
+ TreeItem treeItem = parent.getItem(new Point(event.x, event.y));\r
+ GENodeQueryManager manager = new GENodeQueryManager(explorerContext, null, null, TreeItemReference.create(treeItem.getParentItem()));\r
+ nodeContext = (NodeContext) treeItem.getData();\r
+ labeler = manager.query(nodeContext, BuiltinKeys.SELECTED_LABELER);\r
+ if (nodeContext == null || !(labeler instanceof LabelerStub))\r
+ return false;\r
+ return ((LabelerStub) labeler).shouldCreateToolTip(event, nodeContext);\r
+ }\r
+\r
+ public void setGraphExplorerContext(GraphExplorerContext explorerContext) {\r
+ this.explorerContext = explorerContext;\r
+ }\r
+\r
+}\r
/**\r
* @author Tuukka Lehtonen\r
*/\r
-class ImageLoaderJob extends DatabaseJob {\r
+public class ImageLoaderJob extends DatabaseJob {\r
\r
private GraphExplorerImplBase ge;\r
private AtomicBoolean isScheduled = new AtomicBoolean();\r
\r
if (args.containsKey("treeView") && Boolean.TRUE.equals(args.get("treeView"))) {\r
explorer = createExplorerControl2(explorerComposite, maxChildren);\r
+ } else if (args.containsKey("natTable") && Boolean.TRUE.equals(args.get("natTable"))) {\r
+ explorer = createExplorerControl3(explorerComposite, maxChildren);\r
} else {\r
explorer = createExplorerControl(explorerComposite, maxChildren);\r
}\r
}\r
\r
public void addListenerToControl(int eventType, Listener listener) {\r
- ((Tree)explorer.getControl()).addListener(eventType, listener);\r
+ ((Control)explorer.getControl()).addListener(eventType, listener);\r
}\r
\r
public void finish() {\r
\r
DropTarget target = new DropTarget(control, DND.DROP_COPY | DND.DROP_LINK);\r
target.setTransfer(getAcceptedDataTypes());\r
+ if (control instanceof Tree) {\r
target.addDropListener(new DropTargetListener() {\r
\r
Tree tree = (Tree)explorer.getControl();\r
}\r
\r
});\r
+ }\r
\r
// Add workbench listeners and make sure they are cleaned up\r
setWorkbenchListeners();\r
\r
return ge;\r
}\r
+ \r
+ protected GraphExplorer createExplorerControl3(Composite parent, Integer maxChildren) {\r
+ GraphExplorerFactory factory = GraphExplorerFactory.getInstance();\r
+ if(maxChildren != null) factory = factory.maxChildrenShown(maxChildren);\r
+\r
+ GraphExplorer ge = factory\r
+ .selectionDataResolver(new DefaultSelectionDataResolver())\r
+ .selectionTransformation(selectionTransformation)\r
+ .setServiceLocator(site)\r
+ .create3(parent, style);\r
+\r
+ return ge;\r
+ }\r
\r
protected void setupDragSource(Session session) {\r
if (dragSource instanceof SessionContainer) {\r
* By default, implementations should be editable.\r
*/\r
void setEditable(boolean editable);\r
+ \r
+ /**\r
+ * Returns underlaying data object, which was clicked (with mouse).\r
+ * @param event Mouse Event (usually org.eclipse.swt.events.MouseEvent) \r
+ */\r
+ public Object getClicked(Object event);\r
\r
}\r
} catch (IllegalAccessException e) {\r
throw new BindingException( e ); \r
} catch (InvocationTargetException e) {\r
- throw new BindingException( e ); \r
+ throw new BindingException( e.getCause() ); \r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
throw new BindingException( e );\r
} catch (InvocationTargetException e) {\r
- throw new BindingException( e );\r
+ throw new BindingException( e.getCause() );\r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
throw new BindingException( e );\r
} catch (InvocationTargetException e) {\r
- throw new BindingException( e );\r
+ throw new BindingException( e.getCause() );\r
}\r
}\r
\r
} catch (IllegalArgumentException e) {\r
throw new BindingConstructionException(e);\r
} catch (InvocationTargetException e) {\r
- throw new BindingConstructionException(e);\r
+ throw new BindingConstructionException(e.getCause());\r
} catch (ClassNotFoundException e) {\r
throw new BindingConstructionException(e);\r
}\r
} catch (IllegalArgumentException e) {
throw new BindingException(e);
} catch (InvocationTargetException e) {
- throw new BindingException(e);
+ throw new BindingException(e.getCause());
}
}
} catch (IllegalAccessException e) {
throw new BindingException(e);
} catch (InvocationTargetException e) {
- throw new BindingException(e);
+ throw new BindingException(e.getCause());
}
}
} catch (IllegalArgumentException e) {
throw new BindingException(e);
} catch (InvocationTargetException e) {
- throw new BindingException(e);
+ throw new BindingException(e.getCause());
}
}
} catch (IllegalArgumentException e) {
throw new BindingException(e);
} catch (InvocationTargetException e) {
- e.printStackTrace();
- throw new BindingException(e);
+ e.getCause().printStackTrace();
+ throw new BindingException(e.getCause());
}
}
@Override
} catch (IllegalArgumentException e) {
throw new BindingException(e);
} catch (InvocationTargetException e) {
- throw new BindingException(e);\r
+ throw new BindingException(e.getCause());\r
}
}\r
\r
} catch (IllegalAccessException e) {
throw new BindingConstructionException(e);
} catch (InvocationTargetException e) {
- throw new BindingConstructionException(e);
+ throw new BindingConstructionException(e.getCause());
}
}
} catch (IllegalAccessException e1) {\r
Logger.defaultLogError(e1);\r
} catch (InvocationTargetException e1) {\r
- Logger.defaultLogError(e1);\r
+ Logger.defaultLogError(e1.getCause());\r
}\r
\r
}\r
Logger.defaultLogError(e1);\r
throw t;\r
} catch (InvocationTargetException e1) {\r
- Logger.defaultLogError(e1);\r
+ Logger.defaultLogError(e1.getCause());\r
throw t;\r
} catch (SecurityException e1) {\r
Logger.defaultLogError(e1);\r
} catch (IllegalAccessException e) {\r
Logger.defaultLogError(e);\r
} catch (InvocationTargetException e) {\r
- Logger.defaultLogError(e);\r
+ Logger.defaultLogError(e.getCause());\r
}\r
}\r
}\r
procedure.exception(g, e);\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- procedure.exception(g, e);\r
- e.printStackTrace();\r
+ procedure.exception(g, e.getCause());\r
+ e.getCause().printStackTrace();\r
}\r
\r
} else if( parameters.length == 1 && parameters[0] instanceof ThisResource2) {\r
procedure.exception(g, e);\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- procedure.exception(g, e);\r
- e.printStackTrace();\r
+ procedure.exception(g, e.getCause());\r
+ e.getCause().printStackTrace();\r
}\r
\r
} else {\r
procedure.exception(graph, e);\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- procedure.exception(graph, e);\r
- e.printStackTrace();\r
+ procedure.exception(graph, e.getCause());\r
+ e.getCause().printStackTrace();\r
} catch (DatabaseException e) {\r
procedure.exception(graph, e);\r
e.printStackTrace();\r
procedure.exception(g, e);\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- procedure.exception(g, e);\r
- e.printStackTrace();\r
+ procedure.exception(g, e.getCause());\r
+ e.getCause().printStackTrace();\r
}\r
}\r
\r
L0.RequiresValueType "String"
L0.HasLabel "Target"
+PROPERTIES.hyperlinkTarget : PROPERTIES.ParameterType : L0.FunctionalRelation
+ @PROPERTIES.defAttribute L0.String
+ L0.RequiresValueType "String"
+ L0.HasLabel "Target type (optional)"
+
PROPERTIES.targets : PROPERTIES.ParameterType : L0.FunctionalRelation
@PROPERTIES.defAttribute L0.StringArray
L0.RequiresValueType "Array String"
public final Resource Properties_exists_Inverse;\r
public final Resource Properties_experiment;\r
public final Resource Properties_experiment_Inverse;\r
+ public final Resource Properties_hyperlinkTarget;\r
+ public final Resource Properties_hyperlinkTarget_Inverse;\r
public final Resource Properties_icstate;\r
public final Resource Properties_icstate_Inverse;\r
public final Resource Properties_input;\r
public static final String Properties_exists_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/exists/Inverse";\r
public static final String Properties_experiment = "http://www.simantics.org/Documentation-1.2/Properties/experiment";\r
public static final String Properties_experiment_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/experiment/Inverse";\r
+ public static final String Properties_hyperlinkTarget = "http://www.simantics.org/Documentation-1.2/Properties/hyperlinkTarget";\r
+ public static final String Properties_hyperlinkTarget_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/hyperlinkTarget/Inverse";\r
public static final String Properties_icstate = "http://www.simantics.org/Documentation-1.2/Properties/icstate";\r
public static final String Properties_icstate_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/icstate/Inverse";\r
public static final String Properties_input = "http://www.simantics.org/Documentation-1.2/Properties/input";\r
Properties_exists_Inverse = getResourceOrNull(graph, URIs.Properties_exists_Inverse);\r
Properties_experiment = getResourceOrNull(graph, URIs.Properties_experiment);\r
Properties_experiment_Inverse = getResourceOrNull(graph, URIs.Properties_experiment_Inverse);\r
+ Properties_hyperlinkTarget = getResourceOrNull(graph, URIs.Properties_hyperlinkTarget);\r
+ Properties_hyperlinkTarget_Inverse = getResourceOrNull(graph, URIs.Properties_hyperlinkTarget_Inverse);\r
Properties_icstate = getResourceOrNull(graph, URIs.Properties_icstate);\r
Properties_icstate_Inverse = getResourceOrNull(graph, URIs.Properties_icstate_Inverse);\r
Properties_input = getResourceOrNull(graph, URIs.Properties_input);\r
setErrorMessage("Report failed: " + err.getMessage());\r
ErrorLogger.defaultLogError("Report failed.",err);\r
statusLabel.setText("Report failed.");\r
- } catch (InvocationTargetException err) {\r
+ } catch (InvocationTargetException e) {\r
+ Throwable err = e.getCause();\r
setErrorMessage("Report failed: " + err.getMessage());\r
ErrorLogger.defaultLogError("Report failed.",err);\r
statusLabel.setText("Report failed.");\r
return getValueOrDefault(object, "target", "");\r
}\r
\r
+ public static String getHyperLinkTarget(IJSONObject object){\r
+ return getValueOrDefault(object, "hyperlinkTarget", "");\r
+ }\r
+ \r
public static String getWidth(IJSONObject object) {\r
return getValueOrDefault(object, "width", "");\r
}\r
@SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")\r
public static Variable state(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {\r
Variable session = graph.syncRequest(new ProxySessionRequest(context));\r
+ if (session == null)\r
+ throw new DatabaseException("No state for " + context.getURI(graph));\r
return session.getPossibleChild(graph, "__scl__");\r
}\r
\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.fileimport.ui</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\r
+org.eclipse.jdt.core.compiler.compliance=1.8\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.8\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Simantics Fileimport UI
+Bundle-SymbolicName: org.simantics.fileimport.ui;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.simantics.fileimport.ui.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.e4.ui.model.workbench;bundle-version="1.1.100.v20150407-1430",
+ org.eclipse.e4.core.di,
+ org.eclipse.e4.ui.services,
+ org.simantics.fileimport;bundle-version="1.0.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: javax.inject;version="1.0.0"
+Bundle-ActivationPolicy: lazy
--- /dev/null
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .,\\r
+ fragment.e4xmi,\\r
+ plugin.xml\r
+source.. = src/\r
--- /dev/null
+<?xml version="1.0" encoding="ASCII"?>\r
+<fragment:ModelFragments xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commands="http://www.eclipse.org/ui/2010/UIModel/application/commands" xmlns:fragment="http://www.eclipse.org/ui/2010/UIModel/fragment" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_0WHIYPGxEeWE47-PSvT6lg">\r
+ <fragments xsi:type="fragment:StringModelFragment" xmi:id="_06ZhMPGxEeWE47-PSvT6lg" featurename="commands" parentElementId="xpath:/">\r
+ <elements xsi:type="commands:Command" xmi:id="_jfO1YPGyEeWE47-PSvT6lg" elementId="org.simantics.fileimport.ui.importFile" commandName="Import Generic File" description="Import Generic file into Simantics product"/>\r
+ </fragments>\r
+ <fragments xsi:type="fragment:StringModelFragment" xmi:id="_nQcdMPGyEeWE47-PSvT6lg" featurename="handlers" parentElementId="xpath:/">\r
+ <elements xsi:type="commands:Handler" xmi:id="_ol-FsPGyEeWE47-PSvT6lg" elementId="org.simantics.fileimport.ui.handler.importFile" contributionURI="bundleclass://org.simantics.fileimport.ui/org.simantics.fileimport.ui.ImportFileHandler" command="_jfO1YPGyEeWE47-PSvT6lg"/>\r
+ </fragments>\r
+ <fragments xsi:type="fragment:StringModelFragment" xmi:id="_wkwhMPGyEeWE47-PSvT6lg" featurename="menuContributions" parentElementId="xpath:/">\r
+ <elements xsi:type="menu:MenuContribution" xmi:id="_YOXrgPG6EeWE47-PSvT6lg" elementId="org.simantics.fileimport.ui.menucontribution.importGenericFile" parentId="sFile">\r
+ <children xsi:type="menu:HandledMenuItem" xmi:id="_Zxz0MPG6EeWE47-PSvT6lg" elementId="id.importgenericfile" label="Import Generic File" command="_jfO1YPGyEeWE47-PSvT6lg"/>\r
+ </elements>\r
+ </fragments>\r
+</fragment:ModelFragments>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<plugin>\r
+\r
+ <extension\r
+ id="org.simantics.fileimport.ui.fragment"\r
+ point="org.eclipse.e4.workbench.model">\r
+ <fragment\r
+ uri="fragment.e4xmi">\r
+ </fragment>\r
+ </extension>\r
+\r
+</plugin>\r
--- /dev/null
+package org.simantics.fileimport.ui;\r
+\r
+import org.eclipse.ui.plugin.AbstractUIPlugin;\r
+import org.osgi.framework.BundleContext;\r
+\r
+/**\r
+ * The activator class controls the plug-in life cycle\r
+ */\r
+public class Activator extends AbstractUIPlugin {\r
+\r
+ // The plug-in ID\r
+ public static final String PLUGIN_ID = "org.simantics.fileimport.ui"; //$NON-NLS-1$\r
+\r
+ // The shared instance\r
+ private static Activator plugin;\r
+ \r
+ /**\r
+ * The constructor\r
+ */\r
+ public Activator() {\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)\r
+ */\r
+ public void start(BundleContext context) throws Exception {\r
+ super.start(context);\r
+ plugin = this;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)\r
+ */\r
+ public void stop(BundleContext context) throws Exception {\r
+ plugin = null;\r
+ super.stop(context);\r
+ }\r
+\r
+ /**\r
+ * Returns the shared instance\r
+ *\r
+ * @return the shared instance\r
+ */\r
+ public static Activator getDefault() {\r
+ return plugin;\r
+ }\r
+\r
+}\r
--- /dev/null
+\r
+package org.simantics.fileimport.ui;\r
+\r
+import java.nio.file.Paths;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import javax.inject.Named;\r
+\r
+import org.eclipse.e4.core.di.annotations.CanExecute;\r
+import org.eclipse.e4.core.di.annotations.Execute;\r
+import org.eclipse.e4.ui.services.IServiceConstants;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.widgets.FileDialog;\r
+import org.eclipse.swt.widgets.Shell;\r
+import org.simantics.fileimport.FileImportService;\r
+\r
+public class ImportFileHandler {\r
+\r
+ @CanExecute\r
+ public boolean canExecute() {\r
+ return !FileImportService.supportedExtensionsWithFilters().isEmpty();\r
+ }\r
+\r
+ @Execute\r
+ public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell) {\r
+\r
+ Map<String, String> extensions = FileImportService.supportedExtensionsWithFilters();\r
+ String[] filterExtensions = (String[]) extensions.keySet().toArray(new String[extensions.keySet().size()]);\r
+ String[] filterNames = (String[]) extensions.values().toArray(new String[extensions.values().size()]);\r
+ \r
+ // Sanity check\r
+ for (int i = 0; i < filterExtensions.length; i++) {\r
+ String extension = filterExtensions[i];\r
+ if (!extension.startsWith("*.")) {\r
+ System.err.println("Invalid extension filter provied: " + extension);\r
+ }\r
+ }\r
+\r
+ FileDialog dialog = new FileDialog(shell, SWT.OPEN);\r
+ dialog.setText("Choose File");\r
+ dialog.setFilterExtensions(filterExtensions);\r
+ dialog.setFilterNames(filterNames);\r
+ final String fileName = dialog.open();\r
+ if (fileName == null)\r
+ return;\r
+ FileImportService.performFileImport(Paths.get(fileName), Optional.empty());\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.fileimport</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ds.core.builder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\r
+org.eclipse.jdt.core.compiler.compliance=1.8\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.8\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Simantics Fileimport Interface
+Bundle-SymbolicName: org.simantics.fileimport
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.simantics.fileimport.Activator
+Require-Bundle: org.eclipse.core.runtime,
+ org.apache.log4j,
+ org.simantics.db,
+ org.simantics,
+ org.simantics.graphfile;bundle-version="0.1.0",
+ org.simantics.graphfile.ontology;bundle-version="0.1.0",
+ org.simantics.modeling;bundle-version="1.1.1"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Export-Package: org.simantics.fileimport
+Service-Component: OSGI-INF/FileReferenceFileImport.xml,
+ OSGI-INF/LibraryFolderFileImport.xml
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.simantics.fileimport.filereferencefileimport">\r
+ <implementation class="org.simantics.fileimport.FileReferenceFileImport"/>\r
+ <service>\r
+ <provide interface="org.simantics.fileimport.IGenericFileImport"/>\r
+ </service>\r
+</scr:component>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.simantics.fileimport.LibraryFolderFileImport">\r
+ <implementation class="org.simantics.fileimport.LibraryFolderFileImport"/>\r
+ <service>\r
+ <provide interface="org.simantics.fileimport.IGenericFileImport"/>\r
+ </service>\r
+</scr:component>\r
--- /dev/null
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .,\\r
+ OSGI-INF/FileReferenceFileImport.xml,\\r
+ OSGI-INF/FileReferenceFileImport.xml,\\r
+ OSGI-INF/LibraryFolderFileImport.xml,\\r
+ scl/\r
+source.. = src/\r
--- /dev/null
+import "MMap" as MMap\r
+\r
+importJava "org.simantics.fileimport.scl.DropinsSCL" where\r
+ uploadToDropinsBase64 :: String -> String -> <Proc> ()\r
+ getUploadedFiles :: () -> <Proc> MMap.T String Long\r
+ removeFileForId :: Long -> <Proc> ()\r
+\r
+getUploadedDropinFiles :: () -> <Proc> [Long]\r
+getUploadedDropinFiles dummy = do\r
+ files = getUploadedFiles ()\r
+ MMap.values files
\ No newline at end of file
--- /dev/null
+package org.simantics.fileimport;\r
+\r
+import java.io.IOException;\r
+import java.nio.file.Files;\r
+import java.nio.file.Path;\r
+import java.nio.file.Paths;\r
+\r
+import org.eclipse.core.runtime.IPath;\r
+import org.eclipse.core.runtime.Platform;\r
+import org.osgi.framework.BundleActivator;\r
+import org.osgi.framework.BundleContext;\r
+import org.simantics.fileimport.dropins.FileImportDropins;\r
+\r
+public class Activator implements BundleActivator {\r
+\r
+ private static BundleContext context;\r
+ \r
+ private static Path dropinsFolder = null;\r
+\r
+ static BundleContext getContext() {\r
+ return context;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)\r
+ */\r
+ public void start(BundleContext bundleContext) throws Exception {\r
+ Activator.context = bundleContext;\r
+ FileImportDropins.watchDropinsFolder();\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)\r
+ */\r
+ public void stop(BundleContext bundleContext) throws Exception {\r
+ Activator.context = null;\r
+ FileImportDropins.unwatchDropinsFolder();\r
+ }\r
+ \r
+ public static Path getDropinsFolder() throws IOException {\r
+ if (dropinsFolder == null) {\r
+ IPath state = Platform.getStateLocation(context.getBundle());\r
+ dropinsFolder = Paths.get(state.append("dropins").toOSString());\r
+ if (!Files.exists(dropinsFolder))\r
+ Files.createDirectories(dropinsFolder);\r
+ }\r
+ return dropinsFolder;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.fileimport;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.nio.file.Files;\r
+import java.nio.file.Path;\r
+import java.nio.file.Paths;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+import java.util.Properties;\r
+import java.util.function.Consumer;\r
+\r
+import org.osgi.framework.InvalidSyntaxException;\r
+import org.osgi.framework.ServiceReference;\r
+import org.simantics.Simantics;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.request.UniqueRead;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.layer0.Layer0;\r
+\r
+public class FileImportService {\r
+\r
+ public static final String DB_FILE = ".simanticsdb";\r
+\r
+ public static List<IGenericFileImport> getFileImportServices() {\r
+ ServiceReference<?>[] serviceReferences = new ServiceReference<?>[0];\r
+ try {\r
+ serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(),\r
+ null);\r
+ } catch (InvalidSyntaxException e) {\r
+ e.printStackTrace();\r
+ }\r
+ if (serviceReferences.length == 0)\r
+ return Collections.emptyList();\r
+\r
+ List<IGenericFileImport> services = new ArrayList<>(serviceReferences.length);\r
+ for (ServiceReference<?> reference : serviceReferences) {\r
+ IGenericFileImport service = (IGenericFileImport) Activator.getContext().getService(reference);\r
+ services.add(service);\r
+ }\r
+ return services;\r
+ }\r
+\r
+ public static Map<String, String> supportedExtensionsWithFilters() {\r
+ List<IGenericFileImport> services = getFileImportServices();\r
+ Map<String, String> extensionsWithFilters = new HashMap<>();\r
+ for (IGenericFileImport service : services)\r
+ extensionsWithFilters.putAll(service.allowedExtensionsWithFilters());\r
+\r
+ return extensionsWithFilters;\r
+ }\r
+\r
+ public static void performFileImport(Path file, Optional<Consumer<Throwable>> callback) {\r
+ if (file.getFileName().toString().equals(DB_FILE))\r
+ return;\r
+ Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
+ serviceOp.ifPresent(service -> {\r
+ try {\r
+ Optional<String> resource = service.perform(file);\r
+ saveResourceForPath(file, resource);\r
+ } catch (Throwable t) {\r
+ if (callback.isPresent()) {\r
+ callback.get().accept(t);\r
+ } else {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+ public static void removeResourceForFile(Path file, Optional<Consumer<Throwable>> callback) {\r
+ Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
+ serviceOp.ifPresent(service -> {\r
+ try {\r
+ Optional<String> resource = getResourceForPath(file);\r
+ if (!resource.isPresent())\r
+ return;\r
+ service.remove(resource.get());\r
+ removeResourceForPath(file);\r
+ } catch (Throwable t) {\r
+ if (callback.isPresent()) {\r
+ callback.get().accept(t);\r
+ } else {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+ });\r
+ }\r
+ \r
+ public static void removeFileForResource(long id, Optional<Consumer<Throwable>> callback) {\r
+ Optional<Path> fileOp;\r
+ try {\r
+ fileOp = findPathForId(id);\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ return;\r
+ }\r
+ if (!fileOp.isPresent())\r
+ return;\r
+ Path file = fileOp.get();\r
+ Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
+ serviceOp.ifPresent(service -> {\r
+ try {\r
+ Optional<String> resource = getResourceForPath(file);\r
+ if (!resource.isPresent())\r
+ return;\r
+ service.remove(resource.get());\r
+ removeResourceForPath(file);\r
+ } catch (Throwable t) {\r
+ if (callback.isPresent()) {\r
+ callback.get().accept(t);\r
+ } else {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+ private static Optional<Path> findPathForId(long id) throws IOException {\r
+ Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
+ if (!Files.exists(db))\r
+ Files.createFile(db);\r
+ Properties props = new Properties();\r
+ try (InputStream stream = Files.newInputStream(db)) {\r
+ props.load(stream);\r
+ }\r
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {\r
+ Long value = Long.valueOf(entry.getValue().toString());\r
+ if (value.longValue() == id) {\r
+ String key = (String) entry.getKey();\r
+ return Optional.of(Paths.get(key));\r
+ }\r
+ }\r
+ return Optional.empty();\r
+ }\r
+\r
+ static final String FOLDER = "_folder_";\r
+ \r
+ public static Optional<IGenericFileImport> findServiceForFileExtension(Path file) {\r
+ String extension = "";\r
+\r
+ int i = file.getFileName().toString().lastIndexOf('.');\r
+ if (i > 0) {\r
+ extension = file.getFileName().toString().substring(i);\r
+ } else {\r
+ // Handle case that file is actually a directory\r
+ if (Files.isDirectory(file) || !Files.isRegularFile(file)) {\r
+ extension = FOLDER;\r
+ }\r
+ }\r
+\r
+ List<IGenericFileImport> services = getFileImportServices();\r
+ for (IGenericFileImport service : services) {\r
+ for (Map.Entry<String, String> entry : service.allowedExtensionsWithFilters().entrySet()) {\r
+ String possibleExtensions = entry.getKey();\r
+ if (possibleExtensions.startsWith("*"))\r
+ possibleExtensions = possibleExtensions.substring(1);\r
+ if (possibleExtensions.equals(extension) || possibleExtensions.isEmpty()) {\r
+ if (extension.equals(FOLDER) && possibleExtensions.equals(FOLDER)) {\r
+ return Optional.of(service);\r
+ } else if (!extension.isEmpty() && !extension.equals(FOLDER)){\r
+ return Optional.of(service);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return Optional.empty();\r
+ }\r
+ \r
+ public static Map<String, Long> getPathsAndResources() {\r
+ try {\r
+ Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
+ if (!Files.exists(db))\r
+ Files.createFile(db);\r
+ Properties props = new Properties();\r
+ try (InputStream stream = Files.newInputStream(db)) {\r
+ props.load(stream);\r
+ }\r
+ Map<String, Long> result = Simantics.getSession().syncRequest(new UniqueRead<Map<String, Long>>() {\r
+\r
+ @Override\r
+ public Map<String, Long> perform(ReadGraph graph) throws DatabaseException {\r
+ Map<String, Long> map = new HashMap<>();\r
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {\r
+ String value = (String) entry.getValue();\r
+ Long id = Long.valueOf(value);\r
+ SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
+ try {\r
+ Resource r = ss.getResource(id);\r
+ String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);\r
+ map.put(name, id);\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ return map;\r
+ }\r
+ });\r
+\r
+ return result;\r
+ } catch (IOException | DatabaseException e) {\r
+ e.printStackTrace();\r
+ return Collections.emptyMap();\r
+ }\r
+ }\r
+\r
+ private static void saveResourceForPath(Path file, Optional<String> resource) {\r
+ resource.ifPresent(res -> {\r
+ try {\r
+ Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
+ if (!Files.exists(db))\r
+ Files.createFile(db);\r
+ Properties props = new Properties();\r
+ try (InputStream stream = Files.newInputStream(db)) {\r
+ props.load(stream);\r
+ }\r
+ props.put(file.getFileName().toString(), resource.get());\r
+ try (OutputStream stream = Files.newOutputStream(db)) {\r
+ props.store(stream, null);\r
+ }\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ });\r
+ }\r
+\r
+ private static void removeResourceForPath(Path file) throws IOException {\r
+ Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
+ if (!Files.exists(db))\r
+ Files.createFile(db);\r
+ Properties props = new Properties();\r
+ try (InputStream stream = Files.newInputStream(db)) {\r
+ props.load(stream);\r
+ }\r
+ props.remove(file.getFileName().toString());\r
+ try (OutputStream stream = Files.newOutputStream(db)) {\r
+ props.store(stream, null);\r
+ }\r
+ }\r
+ \r
+ private static Optional<String> getResourceForPath(Path file) throws IOException {\r
+ Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
+ if (!Files.exists(db))\r
+ Files.createFile(db);\r
+ Properties props = new Properties();\r
+ try (InputStream stream = Files.newInputStream(db)) {\r
+ props.load(stream);\r
+ }\r
+ String value = props.getProperty(file.getFileName().toString());\r
+ if (value == null)\r
+ return Optional.empty();\r
+ return Optional.of(value);\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.fileimport;\r
+\r
+import java.nio.file.Path;\r
+import java.util.Collections;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.graphfile.util.GraphFileUtil;\r
+\r
+public class FileReferenceFileImport extends SimanticsResourceFileImport {\r
+\r
+ private static final Map<String, String> ALLOWED_EXTENSIONS = Collections.singletonMap("*.asd", "All files");\r
+ \r
+ @Override\r
+ public Optional<Resource> perform(Resource parent, Path file) {\r
+ try {\r
+ return Optional.of(GraphFileUtil.createFileReference(parent, file));\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ return Optional.empty();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Map<String, String> allowedExtensionsWithFilters() {\r
+ return ALLOWED_EXTENSIONS;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.fileimport;\r
+\r
+import java.nio.file.Path;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+/**\r
+ * @author Jani Simomaa\r
+ *\r
+ */\r
+public interface IGenericFileImport {\r
+\r
+ /**\r
+ * Performs the import procedure for the given file\r
+ * \r
+ * @param file\r
+ * file to import\r
+ */\r
+ Optional<String> perform(Path file) throws Exception;\r
+\r
+ /**\r
+ * @param resource\r
+ */\r
+ void remove(String resource) throws Exception;\r
+ \r
+ /**\r
+ * Returns a key-value map for file extensions this importer can handle\r
+ * \r
+ * @return\r
+ */\r
+ Map<String, String> allowedExtensionsWithFilters();\r
+\r
+}\r
--- /dev/null
+package org.simantics.fileimport;\r
+\r
+import java.nio.file.Path;\r
+import java.util.Collections;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.WriteResultRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.modeling.ModelingUtils;\r
+\r
+public class LibraryFolderFileImport extends SimanticsResourceFileImport {\r
+\r
+ private static final Map<String, String> ALLOWED_EXTENSIONS = Collections.singletonMap(FileImportService.FOLDER, FileImportService.FOLDER);\r
+\r
+ @Override\r
+ public Map<String, String> allowedExtensionsWithFilters() {\r
+ return ALLOWED_EXTENSIONS;\r
+ }\r
+\r
+ @Override\r
+ public Optional<Resource> perform(Resource parent, Path file) {\r
+ final String name = file.getFileName().toString();\r
+ try {\r
+ return Optional.of(Simantics.getSession().syncRequest(new WriteResultRequest<Resource>() {\r
+\r
+ @Override\r
+ public Resource perform(WriteGraph graph) throws DatabaseException {\r
+ return ModelingUtils.createLibrary(graph, parent, name);\r
+ }\r
+ }));\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ return Optional.empty();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.fileimport;\r
+\r
+import java.nio.file.Path;\r
+import java.util.Collection;\r
+import java.util.Optional;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.ObjectsWithType;\r
+import org.simantics.db.common.request.UniqueRead;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.RuntimeDatabaseException;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.db.request.Read;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.layer0.Layer0;\r
+\r
+public abstract class SimanticsResourceFileImport implements IGenericFileImport {\r
+\r
+ @Override\r
+ final public Optional<String> perform(Path file) throws Exception {\r
+ \r
+ Path dropins = Activator.getDropinsFolder(); \r
+ Path parts = dropins.relativize(file);\r
+ Resource parent = resolveParent(null, parts);\r
+ if (parent == null)\r
+ return Optional.empty();\r
+ Optional<Resource> imported = perform(parent, file); \r
+ if (imported.isPresent()) {\r
+ return Optional.of(serialize(imported.get()));\r
+ } else {\r
+ return Optional.empty();\r
+ }\r
+ }\r
+ \r
+ public abstract Optional<Resource> perform(Resource parent, Path file);\r
+ \r
+ @Override\r
+ public void remove(String resourceId) throws Exception {\r
+ Optional<Resource> resource = deserialize(resourceId);\r
+ resource.ifPresent(res -> {\r
+ try {\r
+ Simantics.sync(new WriteRequest() {\r
+\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ RemoverUtil.remove(graph, resource.get());\r
+ }\r
+ });\r
+ } catch (Exception e) {\r
+ throw new RuntimeDatabaseException(e);\r
+ }\r
+ });\r
+ }\r
+\r
+ public String serialize(Resource resource) {\r
+ return Long.toString(resource.getResourceId());\r
+ }\r
+\r
+ public Optional<Resource> deserialize(String serialized) throws Exception {\r
+ long resourceId = Long.valueOf(serialized);\r
+\r
+ Resource resource = Simantics.getSession().syncRequest(new Read<Resource>() {\r
+\r
+ @Override\r
+ public Resource perform(ReadGraph graph) throws DatabaseException {\r
+ SerialisationSupport support = graph.getService(SerialisationSupport.class);\r
+ Resource resource = support.getResource(resourceId);\r
+ return resource;\r
+ }\r
+ });\r
+ return Optional.ofNullable(resource);\r
+ }\r
+ \r
+ private static Resource resolveParent(Resource parent, Path name) {\r
+ if (name.getParent() == null) {\r
+ return Simantics.getProjectResource();\r
+ } else {\r
+ name = name.getParent();\r
+ parent = resolveParent(parent, name);\r
+ }\r
+ final Resource newParent = parent;\r
+ final String folderName = name.getFileName().toString();\r
+\r
+ try {\r
+ return Simantics.getSession().syncRequest(new UniqueRead<Resource>() {\r
+\r
+ @Override\r
+ public Resource perform(ReadGraph graph) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ Collection<Resource> libraries = graph.sync(new ObjectsWithType(newParent, L0.ConsistsOf, L0.Library));\r
+ for (Resource library : libraries) {\r
+ String libraryName = graph.getRelatedValue2(library, L0.HasName, Bindings.STRING);\r
+ if (libraryName.equals(folderName)) {\r
+ return library;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ });\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ return null;\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.fileimport.dropins;\r
+\r
+import static java.nio.file.StandardWatchEventKinds.OVERFLOW;\r
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;\r
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;\r
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;\r
+\r
+import java.io.IOException;\r
+import java.nio.file.FileSystem;\r
+import java.nio.file.FileVisitResult;\r
+import java.nio.file.Files;\r
+import java.nio.file.Path;\r
+import java.nio.file.SimpleFileVisitor;\r
+import java.nio.file.WatchEvent;\r
+import java.nio.file.WatchEvent.Kind;\r
+import java.nio.file.attribute.BasicFileAttributes;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.nio.file.WatchKey;\r
+import java.nio.file.WatchService;\r
+\r
+import org.simantics.fileimport.Activator;\r
+import org.simantics.fileimport.FileImportService;\r
+\r
+public class FileImportDropins {\r
+ \r
+ private static Thread watcherThread = null;\r
+ private static DropinsFolderWatcher watcher = null;\r
+\r
+ public static void watchDropinsFolder() {\r
+ if (watcher == null && watcherThread == null) {\r
+ try {\r
+ watcher = new DropinsFolderWatcher(Activator.getDropinsFolder());\r
+ watcherThread = new Thread(watcher, "Simantics Dropins Folder watcher thread");\r
+ watcherThread.setDaemon(true);\r
+ watcherThread.start();\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+ \r
+ public static void unwatchDropinsFolder() {\r
+ watcher.stop();\r
+ try {\r
+ watcherThread.join(500);\r
+ if (watcherThread.isAlive())\r
+ watcherThread.interrupt();\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ }\r
+ watcherThread = null;\r
+ watcher = null;\r
+ }\r
+ \r
+ private static class DropinsFolderWatcher implements Runnable {\r
+\r
+ private final Path dropinsFolder;\r
+ private final WatchService ws;\r
+ private final AtomicBoolean stopped = new AtomicBoolean(true);\r
+ \r
+ private final Map<WatchKey, Path> keys = new HashMap<>();\r
+ \r
+ public DropinsFolderWatcher(Path dropinsFolder) throws IOException {\r
+ this.dropinsFolder = dropinsFolder;\r
+ FileSystem fs = dropinsFolder.getFileSystem();\r
+ this.ws = fs.newWatchService();\r
+ registerAll(this.dropinsFolder);\r
+ }\r
+ \r
+ @Override\r
+ public void run() {\r
+ stopped.set(false);\r
+\r
+ while (!stopped.get()) {\r
+ try {\r
+ WatchKey key = ws.take();\r
+ for (WatchEvent<?> watchEvent : key.pollEvents()) {\r
+ if (OVERFLOW == watchEvent.kind())\r
+ continue; // loop\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ WatchEvent<Path> pathEvent = (WatchEvent<Path>) watchEvent;\r
+ Kind<Path> kind = pathEvent.kind();\r
+ \r
+ Path parent = keys.get(key);\r
+ Path newPath = parent.resolve(pathEvent.context());\r
+ if (FileImportService.DB_FILE.equals(newPath.getFileName().toString()))\r
+ continue;\r
+ if (ENTRY_CREATE == kind) {\r
+ System.out.println("New path created: " + newPath);\r
+ FileImportService.performFileImport(newPath, Optional.empty());\r
+ register(newPath);\r
+ } else if (ENTRY_MODIFY == kind) {\r
+ System.out.println("New path modified: " + newPath);\r
+ } else if (ENTRY_DELETE == kind) {\r
+ System.out.println("New path deleted: " + newPath);\r
+ FileImportService.removeResourceForFile(newPath.toAbsolutePath(), Optional.empty());\r
+ }\r
+ }\r
+ if (!key.reset()) {\r
+ keys.remove(key);\r
+// break; // loop\r
+ }\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ } catch (InterruptedException e) {\r
+ if (!stopped.get())\r
+ e.printStackTrace();\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+ \r
+ public void stop() {\r
+ stopped.set(true);\r
+ }\r
+ \r
+ private void registerAll(Path path) throws IOException {\r
+ Files.walkFileTree(path, new SimpleFileVisitor<Path>() {\r
+ \r
+ @Override\r
+ public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) throws IOException {\r
+ register(file);\r
+ return FileVisitResult.CONTINUE;\r
+ }\r
+ });\r
+ }\r
+\r
+ private void register(Path path) throws IOException {\r
+ if (Files.isDirectory(path)) {\r
+ WatchKey key = path.toAbsolutePath().register(ws, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);\r
+ keys.put(key, path);\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.fileimport.scl;\r
+\r
+import java.io.IOException;\r
+import java.nio.file.Path;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.simantics.databoard.util.Base64;\r
+import org.simantics.fileimport.Activator;\r
+import org.simantics.fileimport.FileImportService;\r
+import org.simantics.fileimport.dropins.FileImportDropins;\r
+import org.simantics.utils.FileUtils;\r
+\r
+public class DropinsSCL {\r
+\r
+ public static void uploadToDropinsBase64(String base64, String fileName) {\r
+ // ensure that watcher is awake\r
+ FileImportDropins.watchDropinsFolder();\r
+ try {\r
+ Path rootFolder = Activator.getDropinsFolder();\r
+ byte[] bytes = Base64.decode(base64);\r
+ FileUtils.writeFile(rootFolder.resolve(fileName).toFile(), bytes);\r
+ \r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ \r
+ public static Map<String, Long> getUploadedFiles() {\r
+ return FileImportService.getPathsAndResources();\r
+ }\r
+ \r
+ public static void removeFileForId(long id) {\r
+ FileImportService.removeFileForResource(id, Optional.empty());\r
+ }\r
+ \r
+}\r
} catch (IllegalAccessException e) {\r
throw new Error(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
@Override\r
\r
GF.HasResourceName <R L0.HasProperty\r
L0.HasDomain GF.SystemResource\r
- L0.HasRange L0.String
\ No newline at end of file
+ L0.HasRange L0.String\r
+\r
+GF.SystemPath <R L0.HasProperty\r
+ L0.HasDomain GF.SystemResource\r
+ L0.HasRange L0.String\r
public final Resource LastModified;\r
public final Resource LastModified_Inverse;\r
public final Resource PartOfSystemResource;\r
+ public final Resource SystemPath;\r
+ public final Resource SystemPath_Inverse;\r
public final Resource SystemResource;\r
\r
public static class URIs {\r
public static final String LastModified = "http://www.simantics.org/GraphFile-0.1/LastModified";\r
public static final String LastModified_Inverse = "http://www.simantics.org/GraphFile-0.1/LastModified/Inverse";\r
public static final String PartOfSystemResource = "http://www.simantics.org/GraphFile-0.1/PartOfSystemResource";\r
+ public static final String SystemPath = "http://www.simantics.org/GraphFile-0.1/SystemPath";\r
+ public static final String SystemPath_Inverse = "http://www.simantics.org/GraphFile-0.1/SystemPath/Inverse";\r
public static final String SystemResource = "http://www.simantics.org/GraphFile-0.1/SystemResource";\r
}\r
\r
LastModified = getResourceOrNull(graph, URIs.LastModified);\r
LastModified_Inverse = getResourceOrNull(graph, URIs.LastModified_Inverse);\r
PartOfSystemResource = getResourceOrNull(graph, URIs.PartOfSystemResource);\r
+ SystemPath = getResourceOrNull(graph, URIs.SystemPath);\r
+ SystemPath_Inverse = getResourceOrNull(graph, URIs.SystemPath_Inverse);\r
SystemResource = getResourceOrNull(graph, URIs.SystemResource);\r
}\r
\r
import java.io.IOException;\r
import java.nio.ByteBuffer;\r
import java.nio.channels.FileChannel;\r
+import java.nio.file.Path;\r
import java.util.Collection;\r
import java.util.HashMap;\r
import java.util.Map;\r
import org.simantics.db.WriteGraph;\r
import org.simantics.db.common.request.ReadRequest;\r
import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.request.WriteResultRequest;\r
import org.simantics.db.common.utils.LiteralFileUtil;\r
import org.simantics.db.exception.DatabaseException;\r
import org.simantics.db.exception.DoesNotContainValueException;\r
}\r
}\r
}\r
+\r
+ public static Resource createFileReference(final Resource parent, final Path path) throws DatabaseException {\r
+ return Simantics.getSession().syncRequest(new WriteResultRequest<Resource>() {\r
+\r
+ @Override\r
+ public Resource perform(WriteGraph graph) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ GraphFileResource GF = GraphFileResource.getInstance(graph);\r
+ Resource file = graph.newResource();\r
+ graph.claim(file, L0.PartOf, parent);\r
+ graph.claim(file, L0.InstanceOf, GF.File);\r
+ String name = path.getFileName().toString();\r
+ graph.claimLiteral(file, L0.HasName, name, Bindings.STRING);\r
+ graph.claimLiteral(file, GF.SystemPath, path.toAbsolutePath().toString(), Bindings.STRING);\r
+ return file;\r
+ }\r
+ });\r
+ }\r
}\r
e.printStackTrace();\r
//Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
} catch (InvocationTargetException e) {\r
- e.printStackTrace();\r
+ e.getCause().printStackTrace();\r
//Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
}\r
}\r
}\r
});\r
} catch (InvocationTargetException e) {\r
- ErrorLogger.defaultLogError(e);\r
+ ErrorLogger.defaultLogError(e.getCause());\r
} catch (InterruptedException e) {\r
ErrorLogger.defaultLogError(e);\r
}\r
VP.VisualsContribution.HasNodeType MBC.Variable\r
VP.VisualsContribution.HasRule MBC.VariableLabelRule\r
\r
+\r
+// Tooltips\r
+MBC\r
+ VP.BrowseContext.HasVisualsContribution MOD.Contributions.VariableTooltip : VP.VisualsContribution\r
+ VP.VisualsContribution.HasNodeType MBC.Variable\r
+ VP.VisualsContribution.HasRule VP.DescriptionTooltipRule\r
+\r
// Images\r
MBC\r
@VP.namedCustomImageRule MOD.Contributions.SubscriptionImage MOD.Subscription MBC.SubscriptionImageRule\r
public final Resource Contributions_VariableChildren;\r
public final Resource Contributions_VariableImage;\r
public final Resource Contributions_VariableLabel;\r
+ public final Resource Contributions_VariableTooltip;\r
public final Resource DefaultStructuralActionContext;\r
public final Resource DefaultStructuralBrowseContext;\r
public final Resource DefaultStructuralImageContext;\r
public static final String Contributions_VariableChildren = "http://www.simantics.org/Modeling-1.2/Contributions/VariableChildren";\r
public static final String Contributions_VariableImage = "http://www.simantics.org/Modeling-1.2/Contributions/VariableImage";\r
public static final String Contributions_VariableLabel = "http://www.simantics.org/Modeling-1.2/Contributions/VariableLabel";\r
+ public static final String Contributions_VariableTooltip = "http://www.simantics.org/Modeling-1.2/Contributions/VariableTooltip";\r
public static final String DefaultStructuralActionContext = "http://www.simantics.org/Modeling-1.2/DefaultStructuralActionContext";\r
public static final String DefaultStructuralBrowseContext = "http://www.simantics.org/Modeling-1.2/DefaultStructuralBrowseContext";\r
public static final String DefaultStructuralImageContext = "http://www.simantics.org/Modeling-1.2/DefaultStructuralImageContext";\r
Contributions_VariableChildren = getResourceOrNull(graph, URIs.Contributions_VariableChildren);\r
Contributions_VariableImage = getResourceOrNull(graph, URIs.Contributions_VariableImage);\r
Contributions_VariableLabel = getResourceOrNull(graph, URIs.Contributions_VariableLabel);\r
+ Contributions_VariableTooltip = getResourceOrNull(graph, URIs.Contributions_VariableTooltip);\r
DefaultStructuralActionContext = getResourceOrNull(graph, URIs.DefaultStructuralActionContext);\r
DefaultStructuralBrowseContext = getResourceOrNull(graph, URIs.DefaultStructuralBrowseContext);\r
DefaultStructuralImageContext = getResourceOrNull(graph, URIs.DefaultStructuralImageContext);\r
try {\r
throw new InvocationTargetException(e);\r
} catch (InvocationTargetException e1) {\r
- // TODO Auto-generated catch block\r
- e1.printStackTrace();\r
+ e1.getCause().printStackTrace();\r
}\r
e.printStackTrace();\r
}\r
}\r
});\r
} catch (InvocationTargetException e) {\r
- ErrorLogger.defaultLogError(e);\r
+ ErrorLogger.defaultLogError(e.getCause());\r
} catch (InterruptedException e) {\r
ErrorLogger.defaultLogError(e);\r
}\r
return new File(fileName);\r
\r
}\r
+ \r
+ public static Resource createLibrary(WriteGraph graph, Resource parent) throws DatabaseException {\r
+ Layer0 l0 = Layer0.getInstance(graph);\r
+ return createLibrary(graph, parent, NameUtils.findFreshName(graph, "Library", parent, l0.ConsistsOf));\r
+ }\r
+ \r
+ public static Resource createLibrary(WriteGraph graph, Resource parent, String name) throws DatabaseException {\r
+ graph.markUndoPoint();\r
+ Layer0 l0 = Layer0.getInstance(graph);\r
+\r
+ Resource library = graph.newResource();\r
+ graph.claim(library, l0.InstanceOf, null, l0.Library);\r
+ graph.addLiteral(library, l0.HasName, l0.NameOf, l0.String, name, Bindings.STRING);\r
+ graph.claim(library, l0.PartOf, parent);\r
+\r
+ Layer0Utils.addCommentMetadata(graph, "Created new Library named " + name + ", resource " + library);\r
+\r
+ return library;\r
+ }\r
+\r
\r
}\r
package org.simantics.modeling.scl;\r
\r
+import java.util.Collection;\r
+\r
import org.simantics.Simantics;\r
import org.simantics.db.ReadGraph;\r
import org.simantics.db.RequestProcessorSpecific;\r
import org.simantics.scl.compiler.source.StringModuleSource;\r
import org.simantics.scl.compiler.source.repository.ModuleSourceRepository;\r
import org.simantics.scl.runtime.SCLContext;\r
+import org.simantics.scl.runtime.tuple.Tuple0;\r
\r
import gnu.trove.procedure.TObjectProcedure;\r
import gnu.trove.set.hash.THashSet;\r
result.add(graph.getURI(module));\r
}\r
}\r
+ \r
+ Collection<Resource> ontologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE);\r
+ for (Resource ontology : ontologies) {\r
+ for(Resource module : ModelingUtils.searchByType(graph, ontology, L0.SCLModule))\r
+ result.add(graph.getURI(module));\r
+ }\r
+ \r
return result;\r
}\r
});\r
childMaps = null;\r
ontology = null;\r
}\r
+ \r
+ @Override\r
+ public String toString() {\r
+ return new StringBuilder().append("OntologyModule ").append(getName()).toString();\r
+ }\r
}\r
if(name != null)\r
g.claimLiteral(terminalRelation, L0.HasName, name);\r
\r
- for(Resource type : g.getObjects(relation, MOD.ImpliesDiagramConnectionRelationType))\r
- g.claim(terminalRelation, L0.InstanceOf, type);\r
+ boolean interfaceGeneratesComponentExternally = !g.hasStatement(relation, MOD.GeneratesConnectionComponentInternally);\r
+ for(Resource type : g.getObjects(relation, MOD.ImpliesDiagramConnectionRelationType)) {\r
+ // #6636: Only instantiate type if it does not generate a component\r
+ // when interface is marked to generate component internally.\r
+ boolean shouldInstantiate = interfaceGeneratesComponentExternally || \r
+ g.getAssertedObjects(type, MOD.DiagramConnectionRelationToComponentType).isEmpty();\r
+ if (shouldInstantiate)\r
+ g.claim(terminalRelation, L0.InstanceOf, type);\r
+ }\r
\r
StructuralUtils.addConnectionPoint(g, definedElement, terminalRelation);\r
}\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
};\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
return removing.size() > 0 || adding.size() > 0;\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
};\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
return true;\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
};\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
return true;\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
};\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
return true;\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
};\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
return removing.size() > 0 || adding.size() > 0;\r
\r
} catch (IllegalAccessException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
}\r
}\r
\r
} catch (IllegalArgumentException e) {\r
throw new MappingException(e);\r
} catch (InvocationTargetException e) {\r
- throw new MappingException(e);\r
+ throw new MappingException(e.getCause());\r
} \r
}\r
\r
public class ServerManagerFactory {\r
public static ServerManager create(String databaseId, String address) throws IOException, DatabaseException {\r
Driver driver = Manager.getDriver(databaseId);\r
+ if (driver == null)\r
+ throw new IllegalArgumentException("Database driver for ID " + databaseId + " Could not be found!");\r
System.out.println("ServerManagerFactory.create called with databaseId=" + databaseId + " and driver is " + driver.toString());\r
DatabaseUserAgent agent = Manager.getUserAgent(databaseId);\r
if (agent != null)\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
};\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
// TODO Auto-generated catch block\r
- e.printStackTrace();\r
+ e.getCause().printStackTrace();\r
}\r
} else {\r
\r
this.module = module;
this.filter = filter;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ModuleImport \"").append(module).append("\"").append(" with filter ").append(filter);
+ return sb.toString();
+ }
}
public NamespaceImpl(THashMap<String, Namespace> namespaceMap,
return 1;\r
return description.compareTo(o.description);\r
}\r
+ \r
+ @Override\r
+ public String toString() {\r
+ return new StringBuilder().append("CompilationError: \"").append(description).append("\" at location ").append(location).toString();\r
+ }\r
}\r
b.append(error.description).append('\n');
return b.toString();
}
+
+ @Override
+ public String toString() {
+ return getErrorsAsString();
+ }
}
package org.simantics.scl.compiler.runtime;
+import java.util.Collection;
+
import org.simantics.scl.compiler.environment.Environment;
public interface RuntimeEnvironment {
Environment getEnvironment();
RuntimeModule getRuntimeModule(String name);
+ Collection<RuntimeModule> getRuntimeModules();
MutableClassLoader getMutableClassLoader();
}
package org.simantics.scl.compiler.runtime;
+import java.util.Collection;
+
import org.simantics.scl.compiler.environment.Environment;
import gnu.trove.map.hash.THashMap;
return classLoader;
}
+ @Override
+ public Collection<RuntimeModule> getRuntimeModules() {
+ return runtimeModuleMap.values();
+ }
+
}
public static final TCon VEC_COMP = con(BUILTIN, "VecComp");
public static final TCon BINDING = con(BUILTIN, "Binding");
+ public static final TCon TYPE = con(BUILTIN, "Type");
+
public static final TCon DYNAMIC = con("Prelude", "Dynamic");
public static final TCon VARIANT = con(BUILTIN, "Variant");
} catch (IllegalAccessException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- //e.printStackTrace();\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
\r
import java.lang.reflect.Method;\r
\r
import org.simantics.scl.runtime.function.FunctionImpl3;\r
-import org.simantics.scl.runtime.function.FunctionImplN;\r
\r
public class ClassMethodFunction3 extends FunctionImpl3 {\r
Method method;\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- //e.printStackTrace();\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
\r
runnable.run(null);\r
}\r
} catch (InvocationTargetException e) {\r
- Activator.logError("Experiment manager shutdown failed, see exception for details.", e);\r
+ Activator.logError("Experiment manager shutdown failed, see exception for details.", e.getCause());\r
} catch (InterruptedException e) {\r
Activator.logError("Experiment manager shutdown was interrupted, see exception for details.", e);\r
}\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.spreadsheet.fileimport</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ds.core.builder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\r
+org.eclipse.jdt.core.compiler.compliance=1.8\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.8\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Fileimport
+Bundle-SymbolicName: org.simantics.spreadsheet.fileimport
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.simantics.spreadsheet.fileimport.Activator
+Require-Bundle: org.eclipse.core.runtime,
+ org.simantics.fileimport,
+ org.simantics.db
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Service-Component: OSGI-INF/component.xml
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.simantics.excel.fileimport">\r
+ <service>\r
+ <provide interface="org.simantics.fileimport.IGenericFileImport"/>\r
+ </service>\r
+ <implementation class="org.simantics.spreadsheet.fileimport.ExcelFileImport"/>\r
+</scr:component>\r
--- /dev/null
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .,\\r
+ OSGI-INF/component.xml\r
+source.. = src/\r
--- /dev/null
+package org.simantics.spreadsheet.fileimport;\r
+\r
+import org.osgi.framework.BundleActivator;\r
+import org.osgi.framework.BundleContext;\r
+\r
+public class Activator implements BundleActivator {\r
+\r
+ private static BundleContext context;\r
+\r
+ static BundleContext getContext() {\r
+ return context;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)\r
+ */\r
+ public void start(BundleContext bundleContext) throws Exception {\r
+ Activator.context = bundleContext;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)\r
+ */\r
+ public void stop(BundleContext bundleContext) throws Exception {\r
+ Activator.context = null;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.spreadsheet.fileimport;\r
+\r
+import java.nio.file.Path;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.simantics.db.Resource;\r
+import org.simantics.fileimport.SimanticsResourceFileImport;\r
+\r
+public class ExcelFileImport extends SimanticsResourceFileImport {\r
+\r
+ private static final Map<String, String> ALLOWED_EXTENSIONS = new HashMap<>(2);\r
+ \r
+ static {\r
+ ALLOWED_EXTENSIONS.put("*.xls", "Excel file *.xls");\r
+ ALLOWED_EXTENSIONS.put("*.xlsx", "Excel file *.xlsx");\r
+ }\r
+ \r
+ @Override\r
+ public Optional<Resource> perform(Resource parent, Path file) {\r
+ return Optional.empty();\r
+ }\r
+\r
+ @Override\r
+ public Map<String, String> allowedExtensionsWithFilters() {\r
+ return ALLOWED_EXTENSIONS;\r
+ }\r
+\r
+}\r
public static Object compileAndEvaluate(ReadGraph graph, Variable context) throws DatabaseException {\r
SCLContext sclContext = SCLContext.getCurrent();\r
Object oldGraph = sclContext.get("graph");\r
+ CompileStructuralValueRequest request = new CompileStructuralValueRequest(graph, context);\r
try {\r
- Function1<Variable,Object> exp = graph.syncRequest(new CompileStructuralValueRequest(graph, context),\r
- TransientCacheListener.<Function1<Variable,Object>>instance());\r
+ Function1<Variable,Object> exp = graph.syncRequest(request, TransientCacheListener.<Function1<Variable,Object>>instance());\r
sclContext.put("graph", graph);\r
return exp.apply(context);\r
- } catch (DatabaseException e) {\r
- throw (DatabaseException)e;\r
} catch (Throwable t) {\r
- throw new DatabaseException(t);\r
+ throw new DatabaseException("Compiling structural value request for component=" + request.component + ", literal=" + request.literal + " and relation " + request.relation + " failed!", t);\r
} finally {\r
sclContext.put("graph", oldGraph);\r
}\r
return stack;\r
}\r
\r
+ public static void openAndShowPart(MPart part) {\r
+ IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class);\r
+ EPartService partService = context.get(EPartService.class);\r
+ if (!partService.isPartVisible(part))\r
+ partService.showPart(part, PartState.ACTIVATE);\r
+ else\r
+ partService.activate(part);\r
+ }\r
+\r
+ \r
+ public static void openAndShowPart(String partId) {\r
+ IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class);\r
+ EPartService partService = context.get(EPartService.class);\r
+ partService.showPart(partId, PartState.ACTIVATE);\r
+ }\r
+\r
+ public static MPart getMPartById(String partId) {\r
+ IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class);\r
+ EPartService partService = context.get(EPartService.class);\r
+ return partService.findPart(partId);\r
+ }\r
+\r
}\r
new ProgressMonitorDialog(shell).run(true, false, undo);\r
actionBars.getStatusLineManager().setMessage( msg.get() );\r
} catch (InvocationTargetException e1) {\r
- throw new ExecutionException("Undo failed, database failure.", e1);\r
+ throw new ExecutionException("Undo failed, database failure.", e1.getCause());\r
} catch (InterruptedException e1) {\r
throw new ExecutionException("Undo failed, interrupted.", e1);\r
}\r
manager.setMessage(msg.get());\r
// statusManager.setMessage( msg.get() );\r
} catch (InvocationTargetException e1) {\r
- throw new ExecutionException("Undo failed, database failure.", e1);\r
+ throw new ExecutionException("Undo failed, database failure.", e1.getCause());\r
} catch (InterruptedException e1) {\r
throw new ExecutionException("Undo failed, interrupted.", e1);\r
}\r
*/\r
@Deprecated\r
@FunctionalInterface\r
-public interface Callback<T> {\r
+public interface Callback<T> extends Consumer<T> {\r
\r
void run(T parameter);\r
- \r
+\r
+ default void accept(T parameter) {\r
+ run(parameter);\r
+ }\r
}\r
} catch (InterruptedException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- e.printStackTrace();\r
+ e.getCause().printStackTrace();\r
}\r
}\r
};\r
} catch (IllegalAccessException e) {\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- e.printStackTrace();\r
+ e.getCause().printStackTrace();\r
}\r
}\r
}\r
} catch (InterruptedException e) {\r
throw new RuntimeException(e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException(e);\r
+ throw new RuntimeException(e.getCause());\r
}\r
}\r
}\r
} catch (IllegalAccessException e) {\r
Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
} catch (InvocationTargetException e) {\r
- Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
+ Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getCause().getMessage(), e.getCause()));\r
}\r
}\r
}\r
} catch (InterruptedException e) {\r
SWT.error(SWT.ERROR_FAILED_EXEC, e);\r
} catch (InvocationTargetException e) {\r
- SWT.error(SWT.ERROR_FAILED_EXEC, e);\r
+ SWT.error(SWT.ERROR_FAILED_EXEC, e.getCause());\r
}\r
\r
this.display = display;\r
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ e.getCause().printStackTrace();
}
}
}
VP.ResourceLabelLabelRule : VP.LabelRule\r
L0.HasDescription """Label rule based on resource labels."""\r
\r
+VP.TooltipRule <T VP.VisualsRule\r
+ L0.HasDescription """Tooltip rule gives a tooltip to a node."""\r
+VP.DescriptionTooltipRule : VP.TooltipRule\r
+ L0.HasDescription """Tooltip rule that reads tooltip from node's L0.HasDescription."""\r
+\r
VP.ModifierRule <T VP.VisualsRule\r
L0.HasDescription """Modifier rule gives a modifier to a node."""\r
VP.NoModifierRule : VP.ModifierRule\r
VP.VisualsContribution.HasNodeType %parentType\r
VP.VisualsContribution.HasRule %modifierRule\r
\r
+VP.namedCustomModifierRule : L0.Template\r
+ @template %browseContext %name %parentType %modifierRule\r
+ %browseContext\r
+ VP.BrowseContext.HasVisualsContribution _ : VP.VisualsContribution\r
+ VP.VisualsContribution.HasNodeType %parentType\r
+ VP.VisualsContribution.HasRule %modifierRule\r
+\r
VP.namedCustomVisualsRule : L0.Template\r
@template %browseContext %name %parentType %labelRule\r
%browseContext\r
public final Resource ConstantLabelRule_HasColumnKey_Inverse;\r
public final Resource ConstantLabelRule_HasLabel;\r
public final Resource ConstantLabelRule_HasLabel_Inverse;\r
+ public final Resource DescriptionTooltipRule;\r
public final Resource DropActionContribution;\r
public final Resource DropActionContribution_HasAction;\r
public final Resource DropActionContribution_HasCondition;\r
public final Resource TestContribution_HasPriority;\r
public final Resource TestContribution_HasPriority_Inverse;\r
public final Resource TestContribution_HasTest;\r
+ public final Resource TooltipRule;\r
public final Resource VariableLabelModifierRule;\r
public final Resource VisualsContribution;\r
public final Resource VisualsContribution_HasCondition;\r
public static final String ConstantLabelRule_HasColumnKey_Inverse = "http://www.simantics.org/Viewpoint-1.2/ConstantLabelRule/HasColumnKey/Inverse";\r
public static final String ConstantLabelRule_HasLabel = "http://www.simantics.org/Viewpoint-1.2/ConstantLabelRule/HasLabel";\r
public static final String ConstantLabelRule_HasLabel_Inverse = "http://www.simantics.org/Viewpoint-1.2/ConstantLabelRule/HasLabel/Inverse";\r
+ public static final String DescriptionTooltipRule = "http://www.simantics.org/Viewpoint-1.2/DescriptionTooltipRule";\r
public static final String DropActionContribution = "http://www.simantics.org/Viewpoint-1.2/DropActionContribution";\r
public static final String DropActionContribution_HasAction = "http://www.simantics.org/Viewpoint-1.2/DropActionContribution/HasAction";\r
public static final String DropActionContribution_HasCondition = "http://www.simantics.org/Viewpoint-1.2/DropActionContribution/HasCondition";\r
public static final String TestContribution_HasPriority = "http://www.simantics.org/Viewpoint-1.2/TestContribution/HasPriority";\r
public static final String TestContribution_HasPriority_Inverse = "http://www.simantics.org/Viewpoint-1.2/TestContribution/HasPriority/Inverse";\r
public static final String TestContribution_HasTest = "http://www.simantics.org/Viewpoint-1.2/TestContribution/HasTest";\r
+ public static final String TooltipRule = "http://www.simantics.org/Viewpoint-1.2/TooltipRule";\r
public static final String VariableLabelModifierRule = "http://www.simantics.org/Viewpoint-1.2/VariableLabelModifierRule";\r
public static final String VisualsContribution = "http://www.simantics.org/Viewpoint-1.2/VisualsContribution";\r
public static final String VisualsContribution_HasCondition = "http://www.simantics.org/Viewpoint-1.2/VisualsContribution/HasCondition";\r
ConstantLabelRule_HasColumnKey_Inverse = getResourceOrNull(graph, URIs.ConstantLabelRule_HasColumnKey_Inverse);\r
ConstantLabelRule_HasLabel = getResourceOrNull(graph, URIs.ConstantLabelRule_HasLabel);\r
ConstantLabelRule_HasLabel_Inverse = getResourceOrNull(graph, URIs.ConstantLabelRule_HasLabel_Inverse);\r
+ DescriptionTooltipRule = getResourceOrNull(graph, URIs.DescriptionTooltipRule);\r
DropActionContribution = getResourceOrNull(graph, URIs.DropActionContribution);\r
DropActionContribution_HasAction = getResourceOrNull(graph, URIs.DropActionContribution_HasAction);\r
DropActionContribution_HasCondition = getResourceOrNull(graph, URIs.DropActionContribution_HasCondition);\r
TestContribution_HasPriority = getResourceOrNull(graph, URIs.TestContribution_HasPriority);\r
TestContribution_HasPriority_Inverse = getResourceOrNull(graph, URIs.TestContribution_HasPriority_Inverse);\r
TestContribution_HasTest = getResourceOrNull(graph, URIs.TestContribution_HasTest);\r
+ TooltipRule = getResourceOrNull(graph, URIs.TooltipRule);\r
VariableLabelModifierRule = getResourceOrNull(graph, URIs.VariableLabelModifierRule);\r
VisualsContribution = getResourceOrNull(graph, URIs.VisualsContribution);\r
VisualsContribution_HasCondition = getResourceOrNull(graph, URIs.VisualsContribution_HasCondition);\r
} catch (IllegalAccessException e) {\r
e.printStackTrace();\r
} catch (InvocationTargetException e) {\r
- e.printStackTrace();\r
+ e.getCause().printStackTrace();\r
}\r
}\r
\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);\r
+ throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e.getCause());\r
}\r
\r
} else {\r
} catch (IllegalAccessException e) {\r
throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);\r
} catch (InvocationTargetException e) {\r
- throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);\r
+ throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e.getCause());\r
}\r
}\r
}\r