From: Jani Simomaa Date: Fri, 26 Aug 2016 11:11:25 +0000 (+0300) Subject: Merge "Testing SonarQube with Simantics Platform SDK" X-Git-Tag: v1.25.0~155 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=86e91ef31d25e4d5950cae130754846d15119c61;hp=3827e346eb6d1f6049e399b8dd7eb27c625bf4f5 Merge "Testing SonarQube with Simantics Platform SDK" --- diff --git a/bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/labelers/LabelerStub.java b/bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/labelers/LabelerStub.java index 122c9ed51..468df9ae8 100644 --- a/bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/labelers/LabelerStub.java +++ b/bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/labelers/LabelerStub.java @@ -13,7 +13,10 @@ package org.simantics.browsing.ui.common.labelers; import java.util.Map; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; import org.simantics.browsing.ui.GraphExplorer.ModificationContext; +import org.simantics.browsing.ui.NodeContext; import org.simantics.browsing.ui.content.Labeler; /** @@ -21,6 +24,10 @@ import org.simantics.browsing.ui.content.Labeler; * * @author Tuukka Lehtonen */ +/** + * @author Jani Simomaa + * + */ public class LabelerStub implements Labeler { protected LabelerContent content = LabelerContent.NO_CONTENT; @@ -64,4 +71,23 @@ public class LabelerStub implements Labeler { public void setListener(LabelerListener listener) { } + /** + * @param event + * @param parent + * @param nodeContext + * @return + */ + public Composite createToolTipContentArea(Event event, Composite parent, NodeContext nodeContext) { + return null; + } + + /** + * @param event + * @param nodeContext + * @return + */ + public boolean shouldCreateToolTip(Event event, NodeContext nodeContext) { + return false; + } + } diff --git a/bundles/org.simantics.browsing.ui.graph.impl/src/org/simantics/browsing/ui/graph/impl/EvaluatorLabeler.java b/bundles/org.simantics.browsing.ui.graph.impl/src/org/simantics/browsing/ui/graph/impl/EvaluatorLabeler.java index 222c0e261..9e0b7b538 100644 --- a/bundles/org.simantics.browsing.ui.graph.impl/src/org/simantics/browsing/ui/graph/impl/EvaluatorLabeler.java +++ b/bundles/org.simantics.browsing.ui.graph.impl/src/org/simantics/browsing/ui/graph/impl/EvaluatorLabeler.java @@ -3,19 +3,28 @@ package org.simantics.browsing.ui.graph.impl; import java.util.Map; import org.simantics.browsing.ui.BuiltinKeys.LabelerKey; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.simantics.Simantics; import org.simantics.browsing.ui.NodeContext; import org.simantics.browsing.ui.PrimitiveQueryUpdater; import org.simantics.browsing.ui.graph.impl.contribution.LabelerContributionImpl; import org.simantics.browsing.ui.model.browsecontexts.BrowseContext; +import org.simantics.browsing.ui.model.tooltips.TooltipContribution; import org.simantics.db.ReadGraph; import org.simantics.db.UndoContext; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.utils.RequestUtil; import org.simantics.db.exception.DatabaseException; +import org.simantics.ui.SimanticsUI; public class EvaluatorLabeler extends LabelerContributionImpl { final BrowseContext browseContext; final boolean useNodeBrowseContexts; + private TooltipContribution currentTooltipContribution; + public EvaluatorLabeler(PrimitiveQueryUpdater updater, NodeContext context, LabelerKey key, BrowseContext browseContext, boolean useNodeBrowseContexts) { @@ -54,5 +63,32 @@ public class EvaluatorLabeler extends LabelerContributionImpl { return "EvaluatorLabeler[" + browseContext + "] " + context; } + + @Override + public boolean shouldCreateToolTip(Event event, NodeContext context) { + try { + currentTooltipContribution = RequestUtil.trySyncRequest( + Simantics.getSession(), + SimanticsUI.UI_THREAD_REQUEST_START_TIMEOUT, + SimanticsUI.UI_THREAD_REQUEST_EXECUTION_TIMEOUT, + null, + new UniqueRead() { + @Override + public TooltipContribution perform(ReadGraph graph) throws DatabaseException { + return BrowseContext.get(graph,context,browseContext,useNodeBrowseContexts).shouldCreateToolTip(graph, event, context); + } + }); + if (currentTooltipContribution != null) + return true; + } catch (DatabaseException | InterruptedException e) { + e.printStackTrace(); + } + return false; + } + + @Override + public Composite createToolTipContentArea(Event event, Composite parent, NodeContext nodeContext) { + return (Composite) currentTooltipContribution.getTooltip(event, parent, nodeContext); + } } diff --git a/bundles/org.simantics.browsing.ui.model/META-INF/MANIFEST.MF b/bundles/org.simantics.browsing.ui.model/META-INF/MANIFEST.MF index 1772d8a88..7367b15df 100644 --- a/bundles/org.simantics.browsing.ui.model/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.browsing.ui.model/META-INF/MANIFEST.MF @@ -26,5 +26,6 @@ Export-Package: org.simantics.browsing.ui.model, 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 diff --git a/bundles/org.simantics.browsing.ui.model/adapters.xml b/bundles/org.simantics.browsing.ui.model/adapters.xml index bd4be8c9b..7a2d83f2e 100644 --- a/bundles/org.simantics.browsing.ui.model/adapters.xml +++ b/bundles/org.simantics.browsing.ui.model/adapters.xml @@ -92,7 +92,7 @@ uri="http://www.simantics.org/Viewpoint-0.0/PassThruSorterRule" class="org.simantics.browsing.ui.model.sorters.PassThruSorterRule"/> - + diff --git a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java index 50281f20d..a1eaf78a8 100644 --- a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java +++ b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java @@ -23,6 +23,7 @@ import java.util.Set; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Shell; import org.simantics.browsing.ui.BuiltinKeys; import org.simantics.browsing.ui.CheckedState; @@ -52,6 +53,7 @@ import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType; import org.simantics.browsing.ui.model.sorters.AlphanumericSorter; import org.simantics.browsing.ui.model.sorters.Sorter; import org.simantics.browsing.ui.model.sorters.SorterContribution; +import org.simantics.browsing.ui.model.tooltips.TooltipContribution; import org.simantics.browsing.ui.model.visuals.FlatNodeContribution; import org.simantics.browsing.ui.model.visuals.VisualsContribution; import org.simantics.db.ReadGraph; @@ -86,6 +88,7 @@ public class BrowseContext { OrderedNodeTypeMultiMap modifierContributions = new OrderedNodeTypeMultiMap(); OrderedNodeTypeMultiMap sorterContributions = new OrderedNodeTypeMultiMap(); OrderedNodeTypeMultiMap flatNodeContributions = new OrderedNodeTypeMultiMap(); + OrderedNodeTypeMultiMap tooltipContributions = new OrderedNodeTypeMultiMap<>(); private final String[] uris; @@ -139,7 +142,8 @@ public class BrowseContext { browseContext.imageDecorationContributions, browseContext.modifierContributions, browseContext.sorterContributions, - browseContext.flatNodeContributions + browseContext.flatNodeContributions, + browseContext.tooltipContributions ); } } @@ -443,6 +447,23 @@ public class BrowseContext { return null; } + public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException { + NodeType nodeType = getNodeType(graph, context); + if(nodeType != null) + for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) { + if (contribution.shouldCreateToolTip(graph, context)) + return contribution; + } + return null; + } + + public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException { + Object tooltip = contribution.getTooltip(event, parent, context); + if (tooltip != null) + return tooltip; + return null; + } + private Graph toGraph() { Graph graph = new Graph(); new Node(graph, "Foo"); diff --git a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/DescriptionTooltipRule.java b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/DescriptionTooltipRule.java new file mode 100644 index 000000000..b581b4cd8 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/DescriptionTooltipRule.java @@ -0,0 +1,90 @@ +package org.simantics.browsing.ui.model.tooltips; + +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; +import org.simantics.browsing.ui.BuiltinKeys; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.layer0.Layer0; + +public class DescriptionTooltipRule implements TooltipRule { + + public static final DescriptionTooltipRule INSTANCE = new DescriptionTooltipRule(); + + public DescriptionTooltipRule() { + } + + @Override + public Object createTooltip(Object event, Object parentComponent, NodeContext context, Map auxiliary) { + Composite parent = (Composite)parentComponent; + Composite composite = new Composite(parent, SWT.NONE); + //ScrolledComposite composite = new ScrolledComposite(parent, SWT.NONE); + + composite.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + GridLayout layout = new GridLayout(1, false); + composite.setLayout(layout); + Text text = new Text(composite, SWT.NONE | SWT.READ_ONLY | SWT.WRAP); + text.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + + String toolTipContent = (String) auxiliary.get("content"); + text.setText(toolTipContent); + + GC gc = new GC(text); + FontMetrics fm = gc.getFontMetrics(); + int width = toolTipContent.length() * fm.getAverageCharWidth(); + gc.dispose(); + GridData gridData = new GridData(); + if (width < 500) + gridData.widthHint = width; + else + gridData.widthHint = 500; + + text.setLayoutData(gridData); + + return composite; + } + + @Override + public boolean isCompatible(Class contentType) { + return (contentType == Resource.class || contentType == Variable.class); + } + + private static String getToolTipContent(ReadGraph graph, NodeContext nodeContext) throws DatabaseException { + Object input = nodeContext.getConstant(BuiltinKeys.INPUT); + String content = null; + if (input instanceof Variable) { + Variable var = (Variable) input; + Resource res = var.getPredicateResource(graph); + Layer0 L0 = Layer0.getInstance(graph); + String description = graph.getPossibleRelatedValue2(res, L0.HasDescription); + return description; + } else if (input instanceof Resource) { + Resource res = (Resource) input; + + Layer0 L0 = Layer0.getInstance(graph); + String description = graph.getPossibleRelatedValue2(res, L0.HasDescription); + return description; + } + return content; + } + + @Override + public boolean shouldCreateToolTip(ReadGraph graph , NodeContext context, Map auxiliary) throws DatabaseException { + String content = getToolTipContent(graph, context); + if (content == null || content.isEmpty()) + return false; + auxiliary.put("content", content); + return true; + } + +} diff --git a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/TooltipContribution.java b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/TooltipContribution.java new file mode 100644 index 000000000..2a9d36d02 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/TooltipContribution.java @@ -0,0 +1,44 @@ +package org.simantics.browsing.ui.model.tooltips; + +import java.util.HashMap; +import java.util.Map; + +import org.simantics.browsing.ui.BuiltinKeys; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.model.InvalidContribution; +import org.simantics.browsing.ui.model.nodetypes.NodeType; +import org.simantics.browsing.ui.model.tests.Test; +import org.simantics.browsing.ui.model.visuals.VisualsContribution; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; + +public class TooltipContribution extends VisualsContribution { + TooltipRule tooltipRule; + + private Map auxiliary = new HashMap<>(); + + public TooltipContribution(NodeType nodeType, Test test, TooltipRule tooltipRule, double priority) throws InvalidContribution { + super(nodeType, test, priority); + if(!tooltipRule.isCompatible(nodeType.getContentType())) + throw new InvalidContribution("Tooltip rule is not compatible with the content type."); + this.tooltipRule = tooltipRule; + } + + public Object getTooltip(Object event, Object parent, NodeContext context) { + try { + return tooltipRule.createTooltip(event, parent, context, auxiliary); + } finally { + auxiliary.clear(); + } + + } + + public boolean shouldCreateToolTip(ReadGraph graph, NodeContext context) throws DatabaseException { + Object content = context.getConstant(BuiltinKeys.INPUT); + if(test != null && !test.test(graph, content)) + return false; + + return tooltipRule.shouldCreateToolTip(graph, context, auxiliary); + } + +} diff --git a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/TooltipRule.java b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/TooltipRule.java new file mode 100644 index 000000000..39b0710e2 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/tooltips/TooltipRule.java @@ -0,0 +1,16 @@ +package org.simantics.browsing.ui.model.tooltips; + +import java.util.Map; + +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.model.visuals.VisualsRule; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; + +public interface TooltipRule extends VisualsRule { + + boolean shouldCreateToolTip(ReadGraph graph, NodeContext context, Map auxiliary) throws DatabaseException; + + Object createTooltip(Object event, Object parentComponent, NodeContext context, Map auxiliary); + +} diff --git a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/visuals/VisualsContribution.java b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/visuals/VisualsContribution.java index a426efd1f..01d2078ce 100644 --- a/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/visuals/VisualsContribution.java +++ b/bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/visuals/VisualsContribution.java @@ -29,6 +29,8 @@ import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap; import org.simantics.browsing.ui.model.sorters.SorterContribution; import org.simantics.browsing.ui.model.sorters.SorterRule; import org.simantics.browsing.ui.model.tests.Test; +import org.simantics.browsing.ui.model.tooltips.TooltipContribution; +import org.simantics.browsing.ui.model.tooltips.TooltipRule; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.exception.AdaptionException; @@ -62,7 +64,8 @@ public class VisualsContribution implements Comparable { OrderedNodeTypeMultiMap imageDecorationContributions, OrderedNodeTypeMultiMap modifierContributions, OrderedNodeTypeMultiMap sorterContributions, - OrderedNodeTypeMultiMap flatNodeContributions) + OrderedNodeTypeMultiMap flatNodeContributions, + OrderedNodeTypeMultiMap tooltipContributions) throws DatabaseException, InvalidContribution { ViewpointResource vr = ViewpointResource.getInstance(g); @@ -102,6 +105,8 @@ public class VisualsContribution implements Comparable { imageDecorationContributions.put(nodeType, new ImageDecorationContribution(nodeType, test, (ImageDecorationRule)rule, priority)); if(rule instanceof SorterRule) sorterContributions.put(nodeType, new SorterContribution(nodeType, test, (SorterRule)rule, priority)); + if(rule instanceof TooltipRule) + tooltipContributions.put(nodeType, new TooltipContribution(nodeType, test, (TooltipRule)rule, priority)); } catch(InvalidContribution e) { e.printStackTrace(); continue; diff --git a/bundles/org.simantics.browsing.ui.nattable/.classpath b/bundles/org.simantics.browsing.ui.nattable/.classpath new file mode 100644 index 000000000..b862a296d --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.browsing.ui.nattable/.project b/bundles/org.simantics.browsing.ui.nattable/.project new file mode 100644 index 000000000..13a05a2d1 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/.project @@ -0,0 +1,28 @@ + + + org.simantics.browsing.ui.nattable + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.browsing.ui.nattable/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.browsing.ui.nattable/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..295926d96 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.simantics.browsing.ui.nattable/META-INF/MANIFEST.MF b/bundles/org.simantics.browsing.ui.nattable/META-INF/MANIFEST.MF new file mode 100644 index 000000000..47fd31cae --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +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 diff --git a/bundles/org.simantics.browsing.ui.nattable/build.properties b/bundles/org.simantics.browsing.ui.nattable/build.properties new file mode 100644 index 000000000..41eb6ade2 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/Activator.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/Activator.java new file mode 100644 index 000000000..55e1944a7 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/Activator.java @@ -0,0 +1,50 @@ +package org.simantics.browsing.ui.nattable; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "og.simantics.browsing.ui.nattable"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEColumnAccessor.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEColumnAccessor.java new file mode 100644 index 000000000..4ccc2a7c3 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEColumnAccessor.java @@ -0,0 +1,60 @@ +package org.simantics.browsing.ui.nattable; + +import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor; +import org.simantics.browsing.ui.Column; +import org.simantics.browsing.ui.content.Labeler.Modifier; + + +public class GEColumnAccessor implements IColumnPropertyAccessor { + NatTableGraphExplorer ge; + + public GEColumnAccessor(NatTableGraphExplorer ge) { + this.ge = ge; + } + + @Override + public int getColumnCount() { + return ge.getColumns().length; + } + + @Override + public Object getDataValue(TreeNode rowObject, int columnIndex) { + + if (columnIndex > 0) + return rowObject.getValueString(columnIndex); + else { + String val = ""; + for (int i = 0 ; i 0) + dataLayer.setColumnWidthByPosition(i, w); + } + } + + public DataLayer getDataLayer() { + return dataLayer; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEEditBindings.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEEditBindings.java new file mode 100644 index 000000000..19ada0233 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEEditBindings.java @@ -0,0 +1,56 @@ +package org.simantics.browsing.ui.nattable; + +import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration; +import org.eclipse.nebula.widgets.nattable.edit.action.CellEditDragMode; +import org.eclipse.nebula.widgets.nattable.edit.action.KeyEditAction; +import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction; +import org.eclipse.nebula.widgets.nattable.grid.GridRegion; +import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter; +import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; +import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry; +import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher; +import org.eclipse.nebula.widgets.nattable.ui.matcher.CellPainterMouseEventMatcher; +import org.eclipse.nebula.widgets.nattable.ui.matcher.KeyEventMatcher; +import org.eclipse.nebula.widgets.nattable.ui.matcher.LetterOrDigitKeyEventMatcher; +import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher; +import org.eclipse.swt.SWT; + +public class GEEditBindings extends AbstractUiBindingConfiguration { + + @Override + public void configureUiBindings(UiBindingRegistry uiBindingRegistry) { + // configure the space key to activate a cell editor via keyboard + // this is especially useful for changing the value for a checkbox + uiBindingRegistry.registerKeyBinding( + new KeyEventMatcher(SWT.NONE, 32), + new KeyEditAction()); + uiBindingRegistry.registerKeyBinding( + new KeyEventMatcher(SWT.NONE, SWT.F2), + new KeyEditAction()); + uiBindingRegistry.registerKeyBinding( + new LetterOrDigitKeyEventMatcher(), + new KeyEditAction()); + uiBindingRegistry.registerKeyBinding( + new LetterOrDigitKeyEventMatcher(SWT.MOD2), + new KeyEditAction()); + + uiBindingRegistry.registerSingleClickBinding( + new SelectedCellEditorMatcher(GridRegion.BODY), + new MouseEditAction()); + + uiBindingRegistry.registerMouseDragMode( + new CellEditorMouseEventMatcher(GridRegion.BODY), + new CellEditDragMode()); + + uiBindingRegistry.registerFirstSingleClickBinding( + new CellPainterMouseEventMatcher(GridRegion.BODY, MouseEventMatcher.LEFT_BUTTON, CheckBoxPainter.class), + new MouseEditAction()); + + uiBindingRegistry.registerFirstMouseDragMode( + new CellPainterMouseEventMatcher(GridRegion.BODY, MouseEventMatcher.LEFT_BUTTON, CheckBoxPainter.class), + new CellEditDragMode()); + + } + + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEIconPainter.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEIconPainter.java new file mode 100644 index 000000000..8d7ad9bcd --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEIconPainter.java @@ -0,0 +1,193 @@ +package org.simantics.browsing.ui.nattable; + +import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; +import org.eclipse.nebula.widgets.nattable.layer.ILayer; +import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; +import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter; +import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; +import org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter; +import org.eclipse.nebula.widgets.nattable.resize.command.ColumnResizeCommand; +import org.eclipse.nebula.widgets.nattable.resize.command.RowResizeCommand; +import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; +import org.eclipse.nebula.widgets.nattable.style.CellStyleUtil; +import org.eclipse.nebula.widgets.nattable.style.IStyle; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; + +/** + * Modified org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter, which does not allow setting wrapped painter + * + * @author Marko Luukkainen + * + */ +public class GEIconPainter extends BackgroundPainter { + + protected boolean calculateByWidth; + protected boolean calculateByHeight; + + public GEIconPainter(ICellPainter painter) { + super(painter); + } + + @Override + public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { + Image image = getImage(cell, configRegistry); + if (image != null) { + return image.getBounds().width; + } else { + return 0; + } + } + + @Override + public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { + Image image = getImage(cell, configRegistry); + if (image != null) { + return image.getBounds().height; + } else { + return 0; + } + } + + @Override + public ICellPainter getCellPainterAt(int x, int y, ILayerCell cell, GC gc, + Rectangle bounds, IConfigRegistry configRegistry) { + + Image image = getImage(cell, configRegistry); + if (image != null) { + Rectangle imageBounds = image.getBounds(); + IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry); + int x0 = bounds.x + + CellStyleUtil.getHorizontalAlignmentPadding( + cellStyle, bounds, imageBounds.width); + int y0 = bounds.y + + CellStyleUtil.getVerticalAlignmentPadding( + cellStyle, bounds, imageBounds.height); + if (x >= x0 && x < x0 + imageBounds.width + && y >= y0 && y < y0 + imageBounds.height) { + return super.getCellPainterAt(x, y, cell, gc, bounds, configRegistry); + } + } + return null; + } + + @Override + public void paintCell(ILayerCell cell, GC gc, Rectangle bounds, IConfigRegistry configRegistry) { + + + Image image = getImage(cell, configRegistry); + if (image != null) { + Rectangle imageBounds = image.getBounds(); + IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry); + + int contentHeight = imageBounds.height; + if (this.calculateByHeight && (contentHeight > bounds.height)) { + int contentToCellDiff = (cell.getBounds().height - bounds.height); + ILayer layer = cell.getLayer(); + layer.doCommand(new RowResizeCommand( + layer, + cell.getRowPosition(), + contentHeight + contentToCellDiff)); + } + + int contentWidth = imageBounds.width; + if (this.calculateByWidth && (contentWidth > bounds.width)) { + int contentToCellDiff = (cell.getBounds().width - bounds.width); + ILayer layer = cell.getLayer(); + layer.doCommand(new ColumnResizeCommand( + layer, + cell.getColumnPosition(), + contentWidth + contentToCellDiff)); + } + int px = CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, bounds, imageBounds.width); + int py = CellStyleUtil.getVerticalAlignmentPadding(cellStyle, bounds, imageBounds.height); + Rectangle b = new Rectangle(bounds.x + px + imageBounds.width, bounds.y, bounds.width - px - imageBounds.width, bounds.height); + super.paintCell(cell, gc, b, configRegistry); + gc.drawImage( + image, + bounds.x + px, + bounds.y + py); + } else { + super.paintCell(cell, gc, bounds, configRegistry); + } + } + +// @Override +// public Rectangle getWrappedPainterBounds(ILayerCell cell, GC gc, Rectangle bounds, IConfigRegistry configRegistry) { +// Image image = getImage(cell, configRegistry); +// if (image != null) { +// Rectangle imageBounds = image.getBounds(); +// IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry); +// int px = CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, bounds, imageBounds.width); +// int py = CellStyleUtil.getVerticalAlignmentPadding(cellStyle, bounds, imageBounds.height); +// Rectangle b = new Rectangle(bounds.x + px + imageBounds.width, bounds.y, bounds.width - px - imageBounds.width, bounds.height); +// return b; +// +// } +// return super.getWrappedPainterBounds(cell, gc, bounds, configRegistry); +// } + + /** + * + * @param cell + * The {@link ILayerCell} for which this {@link ImagePainter} is + * called. + * @param configRegistry + * The current {@link IConfigRegistry} to retrieve the cell style + * information from. + * @return The {@link Image} that should be painted by this + * {@link ImagePainter}. + */ + protected Image getImage(ILayerCell cell, IConfigRegistry configRegistry) { + return CellStyleUtil.getCellStyle(cell, configRegistry).getAttributeValue(CellStyleAttributes.IMAGE); + } + + /** + * @return true if this {@link ImagePainter} is resizing the + * cell width to show the whole configured image, false + * if the cell width is not touched by this painter. + */ + public boolean isCalculateByWidth() { + return this.calculateByWidth; + } + + /** + * Configure whether the {@link ImagePainter} should calculate the cell + * dimensions by containing image width. This means the width of the + * cell is calculated by image width. + * + * @param calculateByWidth + * true to calculate and modify the cell dimension + * according to the image width, false to not + * modifying the cell dimensions. + */ + public void setCalculateByWidth(boolean calculateByWidth) { + this.calculateByWidth = calculateByWidth; + } + + /** + * @return true if this {@link ImagePainter} is resizing the + * cell height to show the whole configured image, + * false if the cell height is not touched by this + * painter. + */ + public boolean isCalculateByHeight() { + return this.calculateByHeight; + } + + /** + * Configure whether the {@link ImagePainter} should calculate the cell + * dimensions by containing image height. This means the height of + * the cell is calculated by image height. + * + * @param calculateByHeight + * true to calculate and modify the cell dimension + * according to the image height, false to not + * modifying the cell dimensions. + */ + public void setCalculateByHeight(boolean calculateByHeight) { + this.calculateByHeight = calculateByHeight; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GENatTableThemeConfiguration.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GENatTableThemeConfiguration.java new file mode 100644 index 000000000..bcb8bba53 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GENatTableThemeConfiguration.java @@ -0,0 +1,25 @@ +package org.simantics.browsing.ui.nattable; + +import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter; +import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.PaddingDecorator; +import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration; +import org.eclipse.nebula.widgets.nattable.util.GUIHelper; + +public class GENatTableThemeConfiguration extends ModernNatTableThemeConfiguration{ + + public GENatTableThemeConfiguration(GETreeData treeData) { + super(); + this.oddRowBgColor = GUIHelper.getColor(250, 250, 250); + this.defaultCellPainter = + new GEStyler(treeData, + new GEIconPainter( + new PaddingDecorator( + new TextPainter(), + 0, + 5, + 0, + 5, + false))); + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEStyler.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEStyler.java new file mode 100644 index 000000000..3ab490c6f --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GEStyler.java @@ -0,0 +1,161 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; +import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; +import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; +import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; +import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper; +import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; +import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; +import org.eclipse.nebula.widgets.nattable.style.ConfigAttribute; +import org.eclipse.nebula.widgets.nattable.style.DisplayMode; +import org.eclipse.nebula.widgets.nattable.style.IDisplayModeOrdering; +import org.eclipse.nebula.widgets.nattable.style.Style; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; + +public class GEStyler extends CellPainterWrapper{ + + private GETreeData treeData; + + public GEStyler(GETreeData treeData, ICellPainter painter) { + super(painter); + this.treeData = treeData; + } + + private ConfigRegistryWrapper wrapper = new ConfigRegistryWrapper(); + + @Override + public void paintCell(ILayerCell cell, GC gc, Rectangle rectangle, IConfigRegistry configRegistry) { + wrapper.clear(); + wrapper.wrappedRegistry = configRegistry; + TreeNode node = treeData.getDataAtIndex(cell.getRowIndex()); + Style style = new Style(); + node.getStyle(cell.getColumnIndex(), style); + Image image = node.getImage(cell.getColumnIndex()); + if (image != null) + style.setAttributeValue(CellStyleAttributes.IMAGE, image); + + wrapper.setSpecificConfigAttribute(CellConfigAttributes.CELL_STYLE, DisplayMode.NORMAL, "BODY", style); +// wrapper.setSpecificConfigAttribute(CellStyleAttributes.FOREGROUND_COLOR, DisplayMode.NORMAL, "BODY", style.getAttributeValue(CellStyleAttributes.FOREGROUND_COLOR)); +// wrapper.setSpecificConfigAttribute(CellStyleAttributes.BACKGROUND_COLOR, DisplayMode.NORMAL, "BODY", style.getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR)); +// wrapper.setSpecificConfigAttribute(CellStyleAttributes.FONT, DisplayMode.NORMAL, "BODY", style.getAttributeValue(CellStyleAttributes.FONT)); + super.paintCell(cell, gc, rectangle, wrapper); + } + + private class ConfigRegistryWrapper extends ConfigRegistry { + IConfigRegistry wrappedRegistry; + Map, Map>> configRegistry = new HashMap, Map>>(); + + public void clear() { + configRegistry.clear(); + } + + @Override + public T getConfigAttribute(ConfigAttribute configAttribute, String targetDisplayMode, + String... configLabels) { + return wrappedRegistry.getConfigAttribute(configAttribute, targetDisplayMode, configLabels); + } + + @Override + public T getConfigAttribute(ConfigAttribute configAttribute, String targetDisplayMode, + List configLabels) { + return wrappedRegistry.getConfigAttribute(configAttribute, targetDisplayMode, configLabels); + } + + @Override + public T getSpecificConfigAttribute(ConfigAttribute configAttribute, String displayMode, + String configLabel) { + T value = _getSpecificConfigAttribute(configAttribute, displayMode, configLabel); + if (value != null) + return value; + return wrappedRegistry.getSpecificConfigAttribute(configAttribute, displayMode, configLabel); + } + + public T _getSpecificConfigAttribute(ConfigAttribute configAttribute, + String displayMode, String configLabel) { + T attributeValue = null; + + Map> displayModeConfigAttributeMap = this.configRegistry + .get(configAttribute); + if (displayModeConfigAttributeMap != null) { + Map configAttributeMap = (Map) displayModeConfigAttributeMap.get(displayMode); + if (configAttributeMap != null) { + attributeValue = configAttributeMap.get(configLabel); + if (attributeValue != null) { + return attributeValue; + } + } + } + + return attributeValue; + } + + public void setSpecificConfigAttribute(ConfigAttribute configAttribute, String displayMode, + String configLabel, T attributeValue) { + Map> displayModeConfigAttributeMap = this.configRegistry + .get(configAttribute); + if (displayModeConfigAttributeMap == null) { + displayModeConfigAttributeMap = new HashMap>(); + this.configRegistry.put(configAttribute, displayModeConfigAttributeMap); + } + + Map configAttributeMap = (Map) displayModeConfigAttributeMap.get(displayMode); + if (configAttributeMap == null) { + configAttributeMap = new HashMap(); + displayModeConfigAttributeMap.put(displayMode, configAttributeMap); + } + + configAttributeMap.put(configLabel, attributeValue); + } + + @Override + public void registerConfigAttribute(ConfigAttribute configAttribute, T attributeValue) { + wrappedRegistry.registerConfigAttribute(configAttribute, attributeValue); + + } + + @Override + public void registerConfigAttribute(ConfigAttribute configAttribute, T attributeValue, + String targetDisplayMode) { + wrappedRegistry.registerConfigAttribute(configAttribute, attributeValue, targetDisplayMode); + + } + + @Override + public void registerConfigAttribute(ConfigAttribute configAttribute, T attributeValue, + String targetDisplayMode, String configLabel) { + wrappedRegistry.registerConfigAttribute(configAttribute, attributeValue, targetDisplayMode, configLabel); + + } + + @Override + public void unregisterConfigAttribute(ConfigAttribute configAttributeType) { + wrappedRegistry.unregisterConfigAttribute(configAttributeType); + + } + + @Override + public void unregisterConfigAttribute(ConfigAttribute configAttributeType, String displayMode) { + wrappedRegistry.unregisterConfigAttribute(configAttributeType, displayMode); + + } + + @Override + public void unregisterConfigAttribute(ConfigAttribute configAttributeType, String displayMode, + String configLabel) { + wrappedRegistry.unregisterConfigAttribute(configAttributeType, displayMode, configLabel); + } + + @Override + public IDisplayModeOrdering getDisplayModeOrdering() { + return wrappedRegistry.getDisplayModeOrdering(); + } + + } +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeData.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeData.java new file mode 100644 index 000000000..956a2d191 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeData.java @@ -0,0 +1,106 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.nebula.widgets.nattable.tree.ITreeData; + +public class GETreeData implements ITreeData { + List list; + + public GETreeData(List list) { + this.list = list; + } + + @Override + public String formatDataForDepth(int depth, TreeNode object) { + return null; + } + + @Override + public List getChildren(TreeNode object) { + return (List)object.getChildren(); + } + + @Override + public TreeNode getDataAtIndex(int index) { + if (index < 0 || index >= list.size() ) + return null; + return list.get(index); + } + + @Override + public int getDepthOfData(TreeNode object) { + int count = object.getDepth()-1; // -1 removes invisible root. + return count; + } + + @Override + public boolean hasChildren(TreeNode object) { + return object.getChildren().size() > 0; + } + + @Override + public int indexOf(TreeNode child) { + return child.getListIndex(); + } + + @Override + public boolean hasChildren(int index) { + return hasChildren(list.get(index)); + } + + @Override + public String formatDataForDepth(int depth, int index) { + return formatDataForDepth(depth, list.get(index)); + } + + @Override + public List getChildren(int index) { + return getChildren(list.get(index)); + } + + @Override + public List getChildren(TreeNode object, boolean fullDepth) { + if (!fullDepth) { + return getChildren(object); + } else { + List list = new ArrayList(); + _convertToList(list, object); + return list; + } + + } + private void _convertToList(List list, TreeNode task) { + list.add(task); + for (TreeNode t : task.getChildren()) { + _convertToList(list, t); + } + } + @Override + public int getDepthOfData(int index) { + return getDepthOfData(list.get(index)); + } + + @Override + public boolean isValidIndex(int index) { + if (index < 0) + return false; + if (index >= list.size()) + return false; + return true; + } + + @Override + public int getElementCount() { + return list.size(); + } + + public boolean isRoot(TreeNode object) { + TreeNode parent = object.getParent(); + parent = parent.getParent(); + return (parent == null); + } + + +} \ No newline at end of file diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeLayer.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeLayer.java new file mode 100644 index 000000000..dbc30050a --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeLayer.java @@ -0,0 +1,345 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +import org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer; +import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent; +import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent; +import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; +import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; +import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent; +import org.simantics.browsing.ui.nattable.override.TreeLayer2; +import org.simantics.databoard.util.IdentityHashSet; + +import it.unimi.dsi.fastutil.ints.IntRBTreeSet; +/** + * NatTable TreeLayer for IEcoReportTask tree. + * + * Keeps track of collapsed nodes so that current sorting mechanism works. + * + * + * @author Marko Luukkainen + * + */ +public class GETreeLayer extends TreeLayer2 { + + //Set collapsed = new HashSet(); + Set collapsed = new IdentityHashSet(); + GETreeData treeData; + Comparator comparator = new FirstElementComparator(); + + public GETreeLayer(IUniqueIndexLayer underlyingLayer, GETreeRowModel treeRowModel,boolean useDefaultConfiguration) { + super(underlyingLayer, treeRowModel, useDefaultConfiguration); + + if (underlyingLayer instanceof AbstractRowHideShowLayer) { + throw new IllegalArgumentException("Cannot use treelayer above row hide layer"); + } + + this.treeData = (GETreeData)treeRowModel.getTreeData(); + hiddenPos = new ArrayList(); + hiddenPos.add(new int[]{0,0}); + } + + + @Override + public void collapseTreeRow(int parentIndex) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + collapsed.add(task); + task.setExpanded(false); + super.collapseTreeRow(parentIndex); + } + + public void fullCollapseTreeRow(int parentIndex) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + List indices = new ArrayList(); + + Stack stack = new Stack(); + stack.add(task); + while (!stack.isEmpty()) { + TreeNode t = stack.pop(); + indices.add(treeData.indexOf(t)); + stack.addAll(t.getChildren()); + } + collapseTreeRow(indices); + } + + @Override + public void expandTreeRow(int parentIndex) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + collapsed.remove(task); + task.setExpanded(true); + super.expandTreeRow(parentIndex); + } + + public void expandToTreeRow(int parentIndex) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + List ancestors = new ArrayList(); + while (true) { + task = task.getParent(); + if (task == null) + break; + else + ancestors.add(0, task); + } + for (TreeNode t : ancestors) { + if (treeData.getDepthOfData(t) >= 0) + expandTreeRow(treeData.indexOf(t)); + } + } + + public void fullExpandTreeRow(int parentIndex) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + List indices = new ArrayList(); + + Stack stack = new Stack(); + stack.add(task); + while (!stack.isEmpty()) { + TreeNode t = stack.pop(); + indices.add(treeData.indexOf(t)); + stack.addAll(t.getChildren()); + } + expandTreeRow(indices); + } + + public void collapseTreeRow(int parentIndices[]) { + List rowPositions = new ArrayList(); + List rowIndexes = new ArrayList(); + // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set. + for (int parentIndex : parentIndices) { + if (parentIndex >= 0) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + if (task != null) { + task.setExpanded(false); + collapsed.add(task); + } + rowIndexes.addAll(getModel().collapse(parentIndex)); + } + } + for (Integer rowIndex : rowIndexes) { + int rowPos = getRowPositionByIndex(rowIndex); + //if the rowPos is negative, it is not visible because of hidden state in an underlying layer + if (rowPos >= 0) { + rowPositions.add(rowPos); + } + } + //this.getHiddenRowIndexes().addAll(rowIndexes); + for (int i = 0; i < rowIndexes.size(); i++) { + this.getHiddenRowIndexes().add(rowIndexes.get(i)); + } + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); + } + + public void collapseTreeRow(List parentIndices) { + List rowPositions = new ArrayList(); + List rowIndexes = new ArrayList(); + // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set. + for (int parentIndex : parentIndices) { + if (parentIndex >= 0) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + task.setExpanded(false); + collapsed.add(task); + rowIndexes.addAll(getModel().collapse(parentIndex)); + } + } + for (Integer rowIndex : rowIndexes) { + int rowPos = getRowPositionByIndex(rowIndex); + //if the rowPos is negative, it is not visible because of hidden state in an underlying layer + if (rowPos >= 0) { + rowPositions.add(rowPos); + } + } + //this.getHiddenRowIndexes().addAll(rowIndexes); + for (int i = 0; i < rowIndexes.size(); i++) { + this.getHiddenRowIndexes().add(rowIndexes.get(i)); + } + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); + } + + public void collapseAllRows() { + int count = treeData.getElementCount(); + List rowIndexes = new ArrayList(count); + for (int i = 0; i < count; i++) { + TreeNode t = treeData.getDataAtIndex(i); + // we don't want to hide the roots of the tree + if (!treeData.isRoot(t)) { + rowIndexes.add(i); + + } + t.setExpanded(false); + collapsed.add(t); + getModel().collapse(i); + + } + this.getHiddenRowIndexes().addAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowIndexes)); + } + + public void expandTreeRow(int parentIndices[]) { + List rowIndexes = new ArrayList(); + for (int parentIndex : parentIndices) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + task.setExpanded(true); + collapsed.remove(task); + rowIndexes.addAll(getModel().expand(parentIndex)); + } + + //Implementation uses tree set, so removing in reverse order is faster. + for (int i = rowIndexes.size() -1 ; i >= 0; i--) { + this.getHiddenRowIndexes().remove(rowIndexes.get(i)); + } + //this.getHiddenRowIndexes().removeAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + public void expandTreeRow(List parentIndices) { + List rowIndexes = new ArrayList(); + for (int parentIndex : parentIndices) { + TreeNode task = treeData.getDataAtIndex(parentIndex); + task.setExpanded(true); + collapsed.remove(task); + rowIndexes.addAll(getModel().expand(parentIndex)); + } + + //Implementation uses tree set, so removing in reverse order is faster. + for (int i = rowIndexes.size() -1 ; i >= 0; i--) { + this.getHiddenRowIndexes().remove(rowIndexes.get(i)); + } + //this.getHiddenRowIndexes().removeAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + public void expandAllRows() { + Collection parentIndices = getHiddenRowIndexes(); + List rowIndexes = new ArrayList(); + for (int parentIndex : parentIndices) { + rowIndexes.addAll(getModel().expand(parentIndex)); + } + for (TreeNode t : collapsed) + t.setExpanded(true); + collapsed.clear(); + getHiddenRowIndexes().clear(); + ((GETreeRowModel)getModel()).clear(); + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + @Override + protected void invalidateCache() { + super.invalidateCache(); + hiddenPos.clear(); + hiddenPos.add(new int[]{0,0}); + } + + @Override + public void handleLayerEvent(ILayerEvent event) { + // Currently sorting is implemented by sorting the underlaying list. + // Since all layers are storing just indices, we have to keep track the indices after sorting, + // and refresh the layers accordingly. + + // Another option would use some sort of sorting layers, so that the original data is kept intact, and + // sorting layer would map the row indexes to sorted row positions. + + // preserve collapsed nodes + Set coll = null; + if (event instanceof IStructuralChangeEvent) { + IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event; + if (structuralChangeEvent.isVerticalStructureChanged()) { + // expand old indices + ((GETreeRowModel)getModel()).clear(); + getHiddenRowIndexes().clear(); + coll = collapsed; + } + } + super.handleLayerEvent(event); + if (coll != null) { + // collapse new indices + int ind[] = new int[coll.size()]; + Iterator iter = coll.iterator(); + for (int i = 0; i < ind.length; i++) { + ind[i] = treeData.indexOf(iter.next()); + } + collapseTreeRow(ind); + } + } + + public Set getCollapsed() { + return collapsed; + } + + List hiddenPos; + + @Override + public int getStartYOfRowPosition(int localRowPosition) { + Integer cachedStartY = startYCache.get(Integer.valueOf(localRowPosition)); + if (cachedStartY != null) { + return cachedStartY.intValue(); + } + + IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer(); + int underlyingPosition = localToUnderlyingRowPosition(localRowPosition); + int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition); + if (underlyingStartY < 0) { + return -1; + } + + int h = 0; + int start = 0; + + if (hiddenPos.size() < 2) { // is cache empty? (hiddenPos contains {0,0} element by default) + if (getHiddenRowIndexes().size() > 0) // check if there are hidden rows. + start = getHiddenRowIndexes().iterator().next(); + } else { + int[] d = hiddenPos.get(hiddenPos.size()-1); // take the last element of the cache. + start = d[0]+1; // set to search from the next element. + h = d[1]; + } + if (start < underlyingPosition) { // check if we can find the amount of hidden space from the cache. + // cache positions of hidden nodes and hidden space. + //for (Integer hiddenIndex : ((TreeSet)getHiddenRowIndexes()).tailSet(start)) { + for (int hiddenIndex : ((IntRBTreeSet)getHiddenRowIndexes()).tailSet(start)) { + // safety check (could be disabled, but this does not seem to cause considerable performance hit) + int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);//.intValue()); + if (hiddenPosition != hiddenIndex)//.intValue()) + throw new RuntimeException("Underlying layer is swithing indices"); + if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) { + h += underlyingLayer.getRowHeightByPosition(hiddenPosition); + hiddenPos.add(new int[]{hiddenPosition,h}); + } else if (hiddenPosition > underlyingPosition) { + break; + } + } + } else { + // use binary search to find hidden space. + h = 0; + int index = Collections.binarySearch(hiddenPos, new int[]{underlyingPosition,0}, comparator); + if (index < 0) { // exact element is not cached, but we can use the closest match. + index = -index-2; + } + h = hiddenPos.get(index)[1]; + } + underlyingStartY -= h; + startYCache.put(Integer.valueOf(localRowPosition), Integer.valueOf(underlyingStartY)); + return underlyingStartY; + } + + + private static class FirstElementComparator implements Comparator { + @Override + public int compare(int[] o1, int[] o2) { + return o1[0]-o2[0]; + } + } + +} \ No newline at end of file diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeRowModel.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeRowModel.java new file mode 100644 index 000000000..ebfb962db --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeRowModel.java @@ -0,0 +1,242 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import org.eclipse.nebula.widgets.nattable.tree.ITreeData; +import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel; +import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModelListener; + +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; + +/** + * ITreeRowModel that does not automatically expand all child nodes (as TreeRowModel does). + * + * @author Marko Luukkainen + * + * @param + */ +public class GETreeRowModel implements ITreeRowModel{ + //private final HashSet parentIndexes = new HashSet(); + //private final TIntHashSet parentIndexes = new TIntHashSet(1000, 0.8f); + private final IntOpenHashSet parentIndexes = new IntOpenHashSet(); + + private final Collection listeners = new HashSet(); + + private final ITreeData treeData; + + public GETreeRowModel(ITreeData treeData) { + this.treeData = treeData; + } + + public void registerRowGroupModelListener(ITreeRowModelListener listener) { + this.listeners.add(listener); + } + + public void notifyListeners() { + for (ITreeRowModelListener listener : this.listeners) { + listener.treeRowModelChanged(); + } + } + + public int depth(int index) { + return this.treeData.getDepthOfData(this.treeData.getDataAtIndex(index)); + } + + public boolean isLeaf(int index) { + return !hasChildren(index); + } + + public String getObjectAtIndexAndDepth(int index, int depth) { + return this.treeData.formatDataForDepth(depth,this.treeData.getDataAtIndex(index)); + } + + public boolean hasChildren(int index) { + return this.treeData.hasChildren(this.treeData.getDataAtIndex(index)); + } + + public boolean isCollapsed(int index) { + return this.parentIndexes.contains(index); + } + + public void clear() { + this.parentIndexes.clear(); + } + + @Override + public boolean isCollapsible(int index) { + return hasChildren(index); + } + + @Override + public List collapse(int index) { + this.parentIndexes.add(index); + notifyListeners(); + return getChildIndexes(index); + } + + + + @Override + public List expand(int index) { + this.parentIndexes.remove(index); + notifyListeners(); + List children = getExpandedChildIndexes(index); + return children; + } + + @Override + public List collapseAll() { + // TODO Auto-generated method stub + return null; + } + + + @Override + public List expandToLevel(int level) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List expandToLevel(T object, int level) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List expandAll() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List expandToLevel(int parentIndex, int level) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getChildren(int parentIndex) { + T t = treeData.getDataAtIndex(parentIndex); + return treeData.getChildren(t,true); + } + + @Override + public List getDirectChildren(int parentIndex) { + return treeData.getChildren(parentIndex); + } + + + @Override + public List collapse(T object) { + int index = treeData.indexOf(object); + return collapse(index); + } + + @Override + public List expand(T object) { + int index = treeData.indexOf(object); + return expand(index); + } + + @Override + public boolean isCollapsed(T object) { + int index = treeData.indexOf(object); + return isCollapsed(index); + } + + + @SuppressWarnings("unchecked") + public List getChildIndexes(int parentIndex) { + List result = new ArrayList(); + T t = this.treeData.getDataAtIndex(parentIndex); + if (t == null) + return Collections.EMPTY_LIST; + List children = this.treeData.getChildren(t); + for (T child : children) { + int index = this.treeData.indexOf(child); + if (index >= 0) { + result.add(index); + result.addAll(getChildIndexes(index)); + } else { + result.addAll(getChildIndexes(child)); + } + } + return result; + } + + public List getChildIndexes(T t) { + List result = new ArrayList(); + List children = this.treeData.getChildren(t); + for (T child : children) { + int index = this.treeData.indexOf(child); + if (index >= 0) { + result.add(index); + result.addAll(getChildIndexes(index)); + } else { + result.addAll(getChildIndexes(child)); + } + } + return result; + } + + @SuppressWarnings("unchecked") + public List getExpandedChildIndexes(int parentIndex) { + List result = new ArrayList(); + T t = this.treeData.getDataAtIndex(parentIndex); + if (t == null) + return Collections.EMPTY_LIST; + List children = this.treeData.getChildren(t); + for (T child : children) { + int index = this.treeData.indexOf(child); + if (index >= 0) { + result.add(index); + if (!parentIndexes.contains(index)) + result.addAll(getExpandedChildIndexes(index)); + } else { + result.addAll(getExpandedChildIndexes(child)); + } + } + return result; + } + + public List getExpandedChildIndexes(T t) { + List result = new ArrayList(); + List children = this.treeData.getChildren(t); + for (T child : children) { + int index = this.treeData.indexOf(child); + if (index >= 0) { + result.add(index); + if (!parentIndexes.contains(index)) + result.addAll(getExpandedChildIndexes(index)); + } else { + result.addAll(getExpandedChildIndexes(child)); + } + } + return result; + } + + @Override + public List getDirectChildIndexes(int parentIndex) { + List result = new ArrayList(); + T t = this.treeData.getDataAtIndex(parentIndex); + if (t == null) + return Collections.EMPTY_LIST; + List children = this.treeData.getChildren(t); + for (T child : children) { + int index = this.treeData.indexOf(child); + if (index >= 0) { + result.add(index); + } + } + return result; + } + + public ITreeData getTreeData() { + return treeData; + } +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/ImageTask.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/ImageTask.java new file mode 100644 index 000000000..fe27ffddd --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/ImageTask.java @@ -0,0 +1,11 @@ +package org.simantics.browsing.ui.nattable; + +public class ImageTask { + TreeNode node; + Object descsOrImage; + public ImageTask(TreeNode node, Object descsOrImage) { + this.node = node; + this.descsOrImage = descsOrImage; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableColumnLayout.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableColumnLayout.java new file mode 100644 index 000000000..b0b6904ce --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableColumnLayout.java @@ -0,0 +1,282 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.ColumnLayoutData; +import org.eclipse.jface.viewers.ColumnPixelData; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.nebula.widgets.nattable.coordinate.Range; +import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer; +import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; +import org.eclipse.nebula.widgets.nattable.layer.event.ColumnDeleteEvent; +import org.eclipse.nebula.widgets.nattable.layer.event.ColumnInsertEvent; +import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; +import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Scrollable; + + +/** + * Modified org.eclipse.jface.layout.AbstractColumnLayout and TreeColumnLayout to NatTable compatible. + * + * @author Marko Luukkainen + * + */ +public class NatTableColumnLayout extends Layout implements ILayerListener{ + private static int COLUMN_TRIM; + static { + if (Util.isWindows()) { + COLUMN_TRIM = 4; + } else if (Util.isMac()) { + COLUMN_TRIM = 24; + } else { + COLUMN_TRIM = 3; + } + } + + NatTable natTable; + GEColumnHeaderDataProvider columnHeaderDataProvider; + DefaultRowHeaderDataLayer rowHeaderDataLayer; + Map layoutDatas = new HashMap<>(); + + private boolean inupdateMode = false; + + private boolean relayout = true; + + public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider) { + this.natTable = natTable; + this.columnHeaderDataProvider = columnHeaderDataProvider; + this.natTable.addLayerListener(this); + } + + public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider, DefaultRowHeaderDataLayer rowHeaderDataLayer) { + this.natTable = natTable; + this.columnHeaderDataProvider = columnHeaderDataProvider; + this.natTable.addLayerListener(this); + this.rowHeaderDataLayer = rowHeaderDataLayer; + } + + public void setColumnData(int column, ColumnLayoutData data) { + layoutDatas.put(column, data); + } + + protected void setColumnWidths(Scrollable tree, int[] widths) { + for (int i=0; i < widths.length; i++) { + columnHeaderDataProvider.getDataLayer().setColumnWidthByPosition(i, widths[i]); + } + } + + @Override + public void handleLayerEvent(ILayerEvent event) { + if (inupdateMode) + return; + if (event instanceof ColumnResizeEvent) { + ColumnResizeEvent evt = (ColumnResizeEvent)event; + for (Range r : evt.getColumnPositionRanges()) { + int colIndex = evt.getLayer().getColumnIndexByPosition(r.start); + int w = columnHeaderDataProvider.getDataLayer().getColumnWidthByPosition(colIndex); + setColumnData(colIndex, new ColumnPixelData(w)); + } + update(); + } else if (event instanceof ColumnInsertEvent || + event instanceof ColumnDeleteEvent) { + update(); + } + } + + boolean updateCalled = false; + + private void update() { + if (updateCalled) + return; + updateCalled = true; + natTable.getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + if (!natTable.isDisposed()) { + natTable.update(); + natTable.getParent().layout(); + } + updateCalled = false; + } + }); + } + + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + return computeTableTreeSize(getControl(composite), wHint, hHint); + } + + Scrollable getControl(Composite composite) { + return natTable; + } + + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle area = composite.getClientArea(); + Scrollable table = getControl(composite); + int tableWidth = table.getSize().x; + int trim = computeTrim(area, table, tableWidth); + int width = Math.max(0, area.width - trim); + if (rowHeaderDataLayer != null) + width -= rowHeaderDataLayer.getWidth(); + + if (width > 1) + layoutTableTree(table, width, area, tableWidth < area.width); + + // For the first time we need to relayout because Scrollbars are not + // calculate appropriately + if (relayout) { + relayout = false; + composite.layout(); + } + + } + + protected ColumnLayoutData getLayoutData(Scrollable tableTree, + int columnIndex) { + return layoutDatas.get(columnIndex); + } + + protected int getColumnCount(Scrollable tableTree) { + return columnHeaderDataProvider.getColumnCount(); + } + + private Point computeTableTreeSize(Scrollable scrollable, int wHint, + int hHint) { + Point result = scrollable.computeSize(wHint, hHint); + + int width = 0; + int size = getColumnCount(scrollable); + if (rowHeaderDataLayer != null) + width += rowHeaderDataLayer.getWidth(); + for (int i = 0; i < size; ++i) { + ColumnLayoutData layoutData = getLayoutData(scrollable, i); + if (layoutData instanceof ColumnPixelData) { + ColumnPixelData col = (ColumnPixelData) layoutData; + width += col.width; + if (col.addTrim) { + width += getColumnTrim(); + } + } else if (layoutData instanceof ColumnWeightData) { + ColumnWeightData col = (ColumnWeightData) layoutData; + width += col.minimumWidth; + } else { + Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$ + } + } + if (width > result.x) + result.x = width; + + return result; + } + + private void layoutTableTree(final Scrollable scrollable, final int width, + final Rectangle area, final boolean increase) { + final int numberOfColumns = getColumnCount(scrollable); + final int[] widths = new int[numberOfColumns]; + + final int[] weightColumnIndices = new int[numberOfColumns]; + int numberOfWeightColumns = 0; + + int fixedWidth = 0; + int totalWeight = 0; + + // First calc space occupied by fixed columns + for (int i = 0; i < numberOfColumns; i++) { + ColumnLayoutData col = getLayoutData(scrollable, i); + if (col instanceof ColumnPixelData) { + ColumnPixelData cpd = (ColumnPixelData) col; + int pixels = cpd.width; + if (cpd.addTrim) { + pixels += getColumnTrim(); + } + widths[i] = pixels; + fixedWidth += pixels; + } else if (col instanceof ColumnWeightData) { + ColumnWeightData cw = (ColumnWeightData) col; + weightColumnIndices[numberOfWeightColumns] = i; + numberOfWeightColumns++; + totalWeight += cw.weight; + } else { + Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$ + } + } + + boolean recalculate; + do { + recalculate = false; + for (int i = 0; i < numberOfWeightColumns; i++) { + int colIndex = weightColumnIndices[i]; + ColumnWeightData cw = (ColumnWeightData) getLayoutData( + scrollable, colIndex); + final int minWidth = cw.minimumWidth; + final int allowedWidth = totalWeight == 0 ? 0 + : (width - fixedWidth) * cw.weight / totalWeight; + if (allowedWidth < minWidth) { + /* + * if the width assigned by weight is less than the minimum, + * then treat this column as fixed, remove it from weight + * calculations, and recalculate other weights. + */ + numberOfWeightColumns--; + totalWeight -= cw.weight; + fixedWidth += minWidth; + widths[colIndex] = minWidth; + System.arraycopy(weightColumnIndices, i + 1, + weightColumnIndices, i, numberOfWeightColumns - i); + recalculate = true; + break; + } + widths[colIndex] = allowedWidth; + } + } while (recalculate); + + if (increase) { + scrollable.setSize(area.width, area.height); + } + + inupdateMode = true; + setColumnWidths(scrollable, widths); + scrollable.update(); + inupdateMode = false; + + if (!increase) { + scrollable.setSize(area.width, area.height); + } + } + + private int computeTrim(Rectangle area, Scrollable scrollable, + int currentWidth) { + int trim; + + if (currentWidth > 1) { + trim = currentWidth - scrollable.getClientArea().width; + } else { + // initially, the table has no extend and no client area - use the + // border with + // plus some padding as educated guess + trim = 2 * scrollable.getBorderWidth() + 1; + } + + return trim; + } + + protected int getColumnTrim() { + return COLUMN_TRIM; + } + + + + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableGraphExplorer.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableGraphExplorer.java new file mode 100644 index 000000000..aa2c5b21e --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableGraphExplorer.java @@ -0,0 +1,2762 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.TreeColumnLayout; +import org.eclipse.jface.resource.ColorDescriptor; +import org.eclipse.jface.resource.DeviceResourceException; +import org.eclipse.jface.resource.DeviceResourceManager; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.ICellEditorValidator; +import org.eclipse.jface.viewers.IPostSelectionProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration; +import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; +import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; +import org.eclipse.nebula.widgets.nattable.config.IEditableRule; +import org.eclipse.nebula.widgets.nattable.coordinate.Range; +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; +import org.eclipse.nebula.widgets.nattable.data.ListDataProvider; +import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDisplayConverter; +import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes; +import org.eclipse.nebula.widgets.nattable.edit.EditConfigHelper; +import org.eclipse.nebula.widgets.nattable.edit.ICellEditHandler; +import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditBindings; +import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration; +import org.eclipse.nebula.widgets.nattable.edit.editor.AbstractCellEditor; +import org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor; +import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor; +import org.eclipse.nebula.widgets.nattable.edit.editor.IEditErrorHandler; +import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor; +import org.eclipse.nebula.widgets.nattable.edit.gui.AbstractDialogCellEditor; +import org.eclipse.nebula.widgets.nattable.grid.GridRegion; +import org.eclipse.nebula.widgets.nattable.grid.cell.AlternatingRowConfigLabelAccumulator; +import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider; +import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider; +import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer; +import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer; +import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent; +import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent; +import org.eclipse.nebula.widgets.nattable.layer.DataLayer; +import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; +import org.eclipse.nebula.widgets.nattable.layer.LabelStack; +import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator; +import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; +import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; +import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; +import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer; +import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; +import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum; +import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration; +import org.eclipse.nebula.widgets.nattable.style.DisplayMode; +import org.eclipse.nebula.widgets.nattable.ui.menu.AbstractHeaderMenuConfiguration; +import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder; +import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; +import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.services.IServiceLocator; +import org.eclipse.ui.swt.IFocusService; +import org.simantics.browsing.ui.BuiltinKeys; +import org.simantics.browsing.ui.Column; +import org.simantics.browsing.ui.Column.Align; +import org.simantics.browsing.ui.DataSource; +import org.simantics.browsing.ui.ExplorerState; +import org.simantics.browsing.ui.GraphExplorer; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.NodeContext.CacheKey; +import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey; +import org.simantics.browsing.ui.NodeContext.QueryKey; +import org.simantics.browsing.ui.NodeQueryManager; +import org.simantics.browsing.ui.NodeQueryProcessor; +import org.simantics.browsing.ui.PrimitiveQueryProcessor; +import org.simantics.browsing.ui.PrimitiveQueryUpdater; +import org.simantics.browsing.ui.SelectionDataResolver; +import org.simantics.browsing.ui.SelectionFilter; +import org.simantics.browsing.ui.StatePersistor; +import org.simantics.browsing.ui.common.ColumnKeys; +import org.simantics.browsing.ui.common.ErrorLogger; +import org.simantics.browsing.ui.common.NodeContextBuilder; +import org.simantics.browsing.ui.common.NodeContextUtil; +import org.simantics.browsing.ui.common.internal.GENodeQueryManager; +import org.simantics.browsing.ui.common.internal.IGECache; +import org.simantics.browsing.ui.common.internal.IGraphExplorerContext; +import org.simantics.browsing.ui.common.internal.UIElementReference; +import org.simantics.browsing.ui.common.processors.AbstractPrimitiveQueryProcessor; +import org.simantics.browsing.ui.common.processors.DefaultCheckedStateProcessor; +import org.simantics.browsing.ui.common.processors.DefaultComparableChildrenProcessor; +import org.simantics.browsing.ui.common.processors.DefaultFinalChildrenProcessor; +import org.simantics.browsing.ui.common.processors.DefaultImageDecoratorProcessor; +import org.simantics.browsing.ui.common.processors.DefaultImagerFactoriesProcessor; +import org.simantics.browsing.ui.common.processors.DefaultImagerProcessor; +import org.simantics.browsing.ui.common.processors.DefaultLabelDecoratorProcessor; +import org.simantics.browsing.ui.common.processors.DefaultLabelerFactoriesProcessor; +import org.simantics.browsing.ui.common.processors.DefaultLabelerProcessor; +import org.simantics.browsing.ui.common.processors.DefaultPrunedChildrenProcessor; +import org.simantics.browsing.ui.common.processors.DefaultSelectedImageDecoratorFactoriesProcessor; +import org.simantics.browsing.ui.common.processors.DefaultSelectedLabelDecoratorFactoriesProcessor; +import org.simantics.browsing.ui.common.processors.DefaultSelectedLabelerProcessor; +import org.simantics.browsing.ui.common.processors.DefaultSelectedViewpointFactoryProcessor; +import org.simantics.browsing.ui.common.processors.DefaultSelectedViewpointProcessor; +import org.simantics.browsing.ui.common.processors.DefaultViewpointContributionProcessor; +import org.simantics.browsing.ui.common.processors.DefaultViewpointContributionsProcessor; +import org.simantics.browsing.ui.common.processors.DefaultViewpointProcessor; +import org.simantics.browsing.ui.common.processors.IsExpandedProcessor; +import org.simantics.browsing.ui.common.processors.NoSelectionRequestProcessor; +import org.simantics.browsing.ui.common.processors.ProcessorLifecycle; +import org.simantics.browsing.ui.content.Labeler; +import org.simantics.browsing.ui.content.Labeler.CustomModifier; +import org.simantics.browsing.ui.content.Labeler.DialogModifier; +import org.simantics.browsing.ui.content.Labeler.EnumerationModifier; +import org.simantics.browsing.ui.content.Labeler.Modifier; +import org.simantics.browsing.ui.nattable.override.DefaultTreeLayerConfiguration2; +import org.simantics.browsing.ui.swt.Activator; +import org.simantics.browsing.ui.swt.AdaptableHintContext; +import org.simantics.browsing.ui.swt.DefaultImageDecoratorsProcessor; +import org.simantics.browsing.ui.swt.DefaultIsExpandedProcessor; +import org.simantics.browsing.ui.swt.DefaultLabelDecoratorsProcessor; +import org.simantics.browsing.ui.swt.DefaultSelectedImagerProcessor; +import org.simantics.browsing.ui.swt.DefaultShowMaxChildrenProcessor; +import org.simantics.browsing.ui.swt.GraphExplorerImplBase; +import org.simantics.browsing.ui.swt.ImageLoaderJob; +import org.simantics.browsing.ui.swt.ViewerCellReference; +import org.simantics.browsing.ui.swt.ViewerRowReference; +import org.simantics.browsing.ui.swt.internal.Threads; +import org.simantics.db.layer0.SelectionHints; +import org.simantics.utils.datastructures.BinaryFunction; +import org.simantics.utils.datastructures.MapList; +import org.simantics.utils.datastructures.disposable.AbstractDisposable; +import org.simantics.utils.datastructures.hints.IHintContext; +import org.simantics.utils.threads.IThreadWorkQueue; +import org.simantics.utils.threads.SWTThread; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.AdaptionUtils; +import org.simantics.utils.ui.ISelectionUtils; +import org.simantics.utils.ui.jface.BasePostSelectionProvider; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.map.hash.TObjectIntHashMap; + +/** + * NatTable base GraphExplorer + * + * + * FIXME : asynchronous node loading does not work properly + check expanded/collapsed sate handling + * TODO: InputValidators + input errors + * TODO: ability to hide headers + * TODO: code cleanup (copied from GraphExplorerImpl2) + * + * @author Marko Luukkainen + * + */ +public class NatTableGraphExplorer extends GraphExplorerImplBase implements GraphExplorer{ + public static final int DEFAULT_MAX_CHILDREN = 1000; + private static final boolean DEBUG_SELECTION_LISTENERS = false; + private static final boolean DEBUG = false; + + private Composite composite; + private NatTable natTable; + + private GETreeLayer treeLayer; + private DataLayer dataLayer; + private ViewportLayer viewportLayer; + private SelectionLayer selectionLayer; + private GEColumnHeaderDataProvider columnHeaderDataProvider; + private GEColumnAccessor columnAccessor; + private DefaultRowHeaderDataLayer rowHeaderDataLayer; + private DataLayer columnHeaderDataLayer; + private DataLayer cornerDataLayer; + + private List list = new ArrayList<>(); + + private NatTableSelectionAdaptor selectionAdaptor; + private NatTableColumnLayout layout; + + LocalResourceManager localResourceManager; + DeviceResourceManager resourceManager; + + + private IThreadWorkQueue thread; + + @SuppressWarnings({ "rawtypes" }) + final HashMap, NodeQueryProcessor> processors = new HashMap, NodeQueryProcessor>(); + @SuppressWarnings({ "rawtypes" }) + final HashMap primitiveProcessors = new HashMap(); + @SuppressWarnings({ "rawtypes" }) + final HashMap dataSources = new HashMap(); + + FontDescriptor originalFont; + protected ColorDescriptor originalForeground; + protected ColorDescriptor originalBackground; + private Color invalidModificationColor; + + private Column[] columns; + private Map columnKeyToIndex; + private boolean columnsAreVisible = true; + + private NodeContext rootContext; + private TreeNode rootNode; + private StatePersistor persistor = null; + + private boolean editable = true; + + private boolean disposed = false; + + private final CopyOnWriteArrayList focusListeners = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList mouseListeners = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList keyListeners = new CopyOnWriteArrayList(); + + private int autoExpandLevel = 0; + private IServiceLocator serviceLocator; + private IContextService contextService = null; + private IFocusService focusService = null; + private IContextActivation editingContext = null; + + GeViewerContext explorerContext = new GeViewerContext(this); + + private GraphExplorerPostSelectionProvider postSelectionProvider = new GraphExplorerPostSelectionProvider(this); + private BasePostSelectionProvider selectionProvider = new BasePostSelectionProvider(); + private SelectionDataResolver selectionDataResolver; + private SelectionFilter selectionFilter; + + private MapList contextToNodeMap; + + private ModificationContext modificationContext = null; + + private boolean filterSelectionEdit = true; + + private boolean expand; + private boolean verticalBarVisible = false; + + private BinaryFunction selectionTransformation = new BinaryFunction() { + + @Override + public Object[] call(GraphExplorer explorer, Object[] objects) { + Object[] result = new Object[objects.length]; + for (int i = 0; i < objects.length; i++) { + IHintContext context = new AdaptableHintContext(SelectionHints.KEY_MAIN); + context.setHint(SelectionHints.KEY_MAIN, objects[i]); + result[i] = context; + } + return result; + } + + }; + + static class TransientStateImpl implements TransientExplorerState { + + private Integer activeColumn = null; + + @Override + public synchronized Integer getActiveColumn() { + return activeColumn; + } + + public synchronized void setActiveColumn(Integer column) { + activeColumn = column; + } + + } + + private TransientStateImpl transientState = new TransientStateImpl(); + + public NatTableGraphExplorer(Composite parent) { + this(parent, SWT.BORDER | SWT.MULTI ); + } + + public NatTableGraphExplorer(Composite parent, int style) { + this.composite = parent; + + + this.localResourceManager = new LocalResourceManager(JFaceResources.getResources()); + this.resourceManager = new DeviceResourceManager(parent.getDisplay()); + + this.imageLoaderJob = new ImageLoaderJob(this); + this.imageLoaderJob.setPriority(Job.DECORATE); + contextToNodeMap = new MapList(); + + invalidModificationColor = (Color) localResourceManager.get(ColorDescriptor.createFrom(new RGB(255, 128, 128))); + + this.thread = SWTThread.getThreadAccess(parent); + + for (int i = 0; i < 10; i++) + explorerContext.activity.push(0); + + originalFont = JFaceResources.getDefaultFontDescriptor(); + + columns = new Column[0]; + createNatTable(); + layout = new NatTableColumnLayout(natTable, columnHeaderDataProvider, rowHeaderDataLayer); + this.composite.setLayout(layout); + + setBasicListeners(); + setDefaultProcessors(); + + natTable.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + doDispose(); + + } + }); + + Listener listener = new Listener() { + + @Override + public void handleEvent(Event event) { + + switch (event.type) { + case SWT.Activate: + case SWT.Show: + case SWT.Paint: + { + visible = true; + activate(); + break; + } + case SWT.Deactivate: + case SWT.Hide: + visible = false; + } + } + }; + + natTable.addListener(SWT.Activate, listener); + natTable.addListener(SWT.Deactivate, listener); + natTable.addListener(SWT.Show, listener); + natTable.addListener(SWT.Hide, listener); + natTable.addListener(SWT.Paint,listener); + + setColumns( new Column[] { new Column(ColumnKeys.SINGLE) }); + + } + + private long focusGainedAt = 0L; + private boolean visible = false; + private Collection selectedNodes = new ArrayList(); + + protected void setBasicListeners() { + + natTable.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + focusGainedAt = ((long) e.time) & 0xFFFFFFFFL; + for (FocusListener listener : focusListeners) + listener.focusGained(e); + } + @Override + public void focusLost(FocusEvent e) { + for (FocusListener listener : focusListeners) + listener.focusLost(e); + } + }); + natTable.addMouseListener(new MouseListener() { + @Override + public void mouseDoubleClick(MouseEvent e) { + for (MouseListener listener : mouseListeners) { + listener.mouseDoubleClick(e); + } + } + @Override + public void mouseDown(MouseEvent e) { + for (MouseListener listener : mouseListeners) { + listener.mouseDown(e); + } + } + @Override + public void mouseUp(MouseEvent e) { + for (MouseListener listener : mouseListeners) { + listener.mouseUp(e); + } + } + }); + natTable.addKeyListener(new KeyListener() { + @Override + public void keyPressed(KeyEvent e) { + for (KeyListener listener : keyListeners) { + listener.keyPressed(e); + } + } + @Override + public void keyReleased(KeyEvent e) { + for (KeyListener listener : keyListeners) { + listener.keyReleased(e); + } + } + }); + + selectionAdaptor.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + //System.out.println("GraphExplorerImpl2.fireSelection"); + selectedNodes = AdaptionUtils.adaptToCollection(event.getSelection(), TreeNode.class); + Collection selectedContexts = AdaptionUtils.adaptToCollection(event.getSelection(), NodeContext.class); + selectionProvider.setAndFireSelection(constructSelection(selectedContexts.toArray(new NodeContext[selectedContexts.size()]))); + } + }); + + selectionAdaptor.addPostSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + //System.out.println("GraphExplorerImpl2.firePostSelection"); + Collection selectedContexts = AdaptionUtils.adaptToCollection(event.getSelection(), NodeContext.class); + selectionProvider.firePostSelection(constructSelection(selectedContexts.toArray(new NodeContext[selectedContexts.size()]))); + + } + }); + + } + + private NodeContext pendingRoot; + + private void activate() { + if (pendingRoot != null && !expand) { + doSetRoot(pendingRoot); + pendingRoot = null; + } + } + + /** + * Invoke only from SWT thread to reset the root of the graph explorer tree. + * + * @param root + */ + private void doSetRoot(NodeContext root) { + Display display = composite.getDisplay(); + if (display.getThread() != Thread.currentThread()) { + throw new RuntimeException("Invoke from SWT thread only"); + } +// System.out.println("doSetRoot " + root); + if (isDisposed()) + return; + if (natTable.isDisposed()) + return; + if (root.getConstant(BuiltinKeys.INPUT) == null) { + ErrorLogger.defaultLogError("root node context does not contain BuiltinKeys.INPUT key. Node = " + root, new Exception("trace")); + return; + } + + + + // Empty caches, release queries. + if (rootNode != null) { + rootNode.dispose(); + } + GeViewerContext oldContext = explorerContext; + GeViewerContext newContext = new GeViewerContext(this); + this.explorerContext = newContext; + oldContext.safeDispose(); + + // Need to empty these or otherwise they won't be emptied until the + // explorer is disposed which would mean that many unwanted references + // will be held by this map. + clearPrimitiveProcessors(); + + this.rootContext = root.getConstant(BuiltinKeys.IS_ROOT) != null ? root + : NodeContextUtil.withConstant(root, BuiltinKeys.IS_ROOT, Boolean.TRUE); + + explorerContext.getCache().incRef(this.rootContext); + + initializeState(); + + + select(rootContext); + //refreshColumnSizes(); + rootNode = new TreeNode(rootContext, explorerContext); + if (DEBUG) System.out.println("setRoot " + rootNode); + + // viewer.setInput(rootNode); + + // Delay content reading. + + // This is required for cases when GEImpl2 is attached to selection view. Reading content + // instantly could stagnate SWT thread under rapid changes in selection. By delaying the + // content reading we give the system a change to dispose the GEImpl2 before the content is read. + display.asyncExec(new Runnable() { + + @Override + public void run() { + if (rootNode != null) { + rootNode.updateChildren(); + listReIndex(); + natTable.refresh(true); + } + } + }); + + } + + private synchronized void listReIndex() { + list.clear(); + for (TreeNode c : rootNode.getChildren()) + _insertToList(c); + } + + private void _insertToList(TreeNode n) { + n.setListIndex(list.size()); + list.add(n); + for (TreeNode c : n.getChildren()) { + _insertToList(c); + } + } + + private void initializeState() { + if (persistor == null) + return; + + ExplorerState state = persistor.deserialize( + Platform.getStateLocation(Activator.getDefault().getBundle()).toFile(), + getRoot()); + + + Object processor = getPrimitiveProcessor(BuiltinKeys.IS_EXPANDED); + if (processor instanceof DefaultIsExpandedProcessor) { + DefaultIsExpandedProcessor isExpandedProcessor = (DefaultIsExpandedProcessor)processor; + for(NodeContext expanded : state.expandedNodes) { + isExpandedProcessor.setExpanded(expanded, true); + } + } + } + + @Override + public NodeContext getRoot() { + return rootContext; + } + + @Override + public IThreadWorkQueue getThread() { + return thread; + } + + @Override + public NodeContext getParentContext(NodeContext context) { + if (disposed) + throw new IllegalStateException("disposed"); + if (!thread.currentThreadAccess()) + throw new IllegalStateException("not in SWT display thread " + thread.getThread()); + + List nodes = contextToNodeMap.getValuesUnsafe(context); + for (int i = 0; i < nodes.size(); i++) { + if (nodes.get(i).getParent() != null) + return nodes.get(i).getParent().getContext(); + } + return null; + + } + + + @SuppressWarnings("unchecked") + @Override + public T getAdapter(Class adapter) { + if(ISelectionProvider.class == adapter) return (T) postSelectionProvider; + else if(IPostSelectionProvider.class == adapter) return (T) postSelectionProvider; + return null; + } + + + protected void setDefaultProcessors() { + // Add a simple IMAGER query processor that always returns null. + // With this processor no images will ever be shown. + // setPrimitiveProcessor(new StaticImagerProcessor(null)); + + setProcessor(new DefaultComparableChildrenProcessor()); + setProcessor(new DefaultLabelDecoratorsProcessor()); + setProcessor(new DefaultImageDecoratorsProcessor()); + setProcessor(new DefaultSelectedLabelerProcessor()); + setProcessor(new DefaultLabelerFactoriesProcessor()); + setProcessor(new DefaultSelectedImagerProcessor()); + setProcessor(new DefaultImagerFactoriesProcessor()); + setPrimitiveProcessor(new DefaultLabelerProcessor()); + setPrimitiveProcessor(new DefaultCheckedStateProcessor()); + setPrimitiveProcessor(new DefaultImagerProcessor()); + setPrimitiveProcessor(new DefaultLabelDecoratorProcessor()); + setPrimitiveProcessor(new DefaultImageDecoratorProcessor()); + setPrimitiveProcessor(new NoSelectionRequestProcessor()); + + setProcessor(new DefaultFinalChildrenProcessor(this)); + + setProcessor(new DefaultPrunedChildrenProcessor()); + setProcessor(new DefaultSelectedViewpointProcessor()); + setProcessor(new DefaultSelectedLabelDecoratorFactoriesProcessor()); + setProcessor(new DefaultSelectedImageDecoratorFactoriesProcessor()); + setProcessor(new DefaultViewpointContributionsProcessor()); + + setPrimitiveProcessor(new DefaultViewpointProcessor()); + setPrimitiveProcessor(new DefaultViewpointContributionProcessor()); + setPrimitiveProcessor(new DefaultSelectedViewpointFactoryProcessor()); + setPrimitiveProcessor(new TreeNodeIsExpandedProcessor()); + setPrimitiveProcessor(new DefaultShowMaxChildrenProcessor()); + } + + @Override + public Column[] getColumns() { + return Arrays.copyOf(columns, columns.length); + } + + @Override + public void setColumnsVisible(boolean visible) { + columnsAreVisible = visible; + //FIXME if(natTable != null) this.columnHeaderDataLayer.setHeaderVisible(columnsAreVisible); + } + + @Override + public void setColumns(final Column[] columns) { + setColumns(columns, null); + } + + @Override + public void setColumns(final Column[] columns, Consumer> callback) { + assertNotDisposed(); + checkUniqueColumnKeys(columns); + + Display d = composite.getDisplay(); + if (d.getThread() == Thread.currentThread()) { + doSetColumns(columns, callback); + natTable.refresh(true); + }else + d.asyncExec(new Runnable() { + @Override + public void run() { + if (natTable == null) + return; + if (natTable.isDisposed()) + return; + doSetColumns(columns, callback); + natTable.refresh(true); + natTable.getParent().layout(); + } + }); + } + + private void checkUniqueColumnKeys(Column[] cols) { + Set usedColumnKeys = new HashSet(); + List duplicateColumns = new ArrayList(); + for (Column c : cols) { + if (!usedColumnKeys.add(c.getKey())) + duplicateColumns.add(c); + } + if (!duplicateColumns.isEmpty()) { + throw new IllegalArgumentException("All columns do not have unique keys: " + cols + ", overlapping: " + duplicateColumns); + } + } + + private void doSetColumns(Column[] cols, Consumer> callback) { + + HashMap keyToIndex = new HashMap(); + for (int i = 0; i < cols.length; ++i) { + keyToIndex.put(cols[i].getKey(), i); + } + + this.columns = Arrays.copyOf(cols, cols.length); + //this.columns[cols.length] = FILLER_COLUMN; + this.columnKeyToIndex = keyToIndex; + + columnHeaderDataProvider.updateColumnSizes(); + + Map map = new HashMap(); + + // FIXME : temporary workaround for ModelBrowser. +// natTable.setHeaderVisible(columns.length == 1 ? false : columnsAreVisible); + + int columnIndex = 0; + + for (Column column : columns) { + int width = column.getWidth(); + if(column.hasGrab()) { + if (width < 0) + width = 1; + layout.setColumnData(columnIndex, new ColumnWeightData(column.getWeight(), width)); + + } else { + if (width < 0) + width = 50; + layout.setColumnData(columnIndex, new ColumnWeightData(columns.length > 1 ? 0 : 1, width)); + + } + columnIndex++; + } + + + + if(callback != null) callback.accept(map); + } + + int toSWT(Align alignment) { + switch (alignment) { + case LEFT: return SWT.LEFT; + case CENTER: return SWT.CENTER; + case RIGHT: return SWT.RIGHT; + default: throw new Error("unhandled alignment: " + alignment); + } + } + + @Override + public void setProcessor(NodeQueryProcessor processor) { + assertNotDisposed(); + if (processor == null) + throw new IllegalArgumentException("null processor"); + + processors.put(processor.getIdentifier(), processor); + } + + @Override + public void setPrimitiveProcessor(PrimitiveQueryProcessor processor) { + assertNotDisposed(); + if (processor == null) + throw new IllegalArgumentException("null processor"); + + PrimitiveQueryProcessor oldProcessor = primitiveProcessors.put( + processor.getIdentifier(), processor); + + if (oldProcessor instanceof ProcessorLifecycle) + ((ProcessorLifecycle) oldProcessor).detached(this); + if (processor instanceof ProcessorLifecycle) + ((ProcessorLifecycle) processor).attached(this); + } + + @Override + public void setDataSource(DataSource provider) { + assertNotDisposed(); + if (provider == null) + throw new IllegalArgumentException("null provider"); + dataSources.put(provider.getProvidedClass(), provider); + } + + @SuppressWarnings("unchecked") + @Override + public DataSource removeDataSource(Class forProvidedClass) { + assertNotDisposed(); + if (forProvidedClass == null) + throw new IllegalArgumentException("null class"); + return dataSources.remove(forProvidedClass); + } + + @Override + public void setPersistor(StatePersistor persistor) { + this.persistor = persistor; + } + + @Override + public SelectionDataResolver getSelectionDataResolver() { + return selectionDataResolver; + } + + @Override + public void setSelectionDataResolver(SelectionDataResolver r) { + this.selectionDataResolver = r; + } + + @Override + public SelectionFilter getSelectionFilter() { + return selectionFilter; + } + + @Override + public void setSelectionFilter(SelectionFilter f) { + this.selectionFilter = f; + // TODO: re-filter current selection? + } + + protected ISelection constructSelection(NodeContext... contexts) { + if (contexts == null) + throw new IllegalArgumentException("null contexts"); + if (contexts.length == 0) + return StructuredSelection.EMPTY; + if (selectionFilter == null) + return new StructuredSelection(transformSelection(contexts)); + return new StructuredSelection( transformSelection(filter(selectionFilter, contexts)) ); + } + + protected Object[] transformSelection(Object[] objects) { + return selectionTransformation.call(this, objects); + } + + protected static Object[] filter(SelectionFilter filter, NodeContext[] contexts) { + int len = contexts.length; + Object[] objects = new Object[len]; + for (int i = 0; i < len; ++i) + objects[i] = filter.filter(contexts[i]); + return objects; + } + + @Override + public void setSelectionTransformation( + BinaryFunction f) { + this.selectionTransformation = f; + } + + public ISelection getWidgetSelection() { + return selectionAdaptor.getSelection(); + } + + @Override + public void addListener(T listener) { + if (listener instanceof FocusListener) { + focusListeners.add((FocusListener) listener); + } else if (listener instanceof MouseListener) { + mouseListeners.add((MouseListener) listener); + } else if (listener instanceof KeyListener) { + keyListeners.add((KeyListener) listener); + } + } + + @Override + public void removeListener(T listener) { + if (listener instanceof FocusListener) { + focusListeners.remove(listener); + } else if (listener instanceof MouseListener) { + mouseListeners.remove(listener); + } else if (listener instanceof KeyListener) { + keyListeners.remove(listener); + } + } + + public void addSelectionListener(SelectionListener listener) { + selectionAdaptor.addSelectionListener(listener); + } + + public void removeSelectionListener(SelectionListener listener) { + selectionAdaptor.removeSelectionListener(listener); + } + + private Set uiContexts; + + @Override + public void setUIContexts(Set contexts) { + this.uiContexts = contexts; + } + + @Override + public void setRoot(final Object root) { + if(uiContexts != null && uiContexts.size() == 1) + setRootContext0(NodeContextBuilder.buildWithData(BuiltinKeys.INPUT, root, BuiltinKeys.UI_CONTEXT, uiContexts.iterator().next())); + else + setRootContext0(NodeContextBuilder.buildWithData(BuiltinKeys.INPUT, root)); + } + + @Override + public void setRootContext(final NodeContext context) { + setRootContext0(context); + } + + private void setRoot(NodeContext context) { + if (!visible) { + pendingRoot = context; + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (natTable!= null && !natTable.isDisposed()) + natTable.redraw(); + } + }); + return; + } + doSetRoot(context); + } + + private void setRootContext0(final NodeContext context) { + Assert.isNotNull(context, "root must not be null"); + if (isDisposed() || natTable.isDisposed()) + return; + Display display = natTable.getDisplay(); + if (display.getThread() == Thread.currentThread()) { + setRoot(context); + } else { + display.asyncExec(new Runnable() { + @Override + public void run() { + setRoot(context); + } + }); + } + } + + @Override + public void setFocus() { + natTable.setFocus(); + } + + @SuppressWarnings("unchecked") + @Override + public T getControl() { + return (T)natTable; + } + + + @Override + public boolean isDisposed() { + return disposed; + } + + protected void assertNotDisposed() { + if (isDisposed()) + throw new IllegalStateException("disposed"); + } + + @Override + public boolean isEditable() { + return editable; + } + + @Override + public void setEditable(boolean editable) { + if (!thread.currentThreadAccess()) + throw new IllegalStateException("not in SWT display thread " + thread.getThread()); + + this.editable = editable; + Display display = natTable.getDisplay(); + natTable.setBackground(editable ? null : display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); + } + + private void doDispose() { + if (disposed) + return; + disposed = true; + // TODO: Since GENodeQueryManager is cached in QueryChache and it refers to this class + // we have to remove all references here to reduce memory consumption. + // + // Proper fix would be to remove references between QueryCache and GENodeQueryManagers. + if (rootNode != null) { + rootNode.dispose(); + rootNode = null; + } + explorerContext.dispose(); + explorerContext = null; + processors.clear(); + detachPrimitiveProcessors(); + primitiveProcessors.clear(); + dataSources.clear(); + pendingItems.clear(); + rootContext = null; + mouseListeners.clear(); + selectionProvider.clearListeners(); + selectionProvider = null; + selectionDataResolver = null; + selectedNodes.clear(); + selectedNodes = null; + selectionTransformation = null; + originalFont = null; + localResourceManager.dispose(); + localResourceManager = null; + // Must shutdown image loader job before disposing its ResourceManager + imageLoaderJob.dispose(); + imageLoaderJob.cancel(); + try { + imageLoaderJob.join(); + imageLoaderJob = null; + } catch (InterruptedException e) { + ErrorLogger.defaultLogError(e); + } + resourceManager.dispose(); + resourceManager = null; + + contextToNodeMap.clear(); // should be empty at this point. + contextToNodeMap = null; + if (postSelectionProvider != null) { + postSelectionProvider.dispose(); + postSelectionProvider = null; + } + imageTasks = null; + modificationContext = null; + focusService = null; + contextService = null; + serviceLocator = null; + columns = null; + columnKeyToIndex.clear(); + columnKeyToIndex = null; +// if (natTable != null) { +// natTable.dispose(); +// natTable = null; +// } + treeLayer = null; + dataLayer = null; + viewportLayer = null; + selectionLayer = null; + columnHeaderDataProvider = null; + columnAccessor = null; + rowHeaderDataLayer = null; + columnHeaderDataLayer = null; + cornerDataLayer = null; + + } + + @Override + public boolean select(NodeContext context) { + + assertNotDisposed(); + + if (context == null || context.equals(rootContext) || contextToNodeMap.getValuesUnsafe(context).size() == 0) { + StructuredSelection s = new StructuredSelection(); + selectionAdaptor.setSelection(s); + selectionProvider.setAndFireNonEqualSelection(s); + return true; + } + + selectionAdaptor.setSelection(new StructuredSelection(contextToNodeMap.getValuesUnsafe(context).get(0))); + + return false; + + } + + @Override + public boolean selectPath(Collection contexts) { + + if(contexts == null) throw new IllegalArgumentException("Null list is not allowed"); + if(contexts.isEmpty()) throw new IllegalArgumentException("Empty list is not allowed"); + + return selectPathInternal(contexts.toArray(new NodeContext[contexts.size()]), 0); + + } + + private boolean selectPathInternal(NodeContext[] contexts, int position) { + + NodeContext head = contexts[position]; + + if(position == contexts.length-1) { + return select(head); + + } + + setExpanded(head, true); + if(!waitVisible(contexts[position+1])) return false; + + return selectPathInternal(contexts, position+1); + + } + + private boolean waitVisible(NodeContext context) { + long start = System.nanoTime(); + while(!isVisible(context)) { + Display.getCurrent().readAndDispatch(); + long duration = System.nanoTime() - start; + if(duration > 10e9) return false; + } + return true; + } + + @Override + public boolean isVisible(NodeContext context) { + if (contextToNodeMap.getValuesUnsafe(context).size() == 0) + return false; + + return true; //FIXME +// Object elements[] = viewer.getVisibleExpandedElements(); +// return org.simantics.utils.datastructures.Arrays.contains(elements, contextToNodeMap.getValuesUnsafe(context).get(0)); + + + } + + @Override + public TransientExplorerState getTransientState() { + if (!thread.currentThreadAccess()) + throw new AssertionError(getClass().getSimpleName() + ".getActiveColumn called from non SWT-thread: " + Thread.currentThread()); + return transientState; + } + + @Override + public T query(NodeContext context, CacheKey key) { + return this.explorerContext.cache.get(context, key); + } + + /** + * For setting a more local service locator for the explorer than the global + * workbench service locator. Sometimes required to give this implementation + * access to local workbench services like IFocusService. + * + *

+ * Must be invoked during right after construction. + * + * @param serviceLocator + * a specific service locator or null to use the + * workbench global service locator + */ + public void setServiceLocator(IServiceLocator serviceLocator) { + if (serviceLocator == null && PlatformUI.isWorkbenchRunning()) + serviceLocator = PlatformUI.getWorkbench(); + this.serviceLocator = serviceLocator; + if (serviceLocator != null) { + this.contextService = (IContextService) serviceLocator.getService(IContextService.class); + this.focusService = (IFocusService) serviceLocator.getService(IFocusService.class); + } + } + + private void detachPrimitiveProcessors() { + for (PrimitiveQueryProcessor p : primitiveProcessors.values()) { + if (p instanceof ProcessorLifecycle) { + ((ProcessorLifecycle) p).detached(this); + } + } + } + + private void clearPrimitiveProcessors() { + for (PrimitiveQueryProcessor p : primitiveProcessors.values()) { + if (p instanceof ProcessorLifecycle) { + ((ProcessorLifecycle) p).clear(); + } + } + } + + @Override + public void setExpanded(NodeContext context, boolean expanded) { + for (TreeNode n : contextToNodeMap.getValues(context)) { + if (expanded) + treeLayer.expandTreeRow(n.getListIndex()); + else + treeLayer.collapseTreeRow(n.getListIndex()); + } + //viewer.setExpandedState(context, expanded); + + } + + @Override + public void setAutoExpandLevel(int level) { + this.autoExpandLevel = level; + treeLayer.expandAllToLevel(level); + //viewer.setAutoExpandLevel(level); + } + + int maxChildren = DEFAULT_MAX_CHILDREN; + + @Override + public int getMaxChildren() { + return maxChildren; + } + + @Override + public void setMaxChildren(int maxChildren) { + this.maxChildren = maxChildren; + + } + + @Override + public int getMaxChildren(NodeQueryManager manager, NodeContext context) { + Integer result = manager.query(context, BuiltinKeys.SHOW_MAX_CHILDREN); + //System.out.println("getMaxChildren(" + manager + ", " + context + "): " + result); + if (result != null) { + if (result < 0) + throw new AssertionError("BuiltinKeys.SHOW_MAX_CHILDREN query must never return < 0, got " + result); + return result; + } + return maxChildren; + } + + @Override + public NodeQueryProcessor getProcessor(QueryKey key) { + return explorerContext.getProcessor(key); + } + + @Override + public PrimitiveQueryProcessor getPrimitiveProcessor(PrimitiveQueryKey key) { + return explorerContext.getPrimitiveProcessor(key); + } + + private HashSet pendingItems = new HashSet(); + private boolean updating = false; + private int updateCounter = 0; + final ScheduledExecutorService uiUpdateScheduler = ThreadUtils.getNonBlockingWorkExecutor(); + + private class UpdateItem { + TreeNode element; + int columnIndex; + + public UpdateItem(TreeNode element) { + this(element,-1); + } + + public UpdateItem(TreeNode element, int columnIndex) { + this.element = element; + this.columnIndex = columnIndex; + if (element != null && element.isDisposed()) { + throw new IllegalArgumentException("Node is disposed. " + element); + } + } + + public void update(NatTable natTable) { + if (element != null) { + + if (element.isDisposed()) { + return; + } + if (((TreeNode)element).updateChildren()) { + listReIndex(); + natTable.refresh(true); + //viewer.refresh(element,true); + } else { + if (columnIndex >= 0) { + natTable.redraw(); + //viewer.update(element, new String[]{columns[columnIndex].getKey()}); + } else { + natTable.redraw(); + //viewer.refresh(element,true); + } + } + + if (!element.isDisposed() && autoExpandLevel > 1 && !element.isExpanded() && element.getDepth() <= autoExpandLevel) { + expand = true; + treeLayer.expandTreeRow(element.getListIndex()); + //viewer.setExpandedState(element, true); + expand = false; + } + } else { + if (rootNode.updateChildren()) { + listReIndex(); + natTable.refresh(true); + //viewer.refresh(rootNode,true); + } + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj.getClass() != getClass()) + return false; + UpdateItem other = (UpdateItem)obj; + if (columnIndex != other.columnIndex) + return false; + if (element != null) + return element.equals(other.element); + return other.element == null; + } + + @Override + public int hashCode() { + if (element != null) + return element.hashCode() + columnIndex; + return 0; + } + } + + private void update(final TreeNode element, final int columnIndex) { + if (DEBUG)System.out.println("update " + element + " " + columnIndex); + if (natTable.isDisposed()) + return; + synchronized (pendingItems) { + pendingItems.add(new UpdateItem(element, columnIndex)); + if (updating) return; + updateCounter++; + scheduleUpdater(); + } + } + + private void update(final TreeNode element) { + if (DEBUG)System.out.println("update " + element); + if (natTable.isDisposed()) + return; + if (element != null && element.isDisposed()) + return; + synchronized (pendingItems) { + + pendingItems.add(new UpdateItem(element)); + if (updating) return; + updateCounter++; + scheduleUpdater(); + } + } + + boolean scheduleUpdater() { + + if (natTable.isDisposed()) + return false; + + if (!pendingItems.isEmpty()) { + + int activity = explorerContext.activityInt; + long delay = 30; + if (activity < 100) { + //System.out.println("Scheduling update immediately."); + } else if (activity < 1000) { + //System.out.println("Scheduling update after 500ms."); + delay = 500; + } else { + //System.out.println("Scheduling update after 3000ms."); + delay = 3000; + } + + updateCounter = 0; + + //System.out.println("Scheduling UI update after " + delay + " ms."); + uiUpdateScheduler.schedule(new Runnable() { + @Override + public void run() { + + if (natTable == null || natTable.isDisposed()) + return; + + if (updateCounter > 0) { + updateCounter = 0; + uiUpdateScheduler.schedule(this, 50, TimeUnit.MILLISECONDS); + } else { + natTable.getDisplay().asyncExec(new UpdateRunner(NatTableGraphExplorer.this, NatTableGraphExplorer.this.explorerContext)); + } + + } + }, delay, TimeUnit.MILLISECONDS); + + updating = true; + return true; + } + + return false; + } + + @Override + public String startEditing(NodeContext context, String columnKey) { + assertNotDisposed(); + if (!thread.currentThreadAccess()) + throw new IllegalStateException("not in SWT display thread " + thread.getThread()); + + if(columnKey.startsWith("#")) { + columnKey = columnKey.substring(1); + } + + Integer columnIndex = columnKeyToIndex.get(columnKey); + if (columnIndex == null) + return "Rename not supported for selection"; +// FIXME: +// viewer.editElement(context, columnIndex); +// if(viewer.isCellEditorActive()) return null; + return "Rename not supported for selection"; + } + + @Override + public String startEditing(String columnKey) { + ISelection selection = postSelectionProvider.getSelection(); + if(selection == null) return "Rename not supported for selection"; + NodeContext context = ISelectionUtils.filterSingleSelection(selection, NodeContext.class); + if(context == null) return "Rename not supported for selection"; + + return startEditing(context, columnKey); + + } + + public void setSelection(final ISelection selection, boolean forceControlUpdate) { + assertNotDisposed(); + boolean equalsOld = selectionProvider.selectionEquals(selection); + if (equalsOld && !forceControlUpdate) { + // Just set the selection object instance, fire no events nor update + // the viewer selection. + selectionProvider.setSelection(selection); + } else { + Collection coll = AdaptionUtils.adaptToCollection(selection, NodeContext.class); + Collection nodes = new ArrayList(); + for (NodeContext c : coll) { + List match = contextToNodeMap.getValuesUnsafe(c); + if(match.size() > 0) + nodes.add(match.get(0)); + } + final ISelection sel = new StructuredSelection(nodes.toArray()); + if (coll.size() == 0) + return; + // Schedule viewer and selection update if necessary. + if (natTable.isDisposed()) + return; + Display d = natTable.getDisplay(); + if (d.getThread() == Thread.currentThread()) { + selectionAdaptor.setSelection(sel); + } else { + d.asyncExec(new Runnable() { + @Override + public void run() { + if (natTable.isDisposed()) + return; + selectionAdaptor.setSelection(sel); + } + }); + } + } + } + + @Override + public void setModificationContext(ModificationContext modificationContext) { + this.modificationContext = modificationContext; + + } + + final ExecutorService queryUpdateScheduler = Threads.getExecutor(); + + + private double getDisplayScale() { + Point dpi = Display.getCurrent().getDPI(); + return (double)dpi.x/96.0; + } + + private void createNatTable() { + GETreeData treeData = new GETreeData(list); + GETreeRowModel treeRowModel = new GETreeRowModel(treeData); + columnAccessor = new GEColumnAccessor(this); + + IDataProvider dataProvider = new ListDataProvider(list, columnAccessor); + + int defaultFontSize = 12; + int height = (int)Math.ceil(((double)(defaultFontSize))*getDisplayScale()) + DataLayer.DEFAULT_ROW_HEIGHT-defaultFontSize; + dataLayer = new DataLayer(dataProvider, DataLayer.DEFAULT_COLUMN_WIDTH, height); + + // resizable rows are unnecessary in Sulca report. + dataLayer.setRowsResizableByDefault(false); + + // Row header layer + DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(dataProvider); + rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider); + + // adjust row header column width so that row numbers fit into the column. + //adjustRowHeaderWidth(list.size()); + + // Column header layer + columnHeaderDataProvider = new GEColumnHeaderDataProvider(this, dataLayer); + columnHeaderDataLayer = new DefaultColumnHeaderDataLayer(columnHeaderDataProvider); + columnHeaderDataLayer.setDefaultRowHeight(height); + columnHeaderDataProvider.updateColumnSizes(); + + //ISortModel sortModel = new EcoSortModel(this, generator,dataLayer); + + // Column re-order + hide + ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(dataLayer); + ColumnHideShowLayer columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer); + + + treeLayer = new GETreeLayer(columnHideShowLayer, treeRowModel, false); + + selectionLayer = new SelectionLayer(treeLayer); + + viewportLayer = new ViewportLayer(selectionLayer); + + ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, viewportLayer, selectionLayer); + // Note: The column header layer is wrapped in a filter row composite. + // This plugs in the filter row functionality + + ColumnOverrideLabelAccumulator labelAccumulator = new ColumnOverrideLabelAccumulator(columnHeaderDataLayer); + columnHeaderDataLayer.setConfigLabelAccumulator(labelAccumulator); + + // Register labels + //SortHeaderLayer sortHeaderLayer = new SortHeaderLayer(columnHeaderLayer, sortModel, false); + + RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, viewportLayer, selectionLayer); + + // Corner layer + DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider); + cornerDataLayer = new DataLayer(cornerDataProvider); + //CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer); + CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnHeaderLayer); + + // Grid + //GridLayer gridLayer = new GridLayer(viewportLayer,sortHeaderLayer,rowHeaderLayer, cornerLayer); + GridLayer gridLayer = new GridLayer(viewportLayer, columnHeaderLayer,rowHeaderLayer, cornerLayer, false); + + /* Since 1.4.0, alternative row rendering uses row indexes in the original data list. + When combined with collapsed tree rows, rows with odd or even index may end up next to each other, + which defeats the purpose of alternating colors. This overrides that and returns the functionality + that we had with 1.0.1. */ + gridLayer.setConfigLabelAccumulatorForRegion(GridRegion.BODY, new RelativeAlternatingRowConfigLabelAccumulator()); + gridLayer.addConfiguration(new DefaultEditConfiguration()); + //gridLayer.addConfiguration(new DefaultEditBindings()); + gridLayer.addConfiguration(new GEEditBindings()); + + natTable = new NatTable(composite,gridLayer,false); + + //selectionLayer.registerCommandHandler(new EcoCopyDataCommandHandler(selectionLayer,columnHeaderDataLayer,columnAccessor, columnHeaderDataProvider)); + + natTable.addConfiguration(new NatTableHeaderMenuConfiguration(natTable)); + natTable.addConfiguration(new DefaultTreeLayerConfiguration2(treeLayer)); + natTable.addConfiguration(new SingleClickSortConfiguration()); + //natTable.addLayerListener(this); + + natTable.addConfiguration(new GENatTableThemeConfiguration(treeData)); + natTable.addConfiguration(new NatTableHeaderMenuConfiguration(natTable)); + + natTable.addConfiguration(new AbstractRegistryConfiguration() { + + @Override + public void configureRegistry(IConfigRegistry configRegistry) { + configRegistry.registerConfigAttribute( + EditConfigAttributes.CELL_EDITABLE_RULE, + new IEditableRule() { + + @Override + public boolean isEditable(ILayerCell cell, + IConfigRegistry configRegistry) { + int col = cell.getColumnIndex(); + int row = cell.getRowIndex(); + TreeNode node = list.get(row); + Modifier modifier = getModifier(node,col); + return modifier != null; + + } + + @Override + public boolean isEditable(int columnIndex, int rowIndex) { + // there are no callers? + return false; + } + + }); + configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new AdaptableCellEditor()); + configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, new DefaultDisplayConverter(),DisplayMode.EDIT); + // configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, new GECellPainter(),DisplayMode.NORMAL); + + + } + }); + + natTable.configure(); + +// natTable.addListener(SWT.MenuDetect, new NatTableMenuListener()); + +// DefaultToolTip toolTip = new EcoCellToolTip(natTable, columnAccessor); +// toolTip.setBackgroundColor(natTable.getDisplay().getSystemColor(SWT.COLOR_WHITE)); +// toolTip.setPopupDelay(500); +// toolTip.activate(); +// toolTip.setShift(new Point(10, 10)); + + +// menuManager.createContextMenu(composite); +// natTable.setMenu(getMenuManager().getMenu()); + + selectionAdaptor = new NatTableSelectionAdaptor(natTable, selectionLayer, treeData); + } + + Modifier getModifier(TreeNode element, int columnIndex) { + GENodeQueryManager manager = element.getManager(); + final NodeContext context = element.getContext(); + Labeler labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER); + if (labeler == null) + return null; + Column column = columns[columnIndex]; + + return labeler.getModifier(modificationContext, column.getKey()); + + } + + private class AdaptableCellEditor implements ICellEditor { + ICellEditor editor; + + @Override + public Control activateCell(Composite parent, Object originalCanonicalValue, EditModeEnum editMode, + ICellEditHandler editHandler, ILayerCell cell, IConfigRegistry configRegistry) { + int col = cell.getColumnIndex(); + int row = cell.getRowIndex(); + TreeNode node = list.get(row); + Modifier modifier = getModifier(node, col); + if (modifier == null) + return null; + + editor = null; + if (modifier instanceof DialogModifier) { + DialogModifier mod = (DialogModifier)modifier; + editor = new DialogCellEditor(node, col, mod); + } else if (modifier instanceof CustomModifier) { + CustomModifier mod = (CustomModifier)modifier; + editor = new CustomCellEditor(node, col, mod); + } else if (modifier instanceof EnumerationModifier) { + EnumerationModifier mod = (EnumerationModifier)modifier; + editor = new ComboBoxCellEditor(mod.getValues()); + } else { + editor = new TextCellEditor(); + } + + return editor.activateCell(parent, originalCanonicalValue, editMode, editHandler, cell, configRegistry); + } + + @Override + public int getColumnIndex() { + return editor.getColumnIndex(); + } + + @Override + public int getRowIndex() { + return editor.getRowIndex(); + } + + @Override + public int getColumnPosition() { + return editor.getColumnPosition(); + } + + @Override + public int getRowPosition() { + return editor.getRowPosition(); + } + + @Override + public Object getEditorValue() { + return editor.getEditorValue(); + } + + @Override + public void setEditorValue(Object value) { + editor.setEditorValue(value); + + } + + @Override + public Object getCanonicalValue() { + return editor.getCanonicalValue(); + } + + @Override + public Object getCanonicalValue(IEditErrorHandler conversionErrorHandler) { + return editor.getCanonicalValue(); + } + + @Override + public void setCanonicalValue(Object canonicalValue) { + editor.setCanonicalValue(canonicalValue); + + } + + @Override + public boolean validateCanonicalValue(Object canonicalValue) { + return editor.validateCanonicalValue(canonicalValue); + } + + @Override + public boolean validateCanonicalValue(Object canonicalValue, IEditErrorHandler validationErrorHandler) { + return editor.validateCanonicalValue(canonicalValue, validationErrorHandler); + } + + @Override + public boolean commit(MoveDirectionEnum direction) { + return editor.commit(direction); + } + + @Override + public boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit) { + return editor.commit(direction, closeAfterCommit); + } + + @Override + public boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit, boolean skipValidation) { + return editor.commit(direction, closeAfterCommit, skipValidation); + } + + @Override + public void close() { + editor.close(); + + } + + @Override + public boolean isClosed() { + return editor.isClosed(); + } + + @Override + public Control getEditorControl() { + return editor.getEditorControl(); + } + + @Override + public Control createEditorControl(Composite parent) { + return editor.createEditorControl(parent); + } + + @Override + public boolean openInline(IConfigRegistry configRegistry, List configLabels) { + return EditConfigHelper.openInline(configRegistry, configLabels); + } + + @Override + public boolean supportMultiEdit(IConfigRegistry configRegistry, List configLabels) { + return editor.supportMultiEdit(configRegistry, configLabels); + } + + @Override + public boolean openMultiEditDialog() { + return editor.openMultiEditDialog(); + } + + @Override + public boolean openAdjacentEditor() { + return editor.openAdjacentEditor(); + } + + @Override + public boolean activateAtAnyPosition() { + return true; + } + + @Override + public boolean activateOnTraversal(IConfigRegistry configRegistry, List configLabels) { + return editor.activateOnTraversal(configRegistry, configLabels); + } + + @Override + public void addEditorControlListeners() { + editor.addEditorControlListeners(); + + } + + @Override + public void removeEditorControlListeners() { + editor.removeEditorControlListeners(); + + } + + @Override + public Rectangle calculateControlBounds(Rectangle cellBounds) { + return editor.calculateControlBounds(cellBounds); + } + + + } + + private class CustomCellEditor extends AbstractCellEditor { + TreeNode node; + CustomModifier customModifier; + Control control; + int column; + + public CustomCellEditor(TreeNode node, int column, CustomModifier customModifier) { + this.customModifier = customModifier; + this.node = node; + this.column = column; + } + + @Override + public Object getEditorValue() { + return customModifier.getValue(); + } + + @Override + public void setEditorValue(Object value) { + customModifier.modify(value.toString()); + + } + + @Override + public Control getEditorControl() { + return control; + } + + @Override + public Control createEditorControl(Composite parent) { + return (Control)customModifier.createControl(parent, null, column, node.getContext()); + + } + + @Override + protected Control activateCell(Composite parent, Object originalCanonicalValue) { + this.control = createEditorControl(parent); + return control; + } + + + } + + private class DialogCellEditor extends AbstractDialogCellEditor { + TreeNode node; + DialogModifier dialogModifier; + int column; + + String res = null; + Semaphore sem; + + boolean closed = false; + + public DialogCellEditor(TreeNode node, int column, DialogModifier dialogModifier) { + this.dialogModifier = dialogModifier; + this.node = node; + this.column = column; + } + + @Override + public int open() { + sem = new Semaphore(1); + Consumer callback = result -> { + res = result; + sem.release(); + }; + String status = dialogModifier.query(this.parent.getShell(), null, column, node.getContext(), callback); + if (status != null) { + closed = true; + return Window.CANCEL; + } + + try { + sem.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + closed = true; + return Window.OK; + } + + @Override + public DialogModifier createDialogInstance() { + closed = false; + return dialogModifier; + } + + @Override + public Object getDialogInstance() { + return (DialogModifier)this.dialog; + } + + @Override + public void close() { + + } + + @Override + public Object getEditorValue() { + return null; + } + + @Override + public void setEditorValue(Object value) { + // dialog modifier handles this internally + } + + @Override + public boolean isClosed() { + return closed; + } + + } + + + /** + * The job that is used for off-loading image loading tasks (see + * {@link ImageTask} to a worker thread from the main UI thread. + */ + ImageLoaderJob imageLoaderJob; + + // Map imageTasks = new THashMap(); + Map imageTasks = new THashMap(); + + void queueImageTask(TreeNode node, ImageTask task) { + synchronized (imageTasks) { + imageTasks.put(node, task); + } + imageLoaderJob.scheduleIfNecessary(100); + } + + /** + * Invoked in a job worker thread. + * + * @param monitor + */ + @Override + protected IStatus setPendingImages(IProgressMonitor monitor) { + ImageTask[] tasks = null; + synchronized (imageTasks) { + tasks = imageTasks.values().toArray(new ImageTask[imageTasks.size()]); + imageTasks.clear(); + } + + MultiStatus status = null; + + // Load missing images + for (ImageTask task : tasks) { + Object desc = task.descsOrImage; + if (desc instanceof ImageDescriptor) { + try { + desc = resourceManager.get((ImageDescriptor) desc); + task.descsOrImage = desc; + } catch (DeviceResourceException e) { + if (status == null) + status = new MultiStatus(Activator.PLUGIN_ID, 0, "Problems loading images:", null); + status.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Image descriptor loading failed: " + desc, e)); + } + } + + } + + // Perform final UI updates in the UI thread. + final ImageTask[] _tasks = tasks; + thread.asyncExec(new Runnable() { + @Override + public void run() { + setImages(_tasks); + } + }); + + return status != null ? status : Status.OK_STATUS; + } + + + void setImages(ImageTask[] tasks) { + for (ImageTask task : tasks) + if (task != null) + setImage(task); + } + + void setImage(ImageTask task) { + if (!task.node.isDisposed()) + update(task.node, 0); + } + + private static class GraphExplorerPostSelectionProvider implements IPostSelectionProvider { + + private NatTableGraphExplorer ge; + + GraphExplorerPostSelectionProvider(NatTableGraphExplorer ge) { + this.ge = ge; + } + + void dispose() { + ge = null; + } + + @Override + public void setSelection(final ISelection selection) { + if(ge == null) return; + ge.setSelection(selection, false); + + } + + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + if(ge == null) return; + if(ge.isDisposed()) { + if (DEBUG_SELECTION_LISTENERS) + System.out.println("GraphExplorerImpl is disposed in removeSelectionChangedListener: " + listener); + return; + } + ge.selectionProvider.removeSelectionChangedListener(listener); + } + + @Override + public void addPostSelectionChangedListener(ISelectionChangedListener listener) { + if(ge == null) return; + if (!ge.thread.currentThreadAccess()) + throw new AssertionError(getClass().getSimpleName() + ".addPostSelectionChangedListener called from non SWT-thread: " + Thread.currentThread()); + if(ge.isDisposed()) { + System.out.println("Client BUG: GraphExplorerImpl is disposed in addPostSelectionChangedListener: " + listener); + return; + } + ge.selectionProvider.addPostSelectionChangedListener(listener); + } + + @Override + public void removePostSelectionChangedListener(ISelectionChangedListener listener) { + if(ge == null) return; + if(ge.isDisposed()) { + if (DEBUG_SELECTION_LISTENERS) + System.out.println("GraphExplorerImpl is disposed in removePostSelectionChangedListener: " + listener); + return; + } + ge.selectionProvider.removePostSelectionChangedListener(listener); + } + + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if(ge == null) return; + if (!ge.thread.currentThreadAccess()) + throw new AssertionError(getClass().getSimpleName() + ".addSelectionChangedListener called from non SWT-thread: " + Thread.currentThread()); + if (ge.natTable.isDisposed() || ge.selectionProvider == null) { + System.out.println("Client BUG: GraphExplorerImpl is disposed in addSelectionChangedListener: " + listener); + return; + } + + ge.selectionProvider.addSelectionChangedListener(listener); + } + + + @Override + public ISelection getSelection() { + if(ge == null) return StructuredSelection.EMPTY; + if (!ge.thread.currentThreadAccess()) + throw new AssertionError(getClass().getSimpleName() + ".getSelection called from non SWT-thread: " + Thread.currentThread()); + if (ge.natTable.isDisposed() || ge.selectionProvider == null) + return StructuredSelection.EMPTY; + return ge.selectionProvider.getSelection(); + } + + } + + static class ModifierValidator implements ICellEditorValidator { + private Modifier modifier; + public ModifierValidator(Modifier modifier) { + this.modifier = modifier; + } + + @Override + public String isValid(Object value) { + return modifier.isValid((String)value); + } + } + + static class UpdateRunner implements Runnable { + + final NatTableGraphExplorer ge; + + UpdateRunner(NatTableGraphExplorer ge, IGraphExplorerContext geContext) { + this.ge = ge; + } + + public void run() { + try { + doRun(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public void doRun() { + + if (ge.isDisposed()) + return; + + HashSet items; + + ScrollBar verticalBar = ge.natTable.getVerticalBar(); + + + synchronized (ge.pendingItems) { + items = ge.pendingItems; + ge.pendingItems = new HashSet(); + } + if (DEBUG) System.out.println("UpdateRunner.doRun() " + items.size()); + + ge.natTable.setRedraw(false); + for (UpdateItem item : items) { + item.update(ge.natTable); + } + + // check if vertical scroll bar has become visible and refresh layout. + boolean currentlyVerticalBarVisible = verticalBar.isVisible(); + if (ge.verticalBarVisible != currentlyVerticalBarVisible) { + ge.verticalBarVisible = currentlyVerticalBarVisible; + ge.natTable.getParent().layout(); + } + + ge.natTable.setRedraw(true); + + synchronized (ge.pendingItems) { + if (!ge.scheduleUpdater()) { + ge.updating = false; + } + } + if (DEBUG) { + if (!ge.updating) { + ge.printTree(ge.rootNode, 0); + } + } + } + + } + + + + public static class GeViewerContext extends AbstractDisposable implements IGraphExplorerContext { + // This is for query debugging only. + + private NatTableGraphExplorer ge; + int queryIndent = 0; + + GECache2 cache = new GECache2(); + AtomicBoolean propagating = new AtomicBoolean(false); + Object propagateList = new Object(); + Object propagate = new Object(); + List scheduleList = new ArrayList(); + final Deque activity = new LinkedList(); + int activityInt = 0; + + AtomicReference currentQueryUpdater = new AtomicReference(); + + /** + * Keeps track of nodes that have already been auto-expanded. After + * being inserted into this set, nodes will not be forced to stay in an + * expanded state after that. This makes it possible for the user to + * close auto-expanded nodes. + */ + Map autoExpanded = new WeakHashMap(); + + public GeViewerContext(NatTableGraphExplorer ge) { + this.ge = ge; + } + + public MapList getContextToNodeMap() { + if (ge == null) + return null; + return ge.contextToNodeMap; + } + + public NatTableGraphExplorer getGe() { + return ge; + } + + @Override + protected void doDispose() { + //saveState(); + autoExpanded.clear(); + } + + @Override + public IGECache getCache() { + return cache; + } + + @Override + public int queryIndent() { + return queryIndent; + } + + @Override + public int queryIndent(int offset) { + queryIndent += offset; + return queryIndent; + } + + @Override + @SuppressWarnings("unchecked") + public NodeQueryProcessor getProcessor(Object o) { + if (ge == null) + return null; + return ge.processors.get(o); + } + + @Override + @SuppressWarnings("unchecked") + public PrimitiveQueryProcessor getPrimitiveProcessor(Object o) { + return ge.primitiveProcessors.get(o); + } + + @SuppressWarnings("unchecked") + @Override + public DataSource getDataSource(Class clazz) { + return ge.dataSources.get(clazz); + } + + @Override + public void update(UIElementReference ref) { + if (ref instanceof ViewerCellReference) { + ViewerCellReference tiref = (ViewerCellReference) ref; + Object element = tiref.getElement(); + int columnIndex = tiref.getColumn(); + // NOTE: must be called regardless of the the item value. + // A null item is currently used to indicate a tree root update. + ge.update((TreeNode)element,columnIndex); + } else if (ref instanceof ViewerRowReference) { + ViewerRowReference rref = (ViewerRowReference)ref; + Object element = rref.getElement(); + ge.update((TreeNode)element); + } else { + throw new IllegalArgumentException("Ui Reference is unknkown " + ref); + } + } + + @Override + public Object getPropagateLock() { + return propagate; + } + + @Override + public Object getPropagateListLock() { + return propagateList; + } + + @Override + public boolean isPropagating() { + return propagating.get(); + } + + @Override + public void setPropagating(boolean b) { + this.propagating.set(b); + } + + @Override + public List getScheduleList() { + return scheduleList; + } + + @Override + public void setScheduleList(List list) { + this.scheduleList = list; + } + + @Override + public Deque getActivity() { + return activity; + } + + @Override + public void setActivityInt(int i) { + this.activityInt = i; + } + + @Override + public int getActivityInt() { + return activityInt; + } + + @Override + public void scheduleQueryUpdate(Runnable r) { + if (ge == null) + return; + if (ge.isDisposed()) + return; + if (currentQueryUpdater.compareAndSet(null, r)) { + ge.queryUpdateScheduler.execute(QUERY_UPDATE_SCHEDULER); + } + } + + Runnable QUERY_UPDATE_SCHEDULER = new Runnable() { + @Override + public void run() { + Runnable r = currentQueryUpdater.getAndSet(null); + if (r != null) { + r.run(); + } + } + }; + + @Override + public void dispose() { + cache.dispose(); + cache = new DummyCache(); + scheduleList.clear(); + autoExpanded.clear(); + autoExpanded = null; + ge = null; + + } + } + + private class TreeNodeIsExpandedProcessor extends AbstractPrimitiveQueryProcessor implements + IsExpandedProcessor, ProcessorLifecycle { + /** + * The set of currently expanded node contexts. + */ + private final HashSet expanded = new HashSet(); + private final HashMap expandedQueries = new HashMap(); + + private NatTable natTable; + private List list; + + public TreeNodeIsExpandedProcessor() { + } + + @Override + public Object getIdentifier() { + return BuiltinKeys.IS_EXPANDED; + } + + @Override + public String toString() { + return "IsExpandedProcessor"; + } + + @Override + public Boolean query(PrimitiveQueryUpdater updater, NodeContext context, PrimitiveQueryKey key) { + boolean isExpanded = expanded.contains(context); + expandedQueries.put(context, updater); + return Boolean.valueOf(isExpanded); + } + + @Override + public Collection getExpanded() { + return new HashSet(expanded); + } + + @Override + public boolean getExpanded(NodeContext context) { + return this.expanded.contains(context); + } + + @Override + public boolean setExpanded(NodeContext context, boolean expanded) { + return _setExpanded(context, expanded); + } + + @Override + public boolean replaceExpanded(NodeContext context, boolean expanded) { + return nodeStatusChanged(context, expanded); + } + + private boolean _setExpanded(NodeContext context, boolean expanded) { + if (expanded) { + return this.expanded.add(context); + } else { + return this.expanded.remove(context); + } + } + + ILayerListener treeListener = new ILayerListener() { + + @Override + public void handleLayerEvent(ILayerEvent event) { + // TODO Auto-generated method stub + if (event instanceof ShowRowPositionsEvent) { + ShowRowPositionsEvent e = (ShowRowPositionsEvent)event; + for (Range r : e.getRowPositionRanges()) { + int expanded = viewportLayer.getRowIndexByPosition(r.start-2)+1; + //System.out.println("ex " + expanded); + if (expanded < 0) { + return; + } + nodeStatusChanged(list.get(expanded).getContext(), false); + } + } else if (event instanceof HideRowPositionsEvent) { + HideRowPositionsEvent e = (HideRowPositionsEvent)event; + for (Range r : e.getRowPositionRanges()) { + int collapsed = viewportLayer.getRowIndexByPosition(r.start-2)+1; + //System.out.println("col " + collapsed); + if (collapsed < 0) { + return; + } + nodeStatusChanged(list.get(collapsed).getContext(), false); + } + } + + } + }; + + protected boolean nodeStatusChanged(NodeContext context, boolean expanded) { + boolean result = _setExpanded(context, expanded); + PrimitiveQueryUpdater updater = expandedQueries.get(context); + if (updater != null) + updater.scheduleReplace(context, BuiltinKeys.IS_EXPANDED, expanded); + return result; + } + + @Override + public void attached(GraphExplorer explorer) { + Object control = explorer.getControl(); + if (control instanceof NatTable) { + this.natTable = (NatTable) control; + this.list = ((NatTableGraphExplorer)explorer).list; + natTable.addLayerListener(treeListener); + + } else { + System.out.println("WARNING: " + getClass().getSimpleName() + " attached to unsupported control: " + control); + } + } + + @Override + public void clear() { + expanded.clear(); + expandedQueries.clear(); + } + + @Override + public void detached(GraphExplorer explorer) { + clear(); + if (natTable != null) { + natTable.removeLayerListener(treeListener); +// natTable.removeListener(SWT.Expand, treeListener); +// natTable.removeListener(SWT.Collapse, treeListener); + natTable = null; + } + } + } + + private void printTree(TreeNode node, int depth) { + String s = ""; + for (int i = 0; i < depth; i++) { + s += " "; + } + s += node; + System.out.println(s); + int d = depth+1; + for (TreeNode n : node.getChildren()) { + printTree(n, d); + } + + } + + /** + * Copy-paste of org.simantics.browsing.ui.common.internal.GECache.GECacheKey (internal class that cannot be used) + */ + final private static class GECacheKey { + + private NodeContext context; + private CacheKey key; + + GECacheKey(NodeContext context, CacheKey key) { + this.context = context; + this.key = key; + if (context == null || key == null) + throw new IllegalArgumentException("Null context or key is not accepted"); + } + + GECacheKey(GECacheKey other) { + this.context = other.context; + this.key = other.key; + if (context == null || key == null) + throw new IllegalArgumentException("Null context or key is not accepted"); + } + + void setValues(NodeContext context, CacheKey key) { + this.context = context; + this.key = key; + if (context == null || key == null) + throw new IllegalArgumentException("Null context or key is not accepted"); + } + + @Override + public int hashCode() { + return context.hashCode() | key.hashCode(); + } + + @Override + public boolean equals(Object object) { + + if (this == object) + return true; + else if (object == null) + return false; + + GECacheKey i = (GECacheKey) object; + + return key.equals(i.key) && context.equals(i.context); + + } + + }; + + /** + * Copy-paste of org.simantics.browsing.ui.common.internal.GECache with added capability of purging all NodeContext related data. + */ + public static class GECache2 implements IGECache { + + final HashMap entries = new HashMap(); + final HashMap> treeReferences = new HashMap>(); + final HashMap> keyRefs = new HashMap>(); + + /** + * This single instance is used for all get operations from the cache. This + * should work since the GE cache is meant to be single-threaded within the + * current UI thread, what ever that thread is. For put operations which + * store the key, this is not used. + */ + NodeContext getNC = new NodeContext() { + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + return null; + } + + @Override + public T getConstant(ConstantKey key) { + return null; + } + + @Override + public Set> getKeys() { + return Collections.emptySet(); + } + }; + CacheKey getCK = new CacheKey() { + @Override + public Object processorIdenfitier() { + return this; + } + }; + GECacheKey getKey = new GECacheKey(getNC, getCK); + + + private void addKey(GECacheKey key) { + Set refs = keyRefs.get(key.context); + if (refs != null) { + refs.add(key); + } else { + refs = new HashSet(); + refs.add(key); + keyRefs.put(key.context, refs); + } + } + + private void removeKey(GECacheKey key) { + Set refs = keyRefs.get(key.context); + if (refs != null) { + refs.remove(key); + } + } + + public IGECacheEntry put(NodeContext context, CacheKey key, T value) { +// if (DEBUG) System.out.println("Add entry " + context + " " + key); + IGECacheEntry entry = new GECacheEntry(context, key, value); + GECacheKey gekey = new GECacheKey(context, key); + entries.put(gekey, entry); + addKey(gekey); + return entry; + } + + @SuppressWarnings("unchecked") + public T get(NodeContext context, CacheKey key) { + getKey.setValues(context, key); + IGECacheEntry entry = entries.get(getKey); + if (entry == null) + return null; + return (T) entry.getValue(); + } + + @Override + public IGECacheEntry getEntry(NodeContext context, CacheKey key) { + assert(context != null); + assert(key != null); + getKey.setValues(context, key); + return entries.get(getKey); + } + + @Override + public void remove(NodeContext context, CacheKey key) { +// if (DEBUG) System.out.println("Remove entry " + context + " " + key); + getKey.setValues(context, key); + entries.remove(getKey); + removeKey(getKey); + } + + @Override + public Set getTreeReference(NodeContext context, CacheKey key) { + assert(context != null); + assert(key != null); + getKey.setValues(context, key); + return treeReferences.get(getKey); + } + + @Override + public void putTreeReference(NodeContext context, CacheKey key, UIElementReference reference) { + assert(context != null); + assert(key != null); + //if (DEBUG) System.out.println("Add tree reference " + context + " " + key); + getKey.setValues(context, key); + Set refs = treeReferences.get(getKey); + if (refs != null) { + refs.add(reference); + } else { + refs = new HashSet(4); + refs.add(reference); + GECacheKey gekey = new GECacheKey(getKey); + treeReferences.put(gekey, refs); + addKey(gekey); + } + } + + @Override + public Set removeTreeReference(NodeContext context, CacheKey key) { + assert(context != null); + assert(key != null); + //if (DEBUG) System.out.println("Remove tree reference " + context + " " + key); + getKey.setValues(context, key); + removeKey(getKey); + return treeReferences.remove(getKey); + } + + @Override + public boolean isShown(NodeContext context) { + return references.get(context) > 0; + } + + private TObjectIntHashMap references = new TObjectIntHashMap(); + + @Override + public void incRef(NodeContext context) { + int exist = references.get(context); + references.put(context, exist+1); + } + + @Override + public void decRef(NodeContext context) { + int exist = references.get(context); + references.put(context, exist-1); + if(exist == 1) { + references.remove(context); + } + } + + public void dispose() { + references.clear(); + entries.clear(); + treeReferences.clear(); + keyRefs.clear(); + } + + public void dispose(NodeContext context) { + Set keys = keyRefs.remove(context); + if (keys != null) { + for (GECacheKey key : keys) { + entries.remove(key); + treeReferences.remove(key); + } + } + } + } + + + /** + * Non-functional cache to replace actual cache when GEContext is disposed. + * + * @author mlmarko + * + */ + private static class DummyCache extends GECache2 { + + @Override + public IGECacheEntry getEntry(NodeContext context, CacheKey key) { + return null; + } + + @Override + public IGECacheEntry put(NodeContext context, CacheKey key, + T value) { + return null; + } + + @Override + public void putTreeReference(NodeContext context, CacheKey key, + UIElementReference reference) { + } + + @Override + public T get(NodeContext context, CacheKey key) { + return null; + } + + @Override + public Set getTreeReference( + NodeContext context, CacheKey key) { + return null; + } + + @Override + public void remove(NodeContext context, CacheKey key) { + + } + + @Override + public Set removeTreeReference( + NodeContext context, CacheKey key) { + return null; + } + + @Override + public boolean isShown(NodeContext context) { + return false; + } + + @Override + public void incRef(NodeContext context) { + + } + + @Override + public void decRef(NodeContext context) { + + } + + @Override + public void dispose() { + super.dispose(); + } + } + +private class NatTableHeaderMenuConfiguration extends AbstractHeaderMenuConfiguration { + + + public NatTableHeaderMenuConfiguration(NatTable natTable) { + super(natTable); + } + + @Override + protected PopupMenuBuilder createColumnHeaderMenu(NatTable natTable) { + return super.createColumnHeaderMenu(natTable) + .withHideColumnMenuItem() + .withShowAllColumnsMenuItem() + .withAutoResizeSelectedColumnsMenuItem(); + } + + @Override + protected PopupMenuBuilder createCornerMenu(NatTable natTable) { + return super.createCornerMenu(natTable) + .withShowAllColumnsMenuItem(); + } + @Override + protected PopupMenuBuilder createRowHeaderMenu(NatTable natTable) { + return super.createRowHeaderMenu(natTable); + } + } + + private static class RelativeAlternatingRowConfigLabelAccumulator extends AlternatingRowConfigLabelAccumulator { + + @Override + public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) { + configLabels.addLabel((rowPosition % 2 == 0 ? EVEN_ROW_CONFIG_TYPE : ODD_ROW_CONFIG_TYPE)); + } + } + + @Override + public Object getClicked(Object event) { + MouseEvent e = (MouseEvent)event; + final NatTable tree = (NatTable) e.getSource(); + Point point = new Point(e.x, e.y); + int y = natTable.getRowPositionByY(point.y); + int x = natTable.getColumnPositionByX(point.x); + if (x < 0 | y < 0) + return null; + return list.get(y); + } +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableSelectionAdaptor.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableSelectionAdaptor.java new file mode 100644 index 000000000..4302cdeac --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableSelectionAdaptor.java @@ -0,0 +1,139 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.viewers.IPostSelectionProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate; +import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; +import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; +import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; +import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; +import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Event; +import org.simantics.utils.datastructures.MapList; + +public class NatTableSelectionAdaptor implements ISelectionProvider, IPostSelectionProvider, ILayerListener { + NatTable natTable; + SelectionLayer selectionLayer; + GETreeData treeData; + StructuredSelection selection; + + private List selectionListeners = new ArrayList<>(); + private List postSelectionListeners = new ArrayList<>(); + private List selListeners = new ArrayList<>(); + + public NatTableSelectionAdaptor(NatTable natTable, SelectionLayer selectionLayer, GETreeData treeData) { + this.natTable = natTable; + this.selectionLayer = selectionLayer; + this.natTable.addLayerListener(this); + this.treeData = treeData; + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + selectionListeners.add(listener); + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + selectionListeners.remove(listener); + } + + @Override + public void addPostSelectionChangedListener(ISelectionChangedListener listener) { + postSelectionListeners.add(listener); + } + + @Override + public void removePostSelectionChangedListener(ISelectionChangedListener listener) { + postSelectionListeners.remove(listener); + } + + public void addSelectionListener(SelectionListener listener) { + selListeners.add(listener); + } + + public void removeSelectionListener(SelectionListener listener) { + selListeners.remove(listener); + } + + @Override + public ISelection getSelection() { + return selection; + } + + @Override + public void setSelection(ISelection selection) { + if (!(selection instanceof StructuredSelection)) + throw new IllegalArgumentException("Selection must be structured selection"); + + } + + + + private List selectedCells = new ArrayList(); + + @Override + public void handleLayerEvent(ILayerEvent event) { + if (event instanceof CellSelectionEvent) { + evaluateSeletedCells(); + } + } + + /** + * Processes current selection to data indices. + */ + private void evaluateSeletedCells() { + selectedCells.clear(); + for (PositionCoordinate pc : selectionLayer.getSelectedCellPositions()) { + ILayerCell cell = pc.getLayer().getCellByPosition(pc.columnPosition, pc.rowPosition); + selectedCells.add(new Point(cell.getColumnIndex(), cell.getRowIndex())); + } + MapList rowMap = new MapList<>(); + for (Point p : selectedCells) { + rowMap.add(p.y, p.x); + } + List selectionItems = new ArrayList<>(rowMap.getKeySize()); + for (Integer row : rowMap.getKeys()) { + List cols = rowMap.getValues(row); + int col[] = new int[cols.size()]; + for (int i = 0; i < col.length; i++) + col[i] = cols.get(i); + selectionItems.add(new RowSelectionItem(treeData.getDataAtIndex(row), row, col)); + } + this.selection = new StructuredSelection(selectionItems); + fireEvents(); + } + + private void fireEvents() { + for (ISelectionChangedListener l : selectionListeners) { + l.selectionChanged(new SelectionChangedEvent(this, selection)); + } + for (ISelectionChangedListener l : postSelectionListeners) { + l.selectionChanged(new SelectionChangedEvent(this, selection)); + } + Event evt = new Event(); + evt.widget = natTable; + evt.display = natTable.getDisplay(); + evt.data = selection; + for (SelectionListener l : selListeners) { + SelectionEvent sel = new SelectionEvent(evt); + l.widgetSelected(sel); + } + } + + public List getSelectedCells() { + return selectedCells; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/RowSelectionItem.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/RowSelectionItem.java new file mode 100644 index 000000000..2d0b8e33b --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/RowSelectionItem.java @@ -0,0 +1,29 @@ +package org.simantics.browsing.ui.nattable; + +import org.simantics.browsing.ui.swt.AdaptableHintContext; +import org.simantics.db.layer0.SelectionHints; + +public class RowSelectionItem extends AdaptableHintContext { + int rowIndex; + int columnIndex[]; + TreeNode item; + + public RowSelectionItem(TreeNode item, int rowIndex, int columnIndex[]) { + super(SelectionHints.KEY_MAIN); + this.item = item; + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + setHint(SelectionHints.KEY_MAIN,item); + } + + public TreeNode getItem() { + return item; + } + public int getRowIndex() { + return rowIndex; + } + + public int[] getColumnIndex() { + return columnIndex; + } +} \ No newline at end of file diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/SelectedCellEditorMatcher.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/SelectedCellEditorMatcher.java new file mode 100644 index 000000000..4cf1e16be --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/SelectedCellEditorMatcher.java @@ -0,0 +1,39 @@ +package org.simantics.browsing.ui.nattable; + +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.nebula.widgets.nattable.layer.LabelStack; +import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; +import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher; +import org.eclipse.swt.events.MouseEvent; + +public class SelectedCellEditorMatcher extends CellEditorMouseEventMatcher{ + + public SelectedCellEditorMatcher( String regionLabel) { + super(regionLabel); + } + + ILayerCell previous; + int previousTime = 0; + @Override + public boolean matches(NatTable natTable, MouseEvent event, LabelStack regionLabels) { + if (super.matches(natTable, event, regionLabels)) { + int px = natTable.getColumnPositionByX(event.x); + int py = natTable.getRowPositionByY(event.y); + ILayerCell cell = natTable.getCellByPosition(px,py); + int time = event.time; + if (previous != null && + cell.getColumnIndex() == previous.getColumnIndex() && + cell.getRowIndex() == previous.getRowIndex() && + time - previousTime > event.display.getDoubleClickTime()) + return true; + previous = cell; + previousTime = time; + } + return false; + } + + + + + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/TreeNode.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/TreeNode.java new file mode 100644 index 000000000..941c6f319 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/TreeNode.java @@ -0,0 +1,367 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.resource.ColorDescriptor; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; +import org.eclipse.nebula.widgets.nattable.style.Style; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.simantics.browsing.ui.BuiltinKeys; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.common.internal.GENodeQueryManager; +import org.simantics.browsing.ui.content.ImageDecorator; +import org.simantics.browsing.ui.content.Imager; +import org.simantics.browsing.ui.content.LabelDecorator; +import org.simantics.browsing.ui.content.Labeler; +import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GECache2; +import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GeViewerContext; +import org.simantics.browsing.ui.swt.ViewerRowReference; +import org.simantics.utils.datastructures.BijectionMap; + +public class TreeNode implements IAdaptable { + private static boolean DEBUG = false; + + private NodeContext context; + GENodeQueryManager manager; + GeViewerContext explorerContext; + + TreeNode parent; + List children = new ArrayList(); + boolean expanded; + + public TreeNode(NodeContext context, GeViewerContext explorerContext) { + this.context = context; + this.explorerContext = explorerContext; + this.expanded = false; + manager = new GENodeQueryManager(explorerContext, null, null, ViewerRowReference.create(this)); + explorerContext.getContextToNodeMap().add(context, this); + } + + int getDepth() { + if (parent == null) + return 0; + return parent.getDepth() + 1; + } + + int listIndex; + + public int getListIndex() { + return listIndex; + } + + public void setListIndex(int listIndex) { + this.listIndex = listIndex; + } + + List getChildren() { + return children; + } + + public TreeNode getParent() { + return parent; + } + + public void setExpanded(boolean expanded) { + this.expanded = expanded; + } + + public boolean isExpanded() { + return expanded; + } + + public NodeContext getContext() { + return context; + } + + private Labeler labeler; + private Imager imager; + Collection labelDecorators; + Collection imageDecorators; + + Map labels; + Map runtimeLabels; + + public String getValueString(int column) { + if (column == 0) { + initData(); + } + if (labeler != null) { + String key = explorerContext.getGe().getColumns()[column].getKey(); + String s = null; + if (runtimeLabels != null) + s = runtimeLabels.get(key); + if (s == null) + s = labels.get(key); + if (labelDecorators != null && !labelDecorators.isEmpty()) { + int index = 0; + for (LabelDecorator ld : labelDecorators) { + String ds = ld.decorateLabel(s, key, index); + if (ds != null) + s = ds; + } + } + return s; + } + return null; + } + + public Image getImage(int column) { + String key = explorerContext.getGe().getColumns()[column].getKey(); + if (imager != null) { + Object descOrImage = null; + boolean hasUncachedImages = false; + + ImageDescriptor desc = imager.getImage(key); + if (desc != null) { + int index = 0; + // Attempt to decorate the label + if (!imageDecorators.isEmpty()) { + for (ImageDecorator id : imageDecorators) { + ImageDescriptor ds = id.decorateImage(desc, key, index); + if (ds != null) + desc = ds; + } + } + + // Try resolving only cached images here and now + Object img = explorerContext.getGe().localResourceManager.find(desc); + if (img == null) + img = explorerContext.getGe().resourceManager.find(desc); + + descOrImage = img != null ? img : desc; + hasUncachedImages |= img == null; + } + + if (!hasUncachedImages) { + return (Image) descOrImage; + } else { + // Schedule loading to another thread to refrain from + // blocking + // the UI with database operations. + explorerContext.getGe().queueImageTask(this, new ImageTask(this, descOrImage)); + return null; + } + } else { + return null; + } + } + + public void getStyle(int column, Style style) { + String key = explorerContext.getGe().getColumns()[column].getKey(); + FontDescriptor font = explorerContext.getGe().originalFont; + ColorDescriptor bg = explorerContext.getGe().originalBackground; + ColorDescriptor fg = explorerContext.getGe().originalForeground; + + // Attempt to decorate the label + if (labelDecorators != null && !labelDecorators.isEmpty()) { + int index = 0; + for (LabelDecorator ld : labelDecorators) { + + FontDescriptor dfont = ld.decorateFont(font, key, index); + if (dfont != null) + font = dfont; + + ColorDescriptor dbg = ld.decorateBackground(bg, key, index); + if (dbg != null) + bg = dbg; + + ColorDescriptor dfg = ld.decorateForeground(fg, key, index); + if (dfg != null) + fg = dfg; + } + } + + if (font != explorerContext.getGe().originalFont) { + // System.out.println("set font: " + index + ": " + + // font); + style.setAttributeValue(CellStyleAttributes.FONT,(Font) explorerContext.getGe().localResourceManager.get(font)); + } else { + style.setAttributeValue(CellStyleAttributes.FONT,(Font) (explorerContext.getGe().originalFont != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalFont) : null)); + } + if (bg != explorerContext.getGe().originalBackground) + style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(bg)); + else + style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) (explorerContext.getGe().originalBackground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalBackground) : null)); + if (fg != explorerContext.getGe().originalForeground) + style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(fg)); + else + style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) (explorerContext.getGe().originalForeground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalForeground) : null)); + + } + + private void initData() { + labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER); + imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER); + labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS); + imageDecorators = manager.query(context, BuiltinKeys.IMAGE_DECORATORS); + + if (labeler != null) { + labels = labeler.getLabels(); + runtimeLabels = labeler.getRuntimeLabels(); + } else { + labels = null; + runtimeLabels = null; + } + } + + public TreeNode addChild(NodeContext context, GeViewerContext explorerContext) { + TreeNode child = new TreeNode(context, explorerContext); + child.parent = this; + children.add(child); + if (DEBUG) System.out.println("Add " + this + " -> " + child); + return child; + } + + public TreeNode addChild(int index, NodeContext context,GeViewerContext explorerContext) { + + TreeNode child = new TreeNode(context, explorerContext); + child.parent = this; + children.add(index,child); + if (DEBUG) System.out.println("Add " + this + " -> " + child + " at " + index); + return child; + } + + public TreeNode setChild(int index, NodeContext context, GeViewerContext explorerContext) { + + TreeNode child = new TreeNode(context, explorerContext); + child.parent = this; + children.set(index,child); + if (DEBUG) System.out.println("Set " + this + " -> " + child + " at " + index); + return child; + } + + public void dispose() { + if (parent != null) + parent.children.remove(this); + dispose2(); + } + + public void dispose2() { + if (DEBUG) System.out.println("dispose " + this); + parent = null; + for (TreeNode n : children) { + n.dispose2(); + } + clearCache(); + children.clear(); + explorerContext.getContextToNodeMap().remove(context, this); + context = null; + explorerContext = null; + manager.dispose(); + manager = null; + } + + private void clearCache() { + if (explorerContext != null) { + GECache2 cache = explorerContext.cache; + + if (cache != null) { + cache.dispose(context); + } + } + } + + public boolean updateChildren() { + if (context == null) + throw new IllegalStateException("Node is disposed."); + + NodeContext[] childContexts = manager.query(context, BuiltinKeys.FINAL_CHILDREN); + + if (DEBUG) System.out.println("updateChildren " + childContexts.length + " " + this); + + + boolean modified = false; + synchronized (children) { + + int oldCount = children.size(); + BijectionMap indexes = new BijectionMap(); + Set mapped = new HashSet(); + boolean reorder = false; + // locate matching pairs form old and new children + for (int i = 0; i < oldCount; i++) { + NodeContext oldCtx = children.get(i).context; + for (int j = 0; j oldChildren = new ArrayList(oldCount); + oldChildren.addAll(children); + if (childContexts.length >= oldCount) { + for (int i = 0; i < oldCount; i++) { + Integer oldIndex = indexes.getLeft(i); + if (oldIndex == null) { + setChild(i, childContexts[i], explorerContext); + } else { + TreeNode n = oldChildren.get(oldIndex); + children.set(i, n); + } + + } + for (int i = oldCount; i < childContexts.length; i++) { + addChild(childContexts[i], explorerContext); + } + } else { + for (int i = 0; i < childContexts.length; i++) { + Integer oldIndex = indexes.getLeft(i); + if (oldIndex == null) { + setChild(i, childContexts[i], explorerContext); + } else { + TreeNode n = oldChildren.get(oldIndex); + children.set(i, n); + } + } + for (int i = oldCount -1; i >= childContexts.length; i--) { + children.remove(i); + } + } + for (int i = 0; i < oldChildren.size(); i++) { + if (!indexes.containsLeft(i)) { + oldChildren.get(i).dispose2(); + } + } + + } + + } + return modified; + } + + public boolean isDisposed() { + return context == null; + } + + public GENodeQueryManager getManager() { + return manager; + } + + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + if (adapter == NodeContext.class) + return context; + + return context.getAdapter(adapter); + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/AbstractRowHideShowLayer2.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/AbstractRowHideShowLayer2.java new file mode 100644 index 000000000..44f4b7c15 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/AbstractRowHideShowLayer2.java @@ -0,0 +1,301 @@ +/******************************************************************************* + * 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 java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.nebula.widgets.nattable.coordinate.Range; +import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform; +import org.eclipse.nebula.widgets.nattable.layer.ILayer; +import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; +import org.eclipse.nebula.widgets.nattable.layer.LayerUtil; +import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; +import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent; +import org.eclipse.nebula.widgets.nattable.layer.event.VisualRefreshEvent; + +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; + +/** + * AbstractRowHideShowLayer implementation with FastUtils hashmaps. + * + * @see org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer + * + * @author MLMARKO + * + */ +public abstract class AbstractRowHideShowLayer2 extends AbstractLayerTransform implements IUniqueIndexLayer { + + private Int2IntOpenHashMap cachedVisibleRowIndexOrder; + private Int2IntOpenHashMap cachedVisibleRowPositionOrder; + + private Int2IntOpenHashMap cachedHiddenRowIndexToPositionMap; + + protected final Int2IntOpenHashMap startYCache = new Int2IntOpenHashMap(); + + + public AbstractRowHideShowLayer2(IUniqueIndexLayer underlyingLayer) { + super(underlyingLayer); + } + + @Override + public void handleLayerEvent(ILayerEvent event) { + if (event instanceof IStructuralChangeEvent) { + IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event; + if (structuralChangeEvent.isVerticalStructureChanged()) { + // vertical structure has changed, update cached row information + invalidateCache(); + } + } else if (event instanceof VisualRefreshEvent) { + // visual change, e.g. font change, the startYCache needs to be + // cleared in order to re-render correctly + this.startYCache.clear(); + } + super.handleLayerEvent(event); + } + + // Horizontal features + + // Columns + + @Override + public int getColumnPositionByIndex(int columnIndex) { + return ((IUniqueIndexLayer) getUnderlyingLayer()).getColumnPositionByIndex(columnIndex); + } + + // Vertical features + + // Rows + + @Override + public int getRowCount() { + return getCachedVisibleRowIndexes().size(); + } + + @Override + public int getRowIndexByPosition(int rowPosition) { + if (rowPosition < 0 || rowPosition >= getRowCount()) { + return -1; + } + return getCachedVisibleRowPositons().get(rowPosition); + } + + @Override + public int getRowPositionByIndex(int rowIndex) { + return getCachedVisibleRowIndexes().get(rowIndex); + } + + public Collection getRowPositionsByIndexes(Collection rowIndexes) { + IntOpenHashSet rowPositions = new IntOpenHashSet(); + for (int rowIndex : rowIndexes) { + rowPositions.add(getRowPositionByIndex(rowIndex)); + } + return rowPositions; + } + + @Override + public int localToUnderlyingRowPosition(int localRowPosition) { + int rowIndex = getRowIndexByPosition(localRowPosition); + return ((IUniqueIndexLayer) getUnderlyingLayer()).getRowPositionByIndex(rowIndex); + } + + @Override + public int underlyingToLocalRowPosition(ILayer sourceUnderlyingLayer, int underlyingRowPosition) { + int rowIndex = getUnderlyingLayer().getRowIndexByPosition(underlyingRowPosition); + int rowPosition = getRowPositionByIndex(rowIndex); + if (rowPosition >= 0) { + return rowPosition; + } else { + if (this.cachedHiddenRowIndexToPositionMap.containsKey(rowIndex)) { + return this.cachedHiddenRowIndexToPositionMap.get(rowIndex); + } else { + return -1; + } + } + } + + @Override + public Collection underlyingToLocalRowPositions( + ILayer sourceUnderlyingLayer, Collection underlyingRowPositionRanges) { + Collection localRowPositionRanges = new ArrayList(); + + for (Range underlyingRowPositionRange : underlyingRowPositionRanges) { + int startRowPosition = getAdjustedUnderlyingToLocalStartPosition( + sourceUnderlyingLayer, + underlyingRowPositionRange.start, + underlyingRowPositionRange.end); + int endRowPosition = getAdjustedUnderlyingToLocalEndPosition( + sourceUnderlyingLayer, + underlyingRowPositionRange.end, + underlyingRowPositionRange.start); + + // teichstaedt: fixes the problem that ranges where added even if + // the corresponding startPosition weren't found in the underlying + // layer. Without that fix a bunch of ranges of kind Range [-1, 180] + // which causes strange behaviour in Freeze- and other Layers were + // returned. + if (startRowPosition > -1) { + localRowPositionRanges.add(new Range(startRowPosition, endRowPosition)); + } + } + + return localRowPositionRanges; + } + + private int getAdjustedUnderlyingToLocalStartPosition( + ILayer sourceUnderlyingLayer, + int startUnderlyingPosition, + int endUnderlyingPosition) { + int localStartRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, startUnderlyingPosition); + int offset = 0; + while (localStartRowPosition < 0 + && (startUnderlyingPosition + offset < endUnderlyingPosition)) { + localStartRowPosition = + underlyingToLocalRowPosition(sourceUnderlyingLayer, startUnderlyingPosition + offset++); + } + return localStartRowPosition; + } + + private int getAdjustedUnderlyingToLocalEndPosition( + ILayer sourceUnderlyingLayer, + int endUnderlyingPosition, + int startUnderlyingPosition) { + int localEndRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, endUnderlyingPosition - 1); + int offset = 0; + while (localEndRowPosition < 0 + && (endUnderlyingPosition - offset > startUnderlyingPosition)) { + localEndRowPosition = + underlyingToLocalRowPosition(sourceUnderlyingLayer, endUnderlyingPosition - offset++); + } + return localEndRowPosition + 1; + } + + // Height + + @Override + public int getHeight() { + int lastRowPosition = getRowCount() - 1; + return getStartYOfRowPosition(lastRowPosition) + getRowHeightByPosition(lastRowPosition); + } + + // Y + + @Override + public int getRowPositionByY(int y) { + return LayerUtil.getRowPositionByY(this, y); + } + + @Override + public int getStartYOfRowPosition(int localRowPosition) { + int index = this.startYCache.get(localRowPosition); + if (index >= 0) + return index; + + IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer(); + int underlyingPosition = localToUnderlyingRowPosition(localRowPosition); + if (underlyingPosition < 0) { + return -1; + } + int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition); + if (underlyingStartY < 0) { + return -1; + } + + for (Integer hiddenIndex : getHiddenRowIndexes()) { + int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex); + // if the hidden position is -1, it is hidden in the underlying + // layertherefore the underlying layer should handle the positioning + if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) { + underlyingStartY -= underlyingLayer.getRowHeightByPosition(hiddenPosition); + } + } + + this.startYCache.put(localRowPosition, underlyingStartY); + return underlyingStartY; + } + + // Hide/show + + /** + * Will check if the row at the specified index is hidden or not. Checks + * this layer and also the sublayers for the visibility. + * + * @param rowIndex + * The row index of the row whose visibility state should be + * checked. + * @return true if the row at the specified index is hidden, + * false if it is visible. + */ + public abstract boolean isRowIndexHidden(int rowIndex); + + /** + * Will collect and return all indexes of the rows that are hidden in this + * layer. Note: It is not intended that it also collects the row indexes of + * underlying layers. This would cause issues on calculating positions as + * every layer is responsible for those calculations itself. + * + * @return Collection of all row indexes that are hidden in this layer. + */ + public abstract Collection getHiddenRowIndexes(); + + // Cache + + /** + * Invalidate the cache to ensure that information is rebuild. + */ + protected void invalidateCache() { + this.cachedVisibleRowIndexOrder = null; + this.cachedVisibleRowPositionOrder = null; + this.cachedHiddenRowIndexToPositionMap = null; + this.startYCache.clear(); + } + + private Int2IntOpenHashMap getCachedVisibleRowIndexes() { + if (this.cachedVisibleRowIndexOrder == null) { + cacheVisibleRowIndexes(); + } + return this.cachedVisibleRowIndexOrder; + } + + private Int2IntOpenHashMap getCachedVisibleRowPositons() { + if (this.cachedVisibleRowPositionOrder == null) { + cacheVisibleRowIndexes(); + } + return this.cachedVisibleRowPositionOrder; + } + + protected void cacheVisibleRowIndexes() { + this.cachedVisibleRowIndexOrder = new Int2IntOpenHashMap(); + this.cachedVisibleRowPositionOrder = new Int2IntOpenHashMap(); + this.cachedHiddenRowIndexToPositionMap = new Int2IntOpenHashMap(); + this.startYCache.clear(); + + cachedVisibleRowPositionOrder.defaultReturnValue(-1); + cachedVisibleRowIndexOrder.defaultReturnValue(-1); + cachedHiddenRowIndexToPositionMap.defaultReturnValue(-1); + startYCache.defaultReturnValue(-1); + + ILayer underlyingLayer = getUnderlyingLayer(); + int rowPosition = 0; + for (int parentRowPosition = 0; parentRowPosition < underlyingLayer.getRowCount(); parentRowPosition++) { + int rowIndex = underlyingLayer.getRowIndexByPosition(parentRowPosition); + + if (!isRowIndexHidden(rowIndex)) { + this.cachedVisibleRowIndexOrder.put(rowIndex, rowPosition); + this.cachedVisibleRowPositionOrder.put(rowPosition, rowIndex); + rowPosition++; + } else { + this.cachedHiddenRowIndexToPositionMap.put(rowIndex, rowPosition); + } + } + } +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/CopyDataToClipboardSerializer.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/CopyDataToClipboardSerializer.java new file mode 100644 index 000000000..b4eda7d00 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/CopyDataToClipboardSerializer.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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; + } +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/CopyFormattedTextToClipboardSerializer.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/CopyFormattedTextToClipboardSerializer.java new file mode 100644 index 000000000..0d5acfc7c --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/CopyFormattedTextToClipboardSerializer.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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()); + } +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/DefaultTreeLayerConfiguration2.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/DefaultTreeLayerConfiguration2.java new file mode 100644 index 000000000..2a575b8e5 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/DefaultTreeLayerConfiguration2.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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()); + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeCollapseAllCommandHandler.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeCollapseAllCommandHandler.java new file mode 100644 index 000000000..3060f5525 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeCollapseAllCommandHandler.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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 - 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. + *

+ * 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 { + + /** + * 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 getCommandClass() { + return TreeCollapseAllCommand.class; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandAllCommandHandler.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandAllCommandHandler.java new file mode 100644 index 000000000..9f4f2ac91 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandAllCommandHandler.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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 - 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. + *

+ * 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 { + + /** + * 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 getCommandClass() { + return TreeExpandAllCommand.class; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandCollapseCommandHandler.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandCollapseCommandHandler.java new file mode 100644 index 000000000..456f40ed4 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandCollapseCommandHandler.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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 { + + private final TreeLayer2 treeLayer; + + public TreeExpandCollapseCommandHandler(TreeLayer2 treeLayer) { + this.treeLayer = treeLayer; + } + + @Override + public Class getCommandClass() { + return TreeExpandCollapseCommand.class; + } + + @Override + protected boolean doCommand(TreeExpandCollapseCommand command) { + int parentIndex = command.getParentIndex(); + this.treeLayer.expandOrCollapseIndex(parentIndex); + return true; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandToLevelCommandHandler.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandToLevelCommandHandler.java new file mode 100644 index 000000000..b7fa9722a --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeExpandToLevelCommandHandler.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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 - 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. + *

+ * 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 { + + /** + * 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 getCommandClass() { + return TreeExpandToLevelCommand.class; + } + +} diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java new file mode 100644 index 000000000..7d0f64e47 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java @@ -0,0 +1,577 @@ +/******************************************************************************* + * 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 java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.nebula.widgets.nattable.command.ILayerCommand; +import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; +import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowHideCommand; +import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand; +import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent; +import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent; +import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; +import org.eclipse.nebula.widgets.nattable.layer.LabelStack; +import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; +import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter; +import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper; +import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; +import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel; +import org.eclipse.nebula.widgets.nattable.tree.TreeLayer; +import org.eclipse.nebula.widgets.nattable.tree.config.DefaultTreeLayerConfiguration; +import org.eclipse.nebula.widgets.nattable.tree.config.TreeConfigAttributes; +import org.eclipse.nebula.widgets.nattable.tree.painter.IndentedTreeImagePainter; + +import it.unimi.dsi.fastutil.ints.IntRBTreeSet; + +public class TreeLayer2 extends AbstractRowHideShowLayer2 { + + //private static final Log log = LogFactory.getLog(TreeLayer.class); + + public static final String TREE_COLUMN_CELL = "TREE_COLUMN_CELL"; //$NON-NLS-1$ + + public static final int TREE_COLUMN_NUMBER = 0; + + /** + * Flag to configure whether the tree column should be identified by + * position or by index. Default is position. + */ + private boolean useTreeColumnIndex = false; + + /** + * The ITreeRowModelListener that is used to get information about the tree + * structure. + */ + private final ITreeRowModel treeRowModel; + + /** + * Collection of all row indexes that are hidden if tree nodes are + * collapsed. + *

+ * Note: This collection is only in use if the used {@link ITreeRowModel} + * implementation is returning the row indexes of affected rows on + * expand/collapse. There are also implementations that use another approach + * where the hide/show approach is not used (e.g. GlazedListTreeRowModel) + *

+ */ + private final IntRBTreeSet hiddenRowIndexes = new IntRBTreeSet(); + + /** + * The IndentedTreeImagePainter that paints indentation to the left of the + * configured base painter and icons for expand/collapse if possible, to + * render tree structure accordingly. + */ + private IndentedTreeImagePainter indentedTreeImagePainter; + + /** + * Creates a TreeLayer instance based on the given information. Will use a + * default IndentedTreeImagePainter that uses 10 pixels for indentation and + * simple + and - icons for expand/collapse icons. It also uses the + * DefaultTreeLayerConfiguration. + * + * @param underlyingLayer + * The underlying layer on whose top this layer will be set. + * @param treeRowModel + * The ITreeRowModelListener that is used to get information + * about the tree structure. + */ + public TreeLayer2(IUniqueIndexLayer underlyingLayer, ITreeRowModel treeRowModel) { + this(underlyingLayer, treeRowModel, new IndentedTreeImagePainter()); + } + + /** + * Creates a TreeLayer instance based on the given information. Allows to + * specify the IndentedTreeImagePainter while using the + * DefaultTreeLayerConfiguration. + * + * @param underlyingLayer + * The underlying layer on whose top this layer will be set. + * @param treeRowModel + * The ITreeRowModelListener that is used to get information + * about the tree structure. + * @param indentedTreeImagePainter + * The IndentedTreeImagePainter that paints indentation to the + * left of the configured base painter and icons for + * expand/collapse if possible, to render tree structure + * accordingly. + */ + public TreeLayer2( + IUniqueIndexLayer underlyingLayer, + ITreeRowModel treeRowModel, + IndentedTreeImagePainter indentedTreeImagePainter) { + this(underlyingLayer, treeRowModel, indentedTreeImagePainter, true); + } + + /** + * Creates a TreeLayer instance based on the given information. Will use a + * default IndentedTreeImagePainter that uses 10 pixels for indentation and + * simple + and - icons for expand/collapse icons. + * + * @param underlyingLayer + * The underlying layer on whose top this layer will be set. + * @param treeRowModel + * The ITreeRowModelListener that is used to get information + * about the tree structure. + * @param useDefaultConfiguration + * true to use the DefaultTreeLayerConfiguration, + * false if you want to specify your own + * configuration. + */ + public TreeLayer2( + IUniqueIndexLayer underlyingLayer, + ITreeRowModel treeRowModel, + boolean useDefaultConfiguration) { + this(underlyingLayer, + treeRowModel, + new IndentedTreeImagePainter(), + useDefaultConfiguration); + } + + /** + * Creates a TreeLayer instance based on the given information. + * + * @param underlyingLayer + * The underlying layer on whose top this layer will be set. + * @param treeRowModel + * The ITreeRowModelListener that is used to get information + * about the tree structure. + * @param indentedTreeImagePainter + * The IndentedTreeImagePainter that paints indentation to the + * left of the configured base painter and icons for + * expand/collapse if possible, to render tree structure + * accordingly. + * @param useDefaultConfiguration + * true to use the DefaultTreeLayerConfiguration, + * false if you want to specify your own + * configuration. + */ + public TreeLayer2( + IUniqueIndexLayer underlyingLayer, + ITreeRowModel treeRowModel, + IndentedTreeImagePainter indentedTreeImagePainter, + boolean useDefaultConfiguration) { + + super(underlyingLayer); + this.treeRowModel = treeRowModel; + + if (useDefaultConfiguration) { + addConfiguration(new DefaultTreeLayerConfiguration2(this)); + } + + this.indentedTreeImagePainter = indentedTreeImagePainter; + + registerCommandHandler(new TreeExpandCollapseCommandHandler(this)); + registerCommandHandler(new TreeCollapseAllCommandHandler(this)); + registerCommandHandler(new TreeExpandAllCommandHandler(this)); + registerCommandHandler(new TreeExpandToLevelCommandHandler(this)); + } + + @Override + public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) { + LabelStack configLabels = super.getConfigLabelsByPosition(columnPosition, rowPosition); + + if (isTreeColumn(columnPosition)) { + configLabels.addLabelOnTop(TREE_COLUMN_CELL); + + int rowIndex = getRowIndexByPosition(rowPosition); + configLabels.addLabelOnTop( + DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + this.treeRowModel.depth(rowIndex)); + if (!this.treeRowModel.hasChildren(rowIndex)) { + configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE); + } else { + if (this.treeRowModel.isCollapsed(rowIndex)) { + configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE); + } else { + configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE); + } + } + } + return configLabels; + } + + /** + * @return The ITreeRowModelListener that is used to get information about + * the tree structure. + */ + public ITreeRowModel getModel() { + return this.treeRowModel; + } + + /** + * @return The IndentedTreeImagePainter that paints indentation to the left + * of the configured base painter and icons for expand/collapse if + * possible, to render tree structure accordingly. + * + * @deprecated since 1.1 the configured TreeImagePainter should be used + * instead of the hard referenced one + */ + @Deprecated + public IndentedTreeImagePainter getIndentedTreeImagePainter() { + return this.indentedTreeImagePainter; + } + + /** + * @return The ICellPainter that is used to paint the images in the tree by + * the IndentedTreeImagePainter. Usually it is some type of + * TreeImagePainter that paints expand/collapse/leaf icons regarding + * the node state.
+ * Can be null if set explicitly to the + * IndentedTreeImagePainter! + * + * @deprecated since 1.1 the configured TreeImagePainter should be used + * instead of the hard referenced one + */ + @Deprecated + public ICellPainter getTreeImagePainter() { + return this.indentedTreeImagePainter != null ? this.indentedTreeImagePainter + .getTreeImagePainter() : null; + } + + /** + * @param columnPosition + * The column position to check. + * @return true if the given column position is the tree + * column, false if not. + */ + private boolean isTreeColumn(int columnPosition) { + if (this.useTreeColumnIndex) + return getColumnIndexByPosition(columnPosition) == TREE_COLUMN_NUMBER; + + return columnPosition == TREE_COLUMN_NUMBER; + } + + @Override + public ICellPainter getCellPainter( + int columnPosition, int rowPosition, + ILayerCell cell, IConfigRegistry configRegistry) { + ICellPainter cellPainter = super.getCellPainter( + columnPosition, rowPosition, cell, configRegistry); + + if (cell.getConfigLabels().hasLabel(TREE_COLUMN_CELL)) { + + ICellPainter treeCellPainter = configRegistry.getConfigAttribute( + TreeConfigAttributes.TREE_STRUCTURE_PAINTER, + cell.getDisplayMode(), + cell.getConfigLabels().getLabels()); + + if (treeCellPainter != null) { + ICellPainter innerWrapper = treeCellPainter; + IndentedTreeImagePainter treePainter = null; + if (innerWrapper instanceof IndentedTreeImagePainter) { + treePainter = (IndentedTreeImagePainter) innerWrapper; + } else { + while (treePainter == null + && innerWrapper != null + && innerWrapper instanceof CellPainterWrapper + && ((CellPainterWrapper) innerWrapper).getWrappedPainter() != null) { + + innerWrapper = ((CellPainterWrapper) innerWrapper).getWrappedPainter(); + if (innerWrapper instanceof IndentedTreeImagePainter) { + treePainter = (IndentedTreeImagePainter) innerWrapper; + } + } + } + + if (treePainter != null) { + treePainter.setBaseCellPainter(cellPainter); + cellPainter = treeCellPainter; + } else { + // log error +// log.warn("There is no IndentedTreeImagePainter found for TREE_STRUCTURE_PAINTER, " //$NON-NLS-1$ +// + "using local configured IndentedTreeImagePainter as fallback"); //$NON-NLS-1$ + // fallback + this.indentedTreeImagePainter.setBaseCellPainter(cellPainter); + cellPainter = new BackgroundPainter(this.indentedTreeImagePainter); + } + } else { + // backwards compatibility fallback + this.indentedTreeImagePainter.setBaseCellPainter(cellPainter); + cellPainter = new BackgroundPainter(this.indentedTreeImagePainter); + } + } + + return cellPainter; + } + + @Override + public boolean isRowIndexHidden(int rowIndex) { + return this.hiddenRowIndexes.contains(rowIndex) + || isHiddenInUnderlyingLayer(rowIndex); + } + + @Override + public Collection getHiddenRowIndexes() { + return this.hiddenRowIndexes; + } + + /** + * Performs an expand/collapse action dependent on the current state of the + * tree node at the given row index. + * + * @param parentIndex + * The index of the row that shows the tree node for which the + * expand/collapse action should be performed. + */ + public void expandOrCollapseIndex(int parentIndex) { + if (this.treeRowModel.isCollapsed(parentIndex)) { + expandTreeRow(parentIndex); + } else { + collapseTreeRow(parentIndex); + } + } + + /** + * Collapses the tree node for the given row index. + * + * @param parentIndex + * The index of the row that shows the node that should be + * collapsed + */ + public void collapseTreeRow(int parentIndex) { + List rowIndexes = this.treeRowModel.collapse(parentIndex); + List rowPositions = new ArrayList(); + for (Integer rowIndex : rowIndexes) { + int rowPos = getRowPositionByIndex(rowIndex); + // if the rowPos is negative, it is not visible because of hidden + // state in an underlying layer + if (rowPos >= 0) { + rowPositions.add(rowPos); + } + } + this.hiddenRowIndexes.addAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); + } + + /** + * Collapses all tree nodes in the tree. + */ + public void collapseAll() { + List rowIndexes = this.treeRowModel.collapseAll(); + List rowPositions = new ArrayList(); + for (Integer rowIndex : rowIndexes) { + int rowPos = getRowPositionByIndex(rowIndex); + // if the rowPos is negative, it is not visible because of hidden + // state in an underlying layer + if (rowPos >= 0) { + rowPositions.add(rowPos); + } + } + this.hiddenRowIndexes.addAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); + } + + /** + * Expands the tree node for the given row index. + * + * @param parentIndex + * The index of the row that shows the node that should be + * expanded + */ + public void expandTreeRow(int parentIndex) { + List rowIndexes = this.treeRowModel.expand(parentIndex); + // Bug 432865: iterating and removing every single item is faster than + // removeAll() + for (final Integer expandedChildRowIndex : rowIndexes) { + this.hiddenRowIndexes.remove(expandedChildRowIndex); + } + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Expands the tree node for the given row index in the tree to a certain + * level. + * + * @param parentIndex + * The index of the row that shows the node that should be + * expanded + * @param level + * The level to which the tree node should be expanded. + */ + public void expandTreeRowToLevel(int parentIndex, int level) { + List rowIndexes = this.treeRowModel.expandToLevel(parentIndex, level); + // Bug 432865: iterating and removing every single item is faster than + // removeAll() + for (final Integer expandedChildRowIndex : rowIndexes) { + this.hiddenRowIndexes.remove(expandedChildRowIndex); + } + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Expands all tree nodes in the tree. + */ + public void expandAll() { + List rowIndexes = this.treeRowModel.expandAll(); + this.hiddenRowIndexes.clear(); + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Expands all tree nodes in the tree to a certain level. + * + * @param level + * The level to which the tree node should be expanded. + */ + public void expandAllToLevel(int level) { + List rowIndexes = this.treeRowModel.expandToLevel(level); + // Bug 432865: iterating and removing every single item is faster than + // removeAll() +// for (final Integer expandedChildRowIndex : rowIndexes) { +// this.hiddenRowIndexes.remove(expandedChildRowIndex); +// } + if (rowIndexes == null) + return; + for (int i = rowIndexes.size()-1; i>=0; i--) { + this.hiddenRowIndexes.remove(rowIndexes.get(i)); + } + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Checks the underlying layer if the row is hidden by another layer. + * + * @param rowIndex + * The index of the row whose hidden state should be checked + * @return true if the row at the given index is hidden in the + * underlying layer false if not. + */ + private boolean isHiddenInUnderlyingLayer(int rowIndex) { + IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer(); + return (underlyingLayer.getRowPositionByIndex(rowIndex) == -1); + } + + @Override + public boolean doCommand(ILayerCommand command) { + // special command transformations are needed to hide also child nodes + if (command instanceof RowHideCommand) { + return handleRowHideCommand((RowHideCommand) command); + } else if (command instanceof MultiRowHideCommand) { + return handleMultiRowHideCommand((MultiRowHideCommand) command); + } + return super.doCommand(command); + } + + /** + * Checks if the given command tries to hide a row that is a node that is + * not collapsed and has children. In that case also the child rows need to + * be hidden. + * + * @param command + * The {@link RowHideCommand} to process + * @return true if the command has been handled, + * false otherwise + */ + protected boolean handleRowHideCommand(RowHideCommand command) { + // transform position to index + if (command.convertToTargetLayer(this)) { + int rowIndex = getRowIndexByPosition(command.getRowPosition()); + if (this.treeRowModel.hasChildren(rowIndex) + && !this.treeRowModel.isCollapsed(rowIndex)) { + List childIndexes = this.treeRowModel.getChildIndexes(rowIndex); + int[] childPositions = new int[childIndexes.size() + 1]; + childPositions[0] = command.getRowPosition(); + for (int i = 1; i < childIndexes.size() + 1; i++) { + int childPos = getRowPositionByIndex(childIndexes.get(i - 1)); + childPositions[i] = childPos; + } + return super.doCommand(new MultiRowHideCommand(this, childPositions)); + } + } + return super.doCommand(command); + } + + /** + * Checks if the given command tries to hide rows that are nodes that are + * not collapsed and have children. In that case also the child rows need to + * be hidden. + * + * @param command + * The {@link MultiRowHideCommand} to process + * @return true if the command has been handled, + * false otherwise + */ + protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) { + // transform position to index + if (command.convertToTargetLayer(this)) { + List rowPositionsToHide = new ArrayList(); + for (Integer rowPos : command.getRowPositions()) { + rowPositionsToHide.add(rowPos); + int rowIndex = getRowIndexByPosition(rowPos); + if (this.treeRowModel.hasChildren(rowIndex) + && !this.treeRowModel.isCollapsed(rowIndex)) { + List childIndexes = this.treeRowModel.getChildIndexes(rowIndex); + for (Integer childIndex : childIndexes) { + rowPositionsToHide.add(getRowPositionByIndex(childIndex)); + } + } + } + + int[] childPositions = new int[rowPositionsToHide.size()]; + for (int i = 0; i < rowPositionsToHide.size(); i++) { + childPositions[i] = rowPositionsToHide.get(i); + } + return super.doCommand(new MultiRowHideCommand(this, childPositions)); + } + return super.doCommand(command); + } + + /** + * @return true if the column index is used to determine the + * tree column, false if the column position is used. + * Default is false. + */ + public boolean isUseTreeColumnIndex() { + return this.useTreeColumnIndex; + } + + /** + * Configure whether (column index == 0) or (column position == 0) should be + * performed to identify the tree column. + * + * @param useTreeColumnIndex + * true if the column index should be used to + * determine the tree column, false if the column + * position should be used. + */ + public void setUseTreeColumnIndex(boolean useTreeColumnIndex) { + this.useTreeColumnIndex = useTreeColumnIndex; + } + + /** + * @since 1.4 + */ + @Override + public Collection getProvidedLabels() { + Collection result = super.getProvidedLabels(); + + result.add(TreeLayer.TREE_COLUMN_CELL); + result.add(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE); + result.add(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE); + result.add(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE); + // configure 5 levels to be configurable via CSS + // if you need more you need to override this method + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "0"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "1"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "2"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "3"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "4"); //$NON-NLS-1$ + + return result; + } +} diff --git a/bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/GraphExplorerView.java b/bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/GraphExplorerView.java index 477d6815e..12529e64a 100644 --- a/bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/GraphExplorerView.java +++ b/bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/GraphExplorerView.java @@ -261,7 +261,8 @@ public class GraphExplorerView extends GraphExplorerViewBase { } protected void createAuxiliaryControls(Composite parent, Control explorerControl) { - parent.setLayout(LayoutUtils.createNoBorderGridLayout(3, false)); + if (explorerControl instanceof Tree) + parent.setLayout(LayoutUtils.createNoBorderGridLayout(3, false)); if (!hideComparatorSelector) { ComparatorSelector comparatorSelector = new ComparatorSelector(explorer, userSelectedComparableFactoryQueryProcessor, parent, SWT.READ_ONLY); diff --git a/bundles/org.simantics.browsing.ui.swt/META-INF/MANIFEST.MF b/bundles/org.simantics.browsing.ui.swt/META-INF/MANIFEST.MF index 743e97565..9869cab3d 100644 --- a/bundles/org.simantics.browsing.ui.swt/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.browsing.ui.swt/META-INF/MANIFEST.MF @@ -21,6 +21,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 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 diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DefaultMouseListener.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DefaultMouseListener.java index bc639ab9a..f8299ff60 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DefaultMouseListener.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DefaultMouseListener.java @@ -12,7 +12,7 @@ package org.simantics.browsing.ui.swt; import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.Control; import org.simantics.browsing.ui.GraphExplorer; import org.simantics.browsing.ui.common.node.IDoubleClickableNode; import org.simantics.db.common.procedure.adapter.ProcedureAdapter; @@ -45,7 +45,7 @@ public class DefaultMouseListener extends GraphExplorerMouseAdapter { } @Override - protected void handleContextDoubleClick(Tree tree, ISelection selection) { + protected void handleContextDoubleClick(Control tree, ISelection selection) { // First see if node is an IDoubleClickableNode IDoubleClickableNode doubleClickable = AdaptionUtils.adaptToSingle(selection, IDoubleClickableNode.class); if (doubleClickable != null) { diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DoubleClickableNodeMouseListener.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DoubleClickableNodeMouseListener.java index 1ba37c11d..d60506974 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DoubleClickableNodeMouseListener.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/DoubleClickableNodeMouseListener.java @@ -12,7 +12,7 @@ package org.simantics.browsing.ui.swt; import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.Control; import org.simantics.browsing.ui.GraphExplorer; import org.simantics.browsing.ui.common.node.IDoubleClickableNode; import org.simantics.utils.ui.AdaptionUtils; @@ -29,7 +29,7 @@ public class DoubleClickableNodeMouseListener extends GraphExplorerMouseAdapter } @Override - protected void handleContextDoubleClick(Tree tree, ISelection context) { + protected void handleContextDoubleClick(Control tree, ISelection context) { IDoubleClickableNode doubleClickable = AdaptionUtils.adaptToSingle(context, IDoubleClickableNode.class); if (doubleClickable != null) { doubleClickable.handleDoubleClick(); diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerFactory.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerFactory.java index 35a3d1cfb..44464b357 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerFactory.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerFactory.java @@ -11,9 +11,13 @@ *******************************************************************************/ package org.simantics.browsing.ui.swt; +import java.lang.reflect.Method; + +import org.eclipse.core.runtime.Platform; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.services.IServiceLocator; +import org.osgi.framework.Bundle; import org.simantics.Simantics; import org.simantics.browsing.ui.BuiltinKeys; import org.simantics.browsing.ui.GraphExplorer; @@ -32,6 +36,7 @@ import org.simantics.db.layer0.variable.Variables; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.utils.datastructures.BinaryFunction; import org.simantics.utils.datastructures.hints.IHintContext; +import org.simantics.utils.ui.ExceptionUtils; /** * @author Tuukka Lehtonen @@ -184,6 +189,26 @@ public class GraphExplorerFactory { explorer.setServiceLocator(serviceLocator); return explorer; } + + public GraphExplorer create3(Composite parent, int style) { + //GraphExplorerImpl2 explorer = new GraphExplorerImpl2(parent, style); + try { + Bundle bundle = Platform.getBundle("org.simantics.browsing.ui.nattable"); + Class clazz = (Class)bundle.loadClass("org.simantics.browsing.ui.nattable.NatTableGraphExplorer"); + //Class clazz = (Class)bundle.getClass().getClassLoader().loadClass("org.simantics.browsing.ui.nattable.NatTableGraphExplorer"); + GraphExplorer explorer = clazz.getConstructor(Composite.class, int.class).newInstance(parent,style); + explorer.setSelectionDataResolver(selectionDataResolver); + explorer.setSelectionFilter(selectionFilter); + explorer.setSelectionTransformation(selectionTransformation); + Method m = clazz.getMethod("setServiceLocator", IServiceLocator.class); + m.invoke(explorer, serviceLocator); + //explorer.setServiceLocator(serviceLocator); + return explorer; + } catch (Throwable t) { + ExceptionUtils.logAndShowError(t); + return null; + } + } // void hookActions(IWorkbenchSite site) { // IActionBars actionBars = null; diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java index 0978ddb70..3085e6f07 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java @@ -83,6 +83,7 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; @@ -934,7 +935,11 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph deactivateEditingContext(); } }); - editor.setEditor(control, item, columnIndex); + + if (!(control instanceof Shell)) { + editor.setEditor(control, item, columnIndex); + } + control.setFocus(); @@ -1356,6 +1361,8 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph setBasicListeners(); setDefaultProcessors(); + + this.toolTip = new GraphExplorerToolTip(explorerContext, tree); } @Override @@ -1366,6 +1373,8 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph TreeItem previousSingleSelection = null; long focusGainedAt = Long.MIN_VALUE; + protected GraphExplorerToolTip toolTip; + protected void setBasicListeners() { // Keep track of the previous single selection to help // decide whether to start editing a tree node on mouse @@ -1961,6 +1970,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph GENodeQueryManager manager = new GENodeQueryManager(newContext, null, null, TreeItemReference.create(null)); this.explorerContext = newContext; oldContext.safeDispose(); + toolTip.setGraphExplorerContext(explorerContext); // Need to empty these or otherwise they won't be emptied until the // explorer is disposed which would mean that many unwanted references @@ -3546,5 +3556,20 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph this.focusService = (IFocusService) serviceLocator.getService(IFocusService.class); } } + + @Override + public Object getClicked(Object event) { + MouseEvent e = (MouseEvent)event; + final Tree tree = (Tree) e.getSource(); + Point point = new Point(e.x, e.y); + TreeItem item = tree.getItem(point); + + // No selectable item at point? + if (item == null) + return null; + + Object data = item.getData(); + return data; + } } diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl2.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl2.java index f3fca15c4..7cd8efcfd 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl2.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl2.java @@ -88,6 +88,7 @@ import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; @@ -98,6 +99,7 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.contexts.IContextActivation; import org.eclipse.ui.contexts.IContextService; @@ -2929,4 +2931,19 @@ public class GraphExplorerImpl2 extends GraphExplorerImplBase implements GraphEx super.dispose(); } } + + @Override + public Object getClicked(Object event) { + MouseEvent e = (MouseEvent)event; + final Tree tree = (Tree) e.getSource(); + Point point = new Point(e.x, e.y); + TreeItem item = tree.getItem(point); + + // No selectable item at point? + if (item == null) + return null; + + Object data = item.getData(); + return data; + } } diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerMouseAdapter.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerMouseAdapter.java index 9a28ed4f2..77b1b1a53 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerMouseAdapter.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerMouseAdapter.java @@ -16,9 +16,7 @@ import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Control; import org.simantics.browsing.ui.GraphExplorer; import org.simantics.browsing.ui.NodeContext; import org.simantics.utils.ui.AdaptionUtils; @@ -47,15 +45,19 @@ public class GraphExplorerMouseAdapter extends MouseAdapter { } protected ISelection getClickedContext(MouseEvent e) { - final Tree tree = (Tree) e.getSource(); - Point point = new Point(e.x, e.y); - TreeItem item = tree.getItem(point); - - // No selectable item at point? - if (item == null) - return null; - - Object data = item.getData(); +// final Tree tree = (Tree) e.getSource(); +// Point point = new Point(e.x, e.y); +// TreeItem item = tree.getItem(point); +// +// // No selectable item at point? +// if (item == null) +// return null; +// +// Object data = item.getData(); + Object data = ge.getClicked(e); + if (data == null) + return null; + NodeContext context = AdaptionUtils.adaptToSingle(data, NodeContext.class); if (context == null) return null; @@ -81,11 +83,11 @@ public class GraphExplorerMouseAdapter extends MouseAdapter { if (context == null) return; - Tree tree = (Tree) e.getSource(); + Control tree = (Control)e.getSource(); handleContextDoubleClick(tree, context); } - protected void handleContextDoubleClick(Tree tree, ISelection selection) { + protected void handleContextDoubleClick(Control tree, ISelection selection) { } } diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerToolTip.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerToolTip.java new file mode 100644 index 000000000..71e9dd113 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerToolTip.java @@ -0,0 +1,59 @@ +package org.simantics.browsing.ui.swt; + +import org.eclipse.jface.window.ToolTip; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.simantics.browsing.ui.BuiltinKeys; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.common.internal.GENodeQueryManager; +import org.simantics.browsing.ui.common.labelers.LabelerStub; +import org.simantics.browsing.ui.content.Labeler; +import org.simantics.browsing.ui.swt.GraphExplorerImpl.GraphExplorerContext; + +public class GraphExplorerToolTip extends ToolTip { + + private boolean DEBUG = false; + + private Tree parent; + private NodeContext nodeContext; + private Labeler labeler; + + private GraphExplorerContext explorerContext; + + public GraphExplorerToolTip(GraphExplorerContext explorerContext, Tree parent) { + super(parent, NO_RECREATE, false); + setHideOnMouseDown(false); + setPopupDelay(400); + this.explorerContext = explorerContext; + this.parent = parent; + this.nodeContext = null; + if (DEBUG) + System.out.println("GraphExplorerToolTip constructor called for parent : " + parent + ", class : " + parent.getClass().toString()); + } + + @Override + protected Composite createToolTipContentArea(Event event, Composite parent) { + return ((LabelerStub) labeler).createToolTipContentArea(event, parent, nodeContext); + } + + @Override + protected boolean shouldCreateToolTip(Event event) { + TreeItem treeItem = parent.getItem(new Point(event.x, event.y)); + if (treeItem == null) + return false; + GENodeQueryManager manager = new GENodeQueryManager(explorerContext, null, null, TreeItemReference.create(treeItem.getParentItem())); + nodeContext = (NodeContext) treeItem.getData(); + labeler = manager.query(nodeContext, BuiltinKeys.SELECTED_LABELER); + if (nodeContext == null || !(labeler instanceof LabelerStub)) + return false; + return ((LabelerStub) labeler).shouldCreateToolTip(event, nodeContext); + } + + public void setGraphExplorerContext(GraphExplorerContext explorerContext) { + this.explorerContext = explorerContext; + } + +} diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ImageLoaderJob.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ImageLoaderJob.java index 669f3c115..5f5d26b3a 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ImageLoaderJob.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ImageLoaderJob.java @@ -23,7 +23,7 @@ import org.simantics.DatabaseJob; /** * @author Tuukka Lehtonen */ -class ImageLoaderJob extends DatabaseJob { +public class ImageLoaderJob extends DatabaseJob { private GraphExplorerImplBase ge; private AtomicBoolean isScheduled = new AtomicBoolean(); diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/widgets/GraphExplorerComposite.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/widgets/GraphExplorerComposite.java index 5e324fa76..a83c44ab1 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/widgets/GraphExplorerComposite.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/widgets/GraphExplorerComposite.java @@ -363,6 +363,8 @@ public class GraphExplorerComposite extends Composite implements Widget, IAdapta if (args.containsKey("treeView") && Boolean.TRUE.equals(args.get("treeView"))) { explorer = createExplorerControl2(explorerComposite, maxChildren); + } else if (args.containsKey("natTable") && Boolean.TRUE.equals(args.get("natTable"))) { + explorer = createExplorerControl3(explorerComposite, maxChildren); } else { explorer = createExplorerControl(explorerComposite, maxChildren); } @@ -418,7 +420,7 @@ public class GraphExplorerComposite extends Composite implements Widget, IAdapta } public void addListenerToControl(int eventType, Listener listener) { - ((Tree)explorer.getControl()).addListener(eventType, listener); + ((Control)explorer.getControl()).addListener(eventType, listener); } public void finish() { @@ -529,6 +531,7 @@ public class GraphExplorerComposite extends Composite implements Widget, IAdapta DropTarget target = new DropTarget(control, DND.DROP_COPY | DND.DROP_LINK); target.setTransfer(getAcceptedDataTypes()); + if (control instanceof Tree) { target.addDropListener(new DropTargetListener() { Tree tree = (Tree)explorer.getControl(); @@ -570,6 +573,7 @@ public class GraphExplorerComposite extends Composite implements Widget, IAdapta } }); + } // Add workbench listeners and make sure they are cleaned up setWorkbenchListeners(); @@ -1049,6 +1053,19 @@ public class GraphExplorerComposite extends Composite implements Widget, IAdapta return ge; } + + protected GraphExplorer createExplorerControl3(Composite parent, Integer maxChildren) { + GraphExplorerFactory factory = GraphExplorerFactory.getInstance(); + if(maxChildren != null) factory = factory.maxChildrenShown(maxChildren); + + GraphExplorer ge = factory + .selectionDataResolver(new DefaultSelectionDataResolver()) + .selectionTransformation(selectionTransformation) + .setServiceLocator(site) + .create3(parent, style); + + return ge; + } protected void setupDragSource(Session session) { if (dragSource instanceof SessionContainer) { diff --git a/bundles/org.simantics.browsing.ui/src/org/simantics/browsing/ui/GraphExplorer.java b/bundles/org.simantics.browsing.ui/src/org/simantics/browsing/ui/GraphExplorer.java index 570ac7f79..28c3faddb 100644 --- a/bundles/org.simantics.browsing.ui/src/org/simantics/browsing/ui/GraphExplorer.java +++ b/bundles/org.simantics.browsing.ui/src/org/simantics/browsing/ui/GraphExplorer.java @@ -328,5 +328,11 @@ public interface GraphExplorer extends IAdaptable { * By default, implementations should be editable. */ void setEditable(boolean editable); + + /** + * Returns underlaying data object, which was clicked (with mouse). + * @param event Mouse Event (usually org.eclipse.swt.events.MouseEvent) + */ + public Object getClicked(Object event); } diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/impl/ThrowableBinding.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/impl/ThrowableBinding.java index b55b9d0a8..6ad6dd529 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/impl/ThrowableBinding.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/impl/ThrowableBinding.java @@ -82,7 +82,7 @@ public class ThrowableBinding extends RecordBinding { } catch (IllegalAccessException e) { throw new BindingException( e ); } catch (InvocationTargetException e) { - throw new BindingException( e ); + throw new BindingException( e.getCause() ); } } @@ -98,7 +98,7 @@ public class ThrowableBinding extends RecordBinding { } catch (IllegalAccessException e) { throw new BindingException( e ); } catch (InvocationTargetException e) { - throw new BindingException( e ); + throw new BindingException( e.getCause() ); } } @@ -111,7 +111,7 @@ public class ThrowableBinding extends RecordBinding { } catch (IllegalAccessException e) { throw new BindingException( e ); } catch (InvocationTargetException e) { - throw new BindingException( e ); + throw new BindingException( e.getCause() ); } } diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/AsmBindingProvider.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/AsmBindingProvider.java index fe08480f0..6e6c82681 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/AsmBindingProvider.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/AsmBindingProvider.java @@ -64,7 +64,7 @@ public class AsmBindingProvider implements RecordBindingProvider { } catch (IllegalArgumentException e) { throw new BindingConstructionException(e); } catch (InvocationTargetException e) { - throw new BindingConstructionException(e); + throw new BindingConstructionException(e.getCause()); } catch (ClassNotFoundException e) { throw new BindingConstructionException(e); } diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/RecordClassBinding.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/RecordClassBinding.java index c84e76a4a..9bbb257cb 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/RecordClassBinding.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/RecordClassBinding.java @@ -88,7 +88,7 @@ class RecordClassBinding extends ClassBinding { } catch (IllegalArgumentException e) { throw new BindingException(e); } catch (InvocationTargetException e) { - throw new BindingException(e); + throw new BindingException(e.getCause()); } } @@ -108,7 +108,7 @@ class RecordClassBinding extends ClassBinding { } catch (IllegalAccessException e) { throw new BindingException(e); } catch (InvocationTargetException e) { - throw new BindingException(e); + throw new BindingException(e.getCause()); } } @@ -166,7 +166,7 @@ class RecordClassBinding extends ClassBinding { } catch (IllegalArgumentException e) { throw new BindingException(e); } catch (InvocationTargetException e) { - throw new BindingException(e); + throw new BindingException(e.getCause()); } } @@ -252,8 +252,8 @@ class RecordClassBinding extends ClassBinding { } 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 @@ -284,7 +284,7 @@ class RecordClassBinding extends ClassBinding { } catch (IllegalArgumentException e) { throw new BindingException(e); } catch (InvocationTargetException e) { - throw new BindingException(e); + throw new BindingException(e.getCause()); } } diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodInterfaceUtil.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodInterfaceUtil.java index 1ea10b6da..a1ec2a697 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodInterfaceUtil.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodInterfaceUtil.java @@ -291,7 +291,7 @@ public class MethodInterfaceUtil { } catch (IllegalAccessException e) { throw new BindingConstructionException(e); } catch (InvocationTargetException e) { - throw new BindingConstructionException(e); + throw new BindingConstructionException(e.getCause()); } } diff --git a/bundles/org.simantics.db.common/src/org/simantics/db/common/changeset/GenericChangeListener.java b/bundles/org.simantics.db.common/src/org/simantics/db/common/changeset/GenericChangeListener.java index f0d28cdda..e3be243bb 100644 --- a/bundles/org.simantics.db.common/src/org/simantics/db/common/changeset/GenericChangeListener.java +++ b/bundles/org.simantics.db.common/src/org/simantics/db/common/changeset/GenericChangeListener.java @@ -67,7 +67,7 @@ abstract public class GenericChangeListener implements ChangeLi } catch (IllegalAccessException e1) { Logger.defaultLogError(e1); } catch (InvocationTargetException e1) { - Logger.defaultLogError(e1); + Logger.defaultLogError(e1.getCause()); } } diff --git a/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/ExceptionUtil.java b/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/ExceptionUtil.java index e02242418..d265d8ff9 100644 --- a/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/ExceptionUtil.java +++ b/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/ExceptionUtil.java @@ -55,7 +55,7 @@ public final class ExceptionUtil { Logger.defaultLogError(e1); throw t; } catch (InvocationTargetException e1) { - Logger.defaultLogError(e1); + Logger.defaultLogError(e1.getCause()); throw t; } catch (SecurityException e1) { Logger.defaultLogError(e1); diff --git a/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/ManagementSupportImpl.java b/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/ManagementSupportImpl.java index 614e8608b..36577b588 100644 --- a/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/ManagementSupportImpl.java +++ b/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/ManagementSupportImpl.java @@ -83,7 +83,7 @@ public class ManagementSupportImpl implements ManagementSupport { } catch (IllegalAccessException e) { Logger.defaultLogError(e); } catch (InvocationTargetException e) { - Logger.defaultLogError(e); + Logger.defaultLogError(e.getCause()); } } } diff --git a/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/ReflectionAdapter2.java b/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/ReflectionAdapter2.java index 282dad9b2..f8ec0f81a 100644 --- a/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/ReflectionAdapter2.java +++ b/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/ReflectionAdapter2.java @@ -55,8 +55,8 @@ public class ReflectionAdapter2 implements Adapter { procedure.exception(g, e); e.printStackTrace(); } catch (InvocationTargetException e) { - procedure.exception(g, e); - e.printStackTrace(); + procedure.exception(g, e.getCause()); + e.getCause().printStackTrace(); } } else if( parameters.length == 1 && parameters[0] instanceof ThisResource2) { @@ -73,8 +73,8 @@ public class ReflectionAdapter2 implements Adapter { procedure.exception(g, e); e.printStackTrace(); } catch (InvocationTargetException e) { - procedure.exception(g, e); - e.printStackTrace(); + procedure.exception(g, e.getCause()); + e.getCause().printStackTrace(); } } else { @@ -99,8 +99,8 @@ public class ReflectionAdapter2 implements Adapter { procedure.exception(graph, e); e.printStackTrace(); } catch (InvocationTargetException e) { - procedure.exception(graph, e); - e.printStackTrace(); + procedure.exception(graph, e.getCause()); + e.getCause().printStackTrace(); } catch (DatabaseException e) { procedure.exception(graph, e); e.printStackTrace(); diff --git a/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/StaticMethodAdapter.java b/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/StaticMethodAdapter.java index b898e511b..fa131a818 100644 --- a/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/StaticMethodAdapter.java +++ b/bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/reflection/StaticMethodAdapter.java @@ -42,8 +42,8 @@ public class StaticMethodAdapter extends AbstractReflectionAdapter { procedure.exception(g, e); e.printStackTrace(); } catch (InvocationTargetException e) { - procedure.exception(g, e); - e.printStackTrace(); + procedure.exception(g, e.getCause()); + e.getCause().printStackTrace(); } } diff --git a/bundles/org.simantics.document.base.ontology/graph.tg b/bundles/org.simantics.document.base.ontology/graph.tg index 827ef1be8..f23cc04e0 100644 Binary files a/bundles/org.simantics.document.base.ontology/graph.tg and b/bundles/org.simantics.document.base.ontology/graph.tg differ diff --git a/bundles/org.simantics.document.base.ontology/graph/Properties.pgraph b/bundles/org.simantics.document.base.ontology/graph/Properties.pgraph index 4cc93278a..0ecca5e06 100644 --- a/bundles/org.simantics.document.base.ontology/graph/Properties.pgraph +++ b/bundles/org.simantics.document.base.ontology/graph/Properties.pgraph @@ -77,6 +77,11 @@ PROPERTIES.target : PROPERTIES.ParameterType : L0.FunctionalRelation 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" diff --git a/bundles/org.simantics.document.base.ontology/src/org/simantics/document/base/ontology/DocumentationResource.java b/bundles/org.simantics.document.base.ontology/src/org/simantics/document/base/ontology/DocumentationResource.java index f93d3b99a..b76fa9f6e 100644 --- a/bundles/org.simantics.document.base.ontology/src/org/simantics/document/base/ontology/DocumentationResource.java +++ b/bundles/org.simantics.document.base.ontology/src/org/simantics/document/base/ontology/DocumentationResource.java @@ -275,6 +275,8 @@ public class DocumentationResource { public final Resource Properties_exists_Inverse; public final Resource Properties_experiment; public final Resource Properties_experiment_Inverse; + public final Resource Properties_hyperlinkTarget; + public final Resource Properties_hyperlinkTarget_Inverse; public final Resource Properties_icstate; public final Resource Properties_icstate_Inverse; public final Resource Properties_input; @@ -824,6 +826,8 @@ public class DocumentationResource { public static final String Properties_exists_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/exists/Inverse"; public static final String Properties_experiment = "http://www.simantics.org/Documentation-1.2/Properties/experiment"; public static final String Properties_experiment_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/experiment/Inverse"; + public static final String Properties_hyperlinkTarget = "http://www.simantics.org/Documentation-1.2/Properties/hyperlinkTarget"; + public static final String Properties_hyperlinkTarget_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/hyperlinkTarget/Inverse"; public static final String Properties_icstate = "http://www.simantics.org/Documentation-1.2/Properties/icstate"; public static final String Properties_icstate_Inverse = "http://www.simantics.org/Documentation-1.2/Properties/icstate/Inverse"; public static final String Properties_input = "http://www.simantics.org/Documentation-1.2/Properties/input"; @@ -1383,6 +1387,8 @@ public class DocumentationResource { Properties_exists_Inverse = getResourceOrNull(graph, URIs.Properties_exists_Inverse); Properties_experiment = getResourceOrNull(graph, URIs.Properties_experiment); Properties_experiment_Inverse = getResourceOrNull(graph, URIs.Properties_experiment_Inverse); + Properties_hyperlinkTarget = getResourceOrNull(graph, URIs.Properties_hyperlinkTarget); + Properties_hyperlinkTarget_Inverse = getResourceOrNull(graph, URIs.Properties_hyperlinkTarget_Inverse); Properties_icstate = getResourceOrNull(graph, URIs.Properties_icstate); Properties_icstate_Inverse = getResourceOrNull(graph, URIs.Properties_icstate_Inverse); Properties_input = getResourceOrNull(graph, URIs.Properties_input); diff --git a/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/wizard/ReportGeneratePage.java b/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/wizard/ReportGeneratePage.java index 12fe52333..980010318 100644 --- a/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/wizard/ReportGeneratePage.java +++ b/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/wizard/ReportGeneratePage.java @@ -157,7 +157,8 @@ public class ReportGeneratePage extends WizardPage { setErrorMessage("Report failed: " + err.getMessage()); ErrorLogger.defaultLogError("Report failed.",err); statusLabel.setText("Report failed."); - } catch (InvocationTargetException err) { + } catch (InvocationTargetException e) { + Throwable err = e.getCause(); setErrorMessage("Report failed: " + err.getMessage()); ErrorLogger.defaultLogError("Report failed.",err); statusLabel.setText("Report failed."); diff --git a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java index 9bea3d41e..794b9e55b 100644 --- a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java +++ b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java @@ -31,6 +31,10 @@ public class JSONObjectUtils { return getValueOrDefault(object, "target", ""); } + public static String getHyperLinkTarget(IJSONObject object){ + return getValueOrDefault(object, "hyperlinkTarget", ""); + } + public static String getWidth(IJSONObject object) { return getValueOrDefault(object, "width", ""); } diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java index 3892b5015..2fd422f5e 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java @@ -188,6 +188,8 @@ public class Functions { @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable") public static Variable state(ReadGraph graph, Resource converter, Variable context) throws DatabaseException { Variable session = graph.syncRequest(new ProxySessionRequest(context)); + if (session == null) + throw new DatabaseException("No state for " + context.getURI(graph)); return session.getPossibleChild(graph, "__scl__"); } diff --git a/bundles/org.simantics.fileimport.ui/.classpath b/bundles/org.simantics.fileimport.ui/.classpath new file mode 100644 index 000000000..b862a296d --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.fileimport.ui/.project b/bundles/org.simantics.fileimport.ui/.project new file mode 100644 index 000000000..7c2e36546 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/.project @@ -0,0 +1,28 @@ + + + org.simantics.fileimport.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.fileimport.ui/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.fileimport.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..295926d96 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.simantics.fileimport.ui/META-INF/MANIFEST.MF b/bundles/org.simantics.fileimport.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..94cc4b9d2 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +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 diff --git a/bundles/org.simantics.fileimport.ui/build.properties b/bundles/org.simantics.fileimport.ui/build.properties new file mode 100644 index 000000000..50e0a5d56 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/build.properties @@ -0,0 +1,6 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + fragment.e4xmi,\ + plugin.xml +source.. = src/ diff --git a/bundles/org.simantics.fileimport.ui/fragment.e4xmi b/bundles/org.simantics.fileimport.ui/fragment.e4xmi new file mode 100644 index 000000000..1ca9b3676 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/fragment.e4xmi @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/bundles/org.simantics.fileimport.ui/plugin.xml b/bundles/org.simantics.fileimport.ui/plugin.xml new file mode 100644 index 000000000..d56714ac6 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/plugin.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/bundles/org.simantics.fileimport.ui/src/org/simantics/fileimport/ui/Activator.java b/bundles/org.simantics.fileimport.ui/src/org/simantics/fileimport/ui/Activator.java new file mode 100644 index 000000000..140ee4d04 --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/src/org/simantics/fileimport/ui/Activator.java @@ -0,0 +1,50 @@ +package org.simantics.fileimport.ui; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.simantics.fileimport.ui"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/bundles/org.simantics.fileimport.ui/src/org/simantics/fileimport/ui/ImportFileHandler.java b/bundles/org.simantics.fileimport.ui/src/org/simantics/fileimport/ui/ImportFileHandler.java new file mode 100644 index 000000000..ff6ab675b --- /dev/null +++ b/bundles/org.simantics.fileimport.ui/src/org/simantics/fileimport/ui/ImportFileHandler.java @@ -0,0 +1,49 @@ + +package org.simantics.fileimport.ui; + +import java.nio.file.Paths; +import java.util.Map; +import java.util.Optional; + +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; +import org.simantics.fileimport.FileImportService; + +public class ImportFileHandler { + + @CanExecute + public boolean canExecute() { + return !FileImportService.supportedExtensionsWithFilters().isEmpty(); + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell) { + + Map extensions = FileImportService.supportedExtensionsWithFilters(); + String[] filterExtensions = (String[]) extensions.keySet().toArray(new String[extensions.keySet().size()]); + String[] filterNames = (String[]) extensions.values().toArray(new String[extensions.values().size()]); + + // Sanity check + for (int i = 0; i < filterExtensions.length; i++) { + String extension = filterExtensions[i]; + if (!extension.startsWith("*.")) { + System.err.println("Invalid extension filter provied: " + extension); + } + } + + FileDialog dialog = new FileDialog(shell, SWT.OPEN); + dialog.setText("Choose File"); + dialog.setFilterExtensions(filterExtensions); + dialog.setFilterNames(filterNames); + final String fileName = dialog.open(); + if (fileName == null) + return; + FileImportService.performFileImport(Paths.get(fileName), Optional.empty()); + } +} \ No newline at end of file diff --git a/bundles/org.simantics.fileimport/.classpath b/bundles/org.simantics.fileimport/.classpath new file mode 100644 index 000000000..b862a296d --- /dev/null +++ b/bundles/org.simantics.fileimport/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.fileimport/.project b/bundles/org.simantics.fileimport/.project new file mode 100644 index 000000000..bfb71345c --- /dev/null +++ b/bundles/org.simantics.fileimport/.project @@ -0,0 +1,33 @@ + + + org.simantics.fileimport + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.fileimport/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.fileimport/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..295926d96 --- /dev/null +++ b/bundles/org.simantics.fileimport/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF b/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF new file mode 100644 index 000000000..2c23ea6d0 --- /dev/null +++ b/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +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 diff --git a/bundles/org.simantics.fileimport/OSGI-INF/FileReferenceFileImport.xml b/bundles/org.simantics.fileimport/OSGI-INF/FileReferenceFileImport.xml new file mode 100644 index 000000000..e6f6a5617 --- /dev/null +++ b/bundles/org.simantics.fileimport/OSGI-INF/FileReferenceFileImport.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.fileimport/OSGI-INF/LibraryFolderFileImport.xml b/bundles/org.simantics.fileimport/OSGI-INF/LibraryFolderFileImport.xml new file mode 100644 index 000000000..c064c18ec --- /dev/null +++ b/bundles/org.simantics.fileimport/OSGI-INF/LibraryFolderFileImport.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.fileimport/build.properties b/bundles/org.simantics.fileimport/build.properties new file mode 100644 index 000000000..a98f3f123 --- /dev/null +++ b/bundles/org.simantics.fileimport/build.properties @@ -0,0 +1,8 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/FileReferenceFileImport.xml,\ + OSGI-INF/FileReferenceFileImport.xml,\ + OSGI-INF/LibraryFolderFileImport.xml,\ + scl/ +source.. = src/ diff --git a/bundles/org.simantics.fileimport/scl/Dropins/Core.scl b/bundles/org.simantics.fileimport/scl/Dropins/Core.scl new file mode 100644 index 000000000..410bda137 --- /dev/null +++ b/bundles/org.simantics.fileimport/scl/Dropins/Core.scl @@ -0,0 +1,11 @@ +import "MMap" as MMap + +importJava "org.simantics.fileimport.scl.DropinsSCL" where + uploadToDropinsBase64 :: String -> String -> () + getUploadedFiles :: () -> MMap.T String Long + removeFileForId :: Long -> () + +getUploadedDropinFiles :: () -> [Long] +getUploadedDropinFiles dummy = do + files = getUploadedFiles () + MMap.values files \ No newline at end of file diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/Activator.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/Activator.java new file mode 100644 index 000000000..605e50347 --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/Activator.java @@ -0,0 +1,52 @@ +package org.simantics.fileimport; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.simantics.fileimport.dropins.FileImportDropins; + +public class Activator implements BundleActivator { + + private static BundleContext context; + + private static Path dropinsFolder = null; + + static BundleContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception { + Activator.context = bundleContext; + FileImportDropins.watchDropinsFolder(); + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception { + Activator.context = null; + FileImportDropins.unwatchDropinsFolder(); + } + + public static Path getDropinsFolder() throws IOException { + if (dropinsFolder == null) { + IPath state = Platform.getStateLocation(context.getBundle()); + dropinsFolder = Paths.get(state.append("dropins").toOSString()); + if (!Files.exists(dropinsFolder)) + Files.createDirectories(dropinsFolder); + } + return dropinsFolder; + } + +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java new file mode 100644 index 000000000..e07dfaadd --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java @@ -0,0 +1,261 @@ +package org.simantics.fileimport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.function.Consumer; + +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.service.SerialisationSupport; +import org.simantics.layer0.Layer0; + +public class FileImportService { + + public static final String DB_FILE = ".simanticsdb"; + + public static List getFileImportServices() { + ServiceReference[] serviceReferences = new ServiceReference[0]; + try { + serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(), + null); + } catch (InvalidSyntaxException e) { + e.printStackTrace(); + } + if (serviceReferences.length == 0) + return Collections.emptyList(); + + List services = new ArrayList<>(serviceReferences.length); + for (ServiceReference reference : serviceReferences) { + IGenericFileImport service = (IGenericFileImport) Activator.getContext().getService(reference); + services.add(service); + } + return services; + } + + public static Map supportedExtensionsWithFilters() { + List services = getFileImportServices(); + Map extensionsWithFilters = new HashMap<>(); + for (IGenericFileImport service : services) + extensionsWithFilters.putAll(service.allowedExtensionsWithFilters()); + + return extensionsWithFilters; + } + + public static void performFileImport(Path file, Optional> callback) { + if (file.getFileName().toString().equals(DB_FILE)) + return; + Optional serviceOp = findServiceForFileExtension(file); + serviceOp.ifPresent(service -> { + try { + Optional resource = service.perform(file); + saveResourceForPath(file, resource); + } catch (Throwable t) { + if (callback.isPresent()) { + callback.get().accept(t); + } else { + t.printStackTrace(); + } + } + }); + } + + public static void removeResourceForFile(Path file, Optional> callback) { + Optional serviceOp = findServiceForFileExtension(file); + serviceOp.ifPresent(service -> { + try { + Optional resource = getResourceForPath(file); + if (!resource.isPresent()) + return; + service.remove(resource.get()); + removeResourceForPath(file); + } catch (Throwable t) { + if (callback.isPresent()) { + callback.get().accept(t); + } else { + t.printStackTrace(); + } + } + }); + } + + public static void removeFileForResource(long id, Optional> callback) { + Optional fileOp; + try { + fileOp = findPathForId(id); + } catch (IOException e) { + e.printStackTrace(); + return; + } + if (!fileOp.isPresent()) + return; + Path file = fileOp.get(); + Optional serviceOp = findServiceForFileExtension(file); + serviceOp.ifPresent(service -> { + try { + Optional resource = getResourceForPath(file); + if (!resource.isPresent()) + return; + service.remove(resource.get()); + removeResourceForPath(file); + } catch (Throwable t) { + if (callback.isPresent()) { + callback.get().accept(t); + } else { + t.printStackTrace(); + } + } + }); + } + + private static Optional findPathForId(long id) throws IOException { + Path db = Activator.getDropinsFolder().resolve(DB_FILE); + if (!Files.exists(db)) + Files.createFile(db); + Properties props = new Properties(); + try (InputStream stream = Files.newInputStream(db)) { + props.load(stream); + } + for (Map.Entry entry : props.entrySet()) { + Long value = Long.valueOf(entry.getValue().toString()); + if (value.longValue() == id) { + String key = (String) entry.getKey(); + return Optional.of(Paths.get(key)); + } + } + return Optional.empty(); + } + + static final String FOLDER = "_folder_"; + + public static Optional findServiceForFileExtension(Path file) { + String extension = ""; + + int i = file.getFileName().toString().lastIndexOf('.'); + if (i > 0) { + extension = file.getFileName().toString().substring(i); + } else { + // Handle case that file is actually a directory + if (Files.isDirectory(file) || !Files.isRegularFile(file)) { + extension = FOLDER; + } + } + + List services = getFileImportServices(); + for (IGenericFileImport service : services) { + for (Map.Entry entry : service.allowedExtensionsWithFilters().entrySet()) { + String possibleExtensions = entry.getKey(); + if (possibleExtensions.startsWith("*")) + possibleExtensions = possibleExtensions.substring(1); + if (possibleExtensions.equals(extension) || possibleExtensions.isEmpty()) { + if (extension.equals(FOLDER) && possibleExtensions.equals(FOLDER)) { + return Optional.of(service); + } else if (!extension.isEmpty() && !extension.equals(FOLDER)){ + return Optional.of(service); + } + } + } + } + return Optional.empty(); + } + + public static Map getPathsAndResources() { + try { + Path db = Activator.getDropinsFolder().resolve(DB_FILE); + if (!Files.exists(db)) + Files.createFile(db); + Properties props = new Properties(); + try (InputStream stream = Files.newInputStream(db)) { + props.load(stream); + } + Map result = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + Map map = new HashMap<>(); + for (Map.Entry entry : props.entrySet()) { + String value = (String) entry.getValue(); + Long id = Long.valueOf(value); + SerialisationSupport ss = graph.getService(SerialisationSupport.class); + try { + Resource r = ss.getResource(id); + String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName); + map.put(name, id); + } catch (DatabaseException e) { + e.printStackTrace(); + } + } + return map; + } + }); + + return result; + } catch (IOException | DatabaseException e) { + e.printStackTrace(); + return Collections.emptyMap(); + } + } + + private static void saveResourceForPath(Path file, Optional resource) { + resource.ifPresent(res -> { + try { + Path db = Activator.getDropinsFolder().resolve(DB_FILE); + if (!Files.exists(db)) + Files.createFile(db); + Properties props = new Properties(); + try (InputStream stream = Files.newInputStream(db)) { + props.load(stream); + } + props.put(file.getFileName().toString(), resource.get()); + try (OutputStream stream = Files.newOutputStream(db)) { + props.store(stream, null); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + private static void removeResourceForPath(Path file) throws IOException { + Path db = Activator.getDropinsFolder().resolve(DB_FILE); + if (!Files.exists(db)) + Files.createFile(db); + Properties props = new Properties(); + try (InputStream stream = Files.newInputStream(db)) { + props.load(stream); + } + props.remove(file.getFileName().toString()); + try (OutputStream stream = Files.newOutputStream(db)) { + props.store(stream, null); + } + } + + private static Optional getResourceForPath(Path file) throws IOException { + Path db = Activator.getDropinsFolder().resolve(DB_FILE); + if (!Files.exists(db)) + Files.createFile(db); + Properties props = new Properties(); + try (InputStream stream = Files.newInputStream(db)) { + props.load(stream); + } + String value = props.getProperty(file.getFileName().toString()); + if (value == null) + return Optional.empty(); + return Optional.of(value); + } +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileReferenceFileImport.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileReferenceFileImport.java new file mode 100644 index 000000000..5ae271c2c --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileReferenceFileImport.java @@ -0,0 +1,31 @@ +package org.simantics.fileimport; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.graphfile.util.GraphFileUtil; + +public class FileReferenceFileImport extends SimanticsResourceFileImport { + + private static final Map ALLOWED_EXTENSIONS = Collections.singletonMap("*.asd", "All files"); + + @Override + public Optional perform(Resource parent, Path file) { + try { + return Optional.of(GraphFileUtil.createFileReference(parent, file)); + } catch (DatabaseException e) { + e.printStackTrace(); + return Optional.empty(); + } + } + + @Override + public Map allowedExtensionsWithFilters() { + return ALLOWED_EXTENSIONS; + } + +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java new file mode 100644 index 000000000..2de1cc742 --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java @@ -0,0 +1,33 @@ +package org.simantics.fileimport; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +/** + * @author Jani Simomaa + * + */ +public interface IGenericFileImport { + + /** + * Performs the import procedure for the given file + * + * @param file + * file to import + */ + Optional perform(Path file) throws Exception; + + /** + * @param resource + */ + void remove(String resource) throws Exception; + + /** + * Returns a key-value map for file extensions this importer can handle + * + * @return + */ + Map allowedExtensionsWithFilters(); + +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/LibraryFolderFileImport.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/LibraryFolderFileImport.java new file mode 100644 index 000000000..b688895e3 --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/LibraryFolderFileImport.java @@ -0,0 +1,40 @@ +package org.simantics.fileimport; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +import org.simantics.Simantics; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteResultRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.modeling.ModelingUtils; + +public class LibraryFolderFileImport extends SimanticsResourceFileImport { + + private static final Map ALLOWED_EXTENSIONS = Collections.singletonMap(FileImportService.FOLDER, FileImportService.FOLDER); + + @Override + public Map allowedExtensionsWithFilters() { + return ALLOWED_EXTENSIONS; + } + + @Override + public Optional perform(Resource parent, Path file) { + final String name = file.getFileName().toString(); + try { + return Optional.of(Simantics.getSession().syncRequest(new WriteResultRequest() { + + @Override + public Resource perform(WriteGraph graph) throws DatabaseException { + return ModelingUtils.createLibrary(graph, parent, name); + } + })); + } catch (DatabaseException e) { + e.printStackTrace(); + return Optional.empty(); + } + } +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java new file mode 100644 index 000000000..2836b670e --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java @@ -0,0 +1,111 @@ +package org.simantics.fileimport; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Optional; + +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.RuntimeDatabaseException; +import org.simantics.db.layer0.util.RemoverUtil; +import org.simantics.db.request.Read; +import org.simantics.db.service.SerialisationSupport; +import org.simantics.layer0.Layer0; + +public abstract class SimanticsResourceFileImport implements IGenericFileImport { + + @Override + final public Optional perform(Path file) throws Exception { + + Path dropins = Activator.getDropinsFolder(); + Path parts = dropins.relativize(file); + Resource parent = resolveParent(null, parts); + if (parent == null) + return Optional.empty(); + Optional imported = perform(parent, file); + if (imported.isPresent()) { + return Optional.of(serialize(imported.get())); + } else { + return Optional.empty(); + } + } + + public abstract Optional perform(Resource parent, Path file); + + @Override + public void remove(String resourceId) throws Exception { + Optional resource = deserialize(resourceId); + resource.ifPresent(res -> { + try { + Simantics.sync(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + RemoverUtil.remove(graph, resource.get()); + } + }); + } catch (Exception e) { + throw new RuntimeDatabaseException(e); + } + }); + } + + public String serialize(Resource resource) { + return Long.toString(resource.getResourceId()); + } + + public Optional deserialize(String serialized) throws Exception { + long resourceId = Long.valueOf(serialized); + + Resource resource = Simantics.getSession().syncRequest(new Read() { + + @Override + public Resource perform(ReadGraph graph) throws DatabaseException { + SerialisationSupport support = graph.getService(SerialisationSupport.class); + Resource resource = support.getResource(resourceId); + return resource; + } + }); + return Optional.ofNullable(resource); + } + + private static Resource resolveParent(Resource parent, Path name) { + if (name.getParent() == null) { + return Simantics.getProjectResource(); + } else { + name = name.getParent(); + parent = resolveParent(parent, name); + } + final Resource newParent = parent; + final String folderName = name.getFileName().toString(); + + try { + return Simantics.getSession().syncRequest(new UniqueRead() { + + @Override + public Resource perform(ReadGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Collection libraries = graph.sync(new ObjectsWithType(newParent, L0.ConsistsOf, L0.Library)); + for (Resource library : libraries) { + String libraryName = graph.getRelatedValue2(library, L0.HasName, Bindings.STRING); + if (libraryName.equals(folderName)) { + return library; + } + } + return null; + } + }); + } catch (DatabaseException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java new file mode 100644 index 000000000..a32dd9877 --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java @@ -0,0 +1,140 @@ +package org.simantics.fileimport.dropins; + +import static java.nio.file.StandardWatchEventKinds.OVERFLOW; +import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.WatchEvent; +import java.nio.file.WatchEvent.Kind; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; + +import org.simantics.fileimport.Activator; +import org.simantics.fileimport.FileImportService; + +public class FileImportDropins { + + private static Thread watcherThread = null; + private static DropinsFolderWatcher watcher = null; + + public static void watchDropinsFolder() { + if (watcher == null && watcherThread == null) { + try { + watcher = new DropinsFolderWatcher(Activator.getDropinsFolder()); + watcherThread = new Thread(watcher, "Simantics Dropins Folder watcher thread"); + watcherThread.setDaemon(true); + watcherThread.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static void unwatchDropinsFolder() { + watcher.stop(); + try { + watcherThread.join(500); + if (watcherThread.isAlive()) + watcherThread.interrupt(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + watcherThread = null; + watcher = null; + } + + private static class DropinsFolderWatcher implements Runnable { + + private final Path dropinsFolder; + private final WatchService ws; + private final AtomicBoolean stopped = new AtomicBoolean(true); + + private final Map keys = new HashMap<>(); + + public DropinsFolderWatcher(Path dropinsFolder) throws IOException { + this.dropinsFolder = dropinsFolder; + FileSystem fs = dropinsFolder.getFileSystem(); + this.ws = fs.newWatchService(); + registerAll(this.dropinsFolder); + } + + @Override + public void run() { + stopped.set(false); + + while (!stopped.get()) { + try { + WatchKey key = ws.take(); + for (WatchEvent watchEvent : key.pollEvents()) { + if (OVERFLOW == watchEvent.kind()) + continue; // loop + + @SuppressWarnings("unchecked") + WatchEvent pathEvent = (WatchEvent) watchEvent; + Kind kind = pathEvent.kind(); + + Path parent = keys.get(key); + Path newPath = parent.resolve(pathEvent.context()); + if (FileImportService.DB_FILE.equals(newPath.getFileName().toString())) + continue; + if (ENTRY_CREATE == kind) { + System.out.println("New path created: " + newPath); + FileImportService.performFileImport(newPath, Optional.empty()); + register(newPath); + } else if (ENTRY_MODIFY == kind) { + System.out.println("New path modified: " + newPath); + } else if (ENTRY_DELETE == kind) { + System.out.println("New path deleted: " + newPath); + FileImportService.removeResourceForFile(newPath.toAbsolutePath(), Optional.empty()); + } + } + if (!key.reset()) { + keys.remove(key); +// break; // loop + } + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + if (!stopped.get()) + e.printStackTrace(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + + public void stop() { + stopped.set(true); + } + + private void registerAll(Path path) throws IOException { + Files.walkFileTree(path, new SimpleFileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) throws IOException { + register(file); + return FileVisitResult.CONTINUE; + } + }); + } + + private void register(Path path) throws IOException { + if (Files.isDirectory(path)) { + WatchKey key = path.toAbsolutePath().register(ws, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); + keys.put(key, path); + } + } + } +} diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java new file mode 100644 index 000000000..0758d3e78 --- /dev/null +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java @@ -0,0 +1,37 @@ +package org.simantics.fileimport.scl; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +import org.simantics.databoard.util.Base64; +import org.simantics.fileimport.Activator; +import org.simantics.fileimport.FileImportService; +import org.simantics.fileimport.dropins.FileImportDropins; +import org.simantics.utils.FileUtils; + +public class DropinsSCL { + + public static void uploadToDropinsBase64(String base64, String fileName) { + // ensure that watcher is awake + FileImportDropins.watchDropinsFolder(); + try { + Path rootFolder = Activator.getDropinsFolder(); + byte[] bytes = Base64.decode(base64); + FileUtils.writeFile(rootFolder.resolve(fileName).toFile(), bytes); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static Map getUploadedFiles() { + return FileImportService.getPathsAndResources(); + } + + public static void removeFileForId(long id) { + FileImportService.removeFileForResource(id, Optional.empty()); + } + +} diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/HintReflection.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/HintReflection.java index 3edd342b7..2f701cc57 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/HintReflection.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/HintReflection.java @@ -162,7 +162,7 @@ public class HintReflection { } catch (IllegalAccessException e) { throw new Error(e); } catch (InvocationTargetException e) { - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } @Override diff --git a/bundles/org.simantics.graphfile.ontology/graph.tg b/bundles/org.simantics.graphfile.ontology/graph.tg index 583f447be..67308b2c6 100644 Binary files a/bundles/org.simantics.graphfile.ontology/graph.tg and b/bundles/org.simantics.graphfile.ontology/graph.tg differ diff --git a/bundles/org.simantics.graphfile.ontology/graph/graphfile.pgraph b/bundles/org.simantics.graphfile.ontology/graph/graphfile.pgraph index bb11f0e12..6387523aa 100644 --- a/bundles/org.simantics.graphfile.ontology/graph/graphfile.pgraph +++ b/bundles/org.simantics.graphfile.ontology/graph/graphfile.pgraph @@ -35,4 +35,8 @@ GF.HasFile () { + + @Override + public Resource perform(WriteGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + GraphFileResource GF = GraphFileResource.getInstance(graph); + Resource file = graph.newResource(); + graph.claim(file, L0.PartOf, parent); + graph.claim(file, L0.InstanceOf, GF.File); + String name = path.getFileName().toString(); + graph.claimLiteral(file, L0.HasName, name, Bindings.STRING); + graph.claimLiteral(file, GF.SystemPath, path.toAbsolutePath().toString(), Bindings.STRING); + return file; + } + }); + } } diff --git a/bundles/org.simantics.graphviz.ui/src/org/simantics/graphviz/ui/GraphvizComponent.java b/bundles/org.simantics.graphviz.ui/src/org/simantics/graphviz/ui/GraphvizComponent.java index 93338d60c..f46194c3a 100644 --- a/bundles/org.simantics.graphviz.ui/src/org/simantics/graphviz/ui/GraphvizComponent.java +++ b/bundles/org.simantics.graphviz.ui/src/org/simantics/graphviz/ui/GraphvizComponent.java @@ -120,7 +120,7 @@ public class GraphvizComponent extends Composite { e.printStackTrace(); //Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e)); } catch (InvocationTargetException e) { - e.printStackTrace(); + e.getCause().printStackTrace(); //Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e)); } } diff --git a/bundles/org.simantics.issues.ui/src/org/simantics/issues/ui/handler/PurgeResolvedIssues.java b/bundles/org.simantics.issues.ui/src/org/simantics/issues/ui/handler/PurgeResolvedIssues.java index 4bd4f608f..4a3303c4c 100644 --- a/bundles/org.simantics.issues.ui/src/org/simantics/issues/ui/handler/PurgeResolvedIssues.java +++ b/bundles/org.simantics.issues.ui/src/org/simantics/issues/ui/handler/PurgeResolvedIssues.java @@ -58,7 +58,7 @@ public class PurgeResolvedIssues extends AbstractHandler { } }); } catch (InvocationTargetException e) { - ErrorLogger.defaultLogError(e); + ErrorLogger.defaultLogError(e.getCause()); } catch (InterruptedException e) { ErrorLogger.defaultLogError(e); } diff --git a/bundles/org.simantics.modeling.ontology/graph.tg b/bundles/org.simantics.modeling.ontology/graph.tg index d2cedf896..011801300 100644 Binary files a/bundles/org.simantics.modeling.ontology/graph.tg and b/bundles/org.simantics.modeling.ontology/graph.tg differ diff --git a/bundles/org.simantics.modeling.ontology/graph/ModelingViewpoint.pgraph b/bundles/org.simantics.modeling.ontology/graph/ModelingViewpoint.pgraph index e4f42f4af..75d2fd3fd 100644 --- a/bundles/org.simantics.modeling.ontology/graph/ModelingViewpoint.pgraph +++ b/bundles/org.simantics.modeling.ontology/graph/ModelingViewpoint.pgraph @@ -274,6 +274,13 @@ MBC VP.VisualsContribution.HasNodeType MBC.Variable VP.VisualsContribution.HasRule MBC.VariableLabelRule + +// Tooltips +MBC + VP.BrowseContext.HasVisualsContribution MOD.Contributions.VariableTooltip : VP.VisualsContribution + VP.VisualsContribution.HasNodeType MBC.Variable + VP.VisualsContribution.HasRule VP.DescriptionTooltipRule + // Images MBC @VP.namedCustomImageRule MOD.Contributions.SubscriptionImage MOD.Subscription MBC.SubscriptionImageRule diff --git a/bundles/org.simantics.modeling.ontology/src/org/simantics/modeling/ModelingResources.java b/bundles/org.simantics.modeling.ontology/src/org/simantics/modeling/ModelingResources.java index 640214fdb..5d42efbab 100644 --- a/bundles/org.simantics.modeling.ontology/src/org/simantics/modeling/ModelingResources.java +++ b/bundles/org.simantics.modeling.ontology/src/org/simantics/modeling/ModelingResources.java @@ -127,6 +127,7 @@ public class ModelingResources { public final Resource Contributions_VariableChildren; public final Resource Contributions_VariableImage; public final Resource Contributions_VariableLabel; + public final Resource Contributions_VariableTooltip; public final Resource DefaultStructuralActionContext; public final Resource DefaultStructuralBrowseContext; public final Resource DefaultStructuralImageContext; @@ -549,6 +550,7 @@ public class ModelingResources { public static final String Contributions_VariableChildren = "http://www.simantics.org/Modeling-1.2/Contributions/VariableChildren"; public static final String Contributions_VariableImage = "http://www.simantics.org/Modeling-1.2/Contributions/VariableImage"; public static final String Contributions_VariableLabel = "http://www.simantics.org/Modeling-1.2/Contributions/VariableLabel"; + public static final String Contributions_VariableTooltip = "http://www.simantics.org/Modeling-1.2/Contributions/VariableTooltip"; public static final String DefaultStructuralActionContext = "http://www.simantics.org/Modeling-1.2/DefaultStructuralActionContext"; public static final String DefaultStructuralBrowseContext = "http://www.simantics.org/Modeling-1.2/DefaultStructuralBrowseContext"; public static final String DefaultStructuralImageContext = "http://www.simantics.org/Modeling-1.2/DefaultStructuralImageContext"; @@ -981,6 +983,7 @@ public class ModelingResources { Contributions_VariableChildren = getResourceOrNull(graph, URIs.Contributions_VariableChildren); Contributions_VariableImage = getResourceOrNull(graph, URIs.Contributions_VariableImage); Contributions_VariableLabel = getResourceOrNull(graph, URIs.Contributions_VariableLabel); + Contributions_VariableTooltip = getResourceOrNull(graph, URIs.Contributions_VariableTooltip); DefaultStructuralActionContext = getResourceOrNull(graph, URIs.DefaultStructuralActionContext); DefaultStructuralBrowseContext = getResourceOrNull(graph, URIs.DefaultStructuralBrowseContext); DefaultStructuralImageContext = getResourceOrNull(graph, URIs.DefaultStructuralImageContext); diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/StandardPasteHandler.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/StandardPasteHandler.java index f86c3301c..51140988b 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/StandardPasteHandler.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/StandardPasteHandler.java @@ -98,8 +98,7 @@ public class StandardPasteHandler extends AbstractHandler implements IHandler { try { throw new InvocationTargetException(e); } catch (InvocationTargetException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); + e1.getCause().printStackTrace(); } e.printStackTrace(); } diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/NewMasterTypicalDiagram.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/NewMasterTypicalDiagram.java index e6dc21c7e..86926145c 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/NewMasterTypicalDiagram.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/NewMasterTypicalDiagram.java @@ -74,7 +74,7 @@ public class NewMasterTypicalDiagram implements ActionFactory { } }); } catch (InvocationTargetException e) { - ErrorLogger.defaultLogError(e); + ErrorLogger.defaultLogError(e.getCause()); } catch (InterruptedException e) { ErrorLogger.defaultLogError(e); } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/ModelingUtils.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/ModelingUtils.java index 627f41780..e1494cf32 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/ModelingUtils.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/ModelingUtils.java @@ -2322,5 +2322,25 @@ public class ModelingUtils { return new File(fileName); } + + public static Resource createLibrary(WriteGraph graph, Resource parent) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(graph); + return createLibrary(graph, parent, NameUtils.findFreshName(graph, "Library", parent, l0.ConsistsOf)); + } + + public static Resource createLibrary(WriteGraph graph, Resource parent, String name) throws DatabaseException { + graph.markUndoPoint(); + Layer0 l0 = Layer0.getInstance(graph); + + Resource library = graph.newResource(); + graph.claim(library, l0.InstanceOf, null, l0.Library); + graph.addLiteral(library, l0.HasName, l0.NameOf, l0.String, name, Bindings.STRING); + graph.claim(library, l0.PartOf, parent); + + Layer0Utils.addCommentMetadata(graph, "Created new Library named " + name + ", resource " + library); + + return library; + } + } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java index cfc3f4f15..a61563411 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java @@ -1,5 +1,7 @@ package org.simantics.modeling.scl; +import java.util.Collection; + import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessorSpecific; @@ -17,6 +19,7 @@ import org.simantics.scl.compiler.source.ModuleSource; import org.simantics.scl.compiler.source.StringModuleSource; import org.simantics.scl.compiler.source.repository.ModuleSourceRepository; import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.tuple.Tuple0; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; @@ -167,6 +170,13 @@ public enum GraphModuleSourceRepository implements ModuleSourceRepository { result.add(graph.getURI(module)); } } + + Collection ontologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE); + for (Resource ontology : ontologies) { + for(Resource module : ModelingUtils.searchByType(graph, ontology, L0.SCLModule)) + result.add(graph.getURI(module)); + } + return result; } }); diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/OntologyModule.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/OntologyModule.java index bede92d36..736a88fb3 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/OntologyModule.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/OntologyModule.java @@ -307,4 +307,9 @@ public class OntologyModule extends LazyModule { childMaps = null; ontology = null; } + + @Override + public String toString() { + return new StringBuilder().append("OntologyModule ").append(getName()).toString(); + } } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/symbolEditor/PopulateTerminal.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/symbolEditor/PopulateTerminal.java index ddcb7ff67..b028ec2d2 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/symbolEditor/PopulateTerminal.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/symbolEditor/PopulateTerminal.java @@ -52,8 +52,15 @@ public class PopulateTerminal { if(name != null) g.claimLiteral(terminalRelation, L0.HasName, name); - for(Resource type : g.getObjects(relation, MOD.ImpliesDiagramConnectionRelationType)) - g.claim(terminalRelation, L0.InstanceOf, type); + boolean interfaceGeneratesComponentExternally = !g.hasStatement(relation, MOD.GeneratesConnectionComponentInternally); + for(Resource type : g.getObjects(relation, MOD.ImpliesDiagramConnectionRelationType)) { + // #6636: Only instantiate type if it does not generate a component + // when interface is marked to generate component internally. + boolean shouldInstantiate = interfaceGeneratesComponentExternally || + g.getAssertedObjects(type, MOD.DiagramConnectionRelationToComponentType).isEmpty(); + if (shouldInstantiate) + g.claim(terminalRelation, L0.InstanceOf, type); + } StructuralUtils.addConnectionPoint(g, definedElement, terminalRelation); } diff --git a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CollectionAccessor.java b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CollectionAccessor.java index c3569ed42..d2b9f8377 100644 --- a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CollectionAccessor.java +++ b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CollectionAccessor.java @@ -51,7 +51,7 @@ public class CollectionAccessor implements IRangeAccessor implements IRangeAccessor 0 || adding.size() > 0; diff --git a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CompoundGetSetValueAccessor.java b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CompoundGetSetValueAccessor.java index 126f1d042..7dac5f7f0 100644 --- a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CompoundGetSetValueAccessor.java +++ b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/CompoundGetSetValueAccessor.java @@ -47,7 +47,7 @@ public class CompoundGetSetValueAccessor implements IRangeAccessor implements IRangeAccessor implements IRangeAccessor { } catch (IllegalAccessException e) { throw new MappingException(e); } catch (InvocationTargetException e) { - throw new MappingException(e); + throw new MappingException(e.getCause()); } }; @@ -61,7 +61,7 @@ public class GetSetObjectAccessor implements IRangeAccessor { } catch (IllegalAccessException e) { throw new MappingException(e); } catch (InvocationTargetException e) { - throw new MappingException(e); + throw new MappingException(e.getCause()); } return true; diff --git a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/GetSetValueAccessor.java b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/GetSetValueAccessor.java index 6100c9605..9f17e7811 100644 --- a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/GetSetValueAccessor.java +++ b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/GetSetValueAccessor.java @@ -47,7 +47,7 @@ public class GetSetValueAccessor implements IRangeAccessor { } catch (IllegalAccessException e) { throw new MappingException(e); } catch (InvocationTargetException e) { - throw new MappingException(e); + throw new MappingException(e.getCause()); } }; @@ -65,7 +65,7 @@ public class GetSetValueAccessor implements IRangeAccessor { } catch (IllegalAccessException e) { throw new MappingException(e); } catch (InvocationTargetException e) { - throw new MappingException(e); + throw new MappingException(e.getCause()); } return true; diff --git a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/ListAccessor.java b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/ListAccessor.java index 5f79c5856..a384235be 100644 --- a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/ListAccessor.java +++ b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/rules/range/ListAccessor.java @@ -52,7 +52,7 @@ public class ListAccessor implements IRangeAccessor implements IRangeAccessor 0 || adding.size() > 0; diff --git a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/schema/DynamicSimpleLinkType.java b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/schema/DynamicSimpleLinkType.java index bc4362888..0abd423c9 100644 --- a/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/schema/DynamicSimpleLinkType.java +++ b/bundles/org.simantics.objmap2/src/org/simantics/objmap/graph/schema/DynamicSimpleLinkType.java @@ -86,7 +86,7 @@ public class DynamicSimpleLinkType extends SimpleLinkType{ } catch (IllegalAccessException e) { throw new MappingException(e); } catch (InvocationTargetException e) { - throw new MappingException(e); + throw new MappingException(e.getCause()); } } @@ -117,7 +117,7 @@ public class DynamicSimpleLinkType extends SimpleLinkType{ } catch (IllegalArgumentException e) { throw new MappingException(e); } catch (InvocationTargetException e) { - throw new MappingException(e); + throw new MappingException(e.getCause()); } } diff --git a/bundles/org.simantics.project/src/org/simantics/project/management/ServerManagerFactory.java b/bundles/org.simantics.project/src/org/simantics/project/management/ServerManagerFactory.java index 0cf2aa045..3bde5dc42 100644 --- a/bundles/org.simantics.project/src/org/simantics/project/management/ServerManagerFactory.java +++ b/bundles/org.simantics.project/src/org/simantics/project/management/ServerManagerFactory.java @@ -1,208 +1,208 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management - * in Industry THTH ry. - * 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: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.project.management; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLDecoder; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import org.apache.log4j.Logger; -import org.simantics.db.DatabaseUserAgent; -import org.simantics.db.Driver; -import org.simantics.db.Manager; -import org.simantics.db.exception.DatabaseException; -import org.simantics.utils.FileUtils; - -public class ServerManagerFactory { - public static ServerManager create(String databaseId, String address) throws IOException, DatabaseException { - Driver driver = Manager.getDriver(databaseId); - if (driver == null) - throw new DatabaseException("Driver for databaseId=" + databaseId + " not found!"); - System.out.println("ServerManagerFactory.create called with databaseId=" + databaseId + " and driver is " + driver.toString()); - DatabaseUserAgent agent = Manager.getUserAgent(databaseId); - if (agent != null) - driver.setDatabaseUserAgent(address, agent); - return new ServerManager(driver); - } - /** - * Create a server manager in OSGi platform. - * - * @return - * @throws IOException - * @throws ClassNotFoundException - * @throws RuntimeException unexpected problem with OSGi environment - */ - public static ServerManager createOSGI() throws IOException, RuntimeException { - try { - ClassLoader cl = ServerManager.class.getClassLoader(); - - // Object bundle = Platform.getBundle("org.simantics.db.build"); - Class Platform_class = cl.loadClass("org.eclipse.core.runtime.Platform"); - Method Platform_getBundle = Platform_class.getMethod("getBundle", String.class); - Object bundle = Platform_getBundle.invoke(null, "org.simantics.db.build"); - if (bundle==null) throw new RuntimeException("Bundle org.simantics.db.build not found."); - - // URL db_build_url = bundle.getEntry("/"); - Class Bundle_class = cl.loadClass("org.osgi.framework.Bundle"); - Method Bundle_getEntry = Bundle_class.getMethod("getEntry", String.class); - URL db_build_url = (URL) Bundle_getEntry.invoke(bundle, "/"); - - // URL db_build_file_url = FileLocator.toFileURL( db_build_url ); - Class FileLocator_class = cl.loadClass("org.eclipse.core.runtime.FileLocator"); - Method FileLocator_toFileURL = FileLocator_class.getMethod("toFileURL", URL.class); - URL db_build_file_url = (URL) FileLocator_toFileURL.invoke(null, db_build_url); - - @SuppressWarnings("unused") - String buildFile = URLDecoder.decode(db_build_file_url.getPath(), "UTF-8"); - -// File db_build_file = new File( buildFile ); - //return new ServerManager( db_build_file ); - throw new RuntimeException("ServerManager.createOSGI not implemented."); - } catch( ClassNotFoundException e ) { - throw new RuntimeException(e); - } catch (SecurityException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * Create a server manager in an POJO environment. - * - * @return - * @throws IOException - */ - public static ServerManager createPOJO() throws IOException { - String tempPath = System.getenv("tmp"); - if (tempPath==null) tempPath = "c:/temp/"; - File driverDir = new File(tempPath+"/core_drivers"); - if (!driverDir.exists()) { - System.out.println("Extracting Core drivers to "+driverDir); - driverDir.mkdirs(); - ServerManagerFactory.extractDrivers(driverDir); - } else { - System.out.println("Loading Core drivers from "+driverDir); - } - //return new ServerManager(driverDir); - throw new RuntimeException("ServerManager.createPOJO not implemented."); - } - - public static ServerManager createPOJO(File driverDir) throws IOException { - //return new ServerManager(driverDir); - throw new RuntimeException("ServerManager.createPOJO not implemented."); - } - - static ServerManager cached; - - public static boolean isOSGi() { - try { - ServerManager.class.getClassLoader().loadClass("org.osgi.framework.Bundle"); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - - public synchronized static ServerManager getServerManager() { - if (cached!=null) return cached; - try { - try { - return createOSGI(); - } catch(RuntimeException e) { - return createPOJO(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - /** - * Extracts drivers files to a location. - * - * @param path location where to extract application files - * @throws IOException - */ - public static void extractDrivers(File path) - throws IOException - { - // Extract org.simantics.db.build.zip to workspace - InputStream is = ServerManager.class.getResource("org.simantics.db.build.zip").openStream(); - try { - extractZip(is, path); - } finally { - is.close(); - } - } - - /** - * Extract a zip file into a directory - * - * @param zipInput - * @param dst directory - * @throws IOException - */ - private static void extractZip(InputStream zipInput, File dst) throws IOException { - Logger myLogger = Logger.getLogger(FileUtils.class); - byte[] buf = new byte[8192]; - ZipInputStream zis = new ZipInputStream(zipInput); - ZipEntry entry; - - entry = zis.getNextEntry(); - while (entry != null) { - // for each entry to be extracted - String name = entry.getName(); - myLogger.debug("Extracting "+name); - File file = new File(dst, name); - - if (entry.isDirectory()) - { - if ( !file.exists() ) file.mkdirs(); - } else { - File parent = file.getParentFile(); - if (!parent.exists()) parent.mkdirs(); - if (!file.exists()) file.createNewFile(); - - FileOutputStream fileoutputstream = new FileOutputStream(file); - try { - int n = 0; - while ((n = zis.read(buf, 0, buf.length)) > -1) - fileoutputstream.write(buf, 0, n); - } finally { - fileoutputstream.close(); - } - } - - zis.closeEntry(); - entry = zis.getNextEntry(); - }// while - - zis.close(); - } - - -} - +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.project.management; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLDecoder; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.log4j.Logger; +import org.simantics.db.DatabaseUserAgent; +import org.simantics.db.Driver; +import org.simantics.db.Manager; +import org.simantics.db.exception.DatabaseException; +import org.simantics.utils.FileUtils; + +public class ServerManagerFactory { + public static ServerManager create(String databaseId, String address) throws IOException, DatabaseException { + Driver driver = Manager.getDriver(databaseId); + if (driver == null) + throw new IllegalArgumentException("Database driver for ID " + databaseId + " Could not be found!"); + System.out.println("ServerManagerFactory.create called with databaseId=" + databaseId + " and driver is " + driver.toString()); + DatabaseUserAgent agent = Manager.getUserAgent(databaseId); + if (agent != null) + driver.setDatabaseUserAgent(address, agent); + return new ServerManager(driver); + } + /** + * Create a server manager in OSGi platform. + * + * @return + * @throws IOException + * @throws ClassNotFoundException + * @throws RuntimeException unexpected problem with OSGi environment + */ + public static ServerManager createOSGI() throws IOException, RuntimeException { + try { + ClassLoader cl = ServerManager.class.getClassLoader(); + + // Object bundle = Platform.getBundle("org.simantics.db.build"); + Class Platform_class = cl.loadClass("org.eclipse.core.runtime.Platform"); + Method Platform_getBundle = Platform_class.getMethod("getBundle", String.class); + Object bundle = Platform_getBundle.invoke(null, "org.simantics.db.build"); + if (bundle==null) throw new RuntimeException("Bundle org.simantics.db.build not found."); + + // URL db_build_url = bundle.getEntry("/"); + Class Bundle_class = cl.loadClass("org.osgi.framework.Bundle"); + Method Bundle_getEntry = Bundle_class.getMethod("getEntry", String.class); + URL db_build_url = (URL) Bundle_getEntry.invoke(bundle, "/"); + + // URL db_build_file_url = FileLocator.toFileURL( db_build_url ); + Class FileLocator_class = cl.loadClass("org.eclipse.core.runtime.FileLocator"); + Method FileLocator_toFileURL = FileLocator_class.getMethod("toFileURL", URL.class); + URL db_build_file_url = (URL) FileLocator_toFileURL.invoke(null, db_build_url); + + @SuppressWarnings("unused") + String buildFile = URLDecoder.decode(db_build_file_url.getPath(), "UTF-8"); + +// File db_build_file = new File( buildFile ); + //return new ServerManager( db_build_file ); + throw new RuntimeException("ServerManager.createOSGI not implemented."); + } catch( ClassNotFoundException e ) { + throw new RuntimeException(e); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); + } + } + + /** + * Create a server manager in an POJO environment. + * + * @return + * @throws IOException + */ + public static ServerManager createPOJO() throws IOException { + String tempPath = System.getenv("tmp"); + if (tempPath==null) tempPath = "c:/temp/"; + File driverDir = new File(tempPath+"/core_drivers"); + if (!driverDir.exists()) { + System.out.println("Extracting Core drivers to "+driverDir); + driverDir.mkdirs(); + ServerManagerFactory.extractDrivers(driverDir); + } else { + System.out.println("Loading Core drivers from "+driverDir); + } + //return new ServerManager(driverDir); + throw new RuntimeException("ServerManager.createPOJO not implemented."); + } + + public static ServerManager createPOJO(File driverDir) throws IOException { + //return new ServerManager(driverDir); + throw new RuntimeException("ServerManager.createPOJO not implemented."); + } + + static ServerManager cached; + + public static boolean isOSGi() { + try { + ServerManager.class.getClassLoader().loadClass("org.osgi.framework.Bundle"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + public synchronized static ServerManager getServerManager() { + if (cached!=null) return cached; + try { + try { + return createOSGI(); + } catch(RuntimeException e) { + return createPOJO(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + /** + * Extracts drivers files to a location. + * + * @param path location where to extract application files + * @throws IOException + */ + public static void extractDrivers(File path) + throws IOException + { + // Extract org.simantics.db.build.zip to workspace + InputStream is = ServerManager.class.getResource("org.simantics.db.build.zip").openStream(); + try { + extractZip(is, path); + } finally { + is.close(); + } + } + + /** + * Extract a zip file into a directory + * + * @param zipInput + * @param dst directory + * @throws IOException + */ + private static void extractZip(InputStream zipInput, File dst) throws IOException { + Logger myLogger = Logger.getLogger(FileUtils.class); + byte[] buf = new byte[8192]; + ZipInputStream zis = new ZipInputStream(zipInput); + ZipEntry entry; + + entry = zis.getNextEntry(); + while (entry != null) { + // for each entry to be extracted + String name = entry.getName(); + myLogger.debug("Extracting "+name); + File file = new File(dst, name); + + if (entry.isDirectory()) + { + if ( !file.exists() ) file.mkdirs(); + } else { + File parent = file.getParentFile(); + if (!parent.exists()) parent.mkdirs(); + if (!file.exists()) file.createNewFile(); + + FileOutputStream fileoutputstream = new FileOutputStream(file); + try { + int n = 0; + while ((n = zis.read(buf, 0, buf.length)) > -1) + fileoutputstream.write(buf, 0, n); + } finally { + fileoutputstream.close(); + } + } + + zis.closeEntry(); + entry = zis.getNextEntry(); + }// while + + zis.close(); + } + + +} + diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/EventHandlerReflection.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/EventHandlerReflection.java index c3c51eac1..c3bbd4676 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/EventHandlerReflection.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/EventHandlerReflection.java @@ -104,7 +104,7 @@ public class EventHandlerReflection { } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } }; diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/NodeUtil.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/NodeUtil.java index 7f777d8bb..fb2f485f6 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/NodeUtil.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/NodeUtil.java @@ -545,7 +545,7 @@ public final class NodeUtil { e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block - e.printStackTrace(); + e.getCause().printStackTrace(); } } else { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java index bb98f1aa5..98666c11a 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java @@ -33,6 +33,13 @@ public class NamespaceImpl implements Namespace { 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 namespaceMap, diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/CompilationError.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/CompilationError.java index 499fe2bf0..5f2aff156 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/CompilationError.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/CompilationError.java @@ -72,4 +72,9 @@ public class CompilationError implements Comparable { return 1; return description.compareTo(o.description); } + + @Override + public String toString() { + return new StringBuilder().append("CompilationError: \"").append(description).append("\" at location ").append(location).toString(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/ErrorLog.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/ErrorLog.java index dbd2c3fe9..13e80fc6d 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/ErrorLog.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/errors/ErrorLog.java @@ -56,4 +56,9 @@ public class ErrorLog { b.append(error.description).append('\n'); return b.toString(); } + + @Override + public String toString() { + return getErrorsAsString(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironment.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironment.java index 72be89eac..6f31eecaf 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironment.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironment.java @@ -1,9 +1,12 @@ 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 getRuntimeModules(); MutableClassLoader getMutableClassLoader(); } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironmentImpl.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironmentImpl.java index 8d7080394..2652423c8 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironmentImpl.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeEnvironmentImpl.java @@ -1,5 +1,7 @@ package org.simantics.scl.compiler.runtime; +import java.util.Collection; + import org.simantics.scl.compiler.environment.Environment; import gnu.trove.map.hash.THashMap; @@ -37,4 +39,9 @@ public class RuntimeEnvironmentImpl implements RuntimeEnvironment { return classLoader; } + @Override + public Collection getRuntimeModules() { + return runtimeModuleMap.values(); + } + } diff --git a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction.java b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction.java index 0b5760a26..5472ad386 100755 --- a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction.java +++ b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction.java @@ -26,8 +26,7 @@ public class ClassMethodFunction extends FunctionImplN { } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - //e.printStackTrace(); - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } diff --git a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction3.java b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction3.java index 17dafa3ec..fcad12f1b 100644 --- a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction3.java +++ b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ClassMethodFunction3.java @@ -4,7 +4,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.simantics.scl.runtime.function.FunctionImpl3; -import org.simantics.scl.runtime.function.FunctionImplN; public class ClassMethodFunction3 extends FunctionImpl3 { Method method; @@ -26,8 +25,7 @@ public class ClassMethodFunction3 extends FunctionImpl3 { } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - //e.printStackTrace(); - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } diff --git a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ConstructorFunction.java b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ConstructorFunction.java index f7660896f..7bd3b008c 100755 --- a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ConstructorFunction.java +++ b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/ConstructorFunction.java @@ -28,7 +28,7 @@ public class ConstructorFunction extends FunctionImplN { } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } diff --git a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/InstanceMethodFunction.java b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/InstanceMethodFunction.java index 94294d53e..fa729ea7a 100755 --- a/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/InstanceMethodFunction.java +++ b/bundles/org.simantics.scl.reflection/src/org/simantics/scl/reflection/functions/InstanceMethodFunction.java @@ -27,7 +27,7 @@ public class InstanceMethodFunction extends FunctionImplN { } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } diff --git a/bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java b/bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java index b9e2e0864..19d883234 100644 --- a/bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java +++ b/bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java @@ -74,7 +74,7 @@ public class ExperimentManager implements IExperimentManager { runnable.run(null); } } catch (InvocationTargetException e) { - Activator.logError("Experiment manager shutdown failed, see exception for details.", e); + Activator.logError("Experiment manager shutdown failed, see exception for details.", e.getCause()); } catch (InterruptedException e) { Activator.logError("Experiment manager shutdown was interrupted, see exception for details.", e); } diff --git a/bundles/org.simantics.spreadsheet.fileimport/.classpath b/bundles/org.simantics.spreadsheet.fileimport/.classpath new file mode 100644 index 000000000..b862a296d --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.spreadsheet.fileimport/.project b/bundles/org.simantics.spreadsheet.fileimport/.project new file mode 100644 index 000000000..64467e816 --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/.project @@ -0,0 +1,33 @@ + + + org.simantics.spreadsheet.fileimport + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.spreadsheet.fileimport/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.spreadsheet.fileimport/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..295926d96 --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.simantics.spreadsheet.fileimport/META-INF/MANIFEST.MF b/bundles/org.simantics.spreadsheet.fileimport/META-INF/MANIFEST.MF new file mode 100644 index 000000000..b2c4d0f7d --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +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 diff --git a/bundles/org.simantics.spreadsheet.fileimport/OSGI-INF/component.xml b/bundles/org.simantics.spreadsheet.fileimport/OSGI-INF/component.xml new file mode 100644 index 000000000..9e70489f4 --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/OSGI-INF/component.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.spreadsheet.fileimport/build.properties b/bundles/org.simantics.spreadsheet.fileimport/build.properties new file mode 100644 index 000000000..7d2a7a858 --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/build.properties @@ -0,0 +1,5 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/component.xml +source.. = src/ diff --git a/bundles/org.simantics.spreadsheet.fileimport/src/org/simantics/spreadsheet/fileimport/Activator.java b/bundles/org.simantics.spreadsheet.fileimport/src/org/simantics/spreadsheet/fileimport/Activator.java new file mode 100644 index 000000000..ff52043ec --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/src/org/simantics/spreadsheet/fileimport/Activator.java @@ -0,0 +1,30 @@ +package org.simantics.spreadsheet.fileimport; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + private static BundleContext context; + + static BundleContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception { + Activator.context = bundleContext; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception { + Activator.context = null; + } + +} diff --git a/bundles/org.simantics.spreadsheet.fileimport/src/org/simantics/spreadsheet/fileimport/ExcelFileImport.java b/bundles/org.simantics.spreadsheet.fileimport/src/org/simantics/spreadsheet/fileimport/ExcelFileImport.java new file mode 100644 index 000000000..a3029021d --- /dev/null +++ b/bundles/org.simantics.spreadsheet.fileimport/src/org/simantics/spreadsheet/fileimport/ExcelFileImport.java @@ -0,0 +1,30 @@ +package org.simantics.spreadsheet.fileimport; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.simantics.db.Resource; +import org.simantics.fileimport.SimanticsResourceFileImport; + +public class ExcelFileImport extends SimanticsResourceFileImport { + + private static final Map ALLOWED_EXTENSIONS = new HashMap<>(2); + + static { + ALLOWED_EXTENSIONS.put("*.xls", "Excel file *.xls"); + ALLOWED_EXTENSIONS.put("*.xlsx", "Excel file *.xlsx"); + } + + @Override + public Optional perform(Resource parent, Path file) { + return Optional.empty(); + } + + @Override + public Map allowedExtensionsWithFilters() { + return ALLOWED_EXTENSIONS; + } + +} diff --git a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java index 8f68fa98f..801c0c5f3 100644 --- a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java +++ b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java @@ -37,15 +37,13 @@ public class CompileStructuralValueRequest extends AbstractCompileStructuralValu public static Object compileAndEvaluate(ReadGraph graph, Variable context) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.get("graph"); + CompileStructuralValueRequest request = new CompileStructuralValueRequest(graph, context); try { - Function1 exp = graph.syncRequest(new CompileStructuralValueRequest(graph, context), - TransientCacheListener.>instance()); + Function1 exp = graph.syncRequest(request, TransientCacheListener.>instance()); sclContext.put("graph", graph); return exp.apply(context); - } catch (DatabaseException e) { - throw (DatabaseException)e; } catch (Throwable t) { - throw new DatabaseException(t); + throw new DatabaseException("Compiling structural value request for component=" + request.component + ", literal=" + request.literal + " and relation " + request.relation + " failed!", t); } finally { sclContext.put("graph", oldGraph); } diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/e4/E4WorkbenchUtils.java b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/e4/E4WorkbenchUtils.java index 217185ddc..901772ba7 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/e4/E4WorkbenchUtils.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/e4/E4WorkbenchUtils.java @@ -142,4 +142,26 @@ public class E4WorkbenchUtils { return stack; } + public static void openAndShowPart(MPart part) { + IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class); + EPartService partService = context.get(EPartService.class); + if (!partService.isPartVisible(part)) + partService.showPart(part, PartState.ACTIVATE); + else + partService.activate(part); + } + + + public static void openAndShowPart(String partId) { + IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class); + EPartService partService = context.get(EPartService.class); + partService.showPart(partId, PartState.ACTIVATE); + } + + public static MPart getMPartById(String partId) { + IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class); + EPartService partService = context.get(EPartService.class); + return partService.findPart(partId); + } + } diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/SessionUndoHandler.java b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/SessionUndoHandler.java index bcf0803a1..ce58f65da 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/SessionUndoHandler.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/SessionUndoHandler.java @@ -99,7 +99,7 @@ public class SessionUndoHandler extends AbstractHandler { new ProgressMonitorDialog(shell).run(true, false, undo); actionBars.getStatusLineManager().setMessage( msg.get() ); } catch (InvocationTargetException e1) { - throw new ExecutionException("Undo failed, database failure.", e1); + throw new ExecutionException("Undo failed, database failure.", e1.getCause()); } catch (InterruptedException e1) { throw new ExecutionException("Undo failed, interrupted.", e1); } diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/e4/SessionUndoHandler.java b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/e4/SessionUndoHandler.java index dd931e77e..bccedb364 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/e4/SessionUndoHandler.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/handler/e4/SessionUndoHandler.java @@ -116,7 +116,7 @@ public class SessionUndoHandler { manager.setMessage(msg.get()); // statusManager.setMessage( msg.get() ); } catch (InvocationTargetException e1) { - throw new ExecutionException("Undo failed, database failure.", e1); + throw new ExecutionException("Undo failed, database failure.", e1.getCause()); } catch (InterruptedException e1) { throw new ExecutionException("Undo failed, interrupted.", e1); } diff --git a/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/Callback.java b/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/Callback.java index 147e565df..1b386347a 100644 --- a/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/Callback.java +++ b/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/Callback.java @@ -18,8 +18,11 @@ import java.util.function.Consumer; */ @Deprecated @FunctionalInterface -public interface Callback { +public interface Callback extends Consumer { void run(T parameter); - + + default void accept(T parameter) { + run(parameter); + } } diff --git a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/AWTThread.java b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/AWTThread.java index 518cae818..aaf53490e 100644 --- a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/AWTThread.java +++ b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/AWTThread.java @@ -82,7 +82,7 @@ public class AWTThread extends AbstractExecutorService implements IThreadWorkQue } catch (InterruptedException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } diff --git a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/SyncListenerList.java b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/SyncListenerList.java index f2fcd0dbf..17a158e2b 100644 --- a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/SyncListenerList.java +++ b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/SyncListenerList.java @@ -188,7 +188,7 @@ public class SyncListenerList { } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { - e.printStackTrace(); + e.getCause().printStackTrace(); } } }; @@ -292,7 +292,7 @@ public class SyncListenerList { } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { - e.printStackTrace(); + e.getCause().printStackTrace(); } } } diff --git a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ThreadUtils.java b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ThreadUtils.java index 407a66db8..2efc1e562 100644 --- a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ThreadUtils.java +++ b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ThreadUtils.java @@ -935,7 +935,7 @@ class AWTExecutorSync extends AbstractExecutorService { } catch (InterruptedException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { - throw new RuntimeException(e); + throw new RuntimeException(e.getCause()); } } } diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTAWTComponent.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTAWTComponent.java index 7b3c25954..a57067565 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTAWTComponent.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTAWTComponent.java @@ -387,7 +387,7 @@ public abstract class SWTAWTComponent extends Composite { } catch (IllegalAccessException e) { Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e)); } catch (InvocationTargetException e) { - Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e)); + Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getCause().getMessage(), e.getCause())); } } } diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/AwtEnvironment.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/AwtEnvironment.java index 3979d7cb8..dcb975967 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/AwtEnvironment.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/AwtEnvironment.java @@ -137,7 +137,7 @@ public final class AwtEnvironment { } catch (InterruptedException e) { SWT.error(SWT.ERROR_FAILED_EXEC, e); } catch (InvocationTargetException e) { - SWT.error(SWT.ERROR_FAILED_EXEC, e); + SWT.error(SWT.ERROR_FAILED_EXEC, e.getCause()); } this.display = display; diff --git a/bundles/org.simantics.utils.ui/testcases/org/simantics/utils/ui/SWTAWTTest.java b/bundles/org.simantics.utils.ui/testcases/org/simantics/utils/ui/SWTAWTTest.java index fed7f7703..f0a445087 100644 --- a/bundles/org.simantics.utils.ui/testcases/org/simantics/utils/ui/SWTAWTTest.java +++ b/bundles/org.simantics.utils.ui/testcases/org/simantics/utils/ui/SWTAWTTest.java @@ -155,7 +155,7 @@ public class SWTAWTTest { } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { - e.printStackTrace(); + e.getCause().printStackTrace(); } } } diff --git a/bundles/org.simantics.viewpoint.ontology/graph.tg b/bundles/org.simantics.viewpoint.ontology/graph.tg index 2f973120e..d2c54ac7f 100644 Binary files a/bundles/org.simantics.viewpoint.ontology/graph.tg and b/bundles/org.simantics.viewpoint.ontology/graph.tg differ diff --git a/bundles/org.simantics.viewpoint.ontology/graph/Viewpoint.pgraph b/bundles/org.simantics.viewpoint.ontology/graph/Viewpoint.pgraph index 3c902cc0e..867010081 100644 --- a/bundles/org.simantics.viewpoint.ontology/graph/Viewpoint.pgraph +++ b/bundles/org.simantics.viewpoint.ontology/graph/Viewpoint.pgraph @@ -111,6 +111,11 @@ VP.ResourceNameLabelRule : VP.LabelRule VP.ResourceLabelLabelRule : VP.LabelRule L0.HasDescription """Label rule based on resource labels.""" +VP.TooltipRule extends SWTParentNode } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { - e.printStackTrace(); + e.getCause().printStackTrace(); } } @@ -225,7 +225,7 @@ abstract public class SingleSWTViewNode extends SWTParentNode } catch (IllegalAccessException e) { throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e); } catch (InvocationTargetException e) { - throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e); + throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e.getCause()); } } else { @@ -247,7 +247,7 @@ abstract public class SingleSWTViewNode extends SWTParentNode } catch (IllegalAccessException e) { throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e); } catch (InvocationTargetException e) { - throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e); + throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e.getCause()); } } }