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.db.management.ISessionContext;
70 import org.simantics.g3d.property.annotations.CompoundGetPropertyValue;
71 import org.simantics.g3d.property.annotations.CompoundSetPropertyValue;
72 import org.simantics.g3d.property.annotations.GetPropertyValue;
73 import org.simantics.g3d.property.annotations.PropertyTabBlacklist;
74 import org.simantics.g3d.property.annotations.SetPropertyValue;
75 import org.simantics.g3d.scenegraph.IG3DNode;
76 import org.simantics.g3d.scenegraph.NodeMap;
77 import org.simantics.g3d.scenegraph.NodeMapProvider;
78 import org.simantics.g3d.scenegraph.base.INode;
79 import org.simantics.g3d.scenegraph.base.NodeListener;
80 import org.simantics.g3d.scenegraph.base.ParentNode;
81 import org.simantics.g3d.scenegraph.structural.IStructuralNode;
82 import org.simantics.g3d.tools.AdaptationUtils;
83 import org.simantics.selectionview.IPropertyTab;
84 import org.simantics.selectionview.IPropertyTab2;
85 import org.simantics.utils.datastructures.MapList;
87 public class AnnotatedPropertyTabContributorFactory implements PropertyTabContributorFactory {
89 private static final boolean DEBUG = false;
91 @SuppressWarnings("unchecked")
93 public List<PropertyTabContributor> getContributors(Object input) {
94 Map<String,IPropertyItem> items = new LinkedHashMap<String, IPropertyItem>();
95 List<String> blacklist = new ArrayList<String>();
97 collectItems(input.getClass(), items);
98 collectBlacklist(input.getClass(), blacklist);
99 } catch (InstantiationException e) {
100 // TODO Auto-generated catch block
102 } catch (IllegalAccessException e) {
103 // TODO Auto-generated catch block
107 if (items.size() == 0)
108 return Collections.EMPTY_LIST;
110 MapList<String, IPropertyItem> tabMap = new MapList<String, IPropertyItem>();
111 List<String> tabs = new ArrayList<String>();
112 for (String id : items.keySet()) {
113 IPropertyItem item = items.get(id);
114 tabMap.add(item.getTabId(), item);
115 if (!tabs.contains(item.getTabId())) {
116 tabs.add(item.getTabId());
117 //System.out.println(item.tabId + " " + item.name + " " + item.id);
120 for (String s : blacklist) {
124 List<PropertyTabContributor> contributors = new ArrayList<PropertyTabContributor>(tabs.size());
125 for (String tabId : tabs) {
126 contributors.add(new AnnotatedPropertyTabContributor(tabId, tabMap.getValues(tabId)));
133 private static void collectItems(Class<?> clazz, Map<String, IPropertyItem> items) throws InstantiationException, IllegalAccessException {
134 Class<?> superclass = clazz.getSuperclass();
135 if(superclass != null)
136 collectItems(superclass, items);
138 for (Method m : clazz.getDeclaredMethods()) {
139 m.setAccessible(true);
140 for (Annotation annotation : m.getAnnotations()) {
141 if (annotation.annotationType().equals(GetPropertyValue.class)) {
142 GetPropertyValue get = (GetPropertyValue)annotation;
143 PropertyItem item = (PropertyItem)items.get(get.value());
145 item = new PropertyItem(get.value());
146 items.put(item.id, item);
150 item.manipulatorClass = get.manipulator().newInstance().get(m,null);
152 item.tabId = get.tabId();
154 item.name = get.name();
157 } else if (annotation.annotationType().equals(SetPropertyValue.class)) {
158 SetPropertyValue set = (SetPropertyValue)annotation;
159 PropertyItem item = (PropertyItem)items.get(set.value());
161 item = new PropertyItem(set.value());
162 items.put(item.id, item);
166 } else if (annotation.annotationType().equals(CompoundGetPropertyValue.class)) {
167 CompoundGetPropertyValue get = (CompoundGetPropertyValue)annotation;
168 CompoundPropertyItem item = (CompoundPropertyItem)items.get(get.value());
170 item = new CompoundPropertyItem(get.value());
171 items.put(item.id, item);
175 item.manipulatorFactory = get.manipulator().newInstance();
177 item.tabId = get.tabId();
179 item.name = get.name();
180 } else if (annotation.annotationType().equals(CompoundSetPropertyValue.class)) {
181 CompoundSetPropertyValue set = (CompoundSetPropertyValue)annotation;
182 CompoundPropertyItem item = (CompoundPropertyItem)items.get(set.value());
184 item = new CompoundPropertyItem(set.value());
185 items.put(item.id, item);
196 private static void collectBlacklist(Class<?> clazz, List<String> blacklist) throws InstantiationException, IllegalAccessException {
197 Class<?> superclass = clazz.getSuperclass();
198 if(superclass != null)
199 collectBlacklist(superclass, blacklist);
201 PropertyTabBlacklist ann = clazz.getAnnotation(PropertyTabBlacklist.class);
204 String s = ann.value();
209 for (String item : s.split(";")) {
216 private static Map<PropertyItem, PropertyManipulator> createManipulators(CompoundPropertyItem item, Object obj) {
219 @SuppressWarnings("unchecked")
220 Map<String,Object> map = (Map<String, Object>)item.getter.invoke(obj);
221 Map<PropertyItem, PropertyManipulator> result = new HashMap<AnnotatedPropertyTabContributorFactory.PropertyItem, PropertyManipulator>();
222 for (String key : map.keySet()) {
223 MethodWithMapValueProvider provider = new MethodWithMapValueProvider(item.getter, item.setter, key);
224 Class<? extends PropertyManipulator> clazz = item.manipulatorFactory.get(null,map.get(key));
225 PropertyManipulator manipulator = clazz.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
226 PropertyItem i = new PropertyItem(item.id+"."+key);
227 i.getter = item.getter;
228 i.setter = item.setter;
230 i.tabId = item.tabId;
231 result.put(i,manipulator);
234 } catch (Exception e) {
236 return Collections.emptyMap();
241 private static PropertyManipulator createManipulator(PropertyItem item, Object obj) {
243 MethodValueProvider provider = new MethodValueProvider(item.getter, item.setter);
244 PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj);
246 } catch (Exception e) {
252 private static interface IPropertyItem {
253 public String getTabId();
254 public String getName();
257 private static class PropertyItem implements IPropertyItem{
260 private String tabId;
261 private Method getter;
262 private Method setter;
263 private Class<? extends PropertyManipulator> manipulatorClass;
266 public PropertyItem(String id) {
268 throw new NullPointerException();
273 public int hashCode() {
274 return id.hashCode();
278 public String getTabId() {
283 public String getName() {
288 private static class CompoundPropertyItem implements IPropertyItem{
291 private String tabId;
292 private Method getter;
293 private Method setter;
294 private PropertyManipulatorFactory manipulatorFactory;
297 public CompoundPropertyItem(String id) {
299 throw new NullPointerException();
304 public int hashCode() {
305 return id.hashCode();
309 public String getTabId() {
314 public String getName() {
319 private static class AnnotatedPropertyTabContributor implements PropertyTabContributor {
321 List<IPropertyItem> items;
323 public AnnotatedPropertyTabContributor(String id, List<IPropertyItem> items) {
325 throw new NullPointerException();
331 public IPropertyTab create(Composite parent, IWorkbenchSite site,
332 ISessionContext context, Object input) {
333 AnnotatedPropertyTab tab = new AnnotatedPropertyTab(id, items);
334 tab.createControl(parent, context);
339 public String getId() {
345 private static class AnnotatedPropertyTab implements IPropertyTab2, NodeListener {
347 List<IPropertyItem> contibutedItems;
348 List<PropertyItem> resolvedItems = new ArrayList<PropertyItem>();
349 private Map<PropertyItem,PropertyManipulator> manipulators = new HashMap<PropertyItem, PropertyManipulator>();
351 private TableViewer viewer;
354 private NodeMap<?,?,?> nodeMap;
356 private List<TableViewerColumn> valueColumns = new ArrayList<TableViewerColumn>();
358 public AnnotatedPropertyTab(String id, List<IPropertyItem> items) {
360 this.contibutedItems = items;
366 public void createControl(Composite parent, ISessionContext context) {
367 //parent.setLayout(new FillLayout());
368 viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE);
370 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getTable());
372 //viewer.setLabelProvider(new AnnotatedTableLabelProvider(object))
374 viewer.setContentProvider(new PropertyItemContentsProvider());
376 TableViewerColumn name = new TableViewerColumn(viewer, SWT.LEFT);
377 //TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT);
378 name.setLabelProvider(new PropertyItemNameProvider());
379 //value.setLabelProvider(new PropertyValueLabelProvider(null));
380 name.getColumn().setText("Property");
381 //value.getColumn().setText("Value");
382 name.getColumn().setWidth(200);
383 //value.getColumn().setWidth(200);
384 name.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
387 public void selectionChanged(SelectionChangedEvent event) {
388 PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(),PropertyItem.class);
390 PropertyManipulator manipulator = manipulators.get(item);//createManipulator(item, null);
391 for (int i = 0; i < valueColumns.size(); i++) {
392 TableViewerColumn c = valueColumns.get(i);
393 if (i < manipulator.getValueCount()) {
394 c.getColumn().setText(manipulator.getDescription(i));
396 c.getColumn().setText("");
404 viewer.getTable().setHeaderVisible(true);
405 viewer.getTable().setLinesVisible(true);
406 viewer.addSelectionChangedListener(new ISelectionChangedListener() {
409 public void selectionChanged(SelectionChangedEvent event) {
410 PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(), PropertyItem.class);
412 if (!manipulators.get(selectedItem).getEditMode())
413 manipulators.get(selectedItem).setEditMode(true);
416 for (IPropertyItem i : delayedUpdate) {
417 if (!i.equals(selectedItem)) {
418 manipulators.get(i).setEditMode(false);
419 viewer.update(i,null);
422 if (delayedUpdate.contains(selectedItem)) {
423 delayedUpdate.clear();
424 delayedUpdate.add(selectedItem);
426 delayedUpdate.clear();
431 CellNavigationStrategy nStrategy = new CellNavigationStrategy() {
432 private ViewerCell internalFindSelectedCell(
433 ColumnViewer viewer, ViewerCell currentSelectedCell,
435 switch (event.keyCode) {
437 if (currentSelectedCell != null) {
438 return getNeighbor(currentSelectedCell,
439 ViewerCell.ABOVE, false);
443 if (currentSelectedCell != null) {
444 return getNeighbor(currentSelectedCell,
445 ViewerCell.BELOW, false);
449 if (currentSelectedCell != null) {
450 return getNeighbor(currentSelectedCell,
451 ViewerCell.LEFT, true);
454 case SWT.ARROW_RIGHT:
455 if (currentSelectedCell != null) {
456 return getNeighbor(currentSelectedCell,
457 ViewerCell.RIGHT, true);
464 public ViewerCell findSelectedCell(ColumnViewer viewer,
465 ViewerCell currentSelectedCell, Event event) {
466 ViewerCell cell = internalFindSelectedCell(viewer,
467 currentSelectedCell, event);
469 TableColumn t = AnnotatedPropertyTab.this.viewer.getTable().getColumn(
470 cell.getColumnIndex());
471 AnnotatedPropertyTab.this.viewer.getTable().showColumn(t);
477 TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(
478 viewer, new FocusCellOwnerDrawHighlighter(viewer));
480 Field f = focusCellManager.getClass().getSuperclass()
481 .getDeclaredField("navigationStrategy");
482 f.setAccessible(true);
483 f.set(focusCellManager, nStrategy);
484 } catch (SecurityException e) {
486 } catch (NoSuchFieldException e) {
488 } catch (IllegalArgumentException e) {
490 } catch (IllegalAccessException e) {
493 ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(
495 protected boolean isEditorActivationEvent(
496 ColumnViewerEditorActivationEvent event) {
497 return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
498 || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
499 || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR)
500 || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
503 TableViewerEditor.create(viewer, focusCellManager, actSupport,
504 ColumnViewerEditor.TABBING_HORIZONTAL
505 | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
506 | ColumnViewerEditor.TABBING_VERTICAL
507 | ColumnViewerEditor.KEYBOARD_ACTIVATION);
508 viewer.getColumnViewerEditor().addEditorActivationListener(
509 new ColumnViewerEditorActivationListener() {
510 public void afterEditorActivated(
511 ColumnViewerEditorActivationEvent event) {
514 public void afterEditorDeactivated(
515 ColumnViewerEditorDeactivationEvent event) {
518 public void beforeEditorActivated(
519 ColumnViewerEditorActivationEvent event) {
520 ViewerCell cell = (ViewerCell) event.getSource();
521 viewer.getTable().showColumn(
522 viewer.getTable().getColumn(cell.getColumnIndex()));
525 public void beforeEditorDeactivated(
526 ColumnViewerEditorDeactivationEvent event) {
534 private IPropertyItem selectedItem = null;
535 private Set<IPropertyItem> delayedUpdate = new HashSet<IPropertyItem>();
539 public void setInput(ISessionContext context, ISelection selection,
541 Collection<INode> nodes = AdaptationUtils.adaptToCollection(selection, INode.class);
542 if (nodes.size() != 1) {
544 node.removeListener(this);
549 INode n = nodes.iterator().next();
551 if (!node.equals(n)) {
552 node.removeListener(this);
562 private void setInput(INode node) {
564 this.node.addListener(this);
572 if (n instanceof NodeMapProvider<?,?,?>) {
573 nodeMap = ((NodeMapProvider<?,?,?>) n).getNodeMap();
577 n = (IG3DNode)n.getParent();
579 boolean readOnly = (node instanceof IStructuralNode && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot());
581 resolvedItems.clear();
582 manipulators.clear();
583 for (IPropertyItem item : contibutedItems) {
584 if (item instanceof PropertyItem) {
585 resolvedItems.add((PropertyItem)item);
586 manipulators.put((PropertyItem)item, createManipulator((PropertyItem)item, node));
589 CompoundPropertyItem compound = (CompoundPropertyItem)item;
590 Map<PropertyItem, PropertyManipulator> manipulators = createManipulators(compound, node);
591 for (PropertyItem i : manipulators.keySet()) {
592 resolvedItems.add(i);
593 this.manipulators.put(i, manipulators.get(i));
599 for (PropertyManipulator manipulator : manipulators.values()) {
600 if (valueCount < manipulator.getValueCount())
601 valueCount = manipulator.getValueCount();
603 for (int i = 0; i < valueCount; i++) {
604 TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT);
605 //value.getColumn().setText("Value " + (i+1));
606 value.getColumn().setText("");
607 value.getColumn().setWidth(200);
608 valueColumns.add(value);
609 //value.setEditingSupport(new )
612 // create label providers
613 PropertyValueLabelProvider2 p = new PropertyValueLabelProvider2(this);
615 for (TableViewerColumn c : valueColumns) {
616 c.setLabelProvider(p);
618 PropertyEditingSupport support = new PropertyEditingSupport(this, viewer, index++, nodeMap);
619 c.setEditingSupport(support);
622 Collections.sort(resolvedItems, new Comparator<IPropertyItem>() {
624 public int compare(IPropertyItem o1, IPropertyItem o2) {
625 return o1.getName().compareTo(o2.getName());
628 viewer.getTable().setEnabled(!readOnly);
629 viewer.setInput(resolvedItems);
633 public void requestFocus() {
634 viewer.getTable().forceFocus();
638 public void dispose() {
640 node.removeListener(this);
647 public Control getControl() {
648 return viewer.getTable();
652 public ISelectionProvider getSelectionProvider() {
657 public boolean isDisposed() {
658 return viewer.getTable().isDisposed();
662 public <T extends INode> void nodeAdded(ParentNode<T> node,
663 INode child, String rel) {
668 public <T extends INode> void nodeRemoved(ParentNode<T> node,
669 INode child, String rel) {
674 public void propertyChanged(INode node, final String id) {
675 // for (final PropertyItem item : items) {
676 // if (item.id.equals(id)) {
677 // Display.getDefault().asyncExec(new Runnable() {
680 // public void run() {
681 // viewer.update(item, null);
687 if (Thread.currentThread() == Display.getDefault().getThread()) {
688 if (viewer.getTable().isDisposed())
690 if (DEBUG)System.out.println("Viewer refresh " + id);
691 for (PropertyItem item : resolvedItems)
692 if (!item.equals(selectedItem))
693 viewer.refresh(item);
694 if (selectedItem != null)
695 delayedUpdate.add(selectedItem);
696 } else if (!editing){
697 // running delayed refresh when a cell editor is active would cancel cell editing.
698 Display.getDefault().asyncExec(new Runnable() {
701 if (viewer.getTable().isDisposed()) {
702 if (AnnotatedPropertyTab.this.node != null)
703 AnnotatedPropertyTab.this.node.removeListener(AnnotatedPropertyTab.this);
706 if (DEBUG) System.out.println("Viewer threaded refresh " + id);
707 for (PropertyItem item : resolvedItems)
708 if (!item.equals(selectedItem))
709 viewer.refresh(item);
710 if (selectedItem != null)
711 delayedUpdate.add(selectedItem);
716 for (PropertyItem item : resolvedItems) {
717 delayedUpdate.add(item);
726 public void updatePartName(Consumer<String> updateCallback) {
728 updateCallback.accept(node.toString());
732 public PropertyManipulator getManipulator(PropertyItem item) {
733 return manipulators.get(item);
736 private boolean editing = false;
738 public void setEditing(boolean editing) {
739 this.editing = editing;
745 private static class PropertyEditingSupport extends EditingSupport {
746 AnnotatedPropertyTab tab;
748 NodeMap<?,?,?> nodeMap;
752 public PropertyEditingSupport(AnnotatedPropertyTab tab, TableViewer viewer, int index, NodeMap<?,?,?> nodeMap) {
756 this.viewer = viewer;
757 this.nodeMap = nodeMap;
761 protected boolean canEdit(Object element) {
762 PropertyItem item = (PropertyItem)element;
763 if (tab.getManipulator(item).getValueCount() <= index)
765 if (item.setter == null)
767 if (getValue(element) == null)
773 protected CellEditor getCellEditor(Object element) {
775 if (tab.getManipulator((PropertyItem)element).getValueCount() <= index)
778 editor = new TextCellEditor(viewer.getTable(),SWT.NONE) {
780 public void activate() {
781 tab.setEditing(true);
785 public void deactivate() {
787 tab.setEditing(false);
790 if (DEBUG)System.err.println("CELL EDITOR: " + element);
795 protected Object getValue(Object element) {
796 PropertyItem item = (PropertyItem)element;
797 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj);
798 if (manipulator.getValueCount() <= index)
800 Object value = manipulator.getValue(index);
805 protected void setValue(Object element, Object value) {
807 PropertyItem item = (PropertyItem)element;
808 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj);
809 if (manipulator.getValueCount() <= index)
810 throw new IllegalAccessError("Editing value in index " + index + " is not allowed.");
811 if (DEBUG)System.err.println("CELL SET VALUE: " + element + " " + value);
812 manipulator.setValue((String)value,index);
813 viewer.refresh(item);
814 nodeMap.commit("Set " + item.id + " value to " + value);
822 private static class PropertyItemNameProvider extends CellLabelProvider {
826 public void update(ViewerCell cell) {
827 PropertyItem item = (PropertyItem)cell.getElement();
829 if (item.name.length() > 0)
830 cell.setText(item.name);
832 cell.setText(item.id);
838 private static class PropertyValueLabelProvider2 extends CellLabelProvider {
839 AnnotatedPropertyTab tab;
840 //private Object object;
842 public PropertyValueLabelProvider2(AnnotatedPropertyTab tab) {
847 public void update(ViewerCell cell) {
848 PropertyItem item = (PropertyItem)cell.getElement();
849 int index = cell.getColumnIndex() -1;
850 PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, object);
851 if (manipulator.getValueCount() <= index)
853 cell.setText(manipulator.getValue(index));
857 private static class PropertyItemContentsProvider implements IStructuredContentProvider {
858 @SuppressWarnings("unchecked")
860 public Object[] getElements(Object inputElement) {
861 List<PropertyItem> items = (List<PropertyItem>)inputElement;
862 return items.toArray();
866 public void dispose() {
871 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
878 private static ViewerCell getNeighbor(ViewerCell currentCell,
879 int directionMask, boolean sameLevel) {
881 if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) {
882 row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE,
884 } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) {
885 row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW,
888 row = currentCell.getViewerRow();
892 columnIndex = getVisualIndex(row, currentCell.getColumnIndex());
894 if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) {
896 } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) {
899 columnIndex += modifier;
900 if (columnIndex >= 0 && columnIndex < row.getColumnCount()) {
901 ViewerCell cell = getCellAtVisualIndex(row, columnIndex);
904 && columnIndex < row.getColumnCount() - 1
905 && columnIndex > 0) {
906 if (isVisible(cell)) {
909 columnIndex += modifier;
910 cell = getCellAtVisualIndex(row, columnIndex);
924 public static class TableViewerEditor extends ColumnViewerEditor {
926 * This viewer's table editor.
928 private TableEditor tableEditor;
929 private TableViewerFocusCellManager focusCellManager;
934 * the viewer the editor is attached to
935 * @param focusCellManager
936 * the cell focus manager if one used or <code>null</code>
937 * @param editorActivationStrategy
938 * the strategy used to decide about the editor activation
942 TableViewerEditor(TableViewer viewer,
943 TableViewerFocusCellManager focusCellManager,
944 ColumnViewerEditorActivationStrategy editorActivationStrategy,
946 super(viewer, editorActivationStrategy, feature);
947 this.feature = feature;
948 tableEditor = new TableEditor(viewer.getTable());
949 this.focusCellManager = focusCellManager;
953 * Create a customized editor with focusable cells
956 * the viewer the editor is created for
957 * @param focusCellManager
958 * the cell focus manager if one needed else
960 * @param editorActivationStrategy
961 * activation strategy to control if an editor activated
963 * bit mask controlling the editor
965 * <li>{@link ColumnViewerEditor#DEFAULT}</li>
966 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
967 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
969 * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
970 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
972 * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int)
974 public static void create(TableViewer viewer,
975 TableViewerFocusCellManager focusCellManager,
976 ColumnViewerEditorActivationStrategy editorActivationStrategy,
978 TableViewerEditor editor = new TableViewerEditor(viewer,
979 focusCellManager, editorActivationStrategy, feature);
980 viewer.setColumnViewerEditor(editor);
981 if (focusCellManager != null) {
983 Method m = focusCellManager.getClass().getSuperclass()
984 .getDeclaredMethod("init");
985 m.setAccessible(true);
986 m.invoke(focusCellManager);
987 } catch (SecurityException e) {
989 } catch (IllegalArgumentException e) {
991 } catch (IllegalAccessException e) {
993 } catch (NoSuchMethodException e) {
995 } catch (InvocationTargetException e) {
998 // focusCellManager.init();
1003 * Create a customized editor whose activation process is customized
1006 * the viewer the editor is created for
1007 * @param editorActivationStrategy
1008 * activation strategy to control if an editor activated
1010 * bit mask controlling the editor
1012 * <li>{@link ColumnViewerEditor#DEFAULT}</li>
1013 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
1014 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
1016 * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
1017 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
1020 public static void create(TableViewer viewer,
1021 ColumnViewerEditorActivationStrategy editorActivationStrategy,
1023 create(viewer, null, editorActivationStrategy, feature);
1026 protected void setEditor(Control w, Item item, int columnNumber) {
1027 tableEditor.setEditor(w, (TableItem) item, columnNumber);
1030 protected void setLayoutData(LayoutData layoutData) {
1031 tableEditor.grabHorizontal = layoutData.grabHorizontal;
1032 tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
1033 tableEditor.minimumWidth = layoutData.minimumWidth;
1036 public ViewerCell getFocusCell() {
1037 if (focusCellManager != null) {
1038 return focusCellManager.getFocusCell();
1040 return super.getFocusCell();
1043 protected void updateFocusCell(ViewerCell focusCell,
1044 ColumnViewerEditorActivationEvent event) {
1045 // Update the focus cell when we activated the editor with these 2
1047 if (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC
1048 || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL) {
1049 if (focusCellManager != null) {
1051 if (DEBUG)System.err.println("FOCUS CELL: " + focusCell);
1053 Method m = AbstractTableViewer.class.getDeclaredMethod(
1054 "getSelectionFromWidget");
1055 m.setAccessible(true);
1056 @SuppressWarnings("rawtypes")
1057 List l = (List) m.invoke(getViewer());
1058 if (focusCellManager != null) {
1059 m = focusCellManager
1062 .getDeclaredMethod("setFocusCell",
1063 new Class[] { ViewerCell.class });
1064 m.setAccessible(true);
1065 m.invoke(focusCellManager,
1066 new Object[] { focusCell });
1068 if (!l.contains(focusCell.getElement())) {
1069 getViewer().setSelection(
1070 new StructuredSelection(focusCell
1073 } catch (SecurityException e) {
1074 e.printStackTrace();
1075 } catch (IllegalArgumentException e) {
1076 e.printStackTrace();
1077 } catch (IllegalAccessException e) {
1078 e.printStackTrace();
1079 } catch (NoSuchMethodException e) {
1080 e.printStackTrace();
1081 } catch (InvocationTargetException e) {
1082 e.printStackTrace();
1088 protected void processTraverseEvent(int columnIndex, ViewerRow row,
1089 TraverseEvent event) {
1090 ViewerCell cell2edit = null;
1091 if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
1093 if ((event.stateMask & SWT.CTRL) == SWT.CTRL
1094 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
1095 cell2edit = searchCellAboveBelow(row, getViewer(),
1097 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
1098 cell2edit = searchPreviousCell(row,
1099 row.getCell(columnIndex), row.getCell(columnIndex),
1102 } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) {
1104 if ((event.stateMask & SWT.CTRL) == SWT.CTRL
1105 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
1106 cell2edit = searchCellAboveBelow(row, getViewer(),
1107 columnIndex, false);
1108 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
1109 cell2edit = searchNextCell(row, row.getCell(columnIndex),
1110 row.getCell(columnIndex), getViewer());
1113 if (DEBUG) System.err.println("NEXT CELL: " + cell2edit);
1114 if (cell2edit != null) {
1115 getViewer().getControl().setRedraw(false);
1116 ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent(
1119 Method m = ColumnViewer.class
1121 "triggerEditorActivationEvent",
1122 new Class[] { ColumnViewerEditorActivationEvent.class });
1123 m.setAccessible(true);
1124 m.invoke(getViewer(), new Object[] { acEvent });
1125 } catch (SecurityException e) {
1126 e.printStackTrace();
1127 } catch (NoSuchMethodException e) {
1128 e.printStackTrace();
1129 } catch (IllegalArgumentException e) {
1130 e.printStackTrace();
1131 } catch (IllegalAccessException e) {
1132 e.printStackTrace();
1133 } catch (InvocationTargetException e) {
1134 e.printStackTrace();
1136 getViewer().getControl().setRedraw(true);
1140 private ViewerCell searchCellAboveBelow(ViewerRow row,
1141 ColumnViewer viewer, int columnIndex, boolean above) {
1142 ViewerCell rv = null;
1143 ViewerRow newRow = null;
1145 newRow = row.getNeighbor(ViewerRow.ABOVE, false);
1147 newRow = row.getNeighbor(ViewerRow.BELOW, false);
1150 if (newRow != null) {
1151 Method m = ColumnViewer.class.getDeclaredMethod(
1152 "getViewerColumn", new Class[] { int.class });
1153 m.setAccessible(true);
1154 ViewerColumn column = (ViewerColumn) m.invoke(viewer,
1155 new Object[] { new Integer(columnIndex) });
1156 m = ViewerColumn.class.getDeclaredMethod(
1157 "getEditingSupport");
1158 m.setAccessible(true);
1159 EditingSupport es = (EditingSupport) m.invoke(column);
1160 if (column != null && es != null) {
1161 m = EditingSupport.class.getDeclaredMethod("canEdit",
1162 new Class[] { Object.class });
1163 m.setAccessible(true);
1164 Boolean b = (Boolean) m.invoke(es,
1165 new Object[] { newRow.getItem().getData() });
1166 if (b.booleanValue()) {
1167 rv = newRow.getCell(columnIndex);
1170 rv = searchCellAboveBelow(newRow, viewer, columnIndex,
1174 } catch (Exception e) {
1175 e.printStackTrace();
1180 private ViewerCell searchPreviousCell(ViewerRow row,
1181 ViewerCell currentCell, ViewerCell originalCell,
1182 ColumnViewer viewer) {
1183 ViewerCell rv = null;
1184 ViewerCell previousCell;
1185 if (currentCell != null) {
1186 previousCell = getNeighbor(currentCell, ViewerCell.LEFT, true);
1188 if (row.getColumnCount() != 0) {
1189 previousCell = row.getCell(getCreationIndex(row,
1190 row.getColumnCount() - 1));
1192 previousCell = row.getCell(0);
1196 if (originalCell.equals(previousCell)) {
1199 if (previousCell != null) {
1200 if (isCellEditable(viewer, previousCell)) {
1203 rv = searchPreviousCell(row, previousCell, originalCell,
1207 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
1208 rv = searchPreviousCell(row, null, originalCell, viewer);
1209 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
1210 ViewerRow rowAbove = row
1211 .getNeighbor(ViewerRow.ABOVE, false);
1212 if (rowAbove != null) {
1213 rv = searchPreviousCell(rowAbove, null, originalCell,
1221 private ViewerCell searchNextCell(ViewerRow row,
1222 ViewerCell currentCell, ViewerCell originalCell,
1223 ColumnViewer viewer) {
1224 ViewerCell rv = null;
1225 ViewerCell nextCell;
1226 if (currentCell != null) {
1227 nextCell = getNeighbor(currentCell, ViewerCell.RIGHT, true);
1229 nextCell = row.getCell(getCreationIndex(row, 0));
1232 if (originalCell.equals(nextCell)) {
1235 if (nextCell != null) {
1236 if (isCellEditable(viewer, nextCell)) {
1239 rv = searchNextCell(row, nextCell, originalCell, viewer);
1242 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
1243 rv = searchNextCell(row, null, originalCell, viewer);
1244 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
1245 ViewerRow rowBelow = row
1246 .getNeighbor(ViewerRow.BELOW, false);
1247 if (rowBelow != null) {
1248 rv = searchNextCell(rowBelow, null, originalCell,
1256 private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) {
1258 Method m = ColumnViewer.class.getDeclaredMethod(
1259 "getViewerColumn", new Class[] { int.class });
1260 m.setAccessible(true);
1261 ViewerColumn column = (ViewerColumn) m.invoke(viewer,
1262 new Object[] { new Integer(cell.getColumnIndex()) });
1263 m = ViewerColumn.class.getDeclaredMethod("getEditingSupport");
1264 m.setAccessible(true);
1265 EditingSupport es = (EditingSupport) m.invoke(column);
1266 if (column != null && es != null) {
1267 m = EditingSupport.class.getDeclaredMethod("canEdit",
1268 new Class[] { Object.class });
1269 m.setAccessible(true);
1271 Boolean b = (Boolean) m.invoke(es,
1272 new Object[] { cell.getElement() });
1273 return b.booleanValue();
1275 } catch (Exception e) {
1276 e.printStackTrace();
1282 // Reimplementation of ViewerCell-Methods
1283 private static int getVisualIndex(ViewerRow row, int creationIndex) {
1284 TableItem item = (TableItem) row.getItem();
1285 int[] order = item.getParent().getColumnOrder();
1286 for (int i = 0; i < order.length; i++) {
1287 if (order[i] == creationIndex) {
1291 return creationIndex;
1294 private static int getCreationIndex(ViewerRow row, int visualIndex) {
1295 TableItem item = (TableItem) row.getItem();
1296 if (item != null && !item.isDisposed() /*
1297 * && hasColumns() &&
1301 return item.getParent().getColumnOrder()[visualIndex];
1306 private static ViewerCell getCellAtVisualIndex(ViewerRow row,
1308 return getCell(row, getCreationIndex(row, visualIndex));
1311 private static boolean isVisible(ViewerCell cell) {
1312 return getWidth(cell) > 0;
1315 private static int getWidth(ViewerCell cell) {
1316 TableItem item = (TableItem) cell.getViewerRow().getItem();
1317 return item.getParent().getColumn(cell.getColumnIndex()).getWidth();
1320 private static ViewerCell getCell(ViewerRow row, int index) {
1321 return row.getCell(index);