blob: 6c379516c06b0dae63c6b934d0f0ee3ca2ce2aa1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.utility.swt;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.jpt.ui.internal.listeners.SWTCollectionChangeListenerWrapper;
import org.eclipse.jpt.utility.internal.ArrayTools;
import org.eclipse.jpt.utility.internal.StringTools;
import org.eclipse.jpt.utility.internal.Tools;
import org.eclipse.jpt.utility.model.event.CollectionAddEvent;
import org.eclipse.jpt.utility.model.event.CollectionChangeEvent;
import org.eclipse.jpt.utility.model.event.CollectionClearEvent;
import org.eclipse.jpt.utility.model.event.CollectionRemoveEvent;
import org.eclipse.jpt.utility.model.listener.CollectionChangeListener;
import org.eclipse.jpt.utility.model.value.CollectionValueModel;
import org.eclipse.jpt.utility.model.value.ListValueModel;
import org.eclipse.jpt.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
/**
* A value model on the underlying model list.
*/
private final ListValueModel<E> listHolder;
/**
* A writable value model on the underlying model selections.
*/
private final WritableCollectionValueModel<E> selectedItemsHolder;
/**
* 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> listHolder,
WritableCollectionValueModel<E> selectedItemsHolder,
List listBox
) {
super();
if ((listHolder == null) || (selectedItemsHolder == null) || (listBox == null)) {
throw new NullPointerException();
}
this.listHolder = listHolder;
this.selectedItemsHolder = selectedItemsHolder;
this.listBox = listBox;
this.selectedItemsChangeListener = this.buildSelectedItemsChangeListener();
this.selectedItemsHolder.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.selectedItemsHolder.size();
int[] select = new int[selectedItemsSize];
int i = 0;
for (E item : this.selectedItemsHolder) {
select[i++] = this.indexOf(item);
}
int listSize = this.listHolder.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.selectedItemsHolder.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.listHolder.size();
for (int i = 0; i < len; i++) {
if (Tools.valuesAreEqual(this.listHolder.get(i), item)) {
return i;
}
}
// explicitly catch any model bugs
throw new IllegalStateException("selected item not found: " + item);
}
// ********** 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.selectedItemsHolder.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.listHolder.get(selectionIndex));
}
return selectedItems;
}
// ********** standard methods **********
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.selectedItemsHolder);
}
}