SWT Inline-Editierung von MultiColumn TreeViewers

UweR

Mitglied
Hallo,

ich habe einen TreeViewer (JFace) mit zwei Spalten erstellt, deren Inhalt per Doppelklick mit CellEditors geändert werden kann. Hier ein Beispielcode:

Java:
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;

public class TreeViewerEditingTest {
	
	public static class ModelElement {
		public String text1;
		public String text2;
		public ModelElement parent;
		public List<ModelElement> children = new ArrayList<ModelElement>();
		
		public ModelElement(ModelElement parent, String text1, String text2) {
			this.parent = parent;
			this.text1 = text1;
			this.text2 = text2;
			if (parent != null) parent.children.add(this);
		}
	}
	
	public static class TreeContentProvider implements ITreeContentProvider {
		@Override
		public Object[] getChildren(Object parentElement) {
			return ((ModelElement)parentElement).children.toArray();
		}

		@Override
		public Object getParent(Object element) {
			return ((ModelElement)element).parent;
		}

		@Override
		public boolean hasChildren(Object element) {
			return !((ModelElement)element).children.isEmpty();
		}

		@Override
		public Object[] getElements(Object inputElement) {
			return getChildren(inputElement);
		}

		@Override
		public void dispose() {}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
	}
		
	public static class TreeLabelProvider1 extends ColumnLabelProvider {
		@Override
		public String getText(Object element) {
			return ((ModelElement)element).text1;
		}
	}
		
	public static class TreeLabelProvider2 extends ColumnLabelProvider {
		@Override
		public String getText(Object element) {
			return ((ModelElement)element).text2;
		}
	}
	
	public static class TreeEditingSupport extends EditingSupport {
		CellEditor editor;
		int column;
		
		public TreeEditingSupport(TreeViewer viewer, int column) {
			super(viewer);
			this.column = column;
			editor = new TextCellEditor(viewer.getTree());
		}
		
		@Override
		protected boolean canEdit(Object element) {
			return true;
		}
		
		@Override
		protected CellEditor getCellEditor(Object element) {
			return editor;
		}
		
		@Override
		protected Object getValue(Object element) {
			return (column == 0) ? ((ModelElement)element).text1 : ((ModelElement)element).text2;
		}
		
		@Override
		protected void setValue(Object element, Object value) {
			if (column == 0)
				((ModelElement)element).text1 = (String)value;
			else
				((ModelElement)element).text2 = (String)value;
		}
	}	
	
	private TreeViewer createTreeViewer(Composite parent)
	{
		final TreeViewer treeViewer = new TreeViewer(parent, SWT.BORDER);
		treeViewer.setContentProvider(new TreeContentProvider());
		
		// configure the inner tree
		final Tree featureTree = treeViewer.getTree();
		featureTree.setHeaderVisible(true);
		featureTree.setLinesVisible(true);
	
		// defines when (on which events) the cell editor is opened
		ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(treeViewer) {
            protected boolean isEditorActivationEvent(
                    ColumnViewerEditorActivationEvent event) {
            	MouseEvent mouseEvent = (MouseEvent)event.sourceEvent;
            	boolean singleSelect = ((IStructuredSelection)treeViewer.getSelection()).size() == 1;
            	boolean isLeftMouseDoubleSelect = 
            		event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION &&
            		mouseEvent.button == 1;
            	return singleSelect && (isLeftMouseDoubleSelect
        				|| event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC
        				|| event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL 
        				|| (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR));
            }
        };
		
		TreeViewerEditor.create(treeViewer, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL
                | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
                | ColumnViewerEditor.TABBING_VERTICAL
                | ColumnViewerEditor.KEYBOARD_ACTIVATION);
		
		// create the feature column, which contains the feature tree
		TreeViewerColumn featureColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
		featureColumn.getColumn().setText("Column 1");
		featureColumn.getColumn().setWidth(300);
		featureColumn.setEditingSupport(new TreeEditingSupport(treeViewer, 0));
	    featureColumn.setLabelProvider(new TreeLabelProvider1());
	    
	    // create the count column, which contains the count values of features
	    TreeViewerColumn countColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
	    countColumn.getColumn().setText("Column 2");
	    countColumn.getColumn().setWidth(50);
	    countColumn.setEditingSupport(new TreeEditingSupport(treeViewer, 1));
	    countColumn.setLabelProvider(new TreeLabelProvider2());
	    		
		return treeViewer;
	}

