| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |
| } |
| } |
| } |