/******************************************************************************* * Copyright (c) 2012, 2013 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g3d.property; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.viewers.AbstractTableViewer; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CellEditor.LayoutData; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.CellNavigationStrategy; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnViewerEditor; import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener; import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TableViewerFocusCellManager; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerColumn; import org.eclipse.jface.viewers.ViewerRow; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.IWorkbenchSite; import org.simantics.db.management.ISessionContext; import org.simantics.g3d.property.annotations.CompoundGetPropertyValue; import org.simantics.g3d.property.annotations.CompoundSetPropertyValue; import org.simantics.g3d.property.annotations.GetPropertyValue; import org.simantics.g3d.property.annotations.PropertyTabBlacklist; import org.simantics.g3d.property.annotations.SetPropertyValue; import org.simantics.g3d.scenegraph.IG3DNode; import org.simantics.g3d.scenegraph.NodeMap; import org.simantics.g3d.scenegraph.NodeMapProvider; import org.simantics.g3d.scenegraph.base.INode; import org.simantics.g3d.scenegraph.base.NodeListener; import org.simantics.g3d.scenegraph.base.ParentNode; import org.simantics.g3d.scenegraph.structural.IStructuralNode; import org.simantics.g3d.tools.AdaptationUtils; import org.simantics.selectionview.IPropertyTab; import org.simantics.selectionview.IPropertyTab2; import org.simantics.utils.datastructures.Callback; import org.simantics.utils.datastructures.MapList; public class AnnotatedPropertyTabContributorFactory implements PropertyTabContributorFactory { private static final boolean DEBUG = false; @SuppressWarnings("unchecked") @Override public List getContributors(Object input) { Map items = new LinkedHashMap(); List blacklist = new ArrayList(); try { collectItems(input.getClass(), items); collectBlacklist(input.getClass(), blacklist); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (items.size() == 0) return Collections.EMPTY_LIST; MapList tabMap = new MapList(); List tabs = new ArrayList(); for (String id : items.keySet()) { IPropertyItem item = items.get(id); tabMap.add(item.getTabId(), item); if (!tabs.contains(item.getTabId())) { tabs.add(item.getTabId()); //System.out.println(item.tabId + " " + item.name + " " + item.id); } } for (String s : blacklist) { tabs.remove(s); } List contributors = new ArrayList(tabs.size()); for (String tabId : tabs) { contributors.add(new AnnotatedPropertyTabContributor(tabId, tabMap.getValues(tabId))); } return contributors; } private static void collectItems(Class clazz, Map items) throws InstantiationException, IllegalAccessException { Class superclass = clazz.getSuperclass(); if(superclass != null) collectItems(superclass, items); for (Method m : clazz.getDeclaredMethods()) { m.setAccessible(true); for (Annotation annotation : m.getAnnotations()) { if (annotation.annotationType().equals(GetPropertyValue.class)) { GetPropertyValue get = (GetPropertyValue)annotation; PropertyItem item = (PropertyItem)items.get(get.value()); if (item == null) { item = new PropertyItem(get.value()); items.put(item.id, item); } item.getter = m; item.manipulatorClass = get.manipulator().newInstance().get(m,null); item.tabId = get.tabId(); item.name = get.name(); } else if (annotation.annotationType().equals(SetPropertyValue.class)) { SetPropertyValue set = (SetPropertyValue)annotation; PropertyItem item = (PropertyItem)items.get(set.value()); if (item == null) { item = new PropertyItem(set.value()); items.put(item.id, item); } item.setter = m; } else if (annotation.annotationType().equals(CompoundGetPropertyValue.class)) { CompoundGetPropertyValue get = (CompoundGetPropertyValue)annotation; CompoundPropertyItem item = (CompoundPropertyItem)items.get(get.value()); if (item == null) { item = new CompoundPropertyItem(get.value()); items.put(item.id, item); } item.getter = m; item.manipulatorFactory = get.manipulator().newInstance(); item.tabId = get.tabId(); item.name = get.name(); } else if (annotation.annotationType().equals(CompoundSetPropertyValue.class)) { CompoundSetPropertyValue set = (CompoundSetPropertyValue)annotation; CompoundPropertyItem item = (CompoundPropertyItem)items.get(set.value()); if (item == null) { item = new CompoundPropertyItem(set.value()); items.put(item.id, item); } item.setter = m; } } } } private static void collectBlacklist(Class clazz, List blacklist) throws InstantiationException, IllegalAccessException { Class superclass = clazz.getSuperclass(); if(superclass != null) collectBlacklist(superclass, blacklist); PropertyTabBlacklist ann = clazz.getAnnotation(PropertyTabBlacklist.class); if (ann == null) return; String s = ann.value(); if (s == null) return; if (s.length() == 0) return; for (String item : s.split(";")) { blacklist.add(item); } } private static Map createManipulators(CompoundPropertyItem item, Object obj) { try { Map map = (Map)item.getter.invoke(obj); Map result = new HashMap(); for (String key : map.keySet()) { MethodWithMapValueProvider provider = new MethodWithMapValueProvider(item.getter, item.setter, key); Class clazz = item.manipulatorFactory.get(null,map.get(key)); PropertyManipulator manipulator = clazz.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj); PropertyItem i = new PropertyItem(item.id+"."+key); i.getter = item.getter; i.setter = item.setter; i.name = key; i.tabId = item.tabId; result.put(i,manipulator); } return result; } catch (Exception e) { e.printStackTrace(); return Collections.EMPTY_MAP; } } private static PropertyManipulator createManipulator(PropertyItem item, Object obj) { try { MethodValueProvider provider = new MethodValueProvider(item.getter, item.setter); PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj); return manipulator; } catch (Exception e) { e.printStackTrace(); return null; } } private static interface IPropertyItem { public String getTabId(); } private static class PropertyItem implements IPropertyItem{ private String id; private String name; private String tabId; private Method getter; private Method setter; private Class manipulatorClass; public PropertyItem(String id) { if (id == null) throw new NullPointerException(); this.id = id; } @Override public int hashCode() { return id.hashCode(); } @Override public String getTabId() { return tabId; } } private static class CompoundPropertyItem implements IPropertyItem{ private String id; private String name; private String tabId; private Method getter; private Method setter; private PropertyManipulatorFactory manipulatorFactory; public CompoundPropertyItem(String id) { if (id == null) throw new NullPointerException(); this.id = id; } @Override public int hashCode() { return id.hashCode(); } @Override public String getTabId() { return tabId; } } private static class AnnotatedPropertyTabContributor implements PropertyTabContributor { private String id; List items; public AnnotatedPropertyTabContributor(String id, List items) { if (id == null) throw new NullPointerException(); this.id = id; this.items = items; } @Override public IPropertyTab create(Composite parent, IWorkbenchSite site, ISessionContext context, Object input) { AnnotatedPropertyTab tab = new AnnotatedPropertyTab(id, items); tab.createControl(parent, context); return tab; } @Override public String getId() { return id; } } private static class AnnotatedPropertyTab implements IPropertyTab2, NodeListener { //private String id; List contibutedItems; List resolvedItems = new ArrayList(); private Map manipulators = new HashMap(); private TableViewer viewer; private IG3DNode node; private NodeMap nodeMap; private List valueColumns = new ArrayList(); public AnnotatedPropertyTab(String id, List items) { //this.id = id; this.contibutedItems = items; } @Override public void createControl(Composite parent, ISessionContext context) { //parent.setLayout(new FillLayout()); viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getTable()); //viewer.setLabelProvider(new AnnotatedTableLabelProvider(object)) viewer.setContentProvider(new PropertyItemContentsProvider()); TableViewerColumn name = new TableViewerColumn(viewer, SWT.LEFT); //TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT); name.setLabelProvider(new PropertyItemNameProvider()); //value.setLabelProvider(new PropertyValueLabelProvider(null)); name.getColumn().setText("Property"); //value.getColumn().setText("Value"); name.getColumn().setWidth(200); //value.getColumn().setWidth(200); name.getViewer().addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(),PropertyItem.class); if (item != null) { PropertyManipulator manipulator = manipulators.get(item);//createManipulator(item, null); for (int i = 0; i < valueColumns.size(); i++) { TableViewerColumn c = valueColumns.get(i); if (i < manipulator.getValueCount()) { c.getColumn().setText(manipulator.getDescription(i)); } else { c.getColumn().setText(""); } } } } }); int valueCount = 0; for (IPropertyItem item : contibutedItems) { if (item instanceof PropertyItem) { PropertyManipulator manipulator = createManipulator((PropertyItem)item, null); if (manipulator == null) continue; if (valueCount < manipulator.getValueCount()) valueCount = manipulator.getValueCount(); } else if (item instanceof CompoundPropertyItem) { if (valueCount < 1) valueCount = 1; } } for (int i = 0; i < valueCount; i++) { TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT); //value.getColumn().setText("Value " + (i+1)); value.getColumn().setText(""); value.getColumn().setWidth(200); valueColumns.add(value); //value.setEditingSupport(new ) } viewer.getTable().setHeaderVisible(true); viewer.getTable().setLinesVisible(true); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(), PropertyItem.class); selectedItem = item; if (!manipulators.get(selectedItem).getEditMode()) manipulators.get(selectedItem).setEditMode(true); for (IPropertyItem i : delayedUpdate) { if (!i.equals(selectedItem)) { manipulators.get(i).setEditMode(false); viewer.update(i,null); } } if (delayedUpdate.contains(selectedItem)) { delayedUpdate.clear(); delayedUpdate.add(selectedItem); } else { delayedUpdate.clear(); } } }); CellNavigationStrategy nStrategy = new CellNavigationStrategy() { private ViewerCell internalFindSelectedCell( ColumnViewer viewer, ViewerCell currentSelectedCell, Event event) { switch (event.keyCode) { case SWT.ARROW_UP: if (currentSelectedCell != null) { return getNeighbor(currentSelectedCell, ViewerCell.ABOVE, false); } break; case SWT.ARROW_DOWN: if (currentSelectedCell != null) { return getNeighbor(currentSelectedCell, ViewerCell.BELOW, false); } break; case SWT.ARROW_LEFT: if (currentSelectedCell != null) { return getNeighbor(currentSelectedCell, ViewerCell.LEFT, true); } break; case SWT.ARROW_RIGHT: if (currentSelectedCell != null) { return getNeighbor(currentSelectedCell, ViewerCell.RIGHT, true); } break; } return null; } public ViewerCell findSelectedCell(ColumnViewer viewer, ViewerCell currentSelectedCell, Event event) { ViewerCell cell = internalFindSelectedCell(viewer, currentSelectedCell, event); if (cell != null) { TableColumn t = AnnotatedPropertyTab.this.viewer.getTable().getColumn( cell.getColumnIndex()); AnnotatedPropertyTab.this.viewer.getTable().showColumn(t); } return cell; } }; TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager( viewer, new FocusCellOwnerDrawHighlighter(viewer)); try { Field f = focusCellManager.getClass().getSuperclass() .getDeclaredField("navigationStrategy"); f.setAccessible(true); f.set(focusCellManager, nStrategy); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy( viewer) { protected boolean isEditorActivationEvent( ColumnViewerEditorActivationEvent event) { return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR) || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC; } }; TableViewerEditor.create(viewer, focusCellManager, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION); viewer.getColumnViewerEditor().addEditorActivationListener( new ColumnViewerEditorActivationListener() { public void afterEditorActivated( ColumnViewerEditorActivationEvent event) { } public void afterEditorDeactivated( ColumnViewerEditorDeactivationEvent event) { } public void beforeEditorActivated( ColumnViewerEditorActivationEvent event) { ViewerCell cell = (ViewerCell) event.getSource(); viewer.getTable().showColumn( viewer.getTable().getColumn(cell.getColumnIndex())); } public void beforeEditorDeactivated( ColumnViewerEditorDeactivationEvent event) { } }); } private IPropertyItem selectedItem = null; private Set delayedUpdate = new HashSet(); @Override public void setInput(ISessionContext context, ISelection selection, boolean force) { Collection nodes = AdaptationUtils.adaptToCollection(selection, IG3DNode.class); if (nodes.size() != 1) { if (node != null) { node.removeListener(this); node = null; } return; } IG3DNode n = nodes.iterator().next(); if (node != null) { if (!node.equals(n)) { node.removeListener(this); setInput(n); } } else { setInput(n); } } private void setInput(IG3DNode node) { this.node = node; this.node.addListener(this); // resolve nodemap IG3DNode n = node; while (true) { if (n == null) { nodeMap = null; break; } if (n instanceof NodeMapProvider) { nodeMap = ((NodeMapProvider) n).getNodeMap(); if (nodeMap != null) break; } n = (IG3DNode)n.getParent(); } boolean readOnly = (node instanceof IStructuralNode && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()); // create label providers PropertyValueLabelProvider2 p = new PropertyValueLabelProvider2(this); int index = 0; for (TableViewerColumn c : valueColumns) { c.setLabelProvider(p); if (!readOnly) { PropertyEditingSupport support = new PropertyEditingSupport(this, viewer, index++, nodeMap); c.setEditingSupport(support); } } resolvedItems.clear(); manipulators.clear(); for (IPropertyItem item : contibutedItems) { if (item instanceof PropertyItem) { resolvedItems.add((PropertyItem)item); manipulators.put((PropertyItem)item, createManipulator((PropertyItem)item, node)); } else { CompoundPropertyItem compound = (CompoundPropertyItem)item; Map manipulators = createManipulators(compound, node); for (PropertyItem i : manipulators.keySet()) { resolvedItems.add(i); this.manipulators.put(i, manipulators.get(i)); } } } viewer.getTable().setEnabled(!readOnly); viewer.setInput(resolvedItems); } @Override public void requestFocus() { viewer.getTable().forceFocus(); } @Override public void dispose() { if (node != null) { node.removeListener(this); node = null; } } @Override public Control getControl() { return viewer.getTable(); } @Override public ISelectionProvider getSelectionProvider() { return null; } @Override public boolean isDisposed() { return viewer.getTable().isDisposed(); } @Override public void nodeAdded(ParentNode node, INode child, String rel) { } @Override public void nodeRemoved(ParentNode node, INode child, String rel) { } @Override public void propertyChanged(INode node, final String id) { // for (final PropertyItem item : items) { // if (item.id.equals(id)) { // Display.getDefault().asyncExec(new Runnable() { // // @Override // public void run() { // viewer.update(item, null); // // } // }); // } // } if (Thread.currentThread() == Display.getDefault().getThread()) { if (viewer.getTable().isDisposed()) return; if (DEBUG)System.out.println("Viewer refresh " + id); for (PropertyItem item : resolvedItems) if (!item.equals(selectedItem)) viewer.refresh(item); if (selectedItem != null) delayedUpdate.add(selectedItem); } else if (!editing){ // running delayed refresh when a cell editor is active would cancel cell editing. Display.getDefault().asyncExec(new Runnable() { @Override public void run() { if (viewer.getTable().isDisposed()) { if (AnnotatedPropertyTab.this.node != null) AnnotatedPropertyTab.this.node.removeListener(AnnotatedPropertyTab.this); return; } if (DEBUG) System.out.println("Viewer threaded refresh " + id); for (PropertyItem item : resolvedItems) if (!item.equals(selectedItem)) viewer.refresh(item); if (selectedItem != null) delayedUpdate.add(selectedItem); } }); } else { for (PropertyItem item : resolvedItems) { delayedUpdate.add(item); } } } @Override public void updatePartName(Consumer updateCallback) { if (node != null) updateCallback.accept(node.toString()); } public PropertyManipulator getManipulator(PropertyItem item) { return manipulators.get(item); } private boolean editing = false; public void setEditing(boolean editing) { this.editing = editing; } } private static class PropertyEditingSupport extends EditingSupport { AnnotatedPropertyTab tab; int index; NodeMap nodeMap; TableViewer viewer; CellEditor editor; public PropertyEditingSupport(AnnotatedPropertyTab tab, TableViewer viewer, int index, NodeMap nodeMap) { super(viewer); this.tab = tab; this.index = index; this.viewer = viewer; this.nodeMap = nodeMap; } @Override protected boolean canEdit(Object element) { PropertyItem item = (PropertyItem)element; if (tab.getManipulator(item).getValueCount() <= index) return false; if (item.setter == null) return false; if (getValue(element) == null) return false; return true; } @Override protected CellEditor getCellEditor(Object element) { if (tab.getManipulator((PropertyItem)element).getValueCount() <= index) return null; if (editor == null) editor = new TextCellEditor(viewer.getTable(),SWT.NONE) { @Override public void activate() { tab.setEditing(true); } @Override public void deactivate() { super.deactivate(); tab.setEditing(false); } }; if (DEBUG)System.err.println("CELL EDITOR: " + element); return editor; } @Override protected Object getValue(Object element) { PropertyItem item = (PropertyItem)element; PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj); if (manipulator.getValueCount() <= index) return null; Object value = manipulator.getValue(index); return value; } @Override protected void setValue(Object element, Object value) { PropertyItem item = (PropertyItem)element; PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj); if (manipulator.getValueCount() <= index) throw new IllegalAccessError("Editing value in index " + index + " is not allowed."); if (DEBUG)System.err.println("CELL SET VALUE: " + element + " " + value); manipulator.setValue((String)value,index); viewer.refresh(item); nodeMap.commit("Set " + item.id + " value to " + value); } } private static class PropertyItemNameProvider extends CellLabelProvider { @Override public void update(ViewerCell cell) { PropertyItem item = (PropertyItem)cell.getElement(); if (item.name.length() > 0) cell.setText(item.name); else cell.setText(item.id); } } private static class PropertyValueLabelProvider2 extends CellLabelProvider { AnnotatedPropertyTab tab; //private Object object; public PropertyValueLabelProvider2(AnnotatedPropertyTab tab) { this.tab = tab; } @Override public void update(ViewerCell cell) { PropertyItem item = (PropertyItem)cell.getElement(); int index = cell.getColumnIndex() -1; PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, object); if (manipulator.getValueCount() <= index) return; cell.setText(manipulator.getValue(index)); } } private static class PropertyItemContentsProvider implements IStructuredContentProvider { @SuppressWarnings("unchecked") @Override public Object[] getElements(Object inputElement) { List items = (List)inputElement; return items.toArray(); } @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } private static ViewerCell getNeighbor(ViewerCell currentCell, int directionMask, boolean sameLevel) { ViewerRow row; if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) { row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE, sameLevel); } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) { row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW, sameLevel); } else { row = currentCell.getViewerRow(); } if (row != null) { int columnIndex; columnIndex = getVisualIndex(row, currentCell.getColumnIndex()); int modifier = 0; if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) { modifier = -1; } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) { modifier = 1; } columnIndex += modifier; if (columnIndex >= 0 && columnIndex < row.getColumnCount()) { ViewerCell cell = getCellAtVisualIndex(row, columnIndex); if (cell != null) { while (cell != null && columnIndex < row.getColumnCount() - 1 && columnIndex > 0) { if (isVisible(cell)) { break; } columnIndex += modifier; cell = getCellAtVisualIndex(row, columnIndex); if (cell == null) { break; } } } return cell; } } return null; } public static class TableViewerEditor extends ColumnViewerEditor { /** * This viewer's table editor. */ private TableEditor tableEditor; private TableViewerFocusCellManager focusCellManager; private int feature; /** * @param viewer * the viewer the editor is attached to * @param focusCellManager * the cell focus manager if one used or null * @param editorActivationStrategy * the strategy used to decide about the editor activation * @param feature * the feature mask */ TableViewerEditor(TableViewer viewer, TableViewerFocusCellManager focusCellManager, ColumnViewerEditorActivationStrategy editorActivationStrategy, int feature) { super(viewer, editorActivationStrategy, feature); this.feature = feature; tableEditor = new TableEditor(viewer.getTable()); this.focusCellManager = focusCellManager; } /** * Create a customized editor with focusable cells * * @param viewer * the viewer the editor is created for * @param focusCellManager * the cell focus manager if one needed else * null * @param editorActivationStrategy * activation strategy to control if an editor activated * @param feature * bit mask controlling the editor *
    *
  • {@link ColumnViewerEditor#DEFAULT}
  • *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • *
  • * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • *
* @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int) */ public static void create(TableViewer viewer, TableViewerFocusCellManager focusCellManager, ColumnViewerEditorActivationStrategy editorActivationStrategy, int feature) { TableViewerEditor editor = new TableViewerEditor(viewer, focusCellManager, editorActivationStrategy, feature); viewer.setColumnViewerEditor(editor); if (focusCellManager != null) { try { Method m = focusCellManager.getClass().getSuperclass() .getDeclaredMethod("init", null); m.setAccessible(true); m.invoke(focusCellManager, null); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // focusCellManager.init(); } } /** * Create a customized editor whose activation process is customized * * @param viewer * the viewer the editor is created for * @param editorActivationStrategy * activation strategy to control if an editor activated * @param feature * bit mask controlling the editor *
    *
  • {@link ColumnViewerEditor#DEFAULT}
  • *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • *
  • * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • *
*/ public static void create(TableViewer viewer, ColumnViewerEditorActivationStrategy editorActivationStrategy, int feature) { create(viewer, null, editorActivationStrategy, feature); } protected void setEditor(Control w, Item item, int columnNumber) { tableEditor.setEditor(w, (TableItem) item, columnNumber); } protected void setLayoutData(LayoutData layoutData) { tableEditor.grabHorizontal = layoutData.grabHorizontal; tableEditor.horizontalAlignment = layoutData.horizontalAlignment; tableEditor.minimumWidth = layoutData.minimumWidth; } public ViewerCell getFocusCell() { if (focusCellManager != null) { return focusCellManager.getFocusCell(); } return super.getFocusCell(); } protected void updateFocusCell(ViewerCell focusCell, ColumnViewerEditorActivationEvent event) { // Update the focus cell when we activated the editor with these 2 // events if (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL) { if (focusCellManager != null) { try { if (DEBUG)System.err.println("FOCUS CELL: " + focusCell); Method m = AbstractTableViewer.class.getDeclaredMethod( "getSelectionFromWidget", null); m.setAccessible(true); List l = (List) m.invoke(getViewer(), null); if (focusCellManager != null) { m = focusCellManager .getClass() .getSuperclass() .getDeclaredMethod("setFocusCell", new Class[] { ViewerCell.class }); m.setAccessible(true); m.invoke(focusCellManager, new Object[] { focusCell }); } if (!l.contains(focusCell.getElement())) { getViewer().setSelection( new StructuredSelection(focusCell .getElement())); } } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } protected void processTraverseEvent(int columnIndex, ViewerRow row, TraverseEvent event) { ViewerCell cell2edit = null; if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) { event.doit = false; if ((event.stateMask & SWT.CTRL) == SWT.CTRL && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { cell2edit = searchCellAboveBelow(row, getViewer(), columnIndex, true); } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { cell2edit = searchPreviousCell(row, row.getCell(columnIndex), row.getCell(columnIndex), getViewer()); } } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) { event.doit = false; if ((event.stateMask & SWT.CTRL) == SWT.CTRL && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { cell2edit = searchCellAboveBelow(row, getViewer(), columnIndex, false); } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { cell2edit = searchNextCell(row, row.getCell(columnIndex), row.getCell(columnIndex), getViewer()); } } if (DEBUG) System.err.println("NEXT CELL: " + cell2edit); if (cell2edit != null) { getViewer().getControl().setRedraw(false); ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent( cell2edit, event); try { Method m = ColumnViewer.class .getDeclaredMethod( "triggerEditorActivationEvent", new Class[] { ColumnViewerEditorActivationEvent.class }); m.setAccessible(true); m.invoke(getViewer(), new Object[] { acEvent }); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } getViewer().getControl().setRedraw(true); } } private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer, int columnIndex, boolean above) { ViewerCell rv = null; ViewerRow newRow = null; if (above) { newRow = row.getNeighbor(ViewerRow.ABOVE, false); } else { newRow = row.getNeighbor(ViewerRow.BELOW, false); } try { if (newRow != null) { Method m = ColumnViewer.class.getDeclaredMethod( "getViewerColumn", new Class[] { int.class }); m.setAccessible(true); ViewerColumn column = (ViewerColumn) m.invoke(viewer, new Object[] { new Integer(columnIndex) }); m = ViewerColumn.class.getDeclaredMethod( "getEditingSupport", null); m.setAccessible(true); EditingSupport es = (EditingSupport) m.invoke(column, null); if (column != null && es != null) { m = EditingSupport.class.getDeclaredMethod("canEdit", new Class[] { Object.class }); m.setAccessible(true); Boolean b = (Boolean) m.invoke(es, new Object[] { newRow.getItem().getData() }); if (b.booleanValue()) { rv = newRow.getCell(columnIndex); } } else { rv = searchCellAboveBelow(newRow, viewer, columnIndex, above); } } } catch (Exception e) { e.printStackTrace(); } return rv; } private ViewerCell searchPreviousCell(ViewerRow row, ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) { ViewerCell rv = null; ViewerCell previousCell; if (currentCell != null) { previousCell = getNeighbor(currentCell, ViewerCell.LEFT, true); } else { if (row.getColumnCount() != 0) { previousCell = row.getCell(getCreationIndex(row, row.getColumnCount() - 1)); } else { previousCell = row.getCell(0); } } // No endless loop if (originalCell.equals(previousCell)) { return null; } if (previousCell != null) { if (isCellEditable(viewer, previousCell)) { rv = previousCell; } else { rv = searchPreviousCell(row, previousCell, originalCell, viewer); } } else { if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { rv = searchPreviousCell(row, null, originalCell, viewer); } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { ViewerRow rowAbove = row .getNeighbor(ViewerRow.ABOVE, false); if (rowAbove != null) { rv = searchPreviousCell(rowAbove, null, originalCell, viewer); } } } return rv; } private ViewerCell searchNextCell(ViewerRow row, ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) { ViewerCell rv = null; ViewerCell nextCell; if (currentCell != null) { nextCell = getNeighbor(currentCell, ViewerCell.RIGHT, true); } else { nextCell = row.getCell(getCreationIndex(row, 0)); } // No endless loop if (originalCell.equals(nextCell)) { return null; } if (nextCell != null) { if (isCellEditable(viewer, nextCell)) { rv = nextCell; } else { rv = searchNextCell(row, nextCell, originalCell, viewer); } } else { if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { rv = searchNextCell(row, null, originalCell, viewer); } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { ViewerRow rowBelow = row .getNeighbor(ViewerRow.BELOW, false); if (rowBelow != null) { rv = searchNextCell(rowBelow, null, originalCell, viewer); } } } return rv; } private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) { try { Method m = ColumnViewer.class.getDeclaredMethod( "getViewerColumn", new Class[] { int.class }); m.setAccessible(true); ViewerColumn column = (ViewerColumn) m.invoke(viewer, new Object[] { new Integer(cell.getColumnIndex()) }); m = ViewerColumn.class.getDeclaredMethod("getEditingSupport", null); m.setAccessible(true); EditingSupport es = (EditingSupport) m.invoke(column, null); if (column != null && es != null) { m = EditingSupport.class.getDeclaredMethod("canEdit", new Class[] { Object.class }); m.setAccessible(true); // return true; Boolean b = (Boolean) m.invoke(es, new Object[] { cell.getElement() }); return b.booleanValue(); } } catch (Exception e) { e.printStackTrace(); } return false; } } // Reimplementation of ViewerCell-Methods private static int getVisualIndex(ViewerRow row, int creationIndex) { TableItem item = (TableItem) row.getItem(); int[] order = item.getParent().getColumnOrder(); for (int i = 0; i < order.length; i++) { if (order[i] == creationIndex) { return i; } } return creationIndex; } private static int getCreationIndex(ViewerRow row, int visualIndex) { TableItem item = (TableItem) row.getItem(); if (item != null && !item.isDisposed() /* * && hasColumns() && * isValidOrderIndex * (visualIndex) */) { return item.getParent().getColumnOrder()[visualIndex]; } return visualIndex; } private static ViewerCell getCellAtVisualIndex(ViewerRow row, int visualIndex) { return getCell(row, getCreationIndex(row, visualIndex)); } private static boolean isVisible(ViewerCell cell) { return getWidth(cell) > 0; } private static int getWidth(ViewerCell cell) { TableItem item = (TableItem) cell.getViewerRow().getItem(); return item.getParent().getColumn(cell.getColumnIndex()).getWidth(); } private static ViewerCell getCell(ViewerRow row, int index) { return row.getCell(index); } }