1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ListSelectionModel.h"
8
9
10// #pragma mark - ListSelectionModel
11
12
13ListSelectionModel::ListSelectionModel()
14{
15}
16
17
18ListSelectionModel::ListSelectionModel(const ListSelectionModel& other)
19{
20	*this = other;
21}
22
23
24ListSelectionModel::~ListSelectionModel()
25{
26}
27
28
29void
30ListSelectionModel::Clear()
31{
32	int32 selectedCount = fSelectedItems.Count();
33	if (selectedCount > 0) {
34		int32 firstSelected = fSelectedItems[0];
35		int32 lastSelected = fSelectedItems[selectedCount - 1];
36
37		fSelectedItems.Clear();
38
39		_NotifyItemsDeselected(firstSelected, lastSelected - firstSelected + 1);
40	}
41}
42
43
44bool
45ListSelectionModel::SelectItems(int32 itemIndex, int32 count,
46	bool extendSelection)
47{
48	int32 endItemIndex = itemIndex + count;
49
50	int32 index;
51	if (extendSelection) {
52		if (count <= 0)
53			return true;
54
55		index = _FindItem(itemIndex);
56
57		// count already selected items
58		int32 alreadySelectedCount = _CountSelectedItemsInRange(index,
59			endItemIndex);
60		if (alreadySelectedCount == count)
61			return true;
62
63		// make room for the new items
64		if (!fSelectedItems.InsertUninitialized(index + alreadySelectedCount,
65				count - alreadySelectedCount)) {
66			return false;
67		}
68	} else {
69		// TODO: Don't clear -- just resize to the right size!
70		Clear();
71		if (count <= 0)
72			return true;
73
74		index = 0;
75		if (!fSelectedItems.AddUninitialized(count))
76			return false;
77	}
78
79	for (int32 i = 0; i < count; i++)
80		fSelectedItems[index + i] = itemIndex + i;
81
82	_NotifyItemsSelected(itemIndex, count);
83
84	return true;
85}
86
87
88void
89ListSelectionModel::DeselectItems(int32 itemIndex, int32 count)
90{
91	int32 endItemIndex = itemIndex + count;
92	int32 index = _FindItem(itemIndex);
93
94	// count actually selected items
95	int32 actuallySelectedCount = _CountSelectedItemsInRange(index,
96		endItemIndex);
97	if (actuallySelectedCount == 0)
98		return;
99
100	fSelectedItems.Remove(index, actuallySelectedCount);
101
102	_NotifyItemsDeselected(itemIndex, count);
103}
104
105
106void
107ListSelectionModel::ItemsAdded(int32 itemIndex, int32 count)
108{
109	if (count <= 0)
110		return;
111
112	// re-index following items
113	int32 index = _FindItem(itemIndex);
114	int32 selectedCount = fSelectedItems.Count();
115	for (int32 i = index; i < selectedCount; i++)
116		fSelectedItems[i] += count;
117}
118
119
120void
121ListSelectionModel::ItemsRemoved(int32 itemIndex, int32 count)
122{
123	if (count <= 0)
124		return;
125
126	int32 index = _FindItem(itemIndex);
127
128	// count selected items in the range
129	int32 actuallySelectedCount = _CountSelectedItemsInRange(index,
130		itemIndex + count);
131	if (actuallySelectedCount > 0)
132		fSelectedItems.Remove(index, actuallySelectedCount);
133
134	// re-index following items
135	int32 selectedCount = fSelectedItems.Count();
136	for (int32 i = index; i < selectedCount; i++)
137		fSelectedItems[i] -= count;
138}
139
140
141bool
142ListSelectionModel::AddListener(Listener* listener)
143{
144	return fListeners.AddItem(listener);
145}
146
147
148void
149ListSelectionModel::RemoveListener(Listener* listener)
150{
151	fListeners.RemoveItem(listener);
152}
153
154
155ListSelectionModel&
156ListSelectionModel::operator=(const ListSelectionModel& other)
157{
158	Clear();
159
160	fSelectedItems = other.fSelectedItems;
161
162	int32 selectedCount = CountSelectedItems();
163	if (selectedCount > 0) {
164		int32 firstSelected = fSelectedItems[0];
165		int32 lastSelected = fSelectedItems[selectedCount - 1];
166		_NotifyItemsDeselected(firstSelected, lastSelected - firstSelected + 1);
167	}
168
169	return *this;
170}
171
172
173int32
174ListSelectionModel::_FindItem(int32 itemIndex) const
175{
176	// binary search the index of the first item >= itemIndex
177	int32 lower = 0;
178	int32 upper = fSelectedItems.Count();
179
180	while (lower < upper) {
181		int32 mid = (lower + upper) / 2;
182
183		if (fSelectedItems[mid] < itemIndex)
184			lower = mid + 1;
185		else
186			upper = mid;
187	}
188
189	return lower;
190}
191
192
193int32
194ListSelectionModel::_CountSelectedItemsInRange(int32 index,
195	int32 endItemIndex) const
196{
197	int32 count = 0;
198	int32 selectedCount = fSelectedItems.Count();
199	for (int32 i = index; i < selectedCount; i++) {
200		if (SelectedItemAt(i) >= endItemIndex)
201			break;
202		count++;
203	}
204
205	return count;
206}
207
208
209void
210ListSelectionModel::_NotifyItemsSelected(int32 index, int32 count)
211{
212	int32 listenerCount = fListeners.CountItems();
213	for (int32 i = listenerCount - 1; i >= 0; i--)
214		fListeners.ItemAt(i)->ItemsSelected(this, index, count);
215}
216
217
218void
219ListSelectionModel::_NotifyItemsDeselected(int32 index, int32 count)
220{
221	int32 listenerCount = fListeners.CountItems();
222	for (int32 i = listenerCount - 1; i >= 0; i--)
223		fListeners.ItemAt(i)->ItemsDeselected(this, index, count);
224}
225
226
227// #pragma mark - Listener
228
229
230ListSelectionModel::Listener::~Listener()
231{
232}
233
234
235void
236ListSelectionModel::Listener::ItemsSelected(ListSelectionModel* model,
237	int32 index, int32 count)
238{
239}
240
241
242void
243ListSelectionModel::Listener::ItemsDeselected(ListSelectionModel* model,
244	int32 index, int32 count)
245{
246}
247