How to find index of SWT table column?
Asked Answered
I

5

5

I have a SWT table which has zero rows and 6 columns.

When I right click on any one of the table header, how can I calculate the index of the table column that is clicked?

Intelligencer answered 25/6, 2013 at 5:16 Comment(0)
S
3

Here is some code that does what you want:

private static Map<TableColumn, Integer> mapping = new HashMap<TableColumn, Integer>();

public static void main(String[] args)
{
    Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setText("StackOverflow");
    shell.setLayout(new FillLayout());

    Listener listener = new Listener()
    {
        @Override
        public void handleEvent(Event arg0)
        {
            TableColumn column = (TableColumn) arg0.widget;

            System.out.println(mapping.get(column));
        }
    };

    Table table = new Table(shell, SWT.NONE);
    table.setHeaderVisible(true);

    for(int i = 0; i < 5; i++)
    {
        TableColumn column = new TableColumn(table, SWT.NONE);
        column.setText("Column " + i);
        column.addListener(SWT.Selection, listener);
        column.pack();

        mapping.put(column, i);
    }

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

It basically generates a Map storing the TableColumn as key and the position as the value.

Alternatively you could iterate over all the columns of the Table within the Listener and compare them to the clicked column. The first approach (Map) is faster but uses more memory, whereas the second approach (iteration) is slower but uses less memmory.

Sapindaceous answered 25/6, 2013 at 6:33 Comment(5)
Thanks a lot for nice snippet:) This is working fine while column selection but not while column right click. I tried modifying SWT.Selection to SWT.MouseDown with no luck. any input?Intelligencer
@Intelligencer There is a quite old bug report here that suggests that the TableColumn header does not support right clicks.Sapindaceous
@Intelligencer However, if you are using a right-click menu, you can have a look at this code example and use SWT.MenuDetect.Sapindaceous
Actually i am using IMenuListener->menuAboutToShow() for adding other actions while right clicking table header.Intelligencer
@Intelligencer Can't you get the TableColumn the user clicked on from the Listener somehow?Sapindaceous
I
4

I did write a CustomTable for TableHeader Menu on Right click sometime back.

This code helps you in detecting the TableColumn on right click on table header. But, this code breaks when the column order is changed. But you can fix it comparing the indices of reordered column order vs original column order.

addListener(SWT.MenuDetect, new Listener() {
        @Override
        public void handleEvent(Event e) {
            Point pt = getShell().getDisplay().map(null, CustomTable.this, new Point(e.x, e.y));
            Rectangle clientArea = CustomTable.this.getClientArea();
            boolean header = clientArea.y <= pt.y && pt.y < (clientArea.y + CustomTable.this.getHeaderHeight());
            //code to calculate column of Right click - START
            int width = 0;
            for(int i = 0; i< CustomTable.this.getColumns().length; i++){
                TableColumn tc = CustomTable.this.getColumns()[i];
                if(width < pt.x  &&  pt.x < width + tc.getWidth()){
                    System.out.println("Right Click on " + tc.getText());
                }
                width += tc.getWidth();
            }
            //code to calculate column of Right click - END
            if (header) {
                if(tableMenu != null){
                    tableMenu.setVisible(false);
                }
                CustomTable.super.setMenu(headerMenu);
                headerMenu.setLocation(e.x, e.y);
                headerMenu.setVisible(true);
                e.doit = false;
            } else {
                headerMenu.setVisible(false);
                CustomTable.super.setMenu(tableMenu);
                if(tableMenu != null){
                    tableMenu.setLocation(e.x, e.y);
                    tableMenu.setVisible(true);
                }
            }
        }
    });
Irksome answered 25/6, 2013 at 22:17 Comment(3)
Will this code work if the table control has a horizontal scrollbar which is not positioned at the beginning?Podvin
@MikeNakis It should, as headerHeight and clientArea are calculated w.r.t scrollBars, if present. Give it a try.Irksome
I do not think clientArea is calculated w.r.t scrollbars, and in any case, you are not using clientArea.x in your calculations to find the column, so it seems to me that it would not work even if clientArea was affected by scroll. Since you already have the code, which presumably is part of some app you have written, why don't you try it and tell us?Podvin
S
3

Here is some code that does what you want:

private static Map<TableColumn, Integer> mapping = new HashMap<TableColumn, Integer>();

public static void main(String[] args)
{
    Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setText("StackOverflow");
    shell.setLayout(new FillLayout());

    Listener listener = new Listener()
    {
        @Override
        public void handleEvent(Event arg0)
        {
            TableColumn column = (TableColumn) arg0.widget;

            System.out.println(mapping.get(column));
        }
    };

    Table table = new Table(shell, SWT.NONE);
    table.setHeaderVisible(true);

    for(int i = 0; i < 5; i++)
    {
        TableColumn column = new TableColumn(table, SWT.NONE);
        column.setText("Column " + i);
        column.addListener(SWT.Selection, listener);
        column.pack();

        mapping.put(column, i);
    }

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

It basically generates a Map storing the TableColumn as key and the position as the value.

Alternatively you could iterate over all the columns of the Table within the Listener and compare them to the clicked column. The first approach (Map) is faster but uses more memory, whereas the second approach (iteration) is slower but uses less memmory.

Sapindaceous answered 25/6, 2013 at 6:33 Comment(5)
Thanks a lot for nice snippet:) This is working fine while column selection but not while column right click. I tried modifying SWT.Selection to SWT.MouseDown with no luck. any input?Intelligencer
@Intelligencer There is a quite old bug report here that suggests that the TableColumn header does not support right clicks.Sapindaceous
@Intelligencer However, if you are using a right-click menu, you can have a look at this code example and use SWT.MenuDetect.Sapindaceous
Actually i am using IMenuListener->menuAboutToShow() for adding other actions while right clicking table header.Intelligencer
@Intelligencer Can't you get the TableColumn the user clicked on from the Listener somehow?Sapindaceous
E
1

