ListCursor: store only height, move width to class ListWindow
[ncmpc-debian.git] / src / ListCursor.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2019 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "ListCursor.hxx"
21 #include "Options.hxx"
22
23 void
24 ListCursor::Reset() noexcept
25 {
26         selected = 0;
27         range_selection = false;
28         range_base = 0;
29         start = 0;
30 }
31
32 unsigned
33 ListCursor::ValidateIndex(unsigned i) const noexcept
34 {
35         if (length == 0)
36                 return 0;
37         else if (i >= length)
38                 return length - 1;
39         else
40                 return i;
41 }
42
43 void
44 ListCursor::CheckSelected() noexcept
45 {
46         selected = ValidateIndex(selected);
47
48         if (range_selection)
49                 range_base = ValidateIndex(range_base);
50 }
51
52 void
53 ListCursor::SetHeight(unsigned _height) noexcept
54 {
55         height = _height;
56         CheckOrigin();
57 }
58
59 void
60 ListCursor::SetLength(unsigned _length) noexcept
61 {
62         if (_length == length)
63                 return;
64
65         length = _length;
66
67         CheckSelected();
68         CheckOrigin();
69 }
70
71 void
72 ListCursor::Center(unsigned n) noexcept
73 {
74         if (n > GetHeight() / 2)
75                 start = n - GetHeight() / 2;
76         else
77                 start = 0;
78
79         if (start + GetHeight() > length) {
80                 if (GetHeight() < length)
81                         start = length - GetHeight();
82                 else
83                         start = 0;
84         }
85 }
86
87 void
88 ListCursor::ScrollTo(unsigned n) noexcept
89 {
90         int new_start = start;
91
92         if ((unsigned) options.scroll_offset * 2 >= GetHeight())
93                 // Center if the offset is more than half the screen
94                 new_start = n - GetHeight() / 2;
95         else {
96                 if (n < start + options.scroll_offset)
97                         new_start = n - options.scroll_offset;
98
99                 if (n >= start + GetHeight() - options.scroll_offset)
100                         new_start = n - GetHeight() + 1 + options.scroll_offset;
101         }
102
103         if (new_start + GetHeight() > length)
104                 new_start = length - GetHeight();
105
106         if (new_start < 0 || length == 0)
107                 new_start = 0;
108
109         start = new_start;
110 }
111
112 void
113 ListCursor::SetCursor(unsigned i) noexcept
114 {
115         range_selection = false;
116         selected = i;
117
118         CheckSelected();
119         CheckOrigin();
120 }
121
122 void
123 ListCursor::MoveCursor(unsigned n) noexcept
124 {
125         selected = n;
126
127         CheckSelected();
128         CheckOrigin();
129 }
130
131 void
132 ListCursor::FetchCursor() noexcept
133 {
134         if (start > 0 &&
135             selected < start + options.scroll_offset)
136                 MoveCursor(start + options.scroll_offset);
137         else if (start + GetHeight() < length &&
138                  selected > start + GetHeight() - 1 - options.scroll_offset)
139                 MoveCursor(start + GetHeight() - 1 - options.scroll_offset);
140 }
141
142 ListWindowRange
143 ListCursor::GetRange() const noexcept
144 {
145         if (length == 0) {
146                 /* empty list - no selection */
147                 return {0, 0};
148         } else if (range_selection) {
149                 /* a range selection */
150                 if (range_base < selected) {
151                         return {range_base, selected + 1};
152                 } else {
153                         return {selected, range_base + 1};
154                 }
155         } else {
156                 /* no range, just the cursor */
157                 return {selected, selected + 1};
158         }
159 }
160
161 void
162 ListCursor::MoveCursorNext() noexcept
163 {
164         if (selected + 1 < length)
165                 MoveCursor(selected + 1);
166         else if (options.list_wrap)
167                 MoveCursor(0);
168 }
169
170 void
171 ListCursor::MoveCursorPrevious() noexcept
172 {
173         if (selected > 0)
174                 MoveCursor(selected - 1);
175         else if (options.list_wrap)
176                 MoveCursor(length - 1);
177 }
178
179 void
180 ListCursor::MoveCursorTop() noexcept
181 {
182         if (start == 0)
183                 MoveCursor(start);
184         else
185                 if ((unsigned) options.scroll_offset * 2 >= GetHeight())
186                         MoveCursor(start + GetHeight() / 2);
187                 else
188                         MoveCursor(start + options.scroll_offset);
189 }
190
191 void
192 ListCursor::MoveCursorMiddle() noexcept
193 {
194         if (length >= GetHeight())
195                 MoveCursor(start + GetHeight() / 2);
196         else
197                 MoveCursor(length / 2);
198 }
199
200 void
201 ListCursor::MoveCursorBottom() noexcept
202 {
203         if (length >= GetHeight())
204                 if ((unsigned) options.scroll_offset * 2 >= GetHeight())
205                         MoveCursor(start + GetHeight() / 2);
206                 else
207                         if (start + GetHeight() == length)
208                                 MoveCursor(length - 1);
209                         else
210                                 MoveCursor(start + GetHeight() - 1 - options.scroll_offset);
211         else
212                 MoveCursor(length - 1);
213 }
214
215 void
216 ListCursor::MoveCursorFirst() noexcept
217 {
218         MoveCursor(0);
219 }
220
221 void
222 ListCursor::MoveCursorLast() noexcept
223 {
224         if (length > 0)
225                 MoveCursor(length - 1);
226         else
227                 MoveCursor(0);
228 }
229
230 void
231 ListCursor::MoveCursorNextPage() noexcept
232 {
233         if (GetHeight() < 2)
234                 return;
235         if (selected + GetHeight() < length)
236                 MoveCursor(selected + GetHeight() - 1);
237         else
238                 MoveCursorLast();
239 }
240
241 void
242 ListCursor::MoveCursorPreviousPage() noexcept
243 {
244         if (GetHeight() < 2)
245                 return;
246         if (selected > GetHeight() - 1)
247                 MoveCursor(selected - GetHeight() + 1);
248         else
249                 MoveCursorFirst();
250 }
251
252 void
253 ListCursor::ScrollUp(unsigned n) noexcept
254 {
255         if (start > 0) {
256                 if (n > start)
257                         start = 0;
258                 else
259                         start -= n;
260
261                 FetchCursor();
262         }
263 }
264
265 void
266 ListCursor::ScrollDown(unsigned n) noexcept
267 {
268         if (start + GetHeight() < length) {
269                 if (start + GetHeight() + n > length - 1)
270                         start = length - GetHeight();
271                 else
272                         start += n;
273
274                 FetchCursor();
275         }
276 }
277
278 void
279 ListCursor::ScrollNextPage() noexcept
280 {
281         start += GetHeight();
282         if (start + GetHeight() > length)
283                 start = length > GetHeight()
284                         ? GetLength() - GetHeight()
285                         : 0;
286 }
287
288 void
289 ListCursor::ScrollPreviousPage() noexcept
290 {
291         start = start > GetHeight()
292                 ? start - GetHeight()
293                 : 0;
294 }
295
296 void
297 ListCursor::ScrollNextHalfPage() noexcept
298 {
299         start += (GetHeight() - 1) / 2;
300         if (start + GetHeight() > length) {
301                 start = length > GetHeight()
302                         ? length - GetHeight()
303                         : 0;
304         }
305 }
306
307 void
308 ListCursor::ScrollPreviousHalfPage() noexcept
309 {
310         start = start > (GetHeight() - 1) / 2
311                 ? start - (GetHeight() - 1) / 2
312                 : 0;
313 }