8ab35b10670d4d45e4d1c205500a234035cc99e8
1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
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.
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.
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 */
20 #include "list_window.h"
21 #include "config.h"
22 #include "options.h"
23 #include "charset.h"
24 #include "match.h"
25 #include "command.h"
26 #include "colors.h"
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
33 extern void screen_bell(void);
35 struct list_window *
36 list_window_init(WINDOW *w, unsigned width, unsigned height)
37 {
38 struct list_window *lw;
40 lw = g_malloc0(sizeof(list_window_t));
41 lw->w = w;
42 lw->cols = width;
43 lw->rows = height;
44 return lw;
45 }
47 void
48 list_window_free(struct list_window *lw)
49 {
50 if (lw) {
51 memset(lw, 0, sizeof(list_window_t));
52 g_free(lw);
53 }
54 }
56 void
57 list_window_reset(struct list_window *lw)
58 {
59 lw->selected = 0;
60 lw->xoffset = 0;
61 lw->start = 0;
62 }
64 void
65 list_window_check_selected(struct list_window *lw, unsigned length)
66 {
67 if (lw->start + lw->rows > length) {
68 if (length > lw->rows)
69 lw->start = length - lw->rows;
70 else
71 lw->start = 0;
72 }
74 if (lw->selected < lw->start)
75 lw->selected = lw->start;
77 if (length == 0)
78 lw->selected = 0;
79 else if (lw->selected >= length)
80 lw->selected = length - 1;
81 }
83 void
84 list_window_center(struct list_window *lw, unsigned rows, unsigned n)
85 {
86 if (n > lw->rows / 2)
87 lw->start = n - lw->rows / 2;
88 else
89 lw->start = 0;
91 if (lw->start + lw->rows > rows) {
92 if (lw->rows < rows)
93 lw->start = rows - lw->rows;
94 else
95 lw->start = 0;
96 }
97 }
99 void
100 list_window_set_selected(struct list_window *lw, unsigned n)
101 {
102 lw->selected = n;
103 }
105 static void
106 list_window_next(struct list_window *lw, unsigned length)
107 {
108 if (lw->selected + 1 < length)
109 lw->selected++;
110 else if (options.list_wrap)
111 lw->selected = 0;
112 }
114 static void
115 list_window_previous(struct list_window *lw, unsigned length)
116 {
117 if (lw->selected > 0)
118 lw->selected--;
119 else if (options.list_wrap)
120 lw->selected = length - 1;
121 }
123 static void
124 list_window_first(struct list_window *lw)
125 {
126 lw->xoffset = 0;
127 lw->selected = 0;
128 }
130 static void
131 list_window_last(struct list_window *lw, unsigned length)
132 {
133 lw->xoffset = 0;
134 if (length > 0)
135 lw->selected = length - 1;
136 else
137 lw->selected = 0;
138 }
140 static void
141 list_window_next_page(struct list_window *lw, unsigned length)
142 {
143 if (lw->rows < 2)
144 return;
145 if (lw->selected + lw->rows < length)
146 lw->selected += lw->rows - 1;
147 else
148 list_window_last(lw, length);
149 }
151 static void
152 list_window_previous_page(struct list_window *lw)
153 {
154 if (lw->rows < 2)
155 return;
156 if (lw->selected > lw->rows - 1)
157 lw->selected -= lw->rows - 1;
158 else
159 list_window_first(lw);
160 }
163 void
164 list_window_paint(struct list_window *lw,
165 list_window_callback_fn_t callback,
166 void *callback_data)
167 {
168 unsigned i;
169 bool fill = options.wide_cursor;
170 bool show_cursor = !lw->hide_cursor;
172 if (show_cursor) {
173 if (lw->selected < lw->start)
174 lw->start = lw->selected;
176 if (lw->selected >= lw->start + lw->rows)
177 lw->start = lw->selected - lw->rows + 1;
178 }
180 for (i = 0; i < lw->rows; i++) {
181 bool highlight = false;
182 const char *label;
184 label = callback(lw->start + i, &highlight, callback_data);
185 wmove(lw->w, i, 0);
187 if (label) {
188 bool selected = lw->start + i == lw->selected;
189 unsigned len = utf8_width(label);
191 if (highlight)
192 colors_use(lw->w, COLOR_LIST_BOLD);
193 else
194 colors_use(lw->w, COLOR_LIST);
196 if (show_cursor && selected)
197 wattron(lw->w, A_REVERSE);
199 //waddnstr(lw->w, label, lw->cols);
200 waddstr(lw->w, label);
201 if (fill && len < lw->cols)
202 whline(lw->w, ' ', lw->cols-len);
204 if (selected)
205 wattroff(lw->w, A_REVERSE);
207 if (!fill && len < lw->cols)
208 wclrtoeol(lw->w);
209 } else
210 wclrtoeol(lw->w);
211 }
212 }
214 bool
215 list_window_find(struct list_window *lw,
216 list_window_callback_fn_t callback,
217 void *callback_data,
218 const char *str,
219 bool wrap)
220 {
221 bool h;
222 unsigned i = lw->selected + 1;
223 const char *label;
225 do {
226 while ((label = callback(i,&h,callback_data))) {
227 if (str && label && match_line(label, str)) {
228 lw->selected = i;
229 return true;
230 }
231 if (wrap && i == lw->selected)
232 return false;
233 i++;
234 }
235 if (wrap) {
236 if (i == 0) /* empty list */
237 return 1;
238 i=0; /* first item */
239 screen_bell();
240 }
241 } while (wrap);
243 return false;
244 }
246 bool
247 list_window_rfind(struct list_window *lw,
248 list_window_callback_fn_t callback,
249 void *callback_data,
250 const char *str,
251 bool wrap,
252 unsigned rows)
253 {
254 bool h;
255 int i = lw->selected - 1;
256 const char *label;
258 if (rows == 0)
259 return false;
261 do {
262 while (i >= 0 && (label = callback(i,&h,callback_data))) {
263 if( str && label && match_line(label, str) ) {
264 lw->selected = i;
265 return true;
266 }
267 if (wrap && i == (int)lw->selected)
268 return false;
269 i--;
270 }
271 if (wrap) {
272 i = rows - 1; /* last item */
273 screen_bell();
274 }
275 } while (wrap);
277 return false;
278 }
280 /* perform basic list window commands (movement) */
281 bool
282 list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd)
283 {
284 switch (cmd) {
285 case CMD_LIST_PREVIOUS:
286 list_window_previous(lw, rows);
287 break;
288 case CMD_LIST_NEXT:
289 list_window_next(lw, rows);
290 break;
291 case CMD_LIST_FIRST:
292 list_window_first(lw);
293 break;
294 case CMD_LIST_LAST:
295 list_window_last(lw, rows);
296 break;
297 case CMD_LIST_NEXT_PAGE:
298 list_window_next_page(lw, rows);
299 break;
300 case CMD_LIST_PREVIOUS_PAGE:
301 list_window_previous_page(lw);
302 break;
303 default:
304 return false;
305 }
307 return true;
308 }
310 bool
311 list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd)
312 {
313 switch (cmd) {
314 case CMD_LIST_PREVIOUS:
315 if (lw->start > 0)
316 lw->start--;
317 break;
319 case CMD_LIST_NEXT:
320 if (lw->start + lw->rows < rows)
321 lw->start++;
322 break;
324 case CMD_LIST_FIRST:
325 lw->start = 0;
326 break;
328 case CMD_LIST_LAST:
329 if (rows > lw->rows)
330 lw->start = rows - lw->rows;
331 else
332 lw->start = 0;
333 break;
335 case CMD_LIST_NEXT_PAGE:
336 lw->start += lw->rows - 1;
337 if (lw->start + lw->rows > rows) {
338 if (rows > lw->rows)
339 lw->start = rows - lw->rows;
340 else
341 lw->start = 0;
342 }
343 break;
345 case CMD_LIST_PREVIOUS_PAGE:
346 if (lw->start > lw->rows)
347 lw->start -= lw->rows;
348 else
349 lw->start = 0;
350 break;
352 default:
353 return false;
354 }
356 return true;
357 }
359 #ifdef HAVE_GETMOUSE
360 bool
361 list_window_mouse(struct list_window *lw, unsigned rows,
362 unsigned long bstate, int y)
363 {
364 assert(lw != NULL);
366 /* if the even occured above the list window move up */
367 if (y < 0) {
368 if (bstate & BUTTON3_CLICKED)
369 list_window_first(lw);
370 else
371 list_window_previous_page(lw);
372 return true;
373 }
375 /* if the even occured below the list window move down */
376 if ((unsigned)y >= rows) {
377 if (bstate & BUTTON3_CLICKED)
378 list_window_last(lw, rows);
379 else
380 list_window_next_page(lw, rows);
381 return true;
382 }
384 return false;
385 }
386 #endif