1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.wizards.dialogfields;
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 import java.util.List;
17 import net.sourceforge.phpdt.internal.ui.util.PixelConverter;
18 import net.sourceforge.phpdt.internal.ui.util.SWTUtil;
19 import net.sourceforge.phpdt.internal.ui.util.TableLayoutComposite;
21 import org.eclipse.jface.util.Assert;
22 import org.eclipse.jface.viewers.ColumnLayoutData;
23 import org.eclipse.jface.viewers.ColumnWeightData;
24 import org.eclipse.jface.viewers.DoubleClickEvent;
25 import org.eclipse.jface.viewers.IDoubleClickListener;
26 import org.eclipse.jface.viewers.ILabelProvider;
27 import org.eclipse.jface.viewers.ISelection;
28 import org.eclipse.jface.viewers.ISelectionChangedListener;
29 import org.eclipse.jface.viewers.IStructuredContentProvider;
30 import org.eclipse.jface.viewers.IStructuredSelection;
31 import org.eclipse.jface.viewers.SelectionChangedEvent;
32 import org.eclipse.jface.viewers.StructuredSelection;
33 import org.eclipse.jface.viewers.TableLayout;
34 import org.eclipse.jface.viewers.TableViewer;
35 import org.eclipse.jface.viewers.Viewer;
36 import org.eclipse.jface.viewers.ViewerSorter;
37 import org.eclipse.swt.SWT;
38 import org.eclipse.swt.events.KeyAdapter;
39 import org.eclipse.swt.events.KeyEvent;
40 import org.eclipse.swt.events.SelectionEvent;
41 import org.eclipse.swt.events.SelectionListener;
42 import org.eclipse.swt.layout.GridData;
43 import org.eclipse.swt.layout.GridLayout;
44 import org.eclipse.swt.widgets.Button;
45 import org.eclipse.swt.widgets.Composite;
46 import org.eclipse.swt.widgets.Control;
47 import org.eclipse.swt.widgets.Display;
48 import org.eclipse.swt.widgets.Label;
49 import org.eclipse.swt.widgets.Table;
50 import org.eclipse.swt.widgets.TableColumn;
53 * A list with a button bar. Typical buttons are 'Add', 'Remove', 'Up' and
54 * 'Down'. List model is independend of widget creation. DialogFields controls
55 * are: Label, List and Composite containing buttons.
57 public class ListDialogField extends DialogField {
59 public static class ColumnsDescription {
60 private ColumnLayoutData[] columns;
62 private String[] headers;
64 private boolean drawLines;
66 public ColumnsDescription(ColumnLayoutData[] columns, String[] headers,
68 this.columns = columns;
69 this.headers = headers;
70 this.drawLines = drawLines;
73 public ColumnsDescription(String[] headers, boolean drawLines) {
74 this(createColumnWeightData(headers.length), headers, drawLines);
77 public ColumnsDescription(int nColumns, boolean drawLines) {
78 this(createColumnWeightData(nColumns), null, drawLines);
81 private static ColumnLayoutData[] createColumnWeightData(int nColumns) {
82 ColumnLayoutData[] data = new ColumnLayoutData[nColumns];
83 for (int i = 0; i < nColumns; i++) {
84 data[i] = new ColumnWeightData(1);
90 protected TableViewer fTable;
92 protected ILabelProvider fLabelProvider;
94 protected ListViewerAdapter fListViewerAdapter;
96 protected List fElements;
98 protected ViewerSorter fViewerSorter;
100 protected String[] fButtonLabels;
102 private Button[] fButtonControls;
104 private boolean[] fButtonsEnabled;
106 private int fRemoveButtonIndex;
108 private int fUpButtonIndex;
110 private int fDownButtonIndex;
112 private Label fLastSeparator;
114 private Control fTableControl;
116 private Composite fButtonsControl;
118 private ISelection fSelectionWhenEnabled;
120 private IListAdapter fListAdapter;
122 private Object fParentElement;
124 private ColumnsDescription fTableColumns;
127 * Creates the <code>ListDialogField</code>.
130 * A listener for button invocation, selection changes. Can be
132 * @param buttonLabels
133 * The labels of all buttons: <code>null</code> is a valid
134 * array entry and marks a separator.
136 * The label provider to render the table entries
138 public ListDialogField(IListAdapter adapter, String[] buttonLabels,
139 ILabelProvider lprovider) {
141 fListAdapter = adapter;
143 fLabelProvider = lprovider;
144 fListViewerAdapter = new ListViewerAdapter();
145 fParentElement = this;
147 fElements = new ArrayList(10);
149 fButtonLabels = buttonLabels;
150 if (fButtonLabels != null) {
151 int nButtons = fButtonLabels.length;
152 fButtonsEnabled = new boolean[nButtons];
153 for (int i = 0; i < nButtons; i++) {
154 fButtonsEnabled[i] = true;
159 fTableControl = null;
160 fButtonsControl = null;
161 fTableColumns = null;
163 fRemoveButtonIndex = -1;
165 fDownButtonIndex = -1;
169 * Sets the index of the 'remove' button in the button label array passed in
170 * the constructor. The behaviour of the button marked as the 'remove'
171 * button will then be handled internally. (enable state, button invocation
174 public void setRemoveButtonIndex(int removeButtonIndex) {
175 Assert.isTrue(removeButtonIndex < fButtonLabels.length);
176 fRemoveButtonIndex = removeButtonIndex;
180 * Sets the index of the 'up' button in the button label array passed in the
181 * constructor. The behaviour of the button marked as the 'up' button will
182 * then be handled internally. (enable state, button invocation behaviour)
184 public void setUpButtonIndex(int upButtonIndex) {
185 Assert.isTrue(upButtonIndex < fButtonLabels.length);
186 fUpButtonIndex = upButtonIndex;
190 * Sets the index of the 'down' button in the button label array passed in
191 * the constructor. The behaviour of the button marked as the 'down' button
192 * will then be handled internally. (enable state, button invocation
195 public void setDownButtonIndex(int downButtonIndex) {
196 Assert.isTrue(downButtonIndex < fButtonLabels.length);
197 fDownButtonIndex = downButtonIndex;
201 * Sets the viewerSorter.
203 * @param viewerSorter
204 * The viewerSorter to set
206 public void setViewerSorter(ViewerSorter viewerSorter) {
207 fViewerSorter = viewerSorter;
210 public void setTableColumns(ColumnsDescription column) {
211 fTableColumns = column;
214 // ------ adapter communication
216 private void buttonPressed(int index) {
217 if (!managedButtonPressed(index) && fListAdapter != null) {
218 fListAdapter.customButtonPressed(this, index);
223 * Checks if the button pressed is handled internally
225 * @return Returns true if button has been handled.
227 protected boolean managedButtonPressed(int index) {
228 if (index == fRemoveButtonIndex) {
230 } else if (index == fUpButtonIndex) {
232 } else if (index == fDownButtonIndex) {
240 // ------ layout helpers
243 * @see DialogField#doFillIntoGrid
245 public Control[] doFillIntoGrid(Composite parent, int nColumns) {
246 PixelConverter converter = new PixelConverter(parent);
248 assertEnoughColumns(nColumns);
250 Label label = getLabelControl(parent);
251 GridData gd = gridDataForLabel(1);
252 gd.verticalAlignment = GridData.BEGINNING;
253 label.setLayoutData(gd);
255 Control list = getListControl(parent);
257 gd.horizontalAlignment = GridData.FILL;
258 gd.grabExcessHorizontalSpace = false;
259 gd.verticalAlignment = GridData.FILL;
260 gd.grabExcessVerticalSpace = true;
261 gd.horizontalSpan = nColumns - 2;
262 gd.widthHint = converter.convertWidthInCharsToPixels(50);
263 gd.heightHint = converter.convertHeightInCharsToPixels(6);
265 list.setLayoutData(gd);
267 Composite buttons = getButtonBox(parent);
269 gd.horizontalAlignment = GridData.FILL;
270 gd.grabExcessHorizontalSpace = false;
271 gd.verticalAlignment = GridData.FILL;
272 gd.grabExcessVerticalSpace = true;
273 gd.horizontalSpan = 1;
274 buttons.setLayoutData(gd);
276 return new Control[] { label, list, buttons };
280 * @see DialogField#getNumberOfControls
282 public int getNumberOfControls() {
287 * Sets the minimal width of the buttons. Must be called after widget
290 public void setButtonsMinWidth(int minWidth) {
291 if (fLastSeparator != null) {
292 ((GridData) fLastSeparator.getLayoutData()).widthHint = minWidth;
296 // ------ ui creation
299 * Returns the list control. When called the first time, the control will be
303 * parent composite when called the first time, or
304 * <code>null</code> after.
306 public Control getListControl(Composite parent) {
307 if (fTableControl == null) {
308 assertCompositeNotNull(parent);
310 if (fTableColumns == null) {
311 fTable = createTableViewer(parent);
312 Table tableControl = fTable.getTable();
314 fTableControl = tableControl;
315 tableControl.setLayout(new TableLayout());
317 TableLayoutComposite composite = new TableLayoutComposite(
319 fTableControl = composite;
321 fTable = createTableViewer(composite);
322 Table tableControl = fTable.getTable();
324 tableControl.setHeaderVisible(fTableColumns.headers != null);
325 tableControl.setLinesVisible(fTableColumns.drawLines);
326 ColumnLayoutData[] columns = fTableColumns.columns;
327 for (int i = 0; i < columns.length; i++) {
328 composite.addColumnData(columns[i]);
329 TableColumn column = new TableColumn(tableControl, SWT.NONE);
330 // tableLayout.addColumnData(columns[i]);
331 if (fTableColumns.headers != null) {
332 column.setText(fTableColumns.headers[i]);
337 fTable.getTable().addKeyListener(new KeyAdapter() {
338 public void keyPressed(KeyEvent e) {
343 // fTableControl.setLayout(tableLayout);
345 fTable.setContentProvider(fListViewerAdapter);
346 fTable.setLabelProvider(fLabelProvider);
347 fTable.addSelectionChangedListener(fListViewerAdapter);
348 fTable.addDoubleClickListener(fListViewerAdapter);
350 fTable.setInput(fParentElement);
352 if (fViewerSorter != null) {
353 fTable.setSorter(fViewerSorter);
356 fTableControl.setEnabled(isEnabled());
357 if (fSelectionWhenEnabled != null) {
358 postSetSelection(fSelectionWhenEnabled);
361 return fTableControl;
365 * Returns the internally used table viewer.
367 public TableViewer getTableViewer() {
372 * Subclasses may override to specify a different style.
374 protected int getListStyle() {
375 int style = SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL;
376 if (fTableColumns != null) {
377 style |= SWT.FULL_SELECTION;
382 protected TableViewer createTableViewer(Composite parent) {
383 Table table = new Table(parent, getListStyle());
384 return new TableViewer(table);
387 protected Button createButton(Composite parent, String label,
388 SelectionListener listener) {
389 Button button = new Button(parent, SWT.PUSH);
390 button.setText(label);
391 button.addSelectionListener(listener);
392 GridData gd = new GridData();
393 gd.horizontalAlignment = GridData.FILL;
394 gd.grabExcessHorizontalSpace = true;
395 gd.verticalAlignment = GridData.BEGINNING;
396 gd.heightHint = SWTUtil.getButtonHeightHint(button);
397 gd.widthHint = SWTUtil.getButtonWidthHint(button);
399 button.setLayoutData(gd);
403 private Label createSeparator(Composite parent) {
404 Label separator = new Label(parent, SWT.NONE);
405 separator.setVisible(false);
406 GridData gd = new GridData();
407 gd.horizontalAlignment = GridData.FILL;
408 gd.verticalAlignment = GridData.BEGINNING;
410 separator.setLayoutData(gd);
415 * Returns the composite containing the buttons. When called the first time,
416 * the control will be created.
419 * parent composite when called the first time, or
420 * <code>null</code> after.
422 public Composite getButtonBox(Composite parent) {
423 if (fButtonsControl == null) {
424 assertCompositeNotNull(parent);
426 SelectionListener listener = new SelectionListener() {
427 public void widgetDefaultSelected(SelectionEvent e) {
431 public void widgetSelected(SelectionEvent e) {
436 Composite contents = new Composite(parent, SWT.NULL);
437 GridLayout layout = new GridLayout();
438 layout.marginWidth = 0;
439 layout.marginHeight = 0;
440 contents.setLayout(layout);
442 if (fButtonLabels != null) {
443 fButtonControls = new Button[fButtonLabels.length];
444 for (int i = 0; i < fButtonLabels.length; i++) {
445 String currLabel = fButtonLabels[i];
446 if (currLabel != null) {
447 fButtonControls[i] = createButton(contents, currLabel,
449 fButtonControls[i].setEnabled(isEnabled()
450 && fButtonsEnabled[i]);
452 fButtonControls[i] = null;
453 createSeparator(contents);
458 fLastSeparator = createSeparator(contents);
461 fButtonsControl = contents;
464 return fButtonsControl;
467 private void doButtonSelected(SelectionEvent e) {
468 if (fButtonControls != null) {
469 for (int i = 0; i < fButtonControls.length; i++) {
470 if (e.widget == fButtonControls[i]) {
479 * Handles key events in the table viewer. Specifically when the delete key
482 protected void handleKeyPressed(KeyEvent event) {
483 if (event.character == SWT.DEL && event.stateMask == 0) {
484 if (fRemoveButtonIndex != -1
485 && isButtonEnabled(fTable.getSelection(),
486 fRemoveButtonIndex)) {
487 managedButtonPressed(fRemoveButtonIndex);
492 // ------ enable / disable management
495 * @see DialogField#dialogFieldChanged
497 public void dialogFieldChanged() {
498 super.dialogFieldChanged();
503 * Updates the enable state of the all buttons
505 protected void updateButtonState() {
506 if (fButtonControls != null) {
507 ISelection sel = fTable.getSelection();
508 for (int i = 0; i < fButtonControls.length; i++) {
509 Button button = fButtonControls[i];
510 if (isOkToUse(button)) {
511 button.setEnabled(isButtonEnabled(sel, i));
517 protected boolean getManagedButtonState(ISelection sel, int index) {
518 if (index == fRemoveButtonIndex) {
519 return !sel.isEmpty();
520 } else if (index == fUpButtonIndex) {
521 return !sel.isEmpty() && canMoveUp();
522 } else if (index == fDownButtonIndex) {
523 return !sel.isEmpty() && canMoveDown();
529 * @see DialogField#updateEnableState
531 protected void updateEnableState() {
532 super.updateEnableState();
534 boolean enabled = isEnabled();
535 if (isOkToUse(fTableControl)) {
537 fSelectionWhenEnabled = fTable.getSelection();
538 selectElements(null);
540 selectElements(fSelectionWhenEnabled);
541 fSelectionWhenEnabled = null;
543 fTableControl.setEnabled(enabled);
549 * Sets a button enabled or disabled.
551 public void enableButton(int index, boolean enable) {
552 if (fButtonsEnabled != null && index < fButtonsEnabled.length) {
553 fButtonsEnabled[index] = enable;
558 private boolean isButtonEnabled(ISelection sel, int index) {
559 boolean extraState = getManagedButtonState(sel, index);
560 return isEnabled() && extraState && fButtonsEnabled[index];
563 // ------ model access
566 * Sets the elements shown in the list.
568 public void setElements(List elements) {
569 fElements = new ArrayList(elements);
570 if (fTable != null) {
573 dialogFieldChanged();
577 * Gets the elements shown in the list. The list returned is a copy, so it
578 * can be modified by the user.
580 public List getElements() {
581 return new ArrayList(fElements);
585 * Gets the elements shown at the given index.
587 public Object getElement(int index) {
588 return fElements.get(index);
592 * Gets the index of an element in the list or -1 if element is not in list.
594 public int getIndexOfElement(Object elem) {
595 return fElements.indexOf(elem);
599 * Replace an element.
601 public void replaceElement(Object oldElement, Object newElement)
602 throws IllegalArgumentException {
603 int idx = fElements.indexOf(oldElement);
605 fElements.set(idx, newElement);
606 if (fTable != null) {
607 List selected = getSelectedElements();
608 if (selected.remove(oldElement)) {
609 selected.add(newElement);
612 selectElements(new StructuredSelection(selected));
614 dialogFieldChanged();
616 throw new IllegalArgumentException();
621 * Adds an element at the end of the list.
623 public void addElement(Object element) {
624 if (fElements.contains(element)) {
627 fElements.add(element);
628 if (fTable != null) {
631 dialogFieldChanged();
635 * Adds elements at the end of the list.
637 public void addElements(List elements) {
638 int nElements = elements.size();
642 ArrayList elementsToAdd = new ArrayList(nElements);
644 for (int i = 0; i < nElements; i++) {
645 Object elem = elements.get(i);
646 if (!fElements.contains(elem)) {
647 elementsToAdd.add(elem);
650 fElements.addAll(elementsToAdd);
651 if (fTable != null) {
652 fTable.add(elementsToAdd.toArray());
654 dialogFieldChanged();
659 * Adds an element at a position.
661 public void insertElementAt(Object element, int index) {
662 if (fElements.contains(element)) {
665 fElements.add(index, element);
666 if (fTable != null) {
670 dialogFieldChanged();
674 * Adds an element at a position.
676 public void removeAllElements() {
677 if (fElements.size() > 0) {
679 if (fTable != null) {
682 dialogFieldChanged();
687 * Removes an element from the list.
689 public void removeElement(Object element) throws IllegalArgumentException {
690 if (fElements.remove(element)) {
691 if (fTable != null) {
692 fTable.remove(element);
694 dialogFieldChanged();
696 throw new IllegalArgumentException();
701 * Removes elements from the list.
703 public void removeElements(List elements) {
704 if (elements.size() > 0) {
705 fElements.removeAll(elements);
706 if (fTable != null) {
707 fTable.remove(elements.toArray());
709 dialogFieldChanged();
714 * Gets the number of elements
716 public int getSize() {
717 return fElements.size();
720 public void selectElements(ISelection selection) {
721 fSelectionWhenEnabled = selection;
722 if (fTable != null) {
723 fTable.setSelection(selection, true);
727 public void selectFirstElement() {
728 Object element = null;
729 if (fViewerSorter != null) {
730 Object[] arr = fElements.toArray();
731 fViewerSorter.sort(fTable, arr);
732 if (arr.length > 0) {
736 if (fElements.size() > 0) {
737 element = fElements.get(0);
740 if (element != null) {
741 selectElements(new StructuredSelection(element));
745 public void postSetSelection(final ISelection selection) {
746 if (isOkToUse(fTableControl)) {
747 Display d = fTableControl.getDisplay();
748 d.asyncExec(new Runnable() {
750 if (isOkToUse(fTableControl)) {
751 selectElements(selection);
759 * Refreshes the table.
761 public void refresh() {
762 if (fTable != null) {
767 // ------- list maintenance
769 private List moveUp(List elements, List move) {
770 int nElements = elements.size();
771 List res = new ArrayList(nElements);
772 Object floating = null;
773 for (int i = 0; i < nElements; i++) {
774 Object curr = elements.get(i);
775 if (move.contains(curr)) {
778 if (floating != null) {
784 if (floating != null) {
790 private void moveUp(List toMoveUp) {
791 if (toMoveUp.size() > 0) {
792 setElements(moveUp(fElements, toMoveUp));
793 fTable.reveal(toMoveUp.get(0));
797 private void moveDown(List toMoveDown) {
798 if (toMoveDown.size() > 0) {
799 setElements(reverse(moveUp(reverse(fElements), toMoveDown)));
800 fTable.reveal(toMoveDown.get(toMoveDown.size() - 1));
804 private List reverse(List p) {
805 List reverse = new ArrayList(p.size());
806 for (int i = p.size() - 1; i >= 0; i--) {
807 reverse.add(p.get(i));
812 private void remove() {
813 removeElements(getSelectedElements());
817 moveUp(getSelectedElements());
820 private void down() {
821 moveDown(getSelectedElements());
824 private boolean canMoveUp() {
825 if (isOkToUse(fTableControl)) {
826 int[] indc = fTable.getTable().getSelectionIndices();
827 for (int i = 0; i < indc.length; i++) {
836 private boolean canMoveDown() {
837 if (isOkToUse(fTableControl)) {
838 int[] indc = fTable.getTable().getSelectionIndices();
839 int k = fElements.size() - 1;
840 for (int i = indc.length - 1; i >= 0; i--, k--) {
850 * Returns the selected elements.
852 public List getSelectedElements() {
853 List result = new ArrayList();
854 if (fTable != null) {
855 ISelection selection = fTable.getSelection();
856 if (selection instanceof IStructuredSelection) {
857 Iterator iter = ((IStructuredSelection) selection).iterator();
858 while (iter.hasNext()) {
859 result.add(iter.next());
866 // ------- ListViewerAdapter
868 private class ListViewerAdapter implements IStructuredContentProvider,
869 ISelectionChangedListener, IDoubleClickListener {
871 // ------- ITableContentProvider Interface ------------
873 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
877 public boolean isDeleted(Object element) {
881 public void dispose() {
884 public Object[] getElements(Object obj) {
885 return fElements.toArray();
888 // ------- ISelectionChangedListener Interface ------------
890 public void selectionChanged(SelectionChangedEvent event) {
891 doListSelected(event);
897 * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
899 public void doubleClick(DoubleClickEvent event) {
900 doDoubleClick(event);
905 protected void doListSelected(SelectionChangedEvent event) {
907 if (fListAdapter != null) {
908 fListAdapter.selectionChanged(this);
912 protected void doDoubleClick(DoubleClickEvent event) {
913 if (fListAdapter != null) {
914 fListAdapter.doubleClicked(this);