From 145a2884933f2ffdd48d6835729e58f1152d274e Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 14 Oct 2016 15:00:33 +0300 Subject: [PATCH] Sync git svn branch with SVN repository r33334. refs #6654 refs #6756 --- .../images/transparent.png | Bin 0 -> 144 bytes .../GENatTableThemeConfiguration.java | 57 ++++- .../browsing/ui/nattable/GETreeLayer.java | 117 ++++++--- .../browsing/ui/nattable/GETreeRowModel.java | 19 +- .../ui/nattable/KeyToSelectionAdapter.java | 163 +++++++++++++ .../ui/nattable/NatTableGraphExplorer.java | 223 +++++++++++++----- .../ui/nattable/NatTableSelectionAdaptor.java | 34 +++ .../browsing/ui/nattable/TreeNode.java | 44 +++- .../org/simantics/modeling/ModelingUtils.java | 1 + 9 files changed, 558 insertions(+), 100 deletions(-) create mode 100644 bundles/org.simantics.browsing.ui.nattable/images/transparent.png create mode 100644 bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/KeyToSelectionAdapter.java diff --git a/bundles/org.simantics.browsing.ui.nattable/images/transparent.png b/bundles/org.simantics.browsing.ui.nattable/images/transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..e9cd02b02df58a002a524049954d457d88216a6e GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VEX7WqAsj$Z!;#Vfk}U9uEC#B- z4#JF18nY{af|4b!5hcO-X(i=}MX3zs<>h*rdD+Fui3O>8`9bP0l+XkKL%AW0 literal 0 HcmV?d00001 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 index bcb8bba53..41d5f2e46 100644 --- 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 @@ -1,13 +1,19 @@ package org.simantics.browsing.ui.nattable; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter; 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.tree.painter.IndentedTreeImagePainter; +import org.eclipse.nebula.widgets.nattable.tree.painter.TreeImagePainter; +import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum; import org.eclipse.nebula.widgets.nattable.util.GUIHelper; public class GENatTableThemeConfiguration extends ModernNatTableThemeConfiguration{ - public GENatTableThemeConfiguration(GETreeData treeData) { + public GENatTableThemeConfiguration(GETreeData treeData, int style) { super(); this.oddRowBgColor = GUIHelper.getColor(250, 250, 250); this.defaultCellPainter = @@ -20,6 +26,55 @@ public class GENatTableThemeConfiguration extends ModernNatTableThemeConfigurati 0, 5, false))); + + TreeImagePainter treeImagePainter = + new TreeImagePainter( + false, + GUIHelper.getImage("right"), //$NON-NLS-1$ + GUIHelper.getImage("right_down"), //$NON-NLS-1$ + GUIHelper.getImageByURL("transparent", + FileLocator.find(Activator.getDefault().getBundle(), + new Path("images/transparent.png"), null))); //$NON-NLS-1$ + this.treeStructurePainter = + new BackgroundPainter( + new PaddingDecorator( + new IndentedTreeImagePainter( + 10, + null, + CellEdgeEnum.LEFT, + treeImagePainter, + false, + 2, + true), + 0, + 5, + 0, + 5, + false)); + TreeImagePainter treeSelectionImagePainter = + new TreeImagePainter( + false, + GUIHelper.getImage("right_inv"), //$NON-NLS-1$ + GUIHelper.getImage("right_down_inv"), //$NON-NLS-1$ + GUIHelper.getImageByURL("transparent", + FileLocator.find(Activator.getDefault().getBundle(), + new Path("images/transparent.png"), null))); //$NON-NLS-1$ + this.treeStructureSelectionPainter = + new BackgroundPainter( + new PaddingDecorator( + new IndentedTreeImagePainter( + 10, + null, + CellEdgeEnum.LEFT, + treeSelectionImagePainter, + false, + 2, + true), + 0, + 5, + 0, + 5, + false)); } } 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 index dbc30050a..309e4daa8 100644 --- 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 @@ -1,9 +1,9 @@ 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.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -31,7 +31,7 @@ import it.unimi.dsi.fastutil.ints.IntRBTreeSet; public class GETreeLayer extends TreeLayer2 { //Set collapsed = new HashSet(); - Set collapsed = new IdentityHashSet(); + Set expanded = new IdentityHashSet(); GETreeData treeData; Comparator comparator = new FirstElementComparator(); @@ -51,7 +51,7 @@ public class GETreeLayer extends TreeLayer2 { @Override public void collapseTreeRow(int parentIndex) { TreeNode task = treeData.getDataAtIndex(parentIndex); - collapsed.add(task); + expanded.remove(task); task.setExpanded(false); super.collapseTreeRow(parentIndex); } @@ -73,7 +73,7 @@ public class GETreeLayer extends TreeLayer2 { @Override public void expandTreeRow(int parentIndex) { TreeNode task = treeData.getDataAtIndex(parentIndex); - collapsed.remove(task); + expanded.add(task); task.setExpanded(true); super.expandTreeRow(parentIndex); } @@ -117,7 +117,7 @@ public class GETreeLayer extends TreeLayer2 { TreeNode task = treeData.getDataAtIndex(parentIndex); if (task != null) { task.setExpanded(false); - collapsed.add(task); + expanded.remove(task); } rowIndexes.addAll(getModel().collapse(parentIndex)); } @@ -145,7 +145,7 @@ public class GETreeLayer extends TreeLayer2 { if (parentIndex >= 0) { TreeNode task = treeData.getDataAtIndex(parentIndex); task.setExpanded(false); - collapsed.add(task); + expanded.remove(task); rowIndexes.addAll(getModel().collapse(parentIndex)); } } @@ -175,7 +175,7 @@ public class GETreeLayer extends TreeLayer2 { } t.setExpanded(false); - collapsed.add(t); + expanded.remove(t); getModel().collapse(i); } @@ -189,8 +189,9 @@ public class GETreeLayer extends TreeLayer2 { for (int parentIndex : parentIndices) { TreeNode task = treeData.getDataAtIndex(parentIndex); task.setExpanded(true); - collapsed.remove(task); + expanded.add(task); rowIndexes.addAll(getModel().expand(parentIndex)); + rowIndexes.add(parentIndex); } //Implementation uses tree set, so removing in reverse order is faster. @@ -207,7 +208,7 @@ public class GETreeLayer extends TreeLayer2 { for (int parentIndex : parentIndices) { TreeNode task = treeData.getDataAtIndex(parentIndex); task.setExpanded(true); - collapsed.remove(task); + expanded.add(task); rowIndexes.addAll(getModel().expand(parentIndex)); } @@ -220,20 +221,20 @@ public class GETreeLayer extends TreeLayer2 { 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)); - } +// 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() { @@ -242,6 +243,23 @@ public class GETreeLayer extends TreeLayer2 { hiddenPos.add(new int[]{0,0}); } + private 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); + + } + getModel().collapse(i); + + } + this.getHiddenRowIndexes().addAll(rowIndexes); + invalidateCache(); + } + @Override public void handleLayerEvent(ILayerEvent event) { // Currently sorting is implemented by sorting the underlaying list. @@ -251,31 +269,68 @@ public class GETreeLayer extends TreeLayer2 { // 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 + // preserve expanded nodes Set coll = null; +// int hiddenCount = 0; if (event instanceof IStructuralChangeEvent) { IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event; if (structuralChangeEvent.isVerticalStructureChanged()) { - // expand old indices - ((GETreeRowModel)getModel()).clear(); + // store old indices + internalRefresh = true; + ((GETreeRowModel)getModel()).clear(); +// hiddenCount = getHiddenRowIndexes().size(); getHiddenRowIndexes().clear(); - coll = collapsed; + // store expanded nodes and clear disposed nodes. + coll = new HashSet<>(); + for (TreeNode n : expanded) + if (!n.isDisposed()) + coll.add(n); + expanded.clear(); + expanded.addAll(coll); + // filter hidden nodes (nodes that have collapsed ancestors) + coll.clear(); + for (TreeNode n : expanded) + if (!n.isHidden()) + coll.add(n); } } super.handleLayerEvent(event); if (coll != null) { - // collapse new indices + _collapseAllRows(); + // expand 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); + expandTreeRow(ind); +// if (getHiddenRowIndexes().size() != hiddenCount) { +// System.out.println(getHiddenRowIndexes().size() + " != " + hiddenCount); +// ((GETreeRowModel)getModel()).clear(); +// getHiddenRowIndexes().clear(); +// _collapseAllRows(); +// //collapseAll(); +// // expand new indices +// iter = coll.iterator(); +// for (int i = 0; i < ind.length; i++) { +// ind[i] = treeData.indexOf(iter.next()); +// } +// expandTreeRow(ind); +// } + internalRefresh = false; } } - public Set getCollapsed() { - return collapsed; + private boolean internalRefresh = false; + + public void fireLayerEvent(ILayerEvent event) { + if (!internalRefresh) + super.fireLayerEvent(event); + + } + + public Set getExpanded() { + return expanded; } List hiddenPos; 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 index ebfb962db..762b47ce0 100644 --- 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 @@ -22,7 +22,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; 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 IntOpenHashSet expandedIndexes = new IntOpenHashSet(); private final Collection listeners = new HashSet(); @@ -59,11 +59,11 @@ public class GETreeRowModel implements ITreeRowModel{ } public boolean isCollapsed(int index) { - return this.parentIndexes.contains(index); + return !this.expandedIndexes.contains(index); } public void clear() { - this.parentIndexes.clear(); + this.expandedIndexes.clear(); } @Override @@ -73,16 +73,18 @@ public class GETreeRowModel implements ITreeRowModel{ @Override public List collapse(int index) { - this.parentIndexes.add(index); + this.expandedIndexes.remove(index); notifyListeners(); - return getChildIndexes(index); + List list = getChildIndexes(index); + //this.parentIndexes.addAll(list); + return list; } @Override public List expand(int index) { - this.parentIndexes.remove(index); + this.expandedIndexes.add(index); notifyListeners(); List children = getExpandedChildIndexes(index); return children; @@ -90,7 +92,6 @@ public class GETreeRowModel implements ITreeRowModel{ @Override public List collapseAll() { - // TODO Auto-generated method stub return null; } @@ -195,7 +196,7 @@ public class GETreeRowModel implements ITreeRowModel{ int index = this.treeData.indexOf(child); if (index >= 0) { result.add(index); - if (!parentIndexes.contains(index)) + if (expandedIndexes.contains(index)) result.addAll(getExpandedChildIndexes(index)); } else { result.addAll(getExpandedChildIndexes(child)); @@ -211,7 +212,7 @@ public class GETreeRowModel implements ITreeRowModel{ int index = this.treeData.indexOf(child); if (index >= 0) { result.add(index); - if (!parentIndexes.contains(index)) + if (expandedIndexes.contains(index)) result.addAll(getExpandedChildIndexes(index)); } else { result.addAll(getExpandedChildIndexes(child)); diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/KeyToSelectionAdapter.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/KeyToSelectionAdapter.java new file mode 100644 index 000000000..9d43c351f --- /dev/null +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/KeyToSelectionAdapter.java @@ -0,0 +1,163 @@ +package org.simantics.browsing.ui.nattable; + +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.simantics.browsing.ui.GraphExplorer; +import org.simantics.utils.ui.AdaptionUtils; + +/** + * Selects tree items based on pressed key events.