	public static void main(String[] args) {
		TreeViewerEditingTest test = new TreeViewerEditingTest();
		Display display = Display.getDefault();
		Shell shell = new Shell(display);
		shell.setText("TreeViewerEditingTest");
		shell.setLayout(new FillLayout());
		shell.setSize(400, 200);
		TreeViewer viewer = test.createTreeViewer(shell);
		
		ModelElement root = new ModelElement(null, null, null);
		ModelElement top = new ModelElement(root, "Top", "1");
		new ModelElement(top, "Sub1", "2");
		new ModelElement(top, "Sub2", "3");
		new ModelElement(top, "Sub3", "4");
		
		viewer.setInput(root);
		viewer.expandAll();
		
		shell.open();

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}	
	}
}

Nun ist es aber so, dass wenn ich in der zweiten Spalte den Text ändern will, der Editor nur geöffnet wird, wenn die erste Spalte in der gleichen Zeile selektiert ist.

In diesem Snippet steht, dass es nur mit SWT.FULL_SELECTION geht, was aber blöd aussieht, da immer die komplette Zeile markiert ist. Im gleichem Snippet wird versucht die Selektion grafisch zu verhindern, was aber nicht so richtig klappt (die Zeile ist dann komplett leer).

Wie kann ich das Problem lösen?
Gibt es Alternativen zu FULL_SELECTION?

Uwe
 

UweR

Mitglied
Beim Einsatz von FULL_SELECTION verschwinden auch die Linien zwischen Vater- und Kindknoten.
Gibt es dafür eine Erklärung?

Uwe
 

UweR

Mitglied
Ich habe jetzt eine Lösung gefunden, wie man bei MultiColumn-TreeViewers ohne SWT.FULL_SELECTION trotzdem die zweite Spalte editieren kann ohne die entsprechende Item in der ersten Spalte ausgewählt zu haben. Man muss die Methode TreeViewer#getItemAt(Point) überschreiben.

Java:
public class AllColumnsEditableTreeViewer extends TreeViewer {
    
	@Override
	protected Item getItemAt(Point p) {
		Tree tree = getTree();
		TreeItem[] selection = tree.getSelection();
		if( selection.length == 1 ) {
			int columnCount = tree.getColumnCount();

			for( int i = 0; i < columnCount; i++ ) {
				if( selection[0].getBounds(i).contains(p) ) {
					return selection[0];
				}
			}
		}
		
		// TreeItem item = tree.getItem(p); <- this checks only the first column if SWT.FULL_SELECTION is not set
		TreeItem item = searchTreeItem(null, p);
		return item;
	}
	
	private TreeItem searchTreeItem(TreeItem parentItem, Point point) {
		TreeItem[] items = (parentItem == null) ? getTree().getItems() : parentItem.getItems();
		int columnCount = getTree().getColumnCount();
		for (int i = 0; i < items.length; i++) {
			for (int c = 0; c < columnCount; c++) {
				if (items[i].getBounds(c).contains(point))
					return items[i];
			}
			TreeItem foundItem = null;
			if (items[i].getExpanded() == true)
				foundItem = searchTreeItem(items[i], point);
			if (foundItem != null)
				return foundItem;
		}
		return null;
	}
}

Die Original-Methode werden zuerst alle Spalten der selektierten Einträge durchsucht (Zeile 6-15, vom Original übernommen). Wird nichts gefunden wird Tree#getItem(Point) gefragt (Zeile 17, auskommentiert). Diese Methode gibt aber, wenn SWT.FULL_SELECTION nicht gewählt ist, nur bei einem Point in der ersten Spalte ein Ergebnis zurück.

Die Lösung ist es nun nicht den Tree zu fragen, sondern selbst alle Items und dessen Bounds durchzugehen. Dies ist zwar aufwändiger, aber bei kleineren Listen nicht so gravierend. Es gibt hier aber noch Optimierungspotential.

Uwe
 
Zuletzt bearbeitet:

akswiff

Neues Mitglied
Hi und danke für die Beschreibung deiner Lösung!
Man findet Threads mit diesem Problem in vielen Foren, aber dies ist der einzige Eintrag mit Lösung.

Ich habe deinen Code kopiert, allerdings funktioniert nicht alles ganz:
Nachdem man ein Feld editiert hat wird der geänderte Text nicht übernommen. Nach z.B. Enter drücken erscheint wieder der alte Text. Was fehlt da noch?

Viele Grüße,
Alex
 

Ähnliche Java Themen

Neue Themen


Oben