Code

screen: disable the welcome screen list with a GLib timer
[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_message.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->range_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->range_selection = false;
66         lw->range_base = 0;
67         lw->start = 0;
68 }
70 void
71 list_window_check_selected(struct list_window *lw, unsigned length)
72 {
73         if (lw->start + lw->rows > length) {
74                 if (length > lw->rows)
75                         lw->start = length - lw->rows;
76                 else
77                         lw->start = 0;
78         }
80         if (lw->selected < lw->start)
81                 lw->selected = lw->start;
83         if (length == 0)
84                 lw->selected = 0;
85         else if (lw->selected >= length)
86                 lw->selected = length - 1;
88         if(lw->range_selection)
89         {
90                 if (length == 0) {
91                         lw->selected_start = 0;
92                         lw->selected_end = 0;
93                         lw->range_base = 0;
94                 } else {
95                         if (lw->selected_start >= length)
96                                 lw->selected_start = length - 1;
97                         if (lw->selected_end >= length)
98                                 lw->selected_end = length - 1;
99                         if (lw->range_base >= length)
100                                 lw->range_base = length - 1;
101                 }
103                 if(lw->range_base > lw->selected_end)
104                           lw->selected_end = lw->selected;
105                 if(lw->range_base < lw->selected_start)
106                           lw->selected_start = lw->selected;
107         }
108         else
109         {
110                 lw->selected_start = lw->selected;
111                 lw->selected_end = lw->selected;
112         }
115 void
116 list_window_center(struct list_window *lw, unsigned rows, unsigned n)
118         if (n > lw->rows / 2)
119                 lw->start = n - lw->rows / 2;
120         else
121                 lw->start = 0;
123         if (lw->start + lw->rows > rows) {
124                 if (lw->rows < rows)
125                         lw->start = rows - lw->rows;
126                 else
127                         lw->start = 0;
128         }
131 void
132 list_window_set_selected(struct list_window *lw, unsigned n)
134         lw->selected = n;
135         if(lw->range_selection)
136         {
137                 if(n >= lw->range_base)
138                 {
139                         lw->selected_end = n;
140                         lw->selected_start = lw->range_base;
141                 }
142                 if(n <= lw->range_base)
143                 {
144                         lw->selected_start = n;
145                         lw->selected_end = lw->range_base;
146                 }
147         }
148         else
149         {
150                 lw->selected_start = n;
151                 lw->selected_end = n;
152         }
155 static void
156 list_window_next(struct list_window *lw, unsigned length)
158         if (lw->selected + 1 < length)
159                 list_window_set_selected(lw, lw->selected + 1);
160         else if (options.list_wrap)
161                 list_window_set_selected(lw, 0);
164 static void
165 list_window_previous(struct list_window *lw, unsigned length)
167         if (lw->selected > 0)
168                 list_window_set_selected(lw, lw->selected - 1);
169         else if (options.list_wrap)
170                 list_window_set_selected(lw, length-1);
173 static void
174 list_window_top(struct list_window *lw)
176         if (lw->start == 0)
177                 list_window_set_selected(lw, lw->start);
178         else
179                 if ((unsigned) options.scroll_offset * 2 >= lw->rows)
180                         list_window_set_selected(lw, lw->start + lw->rows / 2);
181                 else
182                         list_window_set_selected(lw, lw->start + options.scroll_offset);
185 static void
186 list_window_middle(struct list_window *lw, unsigned length)
188         if (length >= lw->rows)
189                 list_window_set_selected(lw, lw->start + lw->rows / 2);
190         else
191                 list_window_set_selected(lw, length / 2);
194 static void
195 list_window_bottom(struct list_window *lw, unsigned length)
197         if (length >= lw->rows)
198                 if ((unsigned) options.scroll_offset * 2 >= lw->rows)
199                         list_window_set_selected(lw, lw->start + lw->rows / 2);
200                 else
201                         if (lw->start + lw->rows == length)
202                                 list_window_set_selected(lw, length - 1);
203                         else
204                                 list_window_set_selected(lw, lw->start + lw->rows - 1 - options.scroll_offset);
205         else
206                 list_window_set_selected(lw, length - 1);
209 static void
210 list_window_first(struct list_window *lw)
212         list_window_set_selected(lw, 0);
215 static void
216 list_window_last(struct list_window *lw, unsigned length)
218         if (length > 0)
219                 list_window_set_selected(lw, length - 1);
220         else
221                 list_window_set_selected(lw, 0);
224 static void
225 list_window_next_page(struct list_window *lw, unsigned length)
227         if (lw->rows < 2)
228                 return;
229         if (lw->selected + lw->rows < length)
230                 list_window_set_selected(lw, lw->selected + lw->rows - 1);
231         else
232                 list_window_last(lw, length);
235 static void
236 list_window_previous_page(struct list_window *lw)
238         if (lw->rows < 2)
239                 return;
240         if (lw->selected > lw->rows - 1)
241                 list_window_set_selected(lw, lw->selected - lw->rows + 1);
242         else
243                 list_window_first(lw);
246 static void
247 list_window_scroll_up(struct list_window *lw, unsigned n)
249         if (lw->start > 0) {
250                 if (n > lw->start)
251                         lw->start = 0;
252                 else
253                         lw->start -= n;
254                 if (lw->selected > lw->start + lw->rows - 1 - options.scroll_offset) {
255                         lw->selected = lw->start + lw->rows - 1 - options.scroll_offset;
256                         if (lw->range_selection) {
257                                 if (lw->selected < lw->range_base) {
258                                         lw->selected_start = lw->selected;
259                                         lw->selected_end = lw->range_base;
260                                 } else {
261                                         lw->selected_end = lw->selected;
262                                 }
263                         } else {
264                                 lw->selected_start = lw->selected;
265                                 lw->selected_end = lw->selected;
266                         }
267                 }
268         }
271 static void
272 list_window_scroll_down(struct list_window *lw, unsigned length, unsigned n)
274         if (lw->start + lw->rows < length)
275         {
276                 if ( lw->start + lw->rows + n > length - 1)
277                         lw->start = length - lw->rows;
278                 else
279                         lw->start += n;
280                 if (lw->selected < lw->start + options.scroll_offset) {
281                         lw->selected = lw->start + options.scroll_offset;
282                         if (lw->range_selection) {
283                                 if (lw->selected > lw->range_base) {
284                                         lw->selected_end = lw->selected;
285                                         lw->selected_start = lw->range_base;
286                                 } else {
287                                         lw->selected_start = lw->selected;
288                                 }
289                         } else {
290                                 lw->selected_start = lw->selected;
291                                 lw->selected_end = lw->selected;
292                         }
293                 }
294         }
297 void
298 list_window_paint(struct list_window *lw,
299                   list_window_callback_fn_t callback,
300                   void *callback_data)
302         unsigned i;
303         bool fill = options.wide_cursor;
304         bool show_cursor = !lw->hide_cursor;
305         bool highlight = false;
307         if (show_cursor) {
308                 int start = lw->start;
309                 if ((unsigned) options.scroll_offset * 2 >= lw->rows)
310                         // Center if the offset is more than half the screen
311                         start = lw->selected - lw->rows / 2;
312                 else
313                 {
314                         if (lw->selected < lw->start + options.scroll_offset)
315                                 start = lw->selected - options.scroll_offset;
317                         if (lw->selected >= lw->start + lw->rows - options.scroll_offset)
318                         {
319                                 start = lw->selected - lw->rows + 1 + options.scroll_offset;
320                         }
321                 }
322                 if (start < 0)
323                         lw->start = 0;
324                 else
325                 {
326                         while ( start > 0 && callback(start + lw->rows - 1, &highlight, NULL, callback_data) == NULL)
327                                 start--;
328                         lw->start = start;
329                 }
330         }
332         for (i = 0; i < lw->rows; i++) {
333                 const char *label;
334                 char *second_column = NULL;
335                 highlight = false;
337                 label = callback(lw->start + i, &highlight, &second_column, callback_data);
338                 wmove(lw->w, i, 0);
340                 if (label) {
341                         bool selected = (lw->start + i >= lw->selected_start && lw->start + i <= lw->selected_end);
342                         unsigned len = utf8_width(label);
344                         if (highlight)
345                                 colors_use(lw->w, COLOR_LIST_BOLD);
346                         else
347                                 colors_use(lw->w, COLOR_LIST);
349                         if (show_cursor && selected &&
350                             (!options.hardware_cursor || lw->range_selection))
351                                 wattron(lw->w, A_REVERSE);
353                         //waddnstr(lw->w, label, lw->cols);
354                         waddstr(lw->w, label);
355                         if (fill && len < lw->cols)
356                                 whline(lw->w,  ' ', lw->cols-len);
358                         if(second_column)
359                         {
360                                 unsigned sc_len = utf8_width(second_column) + 1;
361                                 if(lw->cols > sc_len)
362                                 {
363                                         wmove(lw->w, i, lw->cols - sc_len);
364                                         waddstr(lw->w, " ");
365                                         wmove(lw->w, i, lw->cols - sc_len + 1);
366                                         waddstr(lw->w, second_column);
367                                 }
368                                 g_free(second_column);
369                         }
371                         if (selected)
372                                 wattroff(lw->w, A_REVERSE);
374                         if (!fill && len < lw->cols)
375                                 wclrtoeol(lw->w);
376                 } else
377                         wclrtoeol(lw->w);
378         }
380         if (options.hardware_cursor && lw->selected >= lw->start &&
381             lw->selected < lw->start + lw->rows) {
382                 curs_set(1);
383                 wmove(lw->w, lw->selected - lw->start, 0);
384         }
387 bool
388 list_window_find(struct list_window *lw,
389                  list_window_callback_fn_t callback,
390                  void *callback_data,
391                  const char *str,
392                  bool wrap,
393                  bool bell_on_wrap)
395         bool h;
396         unsigned i = lw->selected + 1;
397         const char *label;
399         do {
400                 while ((label = callback(i,&h,NULL,callback_data))) {
401                         if (str && label && match_line(label, str)) {
402                                 lw->selected = i;
403                                 if(!lw->range_selection || i > lw->selected_end)
404                                           lw->selected_end = i;
405                                 if(!lw->range_selection || i < lw->selected_start)
406                                           lw->selected_start = i;
407                                 return true;
408                         }
409                         if (wrap && i == lw->selected)
410                                 return false;
411                         i++;
412                 }
413                 if (wrap) {
414                         if (i == 0) /* empty list */
415                                 return 1;
416                         i=0; /* first item */
417                         if (bell_on_wrap) {
418                                 screen_bell();
419                         }
420                 }
421         } while (wrap);
423         return false;
426 bool
427 list_window_rfind(struct list_window *lw,
428                   list_window_callback_fn_t callback,
429                   void *callback_data,
430                   const char *str,
431                   bool wrap,
432                   bool bell_on_wrap,
433                   unsigned rows)
435         bool h;
436         int i = lw->selected - 1;
437         const char *label;
439         if (rows == 0)
440                 return false;
442         do {
443                 while (i >= 0 && (label = callback(i,&h,NULL,callback_data))) {
444                         if( str && label && match_line(label, str) ) {
445                                 lw->selected = i;
446                                 if(!lw->range_selection || i > (int)lw->selected_end)
447                                           lw->selected_end = i;
448                                 if(!lw->range_selection || i < (int)lw->selected_start)
449                                           lw->selected_start = i;
450                                 return true;
451                         }
452                         if (wrap && i == (int)lw->selected)
453                                 return false;
454                         i--;
455                 }
456                 if (wrap) {
457                         i = rows - 1; /* last item */
458                         if (bell_on_wrap) {
459                                 screen_bell();
460                         }
461                 }
462         } while (wrap);
464         return false;
467 bool
468 list_window_jump(struct list_window *lw,
469                  list_window_callback_fn_t callback,
470                  void *callback_data,
471                  const char *str)
473         bool h;
474         unsigned i = 0;
475         const char *label;
477         while ((label = callback(i,&h,NULL,callback_data))) {
478                 if (label && label[0] == '[')
479                         label++;
480 #ifndef NCMPC_MINI
481                 if (str && label &&
482                                 ((options.jump_prefix_only && g_ascii_strncasecmp(label, str, strlen(str)) == 0) ||
483                                  (!options.jump_prefix_only && match_line(label, str))) )
484 #else
485                 if (str && label && g_ascii_strncasecmp(label, str, strlen(str)) == 0)
486 #endif
487                 {
488                         lw->selected = i;
489                         if(!lw->range_selection || i > lw->selected_end)
490                                 lw->selected_end = i;
491                         if(!lw->range_selection || i < lw->selected_start)
492                                 lw->selected_start = i;
493                         return true;
494                 }
495                 i++;
496         }
497         return false;
500 /* perform basic list window commands (movement) */
501 bool
502 list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd)
504         switch (cmd) {
505         case CMD_LIST_PREVIOUS:
506                 list_window_previous(lw, rows);
507                 break;
508         case CMD_LIST_NEXT:
509                 list_window_next(lw, rows);
510                 break;
511         case CMD_LIST_TOP:
512                 list_window_top(lw);
513                 break;
514         case CMD_LIST_MIDDLE:
515                 list_window_middle(lw,rows);
516                 break;
517         case CMD_LIST_BOTTOM:
518                 list_window_bottom(lw,rows);
519                 break;
520         case CMD_LIST_FIRST:
521                 list_window_first(lw);
522                 break;
523         case CMD_LIST_LAST:
524                 list_window_last(lw, rows);
525                 break;
526         case CMD_LIST_NEXT_PAGE:
527                 list_window_next_page(lw, rows);
528                 break;
529         case CMD_LIST_PREVIOUS_PAGE:
530                 list_window_previous_page(lw);
531                 break;
532         case CMD_LIST_RANGE_SELECT:
533                 if(lw->range_selection)
534                 {
535                         screen_status_printf(_("Range selection disabled"));
536                         lw->range_selection = false;
537                         list_window_set_selected(lw, lw->selected);
538                 }
539                 else
540                 {
541                         screen_status_printf(_("Range selection enabled"));
542                         lw->range_base = lw->selected;
543                         lw->range_selection = true;
544                 }
545                 break;
546         case CMD_LIST_SCROLL_UP_LINE:
547                 list_window_scroll_up(lw, 1);
548                 break;
549         case CMD_LIST_SCROLL_DOWN_LINE:
550                 list_window_scroll_down(lw, rows, 1);
551                 break;
552         case CMD_LIST_SCROLL_UP_HALF:
553                 list_window_scroll_up(lw, (lw->rows - 1) / 2);
554                 break;
555         case CMD_LIST_SCROLL_DOWN_HALF:
556                 list_window_scroll_down(lw, rows, (lw->rows - 1) / 2);
557                 break;
558         default:
559                 return false;
560         }
562         return true;
565 bool
566 list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd)
568         switch (cmd) {
569         case CMD_LIST_SCROLL_UP_LINE:
570         case CMD_LIST_PREVIOUS:
571                 if (lw->start > 0)
572                         lw->start--;
573                 break;
575         case CMD_LIST_SCROLL_DOWN_LINE:
576         case CMD_LIST_NEXT:
577                 if (lw->start + lw->rows < rows)
578                         lw->start++;
579                 break;
581         case CMD_LIST_FIRST:
582                 lw->start = 0;
583                 break;
585         case CMD_LIST_LAST:
586                 if (rows > lw->rows)
587                         lw->start = rows - lw->rows;
588                 else
589                         lw->start = 0;
590                 break;
592         case CMD_LIST_NEXT_PAGE:
593                 lw->start += lw->rows - 1;
594                 if (lw->start + lw->rows > rows) {
595                         if (rows > lw->rows)
596                                 lw->start = rows - lw->rows;
597                         else
598                                 lw->start = 0;
599                 }
600                 break;
602         case CMD_LIST_PREVIOUS_PAGE:
603                 if (lw->start > lw->rows)
604                         lw->start -= lw->rows;
605                 else
606                         lw->start = 0;
607                 break;
609         case CMD_LIST_SCROLL_UP_HALF:
610                 if (lw->start > (lw->rows - 1) / 2)
611                         lw->start -= (lw->rows - 1) / 2;
612                 else
613                         lw->start = 0;
614                 break;
616         case CMD_LIST_SCROLL_DOWN_HALF:
617                 lw->start += (lw->rows - 1) / 2;
618                 if (lw->start + lw->rows > rows) {
619                         if (rows > lw->rows)
620                                 lw->start = rows - lw->rows;
621                         else
622                                 lw->start = 0;
623                 }
624                 break;
626         default:
627                 return false;
628         }
630         return true;
633 #ifdef HAVE_GETMOUSE
634 bool
635 list_window_mouse(struct list_window *lw, unsigned rows,
636                   unsigned long bstate, int y)
638         assert(lw != NULL);
640         /* if the even occurred above the list window move up */
641         if (y < 0) {
642                 if (bstate & BUTTON3_CLICKED)
643                         list_window_first(lw);
644                 else
645                         list_window_previous_page(lw);
646                 return true;
647         }
649         /* if the even occurred below the list window move down */
650         if ((unsigned)y >= rows) {
651                 if (bstate & BUTTON3_CLICKED)
652                         list_window_last(lw, rows);
653                 else
654                         list_window_next_page(lw, rows);
655                 return true;
656         }
658         return false;
660 #endif