+ * + * The default implementation of SWT.Tree (Windows?) uses only the the first column when matching the items.

+ * + * This implementation checks all columns. Override

matches(), matchesColumn()
for customized behavior.

+ * + * @author Marko Luukkainen + * + */ +public class KeyToSelectionAdapter extends KeyAdapter { + + private static final int KEY_INPUT_DELAY = 500; + + private final NatTableGraphExplorer explorer; + + private String matcher = ""; + private int prevEvent = 0; + private int columns = 0; + + protected Pattern alphaNum; + + /** + * @param contextProvider + * @param explorer + */ + public KeyToSelectionAdapter(GraphExplorer explorer) { + assert explorer != null; + + this.explorer = (NatTableGraphExplorer)explorer; + this.alphaNum = Pattern.compile("\\p{Alnum}"); + } + + @Override + public void keyPressed(KeyEvent e) { + if (explorer.isDisposed()) + return; + + + if (!alphaNum.matcher(Character.toString(e.character)).matches()) + return; + // concatenate / replace matcher. + if ((e.time - prevEvent) > KEY_INPUT_DELAY ) + matcher = ""; + prevEvent = e.time; + matcher = matcher += Character.toString(e.character); + + + //TreeItem item = null; + NatTable tree = explorer.getControl(); + columns = explorer.getColumns().length; + + IStructuredSelection sel = (IStructuredSelection)explorer.getWidgetSelection(); + Collection selected = AdaptionUtils.adaptToCollection(sel, RowSelectionItem.class); + + + TreeNode item = find(tree, selected); + + if (item == null && matcher.length() > 1) { + matcher = matcher.substring(matcher.length()-1); + item = find(tree, selected); + } + + if (item != null) { + explorer.select(item); + explorer.show(item); +// tree.select(item); +// tree.showItem(item); + + + } + // without this the default handling would take over. + e.doit = false; + } + + private TreeNode previous = null; + private boolean foundPrev = false; + + private TreeNode find(NatTable tree, Collection selected) { + TreeNode item = null; + + List items = explorer.getItems(); + + if (selected.size() == 0) { + previous = null; + foundPrev = true; + item = findItem(items); + + } else { + previous = selected.iterator().next().item; + foundPrev = false; + item = findItem(items); + if (item == null) { + previous = null; + foundPrev = true; + item = findItem(items); + } + } + return item; + } + + private TreeNode findItem(List items) { + for (int i = 0; i < items.size(); i++) { + TreeNode item = items.get(i); + if (item != previous) { + if (foundPrev && matches(item, columns, matcher)) + return item; + + } else { + foundPrev = true; + } + } + return null; + } + + + /** + * + * @param item + * @param depth Depth of the item in the tree. + * @param columns Number of columns. + * @param string Matching string. + * @return + */ + protected boolean matches(TreeNode item, int columns, String matcher) { + for (int c = 0; c < columns; c++) { + if (matchesColumn(item, c, matcher)) { + return true; + } + } + return false; + } + + /** + * + * @param item + * @param column + * @param matcher + * @return + */ + protected boolean matchesColumn(TreeNode item, int column, String matcher) { + String text = item.getValueString(column); + if (text.toLowerCase().startsWith(matcher)) { + return true; + } + return false; + } + + +} 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 index aa2c5b21e..f38f5fd93 100644 --- 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 @@ -28,8 +28,6 @@ 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; @@ -55,11 +53,13 @@ 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.data.validate.IDataValidator; +import org.eclipse.nebula.widgets.nattable.data.validate.ValidationFailedException; 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.config.DialogErrorHandling; 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; @@ -85,14 +85,18 @@ 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.painter.NatTableBorderOverlayPainter; 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.selection.command.SelectCellCommand; import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration; +import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; import org.eclipse.nebula.widgets.nattable.style.DisplayMode; +import org.eclipse.nebula.widgets.nattable.style.Style; import org.eclipse.nebula.widgets.nattable.ui.menu.AbstractHeaderMenuConfiguration; import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder; +import org.eclipse.nebula.widgets.nattable.util.GUIHelper; import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum; import org.eclipse.swt.SWT; @@ -106,7 +110,6 @@ 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; @@ -202,11 +205,13 @@ import gnu.trove.map.hash.THashMap; import gnu.trove.map.hash.TObjectIntHashMap; /** - * NatTable base GraphExplorer + * NatTable based GraphExplorer + * + * This GraphExplorer is not fully compatible with the other implementations, since it is not based on SWT.Tree. + * + * This implementation is useful in scenarios, where there are a lot of data to be displayed, the performance of NatTable is much better to SWT.Tree based implementations. * * - * 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) * @@ -214,7 +219,7 @@ import gnu.trove.map.hash.TObjectIntHashMap; * */ public class NatTableGraphExplorer extends GraphExplorerImplBase implements GraphExplorer{ - public static final int DEFAULT_MAX_CHILDREN = 1000; + public static final int DEFAULT_MAX_CHILDREN = 10000; private static final boolean DEBUG_SELECTION_LISTENERS = false; private static final boolean DEBUG = false; @@ -349,7 +354,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap originalFont = JFaceResources.getDefaultFontDescriptor(); columns = new Column[0]; - createNatTable(); + createNatTable(style); layout = new NatTableColumnLayout(natTable, columnHeaderDataProvider, rowHeaderDataLayer); this.composite.setLayout(layout); @@ -531,8 +536,6 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap //refreshColumnSizes(); rootNode = new TreeNode(rootContext, explorerContext); if (DEBUG) System.out.println("setRoot " + rootNode); - - // viewer.setInput(rootNode); // Delay content reading. @@ -545,8 +548,8 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap public void run() { if (rootNode != null) { rootNode.updateChildren(); + rootNode.setExpanded(true); listReIndex(); - natTable.refresh(true); } } }); @@ -554,9 +557,13 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } private synchronized void listReIndex() { + for (TreeNode n : list) { + n.setListIndex(-2); + } list.clear(); for (TreeNode c : rootNode.getChildren()) _insertToList(c); + natTable.refresh(); } private void _insertToList(TreeNode n) { @@ -567,6 +574,10 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } } + public List getItems() { + return Collections.unmodifiableList(list); + } + private void initializeState() { if (persistor == null) return; @@ -689,7 +700,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap if (natTable.isDisposed()) return; doSetColumns(columns, callback); - natTable.refresh(true); + natTable.refresh(); natTable.getParent().layout(); } }); @@ -1065,6 +1076,30 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } + public boolean select(TreeNode node) { + assertNotDisposed(); + + if (!list.contains(node)) { + StructuredSelection s = new StructuredSelection(); + selectionAdaptor.setSelection(s); + selectionProvider.setAndFireNonEqualSelection(s); + return true; + } + selectionAdaptor.setSelection(new StructuredSelection(node)); + return false; + } + + public void show(TreeNode node) { + int index = node.getListIndex(); + + int position = treeLayer.getRowPositionByIndex(index); + if (position < 0) { + treeLayer.expandToTreeRow(index); + position = treeLayer.getRowPositionByIndex(index); + } + viewportLayer.moveRowPositionIntoViewport(position); + } + @Override public boolean selectPath(Collection contexts) { @@ -1171,7 +1206,6 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap else treeLayer.collapseTreeRow(n.getListIndex()); } - //viewer.setExpandedState(context, expanded); } @@ -1179,7 +1213,6 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap public void setAutoExpandLevel(int level) { this.autoExpandLevel = level; treeLayer.expandAllToLevel(level); - //viewer.setAutoExpandLevel(level); } int maxChildren = DEFAULT_MAX_CHILDREN; @@ -1244,22 +1277,51 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap if (element.isDisposed()) { return; } - if (((TreeNode)element).updateChildren()) { + if (element.updateChildren()) { + if (DEBUG) { + System.out.println("Update Item updateChildren " + element.listIndex + " " + element); + printDebug(NatTableGraphExplorer.this); + } listReIndex(); - natTable.refresh(true); - //viewer.refresh(element,true); + if (!element.isHidden()) { + if (!element.isExpanded()) { + if (element.listIndex >= 0) + treeLayer.collapseTreeRow(element.listIndex); + if (DEBUG) { + System.out.println("Update Item collapse " + element.listIndex); + printDebug(NatTableGraphExplorer.this); + } + } else { + for (TreeNode c : element.getChildren()) + c.initData(); + } + } else { + TreeNode p = element.getCollapsedAncestor(); + if (p != null) { + if (element.listIndex >= 0) + treeLayer.collapseTreeRow(element.listIndex); + if (p.listIndex >= 0) + treeLayer.collapseTreeRow(p.listIndex); + if (DEBUG) { + System.out.println("Update Item ancetor collapse " + p.listIndex); + printDebug(NatTableGraphExplorer.this); + } + } + } } else { - if (columnIndex >= 0) { - natTable.redraw(); - //viewer.update(element, new String[]{columns[columnIndex].getKey()}); - } else { - natTable.redraw(); - //viewer.refresh(element,true); - } +// if (columnIndex >= 0) { +// viewer.update(element, new String[]{columns[columnIndex].getKey()}); +// } else { +// viewer.refresh(element,true); +// } + natTable.redraw(); } - if (!element.isDisposed() && autoExpandLevel > 1 && !element.isExpanded() && element.getDepth() <= autoExpandLevel) { + if (!element.autoExpanded && !element.isDisposed() && autoExpandLevel > 1 && !element.isExpanded() && element.getDepth() <= autoExpandLevel) { expand = true; + element.autoExpanded = true; + element.initData(); + if (DEBUG) System.out.println("Update Item expand " + element.listIndex); treeLayer.expandTreeRow(element.getListIndex()); //viewer.setExpandedState(element, true); expand = false; @@ -1267,8 +1329,6 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } else { if (rootNode.updateChildren()) { listReIndex(); - natTable.refresh(true); - //viewer.refresh(rootNode,true); } } } @@ -1296,11 +1356,13 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } private void update(final TreeNode element, final int columnIndex) { - if (DEBUG)System.out.println("update " + element + " " + columnIndex); if (natTable.isDisposed()) return; + if (element != null && element.isDisposed()) + return; + if (DEBUG) System.out.println("update " + element + " " + columnIndex); synchronized (pendingItems) { - pendingItems.add(new UpdateItem(element, columnIndex)); + pendingItems.add(new UpdateItem(element, columnIndex)); if (updating) return; updateCounter++; scheduleUpdater(); @@ -1308,14 +1370,14 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } private void update(final TreeNode element) { - if (DEBUG)System.out.println("update " + element); + if (natTable.isDisposed()) return; if (element != null && element.isDisposed()) return; + if (DEBUG) System.out.println("update " + element); synchronized (pendingItems) { - - pendingItems.add(new UpdateItem(element)); + pendingItems.add(new UpdateItem(element)); if (updating) return; updateCounter++; scheduleUpdater(); @@ -1449,7 +1511,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap return (double)dpi.x/96.0; } - private void createNatTable() { + private void createNatTable(int style) { GETreeData treeData = new GETreeData(list); GETreeRowModel treeRowModel = new GETreeRowModel(treeData); columnAccessor = new GEColumnAccessor(this); @@ -1529,7 +1591,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap natTable.addConfiguration(new SingleClickSortConfiguration()); //natTable.addLayerListener(this); - natTable.addConfiguration(new GENatTableThemeConfiguration(treeData)); + natTable.addConfiguration(new GENatTableThemeConfiguration(treeData, style)); natTable.addConfiguration(new NatTableHeaderMenuConfiguration(natTable)); natTable.addConfiguration(new AbstractRegistryConfiguration() { @@ -1559,13 +1621,25 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap }); configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new AdaptableCellEditor()); + configRegistry.registerConfigAttribute(EditConfigAttributes.CONVERSION_ERROR_HANDLER, new DialogErrorHandling(), DisplayMode.EDIT); + configRegistry.registerConfigAttribute(EditConfigAttributes.VALIDATION_ERROR_HANDLER, new DialogErrorHandling(), DisplayMode.EDIT); + configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR, new AdaptableDataValidator(),DisplayMode.EDIT); + + Style conversionErrorStyle = new Style(); + conversionErrorStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_RED); + conversionErrorStyle.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR, GUIHelper.COLOR_WHITE); + configRegistry.registerConfigAttribute(EditConfigAttributes.CONVERSION_ERROR_STYLE, conversionErrorStyle, DisplayMode.EDIT); + configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, new DefaultDisplayConverter(),DisplayMode.EDIT); - // configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, new GECellPainter(),DisplayMode.NORMAL); - - + + } }); + if ((style & SWT.BORDER) > 0) { + natTable.addOverlayPainter(new NatTableBorderOverlayPainter()); + } + natTable.configure(); // natTable.addListener(SWT.MenuDetect, new NatTableMenuListener()); @@ -1764,8 +1838,29 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap public Rectangle calculateControlBounds(Rectangle cellBounds) { return editor.calculateControlBounds(cellBounds); } + } + + private class AdaptableDataValidator implements IDataValidator { + @Override + public boolean validate(ILayerCell cell, IConfigRegistry configRegistry, Object newValue) { + int col = cell.getColumnIndex(); + int row = cell.getRowIndex(); + return validate(col, row, newValue); + } - + @Override + public boolean validate(int col, int row, Object newValue) { + TreeNode node = list.get(row); + Modifier modifier = getModifier(node, col); + if (modifier == null) + return false; + + String err = modifier.isValid(newValue.toString()); + if (err == null) + return true; + modifier.isValid(newValue.toString()); + throw new ValidationFailedException(err); + } } private class CustomCellEditor extends AbstractCellEditor { @@ -2079,7 +2174,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } if (DEBUG) System.out.println("UpdateRunner.doRun() " + items.size()); - ge.natTable.setRedraw(false); + //ge.natTable.setRedraw(false); for (UpdateItem item : items) { item.update(ge.natTable); } @@ -2091,7 +2186,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap ge.natTable.getParent().layout(); } - ge.natTable.setRedraw(true); + //ge.natTable.setRedraw(true); synchronized (ge.pendingItems) { if (!ge.scheduleUpdater()) { @@ -2100,13 +2195,36 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } if (DEBUG) { if (!ge.updating) { - ge.printTree(ge.rootNode, 0); + printDebug(ge); } } } } + private static void printDebug(NatTableGraphExplorer ge) { + ge.printTree(ge.rootNode, 0); + System.out.println("Expanded"); + for (TreeNode n : ge.treeLayer.expanded) + System.out.println(n); + System.out.println("Expanded end"); + System.out.println("Hidden "); + for (int i : ge.treeLayer.getHiddenRowIndexes()) { + System.out.print(i + " "); + } + System.out.println(); +// Display.getCurrent().timerExec(1000, new Runnable() { +// +// @Override +// public void run() { +// System.out.println("Hidden delayed "); +// for (int i : ge.treeLayer.getHiddenRowIndexes()) { +// System.out.print(i + " "); +// } +// System.out.println(); +// } +// }); + } public static class GeViewerContext extends AbstractDisposable implements IGraphExplorerContext { @@ -2348,23 +2466,22 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap @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) { + if (DEBUG)System.out.println("IsExpandedProcessor expand " + expanded); + if (expanded < 0 || expanded >= list.size()) { return; } - nodeStatusChanged(list.get(expanded).getContext(), false); + nodeStatusChanged(list.get(expanded).getContext(), true); } } 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) { + int collapsed = viewportLayer.getRowIndexByPosition(r.start-2); + if (DEBUG)System.out.println("IsExpandedProcessor collapse " + collapsed); + if (collapsed < 0 || collapsed >= list.size()) { return; } nodeStatusChanged(list.get(collapsed).getContext(), false); @@ -2714,7 +2831,7 @@ public class NatTableGraphExplorer extends GraphExplorerImplBase implements Grap } } -private class NatTableHeaderMenuConfiguration extends AbstractHeaderMenuConfiguration { + private class NatTableHeaderMenuConfiguration extends AbstractHeaderMenuConfiguration { public NatTableHeaderMenuConfiguration(NatTable natTable) { @@ -2755,8 +2872,8 @@ private class NatTableHeaderMenuConfiguration extends AbstractHeaderMenuConfigur 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) + if (x < 0 | y <= 0) return null; - return list.get(y); + return list.get(y-1); } } 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 index 4302cdeac..03588ae76 100644 --- 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 @@ -1,6 +1,7 @@ package org.simantics.browsing.ui.nattable; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.eclipse.jface.viewers.IPostSelectionProvider; @@ -21,6 +22,7 @@ import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Event; import org.simantics.utils.datastructures.MapList; +import org.simantics.utils.ui.AdaptionUtils; public class NatTableSelectionAdaptor implements ISelectionProvider, IPostSelectionProvider, ILayerListener { NatTable natTable; @@ -76,9 +78,41 @@ public class NatTableSelectionAdaptor implements ISelectionProvider, IPostSelect public void setSelection(ISelection selection) { if (!(selection instanceof StructuredSelection)) throw new IllegalArgumentException("Selection must be structured selection"); + if (selection.isEmpty()) { + selectionLayer.clear(false); + natTable.redraw(); + return; + } + List rowItems = new ArrayList<>(AdaptionUtils.adaptToCollection(selection, RowSelectionItem.class)); + if (rowItems.size() > 0) { + + setSelectionExternal(rowItems); + return; + } + Collection nodes = AdaptionUtils.adaptToCollection(selection, TreeNode.class); + if (nodes.size() > 0) { + List selected = new ArrayList<>(); + int allCols[] = new int[selectionLayer.getColumnCount()]; + for (int i = 0; i < allCols.length; i++) + allCols[i] = i; + for (TreeNode n : nodes) { + selected.add(new RowSelectionItem(n, n.listIndex, allCols)); + } + setSelectionExternal(selected); + return; + } } + private void setSelectionExternal(List items) { + selectionLayer.clear(true); + for (RowSelectionItem item : items) { + for (int c : item.columnIndex) + selectionLayer.selectCell(c, item.rowIndex, false, true); + } + selection = new StructuredSelection(items); + fireEvents(); + } private List selectedCells = new ArrayList(); 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 index 941c6f319..30a37e329 100644 --- 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 @@ -38,6 +38,7 @@ public class TreeNode implements IAdaptable { TreeNode parent; List children = new ArrayList(); boolean expanded; + boolean autoExpanded = false; public TreeNode(NodeContext context, GeViewerContext explorerContext) { this.context = context; @@ -47,13 +48,13 @@ public class TreeNode implements IAdaptable { explorerContext.getContextToNodeMap().add(context, this); } - int getDepth() { + public int getDepth() { if (parent == null) return 0; return parent.getDepth() + 1; } - int listIndex; + int listIndex = -1; public int getListIndex() { return listIndex; @@ -79,6 +80,27 @@ public class TreeNode implements IAdaptable { return expanded; } + public boolean isHidden() { + TreeNode n = parent; + while (n != null) { + if (!n.isExpanded()) + return true; + n = n.getParent(); + } + return false; + } + + public TreeNode getCollapsedAncestor() { + TreeNode collapsed = null; + TreeNode n = parent; + while (n != null) { + if (!n.isExpanded()) + collapsed = n; + n = n.getParent(); + } + return collapsed; + } + public NodeContext getContext() { return context; } @@ -92,9 +114,8 @@ public class TreeNode implements IAdaptable { Map runtimeLabels; public String getValueString(int column) { - if (column == 0) { + if (column == 0) initData(); - } if (labeler != null) { String key = explorerContext.getGe().getColumns()[column].getKey(); String s = null; @@ -199,7 +220,7 @@ public class TreeNode implements IAdaptable { } - private void initData() { + public void initData() { labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER); imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER); labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS); @@ -319,7 +340,13 @@ public class TreeNode implements IAdaptable { } for (int i = oldCount; i < childContexts.length; i++) { - addChild(childContexts[i], explorerContext); + Integer oldIndex = indexes.getLeft(i); + if (oldIndex == null) { + addChild(childContexts[i], explorerContext); + } else { + TreeNode n = oldChildren.get(oldIndex); + children.add(n); + } } } else { for (int i = 0; i < childContexts.length; i++) { @@ -363,5 +390,10 @@ public class TreeNode implements IAdaptable { return context.getAdapter(adapter); } + + @Override + public String toString() { + return "TreeNode: " + listIndex + " " + (expanded ? "(+)" : "(-)") + " " + context ; + } } 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 e1494cf32..709e981bc 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/ModelingUtils.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/ModelingUtils.java @@ -2312,6 +2312,7 @@ public class ModelingUtils { String extension = (String)t.get(1); filterNames[index] = filterName; extensions[index] = extension; + index++; } dialog.setFilterExtensions(extensions); -- 2.43.2