1package checklist
2
3// Checklist is a module for creating generic checklist implementations
4// See 'Todo' for an implementation example
5type Checklist struct {
6	Items []*ChecklistItem
7
8	checkedIcon   string
9	selected      int
10	uncheckedIcon string
11}
12
13func NewChecklist(checkedIcon, uncheckedIcon string) Checklist {
14	list := Checklist{
15		checkedIcon:   checkedIcon,
16		selected:      -1,
17		uncheckedIcon: uncheckedIcon,
18	}
19
20	return list
21}
22
23/* -------------------- Exported Functions -------------------- */
24
25// Add creates a new item in the checklist
26func (list *Checklist) Add(checked bool, text string) {
27	item := NewChecklistItem(
28		checked,
29		text,
30		list.checkedIcon,
31		list.uncheckedIcon,
32	)
33
34	list.Items = append([]*ChecklistItem{item}, list.Items...)
35}
36
37// CheckedItems returns a slice of all the checked items
38func (list *Checklist) CheckedItems() []*ChecklistItem {
39	items := []*ChecklistItem{}
40
41	for _, item := range list.Items {
42		if item.Checked {
43			items = append(items, item)
44		}
45	}
46
47	return items
48}
49
50// Delete removes the selected item from the checklist
51func (list *Checklist) Delete() {
52	list.Items = append(list.Items[:list.selected], list.Items[list.selected+1:]...)
53	list.Prev()
54}
55
56// IsSelectable returns true if the checklist has selectable items, false if it does not
57func (list *Checklist) IsSelectable() bool {
58	return list.selected >= 0 && list.selected < len(list.Items)
59}
60
61// IsUnselectable returns true if the checklist has no selectable items, false if it does
62func (list *Checklist) IsUnselectable() bool {
63	return !list.IsSelectable()
64}
65
66// LongestLine returns the length of the longest checklist item's text
67func (list *Checklist) LongestLine() int {
68	maxLen := 0
69
70	for _, item := range list.Items {
71		if len(item.Text) > maxLen {
72			maxLen = len(item.Text)
73		}
74	}
75
76	return maxLen
77}
78
79func (list *Checklist) Selected() int {
80	return list.selected
81}
82
83// SelectedItem returns the currently-selected checklist item or nil if no item is selected
84func (list *Checklist) SelectedItem() *ChecklistItem {
85	if list.IsUnselectable() {
86		return nil
87	}
88
89	return list.Items[list.selected]
90}
91
92func (list *Checklist) SetSelectedByItem(selectableItem *ChecklistItem) {
93	for idx, item := range list.Items {
94		if item == selectableItem {
95			list.selected = idx
96			break
97		}
98	}
99}
100
101// Toggle switches the checked state of the currently-selected item
102func (list *Checklist) Toggle() {
103	if list.IsUnselectable() {
104		return
105	}
106
107	list.SelectedItem().Toggle()
108}
109
110// UncheckedItems returns a slice of all the unchecked items
111func (list *Checklist) UncheckedItems() []*ChecklistItem {
112	items := []*ChecklistItem{}
113
114	for _, item := range list.Items {
115		if !item.Checked {
116			items = append(items, item)
117		}
118	}
119
120	return items
121}
122
123// Unselect removes the current select such that no item is selected
124func (list *Checklist) Unselect() {
125	list.selected = -1
126}
127
128// Update sets the text of the currently-selected item to the provided text
129func (list *Checklist) Update(text string) {
130	item := list.SelectedItem()
131
132	if item == nil {
133		return
134	}
135
136	item.Text = text
137}
138
139/* -------------------- Item Movement -------------------- */
140
141// Prev selects the previous item UP in the checklist
142func (list *Checklist) Prev() {
143	list.selected--
144	if list.selected < 0 {
145		list.selected = len(list.Items) - 1
146	}
147}
148
149// Next selects the next item DOWN in the checklist
150func (list *Checklist) Next() {
151	list.selected++
152	if list.selected >= len(list.Items) {
153		list.selected = 0
154	}
155}
156
157// Promote moves the selected item UP in the checklist
158func (list *Checklist) Promote() {
159	if list.IsUnselectable() {
160		return
161	}
162
163	k := list.selected - 1
164	if k < 0 {
165		k = len(list.Items) - 1
166	}
167
168	list.Swap(list.selected, k)
169	list.selected = k
170}
171
172// Demote moves the selected item DOWN in the checklist
173func (list *Checklist) Demote() {
174	if list.IsUnselectable() {
175		return
176	}
177
178	j := list.selected + 1
179	if j >= len(list.Items) {
180		j = 0
181	}
182
183	list.Swap(list.selected, j)
184	list.selected = j
185}
186
187/* -------------------- Sort Interface -------------------- */
188
189func (list *Checklist) Len() int {
190	return len(list.Items)
191}
192
193func (list *Checklist) Less(i, j int) bool {
194	return list.Items[i].Text < list.Items[j].Text
195}
196
197func (list *Checklist) Swap(i, j int) {
198	list.Items[i], list.Items[j] = list.Items[j], list.Items[i]
199}
200