| /******************************************************************************* |
| * Copyright (c) 2008, 2009 Oracle. 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: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.ui.internal.widgets; |
| |
| import org.eclipse.jface.viewers.IBaseLabelProvider; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ITableLabelProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jpt.ui.internal.listeners.SWTPropertyChangeListenerWrapper; |
| import org.eclipse.jpt.ui.internal.swt.ColumnAdapter; |
| import org.eclipse.jpt.ui.internal.swt.TableModelAdapter; |
| import org.eclipse.jpt.ui.internal.swt.TableModelAdapter.SelectionChangeEvent; |
| import org.eclipse.jpt.ui.internal.swt.TableModelAdapter.SelectionChangeListener; |
| import org.eclipse.jpt.ui.internal.util.SWTUtil; |
| import org.eclipse.jpt.utility.internal.model.value.SimplePropertyValueModel; |
| import org.eclipse.jpt.utility.internal.model.value.swing.ObjectListSelectionModel; |
| import org.eclipse.jpt.utility.model.Model; |
| import org.eclipse.jpt.utility.model.event.ListAddEvent; |
| import org.eclipse.jpt.utility.model.event.ListChangeEvent; |
| import org.eclipse.jpt.utility.model.event.ListClearEvent; |
| import org.eclipse.jpt.utility.model.event.ListMoveEvent; |
| import org.eclipse.jpt.utility.model.event.ListRemoveEvent; |
| import org.eclipse.jpt.utility.model.event.ListReplaceEvent; |
| import org.eclipse.jpt.utility.model.event.PropertyChangeEvent; |
| import org.eclipse.jpt.utility.model.listener.PropertyChangeListener; |
| import org.eclipse.jpt.utility.model.value.ListValueModel; |
| import org.eclipse.jpt.utility.model.value.PropertyValueModel; |
| import org.eclipse.jpt.utility.model.value.WritablePropertyValueModel; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Layout; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| |
| /** |
| * This implementation of the <code>AddRemovePane</code> uses a <code>Table</code> |
| * as its main widget, a <code>List</code> can't be used because it doesn't |
| * support showing images. However, the table is displayed like a list. |
| * <p> |
| * Here the layot of this pane: |
| * <pre> |
| * ----------------------------------------------------------------------------- |
| * | ------------------------------------------------------------- ----------- | |
| * | | Item 1 | | Add... | | |
| * | | ... | ----------- | |
| * | | Item n | ----------- | |
| * | | | | Edit... | | |
| * | | | ----------- | |
| * | | | ----------- | |
| * | | | | Remove | | |
| * | | | ----------- | |
| * | ------------------------------------------------------------- | |
| * -----------------------------------------------------------------------------</pre> |
| * |
| * @version 2.0 |
| * @since 1.0 |
| */ |
| @SuppressWarnings("nls") |
| public class AddRemoveListPane<T extends Model> extends AddRemovePane<T> |
| { |
| |
| /** |
| * The main widget of this add/remove pane. |
| */ |
| private Table table; |
| |
| /** |
| * Creates a new <code>AddRemoveListPane</code>. |
| * |
| * @param parentPane The parent container of this one |
| * @param parent The parent container |
| * @param adapter |
| * @param listHolder The <code>ListValueModel</code> containing the items |
| * @param selectedItemHolder The holder of the selected item, if more than |
| * one item or no items are selected, then <code>null</code> will be passed |
| * @param labelProvider The renderer used to format the table holder's items |
| */ |
| public AddRemoveListPane(Pane<? extends T> parentPane, |
| Composite parent, |
| Adapter adapter, |
| ListValueModel<?> listHolder, |
| WritablePropertyValueModel<?> selectedItemHolder, |
| ILabelProvider labelProvider) { |
| |
| super(parentPane, |
| parent, |
| adapter, |
| listHolder, |
| selectedItemHolder, |
| labelProvider); |
| } |
| |
| /** |
| * Creates a new <code>AddRemoveListPane</code>. |
| * |
| * @param parentPane The parent container of this one |
| * @param parent The parent container |
| * @param adapter |
| * @param listHolder The <code>ListValueModel</code> containing the items |
| * @param selectedItemHolder The holder of the selected item, if more than |
| * one item or no items are selected, then <code>null</code> will be passed |
| * @param labelProvider The renderer used to format the table holder's items |
| * @param helpId The topic help ID to be registered with this pane |
| */ |
| public AddRemoveListPane(Pane<? extends T> parentPane, |
| Composite parent, |
| Adapter adapter, |
| ListValueModel<?> listHolder, |
| WritablePropertyValueModel<?> selectedItemHolder, |
| ILabelProvider labelProvider, |
| String helpId) { |
| |
| super(parentPane, |
| parent, |
| adapter, |
| listHolder, |
| selectedItemHolder, |
| labelProvider, |
| helpId); |
| } |
| |
| /** |
| * Creates a new <code>AddRemoveListPane</code>. |
| * |
| * @param parentPane The parent container of this one |
| * @param parent The parent container |
| * @param adapter |
| * @param listHolder The <code>ListValueModel</code> containing the items |
| * @param selectedItemHolder The holder of the selected item, if more than |
| * one item or no items are selected, then <code>null</code> will be passed |
| * @param labelProvider The renderer used to format the table holder's items |
| * @param helpId The topic help ID to be registered with this pane |
| * @param parentManagePane <code>true</code> to have the parent pane manage |
| * the enabled state of this pane |
| */ |
| public AddRemoveListPane(Pane<? extends T> parentPane, |
| Composite parent, |
| Adapter adapter, |
| ListValueModel<?> listHolder, |
| WritablePropertyValueModel<?> selectedItemHolder, |
| ILabelProvider labelProvider, |
| String helpId, |
| boolean parentManagePane) { |
| |
| super(parentPane, |
| parent, |
| adapter, |
| listHolder, |
| selectedItemHolder, |
| labelProvider, |
| helpId, |
| parentManagePane); |
| } |
| |
| /** |
| * Creates a new <code>AddRemoveListPane</code>. |
| * |
| * @param parentPane The parent container of this one |
| * @param subjectHolder The holder of the subject |
| * @param adapter |
| * @param parent The parent container |
| * @param listHolder The <code>ListValueModel</code> containing the items |
| * @param selectedItemHolder The holder of the selected item, if more than |
| * one item or no items are selected, then <code>null</code> will be passed |
| * @param labelProvider The renderer used to format the table holder's items |
| */ |
| public AddRemoveListPane(Pane<?> parentPane, |
| PropertyValueModel<? extends T> subjectHolder, |
| Composite parent, |
| Adapter adapter, |
| ListValueModel<?> listHolder, |
| WritablePropertyValueModel<?> selectedItemHolder, |
| ILabelProvider labelProvider) { |
| |
| super(parentPane, |
| subjectHolder, |
| parent, |
| adapter, |
| listHolder, |
| selectedItemHolder, |
| labelProvider); |
| } |
| |
| /** |
| * Creates a new <code>AddRemoveListPane</code>. |
| * |
| * @param parentPane The parent container of this one |
| * @param subjectHolder The holder of the subject |
| * @param adapter |
| * @param parent The parent container |
| * @param listHolder The <code>ListValueModel</code> containing the items |
| * @param selectedItemHolder The holder of the selected item, if more than |
| * one item or no items are selected, then <code>null</code> will be passed |
| * @param labelProvider The renderer used to format the table holder's items |
| * @param helpId The topic help ID to be registered with this pane |
| */ |
| public AddRemoveListPane(Pane<?> parentPane, |
| PropertyValueModel<? extends T> subjectHolder, |
| Composite parent, |
| Adapter adapter, |
| ListValueModel<?> listHolder, |
| WritablePropertyValueModel<?> selectedItemHolder, |
| ILabelProvider labelProvider, |
| String helpId) { |
| |
| super(parentPane, |
| subjectHolder, |
| parent, |
| adapter, |
| listHolder, |
| selectedItemHolder, |
| labelProvider, |
| helpId); |
| } |
| |
| private ColumnAdapter<Object> buildColumnAdapter() { |
| return new ColumnAdapter<Object>() { |
| public WritablePropertyValueModel<?>[] cellModels(Object subject) { |
| WritablePropertyValueModel<?>[] valueHolders = new WritablePropertyValueModel<?>[1]; |
| valueHolders[0] = new SimplePropertyValueModel<Object>(subject); |
| return valueHolders; |
| } |
| |
| public int columnCount() { |
| return 1; |
| } |
| |
| public String columnName(int columnIndex) { |
| return ""; |
| } |
| }; |
| } |
| |
| @Override |
| protected void itemsAdded(ListAddEvent e) { |
| super.itemsAdded(e); |
| revalidateLayout(); |
| } |
| |
| @Override |
| protected void itemsMoved(ListMoveEvent e) { |
| super.itemsMoved(e); |
| revalidateLayout(); |
| } |
| |
| @Override |
| protected void itemsRemoved(ListRemoveEvent e) { |
| super.itemsRemoved(e); |
| revalidateLayout(); |
| } |
| |
| @Override |
| protected void itemsReplaced(ListReplaceEvent e) { |
| super.itemsReplaced(e); |
| revalidateLayout(); |
| } |
| |
| @Override |
| protected void listChanged(ListChangeEvent e) { |
| super.listChanged(e); |
| revalidateLayout(); |
| } |
| |
| @Override |
| protected void listCleared(ListClearEvent e) { |
| super.listCleared(e); |
| revalidateLayout(); |
| } |
| |
| /** |
| * Revalidates the table layout after the list of items has changed. The |
| * layout has to be done in a new UI thread because our listener might be |
| * notified before the table has been updated (table column added or removed). |
| */ |
| private void revalidateLayout() { |
| SWTUtil.asyncExec(new Runnable() { public void run() { |
| if (!table.isDisposed()) { |
| table.getParent().computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| table.getParent().layout(); |
| } |
| }}); |
| } |
| |
| private PropertyChangeListener buildSelectedItemPropertyChangeListener() { |
| return new SWTPropertyChangeListenerWrapper( |
| buildSelectedItemPropertyChangeListener_() |
| ); |
| } |
| |
| private PropertyChangeListener buildSelectedItemPropertyChangeListener_() { |
| return new PropertyChangeListener() { |
| public void propertyChanged(PropertyChangeEvent e) { |
| if (table.isDisposed()) { |
| return; |
| } |
| getSelectionModel().setSelectedValue(e.getNewValue()); |
| updateButtons(); |
| } |
| }; |
| } |
| |
| private SelectionChangeListener<Object> buildSelectionListener() { |
| return new SelectionChangeListener<Object>() { |
| public void selectionChanged(SelectionChangeEvent<Object> e) { |
| AddRemoveListPane.this.selectionChanged(); |
| } |
| }; |
| } |
| |
| private Composite addTableContainer(Composite container) { |
| |
| container = addPane(container, buildTableContainerLayout()); |
| container.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| return container; |
| } |
| |
| private Layout buildTableContainerLayout() { |
| return new Layout() { |
| @Override |
| protected Point computeSize(Composite composite, |
| int widthHint, |
| int heightHint, |
| boolean flushCache) { |
| |
| Table table = (Table) composite.getChildren()[0]; |
| TableColumn tableColumn = table.getColumn(0); |
| int columnWidth = tableColumn.getWidth(); |
| packColumn(table); |
| |
| // Calculate the table size and adjust it with the hints |
| Point size = table.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| |
| if (widthHint != SWT.DEFAULT) { |
| size.x = widthHint; |
| } |
| |
| if (heightHint != SWT.DEFAULT) { |
| size.y = heightHint; |
| } |
| |
| // Revert the column's width to its current value |
| table.setRedraw(false); |
| table.setLayoutDeferred(true); |
| tableColumn.setWidth(columnWidth); |
| table.setLayoutDeferred(false); |
| table.setRedraw(true); |
| |
| return size; |
| } |
| |
| private boolean isVerticalScrollbarBarVisible(Table table, |
| Rectangle clientArea) { |
| |
| // Get the height of all the rows |
| int height = table.getItemCount() * table.getItemHeight(); |
| |
| // Remove the border from the height |
| height += (table.getBorderWidth() * 2); |
| |
| return (clientArea.height < height); |
| } |
| |
| @Override |
| protected void layout(Composite composite, boolean flushCache) { |
| |
| Rectangle bounds = composite.getClientArea(); |
| |
| if (bounds.width > 0) { |
| |
| Table table = (Table) composite.getChildren()[0]; |
| table.setBounds(0, 0, bounds.width, bounds.height); |
| |
| updateTableColumnWidth( |
| table, |
| bounds.width, |
| isVerticalScrollbarBarVisible(table, bounds) |
| ); |
| } |
| } |
| |
| private void packColumn(Table table) { |
| |
| TableColumn tableColumn = table.getColumn(0); |
| |
| table.setRedraw(false); |
| table.setLayoutDeferred(true); |
| tableColumn.pack(); |
| table.setLayoutDeferred(false); |
| table.setRedraw(true); |
| |
| // Cache the column width so it can be used in |
| // updateTableColumnWidth() when determine which width to use |
| table.setData( |
| "column.width", |
| Integer.valueOf(tableColumn.getWidth()) |
| ); |
| } |
| |
| private void updateTableColumnWidth(Table table, |
| int width, |
| boolean verticalScrollbarBarVisible) { |
| |
| // Remove the border from the width |
| width -= (table.getBorderWidth() * 2); |
| |
| // Remove the scrollbar from the width if it is shown |
| if (verticalScrollbarBarVisible) { |
| width -= table.getVerticalBar().getSize().x; |
| } |
| |
| TableColumn tableColumn = table.getColumn(0); |
| |
| // Retrieve the cached column width, which is required for |
| // determining which width to use (the column width or the |
| // calculated width) |
| Integer columnWitdh = (Integer) table.getData("column.width"); |
| |
| // Use the calculated width if the column is smaller, otherwise |
| // use the column width and a horizontal scroll bar will show up |
| width = Math.max(width, columnWitdh); |
| |
| // Adjust the column width |
| tableColumn.setWidth(width); |
| } |
| }; |
| } |
| |
| private ITableLabelProvider buildTableLabelProvider(IBaseLabelProvider labelProvider) { |
| return new TableLabelProvider((ILabelProvider) labelProvider); |
| } |
| |
| /* |
| * (non-Javadoc) |
| */ |
| @Override |
| public Table getMainControl() { |
| return table; |
| } |
| |
| /* |
| * (non-Javadoc) |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| protected void initializeMainComposite(Composite container, |
| Adapter adapter, |
| ListValueModel<?> listHolder, |
| WritablePropertyValueModel<?> selectedItemHolder, |
| IBaseLabelProvider labelProvider, |
| String helpId) { |
| |
| table = addUnmanagedTable( |
| addTableContainer(container), |
| SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI, |
| helpId |
| ); |
| |
| |
| TableModelAdapter model = TableModelAdapter.adapt( |
| (ListValueModel<Object>) listHolder, |
| getSelectedItemHolder(), |
| table, |
| buildColumnAdapter(), |
| buildTableLabelProvider(labelProvider) |
| ); |
| |
| model.addSelectionChangeListener(buildSelectionListener()); |
| |
| selectedItemHolder.addPropertyChangeListener( |
| PropertyValueModel.VALUE, |
| buildSelectedItemPropertyChangeListener() |
| ); |
| |
| initializeTable(table); |
| } |
| |
| /** |
| * Initializes the given table, which acts like a list in our case. |
| * |
| * @param table The main widget of this pane |
| */ |
| protected void initializeTable(Table table) { |
| |
| table.setData("column.width", new Integer(0)); |
| table.setHeaderVisible(false); |
| table.setLinesVisible(false); |
| } |
| |
| /** |
| * The selection has changed, update (1) the selected item holder, (2) the |
| * selection model and (3) the buttons. |
| */ |
| private void selectionChanged() { |
| WritablePropertyValueModel<Object> selectedItemHolder = getSelectedItemHolder(); |
| ObjectListSelectionModel selectionModel = getSelectionModel(); |
| int selectionCount = this.table.getSelectionCount(); |
| |
| if (selectionCount == 0) { |
| selectedItemHolder.setValue(null); |
| selectionModel.clearSelection(); |
| } |
| else if (selectionCount != 1) { |
| selectedItemHolder.setValue(null); |
| selectionModel.clearSelection(); |
| |
| for (int index : this.table.getSelectionIndices()) { |
| selectionModel.addSelectionInterval(index, index); |
| } |
| } |
| else { |
| int selectedIndex = this.table.getSelectionIndex(); |
| Object selectedItem = getListHolder().get(selectedIndex); |
| |
| selectedItemHolder.setValue(selectedItem); |
| selectionModel.setSelectedValue(selectedItem); |
| } |
| |
| updateButtons(); |
| } |
| |
| /** |
| * This label provider simply delegates the rendering to the provided |
| * <code>ILabelProvider</code>. |
| */ |
| private class TableLabelProvider extends LabelProvider |
| implements ITableLabelProvider { |
| |
| private ILabelProvider labelProvider; |
| |
| TableLabelProvider(ILabelProvider labelProvider) { |
| super(); |
| this.labelProvider = labelProvider; |
| } |
| |
| public Image getColumnImage(Object element, int columnIndex) { |
| return labelProvider.getImage(element); |
| } |
| |
| public String getColumnText(Object element, int columnIndex) { |
| return labelProvider.getText(element); |
| } |
| } |
| } |