Code

list_window: added top, middle, and bottom commands
[ncmpc.git] / src / list_window.c
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"
27 #include "screen.h"
28 #include "i18n.h"
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
35 extern void screen_bell(void);
37 struct list_window *
38 list_window_init(WINDOW *w, unsigned width, unsigned height)
39 {
40         struct list_window *lw;
42         lw = g_malloc0(sizeof(list_window_t));
43         lw->w = w;
44         lw->cols = width;
45         lw->rows = height;
46         lw->visual_selection = false;
47         return lw;
48 }
50 void
51 list_window_free(struct list_window *lw)
52 {
53         if (lw) {
54                 memset(lw, 0, sizeof(list_window_t));
55                 g_free(lw);
56         }
57 }
59 void
60 list_window_reset(struct list_window *lw)
61 {
62         lw->selected = 0;
63         lw->selected_start = 0;
64         lw->selected_end = 0;
65         lw->visual_selection = false;
66         lw->visual_base = 0;
67         lw->xoffset = 0;
68         lw->start = 0;
69 }
71 void
72 list_window_check_selected(struct list_window *lw, unsigned length)
73 {
74         if (lw->start + lw->rows > length) {
75                 if (length > lw->rows)
76                         lw->start = length - lw->rows;
77                 else
78                         lw->start = 0;
79         }
81         if (lw->selected < lw->start)
82                 lw->selected = lw->start;
84         if (length == 0)
85                 lw->selected = 0;
86         else if (lw->selected >= length)
87                 lw->selected = length - 1;
89         if(lw->visual_selection)
90         {
91                 if(lw->visual_base > lw->selected_end)
92                           lw->selected_end = lw->selected;
93                 if(lw->visual_base < lw->selected_start)
94                           lw->selected_start = lw->selected;
95         }
96         else
97         {
98                 lw->selected_start = lw->selected;
99                 lw->selected_end = lw->selected;
100         }
103 void
104 list_window_center(struct list_window *lw, unsigned rows, unsigned n)
106         if (n > lw->rows / 2)
107                 lw->start = n - lw->rows / 2;
108         else
109                 lw->start = 0;
111         if (lw->start + lw->rows > rows) {
112                 if (lw->rows < rows)
113                         lw->start = rows - lw->rows;
114                 else
115                         lw->start = 0;
116         }
119 void
120 list_window_set_selected(struct list_window *lw, unsigned n)
122         lw->selected = n;
123         if(lw->visual_selection)
124         {
125                 if(n >= lw->visual_base)
126                         lw->selected_end = n;
127                 if(n <= lw->visual_base)
128                         lw->selected_start = n;
129         }
130         else
131         {
132                 lw->selected_start = n;
133                 lw->selected_end = n;
134         }
137 static void
138 list_window_next(struct list_window *lw, unsigned length)
140         if (lw->selected + 1 < length)
141                 list_window_set_selected(lw, lw->selected + 1);
142         else if (options.list_wrap)
143                 list_window_set_selected(lw, 0);
146 static void
147 list_window_previous(struct list_window *lw, unsigned length)
149         if (lw->selected > 0)
150                 list_window_set_selected(lw, lw->selected - 1);
151         else if (options.list_wrap)
152                 list_window_set_selected(lw, length-1);
155 static void
156 list_window_top(struct list_window *lw)
158         list_window_set_selected(lw, lw->start);
161 static void
162 list_window_middle(struct list_window *lw, unsigned length)
164         if (length >= lw->rows)
165                 list_window_set_selected(lw, lw->start + lw->rows / 2);
166         else
167                 list_window_set_selected(lw, length / 2);
170 static void
171 list_window_bottom(struct list_window *lw, unsigned length)
173         if (length >= lw->rows)
174                 list_window_set_selected(lw, lw->start + lw->rows - 1);
175         else
176                 list_window_set_selected(lw, length - 1);
179 static void
180 list_window_first(struct list_window *lw)
182         lw->xoffset = 0;
183         list_window_set_selected(lw, 0);
186 static void
187 list_window_last(struct list_window *lw, unsigned length)
189         lw->xoffset = 0;
190         if (length > 0)
191                 list_window_set_selected(lw, length - 1);
192         else
193                 list_window_set_selected(lw, 0);
196 static void
197 list_window_next_page(struct list_window *lw, unsigned length)
199         if (lw->rows < 2)
200                 return;
201         if (lw->selected + lw->rows < length)
202                 list_window_set_selected(lw, lw->selected + lw->rows - 1);
203         else
204                 list_window_last(lw, length);
207 static void
208 list_window_previous_page(struct list_window *lw)
210         if (lw->rows < 2)
211                 return;
212         if (lw->selected > lw->rows - 1)
213                 list_window_set_selected(lw, lw->selected - lw->rows + 1);
214         else
215                 list_window_first(lw);
219 void
220 list_window_paint(struct list_window *lw,
221                   list_window_callback_fn_t callback,
222                   void *callback_data)
224         unsigned i;
225         bool fill = options.wide_cursor;
226         bool show_cursor = !lw->hide_cursor;
228         if (show_cursor) {
229                 if (lw->selected < lw->start)
230                         lw->start = lw->selected;
232                 if (lw->selected >= lw->start + lw->rows)
233                         lw->start = lw->selected - lw->rows + 1;
234         }
236         for (i = 0; i < lw->rows; i++) {
237                 bool highlight = false;
238                 const char *label;
240                 label = callback(lw->start + i, &highlight, callback_data);
241                 wmove(lw->w, i, 0);
243                 if (label) {
244                         bool selected = (lw->start + i >= lw->selected_start && lw->start + i <= lw->selected_end);
245                         unsigned len = utf8_width(label);
247                         if (highlight)
248                                 colors_use(lw->w, COLOR_LIST_BOLD);
249                         else
250                                 colors_use(lw->w, COLOR_LIST);
252                         if (show_cursor && selected)
253                                 wattron(lw->w, A_REVERSE);
255                         //waddnstr(lw->w, label, lw->cols);
256                         waddstr(lw->w, label);
257                         if (fill && len < lw->cols)
258                                 whline(lw->w,  ' ', lw->cols-len);
260                         if (selected)
261                                 wattroff(lw->w, A_REVERSE);
263                         if (!fill && len < lw->cols)
264                                 wclrtoeol(lw->w);
265                 } else
266                         wclrtoeol(lw->w);
267         }
270 bool
271 list_window_find(struct list_window *lw,
272                  list_window_callback_fn_t callback,
273                  void *callback_data,
274                  const char *str,
275                  bool wrap,
276                  bool bell_on_wrap)
278         bool h;
279         unsigned i = lw->selected + 1;
280         const char *label;
282         do {
283                 while ((label = callback(i,&h,callback_data))) {
284                         if (str && label && match_line(label, str)) {
285                                 lw->selected = i;
286                                 if(!lw->visual_selection || i > lw->selected_end)
287                                           lw->selected_end = i;
288                                 if(!lw->visual_selection || i < lw->selected_start)
289                                           lw->selected_start = i;
290                                 return true;
291                         }
292                         if (wrap && i == lw->selected)
293                                 return false;
294                         i++;
295                 }
296                 if (wrap) {
297                         if (i == 0) /* empty list */
298                                 return 1;
299                         i=0; /* first item */
300                         if (bell_on_wrap) {
301                                 screen_bell();
302                         }
303                 }
304         } while (wrap);
306         return false;
309 bool
310 list_window_rfind(struct list_window *lw,
311                   list_window_callback_fn_t callback,
312                   void *callback_data,
313                   const char *str,
314                   bool wrap,
315                   bool bell_on_wrap,
316                   unsigned rows)
318         bool h;
319         int i = lw->selected - 1;
320         const char *label;
322         if (rows == 0)
323                 return false;
325         do {
326                 while (i >= 0 && (label = callback(i,&h,callback_data))) {
327                         if( str && label && match_line(label, str) ) {
328                                 lw->selected = i;
329                                 if(!lw->visual_selection || i > (int)lw->selected_end)
330                                           lw->selected_end = i;
331                                 if(!lw->visual_selection || i < (int)lw->selected_start)
332                                           lw->selected_start = i;
333                                 return true;
334                         }
335                         if (wrap && i == (int)lw->selected)
336                                 return false;
337                         i--;
338                 }
339                 if (wrap) {
340                         i = rows - 1; /* last item */
341                         if (bell_on_wrap) {
342                                 screen_bell();
343                         }
344                 }
345         } while (wrap);
347         return false;
350 bool
351 list_window_jump(struct list_window *lw,
352                  list_window_callback_fn_t callback,
353                  void *callback_data,
354                  const char *str)
356         bool h;
357         unsigned i = 0;
358         const char *label;
360         while ((label = callback(i,&h,callback_data))) {
361                 if (label && label[0] != '[')
362                 {
363                         if (str && label && g_ascii_strncasecmp(label, str, strlen(str)) == 0) {
364                                 lw->selected = i;
365                                 if(!lw->visual_selection || i > lw->selected_end)
366                                           lw->selected_end = i;
367                                 if(!lw->visual_selection || i < lw->selected_start)
368                                           lw->selected_start = i;
369                                 return true;
370                         }
371                 }
372                 else if (str && label && g_ascii_strncasecmp(label+1, str, strlen(str)) == 0) {
373                                 lw->selected = i;
374                                 if(!lw->visual_selection || i > lw->selected_end)
375                                           lw->selected_end = i;
376                                 if(!lw->visual_selection || i < lw->selected_start)
377                                           lw->selected_start = i;
378                                 return true;
379                         }
380                 i++;
381         }
382         return false;
385 /* perform basic list window commands (movement) */
386 bool
387 list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd)
389         switch (cmd) {
390         case CMD_LIST_PREVIOUS:
391                 list_window_previous(lw, rows);
392                 break;
393         case CMD_LIST_NEXT:
394                 list_window_next(lw, rows);
395                 break;
396         case CMD_LIST_TOP:
397                 list_window_top(lw);
398                 break;
399         case CMD_LIST_MIDDLE:
400                 list_window_middle(lw,rows);
401                 break;
402         case CMD_LIST_BOTTOM:
403                 list_window_bottom(lw,rows);
404                 break;
405         case CMD_LIST_FIRST:
406                 list_window_first(lw);
407                 break;
408         case CMD_LIST_LAST:
409                 list_window_last(lw, rows);
410                 break;
411         case CMD_LIST_NEXT_PAGE:
412                 list_window_next_page(lw, rows);
413                 break;
414         case CMD_LIST_PREVIOUS_PAGE:
415                 list_window_previous_page(lw);
416                 break;
417         case CMD_LIST_VISUAL_SELECT:
418                 if(lw->visual_selection)
419                 {
420                         screen_status_printf(_("Visual selection disabled"));
421                         lw->visual_selection = false;
422                         list_window_set_selected(lw, lw->selected);
423                 }
424                 else
425                 {
426                         screen_status_printf(_("Visual selection enabled"));
427                         lw->visual_base = lw->selected;
428                         lw->visual_selection = true;
429                 }
430                 break;
431         default:
432                 return false;
433         }
435         return true;
438 bool
439 list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd)
441         switch (cmd) {
442         case CMD_LIST_PREVIOUS:
443                 if (lw->start > 0)
444                         lw->start--;
445                 break;
447         case CMD_LIST_NEXT:
448                 if (lw->start + lw->rows < rows)
449                         lw->start++;
450                 break;
452         case CMD_LIST_FIRST:
453                 lw->start = 0;
454                 break;
456         case CMD_LIST_LAST:
457                 if (rows > lw->rows)
458                         lw->start = rows - lw->rows;
459                 else
460                         lw->start = 0;
461                 break;
463         case CMD_LIST_NEXT_PAGE:
464                 lw->start += lw->rows - 1;
465                 if (lw->start + lw->rows > rows) {
466                         if (rows > lw->rows)
467                                 lw->start = rows - lw->rows;
468                         else
469                                 lw->start = 0;
470                 }
471                 break;
473         case CMD_LIST_PREVIOUS_PAGE:
474                 if (lw->start > lw->rows)
475                         lw->start -= lw->rows;
476                 else
477                         lw->start = 0;
478                 break;
480         default:
481                 return false;
482         }
484         return true;
487 #ifdef HAVE_GETMOUSE
488 bool
489 list_window_mouse(struct list_window *lw, unsigned rows,
490                   unsigned long bstate, int y)
492         assert(lw != NULL);
494         /* if the even occured above the list window move up */
495         if (y < 0) {
496                 if (bstate & BUTTON3_CLICKED)
497                         list_window_first(lw);
498                 else
499                         list_window_previous_page(lw);
500                 return true;
501         }
503         /* if the even occured below the list window move down */
504         if ((unsigned)y >= rows) {
505                 if (bstate & BUTTON3_CLICKED)
506                         list_window_last(lw, rows);
507                 else
508                         list_window_next_page(lw, rows);
509                 return true;
510         }
512         return false;
514 #endif