How to add Checkbox to table column Header in SWT Java?

32 views Asked by At

In my application, I have a Virtual Table, in that table I have number of columns which is used to list available documents in a Directory, in that table 1st column has checkbox, where we can select any document, now what I need to do is I have to add a checkbox to its header along with header label named "Select All", so when user click that checkbox it will select all the checkbox below, I have no Idea how can I achieve this.

Also, I have a problem with this virtual Table, I have tried implementing select all be clicking that header itself, but when I do it with large set of documents, and when I scroll below, some items were missing in that table random position but its check box was selected, how can I fix this? Any help would be helpful, Thanks in Advance!

This is my code for my VirtualTableViewer Class

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import net.sourceforge.docfetcher.util.Event;
import net.sourceforge.docfetcher.util.Util;
import net.sourceforge.docfetcher.util.annotations.Immutable;
import net.sourceforge.docfetcher.util.annotations.MutableCopy;
import net.sourceforge.docfetcher.util.annotations.NotNull;
import net.sourceforge.docfetcher.util.annotations.Nullable;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;


public abstract class VirtualTableViewer<E> {
    
    public static abstract class Column<E> {
        private String label;
        private final int orientation;
        private final Event<String> evtLabelChanged = new Event<String> ();
        private int lastSortDirection = 1;
        
        public Column(@NotNull String label) {
            this(label, SWT.LEFT);
        }
        public Column(@NotNull String label, int orientation) {
            this.label = Util.checkNotNull(label);
            this.orientation = orientation;
        }
        @NotNull
        public final String getLabel() {
            return label;
        }
        public final void setLabel(@NotNull String label) {
            Util.checkNotNull(label);
            if (this.label.equals(label)) return;
            this.label = label;
            evtLabelChanged.fire(label);
        }
        
        @NotNull protected abstract String getLabel(E element);
        @Nullable protected String getToolTipText() { return null; }
        @Nullable protected Image getImage(E element) { return null; }
        @Nullable protected Color getForeground(E element) { return null; }
        @Nullable protected Color getBackground(E element) { return null; }
        protected int compare(@NotNull E e1, @NotNull E e2) { return 0; }
    }
    
    private final Table table;
    private final List<Column<E>> columns = new ArrayList<Column<E>>();
    private List<E> elements;
    private boolean sortingEnabled = false;
    @Nullable private Column<E> lastSortColumn = null;
    
    public VirtualTableViewer(@NotNull Composite parent, int style) {
        table = new Table(parent, style | SWT.VIRTUAL);
        table.setHeaderVisible(true);
        
        table.addListener(SWT.SetData, new Listener() {
            public void handleEvent(org.eclipse.swt.widgets.Event event) {
                // Bug #3523251: event.index can be -1 sometimes, looks like a
                // bug in SWT.
                // Bug #1298 and others: Sometimes event.index == elements.size().
                if (event.index < 0 || event.index >= elements.size())
                    return;
                Util.checkThat(!columns.isEmpty());
                TableItem item = (TableItem) event.item;
                E element = elements.get(event.index);
                for (int iCol = 0; iCol < columns.size(); iCol++) {
                    Column<E> column = columns.get(iCol);
                    item.setText(iCol, column.getLabel(element));
                    item.setImage(iCol, column.getImage(element));
                    item.setForeground(iCol, column.getForeground(element));
                    item.setBackground(iCol, column.getBackground(element));
                }
                item.setData(element);
            }
        });
    }
    
    @NotNull
    public final Table getControl() {
        return table;
    }

