1 /*
2 * MOC - music on console
3 * Copyright (C) 2004 - 2006 Damian Pietras <daper@daper.net>
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 * Other authors:
11 * - Kamil Tarkowski <kamilt@interia.pl> - sec_to_min_plist()
12 */
13
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 /* _XOPEN_SOURCE is known to break compilation under OpenBSD. */
19 #ifndef OPENBSD
20 # define _XOPEN_SOURCE 500 /* for wcswidth() */
21 #endif
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <strings.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <wctype.h>
32 #include <wchar.h>
33
34 /* This breaks compilation on FreeBSD 5.4, so use it only on
35 * Linux and OpenBSD. */
36 #if defined(LINUX) || defined(OPENBSD)
37 # define _XOPEN_SOURCE_EXTENDED /* for wget_wch() */
38 #endif
39
40 #ifdef HAVE_NCURSESW_H
41 # include <ncursesw/curses.h>
42 #elif HAVE_NCURSES_H
43 # include <ncurses.h>
44 #elif HAVE_CURSES_H
45 # include <curses.h>
46 #endif
47
48 #include "common.h"
49 #include "menu.h"
50 #include "themes.h"
51 #include "lists.h"
52 #include "options.h"
53 #include "interface_elements.h"
54 #include "log.h"
55 #include "files.h"
56 #include "decoder.h"
57 #include "keys.h"
58 #include "playlist.h"
59 #include "protocol.h"
60 #include "interface.h"
61 #include "utf8.h"
62 #include "rcc.h"
63 #include "lyrics.h"
64
65 #ifndef PACKAGE_REVISION
66 #define STARTUP_MESSAGE "Welcome to " PACKAGE_NAME \
67 " (version " PACKAGE_VERSION ")!"
68 #else
69 #define STARTUP_MESSAGE "Welcome to " PACKAGE_NAME \
70 " (version " PACKAGE_VERSION \
71 ", revision " PACKAGE_REVISION ")!"
72 #endif
73 #define HISTORY_SIZE 50
74
75
76 /* TODO: removing/adding a char to the entry may increase width of the text
77 * by more than one column. */
78
79 /* Type of the side menu. */
80 enum side_menu_type
81 {
82 MENU_DIR, /* list of files in a directory */
83 MENU_PLAYLIST, /* a playlist of files */
84 MENU_THEMES, /* list of available themes */
85 MENU_TREE /* tree of directories */
86 };
87
88 struct side_menu
89 {
90 enum side_menu_type type;
91 int visible; /* is it visible (are the other fields initialized) ? */
92 WINDOW *win; /* window for the menu */
93 char *title; /* title of the window */
94
95 /* Position and size of the menu in the window. */
96 int posx;
97 int posy;
98 int width;
99 int height;
100
101 int total_time; /* total time of the files on the playlist */
102 int total_time_for_all; /* is the total file counted for all files? */
103
104 union
105 {
106 struct {
107 struct menu *main; /* visible menu */
108 struct menu *copy; /* copy of the menu when we display
109 matching items while searching */
110 } list;
111 /* struct menu_tree *tree;*/
112 } menu;
113 };
114
115 /* State of the side menu that can be read/restored. It remembers the state
116 * (position of the view, which file is selected, etc) of the menu. */
117 struct side_menu_state
118 {
119 struct menu_state menu_state;
120 };
121
122 /* When used instead of the size parameter it means: fill to the end of the
123 * window. */
124 #define LAYOUT_SIZE_FILL (-1)
125
126 struct window_params
127 {
128 int x, y;
129 int width, height;
130 };
131
132 struct main_win_layout
133 {
134 struct window_params menus[3];
135 };
136
137 static struct main_win
138 {
139 WINDOW *win;
140 char *curr_file; /* currently played file. */
141
142 int in_help; /* are we displaying help screen? */
143 int too_small; /* is the terminal window too small to display mocp? */
144 int help_screen_top; /* first visible line of the help screen. */
145 int in_lyrics; /* are we displaying lyrics screen? */
146 int lyrics_screen_top; /* first visible line of the lyrics screen. */
147
148 struct side_menu menus[3];
149 lists_t_strs *layout_fmt;
150 int selected_menu; /* which menu is currently selected by the user */
151 } main_win;
152
153 /* Bar for displaying mixer state or progress. */
154 struct bar
155 {
156 int width; /* width in chars */
157 float filled; /* how much is it filled in percent */
158 char *orig_title; /* optional title */
159 char title[512]; /* title with the percent value */
160 int show_val; /* show the title and the value? */
161 int show_pct; /* show percentage in the title value? */
162 int fill_color; /* color (ncurses attributes) of the filled part */
163 int empty_color; /* color of the empty part */
164 };
165
166 /* History for entries' values. */
167 struct entry_history
168 {
169 char *items[HISTORY_SIZE];
170 int num; /* number of items */
171 };
172
173 /* An input area where a user can type text to enter a file name etc. */
174 struct entry
175 {
176 enum entry_type type;
177 int width; /* width of the entry part for typing */
178
179 /* The text the user types: */
180 wchar_t text_ucs[512]; /* unicode */
181 wchar_t saved_ucs[512]; /* unicode saved during history scrolling */
182
183 char *title; /* displayed title */
184 char *file; /* optional: file associated with the entry */
185 int cur_pos; /* cursor position */
186 int display_from; /* displaying from this char */
187 struct entry_history *history; /* history to use with this entry or
188 NULL is history is not used */
189 int history_pos; /* current position in the history */
190 };
191
192 /* Type of message. */
193 enum message_type
194 {
195 NORMAL_MSG,
196 ERROR_MSG,
197 QUERY_MSG
198 };
199
200 /* Save a new message for display. */
201 struct queued_message
202 {
203 struct queued_message *next;
204 /* What type is this message? */
205 enum message_type type;
206 /* Message to be displayed instead of the file's title. */
207 char *msg;
208 /* Prompt to use for user query menu. */
209 char *prompt;
210 /* How many seconds does the message linger? */
211 time_t timeout;
212 /* The callback function and opaque data for user replies. */
213 t_user_reply_callback *callback;
214 void *data;
215 };
216
217 static struct info_win
218 {
219 WINDOW *win;
220
221 struct queued_message *current_message; /* Message currently being displayed */
222
223 struct queued_message *queued_message_head; /* FIFO queue on which pending */
224 struct queued_message *queued_message_tail; /* messages get saved */
225 int queued_message_total; /* Total messages on queue */
226 int queued_message_errors; /* Error messages on queue */
227
228 int too_small; /* is the current window too small to display this widget? */
229
230 struct entry entry;
231 int in_entry; /* are we using the entry (is the above
232 structure initialized)? */
233 struct entry_history urls_history;
234 struct entry_history dirs_history;
235 struct entry_history user_history;
236
237 /* true/false options values */
238 int state_stereo;
239 int state_shuffle;
240 int state_repeat;
241 int state_next;
242 int state_net;
243
244 int bitrate; /* in kbps */
245 int rate; /* in kHz */
246
247 int files_in_queue;
248
249 /* time in seconds */
250 int curr_time;
251 int total_time;
252 int block_start;
253 int block_end;
254
255 int plist_time; /* total time of files displayed in the menu */
256 int plist_time_for_all; /* is the above time for all files? */
257
258 char *title; /* title of the played song. */
259 char status_msg[26]; /* status message */
260 int state_play; /* STATE_(PLAY | STOP | PAUSE) */
261
262 /* Saved user reply callback data. */
263 t_user_reply_callback *callback;
264 void *data;
265
266 struct bar mixer_bar;
267 struct bar time_bar;
268 } info_win;
269
270 /* Are we running on xterm? */
271 static bool has_xterm = false;
272
273 /* Are we running inside screen? */
274 static bool has_screen = false;
275
276 /* Was the interface initialized? */
277 static int iface_initialized = 0;
278
279 /* Was initscr() called? */
280 static int screen_initialized = 0;
281
282 /* Chars used to make lines (for borders etc.). */
283 static struct
284 {
285 chtype vert; /* vertical */
286 chtype horiz; /* horizontal */
287 chtype ulcorn; /* upper left corner */
288 chtype urcorn; /* upper right corner */
289 chtype llcorn; /* lower left corner */
290 chtype lrcorn; /* lower right corner */
291 chtype rtee; /* right tee */
292 chtype ltee; /* left tee */
293 } lines;
294
entry_history_init(struct entry_history * h)295 static void entry_history_init (struct entry_history *h)
296 {
297 assert (h != NULL);
298
299 h->num = 0;
300 }
301
entry_history_add(struct entry_history * h,const char * text)302 static void entry_history_add (struct entry_history *h, const char *text)
303 {
304 assert (h != NULL);
305 assert (text != NULL);
306
307 if (strlen (text) != strspn (text, " ")) {
308 if (h->num == 0 || strcmp (text, h->items[h->num - 1])) {
309 if (h->num < HISTORY_SIZE)
310 h->items[h->num++] = xstrdup (text);
311 else {
312 free (h->items[0]);
313 memmove (h->items, h->items + 1,
314 (HISTORY_SIZE - 1) * sizeof (char *));
315 h->items[h->num - 1] = xstrdup (text);
316 }
317 }
318 }
319 }
320
entry_history_replace(struct entry_history * h,int num,const char * text)321 static void entry_history_replace (struct entry_history *h, int num, const char *text)
322 {
323 assert (h != NULL);
324 assert (LIMIT(num, h->num));
325 assert (text != NULL);
326
327 if (strlen (text) != strspn (text, " ") &&
328 strcmp (h->items[num], text)) {
329 free (h->items[num]);
330 h->items[num] = xstrdup (text);
331 }
332 }
333
entry_history_clear(struct entry_history * h)334 static void entry_history_clear (struct entry_history *h)
335 {
336 int i;
337
338 assert (h != NULL);
339
340 for (i = 0; i < h->num; i++)
341 free (h->items[i]);
342
343 h->num = 0;
344 }
345
entry_history_nitems(const struct entry_history * h)346 static int entry_history_nitems (const struct entry_history *h)
347 {
348 assert (h != NULL);
349
350 return h->num;
351 }
352
entry_history_get(const struct entry_history * h,const int num)353 static char *entry_history_get (const struct entry_history *h, const int num)
354 {
355 assert (h != NULL);
356 assert (LIMIT(num, h->num));
357
358 return xstrdup (h->items[num]);
359 }
360
361 /* Draw the entry. Use this function at the end of screen drawing
362 * because it sets the cursor position in the right place. */
entry_draw(const struct entry * e,WINDOW * w,const int posx,const int posy)363 static void entry_draw (const struct entry *e, WINDOW *w, const int posx,
364 const int posy)
365 {
366 char *text;
367 wchar_t *text_ucs;
368 int len;
369
370 assert (e != NULL);
371 assert (w != NULL);
372 assert (posx >= 0);
373 assert (posy >= 0);
374
375 wmove (w, posy, posx);
376 wattrset (w, get_color(CLR_ENTRY_TITLE));
377 xwprintw (w, "%s", e->title);
378
379 wattrset (w, get_color(CLR_ENTRY));
380 len = wcslen(e->text_ucs) - e->display_from;
381
382 text_ucs = (wchar_t *)xmalloc(sizeof(wchar_t) * (len + 1));
383 memcpy (text_ucs, e->text_ucs + e->display_from,
384 sizeof(wchar_t) * (len + 1));
385 if (len > e->width)
386 text_ucs[e->width] = L'\0';
387 len = wcstombs (NULL, text_ucs, -1) + 1;
388 assert (len >= 1);
389
390 text = (char *)xmalloc (len);
391 wcstombs (text, text_ucs, len);
392
393 xwprintw (w, " %-*s", e->width, text);
394
395 /* Move the cursor */
396 wmove (w, posy, e->cur_pos - e->display_from + strwidth(e->title)
397 + posx + 1);
398
399 free (text);
400 free (text_ucs);
401 }
402
entry_init(struct entry * e,const enum entry_type type,const int width,struct entry_history * history,const char * prompt)403 static void entry_init (struct entry *e, const enum entry_type type,
404 const int width, struct entry_history *history, const char *prompt)
405 {
406 const char *title;
407
408 assert (e != NULL);
409
410 switch (type) {
411 case ENTRY_SEARCH:
412 title = "SEARCH";
413 break;
414 case ENTRY_PLIST_SAVE:
415 title = "SAVE PLAYLIST";
416 break;
417 case ENTRY_GO_DIR:
418 title = "GO";
419 break;
420 case ENTRY_GO_URL:
421 title = "URL";
422 break;
423 case ENTRY_ADD_URL:
424 title = "ADD URL";
425 break;
426 case ENTRY_PLIST_OVERWRITE:
427 title = "File exists, overwrite?";
428 break;
429 case ENTRY_USER_QUERY:
430 title = prompt;
431 break;
432 default:
433 abort ();
434 }
435
436 e->type = type;
437 e->text_ucs[0] = L'\0';
438 e->saved_ucs[0] = L'\0';
439 e->file = NULL;
440 e->title = xmalloc (strlen (title) + 2);
441 strcpy (e->title, title);
442 if (e->title[strlen (e->title) - 1] != ':' &&
443 e->title[strlen (e->title) - 1] != '?')
444 strcat (e->title, ":");
445 e->width = width - strwidth(title);
446 e->cur_pos = 0;
447 e->display_from = 0;
448 e->history = history;
449
450 if (history)
451 e->history_pos = history->num;
452 }
453
entry_get_type(const struct entry * e)454 static enum entry_type entry_get_type (const struct entry *e)
455 {
456 assert (e != NULL);
457
458 return e->type;
459 }
460
461 /* Set the entry text as UCS. Move the cursor to the end. */
entry_set_text_ucs(struct entry * e,const wchar_t * text)462 static void entry_set_text_ucs (struct entry *e, const wchar_t *text)
463 {
464 int width, len;
465
466 assert (e != NULL);
467
468 len = MIN (wcslen (text) + 1, ARRAY_SIZE (e->text_ucs));
469 wmemcpy (e->text_ucs, text, len);
470 e->text_ucs[ARRAY_SIZE (e->text_ucs) - 1] = L'\0';
471
472 width = wcswidth (e->text_ucs, WIDTH_MAX);
473 e->cur_pos = wcslen (e->text_ucs);
474
475 e->display_from = 0;
476 if (e->cur_pos > e->width)
477 e->display_from = width - e->width;
478 }
479
480 /* Set the entry text. */
entry_set_text(struct entry * e,const char * text)481 static void entry_set_text (struct entry *e, const char *text)
482 {
483 wchar_t text_ucs[ARRAY_SIZE (e->text_ucs)];
484
485 assert (e != NULL);
486
487 mbstowcs (text_ucs, text, ARRAY_SIZE (e->text_ucs));
488 e->text_ucs[ARRAY_SIZE (e->text_ucs) - 1] = L'\0';
489
490 entry_set_text_ucs (e, text_ucs);
491 }
492
493 /* Add a char to the entry where the cursor is placed. */
entry_add_char(struct entry * e,const wchar_t c)494 static void entry_add_char (struct entry *e, const wchar_t c)
495 {
496 size_t len;
497
498 assert (e != NULL);
499
500 len = wcslen (e->text_ucs);
501 if (len >= ARRAY_SIZE(e->text_ucs) - sizeof(wchar_t))
502 return;
503
504 memmove (e->text_ucs + e->cur_pos + 1,
505 e->text_ucs + e->cur_pos,
506 (len - e->cur_pos + 1) * sizeof(e->text_ucs[0]));
507 e->text_ucs[e->cur_pos] = c;
508 e->cur_pos++;
509
510 if (e->cur_pos - e->display_from > e->width)
511 e->display_from++;
512 }
513
514 /* Delete 'count' chars before the cursor. */
entry_del_chars(struct entry * e,int count)515 static void entry_del_chars (struct entry *e, int count)
516 {
517 assert (e != NULL);
518 assert (e->cur_pos > 0);
519
520 int width = wcslen (e->text_ucs);
521 if (e->cur_pos < count)
522 count = e->cur_pos;
523
524 memmove (e->text_ucs + e->cur_pos - count,
525 e->text_ucs + e->cur_pos,
526 (width - e->cur_pos) * sizeof (e->text_ucs[0]));
527 width -= count;
528 e->text_ucs[width] = L'\0';
529 e->cur_pos -= count;
530
531 if (e->cur_pos < e->display_from)
532 e->display_from = e->cur_pos;
533
534 /* Can we show more after deleting the chars? */
535 if (e->display_from > 0 && width - e->display_from < e->width)
536 e->display_from = width - e->width;
537 if (e->display_from < 0)
538 e->display_from = 0;
539 }
540
541 /* Delete the char before the cursor. */
entry_back_space(struct entry * e)542 static void entry_back_space (struct entry *e)
543 {
544 assert (e != NULL);
545
546 if (e->cur_pos > 0)
547 entry_del_chars (e, 1);
548 }
549
550 /* Delete the char under the cursor. */
entry_del_char(struct entry * e)551 static void entry_del_char (struct entry *e)
552 {
553 int len;
554
555 assert (e != NULL);
556
557 len = wcslen (e->text_ucs);
558 if (e->cur_pos < len) {
559 e->cur_pos += 1;
560 entry_del_chars (e, 1);
561 }
562 }
563
564 /* Delete the chars from cursor to start of line. */
entry_del_to_start(struct entry * e)565 static void entry_del_to_start (struct entry *e)
566 {
567 assert (e != NULL);
568
569 if (e->cur_pos > 0)
570 entry_del_chars (e, e->cur_pos);
571 }
572
573 /* Delete the chars from cursor to end of line. */
entry_del_to_end(struct entry * e)574 static void entry_del_to_end (struct entry *e)
575 {
576 int len;
577
578 assert (e != NULL);
579
580 len = wcslen (e->text_ucs);
581 if (e->cur_pos < len) {
582 int count;
583
584 count = len - e->cur_pos;
585 e->cur_pos = len;
586 entry_del_chars (e, count);
587 }
588 }
589
590 /* Move the cursor one char left. */
entry_curs_left(struct entry * e)591 static void entry_curs_left (struct entry *e)
592 {
593 assert (e != NULL);
594
595 if (e->cur_pos > 0) {
596 e->cur_pos--;
597
598 if (e->cur_pos < e->display_from)
599 e->display_from--;
600 }
601 }
602
603 /* Move the cursor one char right. */
entry_curs_right(struct entry * e)604 static void entry_curs_right (struct entry *e)
605 {
606 int width;
607
608 assert (e != NULL);
609
610 width = wcslen (e->text_ucs);
611
612 if (e->cur_pos < width) {
613 e->cur_pos++;
614
615 if (e->cur_pos > e->width + e->display_from)
616 e->display_from++;
617 }
618 }
619
620 /* Move the cursor to the end of the entry text. */
entry_end(struct entry * e)621 static void entry_end (struct entry *e)
622 {
623 int width;
624
625 assert (e != NULL);
626
627 width = wcslen (e->text_ucs);
628
629 e->cur_pos = width;
630
631 if (width > e->width)
632 e->display_from = width - e->width;
633 else
634 e->display_from = 0;
635 }
636
637 /* Move the cursor to the beginning of the entry field. */
entry_home(struct entry * e)638 static void entry_home (struct entry *e)
639 {
640 assert (e != NULL);
641
642 e->display_from = 0;
643 e->cur_pos = 0;
644 }
645
entry_resize(struct entry * e,const int width)646 static void entry_resize (struct entry *e, const int width)
647 {
648 assert (e != NULL);
649 assert (width > 0);
650
651 e->width = width - strlen (e->title);
652 entry_end (e);
653 }
654
entry_get_text(const struct entry * e)655 static char *entry_get_text (const struct entry *e)
656 {
657 char *text;
658 int len;
659
660 assert (e != NULL);
661
662 len = wcstombs (NULL, e->text_ucs, -1) + 1;
663 assert (len >= 1);
664 text = (char *) xmalloc (sizeof (char) * len);
665 wcstombs (text, e->text_ucs, len);
666
667 return text;
668 }
669
670 /* Copy the previous history item to the entry if available, move the entry
671 * history position down. */
entry_set_history_up(struct entry * e)672 static void entry_set_history_up (struct entry *e)
673 {
674 assert (e != NULL);
675 assert (e->history != NULL);
676
677 if (e->history_pos > 0) {
678 char *t;
679
680 if (e->history_pos == entry_history_nitems (e->history))
681 wmemcpy (e->saved_ucs, e->text_ucs, wcslen (e->text_ucs) + 1);
682 else {
683 t = entry_get_text (e);
684 entry_history_replace (e->history, e->history_pos, t);
685 free (t);
686 }
687 e->history_pos--;
688
689 t = entry_history_get (e->history, e->history_pos);
690 entry_set_text (e, t);
691 free (t);
692 }
693 }
694
695 /* Copy the next history item to the entry if available, move the entry history
696 * position down. */
entry_set_history_down(struct entry * e)697 static void entry_set_history_down (struct entry *e)
698 {
699 assert (e != NULL);
700 assert (e->history != NULL);
701
702 if (e->history_pos < entry_history_nitems (e->history)) {
703 char *t;
704
705 t = entry_get_text (e);
706 entry_history_replace (e->history, e->history_pos, t);
707 free (t);
708
709 e->history_pos++;
710 if (e->history_pos == entry_history_nitems (e->history))
711 entry_set_text_ucs (e, e->saved_ucs);
712 else {
713 t = entry_history_get (e->history, e->history_pos);
714 entry_set_text (e, t);
715 free (t);
716 }
717 }
718 }
719
entry_set_file(struct entry * e,const char * file)720 static void entry_set_file (struct entry *e, const char *file)
721 {
722 assert (e != NULL);
723 assert (file != NULL);
724
725 if (e->file)
726 free (e->file);
727 e->file = xstrdup (file);
728 }
729
entry_get_file(const struct entry * e)730 static char *entry_get_file (const struct entry *e)
731 {
732 return xstrdup (e->file);
733 }
734
entry_destroy(struct entry * e)735 static void entry_destroy (struct entry *e)
736 {
737 assert (e != NULL);
738
739 if (e->file)
740 free (e->file);
741 if (e->title)
742 free (e->title);
743 }
744
entry_add_text_to_history(struct entry * e)745 static void entry_add_text_to_history (struct entry *e)
746 {
747 char *text;
748
749 assert (e != NULL);
750 assert (e->history);
751
752 text = entry_get_text (e);
753 entry_history_add (e->history, text);
754 free (text);
755 }
756
757 /* Return the list menu height inside the side menu. */
side_menu_get_menu_height(const struct side_menu * m)758 static int side_menu_get_menu_height (const struct side_menu *m)
759 {
760 if (m->posy + m->height == LINES - 4)
761 return m->height - 1;
762 return m->height - 2;
763 }
764
side_menu_init_menu(struct side_menu * m)765 static void side_menu_init_menu (struct side_menu *m)
766 {
767 assert (m != NULL);
768
769 m->menu.list.main = menu_new (m->win, m->posx + 1, m->posy + 1,
770 m->width - 2, side_menu_get_menu_height (m));
771 }
772
side_menu_init(struct side_menu * m,const enum side_menu_type type,WINDOW * parent_win,const struct window_params * wp)773 static void side_menu_init (struct side_menu *m, const enum side_menu_type type,
774 WINDOW *parent_win, const struct window_params *wp)
775 {
776 assert (m != NULL);
777 assert (parent_win != NULL);
778 assert (wp != NULL);
779 assert (wp->width >= 8);
780 assert (wp->height >= 3);
781
782 m->type = type;
783 m->win = parent_win;
784 m->posx = wp->x;
785 m->posy = wp->y;
786 m->height = wp->height;
787 m->width = wp->width;
788
789 m->title = NULL;
790
791 m->total_time = 0;
792 m->total_time_for_all = 0;
793
794 if (type == MENU_DIR || type == MENU_PLAYLIST) {
795 side_menu_init_menu (m);
796 m->menu.list.copy = NULL;
797
798 menu_set_items_numbering (m->menu.list.main,
799 type == MENU_PLAYLIST
800 && options_get_int("PlaylistNumbering"));
801 menu_set_show_format (m->menu.list.main,
802 options_get_int("ShowFormat"));
803 menu_set_show_time (m->menu.list.main,
804 strcasecmp(options_get_str("ShowTime"), "no"));
805 menu_set_info_attr_normal (m->menu.list.main,
806 get_color(CLR_MENU_ITEM_INFO));
807 menu_set_info_attr_sel (m->menu.list.main,
808 get_color(CLR_MENU_ITEM_INFO_SELECTED));
809 menu_set_info_attr_marked (m->menu.list.main,
810 get_color(CLR_MENU_ITEM_INFO_MARKED));
811 menu_set_info_attr_sel_marked (m->menu.list.main,
812 get_color(CLR_MENU_ITEM_INFO_MARKED_SELECTED));
813 }
814 else if (type == MENU_THEMES) {
815 side_menu_init_menu (m);
816 m->menu.list.copy = NULL;
817 }
818 else
819 abort ();
820
821 m->visible = 1;
822 }
823
side_menu_destroy(struct side_menu * m)824 static void side_menu_destroy (struct side_menu *m)
825 {
826 assert (m != NULL);
827
828 if (m->visible) {
829 if (m->type == MENU_DIR || m->type == MENU_PLAYLIST
830 || m->type == MENU_THEMES) {
831 menu_free (m->menu.list.main);
832 if (m->menu.list.copy)
833 menu_free (m->menu.list.copy);
834 }
835 else
836 abort ();
837
838 if (m->title)
839 free (m->title);
840 m->visible = 0;
841 }
842 }
843
side_menu_set_title(struct side_menu * m,const char * title)844 static void side_menu_set_title (struct side_menu *m, const char *title)
845 {
846 assert (m != NULL);
847 assert (title != NULL);
848
849 if (m->title)
850 free (m->title);
851 m->title = xstrdup (title);
852 }
853
854 /* Similar function is only available in C99, so do it here. */
xround(const float f)855 static int xround (const float f)
856 {
857 return f - (int)f > 0.5 ? (int)(f + 1.0) : (int)f;
858 }
859
860 /* Parse one layout coordinate from "0,2,54%,1" and put it in val.
861 * Max is the maximum value of the field. It's also used when processing
862 * percent values.
863 * Return false on error. */
parse_layout_coordinate(const char * fmt,int * val,const int max)864 static bool parse_layout_coordinate (const char *fmt, int *val, const int max)
865 {
866 long v;
867 const char *e = fmt;
868
869 if (!strcasecmp (fmt, "FILL")) {
870 *val = LAYOUT_SIZE_FILL;
871 return true;
872 }
873
874 v = strtol (fmt, (char **)&e, 10);
875 if (e == fmt)
876 return false;
877
878 if (*e == '%')
879 *val = xround (max * v / 100.0);
880 else
881 *val = v;
882
883 if (!RANGE(0, *val, max)) {
884 logit ("Coordinate out of range - %d is not in (0, %d)", *val, max);
885 return false;
886 }
887
888 return true;
889 }
890
891 /* Parse the layout string. Return false on error. */
parse_layout(struct main_win_layout * l,lists_t_strs * fmt)892 static bool parse_layout (struct main_win_layout *l, lists_t_strs *fmt)
893 {
894 int ix;
895 bool result;
896 lists_t_strs *format;
897
898 assert (l != NULL);
899 assert (fmt != NULL);
900
901 /* default values */
902 l->menus[0].x = 0;
903 l->menus[0].y = 0;
904 l->menus[0].width = COLS;
905 l->menus[0].height = LINES - 4;
906 l->menus[1] = l->menus[0];
907 l->menus[2] = l->menus[0];
908
909 result = false;
910 format = lists_strs_new (6);
911 for (ix = 0; ix < lists_strs_size (fmt); ix += 1) {
912 const char *menu, *name;
913 struct window_params p;
914
915 lists_strs_clear (format);
916 menu = lists_strs_at (fmt, ix);
917 if (lists_strs_split (format, menu, "(,)") != 5)
918 goto err;
919
920 name = lists_strs_at (format, 0);
921
922 if (!parse_layout_coordinate (lists_strs_at (format, 1), &p.x, COLS)) {
923 logit ("Coordinate parse error when parsing X");
924 goto err;
925 }
926 if (!parse_layout_coordinate (lists_strs_at (format, 2), &p.y, LINES - 4)) {
927 logit ("Coordinate parse error when parsing Y");
928 goto err;
929 }
930 if (!parse_layout_coordinate (lists_strs_at (format, 3), &p.width, COLS)) {
931 logit ("Coordinate parse error when parsing width");
932 goto err;
933 }
934 if (!parse_layout_coordinate (lists_strs_at (format, 4), &p.height, LINES - 4)) {
935 logit ("Coordinate parse error when parsing height");
936 goto err;
937 }
938
939 if (p.width == LAYOUT_SIZE_FILL)
940 p.width = COLS - p.x;
941 if (p.height == LAYOUT_SIZE_FILL)
942 p.height = LINES - 4 - p.y;
943
944 if (p.width < 15) {
945 logit ("Width is less than 15");
946 goto err;
947 }
948 if (p.height < 2) {
949 logit ("Height is less than 2");
950 goto err;
951 }
952 if (p.x + p.width > COLS) {
953 logit ("X + width is more than COLS (%d)", COLS);
954 goto err;
955 }
956 if (p.y + p.height > LINES - 4) {
957 logit ("Y + height is more than LINES - 4 (%d)", LINES - 4);
958 goto err;
959 }
960
961 if (!strcmp(name, "directory"))
962 l->menus[MENU_DIR] = p;
963 else if (!strcmp(name, "playlist"))
964 l->menus[MENU_PLAYLIST] = p;
965 else {
966 logit ("Bad subwindow name '%s'", name);
967 goto err;
968 }
969 }
970
971 result = true;
972
973 err:
974 lists_strs_free (format);
975 return result;
976 }
977
main_win_init(struct main_win * w,lists_t_strs * layout_fmt)978 static void main_win_init (struct main_win *w, lists_t_strs *layout_fmt)
979 {
980 struct main_win_layout l;
981 int res;
982
983 assert (w != NULL);
984
985 w->win = newwin (LINES - 4, COLS, 0, 0);
986 wbkgd (w->win, get_color(CLR_BACKGROUND));
987 nodelay (w->win, TRUE);
988 keypad (w->win, TRUE);
989
990 w->curr_file = NULL;
991 w->in_help = 0;
992 w->in_lyrics = 0;
993 w->too_small = 0;
994 w->help_screen_top = 0;
995 w->lyrics_screen_top = 0;
996 w->layout_fmt = layout_fmt;
997
998 res = parse_layout (&l, layout_fmt);
999 assert (res);
1000
1001 side_menu_init (&w->menus[0], MENU_DIR, w->win, &l.menus[0]);
1002 side_menu_init (&w->menus[1], MENU_PLAYLIST, w->win, &l.menus[1]);
1003 side_menu_set_title (&w->menus[1], "Playlist");
1004 w->menus[2].visible = 0;
1005
1006 w->selected_menu = 0;
1007 }
1008
main_win_destroy(struct main_win * w)1009 static void main_win_destroy (struct main_win *w)
1010 {
1011 assert (w != NULL);
1012
1013 side_menu_destroy (&w->menus[0]);
1014 side_menu_destroy (&w->menus[1]);
1015 side_menu_destroy (&w->menus[2]);
1016
1017 if (w->win)
1018 delwin (w->win);
1019 if (w->curr_file)
1020 free (w->curr_file);
1021 }
1022
1023 /* Make a title suitable to display in a menu from the title of a playlist item.
1024 * Returned memory is malloc()ed.
1025 * made_from tags - was the playlist title made from tags?
1026 * full_paths - If the title is the file name, use the full path?
1027 */
make_menu_title(const char * plist_title,const int made_from_tags,const int full_path)1028 static char *make_menu_title (const char *plist_title,
1029 const int made_from_tags, const int full_path)
1030 {
1031 char *title = xstrdup (plist_title);
1032
1033 if (!made_from_tags) {
1034 if (!full_path && !is_url (title)) {
1035
1036 /* Use only the file name instead of the full path. */
1037 char *slash = strrchr (title, '/');
1038
1039 if (slash && slash != title) {
1040 char *old_title = title;
1041
1042 title = xstrdup (slash + 1);
1043 free (old_title);
1044 }
1045 }
1046 }
1047
1048 return title;
1049 }
1050
1051 /* Add an item from the playlist to the menu.
1052 * If full_paths has non-zero value, full paths will be displayed instead of
1053 * just file names.
1054 * Return a non-zero value if the added item is visible on the screen. */
add_to_menu(struct menu * menu,const struct plist * plist,const int num,const int full_paths)1055 static int add_to_menu (struct menu *menu, const struct plist *plist,
1056 const int num, const int full_paths)
1057 {
1058 bool made_from_tags;
1059 struct menu_item *added;
1060 const struct plist_item *item = &plist->items[num];
1061 char *title;
1062 const char *type_name;
1063
1064 made_from_tags = (options_get_bool ("ReadTags") && item->title_tags);
1065
1066 if (made_from_tags)
1067 title = make_menu_title (item->title_tags, 1, 0);
1068 else
1069 title = make_menu_title (item->title_file, 0, full_paths);
1070 added = menu_add (menu, title, plist_file_type (plist, num), item->file);
1071 free (title);
1072
1073 if (item->tags && item->tags->time != -1) {
1074 char time_str[6];
1075
1076 sec_to_min (time_str, item->tags->time);
1077 menu_item_set_time (added, time_str);
1078 }
1079
1080 menu_item_set_attr_normal (added, get_color(CLR_MENU_ITEM_FILE));
1081 menu_item_set_attr_sel (added, get_color(CLR_MENU_ITEM_FILE_SELECTED));
1082 menu_item_set_attr_marked (added, get_color(CLR_MENU_ITEM_FILE_MARKED));
1083 menu_item_set_attr_sel_marked (added,
1084 get_color(CLR_MENU_ITEM_FILE_MARKED_SELECTED));
1085
1086 if (!(type_name = file_type_name(item->file)))
1087 type_name = "";
1088 menu_item_set_format (added, type_name);
1089 menu_item_set_queue_pos (added, item->queue_pos);
1090
1091 if (full_paths && !made_from_tags)
1092 menu_item_set_align (added, MENU_ALIGN_RIGHT);
1093
1094 return menu_is_visible (menu, added);
1095 }
1096
side_menu_clear(struct side_menu * m)1097 static void side_menu_clear (struct side_menu *m)
1098 {
1099 assert (m != NULL);
1100 assert (m->visible);
1101 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1102 assert (m->menu.list.main != NULL);
1103 assert (m->menu.list.copy == NULL);
1104
1105 menu_free (m->menu.list.main);
1106 side_menu_init_menu (m);
1107 menu_set_items_numbering (m->menu.list.main, m->type == MENU_PLAYLIST
1108 && options_get_int("PlaylistNumbering"));
1109
1110 menu_set_show_format (m->menu.list.main, options_get_int("ShowFormat"));
1111 menu_set_show_time (m->menu.list.main,
1112 strcasecmp(options_get_str("ShowTime"), "no"));
1113 menu_set_info_attr_normal (m->menu.list.main, get_color(CLR_MENU_ITEM_INFO));
1114 menu_set_info_attr_sel (m->menu.list.main, get_color(CLR_MENU_ITEM_INFO_SELECTED));
1115 menu_set_info_attr_marked (m->menu.list.main, get_color(CLR_MENU_ITEM_INFO_MARKED));
1116 menu_set_info_attr_sel_marked (m->menu.list.main, get_color(CLR_MENU_ITEM_INFO_MARKED_SELECTED));
1117 }
1118
1119 /* Fill the directory or playlist side menu with this content. */
side_menu_make_list_content(struct side_menu * m,const struct plist * files,const lists_t_strs * dirs,const lists_t_strs * playlists,const int add_up_dir)1120 static void side_menu_make_list_content (struct side_menu *m,
1121 const struct plist *files, const lists_t_strs *dirs,
1122 const lists_t_strs *playlists, const int add_up_dir)
1123 {
1124 struct menu_item *added;
1125 int i;
1126
1127 assert (m != NULL);
1128 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1129 assert (m->menu.list.main != NULL);
1130 assert (m->menu.list.copy == NULL);
1131
1132 side_menu_clear (m);
1133
1134 if (add_up_dir) {
1135 added = menu_add (m->menu.list.main, "../", F_DIR, "..");
1136 menu_item_set_attr_normal (added, get_color(CLR_MENU_ITEM_DIR));
1137 menu_item_set_attr_sel (added,
1138 get_color(CLR_MENU_ITEM_DIR_SELECTED));
1139 }
1140
1141 if (dirs)
1142 for (i = 0; i < lists_strs_size (dirs) ; i++) {
1143 char title[PATH_MAX];
1144
1145 #ifdef HAVE_RCC
1146 char *t_str = NULL;
1147 if (options_get_int("UseRCCForFilesystem")) {
1148 strcpy (title, strrchr (lists_strs_at (dirs, i), '/') + 1);
1149 strcat (title, "/");
1150 t_str = xstrdup (title);
1151 t_str = rcc_reencode (t_str);
1152 snprintf(title, PATH_MAX, "%s", t_str);
1153 free(t_str);
1154 }
1155 else
1156 #endif
1157 if (options_get_int ("FileNamesIconv"))
1158 {
1159 char *conv_title = files_iconv_str (
1160 strrchr (lists_strs_at (dirs, i), '/') + 1);
1161
1162 strcpy (title, conv_title);
1163 strcat (title, "/");
1164
1165 free (conv_title);
1166 }
1167 else
1168 {
1169 strcpy (title, strrchr (lists_strs_at (dirs, i), '/') + 1);
1170 strcat (title, "/");
1171 }
1172
1173 added = menu_add (m->menu.list.main, title, F_DIR,
1174 lists_strs_at (dirs, i));
1175 menu_item_set_attr_normal (added,
1176 get_color(CLR_MENU_ITEM_DIR));
1177 menu_item_set_attr_sel (added,
1178 get_color(CLR_MENU_ITEM_DIR_SELECTED));
1179 }
1180
1181 if (playlists)
1182 for (i = 0; i < lists_strs_size (playlists); i++){
1183 added = menu_add (m->menu.list.main,
1184 strrchr (lists_strs_at (playlists, i), '/') + 1,
1185 F_PLAYLIST, lists_strs_at (playlists, i));
1186 menu_item_set_attr_normal (added,
1187 get_color(CLR_MENU_ITEM_PLAYLIST));
1188 menu_item_set_attr_sel (added,
1189 get_color(
1190 CLR_MENU_ITEM_PLAYLIST_SELECTED));
1191 }
1192
1193 /* playlist items */
1194 for (i = 0; i < files->num; i++) {
1195 if (!plist_deleted(files, i))
1196 add_to_menu (m->menu.list.main, files, i,
1197 m->type == MENU_PLAYLIST
1198 && options_get_int("PlaylistFullPaths"));
1199 }
1200
1201 m->total_time = plist_total_time (files, &m->total_time_for_all);
1202 }
1203
clear_area(WINDOW * w,const int posx,const int posy,const int width,const int height)1204 static void clear_area (WINDOW *w, const int posx, const int posy,
1205 const int width, const int height)
1206 {
1207 int y;
1208 char line[512];
1209
1210 assert (width < (int)sizeof(line));
1211
1212 memset (line, ' ', width);
1213 line[width] = 0;
1214
1215 wattrset (w, get_color(CLR_BACKGROUND));
1216 for (y = posy; y < posy + height; y++) {
1217 wmove (w, y, posx);
1218 xwaddstr (w, line);
1219 }
1220 }
1221
side_menu_draw_frame(const struct side_menu * m)1222 static void side_menu_draw_frame (const struct side_menu *m)
1223 {
1224 char *title;
1225
1226 assert (m != NULL);
1227 assert (m->visible);
1228
1229 if (m->title) {
1230 if ((int)strwidth(m->title) > m->width - 4) {
1231 char *tail;
1232
1233 tail = xstrtail (m->title, m->width - 7);
1234 title = (char *)xmalloc (strlen(tail) + 4);
1235 sprintf (title, "...%s", tail);
1236 free (tail);
1237 }
1238 else
1239 title = xstrdup (m->title);
1240 }
1241 else
1242 title = NULL;
1243
1244 /* Border */
1245 wattrset (m->win, get_color(CLR_FRAME));
1246
1247 /* upper left corner */
1248 wmove (m->win, m->posy, m->posx);
1249 waddch (m->win, lines.ulcorn);
1250
1251 /* upper line */
1252 whline (m->win, lines.horiz, m->width - 2);
1253
1254 /* upper right corner */
1255 wmove (m->win, m->posy, m->posx + m->width - 1);
1256 waddch (m->win, lines.urcorn);
1257
1258 /* left line */
1259 wmove (m->win, m->posy + 1, m->posx);
1260 wvline (m->win, lines.vert, m->height - 1);
1261
1262 /* right line */
1263 wmove (m->win, m->posy + 1, m->posx + m->width - 1);
1264 wvline (m->win, lines.vert, m->height - 1);
1265
1266 if (m->posy + m->height < LINES - 4) {
1267
1268 /* bottom left corner */
1269 wmove (m->win, m->posy + m->height - 1, m->posx);
1270 waddch (m->win, lines.llcorn);
1271
1272 /* bottom line */
1273 whline (m->win, lines.horiz, m->width - 2);
1274
1275 /* bottom right corner */
1276 wmove (m->win, m->posy + m->height - 1, m->posx + m->width - 1);
1277 waddch (m->win, lines.lrcorn);
1278 }
1279
1280 /* The title */
1281 if (title) {
1282 wmove (m->win, m->posy, m->posx + m->width / 2
1283 - strwidth(title) / 2 - 1);
1284
1285 wattrset (m->win, get_color(CLR_FRAME));
1286 waddch (m->win, lines.rtee);
1287
1288 wattrset (m->win, get_color(CLR_WIN_TITLE));
1289 xwaddstr (m->win, title);
1290
1291 wattrset (m->win, get_color(CLR_FRAME));
1292 waddch (m->win, lines.ltee);
1293
1294 free (title);
1295 }
1296 }
1297
side_menu_draw(const struct side_menu * m,const int active)1298 static void side_menu_draw (const struct side_menu *m, const int active)
1299 {
1300 assert (m != NULL);
1301 assert (m->visible);
1302
1303 clear_area (m->win, m->posx, m->posy, m->width, m->height);
1304 side_menu_draw_frame (m);
1305
1306 if (m->type == MENU_DIR || m->type == MENU_PLAYLIST
1307 || m->type == MENU_THEMES) {
1308 menu_draw (m->menu.list.main, active);
1309 if (options_get_int("UseCursorSelection"))
1310 menu_set_cursor (m->menu.list.main);
1311 }
1312 else
1313 abort ();
1314 }
1315
side_menu_cmd(struct side_menu * m,const enum key_cmd cmd)1316 static void side_menu_cmd (struct side_menu *m, const enum key_cmd cmd)
1317 {
1318 assert (m != NULL);
1319 assert (m->visible);
1320
1321 if (m->type == MENU_DIR || m->type == MENU_PLAYLIST
1322 || m->type == MENU_THEMES) {
1323 switch (cmd) {
1324 case KEY_CMD_MENU_DOWN:
1325 menu_driver (m->menu.list.main, REQ_DOWN);
1326 break;
1327 case KEY_CMD_MENU_UP:
1328 menu_driver (m->menu.list.main, REQ_UP);
1329 break;
1330 case KEY_CMD_MENU_NPAGE:
1331 menu_driver (m->menu.list.main, REQ_PGDOWN);
1332 break;
1333 case KEY_CMD_MENU_PPAGE:
1334 menu_driver (m->menu.list.main, REQ_PGUP);
1335 break;
1336 case KEY_CMD_MENU_FIRST:
1337 menu_driver (m->menu.list.main, REQ_TOP);
1338 break;
1339 case KEY_CMD_MENU_LAST:
1340 menu_driver (m->menu.list.main, REQ_BOTTOM);
1341 break;
1342 default:
1343 abort ();
1344 }
1345 }
1346 else
1347 abort ();
1348 }
1349
side_menu_curritem_get_type(const struct side_menu * m)1350 static enum file_type side_menu_curritem_get_type (const struct side_menu *m)
1351 {
1352 struct menu_item *mi;
1353
1354 assert (m != NULL);
1355 assert (m->visible);
1356 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST
1357 || m->type == MENU_THEMES);
1358
1359 mi = menu_curritem (m->menu.list.main);
1360
1361 if (mi)
1362 return menu_item_get_type (mi);
1363
1364 return F_OTHER;
1365 }
1366
side_menu_get_curr_file(const struct side_menu * m)1367 static char *side_menu_get_curr_file (const struct side_menu *m)
1368 {
1369 struct menu_item *mi;
1370
1371 assert (m != NULL);
1372 assert (m->visible);
1373 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST
1374 || m->type == MENU_THEMES);
1375
1376 mi = menu_curritem (m->menu.list.main);
1377
1378 if (mi)
1379 return menu_item_get_file (mi);
1380
1381 return NULL;
1382 }
1383
find_side_menu(struct main_win * w,const enum side_menu_type type)1384 static struct side_menu *find_side_menu (struct main_win *w,
1385 const enum side_menu_type type)
1386 {
1387 size_t ix;
1388
1389 assert (w != NULL);
1390
1391 for (ix = 0; ix < ARRAY_SIZE(w->menus); ix += 1) {
1392 struct side_menu *m = &w->menus[ix];
1393
1394 if (m->visible && m->type == type)
1395 return m;
1396 }
1397
1398 abort (); /* menu not found - BUG */
1399 }
1400
side_menu_set_curr_item_title(struct side_menu * m,const char * title)1401 static void side_menu_set_curr_item_title (struct side_menu *m,
1402 const char *title)
1403 {
1404 assert (m != NULL);
1405 assert (m->visible);
1406 assert (title != NULL);
1407
1408 menu_setcurritem_title (m->menu.list.main, title);
1409 }
1410
1411 /* Update menu item using the playlist item. */
update_menu_item(struct menu_item * mi,const struct plist * plist,const int n,const int full_path)1412 static void update_menu_item (struct menu_item *mi,
1413 const struct plist *plist,
1414 const int n, const int full_path)
1415 {
1416 bool made_from_tags;
1417 char *title;
1418 const struct plist_item *item;
1419
1420 assert (mi != NULL);
1421 assert (plist != NULL);
1422 assert (n >= 0);
1423
1424 item = &plist->items[n];
1425
1426 if (item->tags && item->tags->time != -1) {
1427 char time_str[6];
1428
1429 sec_to_min (time_str, item->tags->time);
1430 menu_item_set_time (mi, time_str);
1431 }
1432 else
1433 menu_item_set_time (mi, "");
1434
1435 made_from_tags = (options_get_bool ("ReadTags") && item->title_tags);
1436
1437 if (made_from_tags)
1438 title = make_menu_title (item->title_tags, 1, 0);
1439 else
1440 title = make_menu_title (item->title_file, 0, full_path);
1441
1442 menu_item_set_title (mi, title);
1443
1444 if (full_path && !made_from_tags)
1445 menu_item_set_align (mi, MENU_ALIGN_RIGHT);
1446 else
1447 menu_item_set_align (mi, MENU_ALIGN_LEFT);
1448
1449 menu_item_set_queue_pos (mi, item->queue_pos);
1450
1451 free (title);
1452
1453 }
1454
1455 /* Update item title and time for this item if it's present on this menu.
1456 * Return a non-zero value if the item is visible. */
side_menu_update_item(struct side_menu * m,const struct plist * plist,const int n)1457 static int side_menu_update_item (struct side_menu *m,
1458 const struct plist *plist, const int n)
1459 {
1460 struct menu_item *mi;
1461 int visible = 0;
1462 char *file;
1463
1464 assert (m != NULL);
1465 assert (m->visible);
1466 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1467 assert (plist != NULL);
1468 assert (LIMIT(n, plist->num));
1469
1470 file = plist_get_file (plist, n);
1471 assert (file != NULL);
1472
1473 if ((mi = menu_find(m->menu.list.main, file))) {
1474 update_menu_item (mi, plist, n, m->type == MENU_PLAYLIST
1475 && options_get_int("PlaylistFullpaths"));
1476 visible = menu_is_visible (m->menu.list.main, mi);
1477 }
1478 if (m->menu.list.copy
1479 && (mi = menu_find(m->menu.list.copy, file))) {
1480 update_menu_item (mi, plist, n, m->type == MENU_PLAYLIST
1481 && options_get_int("PlaylistFullpaths"));
1482 visible = visible || menu_is_visible (m->menu.list.main, mi);
1483 }
1484
1485 free (file);
1486
1487 m->total_time = plist_total_time (plist, &m->total_time_for_all);
1488
1489 return visible;
1490 }
1491
side_menu_unmark_file(struct side_menu * m)1492 static void side_menu_unmark_file (struct side_menu *m)
1493 {
1494 assert (m != NULL);
1495 assert (m->visible);
1496 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1497
1498 menu_unmark_item (m->menu.list.main);
1499 if (m->menu.list.copy)
1500 menu_unmark_item (m->menu.list.copy);
1501 }
1502
side_menu_mark_file(struct side_menu * m,const char * file)1503 static void side_menu_mark_file (struct side_menu *m, const char *file)
1504 {
1505 assert (m != NULL);
1506 assert (m->visible);
1507 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1508
1509 menu_mark_item (m->menu.list.main, file);
1510 if (m->menu.list.copy)
1511 menu_mark_item (m->menu.list.copy, file);
1512 }
1513
side_menu_add_file(struct side_menu * m,const char * file,const char * title,const enum file_type type)1514 static void side_menu_add_file (struct side_menu *m, const char *file,
1515 const char *title, const enum file_type type)
1516 {
1517 struct menu_item *added;
1518
1519 added = menu_add (m->menu.list.main, title, type, file);
1520
1521 menu_item_set_attr_normal (added, get_color(CLR_MENU_ITEM_FILE));
1522 menu_item_set_attr_sel (added, get_color(CLR_MENU_ITEM_FILE_SELECTED));
1523 menu_item_set_attr_marked (added, get_color(CLR_MENU_ITEM_FILE_MARKED));
1524 menu_item_set_attr_sel_marked (added,
1525 get_color(CLR_MENU_ITEM_FILE_MARKED_SELECTED));
1526 }
1527
side_menu_add_plist_item(struct side_menu * m,const struct plist * plist,const int num)1528 static int side_menu_add_plist_item (struct side_menu *m,
1529 const struct plist *plist, const int num)
1530 {
1531 int visible;
1532
1533 assert (m != NULL);
1534 assert (plist != NULL);
1535 assert (m->visible);
1536 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1537
1538 visible = add_to_menu (m->menu.list.copy ? m->menu.list.copy
1539 : m->menu.list.main,
1540 plist, num,
1541 m->type == MENU_PLAYLIST
1542 && options_get_int("PlaylistFullPaths"));
1543 m->total_time = plist_total_time (plist, &m->total_time_for_all);
1544
1545 return visible;
1546 }
1547
side_menu_is_time_for_all(const struct side_menu * m)1548 static int side_menu_is_time_for_all (const struct side_menu *m)
1549 {
1550 assert (m != NULL);
1551 assert (m->visible);
1552
1553 return m->total_time_for_all;
1554 }
1555
side_menu_get_files_time(const struct side_menu * m)1556 static int side_menu_get_files_time (const struct side_menu *m)
1557 {
1558 assert (m != NULL);
1559 assert (m->visible);
1560
1561 return m->total_time;
1562 }
1563
side_menu_update_show_time(struct side_menu * m)1564 static void side_menu_update_show_time (struct side_menu *m)
1565 {
1566 assert (m != NULL);
1567 assert (m->visible);
1568 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1569
1570 menu_set_show_time (m->menu.list.main,
1571 strcasecmp(options_get_str("ShowTime"), "no"));
1572 }
1573
side_menu_update_show_format(struct side_menu * m)1574 static void side_menu_update_show_format (struct side_menu *m)
1575 {
1576 assert (m != NULL);
1577 assert (m->visible);
1578 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1579
1580 menu_set_show_format (m->menu.list.main, options_get_int("ShowFormat"));
1581 }
1582
side_menu_get_state(const struct side_menu * m,struct side_menu_state * st)1583 static void side_menu_get_state (const struct side_menu *m,
1584 struct side_menu_state *st)
1585 {
1586 assert (m != NULL);
1587 assert (st != NULL);
1588 assert (m->visible);
1589 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1590
1591 menu_get_state (m->menu.list.main, &st->menu_state);
1592 }
1593
side_menu_set_state(struct side_menu * m,const struct side_menu_state * st)1594 static void side_menu_set_state (struct side_menu *m,
1595 const struct side_menu_state *st)
1596 {
1597 assert (m != NULL);
1598 assert (st != NULL);
1599 assert (m->visible);
1600 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1601
1602 menu_set_state (m->menu.list.main, &st->menu_state);
1603 }
1604
side_menu_del_item(struct side_menu * m,const char * file)1605 static void side_menu_del_item (struct side_menu *m, const char *file)
1606 {
1607 assert (m != NULL);
1608 assert (m->visible);
1609 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1610
1611 menu_del_item (m->menu.list.copy ? m->menu.list.copy : m->menu.list.main,
1612 file);
1613 }
1614
side_menu_set_plist_time(struct side_menu * m,const int time,const int time_for_all)1615 static void side_menu_set_plist_time (struct side_menu *m, const int time,
1616 const int time_for_all)
1617 {
1618 assert (m != NULL);
1619 assert (time >= 0);
1620 assert (m->type == MENU_DIR || m->type == MENU_PLAYLIST);
1621
1622 m->total_time = time;
1623 m->total_time_for_all = time_for_all;
1624 }
1625
1626 /* Replace the menu with one having only those items which contain 'pattern'.
1627 * If no items match, don't do anything.
1628 * Return the number of matching items. */
side_menu_filter(struct side_menu * m,const char * pattern)1629 static int side_menu_filter (struct side_menu *m, const char *pattern)
1630 {
1631 struct menu *filtered_menu;
1632
1633 assert (m != NULL);
1634 assert (pattern != NULL);
1635 assert (m->menu.list.main != NULL);
1636
1637 filtered_menu = menu_filter_pattern (m->menu.list.copy
1638 ? m->menu.list.copy : m->menu.list.main, pattern);
1639
1640 if (menu_nitems(filtered_menu) == 0) {
1641 menu_free (filtered_menu);
1642 return 0;
1643 }
1644
1645 if (m->menu.list.copy)
1646 menu_free (m->menu.list.main);
1647 else
1648 m->menu.list.copy = m->menu.list.main;
1649
1650 m->menu.list.main = filtered_menu;
1651
1652 return menu_nitems (filtered_menu);
1653 }
1654
side_menu_use_main(struct side_menu * m)1655 static void side_menu_use_main (struct side_menu *m)
1656 {
1657 assert (m != NULL);
1658 assert (m->menu.list.main != NULL);
1659
1660 if (m->menu.list.copy) {
1661 menu_free (m->menu.list.main);
1662 m->menu.list.main = m->menu.list.copy;
1663 m->menu.list.copy = NULL;
1664 }
1665 }
1666
side_menu_make_visible(struct side_menu * m,const char * file)1667 static void side_menu_make_visible (struct side_menu *m, const char *file)
1668 {
1669 assert (m != NULL);
1670 assert (m->visible);
1671 assert (m->type == MENU_PLAYLIST || m->type == MENU_DIR);
1672 assert (file != NULL);
1673
1674 if (!m->menu.list.copy)
1675 menu_make_visible (m->menu.list.main, file);
1676 }
1677
side_menu_swap_items(struct side_menu * m,const char * file1,const char * file2)1678 static void side_menu_swap_items (struct side_menu *m, const char *file1,
1679 const char *file2)
1680 {
1681 assert (m != NULL);
1682 assert (m->visible);
1683 assert (m->type == MENU_PLAYLIST || m->type == MENU_DIR);
1684 assert (file1 != NULL);
1685 assert (file2 != NULL);
1686 assert (m->menu.list.main != NULL);
1687 assert (m->menu.list.copy == NULL);
1688
1689 menu_swap_items (m->menu.list.main, file1, file2);
1690 }
1691
side_menu_select_file(struct side_menu * m,const char * file)1692 static void side_menu_select_file (struct side_menu *m, const char *file)
1693 {
1694 assert (m != NULL);
1695 assert (file != NULL);
1696
1697 if (m->type == MENU_DIR || m->type == MENU_PLAYLIST)
1698 menu_setcurritem_file (m->menu.list.main, file);
1699 else
1700 abort ();
1701 }
1702
side_menu_resize(struct side_menu * m,const struct window_params * wp)1703 static void side_menu_resize (struct side_menu *m,
1704 const struct window_params *wp)
1705 {
1706 assert (m != NULL);
1707
1708 m->posx = wp->x;
1709 m->posy = wp->y;
1710 m->height = wp->height;
1711 m->width = wp->width;
1712
1713 if (m->type == MENU_DIR || m->type == MENU_PLAYLIST
1714 || m->type == MENU_THEMES) {
1715 menu_update_size (m->menu.list.main, m->posx + 1, m->posy + 1,
1716 m->width - 2, side_menu_get_menu_height(m));
1717 if (m->menu.list.copy)
1718 menu_update_size (m->menu.list.copy, m->posx + 1,
1719 m->posy + 1, m->width - 2,
1720 side_menu_get_menu_height(m));
1721 }
1722 else
1723 abort ();
1724 }
1725
main_win_draw_too_small_screen(const struct main_win * w)1726 static void main_win_draw_too_small_screen (const struct main_win *w)
1727 {
1728 assert (w != NULL);
1729 assert (w->too_small);
1730
1731 werase (w->win);
1732 wbkgd (w->win, get_color(CLR_BACKGROUND));
1733
1734 wmove (w->win, 0, 0);
1735 wattrset (w->win, get_color(CLR_MESSAGE));
1736 xmvwaddstr (w->win, LINES/2,
1737 COLS/2 - (sizeof("...TERMINAL IS TOO SMALL...")-1)/2,
1738 "...TERMINAL TOO SMALL...");
1739 }
1740
main_win_draw_help_screen(const struct main_win * w)1741 static void main_win_draw_help_screen (const struct main_win *w)
1742 {
1743 int i;
1744 int max_lines;
1745 int help_lines;
1746 char **help;
1747
1748 assert (w != NULL);
1749 assert (w->in_help);
1750
1751 max_lines = w->help_screen_top + LINES - 6;
1752
1753 help = get_keys_help (&help_lines);
1754
1755 werase (w->win);
1756 wbkgd (w->win, get_color(CLR_BACKGROUND));
1757
1758 wmove (w->win, 0, 0);
1759 if (w->help_screen_top != 0) {
1760 wattrset (w->win, get_color(CLR_MESSAGE));
1761 xmvwaddstr (w->win, 0, COLS/2 - (sizeof("...MORE...")-1)/2,
1762 "...MORE...");
1763 }
1764 wmove (w->win, 1, 0);
1765 wattrset (w->win, get_color(CLR_LEGEND));
1766 for (i = w->help_screen_top; i < max_lines && i < help_lines; i++) {
1767 xwaddstr (w->win, help[i]);
1768 waddch (w->win, '\n');
1769 }
1770 if (i != help_lines) {
1771 wattrset (w->win, get_color(CLR_MESSAGE));
1772 xmvwaddstr (w->win, LINES-5,
1773 COLS/2 - (sizeof("...MORE...")-1)/2,
1774 "...MORE...");
1775 }
1776 }
1777
main_win_draw_lyrics_screen(const struct main_win * w)1778 static void main_win_draw_lyrics_screen (const struct main_win *w)
1779 {
1780 int i;
1781 int max_lines;
1782 int height, width;
1783 lists_t_strs *lyrics_array;
1784
1785 assert (w != NULL);
1786 assert (w->in_lyrics);
1787
1788 max_lines = w->lyrics_screen_top + LINES - 6;
1789
1790 werase (w->win);
1791 wbkgd (w->win, get_color(CLR_BACKGROUND));
1792
1793 wmove (w->win, 0, 0);
1794 if (w->lyrics_screen_top != 0) {
1795 wattrset (w->win, get_color(CLR_MESSAGE));
1796 xmvwaddstr (w->win, 0, COLS/2 - (sizeof("...MORE...")-1)/2,
1797 "...MORE...");
1798 }
1799 wmove (w->win, 1, 0);
1800 wattrset (w->win, get_color(CLR_LEGEND));
1801 getmaxyx (w->win, height, width);
1802 lyrics_array = lyrics_format (height, width);
1803 for (i = w->lyrics_screen_top; i < max_lines && i < lists_strs_size (lyrics_array); i++)
1804 xwaddstr (w->win, lists_strs_at (lyrics_array, i));
1805 if (i != lists_strs_size (lyrics_array)) {
1806 wattrset (w->win, get_color(CLR_MESSAGE));
1807 xmvwaddstr (w->win, LINES-5,
1808 COLS/2 - (sizeof("...MORE...")-1)/2,
1809 "...MORE...");
1810 }
1811 lists_strs_free (lyrics_array);
1812 }
1813
main_win_draw(struct main_win * w)1814 static void main_win_draw (struct main_win *w)
1815 {
1816 size_t ix;
1817
1818 if (w->in_help)
1819 main_win_draw_help_screen (w);
1820 else if (w->in_lyrics)
1821 main_win_draw_lyrics_screen (w);
1822 else if (w->too_small)
1823 main_win_draw_too_small_screen (w);
1824 else {
1825 werase (w->win);
1826
1827 /* Draw all visible menus. Draw the selected menu last. */
1828 for (ix = 0; ix < ARRAY_SIZE(w->menus); ix += 1)
1829 if (w->menus[ix].visible && ix != (size_t)w->selected_menu)
1830 side_menu_draw (&w->menus[ix], 0);
1831
1832 side_menu_draw (&w->menus[w->selected_menu], 1);
1833 }
1834 }
1835
iface_to_side_menu(const enum iface_menu iface_menu)1836 static enum side_menu_type iface_to_side_menu (const enum iface_menu iface_menu)
1837 {
1838 switch (iface_menu) {
1839 case IFACE_MENU_PLIST:
1840 return MENU_PLAYLIST;
1841 case IFACE_MENU_DIR:
1842 return MENU_DIR;
1843 default:
1844 abort (); /* BUG */
1845 };
1846 }
1847
main_win_set_dir_content(struct main_win * w,const enum iface_menu iface_menu,const struct plist * files,const lists_t_strs * dirs,const lists_t_strs * playlists)1848 static void main_win_set_dir_content (struct main_win *w,
1849 const enum iface_menu iface_menu, const struct plist *files,
1850 const lists_t_strs *dirs, const lists_t_strs *playlists)
1851 {
1852 struct side_menu *m;
1853
1854 assert (w != NULL);
1855
1856 m = find_side_menu (w, iface_to_side_menu(iface_menu));
1857
1858 side_menu_make_list_content (m, files, dirs, playlists,
1859 iface_menu == IFACE_MENU_DIR);
1860 if (w->curr_file)
1861 side_menu_mark_file (m, w->curr_file);
1862 main_win_draw (w);
1863 }
1864
main_win_set_title(struct main_win * w,const enum side_menu_type type,const char * title)1865 static void main_win_set_title (struct main_win *w,
1866 const enum side_menu_type type,
1867 const char *title)
1868 {
1869 struct side_menu *m;
1870
1871 assert (w != NULL);
1872 assert (title != NULL);
1873
1874 m = find_side_menu (w, type);
1875 side_menu_set_title (m, title);
1876 main_win_draw (w);
1877 }
1878
main_win_update_dir_content(struct main_win * w,const enum iface_menu iface_menu,const struct plist * files,const lists_t_strs * dirs,const lists_t_strs * playlists)1879 static void main_win_update_dir_content (struct main_win *w,
1880 const enum iface_menu iface_menu, const struct plist *files,
1881 const lists_t_strs *dirs, const lists_t_strs *playlists)
1882 {
1883 struct side_menu *m;
1884 struct side_menu_state ms;
1885
1886 assert (w != NULL);
1887
1888 m = find_side_menu (w, iface_menu == IFACE_MENU_DIR ? MENU_DIR
1889 : MENU_PLAYLIST);
1890
1891 side_menu_get_state (m, &ms);
1892 side_menu_make_list_content (m, files, dirs, playlists, 1);
1893 side_menu_set_state (m, &ms);
1894 if (w->curr_file)
1895 side_menu_mark_file (m, w->curr_file);
1896 main_win_draw (w);
1897 }
1898
main_win_switch_to(struct main_win * w,const enum side_menu_type menu)1899 static void main_win_switch_to (struct main_win *w,
1900 const enum side_menu_type menu)
1901 {
1902 size_t ix;
1903
1904 assert (w != NULL);
1905
1906 if (w->selected_menu == 2) /* if the themes menu is selected */
1907 side_menu_destroy (&w->menus[2]);
1908
1909 for (ix = 0; ix < ARRAY_SIZE(w->menus); ix += 1)
1910 if (w->menus[ix].type == menu) {
1911 w->selected_menu = ix;
1912 break;
1913 }
1914
1915 assert (ix < ARRAY_SIZE(w->menus));
1916
1917 main_win_draw (w);
1918 }
1919
main_win_switch_to_help(struct main_win * w)1920 static void main_win_switch_to_help (struct main_win *w)
1921 {
1922 assert (w != NULL);
1923
1924 w->in_help = 1;
1925 main_win_draw (w);
1926 }
1927
main_win_switch_to_lyrics(struct main_win * w)1928 static void main_win_switch_to_lyrics (struct main_win *w)
1929 {
1930 assert (w != NULL);
1931
1932 w->in_lyrics = 1;
1933 main_win_draw (w);
1934 }
1935
main_win_create_themes_menu(struct main_win * w)1936 static void main_win_create_themes_menu (struct main_win *w)
1937 {
1938 struct window_params p;
1939
1940 assert (w != NULL);
1941
1942 p.x = 0;
1943 p.y = 0;
1944 p.width = COLS;
1945 p.height = LINES - 4;
1946
1947 side_menu_init (&w->menus[2], MENU_THEMES, w->win, &p);
1948 side_menu_set_title (&w->menus[2], "Themes");
1949 }
1950
main_win_menu_cmd(struct main_win * w,const enum key_cmd cmd)1951 static void main_win_menu_cmd (struct main_win *w, const enum key_cmd cmd)
1952 {
1953 assert (w != NULL);
1954
1955 side_menu_cmd (&w->menus[w->selected_menu], cmd);
1956 main_win_draw (w);
1957 }
1958
main_win_curritem_get_type(const struct main_win * w)1959 static enum file_type main_win_curritem_get_type (const struct main_win *w)
1960 {
1961 assert (w != NULL);
1962
1963 return side_menu_curritem_get_type (&w->menus[w->selected_menu]);
1964 }
1965
main_win_get_curr_file(const struct main_win * w)1966 static char *main_win_get_curr_file (const struct main_win *w)
1967 {
1968 assert (w != NULL);
1969
1970 return side_menu_get_curr_file (&w->menus[w->selected_menu]);
1971 }
1972
main_win_in_dir_menu(const struct main_win * w)1973 static int main_win_in_dir_menu (const struct main_win *w)
1974 {
1975 assert (w != NULL);
1976
1977 return w->menus[w->selected_menu].type == MENU_DIR;
1978 }
1979
main_win_in_help(const struct main_win * w)1980 static int main_win_in_help (const struct main_win *w)
1981 {
1982 assert (w != NULL);
1983
1984 return w->in_help;
1985 }
1986
main_win_in_lyrics(const struct main_win * w)1987 static int main_win_in_lyrics (const struct main_win *w)
1988 {
1989 assert (w != NULL);
1990
1991 return w->in_lyrics;
1992 }
1993
main_win_in_plist_menu(const struct main_win * w)1994 static int main_win_in_plist_menu (const struct main_win *w)
1995 {
1996 assert (w != NULL);
1997
1998 return w->menus[w->selected_menu].type == MENU_PLAYLIST;
1999 }
2000
main_win_in_theme_menu(const struct main_win * w)2001 static int main_win_in_theme_menu (const struct main_win *w)
2002 {
2003 assert (w != NULL);
2004
2005 return w->menus[w->selected_menu].type == MENU_THEMES;
2006 }
2007
main_win_set_curr_item_title(struct main_win * w,const char * title)2008 static void main_win_set_curr_item_title (struct main_win *w, const char *title)
2009 {
2010 assert (w != NULL);
2011 assert (title != NULL);
2012
2013 side_menu_set_curr_item_title (&w->menus[w->selected_menu], title);
2014 main_win_draw (w);
2015 }
2016
2017 /* Update item title and time on all menus where it's present. */
main_win_update_item(struct main_win * w,const enum iface_menu iface_menu,const struct plist * plist,const int n)2018 static void main_win_update_item (struct main_win *w,
2019 const enum iface_menu iface_menu, const struct plist *plist,
2020 const int n)
2021 {
2022 struct side_menu *m;
2023
2024 assert (w != NULL);
2025 assert (plist != NULL);
2026 assert (LIMIT(n, plist->num));
2027
2028 m = find_side_menu (w, iface_to_side_menu(iface_menu));
2029
2030 if (side_menu_update_item(m, plist, n))
2031 main_win_draw (w);
2032 }
2033
2034 /* Mark the played file on all lists of files or unmark it when file is NULL. */
main_win_set_played_file(struct main_win * w,const char * file)2035 static void main_win_set_played_file (struct main_win *w, const char *file)
2036 {
2037 size_t ix;
2038
2039 assert (w != NULL);
2040
2041 if (w->curr_file)
2042 free (w->curr_file);
2043 w->curr_file = xstrdup (file);
2044
2045 for (ix = 0; ix < ARRAY_SIZE(w->menus); ix += 1) {
2046 struct side_menu *m = &w->menus[ix];
2047
2048 if (m->visible && (m->type == MENU_DIR
2049 || m->type == MENU_PLAYLIST)) {
2050 side_menu_unmark_file (m);
2051 if (file)
2052 side_menu_mark_file (m, file);
2053 }
2054 }
2055
2056 main_win_draw (w);
2057 }
2058
main_win_menu_filter(struct main_win * w,const char * pattern)2059 static int main_win_menu_filter (struct main_win *w, const char *pattern)
2060 {
2061 int num;
2062
2063 assert (w != NULL);
2064 assert (pattern != NULL);
2065
2066 num = side_menu_filter (&w->menus[w->selected_menu], pattern);
2067
2068 if (num)
2069 main_win_draw (w);
2070
2071 return num;
2072 }
2073
main_win_clear_filter_menu(struct main_win * w)2074 static void main_win_clear_filter_menu (struct main_win *w)
2075 {
2076 assert (w != NULL);
2077
2078 side_menu_use_main (&w->menus[w->selected_menu]);
2079 main_win_draw (w);
2080 }
2081
main_win_set_plist_time(struct main_win * w,const int time,const int time_for_all)2082 static void main_win_set_plist_time (struct main_win *w, const int time,
2083 const int time_for_all)
2084 {
2085 struct side_menu *m;
2086
2087 assert (w != NULL);
2088
2089 m = find_side_menu (w, MENU_PLAYLIST);
2090 side_menu_set_plist_time (m, time, time_for_all);
2091 }
2092
main_win_add_to_plist(struct main_win * w,const struct plist * plist,const int num)2093 static void main_win_add_to_plist (struct main_win *w, const struct plist *plist,
2094 const int num)
2095 {
2096 struct side_menu *m;
2097 int need_redraw;
2098
2099 assert (plist != NULL);
2100
2101 m = find_side_menu (w, MENU_PLAYLIST);
2102 need_redraw = side_menu_add_plist_item (m, plist, num);
2103 if (w->curr_file)
2104 side_menu_mark_file (m, w->curr_file);
2105 if (need_redraw)
2106 main_win_draw (w);
2107 }
2108
main_win_add_file(struct main_win * w,const char * file,const char * title,const enum file_type type)2109 static void main_win_add_file (struct main_win *w, const char *file,
2110 const char *title, const enum file_type type)
2111 {
2112 assert (w != NULL);
2113 assert (file != NULL);
2114 assert (title != NULL);
2115
2116 side_menu_add_file (&w->menus[w->selected_menu], file, title, type);
2117 main_win_draw (w);
2118 }
2119
main_win_get_files_time(const struct main_win * w,const enum iface_menu menu)2120 static int main_win_get_files_time (const struct main_win *w,
2121 const enum iface_menu menu)
2122 {
2123 struct side_menu *m;
2124
2125 assert (w != NULL);
2126
2127 m = find_side_menu ((struct main_win *)w, iface_to_side_menu(menu));
2128
2129 return side_menu_get_files_time (m);
2130 }
2131
main_win_is_time_for_all(const struct main_win * w,const enum iface_menu menu)2132 static int main_win_is_time_for_all (const struct main_win *w,
2133 const enum iface_menu menu)
2134 {
2135 struct side_menu *m;
2136
2137 assert (w != NULL);
2138
2139 m = find_side_menu ((struct main_win *)w, iface_to_side_menu(menu));
2140
2141 return side_menu_is_time_for_all (m);
2142 }
2143
main_win_get_curr_files_time(const struct main_win * w)2144 static int main_win_get_curr_files_time (const struct main_win *w)
2145 {
2146 assert (w != NULL);
2147
2148 return side_menu_get_files_time (&w->menus[w->selected_menu]);
2149 }
2150
main_win_is_curr_time_for_all(const struct main_win * w)2151 static int main_win_is_curr_time_for_all (const struct main_win *w)
2152 {
2153 assert (w != NULL);
2154
2155 return side_menu_is_time_for_all (&w->menus[w->selected_menu]);
2156 }
2157
main_win_handle_help_key(struct main_win * w,const struct iface_key * k)2158 static void main_win_handle_help_key (struct main_win *w,
2159 const struct iface_key *k)
2160 {
2161 int help_lines;
2162
2163 assert (w != NULL);
2164 assert (w->in_help);
2165
2166 get_keys_help (&help_lines);
2167
2168 if ((k->type == IFACE_KEY_FUNCTION && (
2169 k->key.func == KEY_DOWN
2170 || k->key.func == KEY_NPAGE))
2171 || (k->key.ucs == '\n')) {
2172 if (w->help_screen_top + LINES - 5 <= help_lines)
2173 w->help_screen_top++;
2174 }
2175 else {
2176 if (k->type == IFACE_KEY_FUNCTION && (k->key.func == KEY_UP
2177 || k->key.func == KEY_PPAGE)) {
2178 if (w->help_screen_top > 0)
2179 w->help_screen_top--;
2180 }
2181 else if (k->key.func != KEY_RESIZE)
2182 w->in_help = 0;
2183 }
2184
2185 main_win_draw (w);
2186 }
2187
main_win_handle_lyrics_key(struct main_win * w,const struct iface_key * k)2188 static void main_win_handle_lyrics_key (struct main_win *w,
2189 const struct iface_key *k)
2190 {
2191 int height, width;
2192 lists_t_strs *lyrics_array;
2193
2194 assert (w != NULL);
2195 assert (w->in_lyrics);
2196
2197 if ((k->type == IFACE_KEY_FUNCTION && (
2198 k->key.func == KEY_DOWN
2199 || k->key.func == KEY_NPAGE))
2200 || (k->key.ucs == '\n')) {
2201 getmaxyx (w->win, height, width);
2202 lyrics_array = lyrics_format (height, width);
2203 if (w->lyrics_screen_top + LINES - 5 <= lists_strs_size (lyrics_array))
2204 w->lyrics_screen_top++;
2205 lists_strs_free (lyrics_array);
2206 }
2207 else {
2208 if (k->type == IFACE_KEY_FUNCTION && (k->key.func == KEY_UP
2209 || k->key.func == KEY_PPAGE)) {
2210 if (w->lyrics_screen_top > 0)
2211 w->lyrics_screen_top--;
2212 }
2213 else if (k->key.func != KEY_RESIZE)
2214 w->in_lyrics = 0;
2215 }
2216
2217 main_win_draw (w);
2218 }
2219
main_win_swap_plist_items(struct main_win * w,const char * file1,const char * file2)2220 static void main_win_swap_plist_items (struct main_win *w, const char *file1,
2221 const char *file2)
2222 {
2223 struct side_menu *m;
2224
2225 assert (w != NULL);
2226 assert (file1 != NULL);
2227 assert (file2 != NULL);
2228
2229 m = find_side_menu (w, MENU_PLAYLIST);
2230 side_menu_swap_items (m, file1, file2);
2231 main_win_draw (w);
2232 }
2233
main_win_use_layout(struct main_win * w,lists_t_strs * layout_fmt)2234 static void main_win_use_layout (struct main_win *w, lists_t_strs *layout_fmt)
2235 {
2236 struct main_win_layout l;
2237 int res;
2238
2239 assert (w != NULL);
2240 assert (layout_fmt != NULL);
2241
2242 w->layout_fmt = layout_fmt;
2243
2244 res = parse_layout (&l, layout_fmt);
2245 assert (res);
2246
2247 side_menu_resize (&w->menus[0], &l.menus[0]);
2248 side_menu_resize (&w->menus[1], &l.menus[1]);
2249
2250 main_win_draw (w);
2251 }
2252
validate_layouts()2253 static void validate_layouts ()
2254 {
2255 struct main_win_layout l;
2256 lists_t_strs *layout_fmt;
2257
2258 layout_fmt = options_get_list ("Layout1");
2259 if (lists_strs_empty (layout_fmt) || !parse_layout(&l, layout_fmt))
2260 interface_fatal ("Layout1 is malformed!");
2261
2262 layout_fmt = options_get_list ("Layout2");
2263 if (!lists_strs_empty (layout_fmt) && !parse_layout(&l, layout_fmt))
2264 interface_fatal ("Layout2 is malformed!");
2265
2266 layout_fmt = options_get_list ("Layout3");
2267 if (!lists_strs_empty (layout_fmt) && !parse_layout(&l, layout_fmt))
2268 interface_fatal ("Layout3 is malformed!");
2269 }
2270
2271 /* Handle terminal size change. */
main_win_resize(struct main_win * w)2272 static void main_win_resize (struct main_win *w)
2273 {
2274 struct main_win_layout l;
2275 int res;
2276
2277 assert (w != NULL);
2278
2279 keypad (w->win, TRUE);
2280 wresize (w->win, LINES - 4, COLS);
2281 werase (w->win);
2282
2283 res = parse_layout (&l, w->layout_fmt);
2284 assert (res);
2285
2286 side_menu_resize (&w->menus[0], &l.menus[0]);
2287 side_menu_resize (&w->menus[1], &l.menus[1]);
2288
2289 if (w->menus[2].visible) { /* Themes menu */
2290 struct window_params p;
2291
2292 p.x = 0;
2293 p.y = 0;
2294 p.width = COLS;
2295 p.height = LINES - 4;
2296
2297 side_menu_resize (&w->menus[2], &p);
2298 }
2299
2300 main_win_draw (w);
2301 }
2302
main_win_make_visible(struct main_win * w,const enum side_menu_type type,const char * file)2303 static void main_win_make_visible (struct main_win *w,
2304 const enum side_menu_type type, const char *file)
2305 {
2306 struct side_menu *m;
2307
2308 assert (w != NULL);
2309 assert (file != NULL);
2310
2311 m = find_side_menu (w, type);
2312 side_menu_make_visible (m, file);
2313 main_win_draw (w);
2314 }
2315
main_win_update_show_time(struct main_win * w)2316 static void main_win_update_show_time (struct main_win *w)
2317 {
2318 size_t ix;
2319
2320 assert (w != NULL);
2321
2322 for (ix = 0; ix < ARRAY_SIZE(w->menus); ix += 1) {
2323 struct side_menu *m = &w->menus[ix];
2324
2325 if (m->visible && (m->type == MENU_DIR
2326 || m->type == MENU_PLAYLIST))
2327 side_menu_update_show_time (&w->menus[ix]);
2328 }
2329
2330 main_win_draw (w);
2331 }
2332
main_win_select_file(struct main_win * w,const char * file)2333 static void main_win_select_file (struct main_win *w, const char *file)
2334 {
2335 assert (w != NULL);
2336 assert (file != NULL);
2337
2338 side_menu_select_file (&w->menus[w->selected_menu], file);
2339 main_win_draw (w);
2340 }
2341
main_win_update_show_format(struct main_win * w)2342 static void main_win_update_show_format (struct main_win *w)
2343 {
2344 size_t ix;
2345
2346 assert (w != NULL);
2347
2348 for (ix = 0; ix < ARRAY_SIZE(w->menus); ix += 1) {
2349 struct side_menu *m = &w->menus[ix];
2350
2351 if (m->visible && (m->type == MENU_DIR
2352 || m->type == MENU_PLAYLIST))
2353 side_menu_update_show_format (&w->menus[ix]);
2354 }
2355
2356 main_win_draw (w);
2357 }
2358
main_win_del_plist_item(struct main_win * w,const char * file)2359 static void main_win_del_plist_item (struct main_win *w, const char *file)
2360 {
2361 struct side_menu *m;
2362
2363 assert (w != NULL);
2364 assert (file != NULL);
2365
2366 m = find_side_menu (w, MENU_PLAYLIST);
2367 side_menu_del_item (m, file);
2368 main_win_draw (w);
2369 }
2370
main_win_clear_plist(struct main_win * w)2371 static void main_win_clear_plist (struct main_win *w)
2372 {
2373 struct side_menu *m;
2374
2375 assert (w != NULL);
2376
2377 m = find_side_menu (w, MENU_PLAYLIST);
2378 side_menu_clear (m);
2379 main_win_draw (w);
2380 }
2381
2382 /* Write to a file and log but otherwise ignore any error. */
soft_write(int fd,const void * buf,size_t count)2383 static void soft_write (int fd, const void *buf, size_t count)
2384 {
2385 ssize_t rc;
2386
2387 rc = write (fd, buf, count);
2388 if (rc < 0)
2389 logit ("write() failed: %s", strerror(errno));
2390 }
2391
2392 /* Set the has_xterm variable. */
detect_term()2393 static void detect_term ()
2394 {
2395 char *term;
2396
2397 term = getenv ("TERM");
2398 if (term) {
2399 lists_t_strs *xterms;
2400
2401 xterms = options_get_list ("XTerms");
2402 has_xterm = lists_strs_exists (xterms, term);
2403 }
2404 }
2405
xterm_set_title(const int state,const char * title)2406 static void xterm_set_title (const int state, const char *title)
2407 {
2408 if (has_xterm && options_get_int("SetXtermTitle")) {
2409 soft_write (1, "\033]0;", sizeof("\033]0;")-1);
2410 soft_write (1, "MOC ", sizeof("MOC ")-1);
2411
2412 switch (state) {
2413 case STATE_PLAY:
2414 soft_write (1, "[play]", sizeof("[play]")-1);
2415 break;
2416 case STATE_STOP:
2417 soft_write (1, "[stop]", sizeof("[stop]")-1);
2418 break;
2419 case STATE_PAUSE:
2420 soft_write (1, "[pause]", sizeof("[pause]")-1);
2421 break;
2422 }
2423
2424 if (title)
2425 {
2426 soft_write (1, " - ", sizeof(" - ")-1);
2427 if (options_get_int ("NonUTFXterm"))
2428 {
2429 char *iconv_title = xterm_iconv_str (title);
2430 soft_write (1, iconv_title, strlen(iconv_title));
2431 free (iconv_title);
2432 }
2433 else
2434 {
2435 soft_write (1, title, strlen(title));
2436 }
2437 }
2438
2439 soft_write (1, "\007", 1);
2440 }
2441 }
2442
2443
xterm_clear_title()2444 static void xterm_clear_title ()
2445 {
2446 if (has_xterm && options_get_int("SetXtermTitle"))
2447 soft_write (1, "\033]2;\007", sizeof("\033]2;\007")-1);
2448 }
2449
2450 /* Set the has_screen variable. */
detect_screen()2451 static void detect_screen ()
2452 {
2453 char *term, *window;
2454
2455 term = getenv ("TERM");
2456 window = getenv ("WINDOW");
2457 if (term && window && isdigit (*window)) {
2458 lists_t_strs *screen_terms;
2459
2460 screen_terms = options_get_list ("ScreenTerms");
2461 has_screen = lists_strs_exists (screen_terms, term);
2462 }
2463 }
2464
2465 #define SCREEN_TITLE_START "\033k"
2466 #define SCREEN_TITLE_END "\033\\"
screen_set_title(const int state,const char * title)2467 static void screen_set_title (const int state, const char *title)
2468 {
2469 if (has_screen && options_get_int("SetScreenTitle")) {
2470 soft_write (1, SCREEN_TITLE_START, sizeof(SCREEN_TITLE_START)-1);
2471 soft_write (1, "MOC ", sizeof("MOC ")-1);
2472
2473 switch (state) {
2474 case STATE_PLAY:
2475 soft_write (1, "[play]", sizeof("[play]")-1);
2476 break;
2477 case STATE_STOP:
2478 soft_write (1, "[stop]", sizeof("[stop]")-1);
2479 break;
2480 case STATE_PAUSE:
2481 soft_write (1, "[pause]", sizeof("[pause]")-1);
2482 break;
2483 }
2484
2485 if (title) {
2486 soft_write (1, " - ", sizeof(" - ")-1);
2487 soft_write (1, title, strlen(title));
2488 }
2489
2490 soft_write (1, SCREEN_TITLE_END, sizeof(SCREEN_TITLE_END)-1);
2491 }
2492 }
2493
screen_clear_title()2494 static void screen_clear_title ()
2495 {
2496 if (has_screen && options_get_int("SetScreenTitle"))
2497 {
2498 soft_write (1, SCREEN_TITLE_START, sizeof(SCREEN_TITLE_START)-1);
2499 soft_write (1, SCREEN_TITLE_END, sizeof(SCREEN_TITLE_END)-1);
2500 }
2501 }
2502
2503 /* Based on ASCIILines option initialize line characters with curses lines or
2504 * ASCII characters. */
init_lines()2505 static void init_lines ()
2506 {
2507 if (options_get_int("ASCIILines")) {
2508 lines.vert = '|';
2509 lines.horiz = '-';
2510 lines.ulcorn = '+';
2511 lines.urcorn = '+';
2512 lines.llcorn = '+';
2513 lines.lrcorn = '+';
2514 lines.rtee = '|';
2515 lines.ltee = '|';
2516 }
2517 else {
2518 lines.vert = ACS_VLINE;
2519 lines.horiz = ACS_HLINE;
2520 lines.ulcorn = ACS_ULCORNER;
2521 lines.urcorn = ACS_URCORNER;
2522 lines.llcorn = ACS_LLCORNER;
2523 lines.lrcorn = ACS_LRCORNER;
2524 lines.rtee = ACS_RTEE;
2525 lines.ltee = ACS_LTEE;
2526 }
2527 }
2528
2529 /* End the program if the terminal is too small. */
check_term_size(struct main_win * mw,struct info_win * iw)2530 static void check_term_size (struct main_win *mw, struct info_win *iw)
2531 {
2532 mw->too_small = iw->too_small = COLS < 59 || LINES < 7;
2533 }
2534
2535 /* Update the title with the current fill. */
bar_update_title(struct bar * b)2536 static void bar_update_title (struct bar *b)
2537 {
2538 char pct[8];
2539
2540 assert (b != NULL);
2541 assert (b->show_val);
2542
2543 if (!b->show_pct)
2544 sprintf (b->title, "%*s", b->width, b->orig_title);
2545 else {
2546 sprintf (b->title, "%*s", b->width - 7, b->orig_title);
2547 strcpy (pct, " 100% ");
2548 if (b->filled < 99.99)
2549 snprintf (pct, sizeof (pct), " %02.0f%% ", b->filled);
2550 strncpy (&b->title[b->width - 7], pct, strlen (pct));
2551 }
2552 }
2553
bar_set_title(struct bar * b,const char * title)2554 static void bar_set_title (struct bar *b, const char *title)
2555 {
2556 assert (b != NULL);
2557 assert (b->show_val);
2558 assert (title != NULL);
2559 assert (strlen(title) < sizeof(b->title) - 5);
2560
2561 strncpy (b->orig_title, title, b->width);
2562 b->orig_title[b->width] = 0;
2563 bar_update_title (b);
2564 }
2565
bar_init(struct bar * b,const int width,const char * title,const int show_val,const int show_pct,const int fill_color,const int empty_color)2566 static void bar_init (struct bar *b, const int width, const char *title,
2567 const int show_val, const int show_pct,
2568 const int fill_color, const int empty_color)
2569 {
2570 assert (b != NULL);
2571 assert (width > 5 && width < (int)sizeof(b->title));
2572 assert (title != NULL || !show_val);
2573
2574 b->width = width;
2575 b->filled = 0.0;
2576 b->show_val = show_val;
2577 b->show_pct = show_pct;
2578 b->fill_color = fill_color;
2579 b->empty_color = empty_color;
2580
2581 if (show_val) {
2582 b->orig_title = xmalloc (b->width + 1);
2583 bar_set_title (b, title);
2584 } else {
2585 b->orig_title = NULL;
2586 memset (b->title, ' ', b->width);
2587 b->title[b->width] = 0;
2588 }
2589 }
2590
bar_draw(const struct bar * b,WINDOW * win,const int pos_x,const int pos_y)2591 static void bar_draw (const struct bar *b, WINDOW *win, const int pos_x,
2592 const int pos_y)
2593 {
2594 int fill_chars; /* how many chars are "filled" */
2595
2596 assert (b != NULL);
2597 assert (win != NULL);
2598 assert (LIMIT(pos_x, COLS - b->width));
2599 assert (LIMIT(pos_y, LINES));
2600
2601 fill_chars = b->filled * b->width / 100.0;
2602
2603 wattrset (win, b->fill_color);
2604 xmvwaddnstr (win, pos_y, pos_x, b->title, fill_chars);
2605
2606 wattrset (win, b->empty_color);
2607 xwaddstr (win, b->title + fill_chars);
2608 }
2609
bar_set_fill(struct bar * b,const double fill)2610 static void bar_set_fill (struct bar *b, const double fill)
2611 {
2612 assert (b != NULL);
2613 assert (fill >= 0.0);
2614
2615 b->filled = MIN(fill, 100.0);
2616
2617 if (b->show_val)
2618 bar_update_title (b);
2619 }
2620
bar_resize(struct bar * b,const int width)2621 static void bar_resize (struct bar *b, const int width)
2622 {
2623 assert (b != NULL);
2624 assert (width > 5 && width < (int)sizeof(b->title));
2625
2626 if (b->show_val && b->width < width) {
2627 char *new_title = xmalloc (width + 1);
2628 strcpy (new_title, b->orig_title);
2629 free (b->orig_title);
2630 b->orig_title = new_title;
2631 }
2632
2633 b->width = width;
2634
2635 if (b->show_val)
2636 bar_update_title (b);
2637 else {
2638 memset (b->title, ' ', b->width);
2639 b->title[b->width] = 0;
2640 }
2641 }
2642
queued_message_create(enum message_type type)2643 static struct queued_message *queued_message_create (enum message_type type)
2644 {
2645 struct queued_message *result;
2646
2647 result = (struct queued_message *) xmalloc (sizeof (struct queued_message));
2648 result->next = NULL;
2649 result->type = type;
2650 result->msg = NULL;
2651 result->prompt = NULL;
2652 result->timeout = 0;
2653 result->callback = NULL;
2654 result->data = NULL;
2655
2656 return result;
2657 };
2658
queued_message_destroy(struct queued_message * msg)2659 static void queued_message_destroy (struct queued_message *msg)
2660 {
2661 assert (msg != NULL);
2662
2663 if (msg->msg)
2664 free (msg->msg);
2665 if (msg->prompt)
2666 free (msg->prompt);
2667
2668 free (msg);
2669 };
2670
set_startup_message(struct info_win * w)2671 static void set_startup_message (struct info_win *w)
2672 {
2673 assert (w != NULL);
2674
2675 w->current_message = queued_message_create (NORMAL_MSG);
2676 w->current_message->msg = xstrdup (STARTUP_MESSAGE);
2677 w->current_message->timeout = time (NULL);
2678 w->current_message->timeout += options_get_int ("MessageLingerTime");
2679
2680 if (is_help_still_h ()) {
2681 struct queued_message *msg;
2682
2683 msg = queued_message_create (NORMAL_MSG);
2684 msg->msg = xstrdup ("Press 'h' for the list of commands.");
2685 msg->timeout = options_get_int ("MessageLingerTime");
2686
2687 w->queued_message_head = msg;
2688 w->queued_message_tail = msg;
2689 w->queued_message_total = 1;
2690 }
2691 }
2692
info_win_init(struct info_win * w)2693 static void info_win_init (struct info_win *w)
2694 {
2695 assert (w != NULL);
2696
2697 w->win = newwin (4, COLS, LINES - 4, 0);
2698 wbkgd (w->win, get_color(CLR_BACKGROUND));
2699
2700 w->queued_message_head = NULL;
2701 w->queued_message_tail = NULL;
2702 w->queued_message_total = 0;
2703 w->queued_message_errors = 0;
2704
2705 w->too_small = 0;
2706
2707 w->state_stereo = 0;
2708 w->state_shuffle = 0;
2709 w->state_repeat = 0;
2710 w->state_next = 0;
2711 w->state_play = STATE_STOP;
2712 w->state_net = 0;
2713
2714 w->bitrate = -1;
2715 w->rate = -1;
2716
2717 w->files_in_queue = 0;
2718
2719 w->curr_time = -1;
2720 w->total_time = -1;
2721 w->block_start = -1;
2722 w->block_end = -1;
2723
2724 w->title = NULL;
2725 w->status_msg[0] = 0;
2726
2727 w->in_entry = 0;
2728 entry_history_init (&w->urls_history);
2729 entry_history_init (&w->dirs_history);
2730 entry_history_init (&w->user_history);
2731
2732 set_startup_message (w);
2733
2734 bar_init (&w->mixer_bar, 20, "", 1, 1,
2735 get_color(CLR_MIXER_BAR_FILL),
2736 get_color(CLR_MIXER_BAR_EMPTY));
2737 bar_init (&w->time_bar, COLS - 4, "", 1,
2738 options_get_bool("ShowTimePercent") ? 1 : 0,
2739 get_color(CLR_TIME_BAR_FILL),
2740 get_color(CLR_TIME_BAR_EMPTY));
2741 }
2742
info_win_destroy(struct info_win * w)2743 static void info_win_destroy (struct info_win *w)
2744 {
2745 assert (w != NULL);
2746
2747 if (w->win)
2748 delwin (w->win);
2749 if (w->in_entry)
2750 entry_destroy (&w->entry);
2751
2752 entry_history_clear (&w->urls_history);
2753 entry_history_clear (&w->dirs_history);
2754 entry_history_clear (&w->user_history);
2755 }
2756
2757 /* Set the cursor position in the right place if needed. */
info_win_update_curs(const struct info_win * w)2758 static void info_win_update_curs (const struct info_win *w)
2759 {
2760 assert (w != NULL);
2761
2762 if (w->in_entry && !w->too_small)
2763 entry_draw (&w->entry, w->win, 1, 0);
2764 }
2765
info_win_set_mixer_name(struct info_win * w,const char * name)2766 static void info_win_set_mixer_name (struct info_win *w, const char *name)
2767 {
2768 assert (w != NULL);
2769 assert (name != NULL);
2770
2771 bar_set_title (&w->mixer_bar, name);
2772 if (!w->in_entry && !w->too_small) {
2773 bar_draw (&w->mixer_bar, w->win, COLS - 37, 0);
2774 info_win_update_curs (w);
2775 }
2776 }
2777
info_win_draw_status(const struct info_win * w)2778 static void info_win_draw_status (const struct info_win *w)
2779 {
2780 assert (w != NULL);
2781
2782 if (!w->in_entry && !w->too_small) {
2783 wattrset (w->win, get_color(CLR_STATUS));
2784 wmove (w->win, 0, 6);
2785 xwprintw (w->win, "%-*s", (int) sizeof(w->status_msg) - 1,
2786 w->status_msg);
2787 info_win_update_curs (w);
2788 }
2789 }
2790
info_win_set_status(struct info_win * w,const char * msg)2791 static void info_win_set_status (struct info_win *w, const char *msg)
2792 {
2793 assert (w != NULL);
2794 assert (msg != NULL);
2795 assert (strlen(msg) < sizeof(w->status_msg));
2796
2797 strcpy (w->status_msg, msg);
2798 info_win_draw_status (w);
2799 }
2800
info_win_draw_files_in_queue(const struct info_win * w)2801 static void info_win_draw_files_in_queue (const struct info_win *w)
2802 {
2803 const int hstart = 5 + sizeof(w->status_msg) + 2;
2804
2805 assert (w != NULL);
2806
2807 if(!w->in_entry && !w->too_small) {
2808 if (w->files_in_queue) {
2809 wattrset (w->win, get_color(CLR_STATUS));
2810 mvwaddch (w->win, 0, hstart, lines.rtee);
2811 xwprintw (w->win, "Q:%3d", w->files_in_queue);
2812 waddch (w->win, lines.ltee);
2813 }
2814 else {
2815 wattrset (w->win, get_color(CLR_FRAME));
2816 mvwhline (w->win, 0, hstart, lines.horiz, 9);
2817 }
2818 }
2819
2820 info_win_update_curs (w);
2821 }
2822
info_win_set_files_in_queue(struct info_win * w,const int num)2823 static void info_win_set_files_in_queue (struct info_win *w, const int num)
2824 {
2825 assert (w != NULL);
2826 assert (num >= 0);
2827
2828 w->files_in_queue = num;
2829 info_win_draw_files_in_queue (w);
2830 }
2831
info_win_draw_state(const struct info_win * w)2832 static void info_win_draw_state (const struct info_win *w)
2833 {
2834 const char *state_symbol;
2835
2836 assert (w != NULL);
2837
2838 switch (w->state_play) {
2839 case STATE_PLAY:
2840 state_symbol = " >";
2841 break;
2842 case STATE_STOP:
2843 state_symbol = "[]";
2844 break;
2845 case STATE_PAUSE:
2846 state_symbol = "||";
2847 break;
2848 default:
2849 abort (); /* BUG */
2850 }
2851
2852 if (!w->too_small) {
2853 wattrset (w->win, get_color(CLR_STATE));
2854 xmvwaddstr (w->win, 1, 1, state_symbol);
2855 }
2856
2857 info_win_update_curs (w);
2858 }
2859
2860 /* Draw the title or the message (informative or error). */
info_win_draw_title(const struct info_win * w)2861 static void info_win_draw_title (const struct info_win *w)
2862 {
2863 assert (w != NULL);
2864
2865 if (!w->too_small) {
2866 clear_area (w->win, 4, 1, COLS - 5, 1);
2867
2868 if (w->current_message &&
2869 w->current_message->msg &&
2870 w->current_message->timeout >= time (NULL))
2871 {
2872 wattrset (w->win, w->current_message->type == ERROR_MSG
2873 ? get_color (CLR_ERROR)
2874 : get_color (CLR_MESSAGE));
2875 xmvwaddnstr (w->win, 1, 4, w->current_message->msg, COLS - 5);
2876 }
2877 else
2878 {
2879 wattrset (w->win, get_color (CLR_TITLE));
2880 xmvwaddnstr (w->win, 1, 4, w->title ? w->title : "", COLS - 5);
2881 }
2882 }
2883
2884 info_win_update_curs (w);
2885 }
2886
info_win_set_state(struct info_win * w,const int state)2887 static void info_win_set_state (struct info_win *w, const int state)
2888 {
2889 assert (w != NULL);
2890 assert (state == STATE_PLAY || state == STATE_STOP
2891 || state == STATE_PAUSE);
2892
2893 w->state_play = state;
2894 xterm_set_title (state, w->title);
2895 screen_set_title (state, w->title);
2896 info_win_draw_state (w);
2897 }
2898
info_win_draw_time(const struct info_win * w)2899 static void info_win_draw_time (const struct info_win *w)
2900 {
2901 char time_str[6];
2902
2903 assert (w != NULL);
2904
2905 if (!w->too_small) {
2906 /* current time */
2907 sec_to_min (time_str, w->curr_time != -1 ? w->curr_time : 0);
2908 wattrset (w->win, get_color(CLR_TIME_CURRENT));
2909 xmvwaddstr (w->win, 2, 1, time_str);
2910
2911 /* time left */
2912 if (w->total_time > 0 && w->curr_time >= 0
2913 && w->total_time >= w->curr_time) {
2914 sec_to_min (time_str, w->total_time - w->curr_time);
2915 wmove (w->win, 2, 7);
2916 wattrset (w->win, get_color(CLR_TIME_LEFT));
2917 xwaddstr (w->win, time_str);
2918 }
2919 else
2920 xmvwaddstr (w->win, 2, 7, " ");
2921
2922 /* total time */
2923 sec_to_min (time_str, w->total_time != -1 ? w->total_time : 0);
2924 wmove (w->win, 2, 14);
2925 wattrset (w->win, get_color(CLR_TIME_TOTAL));
2926 xwaddstr (w->win, time_str);
2927
2928 bar_draw (&w->time_bar, w->win, 2, 3);
2929 }
2930 info_win_update_curs (w);
2931 }
2932
info_win_draw_block(const struct info_win * w)2933 static void info_win_draw_block (const struct info_win *w)
2934 {
2935 assert (w != NULL);
2936
2937 if (!w->too_small)
2938 bar_draw (&w->time_bar, w->win, 2, 3);
2939 info_win_update_curs (w);
2940 }
2941
info_win_set_curr_time(struct info_win * w,const int time)2942 static void info_win_set_curr_time (struct info_win *w, const int time)
2943 {
2944 assert (w != NULL);
2945 assert (time >= -1);
2946
2947 w->curr_time = time;
2948 if (w->total_time > 0 && w->curr_time >= 0)
2949 bar_set_fill (&w->time_bar, w->curr_time * 100.0 / w->total_time);
2950 else
2951 bar_set_fill (&w->time_bar, 0.0);
2952
2953 info_win_draw_time (w);
2954 }
2955
info_win_set_total_time(struct info_win * w,const int time)2956 static void info_win_set_total_time (struct info_win *w, const int time)
2957 {
2958 assert (w != NULL);
2959 assert (time >= -1);
2960
2961 w->total_time = time;
2962
2963 if (w->total_time > 0 && w->curr_time >= 0)
2964 bar_set_fill (&w->time_bar, w->curr_time * 100.0 / w->total_time);
2965 else
2966 bar_set_fill (&w->time_bar, 0.0);
2967
2968 info_win_draw_time (w);
2969 }
2970
info_win_set_block_title(struct info_win * w)2971 static void info_win_set_block_title (struct info_win *w)
2972 {
2973 assert (w != NULL);
2974
2975 if (w->total_time == -1) {
2976 bar_set_title (&w->time_bar, "");
2977 } else if (w->block_start == -1 || w->block_end == -1) {
2978 bar_set_title (&w->time_bar, "");
2979 } else if (w->block_start == 0 && w->block_end == w->total_time) {
2980 bar_set_title (&w->time_bar, "");
2981 } else {
2982 int start_pos, end_pos;
2983 char *new_title, *decorators;
2984
2985 start_pos = w->block_start * w->time_bar.width / w->total_time;
2986 if (w->block_end < w->total_time)
2987 end_pos = w->block_end * w->time_bar.width / w->total_time;
2988 else
2989 end_pos = w->time_bar.width - 1;
2990
2991 new_title = xmalloc(w->time_bar.width + 1);
2992 memset(new_title, ' ', w->time_bar.width);
2993 decorators = options_get_str ("BlockDecorators");
2994 if (start_pos == end_pos) {
2995 new_title[start_pos] = decorators[1];
2996 } else {
2997 new_title[start_pos] = decorators[0];
2998 new_title[end_pos] = decorators[2];
2999 }
3000 new_title[w->time_bar.width] = 0x00;
3001
3002 bar_set_title (&w->time_bar, new_title);
3003 }
3004 }
3005
info_win_set_block(struct info_win * w,const int block_start,const int block_end)3006 static void info_win_set_block (struct info_win *w, const int block_start, const int block_end)
3007 {
3008 assert (w != NULL);
3009 assert (block_start == -1 || RANGE(0, block_start, w->total_time));
3010 assert (block_end == -1 || RANGE(0, block_end, w->total_time));
3011
3012 info_win.block_start = block_start;
3013 info_win.block_end = block_end;
3014
3015 info_win_set_block_title (w);
3016 info_win_draw_block (w);
3017 }
3018
info_win_set_played_title(struct info_win * w,const char * title)3019 static void info_win_set_played_title (struct info_win *w, const char *title)
3020 {
3021 assert (w != NULL);
3022
3023 if (!w->title && !title)
3024 return;
3025
3026 if (w->title && title && !strcmp(w->title, title))
3027 return;
3028
3029 if (w->title)
3030 free (w->title);
3031 w->title = xstrdup (title);
3032 xterm_set_title (w->state_play, title);
3033 screen_set_title (w->state_play, title);
3034 info_win_draw_title (w);
3035 }
3036
info_win_draw_rate(const struct info_win * w)3037 static void info_win_draw_rate (const struct info_win *w)
3038 {
3039 assert (w != NULL);
3040
3041 wattrset (w->win, get_color(CLR_SOUND_PARAMS));
3042 wmove (w->win, 2, 22);
3043 if (w->rate != -1)
3044 xwprintw (w->win, "%3d", w->rate);
3045 else
3046 xwaddstr (w->win, " ");
3047 }
3048
info_win_draw_bitrate(const struct info_win * w)3049 static void info_win_draw_bitrate (const struct info_win *w)
3050 {
3051 assert (w != NULL);
3052
3053 if (!w->too_small) {
3054 wattrset (w->win, get_color(CLR_SOUND_PARAMS));
3055 wmove (w->win, 2, 29);
3056 if (w->bitrate != -1)
3057 xwprintw (w->win, "%4d", MIN(w->bitrate, 9999));
3058 else
3059 xwaddstr (w->win, " ");
3060 }
3061 info_win_update_curs (w);
3062 }
3063
info_win_set_bitrate(struct info_win * w,const int bitrate)3064 static void info_win_set_bitrate (struct info_win *w, const int bitrate)
3065 {
3066 assert (w != NULL);
3067 assert (bitrate >= -1);
3068
3069 w->bitrate = bitrate > 0 ? bitrate : -1;
3070 info_win_draw_bitrate (w);
3071 }
3072
info_win_set_rate(struct info_win * w,const int rate)3073 static void info_win_set_rate (struct info_win *w, const int rate)
3074 {
3075 assert (w != NULL);
3076 assert (rate >= -1);
3077
3078 w->rate = rate > 0 ? rate : -1;
3079 info_win_draw_rate (w);
3080 }
3081
info_win_set_mixer_value(struct info_win * w,const int value)3082 static void info_win_set_mixer_value (struct info_win *w, const int value)
3083 {
3084 assert (w != NULL);
3085
3086 bar_set_fill (&w->mixer_bar, (double) value);
3087 if (!w->in_entry && !w->too_small)
3088 bar_draw (&w->mixer_bar, w->win, COLS - 37, 0);
3089 }
3090
3091 /* Draw a switch that is turned on or off in form of [TITLE]. */
info_win_draw_switch(const struct info_win * w,const int posx,const int posy,const char * title,const int value)3092 static void info_win_draw_switch (const struct info_win *w, const int posx,
3093 const int posy, const char *title, const int value)
3094 {
3095 assert (w != NULL);
3096 assert (title != NULL);
3097
3098 if (!w->too_small) {
3099 wattrset (w->win, get_color(
3100 value ? CLR_INFO_ENABLED : CLR_INFO_DISABLED));
3101 wmove (w->win, posy, posx);
3102 xwprintw (w->win, "[%s]", title);
3103 }
3104 info_win_update_curs (w);
3105 }
3106
info_win_draw_options_state(const struct info_win * w)3107 static void info_win_draw_options_state (const struct info_win *w)
3108 {
3109 assert (w != NULL);
3110
3111 info_win_draw_switch (w, 38, 2, "STEREO", w->state_stereo);
3112 info_win_draw_switch (w, 47, 2, "NET", w->state_net);
3113 info_win_draw_switch (w, 53, 2, "SHUFFLE", w->state_shuffle);
3114 info_win_draw_switch (w, 63, 2, "REPEAT", w->state_repeat);
3115 info_win_draw_switch (w, 72, 2, "NEXT", w->state_next);
3116 }
3117
info_win_make_entry(struct info_win * w,const enum entry_type type)3118 static void info_win_make_entry (struct info_win *w, const enum entry_type type)
3119 {
3120 struct entry_history *history;
3121 const char *prompt;
3122
3123 assert (w != NULL);
3124 assert (!w->in_entry);
3125
3126 prompt = NULL;
3127 switch (type) {
3128 case ENTRY_GO_DIR:
3129 history = &w->dirs_history;
3130 break;
3131 case ENTRY_GO_URL:
3132 history = &w->urls_history;
3133 break;
3134 case ENTRY_ADD_URL:
3135 history = &w->urls_history;
3136 break;
3137 case ENTRY_USER_QUERY:
3138 history = &w->user_history;
3139 prompt = w->current_message->prompt;
3140 break;
3141 default:
3142 history = NULL;
3143 }
3144
3145 entry_init (&w->entry, type, COLS - 4, history, prompt);
3146 w->in_entry = 1;
3147 curs_set (1);
3148 entry_draw (&w->entry, w->win, 1, 0);
3149 }
3150
3151 /* Display the next queued message. */
info_win_display_msg(struct info_win * w)3152 static void info_win_display_msg (struct info_win *w)
3153 {
3154 int msg_changed;
3155
3156 assert (w != NULL);
3157
3158 msg_changed = 0;
3159 if (w->current_message && time (NULL) > w->current_message->timeout) {
3160 w->callback = w->current_message->callback;
3161 w->data = w->current_message->data;
3162 queued_message_destroy (w->current_message);
3163 w->current_message = NULL;
3164 msg_changed = 1;
3165 }
3166
3167 if (!w->current_message && w->queued_message_head && !w->in_entry) {
3168 w->current_message = w->queued_message_head;
3169 w->queued_message_head = w->current_message->next;
3170 w->current_message->next = NULL;
3171 if (!w->queued_message_head)
3172 w->queued_message_tail = NULL;
3173 w->queued_message_total -= 1;
3174 if (w->current_message->type == ERROR_MSG)
3175 w->queued_message_errors -= 1;
3176
3177 if (msg_changed &&
3178 w->current_message->msg
3179 && options_get_bool ("PrefixQueuedMessages"))
3180 {
3181 char *msg, *decorator;
3182 int len;
3183
3184 msg = w->current_message->msg;
3185 decorator = options_get_str ("ErrorMessagesQueued");
3186 len = strlen (msg) + strlen (decorator) + 10;
3187 w->current_message->msg = (char *) xmalloc (len);
3188 snprintf (w->current_message->msg, len, "(%d%s) %s",
3189 w->queued_message_total,
3190 (w->queued_message_errors ? decorator : ""),
3191 msg);
3192 w->current_message->msg[len - 1] = 0x00;
3193 free (msg);
3194 }
3195
3196 if (w->current_message->type == QUERY_MSG) {
3197 info_win_make_entry (w, ENTRY_USER_QUERY);
3198 w->current_message->timeout = 86400;
3199 }
3200
3201 w->current_message->timeout += time (NULL);
3202 msg_changed = 1;
3203 }
3204
3205 if (msg_changed)
3206 info_win_draw_title (w);
3207 }
3208
3209 /* Force the next queued message to be displayed. */
info_win_disable_msg(struct info_win * w)3210 static void info_win_disable_msg (struct info_win *w)
3211 {
3212 assert (w != NULL);
3213
3214 if (w->current_message) {
3215 w->current_message->timeout = 0;
3216 info_win_display_msg (w);
3217 }
3218 }
3219
3220 /* Clear all queued messages. */
info_win_clear_msg(struct info_win * w)3221 static void info_win_clear_msg (struct info_win *w)
3222 {
3223 assert (w != NULL);
3224
3225 while (w->queued_message_head) {
3226 struct queued_message *this_msg;
3227
3228 this_msg = w->queued_message_head;
3229 w->queued_message_head = this_msg->next;
3230 queued_message_destroy (this_msg);
3231 }
3232
3233 w->queued_message_total = 0;
3234 w->queued_message_errors = 0;
3235 w->queued_message_tail = NULL;
3236
3237 if (w->current_message) {
3238 queued_message_destroy (w->current_message);
3239 w->current_message = NULL;
3240 }
3241 }
3242
3243 /* Queue a new message for display. */
info_win_msg(struct info_win * w,const char * msg,enum message_type msg_type,const char * prompt,t_user_reply_callback * callback,void * data)3244 static void info_win_msg (struct info_win *w, const char *msg,
3245 enum message_type msg_type, const char *prompt,
3246 t_user_reply_callback *callback, void *data)
3247 {
3248 struct queued_message *this_msg;
3249
3250 assert (w != NULL);
3251 assert (msg != NULL || prompt != NULL);
3252
3253 this_msg = queued_message_create (msg_type);
3254 if (msg)
3255 this_msg->msg = xstrdup (msg);
3256 if (prompt)
3257 this_msg->prompt = xstrdup (prompt);
3258 this_msg->timeout = options_get_int ("MessageLingerTime");
3259 this_msg->callback = callback;
3260 this_msg->data = data;
3261
3262 if (w->queued_message_head) {
3263 w->queued_message_tail->next = this_msg;
3264 w->queued_message_tail = this_msg;
3265 } else {
3266 w->queued_message_head = this_msg;
3267 w->queued_message_tail = this_msg;
3268 }
3269 w->queued_message_total += 1;
3270 if (msg_type == ERROR_MSG)
3271 w->queued_message_errors += 1;
3272
3273 info_win_display_msg (w);
3274 }
3275
iface_win_user_reply(struct info_win * w,const char * reply)3276 static void iface_win_user_reply (struct info_win *w, const char *reply)
3277 {
3278 assert (w != NULL);
3279
3280 if (w->callback)
3281 w->callback (reply, w->data);
3282 }
3283
info_win_user_history_add(struct info_win * w,const char * text)3284 static void info_win_user_history_add (struct info_win *w, const char *text)
3285 {
3286 assert (w != NULL);
3287
3288 entry_history_add (&w->user_history, text);
3289 }
3290
info_win_set_channels(struct info_win * w,const int channels)3291 static void info_win_set_channels (struct info_win *w, const int channels)
3292 {
3293 assert (w != NULL);
3294 assert (channels == 1 || channels == 2);
3295
3296 w->state_stereo = (channels == 2) ? 1 : 0;
3297 info_win_draw_options_state (w);
3298 }
3299
info_win_in_entry(const struct info_win * w)3300 static int info_win_in_entry (const struct info_win *w)
3301 {
3302 assert (w != NULL);
3303
3304 return w->in_entry;
3305 }
3306
info_win_get_entry_type(const struct info_win * w)3307 static enum entry_type info_win_get_entry_type (const struct info_win *w)
3308 {
3309 assert (w != NULL);
3310 assert (w->in_entry);
3311
3312 return entry_get_type (&w->entry);
3313 }
3314
info_win_set_option_state(struct info_win * w,const char * name,const int value)3315 static void info_win_set_option_state (struct info_win *w, const char *name,
3316 const int value)
3317 {
3318 assert (w != NULL);
3319 assert (name != NULL);
3320
3321 if (!strcasecmp(name, "Shuffle"))
3322 w->state_shuffle = value;
3323 else if (!strcasecmp(name, "Repeat"))
3324 w->state_repeat = value;
3325 else if (!strcasecmp(name, "AutoNext"))
3326 w->state_next = value;
3327 else if (!strcasecmp(name, "Net"))
3328 w->state_net = value;
3329 else
3330 abort ();
3331
3332 info_win_draw_options_state (w);
3333 }
3334
3335 /* Convert time in second to min:sec text format(for total time in playlist).
3336 * buff must be 10 chars long. */
sec_to_min_plist(char * buff,const int seconds)3337 static void sec_to_min_plist (char *buff, const int seconds)
3338 {
3339 assert (seconds >= 0);
3340 if (seconds < 999 * 60 * 60 - 1) {
3341
3342 /* the time is less than 999 * 60 minutes */
3343 int hour, min, sec;
3344 hour = seconds / 3600;
3345 min = (seconds / 60) % 60;
3346 sec = seconds % 60;
3347
3348 snprintf (buff, 10, "%03d:%02d:%02d", hour, min, sec);
3349 }
3350 else
3351 strcpy (buff, "!!!!!!!!!");
3352 }
3353
info_win_draw_files_time(const struct info_win * w)3354 static void info_win_draw_files_time (const struct info_win *w)
3355 {
3356 assert (w != NULL);
3357
3358 if (!w->in_entry && !w->too_small) {
3359 char buf[10];
3360
3361 sec_to_min_plist (buf, w->plist_time);
3362 wmove (w->win, 0, COLS - 12);
3363 wattrset (w->win, get_color(CLR_PLIST_TIME));
3364 waddch (w->win, w->plist_time_for_all ? ' ' : '>');
3365 xwaddstr (w->win, buf);
3366 info_win_update_curs (w);
3367 }
3368 }
3369
3370 /* Set the total time for files in the displayed menu. If time_for_all
3371 * has a non zero value, the time is for all files. */
info_win_set_files_time(struct info_win * w,const int time,const int time_for_all)3372 static void info_win_set_files_time (struct info_win *w, const int time,
3373 const int time_for_all)
3374 {
3375 assert (w != NULL);
3376
3377 w->plist_time = time;
3378 w->plist_time_for_all = time_for_all;
3379
3380 info_win_draw_files_time (w);
3381 }
3382
3383 /* Update the message timeout, redraw the window if needed. */
info_win_tick(struct info_win * w)3384 static void info_win_tick (struct info_win *w)
3385 {
3386 info_win_display_msg (w);
3387 }
3388
3389 /* Draw static elements of info_win: frames, legend etc. */
info_win_draw_static_elements(const struct info_win * w)3390 static void info_win_draw_static_elements (const struct info_win *w)
3391 {
3392 assert (w != NULL);
3393
3394 if (!w->too_small) {
3395 /* window frame */
3396 wattrset (w->win, get_color(CLR_FRAME));
3397 wborder (w->win, lines.vert, lines.vert, lines.horiz, lines.horiz,
3398 lines.ltee, lines.rtee, lines.llcorn, lines.lrcorn);
3399
3400 /* mixer frame */
3401 mvwaddch (w->win, 0, COLS - 38, lines.rtee);
3402 mvwaddch (w->win, 0, COLS - 17, lines.ltee);
3403
3404 /* playlist time frame */
3405 mvwaddch (w->win, 0, COLS - 13, lines.rtee);
3406 mvwaddch (w->win, 0, COLS - 2, lines.ltee);
3407
3408 /* total time frames */
3409 wattrset (w->win, get_color(CLR_TIME_TOTAL_FRAMES));
3410 mvwaddch (w->win, 2, 13, '[');
3411 mvwaddch (w->win, 2, 19, ']');
3412
3413 /* time bar frame */
3414 wattrset (w->win, get_color(CLR_FRAME));
3415 mvwaddch (w->win, 3, COLS - 2, lines.ltee);
3416 mvwaddch (w->win, 3, 1, lines.rtee);
3417
3418 /* status line frame */
3419 mvwaddch (w->win, 0, 5, lines.rtee);
3420 mvwaddch (w->win, 0, 5 + sizeof(w->status_msg), lines.ltee);
3421
3422 /* rate and bitrate units */
3423 wmove (w->win, 2, 25);
3424 wattrset (w->win, get_color(CLR_LEGEND));
3425 xwaddstr (w->win, "kHz kbps");
3426 }
3427
3428 info_win_update_curs (w);
3429 }
3430
info_win_draw(const struct info_win * w)3431 static void info_win_draw (const struct info_win *w)
3432 {
3433 assert (w != NULL);
3434
3435 if (!w->too_small) {
3436 info_win_draw_static_elements (w);
3437 info_win_draw_state (w);
3438 info_win_draw_time (w);
3439 info_win_draw_block (w);
3440 info_win_draw_title (w);
3441 info_win_draw_options_state (w);
3442 info_win_draw_status (w);
3443 info_win_draw_files_in_queue (w);
3444 info_win_draw_files_time (w);
3445 info_win_draw_bitrate (w);
3446 info_win_draw_rate (w);
3447
3448 if (w->in_entry)
3449 entry_draw (&w->entry, w->win, 1, 0);
3450 else
3451 bar_draw (&w->mixer_bar, w->win, COLS - 37, 0);
3452
3453 bar_draw (&w->time_bar, w->win, 2, 3);
3454 }
3455 info_win_update_curs (w);
3456 }
3457
info_win_entry_disable(struct info_win * w)3458 static void info_win_entry_disable (struct info_win *w)
3459 {
3460 assert (w != NULL);
3461 assert (w->in_entry);
3462
3463 entry_destroy (&w->entry);
3464 w->in_entry = 0;
3465
3466 if (!options_get_int("UseCursorSelection"))
3467 curs_set (0);
3468 info_win_draw (w);
3469 }
3470
3471 /* Handle a key while in entry. main_win is used to update the menu (filter
3472 * only matching items) when ENTRY_SEARCH is used. */
info_win_entry_handle_key(struct info_win * iw,struct main_win * mw,const struct iface_key * k)3473 static void info_win_entry_handle_key (struct info_win *iw, struct main_win *mw,
3474 const struct iface_key *k)
3475 {
3476 enum key_cmd cmd;
3477 enum entry_type type;
3478
3479 assert (iw != NULL);
3480 assert (mw != NULL);
3481 assert (iw->in_entry);
3482
3483 cmd = get_key_cmd (CON_ENTRY, k);
3484 type = entry_get_type (&iw->entry);
3485
3486 if (type == ENTRY_SEARCH) {
3487 char *text;
3488
3489 if (k->type == IFACE_KEY_CHAR) {
3490 if (iswprint(k->key.ucs)) {
3491 entry_add_char (&iw->entry, k->key.ucs);
3492 text = entry_get_text (&iw->entry);
3493 if (!main_win_menu_filter(mw, text))
3494 entry_back_space (&iw->entry);
3495 free (text);
3496 }
3497
3498 }
3499 else if (k->key.func == KEY_BACKSPACE) {
3500 entry_back_space (&iw->entry);
3501 text = entry_get_text (&iw->entry);
3502 main_win_menu_filter (mw, text);
3503 free (text);
3504 }
3505 else if (cmd == KEY_CMD_CANCEL) {
3506 main_win_clear_filter_menu (mw);
3507 info_win_entry_disable (iw);
3508 }
3509 else {
3510 enum key_cmd cmd = get_key_cmd (CON_MENU, k);
3511
3512 if (cmd == KEY_CMD_MENU_UP
3513 || cmd == KEY_CMD_MENU_DOWN
3514 || cmd == KEY_CMD_MENU_NPAGE
3515 || cmd == KEY_CMD_MENU_PPAGE
3516 || cmd == KEY_CMD_MENU_FIRST
3517 || cmd == KEY_CMD_MENU_LAST)
3518 main_win_menu_cmd (mw, cmd);
3519 }
3520 }
3521 else {
3522 if (k->type == IFACE_KEY_CHAR) {
3523 if (iswprint(k->key.ucs))
3524 entry_add_char (&iw->entry, k->key.ucs);
3525 }
3526 else if (k->key.func == KEY_LEFT)
3527 entry_curs_left (&iw->entry);
3528 else if (k->key.func == KEY_RIGHT)
3529 entry_curs_right (&iw->entry);
3530 else if (k->key.func == KEY_BACKSPACE)
3531 entry_back_space (&iw->entry);
3532 else if (k->key.func == KEY_DC)
3533 entry_del_char (&iw->entry);
3534 else if (k->key.func == KEY_HOME)
3535 entry_home (&iw->entry);
3536 else if (k->key.func == KEY_END)
3537 entry_end (&iw->entry);
3538 else if (cmd == KEY_CMD_CANCEL) {
3539 info_win_entry_disable (iw);
3540 if (type == ENTRY_USER_QUERY)
3541 iface_user_reply (NULL);
3542 }
3543 else if ((type == ENTRY_GO_DIR || type == ENTRY_GO_URL
3544 || type == ENTRY_ADD_URL || type == ENTRY_USER_QUERY)
3545 && cmd != KEY_CMD_WRONG) {
3546 if (cmd == KEY_CMD_HISTORY_UP)
3547 entry_set_history_up (&iw->entry);
3548 else if (cmd == KEY_CMD_HISTORY_DOWN)
3549 entry_set_history_down (&iw->entry);
3550 else if (cmd == KEY_CMD_DELETE_START)
3551 entry_del_to_start (&iw->entry);
3552 else if (cmd == KEY_CMD_DELETE_END)
3553 entry_del_to_end (&iw->entry);
3554 }
3555 }
3556
3557 if (iw->in_entry) /* the entry could be disabled above */
3558 entry_draw (&iw->entry, iw->win, 1, 0);
3559 }
3560
info_win_entry_set_text(struct info_win * w,const char * text)3561 static void info_win_entry_set_text (struct info_win *w, const char *text)
3562 {
3563 assert (w != NULL);
3564 assert (text != NULL);
3565 assert (w->in_entry);
3566
3567 entry_set_text (&w->entry, text);
3568 entry_draw (&w->entry, w->win, 1, 0);
3569 }
3570
info_win_entry_get_text(const struct info_win * w)3571 static char *info_win_entry_get_text (const struct info_win *w)
3572 {
3573 assert (w != NULL);
3574 assert (w->in_entry);
3575
3576 return entry_get_text (&w->entry);
3577 }
3578
info_win_entry_history_add(struct info_win * w)3579 static void info_win_entry_history_add (struct info_win *w)
3580 {
3581 assert (w != NULL);
3582 assert (w->in_entry);
3583
3584 entry_add_text_to_history (&w->entry);
3585 }
3586
info_win_entry_set_file(struct info_win * w,const char * file)3587 static void info_win_entry_set_file (struct info_win *w, const char *file)
3588 {
3589 assert (w != NULL);
3590 assert (w->in_entry);
3591 assert (file != NULL);
3592
3593 entry_set_file (&w->entry, file);
3594 }
3595
info_win_entry_get_file(const struct info_win * w)3596 static char *info_win_entry_get_file (const struct info_win *w)
3597 {
3598 assert (w != NULL);
3599 assert (w->in_entry);
3600
3601 return entry_get_file (&w->entry);
3602 }
3603
3604 /* Handle terminal size change. */
info_win_resize(struct info_win * w)3605 static void info_win_resize (struct info_win *w)
3606 {
3607 assert (w != NULL);
3608 keypad (w->win, TRUE);
3609 wresize (w->win, 4, COLS);
3610 mvwin (w->win, LINES - 4, 0);
3611 werase (w->win);
3612
3613 bar_resize (&w->mixer_bar, 20);
3614 bar_resize (&w->time_bar, COLS - 4);
3615 info_win_set_block_title (w);
3616
3617 if (w->in_entry)
3618 entry_resize (&w->entry, COLS - 4);
3619
3620 info_win_draw (w);
3621 }
3622
windows_init()3623 void windows_init ()
3624 {
3625 if (getenv ("ESCDELAY") == NULL) {
3626 #ifdef HAVE_SET_ESCDELAY
3627 set_escdelay (25);
3628 #else
3629 setenv ("ESCDELAY", "25", 0);
3630 #endif
3631 }
3632
3633 utf8_init ();
3634 if (!initscr ())
3635 fatal ("Can't initialize terminal!");
3636 screen_initialized = 1;
3637 validate_layouts ();
3638 cbreak ();
3639 noecho ();
3640 if (!options_get_int ("UseCursorSelection"))
3641 curs_set (0);
3642 use_default_colors ();
3643
3644 detect_term ();
3645 detect_screen ();
3646 start_color ();
3647 theme_init (has_xterm);
3648 init_lines ();
3649
3650 main_win_init (&main_win, options_get_list ("Layout1"));
3651 info_win_init (&info_win);
3652
3653 check_term_size (&main_win, &info_win);
3654
3655 main_win_draw (&main_win);
3656 info_win_draw (&info_win);
3657
3658 wnoutrefresh (main_win.win);
3659 wnoutrefresh (info_win.win);
3660 doupdate ();
3661
3662 iface_initialized = 1;
3663 }
3664
windows_reset()3665 void windows_reset ()
3666 {
3667 if (screen_initialized) {
3668
3669 /* endwin() sometimes fails on X-terminals when we get SIGCHLD
3670 * at this moment. Double invocation seems to solve this. */
3671 if (endwin () == ERR && endwin () == ERR)
3672 logit ("endwin() failed!");
3673
3674 /* Make sure that the next line after we exit will be "clear". */
3675 printf ("\n");
3676 fflush (stdout);
3677
3678 }
3679 }
3680
windows_end()3681 void windows_end ()
3682 {
3683 if (iface_initialized) {
3684 iface_initialized = 0;
3685
3686 main_win_destroy (&main_win);
3687 info_win_clear_msg (&info_win);
3688 info_win_destroy (&info_win);
3689
3690 xterm_clear_title ();
3691 screen_clear_title ();
3692 utf8_cleanup ();
3693 }
3694
3695 windows_reset ();
3696
3697 lyrics_cleanup ();
3698 }
3699
iface_refresh_screen()3700 static void iface_refresh_screen ()
3701 {
3702 /* We must do it in proper order to get the right cursor position. */
3703 if (iface_in_entry ()) {
3704 wnoutrefresh (main_win.win);
3705 wnoutrefresh (info_win.win);
3706 }
3707 else {
3708 wnoutrefresh (info_win.win);
3709 wnoutrefresh (main_win.win);
3710 }
3711 doupdate ();
3712 }
3713
3714 /* Set state of the options displayed in the information window. */
iface_set_option_state(const char * name,const int value)3715 void iface_set_option_state (const char *name, const int value)
3716 {
3717 assert (name != NULL);
3718
3719 info_win_set_option_state (&info_win, name, value);
3720 iface_refresh_screen ();
3721 }
3722
3723 /* Set the mixer name. */
iface_set_mixer_name(const char * name)3724 void iface_set_mixer_name (const char *name)
3725 {
3726 assert (name != NULL);
3727
3728 info_win_set_mixer_name (&info_win, name);
3729 iface_refresh_screen ();
3730 }
3731
3732 /* Set the status message in the info window. */
iface_set_status(const char * msg)3733 void iface_set_status (const char *msg)
3734 {
3735 assert (msg != NULL);
3736
3737 if (iface_initialized) {
3738 info_win_set_status (&info_win, msg);
3739 iface_refresh_screen ();
3740 }
3741 }
3742
3743 /* Set the number of files in song queue in the info window */
iface_set_files_in_queue(const int num)3744 void iface_set_files_in_queue (const int num)
3745 {
3746 assert (num >= 0);
3747
3748 if (iface_initialized) {
3749 info_win_set_files_in_queue (&info_win, num);
3750 iface_refresh_screen ();
3751 }
3752 }
3753
iface_show_num_files(const int num)3754 static void iface_show_num_files (const int num)
3755 {
3756 char str[20];
3757
3758 snprintf (str, sizeof(str), "Files: %d", num);
3759 iface_set_status (str);
3760 }
3761
3762 /* Change the content of the directory menu to these files, directories, and
3763 * playlists. */
iface_set_dir_content(const enum iface_menu iface_menu,const struct plist * files,const lists_t_strs * dirs,const lists_t_strs * playlists)3764 void iface_set_dir_content (const enum iface_menu iface_menu,
3765 const struct plist *files, const lists_t_strs *dirs,
3766 const lists_t_strs *playlists)
3767 {
3768 main_win_set_dir_content (&main_win, iface_menu, files, dirs,
3769 playlists);
3770 info_win_set_files_time (&info_win,
3771 main_win_get_files_time(&main_win, iface_menu),
3772 main_win_is_time_for_all(&main_win, iface_menu));
3773
3774 iface_show_num_files (plist_count(files)
3775 + (dirs ? lists_strs_size (dirs) : 0)
3776 + (playlists ? lists_strs_size (playlists) : 0));
3777
3778 iface_refresh_screen ();
3779 }
3780
3781 /* Refreshes all menu structs with updated theme attributes. */
iface_update_attrs()3782 void iface_update_attrs ()
3783 {
3784 size_t ix;
3785
3786 info_win.mixer_bar.fill_color = get_color (CLR_MIXER_BAR_FILL);
3787 info_win.mixer_bar.empty_color = get_color (CLR_MIXER_BAR_EMPTY);
3788 info_win.time_bar.fill_color = get_color (CLR_TIME_BAR_FILL);
3789 info_win.time_bar.empty_color = get_color (CLR_TIME_BAR_EMPTY);
3790
3791 for (ix = 0; ix < ARRAY_SIZE(main_win.menus); ix += 1) {
3792 int item_num;
3793 struct side_menu *m = &main_win.menus[ix];
3794 struct menu *menu = m->menu.list.main;
3795 struct menu_item *mi;
3796
3797 if (m->type == MENU_DIR || m->type == MENU_PLAYLIST) {
3798 menu_set_info_attr_normal (menu, get_color (CLR_MENU_ITEM_INFO));
3799 menu_set_info_attr_sel (menu, get_color (CLR_MENU_ITEM_INFO_SELECTED));
3800 menu_set_info_attr_marked (menu, get_color (CLR_MENU_ITEM_INFO_MARKED));
3801 menu_set_info_attr_sel_marked (menu, get_color (CLR_MENU_ITEM_INFO_MARKED_SELECTED));
3802
3803 for (mi = menu->items, item_num = 0;
3804 mi && item_num < menu->nitems;
3805 mi = mi->next, item_num += 1) {
3806 if (mi->type == F_DIR) {
3807 menu_item_set_attr_normal (mi, get_color (CLR_MENU_ITEM_DIR));
3808 menu_item_set_attr_sel (mi, get_color (CLR_MENU_ITEM_DIR_SELECTED));
3809 }
3810 else if (mi->type == F_PLAYLIST) {
3811 menu_item_set_attr_normal (mi, get_color (CLR_MENU_ITEM_PLAYLIST));
3812 menu_item_set_attr_sel (mi, get_color (CLR_MENU_ITEM_PLAYLIST_SELECTED));
3813 }
3814 else {
3815 menu_item_set_attr_normal (mi, get_color (CLR_MENU_ITEM_FILE));
3816 menu_item_set_attr_sel (mi, get_color (CLR_MENU_ITEM_FILE_SELECTED));
3817 }
3818 }
3819 }
3820 else {
3821 menu_set_info_attr_normal (menu, get_color (CLR_MENU_ITEM_FILE));
3822 menu_set_info_attr_sel (menu, get_color (CLR_MENU_ITEM_FILE_SELECTED));
3823
3824 for (mi = menu->items, item_num = 0;
3825 mi && item_num < menu->nitems;
3826 mi = mi->next, item_num += 1) {
3827 menu_item_set_attr_normal (mi, get_color (CLR_MENU_ITEM_FILE));
3828 menu_item_set_attr_sel (mi, get_color (CLR_MENU_ITEM_FILE_SELECTED));
3829 }
3830 }
3831 }
3832 }
3833
iface_update_theme_selection(const char * file)3834 void iface_update_theme_selection (const char *file)
3835 {
3836 /* menus[2] is theme menu. */
3837 assert (main_win.menus[2].menu.list.main->selected);
3838
3839 menu_setcurritem_file (main_win.menus[2].menu.list.main, file);
3840 }
3841
3842 /* Like iface_set_dir_content(), but before replacing the menu content, save
3843 * the menu state (selected file, view position) and restore it after making
3844 * a new menu. */
iface_update_dir_content(const enum iface_menu iface_menu,const struct plist * files,const lists_t_strs * dirs,const lists_t_strs * playlists)3845 void iface_update_dir_content (const enum iface_menu iface_menu,
3846 const struct plist *files, const lists_t_strs *dirs,
3847 const lists_t_strs *playlists)
3848 {
3849 main_win_update_dir_content (&main_win, iface_menu, files, dirs,
3850 playlists);
3851 info_win_set_files_time (&info_win,
3852 main_win_get_files_time(&main_win, iface_menu),
3853 main_win_is_time_for_all(&main_win, iface_menu));
3854
3855 iface_show_num_files (plist_count(files)
3856 + (dirs ? lists_strs_size (dirs) : 0)
3857 + (playlists ? lists_strs_size (playlists) : 0));
3858
3859 iface_refresh_screen ();
3860 }
3861
3862 /* Update item title and time in the menu. */
iface_update_item(const enum iface_menu menu,const struct plist * plist,const int n)3863 void iface_update_item (const enum iface_menu menu,
3864 const struct plist *plist, const int n)
3865 {
3866 assert (plist != NULL);
3867
3868 main_win_update_item (&main_win, menu, plist, n);
3869 info_win_set_files_time (&info_win,
3870 main_win_get_curr_files_time(&main_win),
3871 main_win_is_curr_time_for_all(&main_win));
3872 iface_refresh_screen ();
3873 }
3874
3875 /* Change the current item in the directory menu to this item. */
iface_set_curr_item_title(const char * title)3876 void iface_set_curr_item_title (const char *title)
3877 {
3878 assert (title != NULL);
3879
3880 main_win_set_curr_item_title (&main_win, title);
3881 iface_refresh_screen ();
3882 }
3883
3884 /* Set the title for the directory menu. */
iface_set_title(const enum iface_menu menu,const char * title)3885 void iface_set_title (const enum iface_menu menu, const char *title)
3886 {
3887
3888 assert (title != NULL);
3889
3890 if (options_get_int ("FileNamesIconv"))
3891 {
3892 char *conv_title = NULL;
3893 conv_title = files_iconv_str (title);
3894
3895 main_win_set_title (&main_win,
3896 menu == IFACE_MENU_DIR ? MENU_DIR : MENU_PLAYLIST,
3897 conv_title);
3898
3899 free (conv_title);
3900 }
3901 else
3902 {
3903 main_win_set_title (&main_win,
3904 menu == IFACE_MENU_DIR ? MENU_DIR : MENU_PLAYLIST,
3905 title);
3906 }
3907 iface_refresh_screen ();
3908 }
3909
3910 /* Get the char code from the user with meta flag set if necessary. */
iface_get_key(struct iface_key * k)3911 void iface_get_key (struct iface_key *k)
3912 {
3913 wint_t ch;
3914
3915 if ((ch = wgetch(main_win.win)) == (wint_t)ERR)
3916 interface_fatal ("wgetch() failed!");
3917
3918 if (ch < 32 && ch != '\n' && ch != '\t' && ch != KEY_ESCAPE) {
3919 /* Unprintable, generally control sequences */
3920 k->type = IFACE_KEY_FUNCTION;
3921 k->key.func = ch;
3922 }
3923 else if (ch == 0x7f) {
3924 /* Workaround for backspace on many terminals */
3925 k->type = IFACE_KEY_FUNCTION;
3926 k->key.func = KEY_BACKSPACE;
3927 }
3928 else if (ch < 255) {
3929 /* Regular char */
3930 int meta;
3931
3932 #ifdef HAVE_NCURSESW
3933 ungetch (ch);
3934 if (wget_wch(main_win.win, &ch) == ERR)
3935 interface_fatal ("wget_wch() failed!");
3936 #endif
3937
3938 /* Recognize meta sequences */
3939 if (ch == KEY_ESCAPE) {
3940 meta = wgetch (main_win.win);
3941 if (meta != ERR)
3942 ch = meta | META_KEY_FLAG;
3943 k->type = IFACE_KEY_FUNCTION;
3944 k->key.func = ch;
3945 }
3946 else {
3947 k->type = IFACE_KEY_CHAR;
3948 k->key.ucs = ch;
3949 }
3950 }
3951 else {
3952 k->type = IFACE_KEY_FUNCTION;
3953 k->key.func = ch;
3954 }
3955 }
3956
3957 /* Return a non zero value if the key is not a real key - KEY_RESIZE. */
iface_key_is_resize(const struct iface_key * k)3958 int iface_key_is_resize (const struct iface_key *k)
3959 {
3960 return k->type == IFACE_KEY_FUNCTION && k->key.func == KEY_RESIZE;
3961 }
3962
3963 /* Handle a key command for the menu. */
iface_menu_key(const enum key_cmd cmd)3964 void iface_menu_key (const enum key_cmd cmd)
3965 {
3966 main_win_menu_cmd (&main_win, cmd);
3967 iface_refresh_screen ();
3968 }
3969
3970 /* Get the type of the currently selected item. */
iface_curritem_get_type()3971 enum file_type iface_curritem_get_type ()
3972 {
3973 return main_win_curritem_get_type (&main_win);
3974 }
3975
3976 /* Return a non zero value if a directory menu is currently selected. */
iface_in_dir_menu()3977 int iface_in_dir_menu ()
3978 {
3979 return main_win_in_dir_menu (&main_win);
3980 }
3981
3982 /* Return a non zero value if the playlist menu is currently selected. */
iface_in_plist_menu()3983 int iface_in_plist_menu ()
3984 {
3985 return main_win_in_plist_menu (&main_win);
3986 }
3987
3988 /* Return a non zero value if the theme menu is currently selected. */
iface_in_theme_menu()3989 int iface_in_theme_menu ()
3990 {
3991 return main_win_in_theme_menu (&main_win);
3992 }
3993
3994 /* Return the currently selected file (malloc()ed) or NULL if the menu is
3995 * empty. */
iface_get_curr_file()3996 char *iface_get_curr_file ()
3997 {
3998 return main_win_get_curr_file (&main_win);
3999 }
4000
4001 /* Set the current time of playing. */
iface_set_curr_time(const int time)4002 void iface_set_curr_time (const int time)
4003 {
4004 info_win_set_curr_time (&info_win, time);
4005 iface_refresh_screen ();
4006 }
4007
4008 /* Set the total time for the currently played file. */
iface_set_total_time(const int time)4009 void iface_set_total_time (const int time)
4010 {
4011 info_win_set_total_time (&info_win, time);
4012 info_win_set_block (&info_win, -1, -1);
4013 iface_refresh_screen ();
4014 }
4015
4016 /* Set the start and end marks for the currently played file. */
iface_set_block(const int block_start,const int block_end)4017 void iface_set_block (const int block_start, const int block_end)
4018 {
4019 info_win_set_block (&info_win, block_start, block_end);
4020 iface_refresh_screen ();
4021 }
4022
4023 /* Set the state (STATE_(PLAY|STOP|PAUSE)). */
iface_set_state(const int state)4024 void iface_set_state (const int state)
4025 {
4026 info_win_set_state (&info_win, state);
4027 iface_refresh_screen ();
4028 }
4029
4030 /* Set the bitrate (in kbps). 0 or -1 means no bitrate information. */
iface_set_bitrate(const int bitrate)4031 void iface_set_bitrate (const int bitrate)
4032 {
4033 assert (bitrate >= -1);
4034
4035 info_win_set_bitrate (&info_win, bitrate);
4036 iface_refresh_screen ();
4037 }
4038
4039 /* Set the rate (in kHz). 0 or -1 means no rate information. */
iface_set_rate(const int rate)4040 void iface_set_rate (const int rate)
4041 {
4042 assert (rate >= -1);
4043
4044 info_win_set_rate (&info_win, rate);
4045 iface_refresh_screen ();
4046 }
4047
4048 /* Set the number of channels. */
iface_set_channels(const int channels)4049 void iface_set_channels (const int channels)
4050 {
4051 assert (channels == 1 || channels == 2);
4052
4053 info_win_set_channels (&info_win, channels);
4054 iface_refresh_screen ();
4055 }
4056
4057 /* Set the currently played file. If file is NULL, nothing is played. */
iface_set_played_file(const char * file)4058 void iface_set_played_file (const char *file)
4059 {
4060 main_win_set_played_file (&main_win, file);
4061
4062 if (!file) {
4063 info_win_set_played_title (&info_win, NULL);
4064 info_win_set_bitrate (&info_win, -1);
4065 info_win_set_rate (&info_win, -1);
4066 info_win_set_curr_time (&info_win, -1);
4067 info_win_set_total_time (&info_win, -1);
4068 info_win_set_block (&info_win, -1, -1);
4069 info_win_set_option_state (&info_win, "Net", 0);
4070 }
4071 else if (is_url(file)) {
4072 info_win_set_option_state (&info_win, "Net", 1);
4073 }
4074
4075 iface_refresh_screen ();
4076 }
4077
4078 /* Set the title for the currently played file. */
iface_set_played_file_title(const char * title)4079 void iface_set_played_file_title (const char *title)
4080 {
4081 assert (title != NULL);
4082
4083 info_win_set_played_title (&info_win, title);
4084 iface_refresh_screen ();
4085 }
4086
4087 /* Update timeouts, refresh the screen if needed. This should be called at
4088 * least once a second. */
iface_tick()4089 void iface_tick ()
4090 {
4091 info_win_tick (&info_win);
4092 iface_refresh_screen ();
4093 }
4094
iface_set_mixer_value(const int value)4095 void iface_set_mixer_value (const int value)
4096 {
4097 assert (value >= 0);
4098
4099 info_win_set_mixer_value (&info_win, value);
4100 iface_refresh_screen ();
4101 }
4102
4103 /* Switch to the playlist menu. */
iface_switch_to_plist()4104 void iface_switch_to_plist ()
4105 {
4106 main_win_switch_to (&main_win, MENU_PLAYLIST);
4107 info_win_set_files_time (&info_win,
4108 main_win_get_curr_files_time(&main_win),
4109 main_win_is_curr_time_for_all(&main_win));
4110
4111 iface_refresh_screen ();
4112 }
4113
4114 /* Switch to the directory menu. */
iface_switch_to_dir()4115 void iface_switch_to_dir ()
4116 {
4117 main_win_switch_to (&main_win, MENU_DIR);
4118 info_win_set_files_time (&info_win,
4119 main_win_get_curr_files_time(&main_win),
4120 main_win_is_curr_time_for_all(&main_win));
4121
4122 iface_refresh_screen ();
4123 }
4124
4125 /* Add the item from the playlist to the playlist menu. */
iface_add_to_plist(const struct plist * plist,const int num)4126 void iface_add_to_plist (const struct plist *plist, const int num)
4127 {
4128 assert (plist != NULL);
4129
4130 main_win_add_to_plist (&main_win, plist, num);
4131 info_win_set_files_time (&info_win,
4132 main_win_get_curr_files_time(&main_win),
4133 main_win_is_curr_time_for_all(&main_win));
4134
4135 iface_show_num_files (plist_count(plist));
4136
4137 iface_refresh_screen ();
4138 }
4139
4140 /* Display an error message. */
iface_error(const char * msg)4141 void iface_error (const char *msg)
4142 {
4143 if (iface_initialized) {
4144 info_win_msg (&info_win, msg, ERROR_MSG, NULL, NULL, NULL);
4145 iface_refresh_screen ();
4146 }
4147 else
4148 fprintf (stderr, "ERROR: %s", msg);
4149 }
4150
4151 /* Handle screen resizing. */
iface_resize()4152 void iface_resize ()
4153 {
4154 check_term_size (&main_win, &info_win);
4155 validate_layouts ();
4156 endwin ();
4157 refresh ();
4158 main_win_resize (&main_win);
4159 info_win_resize (&info_win);
4160 iface_refresh_screen ();
4161 }
4162
iface_refresh()4163 void iface_refresh ()
4164 {
4165 wclear (main_win.win);
4166 wclear (info_win.win);
4167
4168 main_win_draw (&main_win);
4169 info_win_draw (&info_win);
4170
4171 iface_refresh_screen ();
4172 }
4173
iface_update_show_time()4174 void iface_update_show_time ()
4175 {
4176 main_win_update_show_time (&main_win);
4177 iface_refresh_screen ();
4178 }
4179
iface_update_show_format()4180 void iface_update_show_format ()
4181 {
4182 main_win_update_show_format (&main_win);
4183 iface_refresh_screen ();
4184 }
4185
iface_clear_plist()4186 void iface_clear_plist ()
4187 {
4188 main_win_clear_plist (&main_win);
4189 iface_refresh_screen ();
4190 }
4191
iface_del_plist_item(const char * file)4192 void iface_del_plist_item (const char *file)
4193 {
4194 assert (file != NULL);
4195
4196 main_win_del_plist_item (&main_win, file);
4197 info_win_set_files_time (&info_win,
4198 main_win_get_curr_files_time(&main_win),
4199 main_win_is_curr_time_for_all(&main_win));
4200 iface_refresh_screen ();
4201 }
4202
iface_make_entry(const enum entry_type type)4203 void iface_make_entry (const enum entry_type type)
4204 {
4205 info_win_make_entry (&info_win, type);
4206 iface_refresh_screen ();
4207 }
4208
iface_get_entry_type()4209 enum entry_type iface_get_entry_type ()
4210 {
4211 return info_win_get_entry_type (&info_win);
4212 }
4213
iface_in_entry()4214 int iface_in_entry ()
4215 {
4216 return info_win_in_entry (&info_win);
4217 }
4218
iface_entry_handle_key(const struct iface_key * k)4219 void iface_entry_handle_key (const struct iface_key *k)
4220 {
4221 info_win_entry_handle_key (&info_win, &main_win, k);
4222 iface_refresh_screen ();
4223 }
4224
iface_entry_set_text(const char * text)4225 void iface_entry_set_text (const char *text)
4226 {
4227 assert (text != NULL);
4228
4229 info_win_entry_set_text (&info_win, text);
4230 iface_refresh_screen ();
4231 }
4232
4233 /* Get text from the entry. Returned memory is malloc()ed. */
iface_entry_get_text()4234 char *iface_entry_get_text ()
4235 {
4236 return info_win_entry_get_text (&info_win);
4237 }
4238
iface_entry_history_add()4239 void iface_entry_history_add ()
4240 {
4241 info_win_entry_history_add (&info_win);
4242 }
4243
iface_entry_disable()4244 void iface_entry_disable ()
4245 {
4246 if (iface_get_entry_type() == ENTRY_SEARCH)
4247 main_win_clear_filter_menu (&main_win);
4248 info_win_entry_disable (&info_win);
4249 iface_refresh_screen ();
4250 }
4251
iface_entry_set_file(const char * file)4252 void iface_entry_set_file (const char *file)
4253 {
4254 assert (file != NULL);
4255
4256 info_win_entry_set_file (&info_win, file);
4257 }
4258
4259 /* Returned memory is malloc()ed. */
iface_entry_get_file()4260 char *iface_entry_get_file ()
4261 {
4262 return info_win_entry_get_file (&info_win);
4263 }
4264
iface_message(const char * msg)4265 void iface_message (const char *msg)
4266 {
4267 assert (msg != NULL);
4268
4269 info_win_msg (&info_win, msg, NORMAL_MSG, NULL, NULL, NULL);
4270 iface_refresh_screen ();
4271 }
4272
iface_disable_message()4273 void iface_disable_message ()
4274 {
4275 info_win_disable_msg (&info_win);
4276 iface_refresh_screen ();
4277 }
4278
iface_user_query(const char * msg,const char * prompt,t_user_reply_callback * callback,void * data)4279 void iface_user_query (const char *msg, const char *prompt,
4280 t_user_reply_callback *callback, void *data)
4281 {
4282 assert (prompt != NULL);
4283
4284 info_win_msg (&info_win, msg, QUERY_MSG, prompt, callback, data);
4285 iface_refresh_screen ();
4286 }
4287
iface_user_reply(const char * reply)4288 void iface_user_reply (const char *reply)
4289 {
4290 info_win_disable_msg (&info_win);
4291 iface_win_user_reply (&info_win, reply);
4292 }
4293
iface_user_history_add(const char * text)4294 void iface_user_history_add (const char *text)
4295 {
4296 info_win_user_history_add (&info_win, text);
4297 }
4298
iface_plist_set_total_time(const int time,const int for_all_files)4299 void iface_plist_set_total_time (const int time, const int for_all_files)
4300 {
4301 if (iface_in_plist_menu())
4302 info_win_set_files_time (&info_win, time, for_all_files);
4303 main_win_set_plist_time (&main_win, time, for_all_files);
4304 iface_refresh_screen ();
4305 }
4306
iface_select_file(const char * file)4307 void iface_select_file (const char *file)
4308 {
4309 assert (file != NULL);
4310
4311 main_win_select_file (&main_win, file);
4312 iface_refresh_screen ();
4313 }
4314
iface_in_help()4315 int iface_in_help ()
4316 {
4317 return main_win_in_help (&main_win);
4318 }
4319
iface_switch_to_help()4320 void iface_switch_to_help ()
4321 {
4322 main_win_switch_to_help (&main_win);
4323 iface_refresh_screen ();
4324 }
4325
iface_handle_help_key(const struct iface_key * k)4326 void iface_handle_help_key (const struct iface_key *k)
4327 {
4328 main_win_handle_help_key (&main_win, k);
4329 iface_refresh_screen ();
4330 }
4331
iface_in_lyrics()4332 int iface_in_lyrics ()
4333 {
4334 return main_win_in_lyrics (&main_win);
4335 }
4336
iface_switch_to_lyrics()4337 void iface_switch_to_lyrics ()
4338 {
4339 main_win_switch_to_lyrics (&main_win);
4340 iface_refresh_screen ();
4341 }
4342
iface_handle_lyrics_key(const struct iface_key * k)4343 void iface_handle_lyrics_key (const struct iface_key *k)
4344 {
4345 main_win_handle_lyrics_key (&main_win, k);
4346 iface_refresh_screen ();
4347 }
4348
iface_toggle_layout()4349 void iface_toggle_layout ()
4350 {
4351 static int curr_layout = 1;
4352 char layout_option[10];
4353 lists_t_strs *layout_fmt;
4354
4355 if (++curr_layout > 3)
4356 curr_layout = 1;
4357
4358 sprintf (layout_option, "Layout%d", curr_layout);
4359 layout_fmt = options_get_list (layout_option);
4360 if (lists_strs_empty (layout_fmt)) {
4361 curr_layout = 1;
4362 layout_fmt = options_get_list ("Layout1");
4363 }
4364
4365 main_win_use_layout (&main_win, layout_fmt);
4366 iface_refresh_screen ();
4367 }
4368
iface_toggle_percent()4369 void iface_toggle_percent ()
4370 {
4371 info_win.time_bar.show_pct = !info_win.time_bar.show_pct;
4372 bar_update_title (&info_win.time_bar);
4373 info_win_draw_block (&info_win);
4374 iface_refresh_screen ();
4375 }
4376
iface_swap_plist_items(const char * file1,const char * file2)4377 void iface_swap_plist_items (const char *file1, const char *file2)
4378 {
4379 main_win_swap_plist_items (&main_win, file1, file2);
4380 iface_refresh_screen ();
4381 }
4382
4383 /* Make sure that this file in this menu is visible. */
iface_make_visible(const enum iface_menu menu,const char * file)4384 void iface_make_visible (const enum iface_menu menu, const char *file)
4385 {
4386 assert (file != NULL);
4387
4388 main_win_make_visible (&main_win,
4389 menu == IFACE_MENU_DIR ? MENU_DIR : MENU_PLAYLIST,
4390 file);
4391 iface_refresh_screen ();
4392 }
4393
iface_switch_to_theme_menu()4394 void iface_switch_to_theme_menu ()
4395 {
4396 main_win_create_themes_menu (&main_win);
4397 main_win_switch_to (&main_win, MENU_THEMES);
4398 iface_refresh_screen ();
4399 }
4400
4401 /* Add a file to the current menu. */
iface_add_file(const char * file,const char * title,const enum file_type type)4402 void iface_add_file (const char *file, const char *title,
4403 const enum file_type type)
4404 {
4405 assert (file != NULL);
4406 assert (file != NULL);
4407
4408 main_win_add_file (&main_win, file, title, type);
4409 iface_refresh_screen ();
4410 }
4411
4412 /* Temporary exit the interface (ncurses mode). */
iface_temporary_exit()4413 void iface_temporary_exit ()
4414 {
4415 endwin ();
4416 }
4417
4418 /* Restore the interface after iface_temporary_exit(). */
iface_restore()4419 void iface_restore ()
4420 {
4421 iface_refresh ();
4422 if (!options_get_int("UseCursorSelection"))
4423 curs_set (0);
4424 }
4425
iface_load_lyrics(const char * file)4426 void iface_load_lyrics (const char *file)
4427 {
4428 lyrics_cleanup ();
4429 lyrics_autoload (file);
4430 main_win.lyrics_screen_top = 0;
4431 main_win_draw (&main_win);
4432 }
4433
update_queue_position(struct plist * playlist,struct plist * dir_list,const char * file,const int pos)4434 static void update_queue_position (struct plist *playlist,
4435 struct plist *dir_list, const char *file, const int pos)
4436 {
4437 int i;
4438
4439 assert (file != NULL);
4440 assert (pos >= 0);
4441
4442 if (playlist && (i = plist_find_fname(playlist, file)) != -1) {
4443 playlist->items[i].queue_pos = pos;
4444 main_win_update_item (&main_win, IFACE_MENU_PLIST,
4445 playlist, i);
4446 }
4447
4448 if (dir_list && (i = plist_find_fname(dir_list, file)) != -1) {
4449 dir_list->items[i].queue_pos = pos;
4450 main_win_update_item (&main_win, IFACE_MENU_DIR,
4451 dir_list, i);
4452 }
4453 }
4454
4455 /* Update queue positions in the playlist and directory menus. Only those items
4456 * which are in the queue (and whose position has therefore changed are
4457 * updated. One exception is the item which was deleted from the queue --
4458 * this one can be passed as the deleted_file parameter */
iface_update_queue_positions(const struct plist * queue,struct plist * playlist,struct plist * dir_list,const char * deleted_file)4459 void iface_update_queue_positions (const struct plist *queue,
4460 struct plist *playlist, struct plist *dir_list,
4461 const char *deleted_file)
4462 {
4463 int i;
4464 int pos = 1;
4465
4466 assert (queue != NULL);
4467
4468 for (i = 0; i < queue->num; i++) {
4469 if (!plist_deleted(queue,i)) {
4470 update_queue_position (playlist, dir_list,
4471 queue->items[i].file, pos);
4472 pos++;
4473 }
4474 }
4475
4476 if (deleted_file)
4477 update_queue_position (playlist, dir_list, deleted_file, 0);
4478
4479 iface_refresh_screen ();
4480 }
4481
4482 /* Clear the queue -- zero the queue positions in playlist and directory
4483 * menus. */
iface_clear_queue_positions(const struct plist * queue,struct plist * playlist,struct plist * dir_list)4484 void iface_clear_queue_positions (const struct plist *queue,
4485 struct plist *playlist, struct plist *dir_list)
4486 {
4487 int i;
4488
4489 assert (queue != NULL);
4490 assert (playlist != NULL);
4491 assert (dir_list != NULL);
4492
4493 for (i = 0; i < queue->num; i++) {
4494 if (!plist_deleted(queue,i)) {
4495 update_queue_position (playlist, dir_list,
4496 queue->items[i].file, 0);
4497 }
4498 }
4499
4500 iface_refresh_screen ();
4501 }
4502
iface_update_queue_position_last(const struct plist * queue,struct plist * playlist,struct plist * dir_list)4503 void iface_update_queue_position_last (const struct plist *queue,
4504 struct plist *playlist, struct plist *dir_list)
4505 {
4506 int i;
4507 int pos;
4508
4509 assert (queue != NULL);
4510
4511 i = plist_last (queue);
4512 pos = plist_get_position (queue, i);
4513 update_queue_position (playlist, dir_list, queue->items[i].file, pos);
4514 iface_refresh_screen ();
4515 }
4516