Similar to the above example, but without using a map to store indices:

...
column.addSelectionListener(getSelectionAdapter1(tbl.getColumnCount()-1));    
...


private SelectionAdapter getSelectionAdapter1(final int index) {
    SelectionAdapter selectionAdapter = new SelectionAdapter() {
        @Override
        public void widgetSelected(SelectionEvent e) {
            System.out.println(index);
        }
    };
    return selectionAdapter;
}
Electrolysis answered 25/6, 2013 at 9:45 Comment(1)
Please note, this will only work if the columns weren't moved. As soon as the columns are moved, you'll have to use table.getColumnOrder() to determine the index of a column.Electrolysis
G
0

in the tablecolumn definitions:

int index = 0;
TableColumn column1 = new TableColumn(table, SWT.NONE);
column1.setText("Column 1 header");
column1.setData(index);
index++;
TableColumn column2 = new TableColumn(table, SWT.NONE);
column2.setText("Column 2 header");
column2.setData(index);
index++;

etc.

in the handler:

int index = (int) ((TableColumn) e.widget).getData();
Goatskin answered 18/4, 2016 at 12:25 Comment(0)
E
0

here is another take. It definitively reacts on right click, works for headers and ordinary rows alike, empty tables and full tables alike, using just a stock SWT Table.

This approach uses combination of SWT.MenuDetect listener and "fake TableItem", created and disposed just for identifying column at the given position. I haven't noticed any undesirable visual side effect, like flickering or flash appearance. It is probably due to to the fake item being disposed inside event processing, before rest of the UI could take notice of its existence.

The SWT.MenuDetect listener reacts on right-click mouse event. Not very intuitive; name suggests it was intended mainly to regulate context menu appearance, but it does its job also as a general right click listener. It is emitted also by table header, unlike SWT.MouseDown. Good! Unfortunately, unlike SWT.Selection the e.widget references whole Table, not just column or cell, so is useless for our purposes as it is used in Baz response. The column has to he detected by using a low level approach with physical coordinates.

Here is the code:

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

/**
 * Table with right click detection that works for table headers and empty
 * tables. It was tested as working for columns reordering, moving or resizing.
 * 
 * @author Espinosa
 */
public class TableWithRightClickDetection {

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Table with right click detection");
        shell.setLayout(new FillLayout());
        shell.setSize(400, 300);

        final Table table = new Table(shell, SWT.BORDER | SWT.V_SCROLL | SWT.FULL_SELECTION);
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        int columnCount = 4;
        for (int i = 0; i < columnCount; i++) {
            TableColumn column = new TableColumn(table, SWT.NONE);
            column.setText("Column " + i);
            column.setMoveable(true);
            column.setResizable(true);
            table.getColumn(i).pack();
        }

        table.addListener(SWT.MenuDetect, (e) -> {
            // SWT.MenuDetect reacts on right-click
            // It is emitted by table header, unlike SWT.MouseDown.
            // Also unlike SWT.Selection the e.widget references whole 
            // Table, not just column or cell, unfortunately, so this has 
            // to be detected using low level approach with physical coordinates.
            Point ptAbsolute = new Point(e.x, e.y);
            Point pt = table.toControl(ptAbsolute); // get position relative to the Tree widget
            int colIndex = columnAtPoint(table, pt);
            if (colIndex >= 0) {
                if (pt.y < table.getHeaderHeight())  {  
                    // for Tree/TreeViews negative Y means table header
                    System.out.println("Header right-clicked on column " + colIndex);
                } else {
                    System.out.println("Row right-clicked on column " + colIndex);
                }
            }
            // prevent showing context menu (if there is any declared)
            e.doit = false;
        });

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

    /**
     * @return column index for given coordinates relative to the given Table widget
     */
    private static int columnAtPoint(Table table, Point pt) {
        int colIndex = -1;
        // The only way how to get column bounds is to get TableItem
        // But empty table hasn't got any and headers does not count.
        // The only way is to temporarily create one and then immediately dispose.
        TableItem fakeRow = new TableItem(table, 0);
        for (int i = 0; i < table.getColumnCount(); i++) {
            Rectangle rec = fakeRow.getBounds(i);
            // It is safer to use X coordinate comparison directly rather then 
            // rec.contains(pt)
            // This way also works for Tree/TreeViews.
            if ((pt.x > rec.x)  && (pt.x < (rec.x + rec.width))) {
                colIndex = i;
            }
        }
        fakeRow.dispose();
        // Not the most efficient way. Rectangles obtained from "fake row" can be cached 
        // and recomputed on column resizes, moves and swaps.
        return colIndex;
    }
}

screenshot from the example app showing also console output from it

Emad answered 5/5, 2017 at 2:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.