/*-
* Copyright 2015 Diamond Light Source Ltd.
*
* 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
*/
package uk.ac.diamond.scisoft.xpdf.views;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TableColumn;
/**
* A table with grouped columns.
* @author Timothy Spain, timothy.spain@diamond.ac.uk
*
*/
class XPDFGroupedTable extends Composite {
private Composite headerTableCompo;
private Composite dataTableCompo;
// Only one of each of these per grouped table
private IStructuredContentProvider cachedContentProvider;
private Object cachedInput;
// private ITableLabelProvider cachedLabelProvider;
private ISelectionChangedListener cachedUserSelectionChangedListener;
private int cachedDragFlags;
private Transfer[] cachedDragTransfers;
private DragSourceAdapter cachedDragSourceListener;
private int cachedDropFlags;
private Transfer[] cachedDropTransfers;
private ViewerDropAdapterFactory cachedDropTargetListenerFactory;
// A TableViewer for the data table and one for the column headers
// vestigial table
private TableViewer dataViewer;
private TableViewer headerViewer;
private List<String> groupNames;
// the name of the header in which column occurs
private List<String> columnsToHeaderNames;
/**
* Constructs a new XPDFGroupedTable, within a {@link Composite} parent,
* with style bits style.
* @param parent
* Parent Composite into which the grouped table will be put.
* Does not need TableColumnLayout
* @param style
* Style bits to apply to the table.
*/
public XPDFGroupedTable(Composite parent, int style) {
super(parent, style);
this.setLayout(new FormLayout());
groupNames = new ArrayList<String>();
columnsToHeaderNames = new ArrayList<String>();
// Two Composites to hold the header table and the data table
headerTableCompo = new Composite(this, SWT.NONE);
headerTableCompo.setLayout(new TableColumnLayout());
headerViewer = new TableViewer(headerTableCompo, SWT.NO_SCROLL);
headerViewer.getTable().setHeaderVisible(true);
dataTableCompo = new Composite(this, SWT.NONE);
dataTableCompo.setLayout(new TableColumnLayout());
dataViewer = new TableViewer(dataTableCompo, SWT.NO_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
dataViewer.getTable().setHeaderVisible(true);
dataViewer.getTable().setLinesVisible(true);
dataViewer.addSelectionChangedListener(new SubTableSelectionChangedListener());
// Attach the table-holding Composites to the sides and each other
FormData headerFormData = new FormData(), dataFormData = new FormData();
headerFormData.left = new FormAttachment(0, 0);
headerFormData.right = new FormAttachment(100, 0);
headerFormData.top = new FormAttachment(0, 0);
headerFormData.bottom = new FormAttachment(dataTableCompo);
headerTableCompo.setLayoutData(headerFormData);
dataFormData.left = new FormAttachment(0, 0);
dataFormData.right = new FormAttachment(100, 0);
dataFormData.top = new FormAttachment(headerTableCompo);
dataFormData.bottom = new FormAttachment(100, 0);
dataTableCompo.setLayoutData(dataFormData);
// null the cached table classes
cachedContentProvider = null;
cachedInput = null;
cachedUserSelectionChangedListener = null;
cachedDragTransfers = null;
cachedDragSourceListener = null;
cachedDropTransfers = null;
cachedDropTargetListenerFactory = null;
}
/**
* Sets the width of a data column.
* @param col
* object representing the column to be set
* @param weight
* the weight to set the column to
*/
public void setColumnWidth(TableViewerColumn col, Integer weight) {
TableColumnLayout tCL = (TableColumnLayout) dataTableCompo.getLayout();
tCL.setColumnData(col.getColumn(), new ColumnWeightData(weight));
col.getColumn().setWidth(weight);
tCL = (TableColumnLayout) headerTableCompo.getLayout();
for (String groupName : groupNames) {
int groupWidth = 0;
for (int iCol = 0 ; iCol < columnsToHeaderNames.size(); iCol++) {
String columnName = columnsToHeaderNames.get(iCol);
if (columnName.equals(groupName))
groupWidth += dataViewer.getTable().getColumn(iCol).getWidth();
}
tCL.setColumnData(headerViewer.getTable().getColumn(groupNames.indexOf(groupName)), new ColumnWeightData(groupWidth));
}
}
// Editing support may need to manufacture its classes, hence a factory interface is used.
/**
* Sets the per column editing support.
* <p>
* Hiding the multiple {@link TableViewer}s means that the
* {@link EditingSupport} objects cannot be create by the surrounding
* objects. To allow creation of the {@link EditindSupport} classes, a
* Factory implementation is defined in {@link EditingSupportFactory}.
* @param column
* column for which the editing is to be defined
* @param editingSupportFactory
* the factory class to produce the editing support classes.
*
*/
public void setColumnEditingSupport(TableViewerColumn column, EditingSupportFactory editingSupportFactory) {
column.setEditingSupport(editingSupportFactory.get(dataViewer));//.get(getIndexOfTableContaining(column.getColumn()))));
}
/**
* Refreshes all sub-tables.
*/
public void refresh() {
dataViewer.refresh();
headerViewer.refresh();
}
/**
* Creates a new column group.
* <p>
* Adds a new named or anonymous column group to the end of the list of
* groups. Repeated named groups are not allowed, but an new anonymous
* group can be added to the end of the list at any point.
* @param groupNameIn
* the name to give the group of columns
*/
public void createColumnGroup(String groupNameIn, boolean isLastGroup) {
String groupName = (groupNameIn != null) ? groupNameIn : "";
// Do not add duplicate named groups
if (!groupName.equals("") && groupNames.contains(groupName)) return;
// Otherwise, add a group with this name to the end of the list of groups
groupNames.add(groupName);
TableViewerColumn tVC = new TableViewerColumn(headerViewer, SWT.NONE);
tVC.getColumn().setText(groupName);
((TableColumnLayout) headerTableCompo.getLayout()).setColumnData(tVC.getColumn(), new ColumnWeightData(10));
}
/**
* Adds a column to a group.
* <p>
* Adds a new column to the end of the named group. A previously unnamed
* group is created at the end. If the group to be added to is anonymous,
* and the last defined group was not, define a new anonymous group at the
* end of the List.
* @param groupID
* The name of the group the column is to be added to
* @param style
* SWT style of the Viewer to be created
* @return
* The new TableViewerColumn
*/
public TableViewerColumn addColumn(String groupID, int style) {
int columnIndex;
if (columnsToHeaderNames.contains(groupID)) {
columnIndex = columnsToHeaderNames.lastIndexOf(groupID) + 1;
} else {
// Add an unknown group to the index
createColumnGroup(groupID, false);
columnIndex = columnsToHeaderNames.size();
}
TableViewerColumn tVC = new TableViewerColumn(dataViewer, style, columnIndex);
columnsToHeaderNames.add(columnIndex, groupID);
return tVC;
}
/**
* Sets the content providers for all sub-tables.
* @param iStructuredContentProvider
* the content provider class to be used.
*/
public void setContentProvider(IStructuredContentProvider iStructuredContentProvider) {
cachedContentProvider = iStructuredContentProvider;
dataViewer.setContentProvider(iStructuredContentProvider);
if (cachedInput != null)
dataViewer.setInput(cachedInput);
}
/**
* Sets the input data.
* @param input
* Object that provides the input.
*/
public void setInput(Object input) {
cachedInput = input;
if (cachedContentProvider != null)
dataViewer.setInput(cachedInput);
}
/**
* Sets the LabelProvider for the entire Table
* @param iTLP
* Label provider for all the tables
*/
public void setLabelProvider(ITableLabelProvider iTLP) {
}
/**
* Adds a {@link ISelectionChangedListener} defined by the user.
* @param iSCL
* ISelectionChangedListener to be used table wide
*/
public void addSelectionChangedListener(ISelectionChangedListener iSCL) {
cachedUserSelectionChangedListener = iSCL;
}
/**
* Adds drag support to the table
* @param dragFlags
* as {@link Table.addDragSupport().
* @param transfers
* as {@link Table.addDragSupport().
* @param dSL
* as {@link Table.addDragSupport().
*/
public void addDragSupport(int dragFlags, Transfer[] transfers, DragSourceAdapter dSL) {
cachedDragFlags = dragFlags;
cachedDragTransfers = transfers;
cachedDragSourceListener = dSL;
dataViewer.addDragSupport(cachedDragFlags, cachedDragTransfers, cachedDragSourceListener);
}
public void addDropSupport(int dropFlags, Transfer[] transfers, ViewerDropAdapterFactory dTLF) {
cachedDropFlags = dropFlags;
cachedDropTransfers = transfers;
cachedDropTargetListenerFactory = dTLF;
dataViewer.addDropSupport(cachedDropFlags, cachedDropTransfers, cachedDropTargetListenerFactory.get(dataViewer));
}
/**
* Gets the selected data
* @return the selected data of the table
*/
public ISelection getSelection() {
return dataViewer.getSelection();
}
/**
* Changes the selection on all sub-tables.
* <p>
* When the selection changes, change the selection on the other
* sub-tables.
*
*/
// By using a object-wide flag we can prevent each call triggering four
// more and causing a stack overflow.
class SubTableSelectionChangedListener implements ISelectionChangedListener {
@Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection instanceof IStructuredSelection) {
if (cachedUserSelectionChangedListener != null)
cachedUserSelectionChangedListener.selectionChanged(event);
}
}
}
/**
* Gets the TableColumn within the table that is sorted
* @return
* the object of the column that is sorted; otherwise -1.
*/
public TableColumn getSortColumn() {
return dataViewer.getTable().getSortColumn();
}
/**
* Gets the direction of the sorted column within the table
* @return
* SWT.UP, SWT.DOWN or SWT.NONE
*/
public int getSortDirection() {
return dataViewer.getTable().getSortDirection();
}
/**
* Set the column to be sorted.
* @param column
* the column to be sorted. Can be a valid column in any group viewer.
*/
public void setSortColumn(TableColumn column) {
dataViewer.getTable().setSortColumn(column);
}
/**
* Sets the sort direction of the sorted column.
* @param direction
* SWT.UP, SWT.DOWN or SWT.NONE
*/
public void setSortDirection(int direction) {
dataViewer.getTable().setSortDirection(direction);
}
/**
* Creates a context menu look in each {@link TableViewer}.
* @param menuManager
* the managed menu to be inserted.
*/
public void createContextMenu(MenuManager menuManager) {
Menu popupMenu = menuManager.createContextMenu(dataViewer.getControl());
dataViewer.getControl().setMenu(popupMenu);
}
@Override
public void addMouseListener(MouseListener listener) {
dataViewer.getTable().addMouseListener(listener);
}
@Override
public void removeMouseListener(MouseListener listener) {
dataViewer.getTable().removeMouseListener(listener);
}
/**
* Adds the listener to the collection of listeners who will be notified
* when the user changes the receiver's selection, by sending it one of the
* messages defined in the SelectionListener interface.
* <p>
* When widgetSelected is called, the item field of the event object is
* valid. If the receiver has the SWT.CHECK style and the check selection
* changes, the event object detail field contains the value SWT.CHECK.
* widgetDefaultSelected is typically called when an item is
* double-clicked. The item field of the event object is valid for default
* selection, but the detail field is not used.
* <p>
* Passes the listener through to the underlying {@link Table}.
* @param listener
* the listener which should be notified when the user changes the receiver's selection
*/
public void addSelectionListener(SelectionListener listener) {
dataViewer.getTable().addSelectionListener(listener);
}
/**
* Removes the listener from the collection of listeners who will be notified when the user changes the receiver's selection.
* <p>
* Removes the listener from the underlying {@link Table}.
* @param listener
* the listener which should no longer be notified.
*/
public void removeSelectionListener(SelectionListener listener) {
dataViewer.getTable().removeSelectionListener(listener);
}
}