| /******************************************************************************* |
| * Copyright (c) 2009, 2010 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.common.ui.internal.utility.swt; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| |
| import org.eclipse.jpt.common.ui.internal.listeners.SWTCollectionChangeListenerWrapper; |
| import org.eclipse.jpt.common.utility.internal.ArrayTools; |
| import org.eclipse.jpt.common.utility.internal.StringTools; |
| import org.eclipse.jpt.common.utility.internal.Tools; |
| import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; |
| import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; |
| import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; |
| import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; |
| import org.eclipse.jpt.common.utility.model.listener.CollectionChangeListener; |
| import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; |
| import org.eclipse.jpt.common.utility.model.value.ListValueModel; |
| import org.eclipse.jpt.common.utility.model.value.WritableCollectionValueModel; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.widgets.List; |
| |
| /** |
| * This binding can be used to keep a list box's selection |
| * synchronized with a model. The selection can be modified by either the list |
| * box or the model, so changes must be coordinated. |
| * |
| * @see ListValueModel |
| * @see WritableCollectionValueModel |
| * @see List |
| * @see SWTTools |
| */ |
| @SuppressWarnings("nls") |
| final class ListBoxSelectionBinding<E> |
| implements ListWidgetModelBinding.SelectionBinding |
| { |
| // ***** model |
| /** |
| * The underlying list model. |
| */ |
| private final ListValueModel<E> listModel; |
| |
| /** |
| * A writable value model on the underlying model selections. |
| */ |
| private final WritableCollectionValueModel<E> selectedItemsModel; |
| |
| /** |
| * A listener that allows us to synchronize the list box's selection with |
| * the model selections. |
| */ |
| private final CollectionChangeListener selectedItemsChangeListener; |
| |
| // ***** UI |
| /** |
| * The list box whose selection we keep synchronized with the model selections. |
| */ |
| private final List listBox; |
| |
| /** |
| * A listener that allows us to synchronize our selected items holder |
| * with the list box's selection. |
| */ |
| private final SelectionListener listBoxSelectionListener; |
| |
| |
| // ********** constructor ********** |
| |
| /** |
| * Constructor - all parameters are required. |
| */ |
| ListBoxSelectionBinding( |
| ListValueModel<E> listModel, |
| WritableCollectionValueModel<E> selectedItemsModel, |
| List listBox |
| ) { |
| super(); |
| if ((listModel == null) || (selectedItemsModel == null) || (listBox == null)) { |
| throw new NullPointerException(); |
| } |
| this.listModel = listModel; |
| this.selectedItemsModel = selectedItemsModel; |
| this.listBox = listBox; |
| |
| this.selectedItemsChangeListener = this.buildSelectedItemsChangeListener(); |
| this.selectedItemsModel.addCollectionChangeListener(CollectionValueModel.VALUES, this.selectedItemsChangeListener); |
| |
| this.listBoxSelectionListener = this.buildListBoxSelectionListener(); |
| this.listBox.addSelectionListener(this.listBoxSelectionListener); |
| } |
| |
| |
| // ********** initialization ********** |
| |
| private CollectionChangeListener buildSelectedItemsChangeListener() { |
| return new SWTCollectionChangeListenerWrapper(this.buildSelectedItemsChangeListener_()); |
| } |
| |
| private CollectionChangeListener buildSelectedItemsChangeListener_() { |
| return new CollectionChangeListener() { |
| public void itemsAdded(CollectionAddEvent event) { |
| ListBoxSelectionBinding.this.selectedItemsAdded(event); |
| } |
| public void itemsRemoved(CollectionRemoveEvent event) { |
| ListBoxSelectionBinding.this.selectedItemsRemoved(event); |
| } |
| public void collectionCleared(CollectionClearEvent event) { |
| ListBoxSelectionBinding.this.selectedItemsCleared(event); |
| } |
| public void collectionChanged(CollectionChangeEvent event) { |
| ListBoxSelectionBinding.this.selectedItemsChanged(event); |
| } |
| @Override |
| public String toString() { |
| return "selected items listener"; |
| } |
| }; |
| } |
| |
| private SelectionListener buildListBoxSelectionListener() { |
| return new SelectionListener() { |
| public void widgetSelected(SelectionEvent event) { |
| ListBoxSelectionBinding.this.listBoxSelectionChanged(event); |
| } |
| public void widgetDefaultSelected(SelectionEvent event) { |
| ListBoxSelectionBinding.this.listBoxDoubleClicked(event); |
| } |
| @Override |
| public String toString() { |
| return "list box selection listener"; |
| } |
| }; |
| } |
| |
| |
| // ********** ListWidgetModelBinding.SelectionBinding implementation ********** |
| |
| /** |
| * Modifying the list box's selected items programmatically does not |
| * trigger a SelectionEvent. |
| * |
| * Pre-condition: The list-box is not disposed. |
| */ |
| public void synchronizeListWidgetSelection() { |
| int selectedItemsSize = this.selectedItemsModel.size(); |
| int[] select = new int[selectedItemsSize]; |
| int i = 0; |
| for (E item : this.selectedItemsModel) { |
| select[i++] = this.indexOf(item); |
| } |
| |
| int listSize = this.listModel.size(); |
| int[] deselect = new int[listSize - selectedItemsSize]; |
| i = 0; |
| for (int j = 0; j < listSize; j++) { |
| if ( ! ArrayTools.contains(select, j)) { |
| deselect[i++] = j; |
| } |
| } |
| |
| int[] old = ArrayTools.sort(this.listBox.getSelectionIndices()); |
| select = ArrayTools.sort(select); |
| if ( ! Arrays.equals(select, old)) { |
| this.listBox.deselect(deselect); |
| this.listBox.select(select); |
| } |
| } |
| |
| public void dispose() { |
| this.listBox.removeSelectionListener(this.listBoxSelectionListener); |
| this.selectedItemsModel.removeCollectionChangeListener(CollectionValueModel.VALUES, this.selectedItemsChangeListener); |
| } |
| |
| |
| // ********** selected items ********** |
| |
| void selectedItemsAdded(CollectionAddEvent event) { |
| if ( ! this.listBox.isDisposed()) { |
| this.selectedItemsAdded_(event); |
| } |
| } |
| |
| /** |
| * Modifying the list box's selected items programmatically does not |
| * trigger a SelectionEvent. |
| */ |
| private void selectedItemsAdded_(CollectionAddEvent event) { |
| int[] indices = new int[event.getItemsSize()]; |
| int i = 0; |
| for (E item : this.getItems(event)) { |
| indices[i++] = this.indexOf(item); |
| } |
| this.listBox.select(indices); |
| } |
| |
| // minimized scope of suppressed warnings |
| @SuppressWarnings("unchecked") |
| private Iterable<E> getItems(CollectionAddEvent event) { |
| return (Iterable<E>) event.getItems(); |
| } |
| |
| void selectedItemsRemoved(CollectionRemoveEvent event) { |
| if ( ! this.listBox.isDisposed()) { |
| this.selectedItemsRemoved_(event); |
| } |
| } |
| |
| /** |
| * Modifying the list box's selected items programmatically does not |
| * trigger a SelectionEvent. |
| */ |
| private void selectedItemsRemoved_(CollectionRemoveEvent event) { |
| int[] indices = new int[event.getItemsSize()]; |
| int i = 0; |
| for (E item : this.getItems(event)) { |
| indices[i++] = this.indexOf(item); |
| } |
| this.listBox.deselect(indices); |
| } |
| |
| // minimized scope of suppressed warnings |
| @SuppressWarnings("unchecked") |
| private Iterable<E> getItems(CollectionRemoveEvent event) { |
| return (Iterable<E>) event.getItems(); |
| } |
| |
| void selectedItemsCleared(CollectionClearEvent event) { |
| if ( ! this.listBox.isDisposed()) { |
| this.selectedItemsCleared_(event); |
| } |
| } |
| |
| /** |
| * Modifying the list box's selected items programmatically does not |
| * trigger a SelectionEvent. |
| */ |
| private void selectedItemsCleared_(@SuppressWarnings("unused") CollectionClearEvent event) { |
| this.listBox.deselectAll(); |
| } |
| |
| void selectedItemsChanged(CollectionChangeEvent event) { |
| if ( ! this.listBox.isDisposed()) { |
| this.selectedItemsChanged_(event); |
| } |
| } |
| |
| private void selectedItemsChanged_(@SuppressWarnings("unused") CollectionChangeEvent event) { |
| this.synchronizeListWidgetSelection(); |
| } |
| |
| private int indexOf(E item) { |
| int len = this.listModel.size(); |
| for (int i = 0; i < len; i++) { |
| if (Tools.valuesAreEqual(this.listModel.get(i), item)) { |
| return i; |
| } |
| } |
| // see comment in DropDownListBoxSelectionBinding.indexOf(E) |
| return -1; |
| } |
| |
| |
| // ********** list box events ********** |
| |
| void listBoxSelectionChanged(SelectionEvent event) { |
| if ( ! this.listBox.isDisposed()) { |
| this.listBoxSelectionChanged_(event); |
| } |
| } |
| |
| void listBoxDoubleClicked(SelectionEvent event) { |
| if ( ! this.listBox.isDisposed()) { |
| this.listBoxSelectionChanged_(event); |
| } |
| } |
| |
| private void listBoxSelectionChanged_(@SuppressWarnings("unused") SelectionEvent event) { |
| this.selectedItemsModel.setValues(this.getListBoxSelectedItems()); |
| } |
| |
| private Iterable<E> getListBoxSelectedItems() { |
| ArrayList<E> selectedItems = new ArrayList<E>(this.listBox.getSelectionCount()); |
| for (int selectionIndex : this.listBox.getSelectionIndices()) { |
| selectedItems.add(this.listModel.get(selectionIndex)); |
| } |
| return selectedItems; |
| } |
| |
| |
| // ********** standard methods ********** |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.selectedItemsModel); |
| } |
| |
| } |