//
// Copyright (c) 2015, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 16 Feb 2015 Brian Frank Creation
//
using dom
**
** Selection manages the selected items and/or indexes
**
@Js abstract class Selection
{
** Enable or disable selection.
Bool enabled := true
** True to enable multiple selection, false for single selection.
Bool multi := false
{
set { &multi=it; refresh }
}
** Is the selection currently empty
abstract Bool isEmpty()
** Number of selected items
abstract Int size()
** Get or set a single item
abstract Obj? item
** Selected items.
abstract Obj[] items
** Get or set a single index
abstract Int? index
** Selected zero based indexes
abstract Int[] indexes
** Clear the selection
Void clear() { items = Obj[,] }
** Validate selection.
internal virtual Void refresh() {}
}
**************************************************************************
** IndexSelection
**************************************************************************
** Internal implementation for supporting index-based Selection widgets.
@NoDoc @Js abstract class IndexSelection : Selection
{
//////////////////////////////////////////////////////////////////////////
// Selection
//////////////////////////////////////////////////////////////////////////
override Bool isEmpty() { indexes.isEmpty }
override Int size() { indexes.size }
override Obj? item
{
get { items.first }
set { items = (it == null) ? Obj[,] : [it] }
}
override Obj[] items
{
get { toItems(indexes) }
set { indexes = toIndexes(it) }
}
override Int? index
{
get { indexes.first }
set { indexes = (it == null) ? Int[,] : [it] }
}
override Int[] indexes := [,]
{
set
{
if (!enabled) return
oldIndexes := &indexes
newIndexes := checkIndexes(it).sort.ro
&indexes = newIndexes
onUpdate(oldIndexes, newIndexes)
}
}
internal override Void refresh()
{
temp := indexes
indexes = temp
}
//////////////////////////////////////////////////////////////////////////
// Subclass Hooks
//////////////////////////////////////////////////////////////////////////
** Max number of items
protected abstract Int max()
** Lookup item at given index
protected abstract Obj toItem(Int index)
** Lookup index for given item
protected abstract Int? toIndex(Obj item)
** Callback when selection is modified
protected abstract Void onUpdate(Int[] oldIndexes, Int[] newIndexes)
//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////
** Verify that all indexes are less than max and enforce multi
private Int[] checkIndexes(Int[] indexes)
{
checked := indexes.findAll |index| { 0 <= index && index < max }
if (!multi && checked.size > 1) checked = [checked.first]
return checked
}
** List of indexes to items
private Obj[] toItems(Int[] indexes)
{
max := this.max
acc := Obj[,]
acc.capacity = indexes.size
indexes.each |index|
{
if (index < max)
{
item := toItem(index)
acc.add(item)
}
}
return acc
}
** List of items to indexes
private Int[] toIndexes(Obj[] items)
{
acc := Int[,]
acc.capacity = items.size
items.each |item|
{
index := toIndex(item)
if (index != null) acc.add(index)
}
return acc
}
}