blob: f049c5dc33e8f3786c78fce429056d2304c97422 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Ericsson AB and others.
*
* 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
*
* Description:
*
* This class implements an editable List-like widget
*
* Contributors:
* Sebastien Dubois - Created for Mylyn Review R4E project
*
******************************************************************************/
package org.eclipse.mylyn.reviews.r4e.ui.internal.utils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.mylyn.reviews.r4e.core.model.R4EParticipant;
import org.eclipse.mylyn.reviews.r4e.ui.R4EUIPlugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
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;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* @author Sebastien Dubois
* @version $Revision: 1.0 $
*/
public class EditableListWidget {
// ------------------------------------------------------------------------
// Member variables
// ------------------------------------------------------------------------
/**
* Field fMainComposite.
*/
protected Composite fMainComposite = null;
/**
* Field fMainTable.
*/
protected Table fMainTable = null;
/**
* Field fAddButton.
*/
protected Button fAddButton = null;
/**
* Field fRemoveButton.
*/
protected Button fRemoveButton = null;
/**
* Field fListener.
*/
protected IEditableListListener fListener = null;
/**
* Field fInstanceId.
*/
protected int fInstanceId = 0;
/**
* Field fValues.
*/
protected String[] fValues = null;
/**
* Field fEditableControl.
*/
Control fEditableControl = null;
/**
* Field fEnabled.
*/
protected boolean fEnabled;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
//TODO: This should be made more generic to accept various tables. Right now it is very hard-coded to our stuff...
/**
* Constructor for EditableListWidget.
*
* @param aToolkit
* - FormToolkit
* @param aParent
* - Composite
* @param aLayoutData
* - Object
* @param aListener
* - IEditableListListener
* @param aInstanceId
* - int
* @param aEditableWidgetClass
* - Class<?>
* @param aEditableValues
* - String[]
*/
public EditableListWidget(FormToolkit aToolkit, Composite aParent, Object aLayoutData,
IEditableListListener aListener, int aInstanceId, Class<?> aEditableWidgetClass, String[] aEditableValues) {
fMainComposite = (null != aToolkit) ? aToolkit.createComposite(aParent) : new Composite(aParent, SWT.NONE);
fMainComposite.setLayoutData(aLayoutData);
fMainComposite.setLayout(new GridLayout(4, false));
fListener = aListener;
fInstanceId = aInstanceId;
fValues = aEditableValues;
createEditableListFromTable(aToolkit, aEditableWidgetClass);
}
// ------------------------------------------------------------------------
// Methods
// ------------------------------------------------------------------------
/**
* Method dispose.
*/
public void dispose() {
fMainTable.dispose();
fMainComposite.dispose();
}
/**
* Method createEditableListFromTable. Builds the editable list in the provided table
*
* @param aToolkit
* - FormToolkit
* @param aEditableWidgetClass
* - Class<?>
*/
public void createEditableListFromTable(FormToolkit aToolkit, final Class<?> aEditableWidgetClass) {
final Composite tableComposite = (null != aToolkit) ? aToolkit.createComposite(fMainComposite) : new Composite(
fMainComposite, SWT.NONE);
fMainTable = (null != aToolkit)
? aToolkit.createTable(tableComposite, SWT.FULL_SELECTION | SWT.BORDER)
: new Table(tableComposite, SWT.FULL_SELECTION | SWT.BORDER);
final GridData tableCompositeData = new GridData(GridData.FILL, GridData.FILL, true, true);
if (aEditableWidgetClass.equals(Date.class) || aEditableWidgetClass.equals(Label.class)) {
tableCompositeData.horizontalSpan = 1;
} else {
tableCompositeData.horizontalSpan = 2;
}
final TableColumnLayout tableColumnLayout = new TableColumnLayout();
tableComposite.setLayout(tableColumnLayout);
final TableColumn tableColumn = new TableColumn(fMainTable, SWT.NONE, 0);
final TableColumn tableColumn2;
fMainTable.setLinesVisible(true);
if (aEditableWidgetClass.equals(Date.class) || aEditableWidgetClass.equals(Label.class)) {
fMainTable.setHeaderVisible(true);
tableColumn2 = new TableColumn(fMainTable, SWT.NONE, 1);
if (aEditableWidgetClass.equals(Date.class)) {
tableColumn.setText(R4EUIConstants.SPENT_TIME_COLUMN_HEADER);
tableColumn2.setText(R4EUIConstants.ENTRY_TIME_COLUMN_HEADER);
} else if (aEditableWidgetClass.equals(Label.class)) {
tableColumn.setText(R4EUIConstants.ID_LABEL);
tableColumn2.setText(R4EUIConstants.EMAIL_LABEL);
}
tableColumn.pack();
tableColumn2.pack();
tableColumnLayout.setColumnData(tableColumn, new ColumnWeightData(50, tableColumn.getWidth() * 2, true));
tableColumnLayout.setColumnData(tableColumn2, new ColumnWeightData(50, tableColumn.getWidth(), true));
} else {
tableColumn2 = null; //only 1 column
tableColumnLayout.setColumnData(tableColumn, new ColumnWeightData(10, 100, true));
}
tableComposite.setLayoutData(tableCompositeData);
fMainComposite.addControlListener(new ControlListener() {
public void controlResized(ControlEvent e) {
//TODO: This does not work 100% as resizing the colums lags when the parent area gets shrinked quickly.
// This causes the rightmost part of the table composite to be clipped away.
// This will need to be improved, see bug 356857.
final Rectangle area = fMainTable.getClientArea();
final Point size = tableComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
final ScrollBar vBar = fMainTable.getVerticalBar();
final int vBarWidth = vBar.getSize().x;
int width = area.width - vBarWidth * 3;
if (width < 0) {
return;
}
if (size.y > area.height + fMainTable.getHeaderHeight()) {
// Subtract the scrollbar width from the total column width
// if a vertical scrollbar will be required
width -= vBarWidth;
}
// Set column width first and then resize the table to
// match the client area width
if (null != tableColumn2) {
tableColumn.setWidth(width / 2);
tableColumn2.setWidth(width - tableColumn.getWidth());
} else {
tableColumn.setWidth(width);
}
}
public void controlMoved(ControlEvent e) {
// ignor
}
});
fMainTable.addListener(SWT.FocusOut, new Listener() {
public void handleEvent(Event event) {
if (fEnabled) {
//Send items updated notification
if (null != fListener) {
//If items are empty, do not consider them
final TableItem[] items = fMainTable.getItems();
for (int i = 0; i < items.length; i++) {
if (items[i].getText().trim().length() < 1) {
fMainTable.remove(i);
}
}
fListener.itemsUpdated(fMainTable.getItems(), fInstanceId);
}
}
}
});
fMainTable.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
if (null != fListener) {
fListener.itemSelected(fMainTable.getSelection()[0], fInstanceId); //Only 1 element selected at any given time
}
}
});
final Composite buttonsComposite = (null != aToolkit)
? aToolkit.createComposite(fMainComposite)
: new Composite(fMainComposite, SWT.NONE);
buttonsComposite.setLayout(new GridLayout());
buttonsComposite.setLayoutData(new GridData(GridData.CENTER, SWT.TOP, false, false));
fAddButton = (null != aToolkit) ? aToolkit.createButton(buttonsComposite, R4EUIConstants.BUTTON_ADD_LABEL,
SWT.NONE) : new Button(buttonsComposite, SWT.NONE);
fAddButton.setText(R4EUIConstants.BUTTON_ADD_LABEL);
if (aEditableWidgetClass.equals(CCombo.class)) {
if (null == fValues || 0 == fValues.length) {
fAddButton.setEnabled(false);
}
}
fRemoveButton = (null != aToolkit) ? aToolkit.createButton(buttonsComposite,
R4EUIConstants.BUTTON_REMOVE_LABEL, SWT.NONE) : new Button(buttonsComposite, SWT.NONE);
fRemoveButton.setText(R4EUIConstants.BUTTON_REMOVE_LABEL);
if (0 == fMainTable.getItemCount()) {
fRemoveButton.setEnabled(false);
} else {
fRemoveButton.setEnabled(true);
}
fAddButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
fAddButton.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
if (aEditableWidgetClass.equals(Label.class)) {
//Get participants to assign
final List<R4EParticipant> participants = UIUtils.getAssignParticipants();
TableItem newItem = null;
for (R4EParticipant participant : participants) {
newItem = null;
String[] tableStrs = new String[2];
tableStrs[0] = participant.getId();
tableStrs[1] = participant.getEmail();
//Check if values already exist
for (TableItem item : fMainTable.getItems()) {
if (item.getText(0).equals(tableStrs[0])) {
newItem = item;
}
}
if (null == newItem) {
newItem = new TableItem(fMainTable, SWT.NONE);
}
fMainTable.showItem(newItem);
newItem.setText(tableStrs);
}
if (null != newItem) {
fMainTable.showItem(newItem);
}
if (null != fListener) {
fListener.itemsUpdated(fMainTable.getItems(), fInstanceId);
}
return;
}
final TableItem newItem = new TableItem(fMainTable, SWT.NONE);
fMainTable.showItem(newItem);
if (aEditableWidgetClass.equals(Text.class)) {
fEditableControl = new Text(fMainTable, SWT.SINGLE | SWT.BORDER);
((Text) fEditableControl).addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent me) {
newItem.setText(((Text) fEditableControl).getText());
}
});
((Text) fEditableControl).addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent ke) {
if (ke.keyCode == SWT.CR) {
return;
}
}
public void keyPressed(KeyEvent ke) {
// Nothing to do
}
});
} else if (aEditableWidgetClass.equals(CCombo.class)) {
fEditableControl = new CCombo(fMainTable, SWT.BORDER | SWT.READ_ONLY);
//Only add the values not already in the table in the CCombo box
final List<String> currentValues = new ArrayList<String>();
for (String currentValue : fValues) {
currentValues.add(currentValue);
}
final TableItem[] currentItems = fMainTable.getItems();
for (TableItem currentItem : currentItems) {
if (currentValues.contains(currentItem.getText())) {
currentValues.remove(currentItem.getText());
}
}
((CCombo) fEditableControl).setItems(currentValues.toArray(new String[currentValues.size()]));
((CCombo) fEditableControl).addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent me) {
newItem.setText(((CCombo) fEditableControl).getText());
}
});
} else if (aEditableWidgetClass.equals(Date.class)) {
fEditableControl = new Text(fMainTable, SWT.NONE);
final DateFormat dateFormat = new SimpleDateFormat(R4EUIConstants.DEFAULT_DATE_FORMAT);
final String[] data = { ((Text) fEditableControl).getText(),
dateFormat.format(Calendar.getInstance().getTime()) };
newItem.setText(data);
((Text) fEditableControl).addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent me) {
//Only accept numbers
String newText = ((Text) fEditableControl).getText();
try {
Integer.valueOf(newText);
} catch (NumberFormatException nfe) {
if (newText.length() > 0) {
newText = newText.substring(0, newText.length() - 1);
((Text) fEditableControl).setText(newText);
((Text) fEditableControl).setSelection(newText.length());
}
}
newItem.setText(0, newText);
}
});
} else {
return;
}
fEditableControl.addFocusListener(new FocusListener() {
public void focusLost(FocusEvent fe) {
((Control) fe.getSource()).dispose();
//Send items updated notification
if (null != fListener) {
//If items are empty, do not consider them
final TableItem[] items = fMainTable.getItems();
final int numColumns = fMainTable.getColumnCount();
boolean isSameItem = false;
for (int i = 0; i < items.length; i++) {
if (items[i].getText().trim().length() < 1) {
fMainTable.remove(i);
return;
}
}
//If items are already present, do not consider the duplicates
if (items.length > 1) {
for (int i = 0; i < items.length; i++) {
for (int j = i + 1; j < items.length; j++) {
isSameItem = true;
for (int k = 0; k < numColumns; k++) {
if (!items[i].getText(k).equals(items[j].getText(k))) {
isSameItem = false;
break; //at least one column is different go to next item
}
}
if (isSameItem) {
fMainTable.remove(j);
return;
}
}
}
}
fListener.itemsUpdated(fMainTable.getItems(), fInstanceId);
}
}
public void focusGained(FocusEvent fe) {
//Nothing to do
}
});
fEditableControl.setFocus();
final TableEditor editor = new TableEditor(fMainTable);
editor.grabHorizontal = true;
editor.grabVertical = true;
editor.setEditor(fEditableControl, newItem, 0);
fRemoveButton.setEnabled(true);
}
public void widgetDefaultSelected(SelectionEvent e) { // $codepro.audit.disable emptyMethod
}
});
fRemoveButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
fRemoveButton.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
final int numItems = fMainTable.getItemCount();
if (numItems > 0) {
//Find the table index for the first control
final Control[] controls = fMainTable.getChildren();
final int firstControlIndex = numItems - controls.length;
//Currently selected item
int tableItemIndex = fMainTable.getSelectionIndex();
if (R4EUIConstants.INVALID_VALUE == tableItemIndex) {
//Remove the selected element (and control if there is one) or the last one if none is selected
tableItemIndex = numItems - 1;
}
if (tableItemIndex >= firstControlIndex) {
controls[tableItemIndex - firstControlIndex].dispose();
}
fMainTable.getItem(tableItemIndex).dispose();
}
if (0 == fMainTable.getItemCount()) {
fRemoveButton.setEnabled(false);
} else {
fRemoveButton.setEnabled(true);
}
if (null != fListener) {
fListener.itemsUpdated(fMainTable.getItems(), fInstanceId);
}
fMainTable.redraw();
}
public void widgetDefaultSelected(SelectionEvent e) { // $codepro.audit.disable emptyMethod
}
});
}
/**
* Method removeAll.
*/
public void removeAll() {
fMainTable.removeAll();
}
/**
* Method addItem.
*
* @return Item
*/
public Item addItem() {
return new TableItem(fMainTable, SWT.NONE);
}
/**
* Method getItem.
*
* @param aIndex
* - int
* @return Item
*/
public Item getItem(int aIndex) {
return fMainTable.getItem(aIndex);
}
/**
* Method getSelectedItem.
*
* @return Item
*/
public Item getSelectedItem() {
if (0 == fMainTable.getSelection().length) {
return null;
}
return fMainTable.getSelection()[0]; //Only one element selectable at any given time
}
/**
* Method getItems.
*
* @return Item[]
*/
public Item[] getItems() {
return fMainTable.getItems();
}
/**
* Method getItemCount.
*
* @return int
*/
public int getItemCount() {
return fMainTable.getItemCount();
}
/**
* Method setEnabled.
*
* @param aEnabled
* - boolean
*/
public void setEnabled(boolean aEnabled) {
fEnabled = aEnabled;
for (TableItem item : fMainTable.getItems()) {
if (aEnabled) {
item.setForeground(UIUtils.ENABLED_FONT_COLOR);
} else {
item.setForeground(UIUtils.DISABLED_FONT_COLOR);
}
}
if (!aEnabled) {
fAddButton.setEnabled(aEnabled);
fRemoveButton.setEnabled(aEnabled);
} else {
updateButtons();
}
}
/**
* Method setVisible.
*
* @param aVisible
* - boolean
*/
public void setVisible(boolean aVisible) {
fMainComposite.setVisible(aVisible);
fMainTable.setVisible(aVisible);
fAddButton.setVisible(aVisible);
fRemoveButton.setVisible(aVisible);
}
/**
* Method getComposite.
*
* @return Composite
*/
public Composite getComposite() {
return fMainComposite;
}
/**
* Method setEditableValues.
*
* @param aValues
* - String[]
*/
public void setEditableValues(String[] aValues) {
fValues = aValues;
if (null == fValues || 0 == fValues.length) {
fAddButton.setEnabled(false);
} else {
fAddButton.setEnabled(true);
}
if (0 == fMainTable.getItemCount()) {
fRemoveButton.setEnabled(false);
} else {
fRemoveButton.setEnabled(true);
}
}
/**
* Method setTableHeader.
*
* @param aIndex
* int
* @param aText
* String
*/
public void setTableHeader(int aIndex, String aText) {
try {
final TableColumn column = fMainTable.getColumn(aIndex);
column.setText(aText);
updateTable();
} catch (IllegalArgumentException e) {
R4EUIPlugin.Ftracer.traceWarning("Exception: " + e.toString() + " (" + e.getMessage() + ")");
R4EUIPlugin.getDefault().logWarning("Exception: " + e.toString(), e);
}
}
/**
* Method setToolTipText.
*
* @param aTooltip
* String
*/
public void setToolTipText(String aTooltip) {
fMainComposite.setToolTipText(aTooltip);
}
/**
* Method updateButtons.
*/
public void updateButtons() {
if (0 == fMainTable.getItemCount()) {
fRemoveButton.setEnabled(false);
} else {
fRemoveButton.setEnabled(true);
}
fAddButton.setEnabled(true);
}
/**
* Method updateButtons.
*/
public void updateTable() {
for (TableColumn column : fMainTable.getColumns()) {
column.pack();
}
}
//Test methods
/**
* Method remove.
*
* @param removeStr
*/
public void remove(String removeStr) {
for (TableItem item : fMainTable.getItems()) {
if (item.getText().equals(removeStr)) {
fMainTable.setSelection(item);
}
}
fRemoveButton.notifyListeners(SWT.Selection, null);
}
/**
* Method add.
*
* @param addStr
*/
public void add(String addStr) {
fAddButton.notifyListeners(SWT.Selection, null);
if (null != fEditableControl) {
if (fEditableControl instanceof Text) {
((Text) fEditableControl).setText(addStr);
} else if (fEditableControl instanceof CCombo) {
final String[] items = ((CCombo) fEditableControl).getItems();
for (int index = 0; index < items.length; index++) {
if (items[index].equals(addStr)) {
((CCombo) fEditableControl).select(index);
}
}
}
}
}
}