    public final void addColumn(@NotNull final Column<E> column) {
        Util.checkNotNull(column);
        columns.add(column);

        final TableColumn tableColumn = new TableColumn(table, column.orientation);
        if (columns.size() == 1) { // Add checkbox only for the first column
            // Set custom renderer for the first column's header
            tableColumn.setData("checkbox", true);
        } else {
            // Set text and tooltip for regular columns
            tableColumn.setText(column.label);
            tableColumn.setToolTipText(column.getToolTipText());
        }

        column.evtLabelChanged.add(new Event.Listener<String>() {
            public void update(String eventData) {
                tableColumn.setText(eventData);
            }
        });

        tableColumn.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                sortByColumn(column);
            }
        });
    }


    public final void sortByColumn(@NotNull final Column<E> column) {
        if (elements == null || !sortingEnabled)
            return;
        final int direction = lastSortColumn != column
            ? 1
            : column.lastSortDirection * -1;
        Collections.sort(elements, new Comparator<E>() {
            public int compare(E e1, E e2) {
                return column.compare(e1, e2) * direction;
            };
        });
        table.clearAll();
        lastSortColumn = column;
        column.lastSortDirection = direction;
    }
    
    public final void sortByColumn(@NotNull final Column<E> column, boolean up) {
        if (elements == null || !sortingEnabled)
            return;
        final int direction = up ? 1 : -1;
        Collections.sort(elements, new Comparator<E>() {
            public int compare(E e1, E e2) {
                return column.compare(e1, e2) * direction;
            };
        });
        table.clearAll();
        lastSortColumn = column;
        column.lastSortDirection = direction;
    }
    
    @Immutable
    @NotNull
    public final List<Column<E>> getColumns() {
        return Collections.unmodifiableList(columns);
    }
    
    @Immutable
    @NotNull
    public final List<Column<E>> getColumnsVisualOrder() {
        List<Column<E>> visualColumns = new ArrayList<Column<E>>(columns.size());
        for (int index : table.getColumnOrder())
            visualColumns.add(columns.get(index));
        return Collections.unmodifiableList(visualColumns);
    }
    
    // does not take sorting into account
    public final void setRoot(@NotNull Object rootElement) {
        Util.checkNotNull(rootElement);
        Util.checkThat(!columns.isEmpty());
        elements = Util.checkNotNull(getElements(rootElement));
        table.setItemCount(elements.size()); // Must be called *before* calling clearAll()
        table.clearAll();
        lastSortColumn = null;
    }
    
    @MutableCopy
    @NotNull
    public final List<E> getSelection() {
        /* Note that we must use table.getSelectionIndices here, rather than
         * table.getSelection, since on a virtual table some TableItems may
         * still be uninitialized. */
        int[] selIndices = table.getSelectionIndices();
        List<E> selElements = new ArrayList<E>(selIndices.length);
        for (int index : selIndices) {
            selElements.add(elements.get(index));
        }
        return selElements;
    }
    
    public final void scrollToTop() {
        ScrollBar verticalBar = table.getVerticalBar();
        if (verticalBar != null)
            verticalBar.setSelection(0);
    }
    
    public final void scrollToBottom() {
        TableItem lastItem = table.getItem(table.getItemCount() - 1);
        table.showItem(lastItem);
    }
    
    // when sorting is enabled, override the compare method on all sortable columns
    public void setSortingEnabled(boolean sortingEnabled) {
        this.sortingEnabled = sortingEnabled;
        if (!sortingEnabled)
            lastSortColumn = null;
    }
    
    @NotNull
    protected abstract List<E> getElements(@NotNull Object rootElement);

}

