Add xterm-white.theme
[cmus.git] / options.c
blobe9c3a1fca05ba705dd858094e74405157e98ddef
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "options.h"
6 #include "list.h"
7 #include "utils.h"
8 #include "xmalloc.h"
9 #include "player.h"
10 #include "buffer.h"
11 #include "ui_curses.h"
12 #include "format_print.h"
13 #include "cmus.h"
14 #include "misc.h"
15 #include "lib.h"
16 #include "pl.h"
17 #include "browser.h"
18 #include "keys.h"
19 #include "filters.h"
20 #include "command_mode.h"
21 #include "file.h"
22 #include "prog.h"
23 #include "output.h"
24 #include "config/datadir.h"
26 #include <stdio.h>
27 #include <curses.h>
28 #include <errno.h>
30 /* initialized option variables */
32 char *output_plugin = NULL;
33 char *status_display_program = NULL;
34 int auto_reshuffle = 0;
35 int confirm_run = 1;
36 int show_hidden = 0;
37 int show_remaining_time = 0;
38 int play_library = 1;
39 int repeat = 0;
40 int shuffle = 0;
42 int colors[NR_COLORS] = {
43 -1,
44 -1,
45 COLOR_RED | BRIGHT,
46 COLOR_YELLOW | BRIGHT,
48 COLOR_BLUE,
49 COLOR_WHITE,
50 COLOR_BLACK,
51 COLOR_BLUE,
53 COLOR_WHITE | BRIGHT,
54 -1,
55 COLOR_YELLOW | BRIGHT,
56 COLOR_BLUE,
58 COLOR_YELLOW | BRIGHT,
59 COLOR_BLUE | BRIGHT,
60 -1,
61 COLOR_WHITE,
63 COLOR_YELLOW | BRIGHT,
64 COLOR_WHITE,
65 COLOR_BLACK,
66 COLOR_BLUE,
68 COLOR_WHITE | BRIGHT,
69 COLOR_BLUE,
70 COLOR_WHITE | BRIGHT
73 /* uninitialized option variables */
74 char *track_win_format = NULL;
75 char *track_win_alt_format = NULL;
76 char *list_win_format = NULL;
77 char *list_win_alt_format = NULL;
78 char *current_format = NULL;
79 char *current_alt_format = NULL;
80 char *window_title_format = NULL;
81 char *window_title_alt_format = NULL;
82 char *id3_default_charset = NULL;
84 static void buf_int(char *buf, int val)
86 snprintf(buf, OPTION_MAX_SIZE, "%d", val);
89 static int parse_int(const char *buf, int minval, int maxval, int *val)
91 long int tmp;
93 if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
94 error_msg("integer in range %d..%d expected", minval, maxval);
95 return 0;
97 *val = tmp;
98 return 1;
101 int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
103 long int tmp;
104 int i;
106 if (str_to_int(buf, &tmp) == 0) {
107 if (tmp < minval || tmp > maxval)
108 goto err;
109 *val = tmp;
110 return 1;
113 for (i = 0; names[i]; i++) {
114 if (strcasecmp(buf, names[i]) == 0) {
115 *val = i + minval;
116 return 1;
119 err:
120 error_msg("name or integer in range %d..%d expected", minval, maxval);
121 return 0;
124 static const char * const bool_names[] = {
125 "false", "true", NULL
128 static int parse_bool(const char *buf, int *val)
130 return parse_enum(buf, 0, 1, bool_names, val);
133 /* this is used as id in struct cmus_opt */
134 enum format_id {
135 FMT_CURRENT_ALT,
136 FMT_PLAYLIST_ALT,
137 FMT_TITLE_ALT,
138 FMT_TRACKWIN_ALT,
139 FMT_CURRENT,
140 FMT_PLAYLIST,
141 FMT_TITLE,
142 FMT_TRACKWIN
144 #define NR_FMTS 8
146 /* callbacks for normal options {{{ */
148 #define SECOND_SIZE (44100 * 16 / 8 * 2)
149 static void get_buffer_seconds(unsigned int id, char *buf)
151 buf_int(buf, (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) / SECOND_SIZE);
154 static void set_buffer_seconds(unsigned int id, const char *buf)
156 int sec;
158 if (parse_int(buf, 1, 20, &sec))
159 player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
162 static void get_id3_default_charset(unsigned int id, char *buf)
164 strcpy(buf, id3_default_charset);
167 static void set_id3_default_charset(unsigned int id, const char *buf)
169 free(id3_default_charset);
170 id3_default_charset = xstrdup(buf);
173 static const char * const valid_sort_keys[] = {
174 "artist",
175 "album",
176 "title",
177 "tracknumber",
178 "discnumber",
179 "date",
180 "genre",
181 "filename",
182 NULL
185 static const char **parse_sort_keys(const char *value)
187 const char **keys;
188 const char *s, *e;
189 int size = 4;
190 int pos = 0;
192 size = 4;
193 keys = xnew(const char *, size);
195 s = value;
196 while (1) {
197 char buf[32];
198 int i, len;
200 while (*s == ' ')
201 s++;
203 e = s;
204 while (*e && *e != ' ')
205 e++;
207 len = e - s;
208 if (len == 0)
209 break;
210 if (len > 31)
211 len = 31;
213 memcpy(buf, s, len);
214 buf[len] = 0;
215 s = e;
217 for (i = 0; ; i++) {
218 if (valid_sort_keys[i] == NULL) {
219 error_msg("invalid sort key '%s'", buf);
220 free(keys);
221 return NULL;
224 if (strcmp(buf, valid_sort_keys[i]) == 0)
225 break;
228 if (pos == size - 1) {
229 size *= 2;
230 keys = xrenew(const char *, keys, size);
232 keys[pos++] = valid_sort_keys[i];
234 keys[pos] = NULL;
235 return keys;
238 static void get_lib_sort(unsigned int id, char *buf)
240 strcpy(buf, lib_editable.sort_str);
243 static void set_lib_sort(unsigned int id, const char *buf)
245 const char **keys = parse_sort_keys(buf);
247 if (keys)
248 editable_set_sort_keys(&lib_editable, keys);
251 static void get_pl_sort(unsigned int id, char *buf)
253 strcpy(buf, pl_editable.sort_str);
256 static void set_pl_sort(unsigned int id, const char *buf)
258 const char **keys = parse_sort_keys(buf);
260 if (keys)
261 editable_set_sort_keys(&pl_editable, keys);
264 static void get_output_plugin(unsigned int id, char *buf)
266 char *value = player_get_op();
268 if (value)
269 strcpy(buf, value);
270 free(value);
273 static void set_output_plugin(unsigned int id, const char *buf)
275 if (ui_initialized) {
276 player_set_op(buf);
277 } else {
278 /* must set it later manually */
279 output_plugin = xstrdup(buf);
283 static void get_status_display_program(unsigned int id, char *buf)
285 if (status_display_program)
286 strcpy(buf, status_display_program);
289 static void set_status_display_program(unsigned int id, const char *buf)
291 free(status_display_program);
292 status_display_program = NULL;
293 if (buf[0])
294 status_display_program = xstrdup(buf);
297 /* }}} */
299 /* callbacks for toggle options {{{ */
301 static void get_auto_reshuffle(unsigned int id, char *buf)
303 strcpy(buf, bool_names[auto_reshuffle]);
306 static void set_auto_reshuffle(unsigned int id, const char *buf)
308 parse_bool(buf, &auto_reshuffle);
311 static void toggle_auto_reshuffle(unsigned int id)
313 auto_reshuffle ^= 1;
316 static void get_continue(unsigned int id, char *buf)
318 strcpy(buf, bool_names[player_cont]);
321 static void set_continue(unsigned int id, const char *buf)
323 if (!parse_bool(buf, &player_cont))
324 return;
325 update_statusline();
328 static void toggle_continue(unsigned int id)
330 player_cont ^= 1;
331 update_statusline();
334 static void get_confirm_run(unsigned int id, char *buf)
336 strcpy(buf, bool_names[confirm_run]);
339 static void set_confirm_run(unsigned int id, const char *buf)
341 parse_bool(buf, &confirm_run);
344 static void toggle_confirm_run(unsigned int id)
346 confirm_run ^= 1;
349 const char * const view_names[NR_VIEWS + 1] = {
350 "tree", "sorted", "playlist", "queue", "browser", "filters", NULL
353 static void get_play_library(unsigned int id, char *buf)
355 strcpy(buf, bool_names[play_library]);
358 static void set_play_library(unsigned int id, const char *buf)
360 if (!parse_bool(buf, &play_library))
361 return;
362 update_statusline();
365 static void toggle_play_library(unsigned int id)
367 play_library ^= 1;
368 update_statusline();
371 static void get_play_sorted(unsigned int id, char *buf)
373 strcpy(buf, bool_names[play_sorted]);
376 static void set_play_sorted(unsigned int id, const char *buf)
378 int tmp;
380 if (!parse_bool(buf, &tmp))
381 return;
383 play_sorted = tmp;
384 update_statusline();
387 static void toggle_play_sorted(unsigned int id)
389 editable_lock();
390 play_sorted = play_sorted ^ 1;
392 /* shuffle would override play_sorted... */
393 if (play_sorted) {
394 /* play_sorted makes no sense in playlist */
395 play_library = 1;
396 shuffle = 0;
399 editable_unlock();
400 update_statusline();
403 const char * const aaa_mode_names[] = {
404 "all", "artist", "album", NULL
407 static void get_aaa_mode(unsigned int id, char *buf)
409 strcpy(buf, aaa_mode_names[aaa_mode]);
412 static void set_aaa_mode(unsigned int id, const char *buf)
414 int tmp;
416 if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
417 return;
419 aaa_mode = tmp;
420 update_statusline();
423 static void toggle_aaa_mode(unsigned int id)
425 editable_lock();
427 /* aaa mode makes no sense in playlist */
428 play_library = 1;
430 aaa_mode++;
431 aaa_mode %= 3;
432 editable_unlock();
433 update_statusline();
436 static void get_repeat(unsigned int id, char *buf)
438 strcpy(buf, bool_names[repeat]);
441 static void set_repeat(unsigned int id, const char *buf)
443 if (!parse_bool(buf, &repeat))
444 return;
445 update_statusline();
448 static void toggle_repeat(unsigned int id)
450 repeat ^= 1;
451 update_statusline();
454 static void get_show_hidden(unsigned int id, char *buf)
456 strcpy(buf, bool_names[show_hidden]);
459 static void set_show_hidden(unsigned int id, const char *buf)
461 if (!parse_bool(buf, &show_hidden))
462 return;
463 browser_reload();
466 static void toggle_show_hidden(unsigned int id)
468 show_hidden ^= 1;
469 browser_reload();
472 static void get_show_remaining_time(unsigned int id, char *buf)
474 strcpy(buf, bool_names[show_remaining_time]);
477 static void set_show_remaining_time(unsigned int id, const char *buf)
479 if (!parse_bool(buf, &show_remaining_time))
480 return;
481 update_statusline();
484 static void toggle_show_remaining_time(unsigned int id)
486 show_remaining_time ^= 1;
487 update_statusline();
490 static void get_shuffle(unsigned int id, char *buf)
492 strcpy(buf, bool_names[shuffle]);
495 static void set_shuffle(unsigned int id, const char *buf)
497 if (!parse_bool(buf, &shuffle))
498 return;
499 update_statusline();
502 static void toggle_shuffle(unsigned int id)
504 shuffle ^= 1;
505 update_statusline();
508 /* }}} */
510 /* special callbacks (id set) {{{ */
512 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
513 "default",
514 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
515 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
516 NULL
519 static void get_color(unsigned int id, char *buf)
521 int val;
523 val = colors[id];
524 if (val < 16) {
525 strcpy(buf, color_enum_names[val + 1]);
526 } else {
527 buf_int(buf, val);
531 static void set_color(unsigned int id, const char *buf)
533 int color;
535 if (!parse_enum(buf, -1, 255, color_enum_names, &color))
536 return;
538 colors[id] = color;
539 update_colors();
540 update_full();
543 static char **id_to_fmt(enum format_id id)
545 switch (id) {
546 case FMT_CURRENT_ALT:
547 return &current_alt_format;
548 case FMT_PLAYLIST_ALT:
549 return &list_win_alt_format;
550 case FMT_TITLE_ALT:
551 return &window_title_alt_format;
552 case FMT_TRACKWIN_ALT:
553 return &track_win_alt_format;
554 case FMT_CURRENT:
555 return &current_format;
556 case FMT_PLAYLIST:
557 return &list_win_format;
558 case FMT_TITLE:
559 return &window_title_format;
560 case FMT_TRACKWIN:
561 return &track_win_format;
563 return NULL;
566 static void get_format(unsigned int id, char *buf)
568 char **fmtp = id_to_fmt(id);
570 strcpy(buf, *fmtp);
573 static void set_format(unsigned int id, const char *buf)
575 char **fmtp = id_to_fmt(id);
577 if (!format_valid(buf)) {
578 error_msg("invalid format string");
579 return;
581 free(*fmtp);
582 *fmtp = xstrdup(buf);
584 update_full();
587 /* }}} */
589 #define DN(name) { #name, get_ ## name, set_ ## name, NULL },
590 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name },
592 static const struct {
593 const char *name;
594 opt_get_cb get;
595 opt_set_cb set;
596 opt_toggle_cb toggle;
597 } simple_options[] = {
598 DT(aaa_mode)
599 DT(auto_reshuffle)
600 DN(buffer_seconds)
601 DT(confirm_run)
602 DT(continue)
603 DN(id3_default_charset)
604 DN(lib_sort)
605 DN(output_plugin)
606 DN(pl_sort)
607 DT(play_library)
608 DT(play_sorted)
609 DT(repeat)
610 DT(show_hidden)
611 DT(show_remaining_time)
612 DT(shuffle)
613 DN(status_display_program)
614 { NULL, NULL, NULL, NULL }
617 static const char * const color_names[NR_COLORS] = {
618 "color_cmdline_bg",
619 "color_cmdline_fg",
620 "color_error",
621 "color_info",
622 "color_separator",
623 "color_statusline_bg",
624 "color_statusline_fg",
625 "color_titleline_bg",
626 "color_titleline_fg",
627 "color_win_bg",
628 "color_win_cur",
629 "color_win_cur_sel_bg",
630 "color_win_cur_sel_fg",
631 "color_win_dir",
632 "color_win_fg",
633 "color_win_inactive_cur_sel_bg",
634 "color_win_inactive_cur_sel_fg",
635 "color_win_inactive_sel_bg",
636 "color_win_inactive_sel_fg",
637 "color_win_sel_bg",
638 "color_win_sel_fg",
639 "color_win_title_bg",
640 "color_win_title_fg"
643 /* default values for the variables which we must initialize but
644 * can't do it statically */
645 static const struct {
646 const char *name;
647 const char *value;
648 } str_defaults[] = {
649 { "altformat_current", " %F " },
650 { "altformat_playlist", " %f%= %d " },
651 { "altformat_title", "%f" },
652 { "altformat_trackwin", " %f%= %d " },
653 { "format_current", " %a - %l - %02n. %t%= %y " },
654 { "format_playlist", " %a - %l - %02n. %t%= %y %d " },
655 { "format_title", "%a - %l - %t (%y)" },
656 { "format_trackwin", " %02n. %t%= %y %d " },
658 { "lib_sort" , "artist album discnumber tracknumber title filename" },
659 { "pl_sort", "" },
660 { "id3_default_charset","ISO-8859-1" },
661 { NULL, NULL }
664 LIST_HEAD(option_head);
665 int nr_options = 0;
667 void option_add(const char *name, unsigned int id, opt_get_cb get,
668 opt_set_cb set, opt_toggle_cb toggle)
670 struct cmus_opt *opt = xnew(struct cmus_opt, 1);
671 struct list_head *item;
673 opt->name = name;
674 opt->id = id;
675 opt->get = get;
676 opt->set = set;
677 opt->toggle = toggle;
679 item = option_head.next;
680 while (item != &option_head) {
681 struct cmus_opt *o = container_of(item, struct cmus_opt, node);
683 if (strcmp(name, o->name) < 0)
684 break;
685 item = item->next;
687 /* add before item */
688 list_add_tail(&opt->node, item);
689 nr_options++;
692 struct cmus_opt *option_find(const char *name)
694 struct cmus_opt *opt;
696 list_for_each_entry(opt, &option_head, node) {
697 if (strcmp(name, opt->name) == 0)
698 return opt;
700 error_msg("no such option %s", name);
701 return NULL;
704 void option_set(const char *name, const char *value)
706 struct cmus_opt *opt = option_find(name);
708 if (opt)
709 opt->set(opt->id, value);
712 static void get_op_option(unsigned int id, char *buf)
714 char *val = NULL;
716 player_get_op_option(id, &val);
717 if (val) {
718 strcpy(buf, val);
719 free(val);
723 static void set_op_option(unsigned int id, const char *buf)
725 int rc = player_set_op_option(id, buf);
727 if (rc) {
728 char *msg = op_get_error_msg(rc, "setting option");
729 error_msg("%s", msg);
730 free(msg);
734 /* id is ((plugin_index << 16) | option_index) */
735 static void add_op_option(unsigned int id, const char *name)
737 option_add(xstrdup(name), id, get_op_option, set_op_option, NULL);
740 void options_add(void)
742 int i;
744 /* add options */
746 for (i = 0; simple_options[i].name; i++)
747 option_add(simple_options[i].name, 0, simple_options[i].get,
748 simple_options[i].set, simple_options[i].toggle);
750 for (i = 0; i < NR_FMTS; i++)
751 option_add(str_defaults[i].name, i, get_format, set_format, NULL);
753 for (i = 0; i < NR_COLORS; i++)
754 option_add(color_names[i], i, get_color, set_color, NULL);
756 player_for_each_op_option(add_op_option);
759 static int handle_line(void *data, const char *line)
761 run_command(line);
762 return 0;
765 int source_file(const char *filename)
767 return file_for_each_line(filename, handle_line, NULL);
770 void options_load(void)
772 char filename[512];
773 int i;
775 display_errors = 1;
777 /* initialize those that can't be statically initialized */
778 for (i = 0; str_defaults[i].name; i++)
779 option_set(str_defaults[i].name, str_defaults[i].value);
781 /* load autosave config */
782 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
783 if (source_file(filename) == -1) {
784 const char *def = DATADIR "/cmus/rc";
786 if (errno != ENOENT)
787 warn_errno("loading %s", filename);
789 /* load defaults */
790 if (source_file(def) == -1)
791 die_errno("loading %s", def);
794 /* load optional static config */
795 snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
796 if (source_file(filename) == -1) {
797 if (errno != ENOENT)
798 warn_errno("loading %s", filename);
802 void options_exit(void)
804 struct cmus_opt *opt;
805 struct filter_entry *filt;
806 char filename[512];
807 FILE *f;
808 int i;
810 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
811 f = fopen(filename, "w");
812 if (f == NULL) {
813 warn_errno("creating %s", filename);
814 return;
817 /* save options */
818 list_for_each_entry(opt, &option_head, node) {
819 char buf[OPTION_MAX_SIZE];
821 buf[0] = 0;
822 opt->get(opt->id, buf);
823 fprintf(f, "set %s=%s\n", opt->name, buf);
826 /* save key bindings */
827 for (i = 0; i < NR_CTXS; i++) {
828 struct binding *b = key_bindings[i];
830 while (b) {
831 fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
832 b = b->next;
836 /* save filters */
837 list_for_each_entry(filt, &filters_head, node)
838 fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
839 fprintf(f, "factivate");
840 list_for_each_entry(filt, &filters_head, node) {
841 if (filt->active)
842 fprintf(f, " %s", filt->name);
844 fprintf(f, "\n");
846 fclose(f);