| /* |
| * Copyright 2010 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.view.client; |
| |
| import com.google.gwt.view.client.SelectionModel.AbstractSelectionModel; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * A simple selection model that allows multiple items to be selected. |
| * |
| * @param <T> the data type of the items |
| */ |
| public class MultiSelectionModel<T> extends AbstractSelectionModel<T> |
| implements SetSelectionModel<T> { |
| |
| /** |
| * Stores an item and its pending selection state. |
| * |
| * @param <T> the data type of the item |
| */ |
| static class SelectionChange<T> { |
| private final T item; |
| private final boolean isSelected; |
| |
| SelectionChange(T item, boolean isSelected) { |
| this.item = item; |
| this.isSelected = isSelected; |
| } |
| |
| public T getItem() { |
| return item; |
| } |
| |
| public boolean isSelected() { |
| return isSelected; |
| } |
| } |
| |
| // Ensure one value per key |
| final Map<Object, T> selectedSet; |
| |
| /** |
| * A map of keys to the item and its pending selection state. |
| */ |
| private final Map<Object, SelectionChange<T>> selectionChanges; |
| |
| /** |
| * Constructs a MultiSelectionModel without a key provider. |
| */ |
| public MultiSelectionModel() { |
| this(null); |
| } |
| |
| /** |
| * Constructs a MultiSelectionModel with the given key provider. |
| * |
| * @param keyProvider an instance of ProvidesKey<T>, or null if the item |
| * should act as its own key |
| */ |
| public MultiSelectionModel(ProvidesKey<T> keyProvider) { |
| this(keyProvider, new HashMap<Object, T>(), new HashMap<Object, SelectionChange<T>>()); |
| } |
| |
| /** |
| * Construct a MultiSelectionModel with the given key provider and |
| * implementations of selectedSet and selectionChanges. Different |
| * implementations allow for enforcing order on selection. |
| * |
| * @param keyProvider an instance of ProvidesKey<T>, or null if the item |
| * should act as its own key |
| * @param selectedSet an instance of Map |
| * @param selectionChanges an instance of Map |
| */ |
| MultiSelectionModel(ProvidesKey<T> keyProvider, Map<Object, T> selectedSet, |
| Map<Object, SelectionChange<T>> selectionChanges) { |
| super(keyProvider); |
| this.selectedSet = selectedSet; |
| this.selectionChanges = selectionChanges; |
| } |
| |
| /** |
| * Deselect all selected values. |
| */ |
| @Override |
| public void clear() { |
| // Clear the current list of pending changes. |
| selectionChanges.clear(); |
| |
| /* |
| * Add a pending change to deselect each key that is currently selected. We |
| * cannot just clear the selected set, because then we would not know which |
| * keys were selected before we cleared, which we need to know to determine |
| * if we should fire an event. |
| */ |
| for (T value : selectedSet.values()) { |
| selectionChanges.put(getKey(value), new SelectionChange<T>(value, false)); |
| } |
| scheduleSelectionChangeEvent(); |
| } |
| |
| /** |
| * Get the set of selected items as a copy. If multiple selected items share |
| * the same key, only the last selected item is included in the set. |
| * |
| * @return the set of selected items |
| */ |
| @Override |
| public Set<T> getSelectedSet() { |
| resolveChanges(); |
| return new HashSet<T>(selectedSet.values()); |
| } |
| |
| @Override |
| public boolean isSelected(T item) { |
| resolveChanges(); |
| return selectedSet.containsKey(getKey(item)); |
| } |
| |
| @Override |
| public void setSelected(T item, boolean selected) { |
| selectionChanges.put(getKey(item), new SelectionChange<T>(item, selected)); |
| scheduleSelectionChangeEvent(); |
| } |
| |
| @Override |
| protected void fireSelectionChangeEvent() { |
| if (isEventScheduled()) { |
| setEventCancelled(true); |
| } |
| resolveChanges(); |
| } |
| |
| void resolveChanges() { |
| if (selectionChanges.isEmpty()) { |
| return; |
| } |
| |
| boolean changed = false; |
| for (Map.Entry<Object, SelectionChange<T>> entry : selectionChanges.entrySet()) { |
| Object key = entry.getKey(); |
| SelectionChange<T> value = entry.getValue(); |
| boolean selected = value.isSelected; |
| |
| T oldValue = selectedSet.get(key); |
| if (selected) { |
| selectedSet.put(key, value.item); |
| Object oldKey = getKey(oldValue); |
| if (!changed) { |
| changed = (oldKey == null) ? (key != null) : !oldKey.equals(key); |
| } |
| } else { |
| if (oldValue != null) { |
| selectedSet.remove(key); |
| changed = true; |
| } |
| } |
| } |
| selectionChanges.clear(); |
| |
| // Fire a selection change event. |
| if (changed) { |
| SelectionChangeEvent.fire(this); |
| } |
| } |
| } |