And this is how I add column to the table:

         viewer.addColumn(new Column<ResultDocument>(Msg.select.get(), SWT.CENTER) {
                protected String getLabel(ResultDocument element) {
                    return "";
                }
                
                protected int compare(ResultDocument e1, ResultDocument e2) {
                    // This column doesn't participate in sorting
                    return 0;
                }
                
            });
     
        
        viewer.addColumn(new VariableHeaderColumn<ResultDocument>(Msg.title.get(), Msg.subject.get()) {
            protected String getLabel(ResultDocument element) {
                return element.getTitle();
            }
            protected Image getImage(ResultDocument element) {
                if (element.isEmail())
                    return Img.EMAIL.get();
                return iconCache.getIcon(element.getFilename(), Img.FILE.get());
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return compareAlphanum(e1.getTitle(), e2.getTitle());
            }
        });
        
        viewer.addColumn(new Column<ResultDocument>(Msg.score.get(), SWT.RIGHT) {
            protected String getLabel(ResultDocument element) {
                return String.valueOf(element.getScore());
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return -1 * Float.compare(e1.getScore(), e2.getScore());
            }
        });
        
        viewer.addColumn(new Column<ResultDocument>(Msg.size.get(), SWT.RIGHT) {
            protected String getLabel(ResultDocument element) {
                return String.format("%,d KB", element.getSizeInKB());
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return -1 * Longs.compare(e1.getSizeInKB(), e2.getSizeInKB());
            }
        });

        viewer.addColumn(new VariableHeaderColumn<ResultDocument>(Msg.filename.get(), Msg.sender.get()) {
            protected String getLabel(ResultDocument element) {
                if (element.isEmail())
                    return element.getSender();
                return element.getFilename();
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return compareAlphanum(getLabel(e1), getLabel(e2));
            }
        });

        viewer.addColumn(new Column<ResultDocument>(Msg.type.get()) {
            protected String getLabel(ResultDocument element) {
                return element.getType();
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return compareAlphanum(e1.getType(), e2.getType());
            }
        });
        
        viewer.addColumn(new Column<ResultDocument>(Msg.path.get()) {
            protected String getLabel(ResultDocument element) {
                return element.getPath().getPath();
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return compareAlphanum(getLabel(e1), getLabel(e2));
            }
        });
        
        viewer.addColumn(new VariableHeaderColumn<ResultDocument>(Msg.authors.get(), Msg.sender.get()) {
            protected String getLabel(ResultDocument element) {
                return element.getAuthors();
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                return compareAlphanum(e1.getAuthors(), e2.getAuthors());
            }
        });
        
        viewer.addColumn(new VariableHeaderColumn<ResultDocument>(Msg.last_modified.get(), Msg.send_date.get()) {
            protected String getLabel(ResultDocument element) {
                Date date = getDate(element);
                return date == null ? "" : dateFormat.format(date);
            }
            protected int compare(ResultDocument e1, ResultDocument e2) {
                Date date1 = getDate(e1);
                Date date2 = getDate(e2);
                if (date1 == null) // Place null dates before non-null dates
                    return date2 == null ? 0 : -1;
                else if (date2 == null)
                    return 1;
                return date1.compareTo(date2);
            }
            @Nullable
            private Date getDate(ResultDocument element) {
                if (element.isEmail())
                    return element.getDate();
                return element.getLastModified();
            }
        });
    ```

Edit: This is What I expected to Achieve:

Checked State Unchecked State

Currently I have two images to switch between these states, this was good for me now, but I want exact implementation to Achieve this using widgets.

This is my Updated Code for addColumn method for adding Checkbox images now.

    public final void addColumn(@NotNull final Column<E> column, boolean isSelectColumn) {
        Util.checkNotNull(column);
        columns.add(column);

        final TableColumn tableColumn = new TableColumn(table, column.orientation);
        tableColumn.setText(column.label);
        tableColumn.setToolTipText(column.getToolTipText());

        column.evtLabelChanged.add(new Event.Listener<String>() {
            public void update(String eventData) {
                tableColumn.setText(eventData);
            }
        });

        if (isSelectColumn) {
            tableColumn.setImage(Img.CHECKBOX_UNCHECKED.get());

            tableColumn.addListener(SWT.Selection, event -> {
                if (tableColumn.getImage() == Img.CHECKBOX_UNCHECKED.get()) {
                    tableColumn.setImage(Img.CHECKBOX_CHECKED.get());
                    isAllSelected = true;
                } else {
                    tableColumn.setImage(Img.CHECKBOX_UNCHECKED.get());
                    isAllSelected = false;
                }
                for (TableItem item : table.getItems()) {
                    item.setChecked(isAllSelected);
                }
                table.redraw();
                Display.getDefault().asyncExec(() -> {
                    List<ResultDocument> selectedItems = new ArrayList<>();
                    for (TableItem item : table.getItems()) {
                        if (item.getChecked()) {
                            selectedItems.add((ResultDocument) item.getData());
                        }
                    }
                    ResultPanel.selDocument.addAll(selectedItems);
                });
            });
        }
    }

This is the issue that I have while selecting Large set of data using Select all method: Data selection issue

When I search for a document it will be shown in the table like below, when I click select all, it select all the data in the table, in this scenario I have around 5000+ data, So it selected all Checkbox, it shows the details of the documents until the table is visible, but this issue occurred when I scroll through the table, Also this issue occurred only for the First time while fetching data, when fetching the same or other data again this issue was gone, Any idea on how can I solve this issue?

0

There are 0 answers