719 lines
28 KiB
Java
719 lines
28 KiB
Java
/*
|
|
* Copyright (c) 2002,2009 Declarative Engineering LLC.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Declarative Engineering LLC
|
|
* verson 1 which accompanies this distribution, and is available at
|
|
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
|
*/
|
|
package com.foundation.view.swt;
|
|
|
|
import com.common.util.*;
|
|
import com.common.util.optimized.*;
|
|
|
|
/**
|
|
* Used as a base class for collection components that are indexed.
|
|
* Components such as a list of strings may be indexed such that objects from which the strings are derived cannot be attached to the row since there isn't the concept of a row.
|
|
* This class tracks the mapping between the row value and the value's index in the collection to allow selections and updates to be converted to and from values and indices.
|
|
*/
|
|
public abstract class IndexedCollectionComponent extends CollectionComponent {
|
|
/** A mapping of collection values by the control assigned indexes. */
|
|
private IIndexedCollection valueByIndexMap = new LiteList(100);
|
|
/** A mapping of control assigned indices by the collection values. Since the item can be in the collection more than once we will either store an Integer or an IntArray. */
|
|
private LiteHashMap indexByValueMap = new LiteHashMap(100, com.common.comparison.Comparator.getLogicalComparator(), null);
|
|
/**
|
|
* IndexedCollectionComponent constructor.
|
|
* @param parent The parent container.
|
|
* @param name The name of the component.
|
|
* @param style The style for the control.
|
|
*/
|
|
public IndexedCollectionComponent(Container parent, String name, int style) {
|
|
super(parent, name, style);
|
|
}//IndexedCollectionComponent()//
|
|
/**
|
|
* Gets the control specific data for a collection item.
|
|
* @param item The item data which will eventually make it to the visual control.
|
|
* @return The data for the item. This can be an array of values if necessary.
|
|
*/
|
|
protected abstract Object getItemData(Object item);
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#updateHiddenData(java.lang.Object, int, java.lang.Object)
|
|
*/
|
|
protected void updateHiddenData(Object item, int hiddenDataIndex, Object value) {
|
|
if(getAllowMultiSelection()) {
|
|
boolean updateSelectionLinks = false;
|
|
int[] selectedIndices = controlGetSelections();
|
|
Object[] selectedValues = new Object[selectedIndices.length];
|
|
|
|
if((selectedIndices != null) && (selectedIndices.length > 0)) {
|
|
//Determine whether any of the selections match with the hidden data's item to see whether we need to update any hidden data linkages.//
|
|
for(int selectionIndex = 0; (!updateSelectionLinks) && (selectionIndex < selectedIndices.length); selectionIndex++) {
|
|
Object selectionValue = valueByIndexMap.get(selectedIndices[selectionIndex]);
|
|
|
|
updateSelectionLinks = selectionValue == item;
|
|
selectedValues[selectionIndex] = selectionValue;
|
|
}//for//
|
|
}//if//
|
|
|
|
if(updateSelectionLinks) {
|
|
getHiddenData(hiddenDataIndex).invokeLinkage(selectedValues);
|
|
}//if//
|
|
}//if//
|
|
else {
|
|
int selectedIndex = controlGetSelection();
|
|
|
|
if(selectedIndex != -1) {
|
|
Object selectedValue = valueByIndexMap.get(selectedIndex);
|
|
|
|
if(selectedValue == item) {
|
|
getHiddenData(hiddenDataIndex).invokeLinkage(selectedValue);
|
|
}//if//
|
|
}//if//
|
|
}//else//
|
|
}//updateHiddenData()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#updateSelectionLinks()
|
|
*/
|
|
protected void updateSelectionLinks() {
|
|
super.updateSelectionLinks();
|
|
|
|
if(getHiddenDataCount() > 0) {
|
|
if(getAllowMultiSelection()) {
|
|
int[] selectedIndices = controlGetSelections();
|
|
Object[] selectedValues = selectedIndices != null && selectedIndices.length > 0 ? new Object[selectedIndices.length] : null;
|
|
|
|
if(selectedValues != null) {
|
|
//Collect the selected values.//
|
|
for(int selectionIndex = 0; selectionIndex < selectedIndices.length; selectionIndex++) {
|
|
selectedValues[selectionIndex] = valueByIndexMap.get(selectedIndices[selectionIndex]);
|
|
}//for//
|
|
}//if//
|
|
|
|
//Update the hidden data linkages.//
|
|
for(int hiddenDataIndex = 0; hiddenDataIndex < getHiddenDataCount(); hiddenDataIndex++) {
|
|
getHiddenData(hiddenDataIndex).invokeLinkage(selectedValues);
|
|
}//for//
|
|
}//if//
|
|
else {
|
|
int selectedIndex = controlGetSelection();
|
|
Object selectedValue = selectedIndex != -1 ? valueByIndexMap.get(selectedIndex) : null;
|
|
|
|
//Update the hidden data linkages.//
|
|
for(int hiddenDataIndex = 0; hiddenDataIndex < getHiddenDataCount(); hiddenDataIndex++) {
|
|
getHiddenData(hiddenDataIndex).invokeLinkage(selectedValue);
|
|
}//for//
|
|
}//else//
|
|
}//if//
|
|
}//updateSelectionLinks()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#internalViewRefreshCollection(com.common.util.ICollection, com.common.util.ICollection)
|
|
*/
|
|
protected void internalViewRefreshCollection(ICollection newCollection, ICollection oldCollection) {
|
|
//TODO: Make this simpler - this is not at all as easy as the tcv code & is less useful.//
|
|
if((newCollection != null) && (newCollection.getSize() > 0)) {
|
|
Object[] items = new Object[newCollection.getSize()];
|
|
IIterator iterator = newCollection.iterator();
|
|
int removedCount = 0;
|
|
|
|
//Remove all index mappings so we reflect the latest collection ordering (in case the user wants the order to reflect a new collection order).//
|
|
valueByIndexMap.removeAll();
|
|
indexByValueMap.removeAll();
|
|
|
|
//Get the array of strings representing the list contents.//
|
|
while(iterator.hasNext()) {
|
|
Object value = iterator.next();
|
|
|
|
//Prevent two objects that are exactly the same from being added to the collection control.//
|
|
if(indexByValueMap.containsKey(value)) {
|
|
//Do nothing.//
|
|
removedCount++;
|
|
}//if//
|
|
else {
|
|
int valueIndex = addValueToMappings(value);
|
|
|
|
//Add the value to the item array.//
|
|
items[valueIndex] = getItemData(value);
|
|
}//else//
|
|
}//while//
|
|
|
|
//Truncate the array so we don't have nulls at the end.//
|
|
if(removedCount > 0) {
|
|
Object[] newItems = new Object[items.length - removedCount];
|
|
|
|
System.arraycopy(items, 0, newItems, 0, newItems.length);
|
|
items = newItems;
|
|
}//if//
|
|
|
|
//Set the collection contents.//
|
|
controlSetItems(items);
|
|
}//if//
|
|
else {
|
|
//Remove all existing items.//
|
|
controlRemoveAll();
|
|
}//else//
|
|
}//internalViewRefreshCollection()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#internalViewRefreshSelection(java.lang.Object)
|
|
*/
|
|
protected void internalViewRefreshSelection(Object selectedItem) {
|
|
//TODO: Make this simpler - this is not at all as easy as the tcv code & is less useful.//
|
|
int selectionIndex = controlGetSelection();
|
|
int modelSelectionIndex = -1;
|
|
Object controlSelection = selectionIndex != -1 ? valueByIndexMap.get(selectionIndex) : null;
|
|
Object indexData = selectedItem != null ? indexByValueMap.get(selectedItem) : null;
|
|
|
|
if(indexData == null) {
|
|
modelSelectionIndex = -1;
|
|
}//if//
|
|
else if(indexData instanceof Integer) {
|
|
modelSelectionIndex = ((Integer) indexData).intValue();
|
|
}//else if//
|
|
else {
|
|
//Select the first one if the selection is not one of the possible ones.//
|
|
if(!((IntArray) indexData).containsValue(modelSelectionIndex)) {
|
|
modelSelectionIndex = ((IntArray) indexData).get(0); //Use the first occurance of the object.//
|
|
}//if//
|
|
}//else//
|
|
|
|
if((getAllowUserItems()) && (selectedItem != null) && (indexData == null)) {
|
|
//Set a custom selection for the control (currently only supported for string values).//
|
|
controlSetCustomItem(selectedItem);
|
|
selectionIndex = -1;
|
|
}//if//
|
|
else if(controlSelection != selectedItem) {
|
|
//Set the control selection.//
|
|
controlSetSelection(modelSelectionIndex);
|
|
}//else if//
|
|
}//internalViewRefreshSelection()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#internalViewRefreshSelections(com.common.util.ICollection, com.common.util.ICollection)
|
|
*/
|
|
protected void internalViewRefreshSelections(ICollection newSelections, ICollection oldSelections) {
|
|
//TODO: Make this simpler - this is not at all as easy as the tcv code & is less useful.//
|
|
if(newSelections != null) {
|
|
int[] controlSelections = controlGetSelections();
|
|
boolean[] markedSelections = new boolean[controlSelections.length];
|
|
IIterator selectionIterator = newSelections.iterator();
|
|
|
|
for(int index = 0; index < markedSelections.length; index++) {
|
|
markedSelections[index] = false;
|
|
}//for//
|
|
|
|
//Apply differences between the selection collection and the control selection. Also remove all impossible selections.//
|
|
while(selectionIterator.hasNext()) {
|
|
Object modelSelection = selectionIterator.next();
|
|
Object indexData = indexByValueMap.get(modelSelection);
|
|
|
|
if(indexData == null) {
|
|
//An invalid selection because the selection is not in the collection of displayed values.//
|
|
selectionIterator.remove();
|
|
}//if//
|
|
else if(indexData instanceof Integer) {
|
|
int selectionIndex = ((Integer) indexData).intValue();
|
|
|
|
for(int index = 0; index < controlSelections.length; index++) {
|
|
if(controlSelections[index] == selectionIndex) {
|
|
if(!markedSelections[index]) {
|
|
//The selection is already in the control.//
|
|
markedSelections[index] = true;
|
|
selectionIndex = -1;
|
|
break;
|
|
}//if//
|
|
else {
|
|
//Cannot have two of the same value selected if the value is only in the data collection once.//
|
|
selectionIterator.remove();
|
|
selectionIndex = -1;
|
|
}//else//
|
|
}//if//
|
|
}//for//
|
|
|
|
//If we did not find the selected object in the array of currently selected indexes, then add it now.//
|
|
if(selectionIndex != -1) {
|
|
controlAddSelection(selectionIndex);
|
|
}//if//
|
|
}//else if//
|
|
else {
|
|
IntArray indices = (IntArray) indexData;
|
|
boolean wasFound = false;
|
|
|
|
//This is a complex bit of code that must as best as possible identify whether the selection is already represented in the control.//
|
|
for(int index = 0; index < controlSelections.length; index++) {
|
|
//If the control selection is one of the indexes assigned to the selected object then check to see if we have already identified it as used.//
|
|
if(indices.containsValue(controlSelections[index])) {
|
|
if(!markedSelections[index]) {
|
|
//The selection is already in the control and has not already been marked.//
|
|
markedSelections[index] = true;
|
|
wasFound = true;
|
|
break;
|
|
}//if//
|
|
}//if//
|
|
}//for//
|
|
|
|
//If we did not find the selected object in the array of currently selected indexes, then add it now.//
|
|
if(!wasFound) {
|
|
IIntIterator indicesIterator = indices.iterator();
|
|
boolean wasAdded = false;
|
|
|
|
//Since we don't know exactly which one to add, we will have to search for one that is not selected.//
|
|
//Note: We could seemingly do this faster by querying the control, but that would require a round trip query to the control which may be very expensive.//
|
|
while((!wasAdded) && (indicesIterator.hasNext())) {
|
|
int nextIndex = indicesIterator.next();
|
|
|
|
//Search the control selections for the next possible selection index.//
|
|
for(int index = 0; index < controlSelections.length; index++) {
|
|
if(nextIndex == controlSelections[index]) {
|
|
nextIndex = -1;
|
|
break;
|
|
}//if//
|
|
}//for//
|
|
|
|
//If the next possible selection index was not in the control's selection collection then we can add it.//
|
|
if(nextIndex != -1) {
|
|
controlAddSelection(nextIndex);
|
|
wasAdded = true;
|
|
}//if//
|
|
}//while//
|
|
|
|
if(!wasAdded) {
|
|
//Cannot have more references to a value in the selection collection than are in the data collection.//
|
|
selectionIterator.remove();
|
|
}//if//
|
|
}//if//
|
|
}//else//
|
|
}//while//
|
|
|
|
//Remove all control selections that have not been marked. These selections were not represented in the model collection of selections.//
|
|
for(int index = 0; index < markedSelections.length; index++) {
|
|
if(!markedSelections[index]) {
|
|
controlRemoveSelection(controlSelections[index]);
|
|
}//if//
|
|
}//for//
|
|
}//if//
|
|
else {
|
|
//Remove all selections.//
|
|
controlSetSelection(-1);
|
|
}//else//
|
|
}//internalViewRefreshSelections()//
|
|
/**
|
|
* Synchronizes the selection to the model from the view.
|
|
* <p>This method has no logic to examine the auto synchronization state. It simply synchronizes the selection immediatly.</p>
|
|
*/
|
|
protected void synchronizeSelection() {
|
|
if(getAllowMultiSelection()) {
|
|
int[] controlSelectionIndices = controlGetSelections();
|
|
ICollection modelSelections = getModelSelections();
|
|
|
|
//Prevent the selection additions from causing a feedback loop.//
|
|
suspendSelectionHooks = true;
|
|
|
|
try {
|
|
if(modelSelections != null) {
|
|
Object[] controlSelectionObjects = new Object[controlSelectionIndices.length];
|
|
boolean[] markedControlSelections = new boolean[controlSelectionObjects.length];
|
|
IIterator modelSelectionIterator = modelSelections.iterator();
|
|
LiteList removed = new LiteList(modelSelections.getSize());
|
|
LiteList added = new LiteList(controlSelectionIndices.length);
|
|
|
|
for(int index = 0; index < controlSelectionIndices.length; index++) {
|
|
controlSelectionObjects[index] = valueByIndexMap.get(controlSelectionIndices[index]);
|
|
markedControlSelections[index] = false;
|
|
}//for//
|
|
|
|
//Add selected items to the selection collection.//
|
|
while(modelSelectionIterator.hasNext()) {
|
|
Object modelSelection = modelSelectionIterator.next();
|
|
boolean found = false;
|
|
|
|
for(int index = 0; (!found) && (index < markedControlSelections.length); index++) {
|
|
if(!markedControlSelections[index]) {
|
|
if(controlSelectionObjects[index] == modelSelection) {
|
|
markedControlSelections[index] = true;
|
|
found = true;
|
|
}//if//
|
|
}//if//
|
|
}//for//
|
|
|
|
if(!found) {
|
|
removed.add(modelSelection);
|
|
}//if//
|
|
}//while//
|
|
|
|
for(int index = 0; index < markedControlSelections.length; index++) {
|
|
if(!markedControlSelections[index]) {
|
|
added.add(controlSelectionObjects[index]);
|
|
}//if//
|
|
}//for//
|
|
|
|
modelSelections.replaceAll(removed, added);
|
|
}//if//
|
|
}//try//
|
|
finally {
|
|
suspendSelectionHooks = false;
|
|
}//finally//
|
|
}//if//
|
|
else {
|
|
int selectionIndex = controlGetSelection();
|
|
Object controlSelection = selectionIndex != -1 ? valueByIndexMap.get(selectionIndex) : null;
|
|
|
|
if((getAllowUserItems()) && (selectionIndex == -1) && (controlGetText().length() > 0)) {
|
|
setModelSelection(controlGetText());
|
|
}//if//
|
|
else {
|
|
setModelSelection(controlSelection);
|
|
}//else if//
|
|
}//else//
|
|
}//internalViewSynchronizeSelection()//
|
|
/**
|
|
* Gets the index (or indices) for the given item.
|
|
* @param item The item whose index is required.
|
|
* @return The index data for the item which will either be an Integer, an IntArray, or null if the item is not mapped.
|
|
*/
|
|
protected Object getItemIndexData(Object item) {
|
|
return (item != null) && (indexByValueMap.containsKey(item)) ? indexByValueMap.get(item) : null;
|
|
}//getItemIndexData()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#itemAdded(java.lang.Object, int)
|
|
*/
|
|
protected void itemAdded(Object item, int index) {
|
|
//Setup a mapping and a new index for the value.//
|
|
index = addValueToMappings(item);
|
|
//Add the value to the control.//
|
|
controlAddItem(getItemData(item), index);
|
|
|
|
//TODO: Should this be allowed? If the user sets the selection to an object not in the collection, should we prevent it? What about controls like a combobox which allow custom input?//
|
|
if((!getAllowMultiSelection()) && (getModelSelection() == item)) {
|
|
controlSetSelection(index);
|
|
}//if//
|
|
}//itemAdded()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#itemRemoved(java.lang.Object, int)
|
|
*/
|
|
protected void itemRemoved(Object item, int index) {
|
|
if(indexByValueMap.containsKey(item)) {
|
|
Object indexData = indexByValueMap.get(item);
|
|
int modelIndex;
|
|
|
|
//Determine whether the removed object occurs more than once in the collection.//
|
|
if(indexData instanceof Integer) {
|
|
modelIndex = ((Integer) indexData).intValue();
|
|
//Remove the item from the control and unregister all event handlers.//
|
|
controlRemoveItem(modelIndex);
|
|
}//if//
|
|
else {
|
|
IntArray indices = (IntArray) indexData;
|
|
//TODO: We could make things look better by removing the selected object when there are more than one possible objects to be removed.//
|
|
//Assume that we should remove the last occurance of the object.//
|
|
modelIndex = indices.get(indices.getSize() - 1);
|
|
//Remove the item from the control.//
|
|
controlRemoveItem(modelIndex);
|
|
}//else//
|
|
|
|
//Remove the object from the mappings.//
|
|
removeValueFromMappings(item, modelIndex);
|
|
}//if//
|
|
}//itemRemoved()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#itemAllRemoved()
|
|
*/
|
|
protected void itemAllRemoved() {
|
|
//Remove all index mappings so we reflect the latest collection ordering (in case the user wants the order to reflect a new collection order).//
|
|
valueByIndexMap.removeAll();
|
|
indexByValueMap.removeAll();
|
|
//Remove all existing items.//
|
|
controlRemoveAll();
|
|
}//itemAllRemoved()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#registerItem(java.lang.Object)
|
|
*/
|
|
protected void registerItem(Object item) {
|
|
internalItemAdded(item);
|
|
}//registerItem()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#unregisterItem(java.lang.Object)
|
|
*/
|
|
protected void unregisterItem(Object item) {
|
|
internalItemRemoved(item);
|
|
}//unregisterItem()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#unregisterItems()
|
|
*/
|
|
protected void unregisterItems() {
|
|
internalItemsRemoved();
|
|
}//unregisterItems()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#itemSorted(int[])
|
|
*/
|
|
protected void itemSorted(int[] mapping) {
|
|
//TODO: Re-order the indicies in the control and rebuild the mappings.//
|
|
}//itemSorted()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#selectionAdded(java.lang.Object)
|
|
*/
|
|
protected void selectionAdded(Object value) {
|
|
checkObjectSelectionCount(value);
|
|
}//selectionAdded()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#selectionRemoved(java.lang.Object)
|
|
*/
|
|
protected void selectionRemoved(Object value) {
|
|
checkObjectSelectionCount(value);
|
|
}//selectionRemoved()//
|
|
/* (non-Javadoc)
|
|
* @see com.foundation.view.swt.CollectionComponent#selectionAllRemoved()
|
|
*/
|
|
protected void selectionAllRemoved() {
|
|
controlRemoveAllSelections();
|
|
}//selectionAllRemoved()//
|
|
/**
|
|
* Verifies that the correct number of selections for the given value exist in the control.
|
|
* @param value The value that is being checked. It may be checked because a selection was added, or removed.
|
|
*/
|
|
protected void checkObjectSelectionCount(Object value) {
|
|
if(!getAllowMultiSelection()) {
|
|
throw new RuntimeException("Cannot call this method: 'checkObjectSelectionCount' if multiple selections are not allowed.");
|
|
}//if//
|
|
|
|
if(indexByValueMap.containsKey(value)) {
|
|
int[] controlSelectionIndices = controlGetSelections();
|
|
Object indexData = indexByValueMap.get(value);
|
|
int possibleCount = indexData == null ? 0 : indexData instanceof Integer ? 1 : ((IntArray) indexData).getSize();
|
|
int modelCount = 0;
|
|
IIterator iterator = getModelSelections().iterator();
|
|
|
|
//Count the number of times the object is in the model's selection collection. Remove impossible references.//
|
|
while(iterator.hasNext()) {
|
|
if(iterator.next() == value) {
|
|
modelCount++;
|
|
|
|
//Fix impossible selections.//
|
|
if(modelCount > possibleCount) {
|
|
modelCount = possibleCount;
|
|
iterator.remove();
|
|
}//if//
|
|
}//if//
|
|
}//while//
|
|
|
|
//Count the number of times the object is selected in the control's selections. Remove or add selections to match the model's selection collection.//
|
|
if(indexData instanceof Integer) {
|
|
int valueIndex = ((Integer) indexData).intValue();
|
|
int controlCount = 0;
|
|
|
|
for(int index = 0; index < controlSelectionIndices.length; index++) {
|
|
if(controlSelectionIndices[index] == valueIndex) {
|
|
controlCount++;
|
|
|
|
if(controlCount > modelCount) {
|
|
controlRemoveSelection(valueIndex);
|
|
}//if//
|
|
|
|
//Stop iterating since there can only be one selection for this object.//
|
|
break;
|
|
}//if//
|
|
}//for//
|
|
|
|
if(controlCount < modelCount) {
|
|
controlAddSelection(valueIndex);
|
|
}//if//
|
|
}//if//
|
|
else {
|
|
int controlCount = 0;
|
|
IntArray valueIndices = new IntArray((IntArray) indexData); //Copy the object indices so we can remove the ones we already have used.//
|
|
|
|
for(int index = 0; index < controlSelectionIndices.length; index++) {
|
|
Object controlSelectionValue = valueByIndexMap.get(controlSelectionIndices[index]);
|
|
|
|
if(controlSelectionValue == value) {
|
|
controlCount++;
|
|
|
|
if(controlCount > modelCount) {
|
|
//Remove the selection because there are too many.//
|
|
controlRemoveSelection(controlSelectionIndices[index]);
|
|
}//if//
|
|
else {
|
|
//Remove used object indexes so it is easy to add more references if they are required.//
|
|
valueIndices.remove(valueIndices.getIndexOf(controlSelectionIndices[index]));
|
|
}//else//
|
|
}//if//
|
|
}//for//
|
|
|
|
//Add selections for the object until the counts match.//
|
|
while(controlCount < modelCount) {
|
|
controlAddSelection(valueIndices.get(valueIndices.getSize()));
|
|
valueIndices.remove(valueIndices.getSize());
|
|
controlCount++;
|
|
}//while//
|
|
}//else//
|
|
}//if//
|
|
}//checkObjectCount()//
|
|
/**
|
|
* Removes a value from the mappings of values and internal indexes.
|
|
* <p>Note: The current implementation is very fast, except when removing items from a very large collection of values. This performance problem is due to the need to clean the object->index mapping after each removal.</p>
|
|
* @param removedValue The value removed from the collection being displayed.
|
|
* @param removedIndex The index of the removed value. This is necessary because a value could be in the collection more than one time.
|
|
*/
|
|
protected void removeValueFromMappings(Object removedValue, int removedIndex) {
|
|
if(indexByValueMap.containsKey(removedValue)) {
|
|
Object indexData = indexByValueMap.get(removedValue);
|
|
IIterator iterator = indexByValueMap.keyIterator();
|
|
|
|
if(indexData instanceof IntArray) {
|
|
IntArray indices = (IntArray) indexData;
|
|
|
|
//Remove the specified index from the array of indices.//
|
|
indices.remove(indices.getIndexOf(removedIndex));
|
|
|
|
//Optimize things by only using an IntArray when necessary.//
|
|
if(indices.getSize() == 1) {
|
|
indexByValueMap.put(removedValue, new Integer(indices.get(0)));
|
|
}//if//
|
|
}//if//
|
|
else {
|
|
//Since there should be no other occurances of the object in the collection we can remove the mapping altogether.//
|
|
indexByValueMap.remove(removedValue);
|
|
}//else//
|
|
|
|
valueByIndexMap.remove(removedIndex);
|
|
|
|
while(iterator.hasNext()) {
|
|
Object next = iterator.next();
|
|
|
|
indexData = indexByValueMap.get(next);
|
|
|
|
if(indexData instanceof Integer) {
|
|
if(((Integer) indexData).intValue() > removedIndex) {
|
|
indexByValueMap.put(next, new Integer(((Integer) indexData).intValue() - 1));
|
|
}//if//
|
|
}//if//
|
|
else {
|
|
IntArray indices = (IntArray) indexData;
|
|
|
|
//TODO: For large collections where items may be regularly removed we may wish to create a special collection that maintains the object to index mappings. We could use the bucket approach to maintaining the index number.//
|
|
for(int index = indices.getSize() - 1; (index >= 0) && (indices.get(index) > removedIndex); index--) {
|
|
indices.replace(index, indices.get(index) - 1);
|
|
}//for//
|
|
}//else//
|
|
}//while//
|
|
}//if//
|
|
}//removeValueFromMappings()//
|
|
/**
|
|
* Adds a value to the mappings of values and internal indexes.
|
|
* @param addedValue The value added to the collection being displayed.
|
|
* @return The internal index of the added value.
|
|
*/
|
|
protected int addValueToMappings(Object addedValue) {
|
|
int index = valueByIndexMap.getSize();
|
|
|
|
//Always add the value to the indexed mapping.//
|
|
valueByIndexMap.add(addedValue);
|
|
|
|
//Either add the index to the index by value map, or add it to a collection indexed by the object if the object is in the collection more than once.//
|
|
if(!indexByValueMap.containsKey(addedValue)) {
|
|
//Add the index to the mappings.//
|
|
indexByValueMap.put(addedValue, new Integer(index));
|
|
}//if//
|
|
else {
|
|
Object indexData = indexByValueMap.get(addedValue);
|
|
|
|
if(indexData instanceof Integer) {
|
|
IntArray indices = new IntArray(10, 30);
|
|
|
|
//Create a collection of indices for the same object.//
|
|
indices.add(((Integer) indexData).intValue());
|
|
indices.add(index);
|
|
indexByValueMap.put(addedValue, indices);
|
|
}//if//
|
|
else {
|
|
//Add the new index to the collection of indices where this same object can be found in the control's list.//
|
|
((IntArray) indexData).add(index);
|
|
}//else//
|
|
}//else//
|
|
|
|
return index;
|
|
}//addValueToMappings()//
|
|
/**
|
|
* Removes all displayed items from the control.
|
|
*/
|
|
protected abstract void controlRemoveAll();
|
|
/**
|
|
* Sets the control's displayed collection values ordered by index.
|
|
* @param items The indexed values displayed by the control.
|
|
*/
|
|
protected abstract void controlSetItems(Object[] items);
|
|
/**
|
|
* Sets an item in the control's collection display.
|
|
* @param index The index at which the item should be set.
|
|
* @param itemData The value displayed by the control.
|
|
*/
|
|
protected abstract void controlSetItem(int index, Object itemData);
|
|
/**
|
|
* Adds an item to the control's collection display.
|
|
* @param itemData The value displayed by the control.
|
|
* @param index The index at which the item should be added.
|
|
*/
|
|
protected abstract void controlAddItem(Object itemData, int index);
|
|
/**
|
|
* Adds an item to the control's collection at the end.
|
|
* @param itemData The value displayed by the control.
|
|
*/
|
|
protected abstract void controlAddItem(Object itemData);
|
|
/**
|
|
* Removes an item from the control's collection display.
|
|
* @param index The index of the item to be removed.
|
|
*/
|
|
protected abstract void controlRemoveItem(int index);
|
|
/**
|
|
* Gets the indices selected within the control.
|
|
* @return The collection of indices selected.
|
|
*/
|
|
protected abstract int[] controlGetSelections();
|
|
/**
|
|
* Gets the index selected within the control.
|
|
* @return The index currently selected.
|
|
*/
|
|
protected abstract int controlGetSelection();
|
|
/**
|
|
* Sets the selected values in the control.
|
|
* @param indices The indices of the selected values.
|
|
*/
|
|
protected abstract void controlSetSelections(int[] indices);
|
|
/**
|
|
* Sets the selected value in the control.
|
|
* @param index The index of the selected value.
|
|
*/
|
|
protected abstract void controlSetSelection(int index);
|
|
/**
|
|
* Adds a selected value in the control.
|
|
* @param index The index of the selected value.
|
|
*/
|
|
protected abstract void controlAddSelection(int index);
|
|
/**
|
|
* Deselects a value in the control.
|
|
* @param index The index of the selected value.
|
|
*/
|
|
protected abstract void controlRemoveSelection(int index);
|
|
/**
|
|
* Deselects all values in the control.
|
|
*/
|
|
protected abstract void controlRemoveAllSelections();
|
|
/**
|
|
* Checks to see if a value is selected.
|
|
* @param index The index of the checked value.
|
|
* @return Whether the value is currently selected.
|
|
*/
|
|
protected abstract boolean controlIsSelected(int index);
|
|
/**
|
|
* Gets the current text for the control.
|
|
* <p>Some controls such as comboboxes have a text which may not be one of the listed items.</p>
|
|
* @return The control's current text.
|
|
*/
|
|
protected abstract String controlGetText();
|
|
/**
|
|
* Sets the current custom selection item for the control.
|
|
* <p>Some controls such as comboboxes have a text which may not be one of the listed items.</p>
|
|
* @param item The controls custom selected item.
|
|
*/
|
|
protected abstract void controlSetCustomItem(Object item);
|
|
/**
|
|
* Forces the selection event handler to be called.
|
|
*/
|
|
protected abstract void forceSelectionEvent(); //TODO: Should call widgetSelected implemented by the subclass. This name may need to change.//
|
|
}//IndexedCollectionComponent// |