blob: 5c7e02c1c8385e02eba61f48e3e28cf65b32427f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 Ericsson
*
* 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:
* Alexandre Montplaisir - Initial API and implementation
* Matthew Khouzam - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.viewers.table;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
import java.util.Collections;
import java.util.Comparator;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.deferred.DeferredContentProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
/**
* Generic {@link TableViewer} wrapper with most standard features enabled.
* <p>
* It provides the following features: <br>
* - Sortable columns <br>
* - Movable columns <br>
* - Resizable columns <br>
* - Tracking last clicked columns
* <p>
* The user of this class should add columns to the table by using the
* {@link #createColumn(String, ColumnLabelProvider, Comparator)} method, and
* set the content provider and input on the supplied {@link TableViewer}.
*
* @since 1.1
*/
public class TmfSimpleTableViewer extends TmfViewer {
/**
* Viewer comparator that ignores the element label strings and uses the
* given comparator to compare the elements directly.
*/
private static class ElementComparator<T> extends ViewerComparator {
private Comparator<T> elementComparator;
public ElementComparator(Comparator<T> comparator) {
elementComparator = comparator;
}
@SuppressWarnings("unchecked")
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
return elementComparator.compare((T) e1, (T) e2);
}
}
/**
* Comparator that compares the text of a column given by the label
* provider, using the text's String ordering.
*/
private static class ColumnLabelComparator implements Comparator<Object> {
private final ILabelProvider fLabelProvider;
public ColumnLabelComparator(ILabelProvider labelProvider) {
fLabelProvider = labelProvider;
}
@Override
public int compare(Object o1, Object o2) {
String s1 = nullToEmptyString(fLabelProvider.getText(o1));
String s2 = nullToEmptyString(fLabelProvider.getText(o2));
return s1.compareTo(s2);
}
}
private final class MouseColumnListener extends MouseAdapter {
@Override
public void mouseDown(MouseEvent e) {
ViewerCell cell = fTableViewer.getCell(new Point(e.x, e.y));
fSelectedColumn = (cell != null) ? cell.getColumnIndex() : -1;
}
}
private final class ColumnSorter<T> extends SelectionAdapter {
private final @NonNull TableColumn fColumn;
private final @NonNull Comparator<T> fComparator;
private ColumnSorter(@NonNull TableColumn column, @NonNull Comparator<T> comparator) {
fColumn = column;
fComparator = comparator;
}
@Override
public void widgetSelected(SelectionEvent e) {
Table table = fTableViewer.getTable();
TableColumn prevSortcolumn = table.getSortColumn();
if (prevSortcolumn == fColumn) {
flipSortDirection();
}
table.setSortDirection(fDirection);
table.setSortColumn(fColumn);
Comparator<T> comparator;
if (fDirection == SWT.DOWN) {
comparator = fComparator;
} else {
comparator = checkNotNull(Collections.reverseOrder(fComparator));
}
IContentProvider contentProvider = fTableViewer.getContentProvider();
if (contentProvider instanceof DeferredContentProvider) {
DeferredContentProvider deferredContentProvider = (DeferredContentProvider) contentProvider;
deferredContentProvider.setSortOrder(comparator);
} else if (contentProvider instanceof ISortingLazyContentProvider) {
ISortingLazyContentProvider sortingLazyContentProvider = (ISortingLazyContentProvider) contentProvider;
sortingLazyContentProvider.setSortOrder(comparator);
} else {
fTableViewer.setComparator(new ElementComparator<>(comparator));
}
}
}
private static final int DEFAULT_COL_WIDTH = 200;
private final TableViewer fTableViewer;
private int fDirection;
private int fSelectedColumn;
private MenuManager fTablePopupMenuManager;
/**
* Constructor that initializes the parent of the viewer
*
* @param table
* the {@link TableViewer} to wrap
*/
public TmfSimpleTableViewer(TableViewer table) {
super(table.getControl().getParent());
fTableViewer = table;
final Table tableControl = fTableViewer.getTable();
tableControl.setHeaderVisible(true);
tableControl.setLinesVisible(true);
fDirection = SWT.DOWN;
fTableViewer.setUseHashlookup(true);
fTableViewer.getControl().addMouseListener(new MouseColumnListener());
fTablePopupMenuManager = new MenuManager();
fTablePopupMenuManager.setRemoveAllWhenShown(true);
fTablePopupMenuManager.addMenuListener((final @Nullable IMenuManager manager) -> {
TableViewer viewer = getTableViewer();
ISelection selection = viewer.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection sel = (IStructuredSelection) selection;
if (manager != null) {
appendToTablePopupMenu(manager, sel);
}
}
});
Menu tablePopup = fTablePopupMenuManager.createContextMenu(getTableViewer().getTable());
getTableViewer().getTable().setMenu(tablePopup);
tableControl.addDisposeListener((e) -> {
internalDispose();
});
}
@Override
public void dispose() {
if (fTableViewer != null) {
fTableViewer.getControl().dispose();
}
}
private void internalDispose() {
if (fTablePopupMenuManager != null) {
fTablePopupMenuManager.dispose();
}
super.dispose();
}
/**
* Method to add commands to the context sensitive menu.
* @param manager
* the menu manager
* @param sel
* the current selection
* @since 2.0
*/
protected void appendToTablePopupMenu(@NonNull IMenuManager manager, @NonNull IStructuredSelection sel) {
// Do nothing
}
/**
* Create a column for the table. The column will have a default width set,
* and will be resizable, moveable and sortable.
*
* @param name
* the name of the column
* @param provider
* the label provider of the column
* @param comparator
* the comparator associated with clicking on the column, if it
* is null, a string comparator on the label will be used
* @return the column that was created
* @since 2.0
*/
public final <T> TableColumn createColumn(String name, ColumnLabelProvider provider, @Nullable Comparator<T> comparator) {
TableViewerColumn col = new TableViewerColumn(fTableViewer, SWT.NONE);
col.setLabelProvider(provider);
final TableColumn column = col.getColumn();
column.setWidth(DEFAULT_COL_WIDTH);
column.setText(name);
column.setResizable(true);
column.setMoveable(true);
if (comparator == null) {
column.addSelectionListener(new ColumnSorter<>(column, new ColumnLabelComparator(provider)));
} else {
column.addSelectionListener(new ColumnSorter<>(column, comparator));
}
return column;
}
/**
* Reverse the sort direction
*/
private void flipSortDirection() {
if (fDirection == SWT.DOWN) {
fDirection = SWT.UP;
} else {
fDirection = SWT.DOWN;
}
}
@Override
public final Control getControl() {
return fTableViewer.getControl();
}
/**
* Gets the wrapped table viewer
*
* @return the table viewer
*/
public final TableViewer getTableViewer() {
return fTableViewer;
}
/**
* Get the selected column index
*
* @return the selected column index or -1
*/
public final int getColumnIndex() {
return fSelectedColumn;
}
@Override
public final void refresh() {
fTableViewer.refresh();
}
}