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;
22 //import org.eclipse.jface.text.Assert;
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.jface.viewers.ColumnLayoutData;
25 import org.eclipse.jface.viewers.ColumnWeightData;
26 import org.eclipse.jface.viewers.DoubleClickEvent;
27 import org.eclipse.jface.viewers.IDoubleClickListener;
28 import org.eclipse.jface.viewers.ILabelProvider;
29 import org.eclipse.jface.viewers.ISelection;
30 import org.eclipse.jface.viewers.ISelectionChangedListener;
31 import org.eclipse.jface.viewers.IStructuredContentProvider;
32 import org.eclipse.jface.viewers.IStructuredSelection;
33 import org.eclipse.jface.viewers.SelectionChangedEvent;
34 import org.eclipse.jface.viewers.StructuredSelection;
35 import org.eclipse.jface.viewers.TableLayout;
36 import org.eclipse.jface.viewers.TableViewer;
37 import org.eclipse.jface.viewers.Viewer;
38 import org.eclipse.jface.viewers.ViewerSorter;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.events.KeyAdapter;
41 import org.eclipse.swt.events.KeyEvent;
42 import org.eclipse.swt.events.SelectionEvent;
43 import org.eclipse.swt.events.SelectionListener;
44 import org.eclipse.swt.layout.GridData;
45 import org.eclipse.swt.layout.GridLayout;
46 import org.eclipse.swt.widgets.Button;
47 import org.eclipse.swt.widgets.Composite;
48 import org.eclipse.swt.widgets.Control;
49 import org.eclipse.swt.widgets.Display;
50 import org.eclipse.swt.widgets.Label;
51 import org.eclipse.swt.widgets.Table;
52 import org.eclipse.swt.widgets.TableColumn;
55 * A list with a button bar. Typical buttons are 'Add', 'Remove', 'Up' and
56 * 'Down'. List model is independend of widget creation. DialogFields controls
57 * are: Label, List and Composite containing buttons.
59 public class ListDialogField extends DialogField {
61 public static class ColumnsDescription {
62 private ColumnLayoutData[] columns;
64 private String[] headers;
66 private boolean drawLines;
68 public ColumnsDescription(ColumnLayoutData[] columns, String[] headers,
70 this.columns = columns;
71 this.headers = headers;
72 this.drawLines = drawLines;
75 public ColumnsDescription(String[] headers, boolean drawLines) {
76 this(createColumnWeightData(headers.length), headers, drawLines);
79 public ColumnsDescription(int nColumns, boolean drawLines) {
80 this(createColumnWeightData(nColumns), null, drawLines);
83 private static ColumnLayoutData[] createColumnWeightData(int nColumns) {
84 ColumnLayoutData[] data = new ColumnLayoutData[nColumns];
85 for (int i = 0; i < nColumns; i++) {
86 data[i] = new ColumnWeightData(1);
92 protected TableViewer fTable;
94 protected ILabelProvider fLabelProvider;
96 protected ListViewerAdapter fListViewerAdapter;
98 protected List fElements;
100 protected ViewerSorter fViewerSorter;
102 protected String[] fButtonLabels;
104 private Button[] fButtonControls;
106 private boolean[] fButtonsEnabled;
108 private int fRemoveButtonIndex;
110 private int fUpButtonIndex;
112 private int fDownButtonIndex;
114 private Label fLastSeparator;
116 private Control fTableControl;
118 private Composite fButtonsControl;
120 private ISelection fSelectionWhenEnabled;
122 private IListAdapter fListAdapter;
124 private Object fParentElement;
126 private ColumnsDescription fTableColumns;
129 * Creates the <code>ListDialogField</code>.
132 * A listener for button invocation, selection changes. Can be
134 * @param buttonLabels
135 * The labels of all buttons: <code>null</code> is a valid
136 * array entry and marks a separator.
138 * The label provider to render the table entries
140 public ListDialogField(IListAdapter adapter, String[] buttonLabels,
141 ILabelProvider lprovider) {
143 fListAdapter = adapter;
145 fLabelProvider = lprovider;
146 fListViewerAdapter = new ListViewerAdapter();
147 fParentElement = this;
149 fElements = new ArrayList(10);
151 fButtonLabels = buttonLabels;
152 if (fButtonLabels != null) {
153 int nButtons = fButtonLabels.length;
154 fButtonsEnabled = new boolean[nButtons];
155 for (int i = 0; i < nButtons; i++) {
156 fButtonsEnabled[i] = true;
161 fTableControl = null;
162 fButtonsControl = null;
163 fTableColumns = null;
165 fRemoveButtonIndex = -1;
167 fDownButtonIndex = -1;
171 * Sets the index of the 'remove' button in the button label array passed in
172 * the constructor. The behaviour of the button marked as the 'remove'
173 * button will then be handled internally. (enable state, button invocation
176 public void setRemoveButtonIndex(int removeButtonIndex) {
177 Assert.isTrue(removeButtonIndex < fButtonLabels.length);
178 fRemoveButtonIndex = removeButtonIndex;
182 * Sets the index of the 'up' button in the button label array passed in the
183 * constructor. The behaviour of the button marked as the 'up' button will
184 * then be handled internally. (enable state, button invocation behaviour)
186 public void setUpButtonIndex(int upButtonIndex) {
187 Assert.isTrue(upButtonIndex < fButtonLabels.length);
188 fUpButtonIndex = upButtonIndex;
192 * Sets the index of the 'down' button in the button label array passed in
193 * the constructor. The behaviour of the button marked as the 'down' button
194 * will then be handled internally. (enable state, button invocation
197 public void setDownButtonIndex(int downButtonIndex) {
198 Assert.isTrue(downButtonIndex < fButtonLabels.length);
199 fDownButtonIndex = downButtonIndex;
203 * Sets the viewerSorter.
205 * @param viewerSorter
206 * The viewerSorter to set
208 public void setViewerSorter(ViewerSorter viewerSorter) {
209 fViewerSorter = viewerSorter;
212 public void setTableColumns(ColumnsDescription column) {
213 fTableColumns = column;
216 // ------ adapter communication
218 private void buttonPressed(int index) {
219 if (!managedButtonPressed(index) && fListAdapter != null) {
220 fListAdapter.customButtonPressed(this, index);
225 * Checks if the button pressed is handled internally
227 * @return Returns true if button has been handled.
229 protected boolean managedButtonPressed(int index) {
230 if (index == fRemoveButtonIndex) {
232 } else if (index == fUpButtonIndex) {
234 } else if (index == fDownButtonIndex) {
242 // ------ layout helpers
245 * @see DialogField#doFillIntoGrid
247 public Control[] doFillIntoGrid(Composite parent, int nColumns) {
248 PixelConverter converter = new PixelConverter(parent);
250 assertEnoughColumns(nColumns);
252 Label label = getLabelControl(parent);
253 GridData gd = gridDataForLabel(1);
254 gd.verticalAlignment = GridData.BEGINNING;
255 label.setLayoutData(gd);
257 Control list = getListControl(parent);
259 gd.horizontalAlignment = GridData.FILL;
260 gd.grabExcessHorizontalSpace = false;
261 gd.verticalAlignment = GridData.FILL;
262 gd.grabExcessVerticalSpace = true;
263 gd.horizontalSpan = nColumns - 2;
264 gd.widthHint = converter.convertWidthInCharsToPixels(50);
265 gd.heightHint = converter.convertHeightInCharsToPixels(6);
267 list.setLayoutData(gd);
269 Composite buttons = getButtonBox(parent);
271 gd.horizontalAlignment = GridData.FILL;
272 gd.grabExcessHorizontalSpace = false;
273 gd.verticalAlignment = GridData.FILL;
274 gd.grabExcessVerticalSpace = true;
275 gd.horizontalSpan = 1;
276 buttons.setLayoutData(gd);
278 return new Control[] { label, list, buttons };
282 * @see DialogField#getNumberOfControls
284 public int getNumberOfControls() {
289 * Sets the minimal width of the buttons. Must be called after widget
292 public void setButtonsMinWidth(int minWidth) {
293 if (fLastSeparator != null) {
294 ((GridData) fLastSeparator.getLayoutData()).widthHint = minWidth;
298 // ------ ui creation
301 * Returns the list control. When called the first time, the control will be
305 * parent composite when called the first time, or
306 * <code>null</code> after.
308 public Control getListControl(Composite parent) {
309 if (fTableControl == null) {
310 assertCompositeNotNull(parent);
312 if (fTableColumns == null) {
313 fTable = createTableViewer(parent);
314 Table tableControl = fTable.getTable();
316 fTableControl = tableControl;
317 tableControl.setLayout(new TableLayout());
319 TableLayoutComposite composite = new TableLayoutComposite(
321 fTableControl = composite;
323 fTable = createTableViewer(composite);
324 Table tableControl = fTable.getTable();
326 tableControl.setHeaderVisible(fTableColumns.headers != null);
327 tableControl.setLinesVisible(fTableColumns.drawLines);
328 ColumnLayoutData[] columns = fTableColumns.columns;
329 for (int i = 0; i < columns.length; i++) {
330 composite.addColumnData(columns[i]);
331 TableColumn column = new TableColumn(tableControl, SWT.NONE);
332 // tableLayout.addColumnData(columns[i]);
333 if (fTableColumns.headers != null) {
334 column.setText(fTableColumns.headers[i]);
339 fTable.getTable().addKeyListener(new KeyAdapter() {
340 public void keyPressed(KeyEvent e) {
345 // fTableControl.setLayout(tableLayout);
347 fTable.setContentProvider(fListViewerAdapter);
348 fTable.setLabelProvider(fLabelProvider);
349 fTable.addSelectionChangedListener(fListViewerAdapter);
350 fTable.addDoubleClickListener(fListViewerAdapter);
352 fTable.setInput(fParentElement);
354 if (fViewerSorter != null) {
355 fTable.setSorter(fViewerSorter);
358 fTableControl.setEnabled(isEnabled());
359 if (fSelectionWhenEnabled != null) {
360 postSetSelection(fSelectionWhenEnabled);
363 return fTableControl;
367 * Returns the internally used table viewer.
369 public TableViewer getTableViewer() {
374 * Subclasses may override to specify a different style.
376 protected int getListStyle() {
377 int style = SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL;
378 if (fTableColumns != null) {
379 style |= SWT.FULL_SELECTION;
384 protected TableViewer createTableViewer(Composite parent) {
385 Table table = new Table(parent, getListStyle());
386 return new TableViewer(table);
389 protected Button createButton(Composite parent, String label,
390 SelectionListener listener) {
391 Button button = new Button(parent, SWT.PUSH);
392 button.setText(label);
393 button.addSelectionListener(listener);
394 GridData gd = new GridData();
395 gd.horizontalAlignment = GridData.FILL;
396 gd.grabExcessHorizontalSpace = true;
397 gd.verticalAlignment = GridData.BEGINNING;
398 gd.heightHint = SWTUtil.getButtonHeightHint(button);
399 gd.widthHint = SWTUtil.getButtonWidthHint(button);
401 button.setLayoutData(gd);
405 private Label createSeparator(Composite parent) {
406 Label separator = new Label(parent, SWT.NONE);
407 separator.setVisible(false);
408 GridData gd = new GridData();
409 gd.horizontalAlignment = GridData.FILL;
410 gd.verticalAlignment = GridData.BEGINNING;
412 separator.setLayoutData(gd);
417 * Returns the composite containing the buttons. When called the first time,
418 * the control will be created.
421 * parent composite when called the first time, or
422 * <code>null</code> after.
424 public Composite getButtonBox(Composite parent) {
425 if (fButtonsControl == null) {
426 assertCompositeNotNull(parent);
428 SelectionListener listener = new SelectionListener() {
429 public void widgetDefaultSelected(SelectionEvent e) {
433 public void widgetSelected(SelectionEvent e) {
438 Composite contents = new Composite(parent, SWT.NULL);
439 GridLayout layout = new GridLayout();
440 layout.marginWidth = 0;
441 layout.marginHeight = 0;
442 contents.setLayout(layout);
444 if (fButtonLabels != null) {
445 fButtonControls = new Button[fButtonLabels.length];
446 for (int i = 0; i < fButtonLabels.length; i++) {
447 String currLabel = fButtonLabels[i];
448 if (currLabel != null) {
449 fButtonControls[i] = createButton(contents, currLabel,
451 fButtonControls[i].setEnabled(isEnabled()
452 && fButtonsEnabled[i]);
454 fButtonControls[i] = null;
455 createSeparator(contents);
460 fLastSeparator = createSeparator(contents);
463 fButtonsControl = contents;
466 return fButtonsControl;
469 private void doButtonSelected(SelectionEvent e) {
470 if (fButtonControls != null) {
471 for (int i = 0; i < fButtonControls.length; i++) {
472 if (e.widget == fButtonControls[i]) {
481 * Handles key events in the table viewer. Specifically when the delete key
484 protected void handleKeyPressed(KeyEvent event) {
485 if (event.character == SWT.DEL && event.stateMask == 0) {
486 if (fRemoveButtonIndex != -1
487 && isButtonEnabled(fTable.getSelection(),
488 fRemoveButtonIndex)) {
489 managedButtonPressed(fRemoveButtonIndex);
494 // ------ enable / disable management
497 * @see DialogField#dialogFieldChanged
499 public void dialogFieldChanged() {
500 super.dialogFieldChanged();
505 * Updates the enable state of the all buttons
507 protected void updateButtonState() {
508 if (fButtonControls != null) {
509 ISelection sel = fTable.getSelection();
510 for (int i = 0; i < fButtonControls.length; i++) {
511 Button button = fButtonControls[i];
512 if (isOkToUse(button)) {
513 button.setEnabled(isButtonEnabled(sel, i));
519 protected boolean getManagedButtonState(ISelection sel, int index) {
520 if (index == fRemoveButtonIndex) {
521 return !sel.isEmpty();
522 } else if (index == fUpButtonIndex) {
523 return !sel.isEmpty() && canMoveUp();
524 } else if (index == fDownButtonIndex) {
525 return !sel.isEmpty() && canMoveDown();
531 * @see DialogField#updateEnableState
533 protected void updateEnableState() {
534 super.updateEnableState();
536 boolean enabled = isEnabled();
537 if (isOkToUse(fTableControl)) {
539 fSelectionWhenEnabled = fTable.getSelection();
540 selectElements(null);
542 selectElements(fSelectionWhenEnabled);
543 fSelectionWhenEnabled = null;
545 fTableControl.setEnabled(enabled);
551 * Sets a button enabled or disabled.
553 public void enableButton(int index, boolean enable) {
554 if (fButtonsEnabled != null && index < fButtonsEnabled.length) {
555 fButtonsEnabled[index] = enable;
560 private boolean isButtonEnabled(ISelection sel, int index) {
561 boolean extraState = getManagedButtonState(sel, index);
562 return isEnabled() && extraState && fButtonsEnabled[index];
565 // ------ model access
568 * Sets the elements shown in the list.
570 public void setElements(List elements) {
571 fElements = new ArrayList(elements);
572 if (fTable != null) {
575 dialogFieldChanged();
579 * Gets the elements shown in the list. The list returned is a copy, so it
580 * can be modified by the user.
582 public List getElements() {
583 return new ArrayList(fElements);
587 * Gets the elements shown at the given index.
589 public Object getElement(int index) {
590 return fElements.get(index);
594 * Gets the index of an element in the list or -1 if element is not in list.
596 public int getIndexOfElement(Object elem) {
597 return fElements.indexOf(elem);
601 * Replace an element.
603 public void replaceElement(Object oldElement, Object newElement)
604 throws IllegalArgumentException {
605 int idx = fElements.indexOf(oldElement);
607 fElements.set(idx, newElement);
608 if (fTable != null) {
609 List selected = getSelectedElements();
610 if (selected.remove(oldElement)) {
611 selected.add(newElement);
614 selectElements(new StructuredSelection(selected));
616 dialogFieldChanged();
618 throw new IllegalArgumentException();
623 * Adds an element at the end of the list.
625 public void addElement(Object element) {
626 if (fElements.contains(element)) {
629 fElements.add(element);
630 if (fTable != null) {
633 dialogFieldChanged();
637 * Adds elements at the end of the list.
639 public void addElements(List elements) {
640 int nElements = elements.size();
644 ArrayList elementsToAdd = new ArrayList(nElements);
646 for (int i = 0; i < nElements; i++) {
647 Object elem = elements.get(i);
648 if (!fElements.contains(elem)) {
649 elementsToAdd.add(elem);
652 fElements.addAll(elementsToAdd);
653 if (fTable != null) {
654 fTable.add(elementsToAdd.toArray());
656 dialogFieldChanged();
661 * Adds an element at a position.
663 public void insertElementAt(Object element, int index) {
664 if (fElements.contains(element)) {
667 fElements.add(index, element);
668 if (fTable != null) {
672 dialogFieldChanged();
676 * Adds an element at a position.
678 public void removeAllElements() {
679 if (fElements.size() > 0) {
681 if (fTable != null) {
684 dialogFieldChanged();
689 * Removes an element from the list.
691 public void removeElement(Object element) throws IllegalArgumentException {
692 if (fElements.remove(element)) {
693 if (fTable != null) {
694 fTable.remove(element);
696 dialogFieldChanged();
698 throw new IllegalArgumentException();
703 * Removes elements from the list.
705 public void removeElements(List elements) {
706 if (elements.size() > 0) {
707 fElements.removeAll(elements);
708 if (fTable != null) {
709 fTable.remove(elements.toArray());
711 dialogFieldChanged();
716 * Gets the number of elements
718 public int getSize() {
719 return fElements.size();
722 public void selectElements(ISelection selection) {
723 fSelectionWhenEnabled = selection;
724 if (fTable != null) {
725 fTable.setSelection(selection, true);
729 public void selectFirstElement() {
730 Object element = null;
731 if (fViewerSorter != null) {
732 Object[] arr = fElements.toArray();
733 fViewerSorter.sort(fTable, arr);
734 if (arr.length > 0) {
738 if (fElements.size() > 0) {
739 element = fElements.get(0);
742 if (element != null) {
743 selectElements(new StructuredSelection(element));
747 public void postSetSelection(final ISelection selection) {
748 if (isOkToUse(fTableControl)) {
749 Display d = fTableControl.getDisplay();
750 d.asyncExec(new Runnable() {
752 if (isOkToUse(fTableControl)) {
753 selectElements(selection);
761 * Refreshes the table.
763 public void refresh() {
764 if (fTable != null) {
769 // ------- list maintenance
771 private List moveUp(List elements, List move) {
772 int nElements = elements.size();
773 List res = new ArrayList(nElements);
774 Object floating = null;
775 for (int i = 0; i < nElements; i++) {
776 Object curr = elements.get(i);
777 if (move.contains(curr)) {
780 if (floating != null) {
786 if (floating != null) {
792 private void moveUp(List toMoveUp) {
793 if (toMoveUp.size() > 0) {
794 setElements(moveUp(fElements, toMoveUp));
795 fTable.reveal(toMoveUp.get(0));
799 private void moveDown(List toMoveDown) {
800 if (toMoveDown.size() > 0) {
801 setElements(reverse(moveUp(reverse(fElements), toMoveDown)));
802 fTable.reveal(toMoveDown.get(toMoveDown.size() - 1));
806 private List reverse(List p) {
807 List reverse = new ArrayList(p.size());
808 for (int i = p.size() - 1; i >= 0; i--) {
809 reverse.add(p.get(i));
814 private void remove() {
815 removeElements(getSelectedElements());
819 moveUp(getSelectedElements());
822 private void down() {
823 moveDown(getSelectedElements());
826 private boolean canMoveUp() {
827 if (isOkToUse(fTableControl)) {
828 int[] indc = fTable.getTable().getSelectionIndices();
829 for (int i = 0; i < indc.length; i++) {
838 private boolean canMoveDown() {
839 if (isOkToUse(fTableControl)) {
840 int[] indc = fTable.getTable().getSelectionIndices();
841 int k = fElements.size() - 1;
842 for (int i = indc.length - 1; i >= 0; i--, k--) {
852 * Returns the selected elements.
854 public List getSelectedElements() {
855 List result = new ArrayList();
856 if (fTable != null) {
857 ISelection selection = fTable.getSelection();
858 if (selection instanceof IStructuredSelection) {
859 Iterator iter = ((IStructuredSelection) selection).iterator();
860 while (iter.hasNext()) {
861 result.add(iter.next());
868 // ------- ListViewerAdapter
870 private class ListViewerAdapter implements IStructuredContentProvider,
871 ISelectionChangedListener, IDoubleClickListener {
873 // ------- ITableContentProvider Interface ------------
875 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
879 public boolean isDeleted(Object element) {
883 public void dispose() {
886 public Object[] getElements(Object obj) {
887 return fElements.toArray();
890 // ------- ISelectionChangedListener Interface ------------
892 public void selectionChanged(SelectionChangedEvent event) {
893 doListSelected(event);
899 * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
901 public void doubleClick(DoubleClickEvent event) {
902 doDoubleClick(event);
907 protected void doListSelected(SelectionChangedEvent event) {
909 if (fListAdapter != null) {
910 fListAdapter.selectionChanged(this);
914 protected void doDoubleClick(DoubleClickEvent event) {
915 if (fListAdapter != null) {
916 fListAdapter.doubleClicked(this);