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