/*
* Ext GWT - Ext for GWT
* Copyright(c) 2007-2009, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.widget.selection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.BaseObservable;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionEvent;
import com.extjs.gxt.ui.client.event.SelectionProvider;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
/**
* Abstract base class for store based selection models.
*
* <dl>
* <dt><b>Events:</b></dt>
*
* <dd><b>SelectionChange</b> : SelectionEvent(source, selection)<br>
* <div>Fires after the selection changes.</div>
* <ul>
* <li>source : this</li>
* <li>selection : the selected items</li>
* </ul>
* </dd>
*
* <dd><b>BeforeSelect</b> : SelectionEvent(source, model)<br>
* <div>Fires before a row is selected. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.</div>
* <ul>
* <li>source : this</li>
* <li>model : the selected item</li>
* <li>index : the row index</li>
* </ul>
* </dd>
*
* @param <M> the model type contained within the store
*/
@SuppressWarnings("unchecked")
public abstract class AbstractStoreSelectionModel<M extends ModelData> extends
BaseObservable implements StoreSelectionModel<M>, SelectionProvider<M> {
protected Store<M> store;
protected SelectionMode selectionMode = SelectionMode.MULTI;
protected List<M> selected = new ArrayList<M>();
protected M lastSelected;
protected boolean locked;
protected StoreListener<M> storeListener = new StoreListener<M>() {
@Override
public void storeAdd(StoreEvent<M> se) {
onAdd(se.getModels());
}
@Override
public void storeClear(StoreEvent<M> se) {
onClear(se);
}
@Override
public void storeRemove(StoreEvent<M> se) {
onRemove(se.getModel());
}
@Override
public void storeUpdate(StoreEvent<M> se) {
onUpdate(se.getModel());
}
};
public void addSelectionChangedListener(SelectionChangedListener<M> listener) {
addListener(Events.SelectionChange, listener);
}
public void bind(Store store) {
if (this.store != null) {
this.store.removeStoreListener(storeListener);
}
this.store = store;
if (store != null) {
store.addStoreListener(storeListener);
}
}
public void deselect(int index) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore)store;
M m = ls.getAt(index);
if (m != null) {
doDeselect(Arrays.asList(m), false);
}
}
}
public void deselect(int start, int end) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore)store;
List<M> list = new ArrayList<M>();
for (int i = start; i < end; i++) {
M m = ls.getAt(i);
if (m != null) {
list.add(m);
}
}
doDeselect(list, false);
}
}
public void deselect(List<M> items) {
doDeselect(items, false);
}
public void deselect(M... items) {
deselect(Arrays.asList(items));
}
public void deselect(M item) {
deselect(Arrays.asList(item));
}
public void deselectAll() {
doDeselect(new ArrayList<M>(selected), false);
}
public M getSelectedItem() {
return lastSelected;
}
public List<M> getSelectedItems() {
return new ArrayList<M>(selected);
}
public List<M> getSelection() {
return getSelectedItems();
}
public SelectionMode getSelectionMode() {
return selectionMode;
}
/**
* Returns true if the selection model is locked.
*
* @return the locked state
*/
public boolean isLocked() {
return locked;
}
public boolean isSelected(M item) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore)store;
int idx = ls.indexOf(item);
for (M m : selected) {
if (ls.indexOf(m) == idx) {
return true;
}
}
}
return false;
}
public void refresh() {
List<M> sel = new ArrayList<M>();
boolean change = false;
for (M m : selected) {
M storeModel = store.findModel(m);
if (storeModel != null) {
if (m != storeModel) {
change = true;
}
sel.add(storeModel);
}
}
if (sel.size() != selected.size()) {
change = true;
}
selected.clear();
lastSelected = null;
doSelect(sel, false, true);
if (change) {
fireSelectionChange();
}
}
public void removeSelectionListener(SelectionChangedListener<M> listener) {
removeListener(Events.SelectionChange, listener);
}
public void select(int index, boolean keepExisting) {
select(index, index, keepExisting);
}
public void select(int start, int end, boolean keepExisting) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore)store;
List<M> sel = new ArrayList<M>();
if (start <= end) {
for (int i = start; i <= end; i++) {
sel.add(ls.getAt(i));
}
} else {
for (int i = start; i >= end; i--) {
sel.add(ls.getAt(i));
}
}
doSelect(sel, keepExisting, false);
}
}
public void select(List<M> items, boolean keepExisting) {
doSelect(items, keepExisting, false);
}
public void select(boolean keepExisting, M... items) {
select(Arrays.asList(items), keepExisting);
}
public void select(M item, boolean keepExisting) {
select(Arrays.asList(item), keepExisting);
}
public void selectAll() {
select(store.getModels(), false);
}
/**
* True to lock the selection model. When locked, all selection changes are
* disabled.
*
* @param locked true to lock
*/
public void setLocked(boolean locked) {
this.locked = locked;
}
public void setSelection(List<M> selection) {
select(selection, false);
}
public void setSelectionMode(SelectionMode selectionMode) {
this.selectionMode = selectionMode;
}
protected void doDeselect(List<M> models, boolean supressEvent) {
if (locked) return;
boolean change = false;
for (M m : models) {
if (isSelected(m)) {
selected.remove(m);
if (lastSelected == m) {
lastSelected = null;
}
onSelectChange(m, false);
change = true;
}
}
if (!supressEvent && change) {
fireSelectionChange();
}
}
protected void doMultiSelect(List<M> models, boolean keepExisting, boolean supressEvent) {
if (locked) return;
boolean change = false;
if (!keepExisting && selected.size() > 0) {
change = true;
doDeselect(new ArrayList<M>(selected), true);
}
for (M m : models) {
SelectionEvent<M> e = new SelectionEvent<M>(this, m);
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore)store;
e.setIndex(ls.indexOf(m));
}
if (e.getIndex() == -1 || !fireEvent(Events.BeforeSelect, e)) {
continue;
}
change = true;
lastSelected = m;
selected.add(m);
onSelectChange(m, true);
}
if (change && !supressEvent) {
fireSelectionChange();
}
}
protected void doSelect(List<M> models, boolean keepExisting, boolean supressEvent) {
if (locked) return;
if (selectionMode == SelectionMode.SINGLE) {
M m = models.size() > 0 ? models.get(0) : null;
if (m != null) {
doSingleSelect(m, supressEvent);
}
} else {
doMultiSelect(models, keepExisting, supressEvent);
}
}
protected void doSingleSelect(M model, boolean supressEvent) {
if (locked) return;
SelectionEvent<M> e = new SelectionEvent<M>(this, model);
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore)store;
e.setIndex(ls.indexOf(model));
}
if (e.getIndex() == -1 || isSelected(model) || !fireEvent(Events.BeforeSelect, e)) {
return;
}
boolean change = false;
if (selected.size() > 0 && !isSelected(model)) {
doDeselect(Arrays.asList(lastSelected), true);
change = true;
}
if (selected.size() == 0) {
change = true;
}
selected.add(model);
lastSelected = model;
onSelectChange(model, true);
if (change && !supressEvent) {
fireSelectionChange();
}
}
protected void onAdd(List<? extends M> models) {
}
protected void onClear(StoreEvent<M> se) {
int oldSize = selected.size();
selected.clear();
lastSelected = null;
if (oldSize > 0) fireSelectionChange();
}
protected void onRemove(M model) {
if (locked) return;
if (isSelected(model)) {
selected.remove(model);
if (lastSelected == model) {
lastSelected = null;
}
fireSelectionChange();
}
}
protected abstract void onSelectChange(M model, boolean select);
protected void onUpdate(M model) {
if (locked) return;
if (isSelected(model)) {
int index = selected.indexOf(model);
selected.remove(model);
selected.add(index, model);
}
}
protected void fireSelectionChange() {
fireEvent(Events.SelectionChange, new SelectionChangedEvent(this, new ArrayList(
selected)));
}
}