4

I am trying to set up a multi-column SWT Tree (via a JFace TreeViewer) that allows its columns to be re-ordered by the user by dragging the column headers. However, the first column should remain fixed and unmoveable. I can use the setMoveable(true) method on each TreeColumn except the first to get real close, but that doesn't prevent the user dragging a different column to the left of it. Any ideas how to resolve this?

Here's a simplified working snippet (without JFace) to illustrate the problem:

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

public class TreeTableColumnMove {

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new RowLayout(SWT.HORIZONTAL));
        Tree tree = new Tree(shell, SWT.BORDER | SWT.CHECK);
        tree.setLayoutData(new RowData(-1, 100));
        tree.setHeaderVisible(true);
        for (int i = 0; i < 5; i++) {
            TreeColumn column = new TreeColumn(tree, SWT.CENTER);
            column.setText("Col " + i);
            column.setWidth(60);
            if (i > 0) column.setMoveable(true);
        }
        TreeItem item = new TreeItem(tree, SWT.NONE);
        item.setText(new String[] { "c0", "c1", "c2", "c3", "c4" });
        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }
}
flavio.donze
  • 7,432
  • 9
  • 58
  • 91
Dave Hartnoll
  • 1,144
  • 11
  • 21

2 Answers2

4

Here is my final solution based on danmec's answer:

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

    public class TreeTableColumnMove {

        public static void main(String[] args) {
            Display display = new Display();
            Shell shell = new Shell(display);
            shell.setLayout(new RowLayout(SWT.HORIZONTAL));
            final Tree tree = new Tree(shell, SWT.BORDER | SWT.CHECK);
            tree.setLayoutData(new RowData(-1, 100));
            tree.setHeaderVisible(true);
            Listener moveListener = new Listener() {
                public void handleEvent(Event e) {
                    int[] order = tree.getColumnOrder();
                    for(int i=0, col=0; i<order.length; i++) {
                        int tmp = order[i];
                        order[i] = col;
                        col = tmp;
                        if(col == 0)
                            if(i == 0) return;
                            else break;
                    }
                    tree.setColumnOrder(order);
                }
            };

            for (int i = 0; i < 5; i++) {
                TreeColumn column = new TreeColumn(tree, SWT.CENTER);
                column.setText("Col " + i);
                column.setWidth(60);
                if (i == 0)                 
                    column.addListener(SWT.Move, moveListener);
                else
                    column.setMoveable(true);
            }
            TreeItem item = new TreeItem(tree, SWT.NONE);
            item.setText(new String[] { "c0", "c1", "c2", "c3", "c4" });
            shell.pack();
            shell.open();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch())
                    display.sleep();
            }
            display.dispose();
        }
    }
Dave Hartnoll
  • 1,144
  • 11
  • 21
  • Dave, I am using TableViewerColumn and TableLAyout and want to achieve the same thing. Any idea how can I control column order there? – user523956 Nov 07 '14 at 08:38
  • Sorry, I'm not familiar enough with JFace/SWT to know offhand whether this solution can be adapted. I suggest you post a separate question and perhaps link to this one. – Dave Hartnoll Nov 07 '14 at 12:40
2

I don't know if it's possible to actually prevent it, but you can handle the result of the column moving, and react to it to achieve what you want.

Maybe something along these lines would be a good starting point? Essentially it listens for the first column being moved (not directly, but being shifted as a result of another column being moved to the left of it), and then re-shuffles the order so it is the first column visually again.

Note that regardless of the visual order, the first column created will always be zero (and so on). The method tree.getColumnOrder() will be ordered visually, with the values being the created order indexes (so if you swap the 2nd and 3rd columns, the method will return [0, 2, 1, 3, 4])

for (int i = 0; i < 5; i++) {
    TreeColumn column = new TreeColumn(tree, SWT.CENTER);
    column.setText("Col " + i);
    column.setWidth(60);
    if (i > 0) column.setMoveable(true);

    // NEW CODE HERE
    if (i == 0) {

        column.addControlListener(new ControlAdapter() {

            public void controlMoved(ControlEvent e) {

                List<Integer> newOrder = new ArrayList<Integer>();

                // add the first
                newOrder.add(0);

                // loop around all columns
                for (int col : tree.getColumnOrder()) {

                    // if not the first that was added, add to list
                    if (col != 0) {
                        newOrder.add(col);
                    }
                }

                // convert list to array
                int[] newOrderArray = new int[newOrder.size()];
                for (int j = 0; j < newOrder.size(); j++) {
                    newOrderArray[j] = newOrder.get(j);
                }

                // update tree order
                tree.setColumnOrder(newOrderArray);
            }
        });
    }
}
mecsco
  • 2,250
  • 1
  • 15
  • 28
  • Thanks for this suggestion. I'm a little disappointed that there is apparently no (simple) way to prevent the drag+drop of the column to an initially invalid location, especially on the Mac as the columns are visually re-ordered during the drag operation. I've gone with a slightly more compact variation of your idea which I'll post as a separate answer. – Dave Hartnoll Sep 19 '11 at 09:31