/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* 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 org.csstudio.ui.util.swt.stringtable;
import java.util.Arrays;
import java.util.List;
import org.csstudio.ui.util.Activator;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
/** Editor for table (list) of String or String[] entries,
* allows up/down ordering, add and delete
* @author Kay Kasemir, Xihui Chen
*/
public class StringTableEditor extends Composite
{
public enum CellEditorType{
TEXT,
CHECKBOX,
DROPDOWN
}
private static final String DELETE = "delete"; //$NON-NLS-1$
private static final String DOWN = "down"; //$NON-NLS-1$
private static final String UP = "up"; //$NON-NLS-1$
private static final String EDIT = "edit"; //$NON-NLS-1$
private final TableViewer tableViewer;
private final static ImageRegistry images = new ImageRegistry();
private Button editButton;
private Button upButton;
private Button downButton;
private Button deleteButton;
static {
// Buttons: edit/up/down/delete
images.put(EDIT, Activator.getImageDescriptor("icons/edit.gif")); //$NON-NLS-1$
images.put(UP, Activator.getImageDescriptor("icons/up.gif")); //$NON-NLS-1$
images.put(DOWN, Activator.getImageDescriptor("icons/down.gif")); //$NON-NLS-1$
images.put(DELETE, Activator.getImageDescriptor("icons/delete.gif")); //$NON-NLS-1$
}
/** Creates an editable table. The size of headers array implies the number of columns.
* @param parent The composite which the table resides in. Cannot be null.
* @param headers Contains the header for each column. Cannot be null.
* @param editable Whether it is editable for each column. The size must be same as headers.
* If it's null, all columns will be editable.
* @param items The items to be displayed and manipulated in the table. Cannot be null.
* Each element in the list, which is an array of string, represents the data in a row.
* In turn, each element in the string array represents the data in a cell.
* So it is required that every string array in the list must has the same size as headers.
* @param rowEditDialog The dialog to edit a row. If it is null, there will be no edit button.
* @param columnsMinWidth The minimum width for each column. Cannot be null.
*/
public StringTableEditor(final Composite parent, final String[] headers,
final boolean[] editable, final List<String[]> items,
final RowEditDialog rowEditDialog,
final int[] columnsMinWidth){
this(parent, headers,editable,items,rowEditDialog,columnsMinWidth, null, null);
}
/** Creates an editable table. The size of headers array implies the number of columns.
* @param parent The composite which the table resides in. Cannot be null.
* @param headers Contains the header for each column. Cannot be null.
* @param editable Whether it is editable for each column. The size must be same as headers.
* If it's null, all columns will be editable.
* @param items The items to be displayed and manipulated in the table. Cannot be null.
* Each element in the list, which is an array of string, represents the data in a row.
* In turn, each element in the string array represents the data in a cell.
* So it is required that every string array in the list must has the same size as headers.
* @param rowEditDialog The dialog to edit a row. If it is null, there will be no edit button.
* @param columnsMinWidth The minimum width for each column. Cannot be null.
* @param cellEditorTypes the cell editor type for each column. null for default text cell editor.
* @param cellEditorDatas the corresponding data for the cell editor. For example, a String[] for dropdown and checkbox cell editor.
* null if not needed. For checkbox, String[0] is the text for false, String[1] is for true.
*/
public StringTableEditor(final Composite parent, final String[] headers,
final boolean[] editable, final List<String[]> items,
final RowEditDialog rowEditDialog,
final int[] columnsMinWidth, final CellEditorType[] cellEditorTypes, final Object[] cellEditorDatas)
{
super(parent, 0);
final int table_columns = headers.length;
boolean[] editableArray = editable;
if(editable == null){
editableArray = new boolean[table_columns];
Arrays.fill(editableArray, true);
}
if (editableArray.length != table_columns ||
columnsMinWidth.length != table_columns)
throw new Error("Inconsistent table column count"); //$NON-NLS-1$
// StringTableEditor itself is a Composite that contains 2 columns:
// Left column: Table
// Right column: Edit/up/down/delete buttons
setLayout(new GridLayout(2, false));
//Edit-able Table in its own Composite for TableColumnLayout
final Composite table_parent = new Composite(this, 0);
final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4);
gd.heightHint = 100;
table_parent.setLayoutData(gd);
final TableColumnLayout table_layout = new TableColumnLayout();
table_parent.setLayout(table_layout);
tableViewer = new TableViewer(table_parent, SWT.BORDER | SWT.FULL_SELECTION |
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
final Table table = tableViewer.getTable();
table.setLinesVisible(true);
table.setHeaderVisible(true);
//Create edit-able columns
for(int i = 0; i < table_columns; i++)
{
final TableViewerColumn view_col = new TableViewerColumn(tableViewer, 0);
final TableColumn col = view_col.getColumn();
col.setText(headers[i]);
col.setMoveable(true);
col.setResizable(true);
table_layout.setColumnData(col, new ColumnWeightData(100, columnsMinWidth[i]));
view_col.setLabelProvider(new StringMultiColumnsLabelProvider(tableViewer, editableArray[i]));
if(editableArray[i]){
CellEditorType cellEditorType;
if(cellEditorTypes == null || cellEditorTypes[i]==null)
cellEditorType = CellEditorType.TEXT;
else
cellEditorType = cellEditorTypes[i];
Object cellEditorData = null;
if(cellEditorDatas!=null)
cellEditorData = cellEditorDatas[i];
view_col.setEditingSupport(new StringMultiColumnsEditor(tableViewer,
table_columns, i, cellEditorType, cellEditorData));
}
}
tableViewer.setContentProvider(new StringTableContentProvider<String[]>());
tableViewer.setInput(items);
if(rowEditDialog != null)
editButton = createEditButton(table_columns, rowEditDialog);
upButton = createUpButton();
downButton = createDownButton();
deleteButton = createDeleteButton();
// Enable buttons when items are selected
tableViewer.addSelectionChangedListener(new ISelectionChangedListener()
{
@Override
public void selectionChanged(SelectionChangedEvent event)
{
setButtonsEnable();
}
});
}
/** Create a string list editor which is a table with only one column.
* @param parent Parent widget
* @param items List of strings, will be changed in-place
*/
public StringTableEditor(final Composite parent, final List<String> items)
{
super(parent, 0);
final GridLayout layout = new GridLayout();
layout.numColumns = 2;
setLayout(layout);
// Edit-able List in its own Composite for TableColumnLayout
final Composite table_parent = new Composite(this, 0);
table_parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
final TableColumnLayout table_layout = new TableColumnLayout();
table_parent.setLayout(table_layout);
tableViewer = new TableViewer(table_parent, SWT.BORDER | SWT.FULL_SELECTION |
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
final Table table = tableViewer.getTable();
table.setLinesVisible(true);
table.setHeaderVisible(false);
// Create edit-able column
final TableViewerColumn view_col = new TableViewerColumn(tableViewer, 0);
final TableColumn col = view_col.getColumn();
col.setText(Messages.StringTableEditor_DefaultColumnHeader);
col.setResizable(true);
table_layout.setColumnData(col, new ColumnWeightData(100, 200));
view_col.setLabelProvider(new StringColumnLabelProvider(tableViewer));
view_col.setEditingSupport(new StringColumnEditor(tableViewer));
tableViewer.setContentProvider(new StringTableContentProvider<String>());
tableViewer.setInput(items);
upButton = createUpButton();
downButton = createDownButton();
deleteButton = createDeleteButton();
// Enable buttons when items are selected
tableViewer.addSelectionChangedListener(new ISelectionChangedListener()
{
@Override
public void selectionChanged(SelectionChangedEvent event)
{
setButtonsEnable();
}
});
}
/** Update the input to the table.
* @param new_items New items. Must be either List of String or String[],
* and type must match whatever was used to construct
* the StringTableEditor.
*/
public void updateInput(List<?> new_items)
{
tableViewer.setInput(new_items);
}
/** Refresh the editor after the list of items was changed */
public void refresh()
{
tableViewer.refresh();
}
private Button createEditButton(final int numColumns,
final RowEditDialog rowEditDialog)
{
final Button edit = new Button(this, SWT.PUSH);
edit.setImage(images.get(EDIT));
edit.setToolTipText(Messages.StringTableEditor_EditToolTip);
edit.setLayoutData(new GridData());
edit.setEnabled(false);
edit.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("unchecked")
@Override
public void widgetSelected(SelectionEvent e) {
final List<String[]> items = (List<String[]>) tableViewer.getInput();
final Integer index = (Integer)
((IStructuredSelection) tableViewer.getSelection()).getFirstElement();
if(index == StringTableContentProvider.ADD_ELEMENT) {
String[] emptyData = new String[numColumns];
Arrays.fill(emptyData, ""); //$NON-NLS-1$
rowEditDialog.setRowData(emptyData);
}else
rowEditDialog.setRowData(items.get(index));
if(rowEditDialog.open() != Window.OK)
return;
if(index == StringTableContentProvider.ADD_ELEMENT)
//when you click <Add>, it already added a new row.
items.set(items.size()-1, rowEditDialog.getRowData());
else
items.set(index, rowEditDialog.getRowData());
tableViewer.refresh();
}
});
return edit;
}
private Button createUpButton() {
final Button up = new Button(this, SWT.PUSH);
up.setImage(images.get(UP));
up.setToolTipText(Messages.StringTableEditor_MoveUpToolTip);
up.setLayoutData(new GridData());
up.setEnabled(false);
up.addSelectionListener(new SelectionAdapter()
{
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public void widgetSelected(SelectionEvent e)
{
final List items = (List)tableViewer.getInput();
final Integer index = (Integer)
((IStructuredSelection)tableViewer.getSelection()).getFirstElement();
if (index == StringTableContentProvider.ADD_ELEMENT ||
index < 1)
return;
//final String[] item = items.get(index);
items.add(index-1, items.get(index));
items.remove(index + 1);
tableViewer.refresh();
tableViewer.getTable().setSelection(index-1);
}
});
return up;
}
private Button createDownButton() {
final Button down = new Button(this, SWT.PUSH);
down.setImage(images.get(DOWN));
down.setToolTipText(Messages.StringTableEditor_MoveDownToolTip);
down.setLayoutData(new GridData());
down.setEnabled(false);
down.addSelectionListener(new SelectionAdapter()
{
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void widgetSelected(SelectionEvent e)
{
final List items = (List) tableViewer.getInput();
final Integer index = (Integer)
((IStructuredSelection)tableViewer.getSelection()).getFirstElement();
if (index == StringTableContentProvider.ADD_ELEMENT ||
index >= items.size()-1)
return;
items.add(index+2, items.get(index));
items.remove(index.intValue());
tableViewer.refresh();
tableViewer.getTable().setSelection(index+1);
}
});
return down;
}
private Button createDeleteButton() {
final Button delete = new Button(this, SWT.PUSH);
delete.setImage(images.get(DELETE));
delete.setToolTipText(Messages.StringTableEditor_DeleteToolTip);
delete.setLayoutData(new GridData());
delete.setEnabled(false);
delete.addSelectionListener(new SelectionAdapter()
{
@SuppressWarnings({ "rawtypes" })
@Override
public void widgetSelected(SelectionEvent e)
{
final List items = (List) tableViewer.getInput();
final Object sel[] =
((IStructuredSelection) tableViewer.getSelection()).toArray();
int adjust = 0;
for (Object s : sel)
{
final Integer index = (Integer)s;
if (index == StringTableContentProvider.ADD_ELEMENT)
continue;
items.remove(index.intValue() - adjust);
// What used to be index N is now N-1...
++adjust;
}
tableViewer.refresh();
}
});
return delete;
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if(enabled) {
tableViewer.getTable().setEnabled(enabled);
setButtonsEnable();
}
else
for(Control control: this.getChildren())
control.setEnabled(enabled);
}
private void setButtonsEnable() {
final IStructuredSelection sel = (IStructuredSelection)tableViewer.getSelection();
final int count = sel.size();
if(editButton != null)
editButton.setEnabled(count == 1);
upButton.setEnabled(count == 1);
downButton.setEnabled(count == 1);
deleteButton.setEnabled(count > 0);
}
public TableViewer getTableViewer() {
return tableViewer;
}
}