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;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
92 public class AnnotatedPropertyTabContributorFactory implements PropertyTabContributorFactory {
94 private static final Logger LOGGER = LoggerFactory.getLogger(AnnotatedPropertyTabContributorFactory.class);
96 @SuppressWarnings("unchecked")
98 public List<PropertyTabContributor> getContributors(Object input) {
99 Map<String,IPropertyItem> items = new LinkedHashMap<String, IPropertyItem>();
100 List<String> blacklist = new ArrayList<String>();
102 collectItems(input.getClass(), items);
103 collectBlacklist(input.getClass(), blacklist);
104 } catch (InstantiationException e) {
105 // TODO Auto-generated catch block
107 } catch (IllegalAccessException e) {
108 // TODO Auto-generated catch block
112 if (items.size() == 0)
113 return Collections.EMPTY_LIST;
115 MapList<String, IPropertyItem> tabMap = new MapList<String, IPropertyItem>();
116 List<String> tabs = new ArrayList<String>();
117 for (String id : items.keySet()) {
118 IPropertyItem item = items.get(id);
119 tabMap.add(item.getTabId(), item);
120 if (!tabs.contains(item.getTabId())) {
121 tabs.add(item.getTabId());
122 //System.out.println(item.tabId + " " + item.name + " " + item.id);
125 for (String s : blacklist) {
129 List<PropertyTabContributor> contributors = new ArrayList<PropertyTabContributor>(tabs.size());
130 for (String tabId : tabs) {
131 contributors.add(new AnnotatedPropertyTabContributor(tabId, tabMap.getValues(tabId)));
138 private static void collectItems(Class<?> clazz, Map<String, IPropertyItem> items) throws InstantiationException, IllegalAccessException {
139 Class<?> superclass = clazz.getSuperclass();
140 if(superclass != null)
141 collectItems(superclass, items);
143 for (Method m : clazz.getDeclaredMethods()) {
144 m.setAccessible(true);
145 for (Annotation annotation : m.getAnnotations()) {
146 if (annotation.annotationType().equals(GetPropertyValue.class)) {
147 GetPropertyValue get = (GetPropertyValue)annotation;
148 PropertyItem item = (PropertyItem)items.get(get.value());
150 item = new PropertyItem(get.value());
151 items.put(item.id, item);
155 item.manipulatorClass = get.manipulator().newInstance().get(m,null);
157 item.tabId = get.tabId();
159 item.name = get.name();
162 } else if (annotation.annotationType().equals(SetPropertyValue.class)) {
163 SetPropertyValue set = (SetPropertyValue)annotation;
164 PropertyItem item = (PropertyItem)items.get(set.value());
166 item = new PropertyItem(set.value());
167 items.put(item.id, item);
171 } else if (annotation.annotationType().equals(CompoundGetPropertyValue.class)) {
172 CompoundGetPropertyValue get = (CompoundGetPropertyValue)annotation;
173 CompoundPropertyItem item = (CompoundPropertyItem)items.get(get.value());
175 item = new CompoundPropertyItem(get.value());
176 items.put(item.id, item);
180 item.manipulatorFactory = get.manipulator().newInstance();
182 item.tabId = get.tabId();
184 item.name = get.name();
185 } else if (annotation.annotationType().equals(CompoundSetPropertyValue.class)) {
186 CompoundSetPropertyValue set = (CompoundSetPropertyValue)annotation;
187 CompoundPropertyItem item = (CompoundPropertyItem)items.get(set.value());
189 item = new CompoundPropertyItem(set.value());
190 items.put(item.id, item);
194 } else if (annotation.annotationType().equals(GetComboPropertyValue.class)) {
195 GetComboPropertyValue get = (GetComboPropertyValue)annotation;
196 ComboPropertyItem item = (ComboPropertyItem)items.get(get.value());
198 item = new ComboPropertyItem(get.value());
199 items.put(item.id, item);
202 item.manipulatorClass = ComboPropertyManipulator.class;
204 item.tabId = get.tabId();
206 item.name = get.name();
207 } else if (annotation.annotationType().equals(SetComboPropertyValue.class)) {
208 SetComboPropertyValue set = (SetComboPropertyValue)annotation;
209 ComboPropertyItem item = (ComboPropertyItem)items.get(set.value());
211 item = new ComboPropertyItem(set.value());
212 items.put(item.id, item);
215 } else if (annotation.annotationType().equals(GetComboProperty.class)) {
216 GetComboProperty get = (GetComboProperty)annotation;
217 ComboPropertyItem item = (ComboPropertyItem)items.get(get.value());
219 item = new ComboPropertyItem(get.value());
220 items.put(item.id, item);
230 private static void collectBlacklist(Class<?> clazz, List<String> blacklist) throws InstantiationException, IllegalAccessException {
231 Class<?> superclass = clazz.getSuperclass();
232 if(superclass != null)
233 collectBlacklist(superclass, blacklist);
235 PropertyTabBlacklist ann = clazz.getAnnotation(PropertyTabBlacklist.class);
238 String s = ann.value();
243 for (String item : s.split(";")) {
250 private static Map<PropertyItem, PropertyManipulator> createManipulators(CompoundPropertyItem item, Object obj) {
253 @SuppressWarnings("unchecked")
254 Map<String,Object> map = (Map<String, Object>)item.getter.invoke(obj);
255 Map<PropertyItem, PropertyManipulator> result = new HashMap<AnnotatedPropertyTabContributorFactory.PropertyItem, PropertyManipulator>();
256 for (String key : map.keySet()) {
257 MethodWithMapValueProvider provider = new MethodWithMapValueProvider(item.getter, item.setter, key);
258 Class<? extends PropertyManipulator> clazz = item.manipulatorFactory.get(null,map.get(key));
259 PropertyManipulator manipulator = clazz.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
260 PropertyItem i = new PropertyItem(item.id+"."+key);
261 i.getter = item.getter;
262 i.setter = item.setter;
264 i.tabId = item.tabId;
265 result.put(i,manipulator);
268 } catch (Exception e) {
270 return Collections.emptyMap();
275 private static PropertyManipulator createManipulator(PropertyItem item, Object obj) {
277 MethodValueProvider provider = new MethodValueProvider(item.getter, item.setter);
278 PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
280 } catch (Exception e) {
286 private static PropertyManipulator createManipulator(ComboPropertyItem item, Object obj) {
288 MethodComboValueProvider provider = new MethodComboValueProvider(item.getter, item.setter,item.values);
289 PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
291 } catch (Exception e) {
297 private static interface IPropertyItem {
298 public String getTabId();
299 public String getName();
300 public String getId();
301 public boolean editable();
304 private static class PropertyItem implements IPropertyItem{
307 private String tabId;
308 private Method getter;
309 private Method setter;
310 private Class<? extends PropertyManipulator> manipulatorClass;
313 public PropertyItem(String id) {
315 throw new NullPointerException();
320 public String getId() {
325 public int hashCode() {
326 return id.hashCode();
330 public String getTabId() {
335 public String getName() {
340 public boolean editable() {
341 return setter != null;
345 private static class CompoundPropertyItem implements IPropertyItem{
348 private String tabId;
349 private Method getter;
350 private Method setter;
351 private PropertyManipulatorFactory manipulatorFactory;
354 public CompoundPropertyItem(String id) {
356 throw new NullPointerException();
361 public String getId() {
366 public int hashCode() {
367 return id.hashCode();
371 public String getTabId() {
376 public String getName() {
381 public boolean editable() {
382 return setter != null;
386 private static class ComboPropertyItem implements IPropertyItem{
389 private String tabId;
390 private Method getter;
391 private Method setter;
392 private Method values;
393 private Class<? extends ComboPropertyManipulator> manipulatorClass;
396 public ComboPropertyItem(String id) {
398 throw new NullPointerException();
403 public String getId() {
408 public int hashCode() {
409 return id.hashCode();
413 public String getTabId() {
418 public String getName() {
423 public boolean editable() {
424 return setter != null;
428 private static class AnnotatedPropertyTabContributor implements PropertyTabContributor {
430 List<IPropertyItem> items;
432 public AnnotatedPropertyTabContributor(String id, List<IPropertyItem> items) {
434 throw new NullPointerException();
440 public IPropertyTab create(Composite parent, IWorkbenchSite site,
441 ISessionContext context, Object input) {
442 AnnotatedPropertyTab tab = new AnnotatedPropertyTab(id, items);
443 tab.createControl(parent, context);
448 public String getId() {
454 private static class AnnotatedPropertyTab implements IPropertyTab2, NodeListener {
456 List<IPropertyItem> contibutedItems;
457 List<IPropertyItem> resolvedItems = new ArrayList<IPropertyItem>();
458 private Map<IPropertyItem,PropertyManipulator> manipulators = new HashMap<IPropertyItem, PropertyManipulator>();
460 private TableViewer viewer;
463 private NodeMap<?,?,?> nodeMap;
465 private List<TableViewerColumn> valueColumns = new ArrayList<TableViewerColumn>();
467 public AnnotatedPropertyTab(String id, List<IPropertyItem> items) {
469 this.contibutedItems = items;
475 public void createControl(Composite parent, ISessionContext context) {
476 //parent.setLayout(new FillLayout());
477 viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE);
479 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getTable());
481 //viewer.setLabelProvider(new AnnotatedTableLabelProvider(object))
483 viewer.setContentProvider(new PropertyItemContentsProvider());
485 TableViewerColumn name = new TableViewerColumn(viewer, SWT.LEFT);
486 //TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT);
487 name.setLabelProvider(new PropertyItemNameProvider());
488 //value.setLabelProvider(new PropertyValueLabelProvider(null));
489 name.getColumn().setText("Property");
490 //value.getColumn().setText("Value");
491 name.getColumn().setWidth(200);
492 //value.getColumn().setWidth(200);
493 name.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
496 public void selectionChanged(SelectionChangedEvent event) {
497 PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(),PropertyItem.class);
499 PropertyManipulator manipulator = manipulators.get(item);//createManipulator(item, null);
500 for (int i = 0; i < valueColumns.size(); i++) {
501 TableViewerColumn c = valueColumns.get(i);
502 if (i < manipulator.getValueCount()) {
503 c.getColumn().setText(manipulator.getDescription(i));
505 c.getColumn().setText("");
513 viewer.getTable().setHeaderVisible(true);
514 viewer.getTable().setLinesVisible(true);
515 viewer.addSelectionChangedListener(new ISelectionChangedListener() {
518 public void selectionChanged(SelectionChangedEvent event) {
519 IPropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(), IPropertyItem.class);
521 if (!manipulators.get(selectedItem).getEditMode())
522 manipulators.get(selectedItem).setEditMode(true);
525 for (IPropertyItem i : delayedUpdate) {
526 if (!i.equals(selectedItem)) {
527 manipulators.get(i).setEditMode(false);
528 viewer.update(i,null);
531 if (delayedUpdate.contains(selectedItem)) {
532 delayedUpdate.clear();
533 delayedUpdate.add(selectedItem);
535 delayedUpdate.clear();
540 CellNavigationStrategy nStrategy = new CellNavigationStrategy() {
541 private ViewerCell internalFindSelectedCell(
542 ColumnViewer viewer, ViewerCell currentSelectedCell,
544 switch (event.keyCode) {
546 if (currentSelectedCell != null) {
547 return getNeighbor(currentSelectedCell,
548 ViewerCell.ABOVE, false);
552 if (currentSelectedCell != null) {
553 return getNeighbor(currentSelectedCell,
554 ViewerCell.BELOW, false);
558 if (currentSelectedCell != null) {
559 return getNeighbor(currentSelectedCell,
560 ViewerCell.LEFT, true);
563 case SWT.ARROW_RIGHT:
564 if (currentSelectedCell != null) {
565 return getNeighbor(currentSelectedCell,
566 ViewerCell.RIGHT, true);
573 public ViewerCell findSelectedCell(ColumnViewer viewer,
574 ViewerCell currentSelectedCell, Event event) {
575 ViewerCell cell = internalFindSelectedCell(viewer,
576 currentSelectedCell, event);
578 TableColumn t = AnnotatedPropertyTab.this.viewer.getTable().getColumn(
579 cell.getColumnIndex());
580 AnnotatedPropertyTab.this.viewer.getTable().showColumn(t);
586 TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(
587 viewer, new FocusCellOwnerDrawHighlighter(viewer));
589 Field f = focusCellManager.getClass().getSuperclass()
590 .getDeclaredField("navigationStrategy");
591 f.setAccessible(true);
592 f.set(focusCellManager, nStrategy);
593 } catch (SecurityException e) {
595 } catch (NoSuchFieldException e) {
597 } catch (IllegalArgumentException e) {
599 } catch (IllegalAccessException e) {
602 ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(
604 protected boolean isEditorActivationEvent(
605 ColumnViewerEditorActivationEvent event) {
606 return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
607 || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION
608 || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
609 || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && (event.keyCode == SWT.CR || event.keyCode == SWT.F2))
610 || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
613 TableViewerEditor.create(viewer, focusCellManager, actSupport,
614 ColumnViewerEditor.TABBING_HORIZONTAL
615 | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
616 | ColumnViewerEditor.TABBING_VERTICAL
617 | ColumnViewerEditor.KEYBOARD_ACTIVATION);
618 viewer.getColumnViewerEditor().addEditorActivationListener(
619 new ColumnViewerEditorActivationListener() {
620 public void afterEditorActivated(
621 ColumnViewerEditorActivationEvent event) {
624 public void afterEditorDeactivated(
625 ColumnViewerEditorDeactivationEvent event) {
628 public void beforeEditorActivated(
629 ColumnViewerEditorActivationEvent event) {
630 ViewerCell cell = (ViewerCell) event.getSource();
631 viewer.getTable().showColumn(
632 viewer.getTable().getColumn(cell.getColumnIndex()));
635 public void beforeEditorDeactivated(
636 ColumnViewerEditorDeactivationEvent event) {
644 private IPropertyItem selectedItem = null;
645 private Set<IPropertyItem> delayedUpdate = new HashSet<IPropertyItem>();
649 public void setInput(ISessionContext context, ISelection selection,
651 Collection<INode> nodes = AdaptationUtils.adaptToCollection(selection, INode.class);
652 if (nodes.size() != 1) {
654 node.removeListener(this);
659 INode n = nodes.iterator().next();
661 if (!node.equals(n)) {
662 node.removeListener(this);
672 private void setInput(INode node) {
674 this.node.addListener(this);
682 if (n instanceof NodeMapProvider<?,?,?>) {
683 nodeMap = ((NodeMapProvider<?,?,?>) n).getNodeMap();
687 n = (INode)n.getParent();
689 boolean readOnly = (node instanceof IStructuralNode && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot());
691 resolvedItems.clear();
692 manipulators.clear();
693 for (IPropertyItem item : contibutedItems) {
694 if (item instanceof PropertyItem) {
695 resolvedItems.add((PropertyItem)item);
696 manipulators.put((PropertyItem)item, createManipulator((PropertyItem)item, node));
697 } else if (item instanceof ComboPropertyItem) {
698 resolvedItems.add((ComboPropertyItem)item);
699 manipulators.put((ComboPropertyItem)item, createManipulator((ComboPropertyItem)item, node));
700 } else if (item instanceof CompoundPropertyItem) {
701 CompoundPropertyItem compound = (CompoundPropertyItem)item;
702 Map<PropertyItem, PropertyManipulator> manipulators = createManipulators(compound, node);
703 for (PropertyItem i : manipulators.keySet()) {
704 resolvedItems.add(i);
705 this.manipulators.put(i, manipulators.get(i));
713 for (PropertyManipulator manipulator : manipulators.values()) {
714 if (valueCount < manipulator.getValueCount())
715 valueCount = manipulator.getValueCount();
717 for (int i = 0; i < valueCount; i++) {
718 TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT);
719 //value.getColumn().setText("Value " + (i+1));
720 value.getColumn().setText("");
721 value.getColumn().setWidth(200);
722 valueColumns.add(value);
723 //value.setEditingSupport(new )
726 // create label providers
727 PropertyValueLabelProvider2 p = new PropertyValueLabelProvider2(this);
729 for (TableViewerColumn c : valueColumns) {
730 c.setLabelProvider(p);
732 PropertyEditingSupport support = new PropertyEditingSupport(this, viewer, index++, nodeMap);
733 c.setEditingSupport(support);
736 Collections.sort(resolvedItems, new Comparator<IPropertyItem>() {
738 public int compare(IPropertyItem o1, IPropertyItem o2) {
739 return o1.getName().compareTo(o2.getName());
742 viewer.getTable().setEnabled(!readOnly);
743 viewer.setInput(resolvedItems);
747 public void requestFocus() {
748 viewer.getTable().forceFocus();
752 public void dispose() {
754 node.removeListener(this);
761 public Control getControl() {
762 return viewer.getTable();
766 public ISelectionProvider getSelectionProvider() {
771 public boolean isDisposed() {
772 return viewer.getTable().isDisposed();
776 public <T extends INode> void nodeAdded(ParentNode<T> node,
777 INode child, String rel) {
782 public <T extends INode> void nodeRemoved(ParentNode<T> node,
783 INode child, String rel) {
788 public void propertyChanged(INode node, final String id) {
789 // for (final PropertyItem item : items) {
790 // if (item.id.equals(id)) {
791 // Display.getDefault().asyncExec(new Runnable() {
794 // public void run() {
795 // viewer.update(item, null);
801 if (Thread.currentThread() == Display.getDefault().getThread()) {
802 if (viewer.getTable().isDisposed())
804 if (LOGGER.isTraceEnabled()) LOGGER.trace("Viewer refresh " + id);
805 for (IPropertyItem item : resolvedItems)
806 if (!item.equals(selectedItem))
807 viewer.refresh(item);
808 if (selectedItem != null)
809 delayedUpdate.add(selectedItem);
810 } else if (!editing){
811 // running delayed refresh when a cell editor is active would cancel cell editing.
812 Display.getDefault().asyncExec(new Runnable() {
815 if (viewer.getTable().isDisposed()) {
816 if (AnnotatedPropertyTab.this.node != null)
817 AnnotatedPropertyTab.this.node.removeListener(AnnotatedPropertyTab.this);
820 if (LOGGER.isTraceEnabled()) LOGGER.trace("Viewer threaded refresh " + id);
821 for (IPropertyItem item : resolvedItems)
822 if (!item.equals(selectedItem))
823 viewer.refresh(item);
824 if (selectedItem != null)
825 delayedUpdate.add(selectedItem);
830 for (IPropertyItem item : resolvedItems) {
831 delayedUpdate.add(item);
840 public void updatePartName(Consumer<String> updateCallback) {
842 updateCallback.accept(node.toString());
846 public PropertyManipulator getManipulator(IPropertyItem item) {
847 return manipulators.get(item);
850 private boolean editing = false;
852 public void setEditing(boolean editing) {
853 this.editing = editing;
859 private static class PropertyEditingSupport extends EditingSupport {
860 AnnotatedPropertyTab tab;
862 NodeMap<?,?,?> nodeMap;
864 CellEditor propertyItemEditor;
865 Map<ComboPropertyItem,CellEditor> comboEditors = new HashMap<>();
867 public PropertyEditingSupport(AnnotatedPropertyTab tab, TableViewer viewer, int index, NodeMap<?,?,?> nodeMap) {
871 this.viewer = viewer;
872 this.nodeMap = nodeMap;
876 protected boolean canEdit(Object element) {
877 IPropertyItem item = (IPropertyItem)element;
878 if (tab.getManipulator(item).getValueCount() <= index)
880 if (!item.editable())
882 if (getValue(element) == null)
888 protected CellEditor getCellEditor(Object element) {
889 IPropertyItem item = (IPropertyItem)element;
890 if (tab.getManipulator(item).getValueCount() <= index)
892 if (item instanceof PropertyItem) {
893 if (propertyItemEditor == null) {
894 propertyItemEditor = new TextCellEditor(viewer.getTable(),SWT.NONE) {
896 public void activate() {
897 tab.setEditing(true);
901 public void deactivate() {
903 tab.setEditing(false);
908 if (LOGGER.isTraceEnabled()) LOGGER.trace("CELL EDITOR: " + element);
909 return propertyItemEditor;
911 else if (item instanceof ComboPropertyItem) {
912 ComboPropertyItem comboPropertyItem = (ComboPropertyItem)item;
913 CellEditor editor = comboEditors.get(comboPropertyItem);
914 if (editor == null) {
915 ComboPropertyManipulator manipulator = (ComboPropertyManipulator)tab.manipulators.get(comboPropertyItem);
916 editor = new ComboBoxCellEditor2(viewer.getTable(), manipulator.getItems(), SWT.DROP_DOWN | SWT.READ_ONLY) {
918 public void activate() {
919 tab.setEditing(true);
923 public void deactivate() {
925 tab.setEditing(false);
929 comboEditors.put(comboPropertyItem, editor);
932 if (LOGGER.isTraceEnabled()) LOGGER.trace("CELL EDITOR: " + element);
936 throw new IllegalStateException("Unsupported property item type " + item.getClass().getName());
941 protected Object getValue(Object element) {
942 IPropertyItem item = (IPropertyItem)element;
943 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj);
944 if (manipulator.getValueCount() <= index)
946 if (manipulator instanceof ComboPropertyManipulator) {
947 return ((ComboPropertyManipulator)manipulator).getValueIndex();
949 Object value = manipulator.getValue(index);
954 protected void setValue(Object element, Object value) {
956 IPropertyItem item = (IPropertyItem)element;
957 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj);
958 if (manipulator.getValueCount() <= index)
959 throw new IllegalAccessError("Editing value in index " + index + " is not allowed.");
960 if (LOGGER.isTraceEnabled()) LOGGER.trace("CELL SET VALUE: " + element + " " + value);
961 manipulator.setValue(value.toString(),index);
962 viewer.refresh(item);
963 nodeMap.commit("Set " + item.getId() + " value to " + value);
971 private static class PropertyItemNameProvider extends CellLabelProvider {
975 public void update(ViewerCell cell) {
976 IPropertyItem item = (IPropertyItem)cell.getElement();
978 if (item.getName().length() > 0)
979 cell.setText(item.getName());
981 cell.setText(item.getId());
987 private static class PropertyValueLabelProvider2 extends CellLabelProvider {
988 AnnotatedPropertyTab tab;
989 //private Object object;
991 public PropertyValueLabelProvider2(AnnotatedPropertyTab tab) {
996 public void update(ViewerCell cell) {
997 IPropertyItem item = (IPropertyItem)cell.getElement();
998 int index = cell.getColumnIndex() -1;
999 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, object);
1000 if (manipulator.getValueCount() <= index)
1002 cell.setText(manipulator.getValue(index));
1006 private static class PropertyItemContentsProvider implements IStructuredContentProvider {
1007 @SuppressWarnings("unchecked")
1009 public Object[] getElements(Object inputElement) {
1010 List<PropertyItem> items = (List<PropertyItem>)inputElement;
1011 return items.toArray();
1015 public void dispose() {
1020 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
1027 private static ViewerCell getNeighbor(ViewerCell currentCell,
1028 int directionMask, boolean sameLevel) {
1030 if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) {
1031 row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE,
1033 } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) {
1034 row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW,
1037 row = currentCell.getViewerRow();
1041 columnIndex = getVisualIndex(row, currentCell.getColumnIndex());
1043 if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) {
1045 } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) {
1048 columnIndex += modifier;
1049 if (columnIndex >= 0 && columnIndex < row.getColumnCount()) {
1050 ViewerCell cell = getCellAtVisualIndex(row, columnIndex);
1053 && columnIndex < row.getColumnCount() - 1
1054 && columnIndex > 0) {
1055 if (isVisible(cell)) {
1058 columnIndex += modifier;
1059 cell = getCellAtVisualIndex(row, columnIndex);
1073 public static class TableViewerEditor extends ColumnViewerEditor {
1075 * This viewer's table editor.
1077 private TableEditor tableEditor;
1078 private TableViewerFocusCellManager focusCellManager;
1079 private int feature;
1083 * the viewer the editor is attached to
1084 * @param focusCellManager
1085 * the cell focus manager if one used or <code>null</code>
1086 * @param editorActivationStrategy
1087 * the strategy used to decide about the editor activation
1091 TableViewerEditor(TableViewer viewer,
1092 TableViewerFocusCellManager focusCellManager,
1093 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1095 super(viewer, editorActivationStrategy, feature);
1096 this.feature = feature;
1097 tableEditor = new TableEditor(viewer.getTable());
1098 this.focusCellManager = focusCellManager;
1102 * Create a customized editor with focusable cells
1105 * the viewer the editor is created for
1106 * @param focusCellManager
1107 * the cell focus manager if one needed else
1109 * @param editorActivationStrategy
1110 * activation strategy to control if an editor activated
1112 * bit mask controlling the editor
1114 * <li>{@link ColumnViewerEditor#DEFAULT}</li>
1115 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
1116 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
1118 * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
1119 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
1121 * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int)
1123 public static void create(TableViewer viewer,
1124 TableViewerFocusCellManager focusCellManager,
1125 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1127 TableViewerEditor editor = new TableViewerEditor(viewer,
1128 focusCellManager, editorActivationStrategy, feature);
1129 viewer.setColumnViewerEditor(editor);
1130 if (focusCellManager != null) {
1132 Method m = focusCellManager.getClass().getSuperclass()
1133 .getDeclaredMethod("init");
1134 m.setAccessible(true);
1135 m.invoke(focusCellManager);
1136 } catch (SecurityException e) {
1137 e.printStackTrace();
1138 } catch (IllegalArgumentException e) {
1139 e.printStackTrace();
1140 } catch (IllegalAccessException e) {
1141 e.printStackTrace();
1142 } catch (NoSuchMethodException e) {
1143 e.printStackTrace();
1144 } catch (InvocationTargetException e) {
1145 e.printStackTrace();
1147 // focusCellManager.init();
1152 * Create a customized editor whose activation process is customized
1155 * the viewer the editor is created for
1156 * @param editorActivationStrategy
1157 * activation strategy to control if an editor activated
1159 * bit mask controlling the editor
1161 * <li>{@link ColumnViewerEditor#DEFAULT}</li>
1162 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
1163 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
1165 * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
1166 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
1169 public static void create(TableViewer viewer,
1170 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1172 create(viewer, null, editorActivationStrategy, feature);
1175 protected void setEditor(Control w, Item item, int columnNumber) {
1176 tableEditor.setEditor(w, (TableItem) item, columnNumber);
1179 protected void setLayoutData(LayoutData layoutData) {
1180 tableEditor.grabHorizontal = layoutData.grabHorizontal;
1181 tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
1182 tableEditor.minimumWidth = layoutData.minimumWidth;
1185 public ViewerCell getFocusCell() {
1186 if (focusCellManager != null) {
1187 return focusCellManager.getFocusCell();
1189 return super.getFocusCell();
1192 protected void updateFocusCell(ViewerCell focusCell,
1193 ColumnViewerEditorActivationEvent event) {
1194 // Update the focus cell when we activated the editor with these 2
1196 if (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC
1197 || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL) {
1198 if (focusCellManager != null) {
1200 if (LOGGER.isTraceEnabled()) LOGGER.trace("FOCUS CELL: " + focusCell);
1202 Method m = AbstractTableViewer.class.getDeclaredMethod(
1203 "getSelectionFromWidget");
1204 m.setAccessible(true);
1205 @SuppressWarnings("rawtypes")
1206 List l = (List) m.invoke(getViewer());
1207 if (focusCellManager != null) {
1208 m = focusCellManager
1211 .getDeclaredMethod("setFocusCell",
1212 new Class[] { ViewerCell.class });
1213 m.setAccessible(true);
1214 m.invoke(focusCellManager,
1215 new Object[] { focusCell });
1217 if (!l.contains(focusCell.getElement())) {
1218 getViewer().setSelection(
1219 new StructuredSelection(focusCell
1222 } catch (SecurityException e) {
1223 e.printStackTrace();
1224 } catch (IllegalArgumentException e) {
1225 e.printStackTrace();
1226 } catch (IllegalAccessException e) {
1227 e.printStackTrace();
1228 } catch (NoSuchMethodException e) {
1229 e.printStackTrace();
1230 } catch (InvocationTargetException e) {
1231 e.printStackTrace();
1237 protected void processTraverseEvent(int columnIndex, ViewerRow row,
1238 TraverseEvent event) {
1239 ViewerCell cell2edit = null;
1240 if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
1242 if ((event.stateMask & SWT.CTRL) == SWT.CTRL
1243 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
1244 cell2edit = searchCellAboveBelow(row, getViewer(),
1246 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
1247 cell2edit = searchPreviousCell(row,
1248 row.getCell(columnIndex), row.getCell(columnIndex),
1251 } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) {
1253 if ((event.stateMask & SWT.CTRL) == SWT.CTRL
1254 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
1255 cell2edit = searchCellAboveBelow(row, getViewer(),
1256 columnIndex, false);
1257 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
1258 cell2edit = searchNextCell(row, row.getCell(columnIndex),
1259 row.getCell(columnIndex), getViewer());
1262 if (LOGGER.isTraceEnabled()) LOGGER.trace("NEXT CELL: " + cell2edit);
1263 if (cell2edit != null) {
1264 getViewer().getControl().setRedraw(false);
1265 ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent(
1268 Method m = ColumnViewer.class
1270 "triggerEditorActivationEvent",
1271 new Class[] { ColumnViewerEditorActivationEvent.class });
1272 m.setAccessible(true);
1273 m.invoke(getViewer(), new Object[] { acEvent });
1274 } catch (SecurityException e) {
1275 e.printStackTrace();
1276 } catch (NoSuchMethodException e) {
1277 e.printStackTrace();
1278 } catch (IllegalArgumentException e) {
1279 e.printStackTrace();
1280 } catch (IllegalAccessException e) {
1281 e.printStackTrace();
1282 } catch (InvocationTargetException e) {
1283 e.printStackTrace();
1285 getViewer().getControl().setRedraw(true);
1289 private ViewerCell searchCellAboveBelow(ViewerRow row,
1290 ColumnViewer viewer, int columnIndex, boolean above) {
1291 ViewerCell rv = null;
1292 ViewerRow newRow = null;
1294 newRow = row.getNeighbor(ViewerRow.ABOVE, false);
1296 newRow = row.getNeighbor(ViewerRow.BELOW, false);
1299 if (newRow != null) {
1300 Method m = ColumnViewer.class.getDeclaredMethod(
1301 "getViewerColumn", new Class[] { int.class });
1302 m.setAccessible(true);
1303 ViewerColumn column = (ViewerColumn) m.invoke(viewer,
1304 new Object[] { new Integer(columnIndex) });
1305 m = ViewerColumn.class.getDeclaredMethod(
1306 "getEditingSupport");
1307 m.setAccessible(true);
1308 EditingSupport es = (EditingSupport) m.invoke(column);
1309 if (column != null && es != null) {
1310 m = EditingSupport.class.getDeclaredMethod("canEdit",
1311 new Class[] { Object.class });
1312 m.setAccessible(true);
1313 Boolean b = (Boolean) m.invoke(es,
1314 new Object[] { newRow.getItem().getData() });
1315 if (b.booleanValue()) {
1316 rv = newRow.getCell(columnIndex);
1319 rv = searchCellAboveBelow(newRow, viewer, columnIndex,
1323 } catch (Exception e) {
1324 e.printStackTrace();
1329 private ViewerCell searchPreviousCell(ViewerRow row,
1330 ViewerCell currentCell, ViewerCell originalCell,
1331 ColumnViewer viewer) {
1332 ViewerCell rv = null;
1333 ViewerCell previousCell;
1334 if (currentCell != null) {
1335 previousCell = getNeighbor(currentCell, ViewerCell.LEFT, true);
1337 if (row.getColumnCount() != 0) {
1338 previousCell = row.getCell(getCreationIndex(row,
1339 row.getColumnCount() - 1));
1341 previousCell = row.getCell(0);
1345 if (originalCell.equals(previousCell)) {
1348 if (previousCell != null) {
1349 if (isCellEditable(viewer, previousCell)) {
1352 rv = searchPreviousCell(row, previousCell, originalCell,
1356 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
1357 rv = searchPreviousCell(row, null, originalCell, viewer);
1358 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
1359 ViewerRow rowAbove = row
1360 .getNeighbor(ViewerRow.ABOVE, false);
1361 if (rowAbove != null) {
1362 rv = searchPreviousCell(rowAbove, null, originalCell,
1370 private ViewerCell searchNextCell(ViewerRow row,
1371 ViewerCell currentCell, ViewerCell originalCell,
1372 ColumnViewer viewer) {
1373 ViewerCell rv = null;
1374 ViewerCell nextCell;
1375 if (currentCell != null) {
1376 nextCell = getNeighbor(currentCell, ViewerCell.RIGHT, true);
1378 nextCell = row.getCell(getCreationIndex(row, 0));
1381 if (originalCell.equals(nextCell)) {
1384 if (nextCell != null) {
1385 if (isCellEditable(viewer, nextCell)) {
1388 rv = searchNextCell(row, nextCell, originalCell, viewer);
1391 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
1392 rv = searchNextCell(row, null, originalCell, viewer);
1393 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
1394 ViewerRow rowBelow = row
1395 .getNeighbor(ViewerRow.BELOW, false);
1396 if (rowBelow != null) {
1397 rv = searchNextCell(rowBelow, null, originalCell,
1405 private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) {
1407 Method m = ColumnViewer.class.getDeclaredMethod(
1408 "getViewerColumn", new Class[] { int.class });
1409 m.setAccessible(true);
1410 ViewerColumn column = (ViewerColumn) m.invoke(viewer,
1411 new Object[] { new Integer(cell.getColumnIndex()) });
1412 m = ViewerColumn.class.getDeclaredMethod("getEditingSupport");
1413 m.setAccessible(true);
1414 EditingSupport es = (EditingSupport) m.invoke(column);
1415 if (column != null && es != null) {
1416 m = EditingSupport.class.getDeclaredMethod("canEdit",
1417 new Class[] { Object.class });
1418 m.setAccessible(true);
1420 Boolean b = (Boolean) m.invoke(es,
1421 new Object[] { cell.getElement() });
1422 return b.booleanValue();
1424 } catch (Exception e) {
1425 e.printStackTrace();
1431 // Reimplementation of ViewerCell-Methods
1432 private static int getVisualIndex(ViewerRow row, int creationIndex) {
1433 TableItem item = (TableItem) row.getItem();
1434 int[] order = item.getParent().getColumnOrder();
1435 for (int i = 0; i < order.length; i++) {
1436 if (order[i] == creationIndex) {
1440 return creationIndex;
1443 private static int getCreationIndex(ViewerRow row, int visualIndex) {
1444 TableItem item = (TableItem) row.getItem();
1445 if (item != null && !item.isDisposed() /*
1446 * && hasColumns() &&
1450 return item.getParent().getColumnOrder()[visualIndex];
1455 private static ViewerCell getCellAtVisualIndex(ViewerRow row,
1457 return getCell(row, getCreationIndex(row, visualIndex));
1460 private static boolean isVisible(ViewerCell cell) {
1461 return getWidth(cell) > 0;
1464 private static int getWidth(ViewerCell cell) {
1465 TableItem item = (TableItem) cell.getViewerRow().getItem();
1466 return item.getParent().getColumn(cell.getColumnIndex()).getWidth();
1469 private static ViewerCell getCell(ViewerRow row, int index) {
1470 return row.getCell(index);