1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g3d.property;
14 import java.lang.annotation.Annotation;
15 import java.lang.reflect.Field;
16 import java.lang.reflect.InvocationTargetException;
17 import java.lang.reflect.Method;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.LinkedHashMap;
25 import java.util.List;
28 import java.util.function.Consumer;
30 import org.eclipse.jface.layout.GridDataFactory;
31 import org.eclipse.jface.viewers.AbstractTableViewer;
32 import org.eclipse.jface.viewers.CellEditor;
33 import org.eclipse.jface.viewers.CellEditor.LayoutData;
34 import org.eclipse.jface.viewers.CellLabelProvider;
35 import org.eclipse.jface.viewers.CellNavigationStrategy;
36 import org.eclipse.jface.viewers.ColumnViewer;
37 import org.eclipse.jface.viewers.ColumnViewerEditor;
38 import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
39 import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener;
40 import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
41 import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
42 import org.eclipse.jface.viewers.EditingSupport;
43 import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
44 import org.eclipse.jface.viewers.ISelection;
45 import org.eclipse.jface.viewers.ISelectionChangedListener;
46 import org.eclipse.jface.viewers.ISelectionProvider;
47 import org.eclipse.jface.viewers.IStructuredContentProvider;
48 import org.eclipse.jface.viewers.SelectionChangedEvent;
49 import org.eclipse.jface.viewers.StructuredSelection;
50 import org.eclipse.jface.viewers.TableViewer;
51 import org.eclipse.jface.viewers.TableViewerColumn;
52 import org.eclipse.jface.viewers.TableViewerFocusCellManager;
53 import org.eclipse.jface.viewers.TextCellEditor;
54 import org.eclipse.jface.viewers.Viewer;
55 import org.eclipse.jface.viewers.ViewerCell;
56 import org.eclipse.jface.viewers.ViewerColumn;
57 import org.eclipse.jface.viewers.ViewerRow;
58 import org.eclipse.swt.SWT;
59 import org.eclipse.swt.custom.TableEditor;
60 import org.eclipse.swt.events.TraverseEvent;
61 import org.eclipse.swt.widgets.Composite;
62 import org.eclipse.swt.widgets.Control;
63 import org.eclipse.swt.widgets.Display;
64 import org.eclipse.swt.widgets.Event;
65 import org.eclipse.swt.widgets.Item;
66 import org.eclipse.swt.widgets.TableColumn;
67 import org.eclipse.swt.widgets.TableItem;
68 import org.eclipse.ui.IWorkbenchSite;
69 import org.simantics.browsing.ui.swt.ComboBoxCellEditor2;
70 import org.simantics.db.management.ISessionContext;
71 import org.simantics.g3d.property.annotations.CompoundGetPropertyValue;
72 import org.simantics.g3d.property.annotations.CompoundSetPropertyValue;
73 import org.simantics.g3d.property.annotations.GetComboProperty;
74 import org.simantics.g3d.property.annotations.GetComboPropertyValue;
75 import org.simantics.g3d.property.annotations.GetPropertyValue;
76 import org.simantics.g3d.property.annotations.PropertyTabBlacklist;
77 import org.simantics.g3d.property.annotations.SetComboPropertyValue;
78 import org.simantics.g3d.property.annotations.SetPropertyValue;
79 import org.simantics.g3d.scenegraph.NodeMap;
80 import org.simantics.g3d.scenegraph.NodeMapProvider;
81 import org.simantics.g3d.scenegraph.base.INode;
82 import org.simantics.g3d.scenegraph.base.NodeListener;
83 import org.simantics.g3d.scenegraph.base.ParentNode;
84 import org.simantics.g3d.scenegraph.structural.IStructuralNode;
85 import org.simantics.g3d.tools.AdaptationUtils;
86 import org.simantics.selectionview.IPropertyTab;
87 import org.simantics.selectionview.IPropertyTab2;
88 import org.simantics.utils.datastructures.MapList;
90 public class AnnotatedPropertyTabContributorFactory implements PropertyTabContributorFactory {
92 private static final boolean DEBUG = false;
94 @SuppressWarnings("unchecked")
96 public List<PropertyTabContributor> getContributors(Object input) {
97 Map<String,IPropertyItem> items = new LinkedHashMap<String, IPropertyItem>();
98 List<String> blacklist = new ArrayList<String>();
100 collectItems(input.getClass(), items);
101 collectBlacklist(input.getClass(), blacklist);
102 } catch (InstantiationException e) {
103 // TODO Auto-generated catch block
105 } catch (IllegalAccessException e) {
106 // TODO Auto-generated catch block
110 if (items.size() == 0)
111 return Collections.EMPTY_LIST;
113 MapList<String, IPropertyItem> tabMap = new MapList<String, IPropertyItem>();
114 List<String> tabs = new ArrayList<String>();
115 for (String id : items.keySet()) {
116 IPropertyItem item = items.get(id);
117 tabMap.add(item.getTabId(), item);
118 if (!tabs.contains(item.getTabId())) {
119 tabs.add(item.getTabId());
120 //System.out.println(item.tabId + " " + item.name + " " + item.id);
123 for (String s : blacklist) {
127 List<PropertyTabContributor> contributors = new ArrayList<PropertyTabContributor>(tabs.size());
128 for (String tabId : tabs) {
129 contributors.add(new AnnotatedPropertyTabContributor(tabId, tabMap.getValues(tabId)));
136 private static void collectItems(Class<?> clazz, Map<String, IPropertyItem> items) throws InstantiationException, IllegalAccessException {
137 Class<?> superclass = clazz.getSuperclass();
138 if(superclass != null)
139 collectItems(superclass, items);
141 for (Method m : clazz.getDeclaredMethods()) {
142 m.setAccessible(true);
143 for (Annotation annotation : m.getAnnotations()) {
144 if (annotation.annotationType().equals(GetPropertyValue.class)) {
145 GetPropertyValue get = (GetPropertyValue)annotation;
146 PropertyItem item = (PropertyItem)items.get(get.value());
148 item = new PropertyItem(get.value());
149 items.put(item.id, item);
153 item.manipulatorClass = get.manipulator().newInstance().get(m,null);
155 item.tabId = get.tabId();
157 item.name = get.name();
160 } else if (annotation.annotationType().equals(SetPropertyValue.class)) {
161 SetPropertyValue set = (SetPropertyValue)annotation;
162 PropertyItem item = (PropertyItem)items.get(set.value());
164 item = new PropertyItem(set.value());
165 items.put(item.id, item);
169 } else if (annotation.annotationType().equals(CompoundGetPropertyValue.class)) {
170 CompoundGetPropertyValue get = (CompoundGetPropertyValue)annotation;
171 CompoundPropertyItem item = (CompoundPropertyItem)items.get(get.value());
173 item = new CompoundPropertyItem(get.value());
174 items.put(item.id, item);
178 item.manipulatorFactory = get.manipulator().newInstance();
180 item.tabId = get.tabId();
182 item.name = get.name();
183 } else if (annotation.annotationType().equals(CompoundSetPropertyValue.class)) {
184 CompoundSetPropertyValue set = (CompoundSetPropertyValue)annotation;
185 CompoundPropertyItem item = (CompoundPropertyItem)items.get(set.value());
187 item = new CompoundPropertyItem(set.value());
188 items.put(item.id, item);
192 } else if (annotation.annotationType().equals(GetComboPropertyValue.class)) {
193 GetComboPropertyValue get = (GetComboPropertyValue)annotation;
194 ComboPropertyItem item = (ComboPropertyItem)items.get(get.value());
196 item = new ComboPropertyItem(get.value());
197 items.put(item.id, item);
200 item.manipulatorClass = ComboPropertyManipulator.class;
202 item.tabId = get.tabId();
204 item.name = get.name();
205 } else if (annotation.annotationType().equals(SetComboPropertyValue.class)) {
206 SetComboPropertyValue set = (SetComboPropertyValue)annotation;
207 ComboPropertyItem item = (ComboPropertyItem)items.get(set.value());
209 item = new ComboPropertyItem(set.value());
210 items.put(item.id, item);
213 } else if (annotation.annotationType().equals(GetComboProperty.class)) {
214 GetComboProperty get = (GetComboProperty)annotation;
215 ComboPropertyItem item = (ComboPropertyItem)items.get(get.value());
217 item = new ComboPropertyItem(get.value());
218 items.put(item.id, item);
228 private static void collectBlacklist(Class<?> clazz, List<String> blacklist) throws InstantiationException, IllegalAccessException {
229 Class<?> superclass = clazz.getSuperclass();
230 if(superclass != null)
231 collectBlacklist(superclass, blacklist);
233 PropertyTabBlacklist ann = clazz.getAnnotation(PropertyTabBlacklist.class);
236 String s = ann.value();
241 for (String item : s.split(";")) {
248 private static Map<PropertyItem, PropertyManipulator> createManipulators(CompoundPropertyItem item, Object obj) {
251 @SuppressWarnings("unchecked")
252 Map<String,Object> map = (Map<String, Object>)item.getter.invoke(obj);
253 Map<PropertyItem, PropertyManipulator> result = new HashMap<AnnotatedPropertyTabContributorFactory.PropertyItem, PropertyManipulator>();
254 for (String key : map.keySet()) {
255 MethodWithMapValueProvider provider = new MethodWithMapValueProvider(item.getter, item.setter, key);
256 Class<? extends PropertyManipulator> clazz = item.manipulatorFactory.get(null,map.get(key));
257 PropertyManipulator manipulator = clazz.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
258 PropertyItem i = new PropertyItem(item.id+"."+key);
259 i.getter = item.getter;
260 i.setter = item.setter;
262 i.tabId = item.tabId;
263 result.put(i,manipulator);
266 } catch (Exception e) {
268 return Collections.emptyMap();
273 private static PropertyManipulator createManipulator(PropertyItem item, Object obj) {
275 MethodValueProvider provider = new MethodValueProvider(item.getter, item.setter);
276 PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
278 } catch (Exception e) {
284 private static PropertyManipulator createManipulator(ComboPropertyItem item, Object obj) {
286 MethodComboValueProvider provider = new MethodComboValueProvider(item.getter, item.setter,item.values);
287 PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
289 } catch (Exception e) {
295 private static interface IPropertyItem {
296 public String getTabId();
297 public String getName();
298 public String getId();
299 public boolean editable();
302 private static class PropertyItem implements IPropertyItem{
305 private String tabId;
306 private Method getter;
307 private Method setter;
308 private Class<? extends PropertyManipulator> manipulatorClass;
311 public PropertyItem(String id) {
313 throw new NullPointerException();
318 public String getId() {
323 public int hashCode() {
324 return id.hashCode();
328 public String getTabId() {
333 public String getName() {
338 public boolean editable() {
339 return setter != null;
343 private static class CompoundPropertyItem implements IPropertyItem{
346 private String tabId;
347 private Method getter;
348 private Method setter;
349 private PropertyManipulatorFactory manipulatorFactory;
352 public CompoundPropertyItem(String id) {
354 throw new NullPointerException();
359 public String getId() {
364 public int hashCode() {
365 return id.hashCode();
369 public String getTabId() {
374 public String getName() {
379 public boolean editable() {
380 return setter != null;
384 private static class ComboPropertyItem implements IPropertyItem{
387 private String tabId;
388 private Method getter;
389 private Method setter;
390 private Method values;
391 private Class<? extends ComboPropertyManipulator> manipulatorClass;
394 public ComboPropertyItem(String id) {
396 throw new NullPointerException();
401 public String getId() {
406 public int hashCode() {
407 return id.hashCode();
411 public String getTabId() {
416 public String getName() {
421 public boolean editable() {
422 return setter != null;
426 private static class AnnotatedPropertyTabContributor implements PropertyTabContributor {
428 List<IPropertyItem> items;
430 public AnnotatedPropertyTabContributor(String id, List<IPropertyItem> items) {
432 throw new NullPointerException();
438 public IPropertyTab create(Composite parent, IWorkbenchSite site,
439 ISessionContext context, Object input) {
440 AnnotatedPropertyTab tab = new AnnotatedPropertyTab(id, items);
441 tab.createControl(parent, context);
446 public String getId() {
452 private static class AnnotatedPropertyTab implements IPropertyTab2, NodeListener {
454 List<IPropertyItem> contibutedItems;
455 List<IPropertyItem> resolvedItems = new ArrayList<IPropertyItem>();
456 private Map<IPropertyItem,PropertyManipulator> manipulators = new HashMap<IPropertyItem, PropertyManipulator>();
458 private TableViewer viewer;
461 private NodeMap<?,?,?> nodeMap;
463 private List<TableViewerColumn> valueColumns = new ArrayList<TableViewerColumn>();
465 public AnnotatedPropertyTab(String id, List<IPropertyItem> items) {
467 this.contibutedItems = items;
473 public void createControl(Composite parent, ISessionContext context) {
474 //parent.setLayout(new FillLayout());
475 viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE);
477 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getTable());
479 //viewer.setLabelProvider(new AnnotatedTableLabelProvider(object))
481 viewer.setContentProvider(new PropertyItemContentsProvider());
483 TableViewerColumn name = new TableViewerColumn(viewer, SWT.LEFT);
484 //TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT);
485 name.setLabelProvider(new PropertyItemNameProvider());
486 //value.setLabelProvider(new PropertyValueLabelProvider(null));
487 name.getColumn().setText("Property");
488 //value.getColumn().setText("Value");
489 name.getColumn().setWidth(200);
490 //value.getColumn().setWidth(200);
491 name.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
494 public void selectionChanged(SelectionChangedEvent event) {
495 PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(),PropertyItem.class);
497 PropertyManipulator manipulator = manipulators.get(item);//createManipulator(item, null);
498 for (int i = 0; i < valueColumns.size(); i++) {
499 TableViewerColumn c = valueColumns.get(i);
500 if (i < manipulator.getValueCount()) {
501 c.getColumn().setText(manipulator.getDescription(i));
503 c.getColumn().setText("");
511 viewer.getTable().setHeaderVisible(true);
512 viewer.getTable().setLinesVisible(true);
513 viewer.addSelectionChangedListener(new ISelectionChangedListener() {
516 public void selectionChanged(SelectionChangedEvent event) {
517 IPropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(), IPropertyItem.class);
519 if (!manipulators.get(selectedItem).getEditMode())
520 manipulators.get(selectedItem).setEditMode(true);
523 for (IPropertyItem i : delayedUpdate) {
524 if (!i.equals(selectedItem)) {
525 manipulators.get(i).setEditMode(false);
526 viewer.update(i,null);
529 if (delayedUpdate.contains(selectedItem)) {
530 delayedUpdate.clear();
531 delayedUpdate.add(selectedItem);
533 delayedUpdate.clear();
538 CellNavigationStrategy nStrategy = new CellNavigationStrategy() {
539 private ViewerCell internalFindSelectedCell(
540 ColumnViewer viewer, ViewerCell currentSelectedCell,
542 switch (event.keyCode) {
544 if (currentSelectedCell != null) {
545 return getNeighbor(currentSelectedCell,
546 ViewerCell.ABOVE, false);
550 if (currentSelectedCell != null) {
551 return getNeighbor(currentSelectedCell,
552 ViewerCell.BELOW, false);
556 if (currentSelectedCell != null) {
557 return getNeighbor(currentSelectedCell,
558 ViewerCell.LEFT, true);
561 case SWT.ARROW_RIGHT:
562 if (currentSelectedCell != null) {
563 return getNeighbor(currentSelectedCell,
564 ViewerCell.RIGHT, true);
571 public ViewerCell findSelectedCell(ColumnViewer viewer,
572 ViewerCell currentSelectedCell, Event event) {
573 ViewerCell cell = internalFindSelectedCell(viewer,
574 currentSelectedCell, event);
576 TableColumn t = AnnotatedPropertyTab.this.viewer.getTable().getColumn(
577 cell.getColumnIndex());
578 AnnotatedPropertyTab.this.viewer.getTable().showColumn(t);
584 TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(
585 viewer, new FocusCellOwnerDrawHighlighter(viewer));
587 Field f = focusCellManager.getClass().getSuperclass()
588 .getDeclaredField("navigationStrategy");
589 f.setAccessible(true);
590 f.set(focusCellManager, nStrategy);
591 } catch (SecurityException e) {
593 } catch (NoSuchFieldException e) {
595 } catch (IllegalArgumentException e) {
597 } catch (IllegalAccessException e) {
600 ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(
602 Object lastSource = null;
605 protected boolean isEditorActivationEvent(
606 ColumnViewerEditorActivationEvent event) {
607 if (!event.getSource().equals(lastSource))
610 lastSource = event.getSource();
612 if (event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION)
615 return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
616 || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
617 || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION && clickCount >= 2
618 || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR)
619 || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
622 TableViewerEditor.create(viewer, focusCellManager, actSupport,
623 ColumnViewerEditor.TABBING_HORIZONTAL
624 | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
625 | ColumnViewerEditor.TABBING_VERTICAL
626 | ColumnViewerEditor.KEYBOARD_ACTIVATION);
627 viewer.getColumnViewerEditor().addEditorActivationListener(
628 new ColumnViewerEditorActivationListener() {
629 public void afterEditorActivated(
630 ColumnViewerEditorActivationEvent event) {
633 public void afterEditorDeactivated(
634 ColumnViewerEditorDeactivationEvent event) {
637 public void beforeEditorActivated(
638 ColumnViewerEditorActivationEvent event) {
639 ViewerCell cell = (ViewerCell) event.getSource();
640 viewer.getTable().showColumn(
641 viewer.getTable().getColumn(cell.getColumnIndex()));
644 public void beforeEditorDeactivated(
645 ColumnViewerEditorDeactivationEvent event) {
653 private IPropertyItem selectedItem = null;
654 private Set<IPropertyItem> delayedUpdate = new HashSet<IPropertyItem>();
658 public void setInput(ISessionContext context, ISelection selection,
660 Collection<INode> nodes = AdaptationUtils.adaptToCollection(selection, INode.class);
661 if (nodes.size() != 1) {
663 node.removeListener(this);
668 INode n = nodes.iterator().next();
670 if (!node.equals(n)) {
671 node.removeListener(this);
681 private void setInput(INode node) {
683 this.node.addListener(this);
691 if (n instanceof NodeMapProvider<?,?,?>) {
692 nodeMap = ((NodeMapProvider<?,?,?>) n).getNodeMap();
696 n = (INode)n.getParent();
698 boolean readOnly = (node instanceof IStructuralNode && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot());
700 resolvedItems.clear();
701 manipulators.clear();
702 for (IPropertyItem item : contibutedItems) {
703 if (item instanceof PropertyItem) {
704 resolvedItems.add((PropertyItem)item);
705 manipulators.put((PropertyItem)item, createManipulator((PropertyItem)item, node));
706 } else if (item instanceof ComboPropertyItem) {
707 resolvedItems.add((ComboPropertyItem)item);
708 manipulators.put((ComboPropertyItem)item, createManipulator((ComboPropertyItem)item, node));
709 } else if (item instanceof CompoundPropertyItem) {
710 CompoundPropertyItem compound = (CompoundPropertyItem)item;
711 Map<PropertyItem, PropertyManipulator> manipulators = createManipulators(compound, node);
712 for (PropertyItem i : manipulators.keySet()) {
713 resolvedItems.add(i);
714 this.manipulators.put(i, manipulators.get(i));
722 for (PropertyManipulator manipulator : manipulators.values()) {
723 if (valueCount < manipulator.getValueCount())
724 valueCount = manipulator.getValueCount();
726 for (int i = 0; i < valueCount; i++) {
727 TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT);
728 //value.getColumn().setText("Value " + (i+1));
729 value.getColumn().setText("");
730 value.getColumn().setWidth(200);
731 valueColumns.add(value);
732 //value.setEditingSupport(new )
735 // create label providers
736 PropertyValueLabelProvider2 p = new PropertyValueLabelProvider2(this);
738 for (TableViewerColumn c : valueColumns) {
739 c.setLabelProvider(p);
741 PropertyEditingSupport support = new PropertyEditingSupport(this, viewer, index++, nodeMap);
742 c.setEditingSupport(support);
745 Collections.sort(resolvedItems, new Comparator<IPropertyItem>() {
747 public int compare(IPropertyItem o1, IPropertyItem o2) {
748 return o1.getName().compareTo(o2.getName());
751 viewer.getTable().setEnabled(!readOnly);
752 viewer.setInput(resolvedItems);
756 public void requestFocus() {
757 viewer.getTable().forceFocus();
761 public void dispose() {
763 node.removeListener(this);
770 public Control getControl() {
771 return viewer.getTable();
775 public ISelectionProvider getSelectionProvider() {
780 public boolean isDisposed() {
781 return viewer.getTable().isDisposed();
785 public <T extends INode> void nodeAdded(ParentNode<T> node,
786 INode child, String rel) {
791 public <T extends INode> void nodeRemoved(ParentNode<T> node,
792 INode child, String rel) {
797 public void propertyChanged(INode node, final String id) {
798 // for (final PropertyItem item : items) {
799 // if (item.id.equals(id)) {
800 // Display.getDefault().asyncExec(new Runnable() {
803 // public void run() {
804 // viewer.update(item, null);
810 if (Thread.currentThread() == Display.getDefault().getThread()) {
811 if (viewer.getTable().isDisposed())
813 if (DEBUG)System.out.println("Viewer refresh " + id);
814 for (IPropertyItem item : resolvedItems)
815 if (!item.equals(selectedItem))
816 viewer.refresh(item);
817 if (selectedItem != null)
818 delayedUpdate.add(selectedItem);
819 } else if (!editing){
820 // running delayed refresh when a cell editor is active would cancel cell editing.
821 Display.getDefault().asyncExec(new Runnable() {
824 if (viewer.getTable().isDisposed()) {
825 if (AnnotatedPropertyTab.this.node != null)
826 AnnotatedPropertyTab.this.node.removeListener(AnnotatedPropertyTab.this);
829 if (DEBUG) System.out.println("Viewer threaded refresh " + id);
830 for (IPropertyItem item : resolvedItems)
831 if (!item.equals(selectedItem))
832 viewer.refresh(item);
833 if (selectedItem != null)
834 delayedUpdate.add(selectedItem);
839 for (IPropertyItem item : resolvedItems) {
840 delayedUpdate.add(item);
849 public void updatePartName(Consumer<String> updateCallback) {
851 updateCallback.accept(node.toString());
855 public PropertyManipulator getManipulator(IPropertyItem item) {
856 return manipulators.get(item);
859 private boolean editing = false;
861 public void setEditing(boolean editing) {
862 this.editing = editing;
868 private static class PropertyEditingSupport extends EditingSupport {
869 AnnotatedPropertyTab tab;
871 NodeMap<?,?,?> nodeMap;
873 CellEditor propertyItemEditor;
874 Map<ComboPropertyItem,CellEditor> comboEditors = new HashMap<>();
876 public PropertyEditingSupport(AnnotatedPropertyTab tab, TableViewer viewer, int index, NodeMap<?,?,?> nodeMap) {
880 this.viewer = viewer;
881 this.nodeMap = nodeMap;
885 protected boolean canEdit(Object element) {
886 IPropertyItem item = (IPropertyItem)element;
887 if (tab.getManipulator(item).getValueCount() <= index)
889 if (!item.editable())
891 if (getValue(element) == null)
897 protected CellEditor getCellEditor(Object element) {
898 IPropertyItem item = (IPropertyItem)element;
899 if (tab.getManipulator(item).getValueCount() <= index)
901 if (item instanceof PropertyItem) {
902 if (propertyItemEditor == null) {
903 propertyItemEditor = new TextCellEditor(viewer.getTable(),SWT.NONE) {
905 public void activate() {
906 tab.setEditing(true);
910 public void deactivate() {
912 tab.setEditing(false);
917 if (DEBUG) System.err.println("CELL EDITOR: " + element);
918 return propertyItemEditor;
920 else if (item instanceof ComboPropertyItem) {
921 ComboPropertyItem comboPropertyItem = (ComboPropertyItem)item;
922 CellEditor editor = comboEditors.get(comboPropertyItem);
923 if (editor == null) {
924 ComboPropertyManipulator manipulator = (ComboPropertyManipulator)tab.manipulators.get(comboPropertyItem);
925 editor = new ComboBoxCellEditor2(viewer.getTable(), manipulator.getItems(), SWT.DROP_DOWN | SWT.READ_ONLY) {
927 public void activate() {
928 tab.setEditing(true);
932 public void deactivate() {
934 tab.setEditing(false);
938 comboEditors.put(comboPropertyItem, editor);
941 if (DEBUG) System.err.println("CELL EDITOR: " + element);
945 throw new IllegalStateException("Unsupported property item type " + item.getClass().getName());
950 protected Object getValue(Object element) {
951 IPropertyItem item = (IPropertyItem)element;
952 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj);
953 if (manipulator.getValueCount() <= index)
955 if (manipulator instanceof ComboPropertyManipulator) {
956 return ((ComboPropertyManipulator)manipulator).getValueIndex();
958 Object value = manipulator.getValue(index);
963 protected void setValue(Object element, Object value) {
965 IPropertyItem item = (IPropertyItem)element;
966 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj);
967 if (manipulator.getValueCount() <= index)
968 throw new IllegalAccessError("Editing value in index " + index + " is not allowed.");
969 if (DEBUG)System.err.println("CELL SET VALUE: " + element + " " + value);
970 manipulator.setValue(value.toString(),index);
971 viewer.refresh(item);
972 nodeMap.commit("Set " + item.getId() + " value to " + value);
980 private static class PropertyItemNameProvider extends CellLabelProvider {
984 public void update(ViewerCell cell) {
985 IPropertyItem item = (IPropertyItem)cell.getElement();
987 if (item.getName().length() > 0)
988 cell.setText(item.getName());
990 cell.setText(item.getId());
996 private static class PropertyValueLabelProvider2 extends CellLabelProvider {
997 AnnotatedPropertyTab tab;
998 //private Object object;
1000 public PropertyValueLabelProvider2(AnnotatedPropertyTab tab) {
1005 public void update(ViewerCell cell) {
1006 IPropertyItem item = (IPropertyItem)cell.getElement();
1007 int index = cell.getColumnIndex() -1;
1008 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, object);
1009 if (manipulator.getValueCount() <= index)
1011 cell.setText(manipulator.getValue(index));
1015 private static class PropertyItemContentsProvider implements IStructuredContentProvider {
1016 @SuppressWarnings("unchecked")
1018 public Object[] getElements(Object inputElement) {
1019 List<PropertyItem> items = (List<PropertyItem>)inputElement;
1020 return items.toArray();
1024 public void dispose() {
1029 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
1036 private static ViewerCell getNeighbor(ViewerCell currentCell,
1037 int directionMask, boolean sameLevel) {
1039 if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) {
1040 row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE,
1042 } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) {
1043 row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW,
1046 row = currentCell.getViewerRow();
1050 columnIndex = getVisualIndex(row, currentCell.getColumnIndex());
1052 if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) {
1054 } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) {
1057 columnIndex += modifier;
1058 if (columnIndex >= 0 && columnIndex < row.getColumnCount()) {
1059 ViewerCell cell = getCellAtVisualIndex(row, columnIndex);
1062 && columnIndex < row.getColumnCount() - 1
1063 && columnIndex > 0) {
1064 if (isVisible(cell)) {
1067 columnIndex += modifier;
1068 cell = getCellAtVisualIndex(row, columnIndex);
1082 public static class TableViewerEditor extends ColumnViewerEditor {
1084 * This viewer's table editor.
1086 private TableEditor tableEditor;
1087 private TableViewerFocusCellManager focusCellManager;
1088 private int feature;
1092 * the viewer the editor is attached to
1093 * @param focusCellManager
1094 * the cell focus manager if one used or <code>null</code>
1095 * @param editorActivationStrategy
1096 * the strategy used to decide about the editor activation
1100 TableViewerEditor(TableViewer viewer,
1101 TableViewerFocusCellManager focusCellManager,
1102 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1104 super(viewer, editorActivationStrategy, feature);
1105 this.feature = feature;
1106 tableEditor = new TableEditor(viewer.getTable());
1107 this.focusCellManager = focusCellManager;
1111 * Create a customized editor with focusable cells
1114 * the viewer the editor is created for
1115 * @param focusCellManager
1116 * the cell focus manager if one needed else
1118 * @param editorActivationStrategy
1119 * activation strategy to control if an editor activated
1121 * bit mask controlling the editor
1123 * <li>{@link ColumnViewerEditor#DEFAULT}</li>
1124 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
1125 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
1127 * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
1128 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
1130 * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int)
1132 public static void create(TableViewer viewer,
1133 TableViewerFocusCellManager focusCellManager,
1134 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1136 TableViewerEditor editor = new TableViewerEditor(viewer,
1137 focusCellManager, editorActivationStrategy, feature);
1138 viewer.setColumnViewerEditor(editor);
1139 if (focusCellManager != null) {
1141 Method m = focusCellManager.getClass().getSuperclass()
1142 .getDeclaredMethod("init");
1143 m.setAccessible(true);
1144 m.invoke(focusCellManager);
1145 } catch (SecurityException e) {
1146 e.printStackTrace();
1147 } catch (IllegalArgumentException e) {
1148 e.printStackTrace();
1149 } catch (IllegalAccessException e) {
1150 e.printStackTrace();
1151 } catch (NoSuchMethodException e) {
1152 e.printStackTrace();
1153 } catch (InvocationTargetException e) {
1154 e.printStackTrace();
1156 // focusCellManager.init();
1161 * Create a customized editor whose activation process is customized
1164 * the viewer the editor is created for
1165 * @param editorActivationStrategy
1166 * activation strategy to control if an editor activated
1168 * bit mask controlling the editor
1170 * <li>{@link ColumnViewerEditor#DEFAULT}</li>
1171 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
1172 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
1174 * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
1175 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
1178 public static void create(TableViewer viewer,
1179 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1181 create(viewer, null, editorActivationStrategy, feature);
1184 protected void setEditor(Control w, Item item, int columnNumber) {
1185 tableEditor.setEditor(w, (TableItem) item, columnNumber);
1188 protected void setLayoutData(LayoutData layoutData) {
1189 tableEditor.grabHorizontal = layoutData.grabHorizontal;
1190 tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
1191 tableEditor.minimumWidth = layoutData.minimumWidth;
1194 public ViewerCell getFocusCell() {
1195 if (focusCellManager != null) {
1196 return focusCellManager.getFocusCell();
1198 return super.getFocusCell();
1201 protected void updateFocusCell(ViewerCell focusCell,
1202 ColumnViewerEditorActivationEvent event) {
1203 // Update the focus cell when we activated the editor with these 2
1205 if (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC
1206 || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL) {
1207 if (focusCellManager != null) {
1209 if (DEBUG)System.err.println("FOCUS CELL: " + focusCell);
1211 Method m = AbstractTableViewer.class.getDeclaredMethod(
1212 "getSelectionFromWidget");
1213 m.setAccessible(true);
1214 @SuppressWarnings("rawtypes")
1215 List l = (List) m.invoke(getViewer());
1216 if (focusCellManager != null) {
1217 m = focusCellManager
1220 .getDeclaredMethod("setFocusCell",
1221 new Class[] { ViewerCell.class });
1222 m.setAccessible(true);
1223 m.invoke(focusCellManager,
1224 new Object[] { focusCell });
1226 if (!l.contains(focusCell.getElement())) {
1227 getViewer().setSelection(
1228 new StructuredSelection(focusCell
1231 } catch (SecurityException e) {
1232 e.printStackTrace();
1233 } catch (IllegalArgumentException e) {
1234 e.printStackTrace();
1235 } catch (IllegalAccessException e) {
1236 e.printStackTrace();
1237 } catch (NoSuchMethodException e) {
1238 e.printStackTrace();
1239 } catch (InvocationTargetException e) {
1240 e.printStackTrace();
1246 protected void processTraverseEvent(int columnIndex, ViewerRow row,
1247 TraverseEvent event) {
1248 ViewerCell cell2edit = null;
1249 if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
1251 if ((event.stateMask & SWT.CTRL) == SWT.CTRL
1252 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
1253 cell2edit = searchCellAboveBelow(row, getViewer(),
1255 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
1256 cell2edit = searchPreviousCell(row,
1257 row.getCell(columnIndex), row.getCell(columnIndex),
1260 } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) {
1262 if ((event.stateMask & SWT.CTRL) == SWT.CTRL
1263 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
1264 cell2edit = searchCellAboveBelow(row, getViewer(),
1265 columnIndex, false);
1266 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
1267 cell2edit = searchNextCell(row, row.getCell(columnIndex),
1268 row.getCell(columnIndex), getViewer());
1271 if (DEBUG) System.err.println("NEXT CELL: " + cell2edit);
1272 if (cell2edit != null) {
1273 getViewer().getControl().setRedraw(false);
1274 ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent(
1277 Method m = ColumnViewer.class
1279 "triggerEditorActivationEvent",
1280 new Class[] { ColumnViewerEditorActivationEvent.class });
1281 m.setAccessible(true);
1282 m.invoke(getViewer(), new Object[] { acEvent });
1283 } catch (SecurityException e) {
1284 e.printStackTrace();
1285 } catch (NoSuchMethodException e) {
1286 e.printStackTrace();
1287 } catch (IllegalArgumentException e) {
1288 e.printStackTrace();
1289 } catch (IllegalAccessException e) {
1290 e.printStackTrace();
1291 } catch (InvocationTargetException e) {
1292 e.printStackTrace();
1294 getViewer().getControl().setRedraw(true);
1298 private ViewerCell searchCellAboveBelow(ViewerRow row,
1299 ColumnViewer viewer, int columnIndex, boolean above) {
1300 ViewerCell rv = null;
1301 ViewerRow newRow = null;
1303 newRow = row.getNeighbor(ViewerRow.ABOVE, false);
1305 newRow = row.getNeighbor(ViewerRow.BELOW, false);
1308 if (newRow != null) {
1309 Method m = ColumnViewer.class.getDeclaredMethod(
1310 "getViewerColumn", new Class[] { int.class });
1311 m.setAccessible(true);
1312 ViewerColumn column = (ViewerColumn) m.invoke(viewer,
1313 new Object[] { new Integer(columnIndex) });
1314 m = ViewerColumn.class.getDeclaredMethod(
1315 "getEditingSupport");
1316 m.setAccessible(true);
1317 EditingSupport es = (EditingSupport) m.invoke(column);
1318 if (column != null && es != null) {
1319 m = EditingSupport.class.getDeclaredMethod("canEdit",
1320 new Class[] { Object.class });
1321 m.setAccessible(true);
1322 Boolean b = (Boolean) m.invoke(es,
1323 new Object[] { newRow.getItem().getData() });
1324 if (b.booleanValue()) {
1325 rv = newRow.getCell(columnIndex);
1328 rv = searchCellAboveBelow(newRow, viewer, columnIndex,
1332 } catch (Exception e) {
1333 e.printStackTrace();
1338 private ViewerCell searchPreviousCell(ViewerRow row,
1339 ViewerCell currentCell, ViewerCell originalCell,
1340 ColumnViewer viewer) {
1341 ViewerCell rv = null;
1342 ViewerCell previousCell;
1343 if (currentCell != null) {
1344 previousCell = getNeighbor(currentCell, ViewerCell.LEFT, true);
1346 if (row.getColumnCount() != 0) {
1347 previousCell = row.getCell(getCreationIndex(row,
1348 row.getColumnCount() - 1));
1350 previousCell = row.getCell(0);
1354 if (originalCell.equals(previousCell)) {
1357 if (previousCell != null) {
1358 if (isCellEditable(viewer, previousCell)) {
1361 rv = searchPreviousCell(row, previousCell, originalCell,
1365 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
1366 rv = searchPreviousCell(row, null, originalCell, viewer);
1367 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
1368 ViewerRow rowAbove = row
1369 .getNeighbor(ViewerRow.ABOVE, false);
1370 if (rowAbove != null) {
1371 rv = searchPreviousCell(rowAbove, null, originalCell,
1379 private ViewerCell searchNextCell(ViewerRow row,
1380 ViewerCell currentCell, ViewerCell originalCell,
1381 ColumnViewer viewer) {
1382 ViewerCell rv = null;
1383 ViewerCell nextCell;
1384 if (currentCell != null) {
1385 nextCell = getNeighbor(currentCell, ViewerCell.RIGHT, true);
1387 nextCell = row.getCell(getCreationIndex(row, 0));
1390 if (originalCell.equals(nextCell)) {
1393 if (nextCell != null) {
1394 if (isCellEditable(viewer, nextCell)) {
1397 rv = searchNextCell(row, nextCell, originalCell, viewer);
1400 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
1401 rv = searchNextCell(row, null, originalCell, viewer);
1402 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
1403 ViewerRow rowBelow = row
1404 .getNeighbor(ViewerRow.BELOW, false);
1405 if (rowBelow != null) {
1406 rv = searchNextCell(rowBelow, null, originalCell,
1414 private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) {
1416 Method m = ColumnViewer.class.getDeclaredMethod(
1417 "getViewerColumn", new Class[] { int.class });
1418 m.setAccessible(true);
1419 ViewerColumn column = (ViewerColumn) m.invoke(viewer,
1420 new Object[] { new Integer(cell.getColumnIndex()) });
1421 m = ViewerColumn.class.getDeclaredMethod("getEditingSupport");
1422 m.setAccessible(true);
1423 EditingSupport es = (EditingSupport) m.invoke(column);
1424 if (column != null && es != null) {
1425 m = EditingSupport.class.getDeclaredMethod("canEdit",
1426 new Class[] { Object.class });
1427 m.setAccessible(true);
1429 Boolean b = (Boolean) m.invoke(es,
1430 new Object[] { cell.getElement() });
1431 return b.booleanValue();
1433 } catch (Exception e) {
1434 e.printStackTrace();
1440 // Reimplementation of ViewerCell-Methods
1441 private static int getVisualIndex(ViewerRow row, int creationIndex) {
1442 TableItem item = (TableItem) row.getItem();
1443 int[] order = item.getParent().getColumnOrder();
1444 for (int i = 0; i < order.length; i++) {
1445 if (order[i] == creationIndex) {
1449 return creationIndex;
1452 private static int getCreationIndex(ViewerRow row, int visualIndex) {
1453 TableItem item = (TableItem) row.getItem();
1454 if (item != null && !item.isDisposed() /*
1455 * && hasColumns() &&
1459 return item.getParent().getColumnOrder()[visualIndex];
1464 private static ViewerCell getCellAtVisualIndex(ViewerRow row,
1466 return getCell(row, getCreationIndex(row, visualIndex));
1469 private static boolean isVisible(ViewerCell cell) {
1470 return getWidth(cell) > 0;
1473 private static int getWidth(ViewerCell cell) {
1474 TableItem item = (TableItem) cell.getViewerRow().getItem();
1475 return item.getParent().getColumn(cell.getColumnIndex()).getWidth();
1478 private static ViewerCell getCell(ViewerRow row, int index) {
1479 return row.getCell(index);