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;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
18 import net.sourceforge.phpdt.internal.ui.util.PixelConverter;
19 import net.sourceforge.phpdt.internal.ui.util.SWTUtil;
20 import net.sourceforge.phpdt.internal.ui.util.TableLayoutComposite;
22 import org.eclipse.jface.util.Assert;
23 import org.eclipse.jface.viewers.ColumnLayoutData;
24 import org.eclipse.jface.viewers.ColumnWeightData;
25 import org.eclipse.jface.viewers.DoubleClickEvent;
26 import org.eclipse.jface.viewers.IDoubleClickListener;
27 import org.eclipse.jface.viewers.ILabelProvider;
28 import org.eclipse.jface.viewers.ISelection;
29 import org.eclipse.jface.viewers.ISelectionChangedListener;
30 import org.eclipse.jface.viewers.IStructuredContentProvider;
31 import org.eclipse.jface.viewers.IStructuredSelection;
32 import org.eclipse.jface.viewers.SelectionChangedEvent;
33 import org.eclipse.jface.viewers.StructuredSelection;
34 import org.eclipse.jface.viewers.TableLayout;
35 import org.eclipse.jface.viewers.TableViewer;
36 import org.eclipse.jface.viewers.Viewer;
37 import org.eclipse.jface.viewers.ViewerSorter;
38 import org.eclipse.swt.SWT;
39 import org.eclipse.swt.events.KeyAdapter;
40 import org.eclipse.swt.events.KeyEvent;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.events.SelectionListener;
43 import org.eclipse.swt.layout.GridData;
44 import org.eclipse.swt.layout.GridLayout;
45 import org.eclipse.swt.widgets.Button;
46 import org.eclipse.swt.widgets.Composite;
47 import org.eclipse.swt.widgets.Control;
48 import org.eclipse.swt.widgets.Display;
49 import org.eclipse.swt.widgets.Label;
50 import org.eclipse.swt.widgets.Table;
51 import org.eclipse.swt.widgets.TableColumn;
54 * A list with a button bar.
55 * Typical buttons are 'Add', 'Remove', 'Up' and 'Down'.
56 * List model is independend of widget creation.
57 * DialogFields controls are: Label, List and Composite containing buttons.
59 public class ListDialogField extends DialogField {
61 public static class ColumnsDescription {
62 private ColumnLayoutData[] columns;
63 private String[] headers;
64 private boolean drawLines;
66 public ColumnsDescription(ColumnLayoutData[] columns, String[] headers, boolean drawLines) {
67 this.columns= columns;
68 this.headers= headers;
69 this.drawLines= drawLines;
72 public ColumnsDescription(String[] headers, boolean drawLines) {
73 this(createColumnWeightData(headers.length), headers, drawLines);
76 public ColumnsDescription(int nColumns, boolean drawLines) {
77 this(createColumnWeightData(nColumns), null, drawLines);
80 private static ColumnLayoutData[] createColumnWeightData(int nColumns) {
81 ColumnLayoutData[] data= new ColumnLayoutData[nColumns];
82 for (int i= 0; i < nColumns; i++) {
83 data[i]= new ColumnWeightData(1);
89 protected TableViewer fTable;
90 protected ILabelProvider fLabelProvider;
91 protected ListViewerAdapter fListViewerAdapter;
92 protected List fElements;
93 protected ViewerSorter fViewerSorter;
95 protected String[] fButtonLabels;
96 private Button[] fButtonControls;
98 private boolean[] fButtonsEnabled;
100 private int fRemoveButtonIndex;
101 private int fUpButtonIndex;
102 private int fDownButtonIndex;
104 private Label fLastSeparator;
106 private Control fTableControl;
107 private Composite fButtonsControl;
108 private ISelection fSelectionWhenEnabled;
110 private IListAdapter fListAdapter;
112 private Object fParentElement;
114 private ColumnsDescription fTableColumns;
118 * Creates the <code>ListDialogField</code>.
119 * @param adapter A listener for button invocation, selection changes. Can
120 * be <code>null</code>.
121 * @param buttonLabels The labels of all buttons: <code>null</code> is a valid array entry and
123 * @param lprovider The label provider to render the table entries
125 public ListDialogField(IListAdapter adapter, String[] buttonLabels, ILabelProvider lprovider) {
127 fListAdapter= adapter;
129 fLabelProvider= lprovider;
130 fListViewerAdapter= new ListViewerAdapter();
131 fParentElement= this;
133 fElements= new ArrayList(10);
135 fButtonLabels= buttonLabels;
136 if (fButtonLabels != null) {
137 int nButtons= fButtonLabels.length;
138 fButtonsEnabled= new boolean[nButtons];
139 for (int i= 0; i < nButtons; i++) {
140 fButtonsEnabled[i]= true;
146 fButtonsControl= null;
149 fRemoveButtonIndex= -1;
151 fDownButtonIndex= -1;
155 * Sets the index of the 'remove' button in the button label array passed in the constructor.
156 * The behaviour of the button marked as the 'remove' button will then be handled internally.
157 * (enable state, button invocation behaviour)
159 public void setRemoveButtonIndex(int removeButtonIndex) {
160 Assert.isTrue(removeButtonIndex < fButtonLabels.length);
161 fRemoveButtonIndex= removeButtonIndex;
165 * Sets the index of the 'up' button in the button label array passed in the constructor.
166 * The behaviour of the button marked as the 'up' button will then be handled internally.
167 * (enable state, button invocation behaviour)
169 public void setUpButtonIndex(int upButtonIndex) {
170 Assert.isTrue(upButtonIndex < fButtonLabels.length);
171 fUpButtonIndex= upButtonIndex;
175 * Sets the index of the 'down' button in the button label array passed in the constructor.
176 * The behaviour of the button marked as the 'down' button will then be handled internally.
177 * (enable state, button invocation behaviour)
179 public void setDownButtonIndex(int downButtonIndex) {
180 Assert.isTrue(downButtonIndex < fButtonLabels.length);
181 fDownButtonIndex= downButtonIndex;
185 * Sets the viewerSorter.
186 * @param viewerSorter The viewerSorter to set
188 public void setViewerSorter(ViewerSorter viewerSorter) {
189 fViewerSorter= viewerSorter;
192 public void setTableColumns(ColumnsDescription column) {
193 fTableColumns= column;
198 // ------ adapter communication
200 private void buttonPressed(int index) {
201 if (!managedButtonPressed(index) && fListAdapter != null) {
202 fListAdapter.customButtonPressed(this, index);
207 * Checks if the button pressed is handled internally
208 * @return Returns true if button has been handled.
210 protected boolean managedButtonPressed(int index) {
211 if (index == fRemoveButtonIndex) {
213 } else if (index == fUpButtonIndex) {
215 } else if (index == fDownButtonIndex) {
224 // ------ layout helpers
227 * @see DialogField#doFillIntoGrid
229 public Control[] doFillIntoGrid(Composite parent, int nColumns) {
230 PixelConverter converter= new PixelConverter(parent);
232 assertEnoughColumns(nColumns);
234 Label label= getLabelControl(parent);
235 GridData gd= gridDataForLabel(1);
236 gd.verticalAlignment= GridData.BEGINNING;
237 label.setLayoutData(gd);
239 Control list= getListControl(parent);
241 gd.horizontalAlignment= GridData.FILL;
242 gd.grabExcessHorizontalSpace= false;
243 gd.verticalAlignment= GridData.FILL;
244 gd.grabExcessVerticalSpace= true;
245 gd.horizontalSpan= nColumns - 2;
246 gd.widthHint= converter.convertWidthInCharsToPixels(50);
247 gd.heightHint= converter.convertHeightInCharsToPixels(6);
249 list.setLayoutData(gd);
251 Composite buttons= getButtonBox(parent);
253 gd.horizontalAlignment= GridData.FILL;
254 gd.grabExcessHorizontalSpace= false;
255 gd.verticalAlignment= GridData.FILL;
256 gd.grabExcessVerticalSpace= true;
257 gd.horizontalSpan= 1;
258 buttons.setLayoutData(gd);
260 return new Control[] { label, list, buttons };
264 * @see DialogField#getNumberOfControls
266 public int getNumberOfControls() {
271 * Sets the minimal width of the buttons. Must be called after widget creation.
273 public void setButtonsMinWidth(int minWidth) {
274 if (fLastSeparator != null) {
275 ((GridData)fLastSeparator.getLayoutData()).widthHint= minWidth;
280 // ------ ui creation
283 * Returns the list control. When called the first time, the control will be created.
284 * @param The parent composite when called the first time, or <code>null</code>
287 public Control getListControl(Composite parent) {
288 if (fTableControl == null) {
289 assertCompositeNotNull(parent);
291 if (fTableColumns == null) {
292 fTable= createTableViewer(parent);
293 Table tableControl= fTable.getTable();
295 fTableControl= tableControl;
296 tableControl.setLayout(new TableLayout());
298 TableLayoutComposite composite= new TableLayoutComposite(parent, SWT.NONE);
299 fTableControl= composite;
301 fTable= createTableViewer(composite);
302 Table tableControl= fTable.getTable();
304 tableControl.setHeaderVisible(fTableColumns.headers != null);
305 tableControl.setLinesVisible(fTableColumns.drawLines);
306 ColumnLayoutData[] columns= fTableColumns.columns;
307 for (int i= 0; i < columns.length; i++) {
308 composite.addColumnData(columns[i]);
309 TableColumn column= new TableColumn(tableControl, SWT.NONE);
310 //tableLayout.addColumnData(columns[i]);
311 if (fTableColumns.headers != null) {
312 column.setText(fTableColumns.headers[i]);
317 fTable.getTable().addKeyListener(new KeyAdapter() {
318 public void keyPressed(KeyEvent e) {
323 //fTableControl.setLayout(tableLayout);
325 fTable.setContentProvider(fListViewerAdapter);
326 fTable.setLabelProvider(fLabelProvider);
327 fTable.addSelectionChangedListener(fListViewerAdapter);
328 fTable.addDoubleClickListener(fListViewerAdapter);
330 fTable.setInput(fParentElement);
332 if (fViewerSorter != null) {
333 fTable.setSorter(fViewerSorter);
336 fTableControl.setEnabled(isEnabled());
337 if (fSelectionWhenEnabled != null) {
338 postSetSelection(fSelectionWhenEnabled);
341 return fTableControl;
345 * Returns the internally used table viewer.
347 public TableViewer getTableViewer() {
352 * Subclasses may override to specify a different style.
354 protected int getListStyle(){
355 int style= SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL ;
356 if (fTableColumns != null) {
357 style |= SWT.FULL_SELECTION;
362 protected TableViewer createTableViewer(Composite parent) {
363 Table table= new Table(parent, getListStyle());
364 return new TableViewer(table);
367 protected Button createButton(Composite parent, String label, SelectionListener listener) {
368 Button button= new Button(parent, SWT.PUSH);
369 button.setText(label);
370 button.addSelectionListener(listener);
371 GridData gd= new GridData();
372 gd.horizontalAlignment= GridData.FILL;
373 gd.grabExcessHorizontalSpace= true;
374 gd.verticalAlignment= GridData.BEGINNING;
375 gd.heightHint = SWTUtil.getButtonHeigthHint(button);
376 gd.widthHint = SWTUtil.getButtonWidthHint(button);
378 button.setLayoutData(gd);
382 private Label createSeparator(Composite parent) {
383 Label separator= new Label(parent, SWT.NONE);
384 separator.setVisible(false);
385 GridData gd= new GridData();
386 gd.horizontalAlignment= GridData.FILL;
387 gd.verticalAlignment= GridData.BEGINNING;
389 separator.setLayoutData(gd);
394 * Returns the composite containing the buttons. When called the first time, the control
396 * @param The parent composite when called the first time, or <code>null</code>
399 public Composite getButtonBox(Composite parent) {
400 if (fButtonsControl == null) {
401 assertCompositeNotNull(parent);
403 SelectionListener listener= new SelectionListener() {
404 public void widgetDefaultSelected(SelectionEvent e) {
407 public void widgetSelected(SelectionEvent e) {
412 Composite contents= new Composite(parent, SWT.NULL);
413 GridLayout layout= new GridLayout();
414 layout.marginWidth= 0;
415 layout.marginHeight= 0;
416 contents.setLayout(layout);
418 if (fButtonLabels != null) {
419 fButtonControls= new Button[fButtonLabels.length];
420 for (int i= 0; i < fButtonLabels.length; i++) {
421 String currLabel= fButtonLabels[i];
422 if (currLabel != null) {
423 fButtonControls[i]= createButton(contents, currLabel, listener);
424 fButtonControls[i].setEnabled(isEnabled() && fButtonsEnabled[i]);
426 fButtonControls[i]= null;
427 createSeparator(contents);
432 fLastSeparator= createSeparator(contents);
435 fButtonsControl= contents;
438 return fButtonsControl;
441 private void doButtonSelected(SelectionEvent e) {
442 if (fButtonControls != null) {
443 for (int i= 0; i < fButtonControls.length; i++) {
444 if (e.widget == fButtonControls[i]) {
453 * Handles key events in the table viewer. Specifically
454 * when the delete key is pressed.
456 protected void handleKeyPressed(KeyEvent event) {
457 if (event.character == SWT.DEL && event.stateMask == 0) {
458 if (fRemoveButtonIndex != -1 && isButtonEnabled(fTable.getSelection(), fRemoveButtonIndex)) {
459 managedButtonPressed(fRemoveButtonIndex);
464 // ------ enable / disable management
467 * @see DialogField#dialogFieldChanged
469 public void dialogFieldChanged() {
470 super.dialogFieldChanged();
475 * Updates the enable state of the all buttons
477 protected void updateButtonState() {
478 if (fButtonControls != null) {
479 ISelection sel= fTable.getSelection();
480 for (int i= 0; i < fButtonControls.length; i++) {
481 Button button= fButtonControls[i];
482 if (isOkToUse(button)) {
483 button.setEnabled(isButtonEnabled(sel, i));
489 protected boolean getManagedButtonState(ISelection sel, int index) {
490 if (index == fRemoveButtonIndex) {
491 return !sel.isEmpty();
492 } else if (index == fUpButtonIndex) {
493 return !sel.isEmpty() && canMoveUp();
494 } else if (index == fDownButtonIndex) {
495 return !sel.isEmpty() && canMoveDown();
501 * @see DialogField#updateEnableState
503 protected void updateEnableState() {
504 super.updateEnableState();
506 boolean enabled= isEnabled();
507 if (isOkToUse(fTableControl)) {
509 fSelectionWhenEnabled= fTable.getSelection();
510 selectElements(null);
512 selectElements(fSelectionWhenEnabled);
513 fSelectionWhenEnabled= null;
515 fTableControl.setEnabled(enabled);
521 * Sets a button enabled or disabled.
523 public void enableButton(int index, boolean enable) {
524 if (fButtonsEnabled != null && index < fButtonsEnabled.length) {
525 fButtonsEnabled[index]= enable;
530 private boolean isButtonEnabled(ISelection sel, int index) {
531 boolean extraState= getManagedButtonState(sel, index);
532 return isEnabled() && extraState && fButtonsEnabled[index];
536 // ------ model access
539 * Sets the elements shown in the list.
541 public void setElements(List elements) {
542 fElements= new ArrayList(elements);
543 if (fTable != null) {
546 dialogFieldChanged();
550 * Gets the elements shown in the list.
551 * The list returned is a copy, so it can be modified by the user.
553 public List getElements() {
554 return new ArrayList(fElements);
558 * Gets the elements shown at the given index.
560 public Object getElement(int index) {
561 return fElements.get(index);
565 * Gets the index of an element in the list or -1 if element is not in list.
567 public int getIndexOfElement(Object elem) {
568 return fElements.indexOf(elem);
572 * Replace an element.
574 public void replaceElement(Object oldElement, Object newElement) throws IllegalArgumentException {
575 int idx= fElements.indexOf(oldElement);
577 fElements.set(idx, newElement);
578 if (fTable != null) {
579 List selected= getSelectedElements();
580 if (selected.remove(oldElement)) {
581 selected.add(newElement);
584 selectElements(new StructuredSelection(selected));
586 dialogFieldChanged();
588 throw new IllegalArgumentException();
593 * Adds an element at the end of the list.
595 public void addElement(Object element) {
596 if (fElements.contains(element)) {
599 fElements.add(element);
600 if (fTable != null) {
603 dialogFieldChanged();
607 * Adds elements at the end of the list.
609 public void addElements(List elements) {
610 int nElements= elements.size();
614 ArrayList elementsToAdd= new ArrayList(nElements);
616 for (int i= 0; i < nElements; i++) {
617 Object elem= elements.get(i);
618 if (!fElements.contains(elem)) {
619 elementsToAdd.add(elem);
622 fElements.addAll(elementsToAdd);
623 if (fTable != null) {
624 fTable.add(elementsToAdd.toArray());
626 dialogFieldChanged();
631 * Adds an element at a position.
633 public void insertElementAt(Object element, int index) {
634 if (fElements.contains(element)) {
637 fElements.add(index, element);
638 if (fTable != null) {
642 dialogFieldChanged();
647 * Adds an element at a position.
649 public void removeAllElements() {
650 if (fElements.size() > 0) {
652 if (fTable != null) {
655 dialogFieldChanged();
660 * Removes an element from the list.
662 public void removeElement(Object element) throws IllegalArgumentException {
663 if (fElements.remove(element)) {
664 if (fTable != null) {
665 fTable.remove(element);
667 dialogFieldChanged();
669 throw new IllegalArgumentException();
674 * Removes elements from the list.
676 public void removeElements(List elements) {
677 if (elements.size() > 0) {
678 fElements.removeAll(elements);
679 if (fTable != null) {
680 fTable.remove(elements.toArray());
682 dialogFieldChanged();
687 * Gets the number of elements
689 public int getSize() {
690 return fElements.size();
694 public void selectElements(ISelection selection) {
695 fSelectionWhenEnabled= selection;
696 if (fTable != null) {
697 fTable.setSelection(selection, true);
701 public void selectFirstElement() {
702 Object element= null;
703 if (fViewerSorter != null) {
704 Object[] arr= fElements.toArray();
705 fViewerSorter.sort(fTable, arr);
706 if (arr.length > 0) {
710 if (fElements.size() > 0) {
711 element= fElements.get(0);
714 if (element != null) {
715 selectElements(new StructuredSelection(element));
720 public void postSetSelection(final ISelection selection) {
721 if (isOkToUse(fTableControl)) {
722 Display d= fTableControl.getDisplay();
723 d.asyncExec(new Runnable() {
725 if (isOkToUse(fTableControl)) {
726 selectElements(selection);
734 * Refreshes the table.
736 public void refresh() {
737 if (fTable != null) {
742 // ------- list maintenance
744 private List moveUp(List elements, List move) {
745 int nElements= elements.size();
746 List res= new ArrayList(nElements);
747 Object floating= null;
748 for (int i= 0; i < nElements; i++) {
749 Object curr= elements.get(i);
750 if (move.contains(curr)) {
753 if (floating != null) {
759 if (floating != null) {
765 private void moveUp(List toMoveUp) {
766 if (toMoveUp.size() > 0) {
767 setElements(moveUp(fElements, toMoveUp));
768 fTable.reveal(toMoveUp.get(0));
772 private void moveDown(List toMoveDown) {
773 if (toMoveDown.size() > 0) {
774 setElements(reverse(moveUp(reverse(fElements), toMoveDown)));
775 fTable.reveal(toMoveDown.get(toMoveDown.size() - 1));
779 private List reverse(List p) {
780 List reverse= new ArrayList(p.size());
781 for (int i= p.size()-1; i >= 0; i--) {
782 reverse.add(p.get(i));
788 private void remove() {
789 removeElements(getSelectedElements());
793 moveUp(getSelectedElements());
796 private void down() {
797 moveDown(getSelectedElements());
800 private boolean canMoveUp() {
801 if (isOkToUse(fTableControl)) {
802 int[] indc= fTable.getTable().getSelectionIndices();
803 for (int i= 0; i < indc.length; i++) {
812 private boolean canMoveDown() {
813 if (isOkToUse(fTableControl)) {
814 int[] indc= fTable.getTable().getSelectionIndices();
815 int k= fElements.size() - 1;
816 for (int i= indc.length - 1; i >= 0 ; i--, k--) {
826 * Returns the selected elements.
828 public List getSelectedElements() {
829 List result= new ArrayList();
830 if (fTable != null) {
831 ISelection selection= fTable.getSelection();
832 if (selection instanceof IStructuredSelection) {
833 Iterator iter= ((IStructuredSelection)selection).iterator();
834 while (iter.hasNext()) {
835 result.add(iter.next());
842 // ------- ListViewerAdapter
844 private class ListViewerAdapter implements IStructuredContentProvider, ISelectionChangedListener, IDoubleClickListener {
846 // ------- ITableContentProvider Interface ------------
848 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
852 public boolean isDeleted(Object element) {
856 public void dispose() {
859 public Object[] getElements(Object obj) {
860 return fElements.toArray();
863 // ------- ISelectionChangedListener Interface ------------
865 public void selectionChanged(SelectionChangedEvent event) {
866 doListSelected(event);
870 * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
872 public void doubleClick(DoubleClickEvent event) {
873 doDoubleClick(event);
879 protected void doListSelected(SelectionChangedEvent event) {
881 if (fListAdapter != null) {
882 fListAdapter.selectionChanged(this);
886 protected void doDoubleClick(DoubleClickEvent event) {
887 if (fListAdapter != null) {
888 fListAdapter.doubleClicked(this);