155 lines
3.3 KiB
Go
155 lines
3.3 KiB
Go
package util
|
|
|
|
type ListSelectionManager struct {
|
|
lastSelectedRow int
|
|
numSelected int
|
|
selected BitSet
|
|
len func() int
|
|
}
|
|
|
|
func NewListSelectionManager(lenFn func() int) ListSelectionManager {
|
|
return ListSelectionManager{lastSelectedRow: -1, len: lenFn}
|
|
}
|
|
|
|
// If the given row is not selected, reset the selection
|
|
// and select only the given row.
|
|
func (l *ListSelectionManager) Select(row int) {
|
|
if row < 0 || l.selected.IsSet(uint(row)) {
|
|
return
|
|
}
|
|
l.UnselectAll()
|
|
l.selectAdd(row)
|
|
}
|
|
|
|
// If it is not selected, add the given row to the selection.
|
|
// If it is selected, unselet it.
|
|
func (l *ListSelectionManager) SelectAddOrRemove(row int) {
|
|
if row < 0 {
|
|
return
|
|
}
|
|
if l.selected.IsSet(uint(row)) {
|
|
// find new last selected row
|
|
if row == l.lastSelectedRow {
|
|
for i := row - 1; i >= -1; i++ {
|
|
if i == -1 {
|
|
l.lastSelectedRow = i
|
|
} else if l.selected.IsSet(uint(i)) {
|
|
l.lastSelectedRow = i
|
|
break
|
|
}
|
|
}
|
|
}
|
|
l.selected.Unset(uint(row))
|
|
l.numSelected -= 1
|
|
return
|
|
}
|
|
|
|
l.selectAdd(row)
|
|
}
|
|
|
|
func (l *ListSelectionManager) selectAdd(row int) {
|
|
l.numSelected += 1
|
|
l.selected.Set(uint(row))
|
|
if row > l.lastSelectedRow {
|
|
l.lastSelectedRow = row
|
|
}
|
|
}
|
|
|
|
// Select a range between the given row and the furthest-down
|
|
// row that is currently selected (which may be above the given row)
|
|
// Note: this is modeled after what, as far as I can tell, is Gmail's selection behavior
|
|
func (l *ListSelectionManager) SelectRange(row int) {
|
|
if row < 0 || l.selected.IsSet(uint(row)) {
|
|
return
|
|
}
|
|
if l.numSelected == 0 {
|
|
l.selectAdd(row)
|
|
return
|
|
}
|
|
m := maxInt(row, l.lastSelectedRow)
|
|
for i := minInt(l.lastSelectedRow, row); i <= m; i++ {
|
|
l.selectAdd(i)
|
|
}
|
|
}
|
|
|
|
func (l *ListSelectionManager) SelectAll() {
|
|
for i := 0; i < l.len(); i++ {
|
|
l.selectAdd(i)
|
|
}
|
|
}
|
|
|
|
func (l *ListSelectionManager) UnselectAll() {
|
|
l.selected = nil
|
|
l.numSelected = 0
|
|
l.lastSelectedRow = -1
|
|
}
|
|
|
|
func (l *ListSelectionManager) IsSelected(row int) bool {
|
|
return row >= 0 && l.selected.IsSet(uint(row))
|
|
}
|
|
|
|
func (l *ListSelectionManager) GetSelection() []int {
|
|
var sel []int
|
|
for i := 0; i < l.len(); i++ {
|
|
if l.selected.IsSet(uint(i)) {
|
|
sel = append(sel, i)
|
|
}
|
|
if len(sel) == l.numSelected {
|
|
break
|
|
}
|
|
}
|
|
return sel
|
|
}
|
|
|
|
func (l *ListSelectionManager) AreAllSelected() bool {
|
|
return l.numSelected == l.len()
|
|
}
|
|
|
|
func minInt(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func maxInt(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// BitSet implementation from
|
|
// https://stackoverflow.com/questions/2311373/how-to-implement-bitset-with-go
|
|
|
|
const uint64size = 64
|
|
|
|
// BitSet is a set of bits that can be set, cleared and queried.
|
|
type BitSet []uint64
|
|
|
|
// Set ensures that the given bit is set in the BitSet.
|
|
func (s *BitSet) Set(i uint) {
|
|
if len(*s) < int(i/uint64size+1) {
|
|
r := make([]uint64, i/uint64size+1)
|
|
copy(r, *s)
|
|
*s = r
|
|
}
|
|
(*s)[i/uint64size] |= 1 << (i % uint64size)
|
|
}
|
|
|
|
// Unset ensures that the given bit is cleared (not set) in the BitSet.
|
|
func (s *BitSet) Unset(i uint) {
|
|
if len(*s) >= int(i/uint64size+1) {
|
|
(*s)[i/uint64size] &^= 1 << (i % uint64size)
|
|
}
|
|
}
|
|
|
|
// IsSet returns true if the given bit is set, false if it is cleared.
|
|
func (s *BitSet) IsSet(i uint) bool {
|
|
idx := i / uint64size
|
|
if idx >= uint(len(*s)) {
|
|
return false
|
|
}
|
|
return (*s)[i/uint64size]&(1<<(i%uint64size)) != 0
|
|
}
|