Don't allow saving if tracks are being added
[cmus.git] / options.c
blobaf80821cd63fec53494a1ce0a1840165896d4f79
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 <errno.h>
29 #if defined(__sun__)
30 #include <ncurses.h>
31 #else
32 #include <curses.h>
33 #endif
35 /* initialized option variables */
37 char *output_plugin = NULL;
38 char *status_display_program = NULL;
39 char *server_password;
40 int auto_reshuffle = 0;
41 int confirm_run = 1;
42 int show_hidden = 0;
43 int show_remaining_time = 0;
44 int set_term_title = 1;
45 int play_library = 1;
46 int repeat = 0;
47 int shuffle = 0;
49 int colors[NR_COLORS] = {
50 -1,
51 -1,
52 COLOR_RED | BRIGHT,
53 COLOR_YELLOW | BRIGHT,
55 COLOR_BLUE,
56 COLOR_WHITE,
57 COLOR_BLACK,
58 COLOR_BLUE,
60 COLOR_WHITE | BRIGHT,
61 -1,
62 COLOR_YELLOW | BRIGHT,
63 COLOR_BLUE,
65 COLOR_YELLOW | BRIGHT,
66 COLOR_BLUE | BRIGHT,
67 -1,
68 COLOR_WHITE,
70 COLOR_YELLOW | BRIGHT,
71 COLOR_WHITE,
72 COLOR_BLACK,
73 COLOR_BLUE,
75 COLOR_WHITE | BRIGHT,
76 COLOR_BLUE,
77 COLOR_WHITE | BRIGHT
80 /* uninitialized option variables */
81 char *track_win_format = NULL;
82 char *track_win_alt_format = NULL;
83 char *list_win_format = NULL;
84 char *list_win_alt_format = NULL;
85 char *current_format = NULL;
86 char *current_alt_format = NULL;
87 char *window_title_format = NULL;
88 char *window_title_alt_format = NULL;
89 char *id3_default_charset = NULL;
91 static void buf_int(char *buf, int val)
93 snprintf(buf, OPTION_MAX_SIZE, "%d", val);
96 static int parse_int(const char *buf, int minval, int maxval, int *val)
98 long int tmp;
100 if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
101 error_msg("integer in range %d..%d expected", minval, maxval);
102 return 0;
104 *val = tmp;
105 return 1;
108 int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
110 long int tmp;
111 int i;
113 if (str_to_int(buf, &tmp) == 0) {
114 if (tmp < minval || tmp > maxval)
115 goto err;
116 *val = tmp;
117 return 1;
120 for (i = 0; names[i]; i++) {
121 if (strcasecmp(buf, names[i]) == 0) {
122 *val = i + minval;
123 return 1;
126 err:
127 error_msg("name or integer in range %d..%d expected", minval, maxval);
128 return 0;
131 static const char * const bool_names[] = {
132 "false", "true", NULL
135 static int parse_bool(const char *buf, int *val)
137 return parse_enum(buf, 0, 1, bool_names, val);
140 /* this is used as id in struct cmus_opt */
141 enum format_id {
142 FMT_CURRENT_ALT,
143 FMT_PLAYLIST_ALT,
144 FMT_TITLE_ALT,
145 FMT_TRACKWIN_ALT,
146 FMT_CURRENT,
147 FMT_PLAYLIST,
148 FMT_TITLE,
149 FMT_TRACKWIN
151 #define NR_FMTS 8
153 /* callbacks for normal options {{{ */
155 #define SECOND_SIZE (44100 * 16 / 8 * 2)
156 static void get_buffer_seconds(unsigned int id, char *buf)
158 buf_int(buf, (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) / SECOND_SIZE);
161 static void set_buffer_seconds(unsigned int id, const char *buf)
163 int sec;
165 if (parse_int(buf, 1, 20, &sec))
166 player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
169 static void get_id3_default_charset(unsigned int id, char *buf)
171 strcpy(buf, id3_default_charset);
174 static void set_id3_default_charset(unsigned int id, const char *buf)
176 free(id3_default_charset);
177 id3_default_charset = xstrdup(buf);
180 static const char * const valid_sort_keys[] = {
181 "artist",
182 "album",
183 "title",
184 "tracknumber",
185 "discnumber",
186 "date",
187 "genre",
188 "filename",
189 "albumartist",
190 NULL
193 static const char **parse_sort_keys(const char *value)
195 const char **keys;
196 const char *s, *e;
197 int size = 4;
198 int pos = 0;
200 size = 4;
201 keys = xnew(const char *, size);
203 s = value;
204 while (1) {
205 char buf[32];
206 int i, len;
208 while (*s == ' ')
209 s++;
211 e = s;
212 while (*e && *e != ' ')
213 e++;
215 len = e - s;
216 if (len == 0)
217 break;
218 if (len > 31)
219 len = 31;
221 memcpy(buf, s, len);
222 buf[len] = 0;
223 s = e;
225 for (i = 0; ; i++) {
226 if (valid_sort_keys[i] == NULL) {
227 error_msg("invalid sort key '%s'", buf);
228 free(keys);
229 return NULL;
232 if (strcmp(buf, valid_sort_keys[i]) == 0)
233 break;
236 if (pos == size - 1) {
237 size *= 2;
238 keys = xrenew(const char *, keys, size);
240 keys[pos++] = valid_sort_keys[i];
242 keys[pos] = NULL;
243 return keys;
246 static void get_lib_sort(unsigned int id, char *buf)
248 strcpy(buf, lib_editable.sort_str);
251 static void set_lib_sort(unsigned int id, const char *buf)
253 const char **keys = parse_sort_keys(buf);
255 if (keys)
256 editable_set_sort_keys(&lib_editable, keys);
259 static void get_pl_sort(unsigned int id, char *buf)
261 strcpy(buf, pl_editable.sort_str);
264 static void set_pl_sort(unsigned int id, const char *buf)
266 const char **keys = parse_sort_keys(buf);
268 if (keys)
269 editable_set_sort_keys(&pl_editable, keys);
272 static void get_output_plugin(unsigned int id, char *buf)
274 char *value = player_get_op();
276 if (value)
277 strcpy(buf, value);
278 free(value);
281 static void set_output_plugin(unsigned int id, const char *buf)
283 if (ui_initialized) {
284 player_set_op(buf);
285 } else {
286 /* must set it later manually */
287 output_plugin = xstrdup(buf);
291 static void get_passwd(unsigned int id, char *buf)
293 if (server_password)
294 strcpy(buf, server_password);
297 static void set_passwd(unsigned int id, const char *buf)
299 int len = strlen(buf);
301 if (len == 0) {
302 free(server_password);
303 server_password = NULL;
304 } else if (len < 6) {
305 error_msg("unsafe password");
306 } else {
307 free(server_password);
308 server_password = xstrdup(buf);
312 static void get_replaygain_preamp(unsigned int id, char *buf)
314 sprintf(buf, "%f", replaygain_preamp);
317 static void set_replaygain_preamp(unsigned int id, const char *buf)
319 double val;
320 char *end;
322 val = strtod(buf, &end);
323 if (end == buf) {
324 error_msg("floating point number expected (dB)");
325 return;
327 player_set_rg_preamp(val);
330 static void get_softvol_state(unsigned int id, char *buf)
332 sprintf(buf, "%d %d", soft_vol_l, soft_vol_r);
335 static void set_softvol_state(unsigned int id, const char *buf)
337 char buffer[OPTION_MAX_SIZE];
338 char *ptr;
339 long int l, r;
341 strcpy(buffer, buf);
342 ptr = strchr(buffer, ' ');
343 if (!ptr)
344 goto err;
345 while (*ptr == ' ')
346 *ptr++ = 0;
348 if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
349 goto err;
350 if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
351 goto err;
353 if (soft_vol) {
354 /* avoid race condition */
355 player_set_volume(l, r);
356 } else {
357 /* can't use player_set_volume(), no race condition */
358 soft_vol_l = l;
359 soft_vol_r = r;
361 return;
362 err:
363 error_msg("two integers in range 0..100 expected");
366 static void get_status_display_program(unsigned int id, char *buf)
368 if (status_display_program)
369 strcpy(buf, status_display_program);
372 static void set_status_display_program(unsigned int id, const char *buf)
374 free(status_display_program);
375 status_display_program = NULL;
376 if (buf[0])
377 status_display_program = xstrdup(buf);
380 /* }}} */
382 /* callbacks for toggle options {{{ */
384 static void get_auto_reshuffle(unsigned int id, char *buf)
386 strcpy(buf, bool_names[auto_reshuffle]);
389 static void set_auto_reshuffle(unsigned int id, const char *buf)
391 parse_bool(buf, &auto_reshuffle);
394 static void toggle_auto_reshuffle(unsigned int id)
396 auto_reshuffle ^= 1;
399 static void get_continue(unsigned int id, char *buf)
401 strcpy(buf, bool_names[player_cont]);
404 static void set_continue(unsigned int id, const char *buf)
406 if (!parse_bool(buf, &player_cont))
407 return;
408 update_statusline();
411 static void toggle_continue(unsigned int id)
413 player_cont ^= 1;
414 update_statusline();
417 static void get_confirm_run(unsigned int id, char *buf)
419 strcpy(buf, bool_names[confirm_run]);
422 static void set_confirm_run(unsigned int id, const char *buf)
424 parse_bool(buf, &confirm_run);
427 static void toggle_confirm_run(unsigned int id)
429 confirm_run ^= 1;
432 const char * const view_names[NR_VIEWS + 1] = {
433 "tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
436 static void get_play_library(unsigned int id, char *buf)
438 strcpy(buf, bool_names[play_library]);
441 static void set_play_library(unsigned int id, const char *buf)
443 if (!parse_bool(buf, &play_library))
444 return;
445 update_statusline();
448 static void toggle_play_library(unsigned int id)
450 play_library ^= 1;
451 update_statusline();
454 static void get_play_sorted(unsigned int id, char *buf)
456 strcpy(buf, bool_names[play_sorted]);
459 static void set_play_sorted(unsigned int id, const char *buf)
461 int tmp;
463 if (!parse_bool(buf, &tmp))
464 return;
466 play_sorted = tmp;
467 update_statusline();
470 static void toggle_play_sorted(unsigned int id)
472 editable_lock();
473 play_sorted = play_sorted ^ 1;
475 /* shuffle would override play_sorted... */
476 if (play_sorted) {
477 /* play_sorted makes no sense in playlist */
478 play_library = 1;
479 shuffle = 0;
482 editable_unlock();
483 update_statusline();
486 const char * const aaa_mode_names[] = {
487 "all", "artist", "album", NULL
490 static void get_aaa_mode(unsigned int id, char *buf)
492 strcpy(buf, aaa_mode_names[aaa_mode]);
495 static void set_aaa_mode(unsigned int id, const char *buf)
497 int tmp;
499 if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
500 return;
502 aaa_mode = tmp;
503 update_statusline();
506 static void toggle_aaa_mode(unsigned int id)
508 editable_lock();
510 /* aaa mode makes no sense in playlist */
511 play_library = 1;
513 aaa_mode++;
514 aaa_mode %= 3;
515 editable_unlock();
516 update_statusline();
519 static void get_repeat(unsigned int id, char *buf)
521 strcpy(buf, bool_names[repeat]);
524 static void set_repeat(unsigned int id, const char *buf)
526 if (!parse_bool(buf, &repeat))
527 return;
528 update_statusline();
531 static void toggle_repeat(unsigned int id)
533 repeat ^= 1;
534 update_statusline();
537 const char * const replaygain_names[] = {
538 "disabled", "track", "album", NULL
541 static void get_replaygain(unsigned int id, char *buf)
543 strcpy(buf, replaygain_names[replaygain]);
546 static void set_replaygain(unsigned int id, const char *buf)
548 int tmp;
550 if (!parse_enum(buf, 0, 2, replaygain_names, &tmp))
551 return;
552 player_set_rg(tmp);
555 static void toggle_replaygain(unsigned int id)
557 player_set_rg((replaygain + 1) % 3);
560 static void get_replaygain_limit(unsigned int id, char *buf)
562 strcpy(buf, bool_names[replaygain_limit]);
565 static void set_replaygain_limit(unsigned int id, const char *buf)
567 int tmp;
569 if (!parse_bool(buf, &tmp))
570 return;
571 player_set_rg_limit(tmp);
574 static void toggle_replaygain_limit(unsigned int id)
576 player_set_rg_limit(replaygain_limit ^ 1);
579 static void get_show_hidden(unsigned int id, char *buf)
581 strcpy(buf, bool_names[show_hidden]);
584 static void set_show_hidden(unsigned int id, const char *buf)
586 if (!parse_bool(buf, &show_hidden))
587 return;
588 browser_reload();
591 static void toggle_show_hidden(unsigned int id)
593 show_hidden ^= 1;
594 browser_reload();
597 static void get_show_remaining_time(unsigned int id, char *buf)
599 strcpy(buf, bool_names[show_remaining_time]);
602 static void set_show_remaining_time(unsigned int id, const char *buf)
604 if (!parse_bool(buf, &show_remaining_time))
605 return;
606 update_statusline();
609 static void toggle_show_remaining_time(unsigned int id)
611 show_remaining_time ^= 1;
612 update_statusline();
615 static void get_set_term_title(unsigned int id, char *buf)
617 strcpy(buf, bool_names[set_term_title]);
620 static void set_set_term_title(unsigned int id, const char *buf)
622 parse_bool(buf, &set_term_title);
625 static void toggle_set_term_title(unsigned int id)
627 set_term_title ^= 1;
630 static void get_shuffle(unsigned int id, char *buf)
632 strcpy(buf, bool_names[shuffle]);
635 static void set_shuffle(unsigned int id, const char *buf)
637 if (!parse_bool(buf, &shuffle))
638 return;
639 update_statusline();
642 static void toggle_shuffle(unsigned int id)
644 shuffle ^= 1;
645 update_statusline();
648 static void get_softvol(unsigned int id, char *buf)
650 strcpy(buf, bool_names[soft_vol]);
653 static void set_softvol(unsigned int id, const char *buf)
655 int soft;
657 if (!parse_bool(buf, &soft))
658 return;
659 player_set_soft_vol(soft);
662 static void toggle_softvol(unsigned int id)
664 player_set_soft_vol(soft_vol ^ 1);
667 /* }}} */
669 /* special callbacks (id set) {{{ */
671 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
672 "default",
673 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
674 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
675 NULL
678 static void get_color(unsigned int id, char *buf)
680 int val;
682 val = colors[id];
683 if (val < 16) {
684 strcpy(buf, color_enum_names[val + 1]);
685 } else {
686 buf_int(buf, val);
690 static void set_color(unsigned int id, const char *buf)
692 int color;
694 if (!parse_enum(buf, -1, 255, color_enum_names, &color))
695 return;
697 colors[id] = color;
698 update_colors();
699 update_full();
702 static char **id_to_fmt(enum format_id id)
704 switch (id) {
705 case FMT_CURRENT_ALT:
706 return &current_alt_format;
707 case FMT_PLAYLIST_ALT:
708 return &list_win_alt_format;
709 case FMT_TITLE_ALT:
710 return &window_title_alt_format;
711 case FMT_TRACKWIN_ALT:
712 return &track_win_alt_format;
713 case FMT_CURRENT:
714 return &current_format;
715 case FMT_PLAYLIST:
716 return &list_win_format;
717 case FMT_TITLE:
718 return &window_title_format;
719 case FMT_TRACKWIN:
720 return &track_win_format;
722 return NULL;
725 static void get_format(unsigned int id, char *buf)
727 char **fmtp = id_to_fmt(id);
729 strcpy(buf, *fmtp);
732 static void set_format(unsigned int id, const char *buf)
734 char **fmtp = id_to_fmt(id);
736 if (!format_valid(buf)) {
737 error_msg("invalid format string");
738 return;
740 free(*fmtp);
741 *fmtp = xstrdup(buf);
743 update_full();
746 /* }}} */
748 #define DN(name) { #name, get_ ## name, set_ ## name, NULL },
749 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name },
751 static const struct {
752 const char *name;
753 opt_get_cb get;
754 opt_set_cb set;
755 opt_toggle_cb toggle;
756 } simple_options[] = {
757 DT(aaa_mode)
758 DT(auto_reshuffle)
759 DN(buffer_seconds)
760 DT(confirm_run)
761 DT(continue)
762 DN(id3_default_charset)
763 DN(lib_sort)
764 DN(output_plugin)
765 DN(passwd)
766 DN(pl_sort)
767 DT(play_library)
768 DT(play_sorted)
769 DT(repeat)
770 DT(replaygain)
771 DT(replaygain_limit)
772 DN(replaygain_preamp)
773 DT(show_hidden)
774 DT(show_remaining_time)
775 DT(set_term_title)
776 DT(shuffle)
777 DT(softvol)
778 DN(softvol_state)
779 DN(status_display_program)
780 { NULL, NULL, NULL, NULL }
783 static const char * const color_names[NR_COLORS] = {
784 "color_cmdline_bg",
785 "color_cmdline_fg",
786 "color_error",
787 "color_info",
788 "color_separator",
789 "color_statusline_bg",
790 "color_statusline_fg",
791 "color_titleline_bg",
792 "color_titleline_fg",
793 "color_win_bg",
794 "color_win_cur",
795 "color_win_cur_sel_bg",
796 "color_win_cur_sel_fg",
797 "color_win_dir",
798 "color_win_fg",
799 "color_win_inactive_cur_sel_bg",
800 "color_win_inactive_cur_sel_fg",
801 "color_win_inactive_sel_bg",
802 "color_win_inactive_sel_fg",
803 "color_win_sel_bg",
804 "color_win_sel_fg",
805 "color_win_title_bg",
806 "color_win_title_fg"
809 /* default values for the variables which we must initialize but
810 * can't do it statically */
811 static const struct {
812 const char *name;
813 const char *value;
814 } str_defaults[] = {
815 { "altformat_current", " %F " },
816 { "altformat_playlist", " %f%= %d " },
817 { "altformat_title", "%f" },
818 { "altformat_trackwin", " %f%= %d " },
819 { "format_current", " %a - %l - %02n. %t%= %y " },
820 { "format_playlist", " %-20a %02n. %t%= %y %d " },
821 { "format_title", "%a - %l - %t (%y)" },
822 { "format_trackwin", " %02n. %t%= %y %d " },
824 { "lib_sort" , "artist album discnumber tracknumber title filename" },
825 { "pl_sort", "" },
826 { "id3_default_charset","ISO-8859-1" },
827 { NULL, NULL }
830 LIST_HEAD(option_head);
831 int nr_options = 0;
833 void option_add(const char *name, unsigned int id, opt_get_cb get,
834 opt_set_cb set, opt_toggle_cb toggle)
836 struct cmus_opt *opt = xnew(struct cmus_opt, 1);
837 struct list_head *item;
839 opt->name = name;
840 opt->id = id;
841 opt->get = get;
842 opt->set = set;
843 opt->toggle = toggle;
845 item = option_head.next;
846 while (item != &option_head) {
847 struct cmus_opt *o = container_of(item, struct cmus_opt, node);
849 if (strcmp(name, o->name) < 0)
850 break;
851 item = item->next;
853 /* add before item */
854 list_add_tail(&opt->node, item);
855 nr_options++;
858 struct cmus_opt *option_find(const char *name)
860 struct cmus_opt *opt;
862 list_for_each_entry(opt, &option_head, node) {
863 if (strcmp(name, opt->name) == 0)
864 return opt;
866 error_msg("no such option %s", name);
867 return NULL;
870 void option_set(const char *name, const char *value)
872 struct cmus_opt *opt = option_find(name);
874 if (opt)
875 opt->set(opt->id, value);
878 static void get_op_option(unsigned int id, char *buf)
880 char *val = NULL;
882 player_get_op_option(id, &val);
883 if (val) {
884 strcpy(buf, val);
885 free(val);
889 static void set_op_option(unsigned int id, const char *buf)
891 int rc = player_set_op_option(id, buf);
893 if (rc) {
894 char *msg = op_get_error_msg(rc, "setting option");
895 error_msg("%s", msg);
896 free(msg);
900 /* id is ((plugin_index << 16) | option_index) */
901 static void add_op_option(unsigned int id, const char *name)
903 option_add(xstrdup(name), id, get_op_option, set_op_option, NULL);
906 void options_add(void)
908 int i;
910 /* add options */
912 for (i = 0; simple_options[i].name; i++)
913 option_add(simple_options[i].name, 0, simple_options[i].get,
914 simple_options[i].set, simple_options[i].toggle);
916 for (i = 0; i < NR_FMTS; i++)
917 option_add(str_defaults[i].name, i, get_format, set_format, NULL);
919 for (i = 0; i < NR_COLORS; i++)
920 option_add(color_names[i], i, get_color, set_color, NULL);
922 player_for_each_op_option(add_op_option);
925 static int handle_line(void *data, const char *line)
927 run_command(line);
928 return 0;
931 int source_file(const char *filename)
933 return file_for_each_line(filename, handle_line, NULL);
936 void options_load(void)
938 char filename[512];
939 int i;
941 /* initialize those that can't be statically initialized */
942 for (i = 0; str_defaults[i].name; i++)
943 option_set(str_defaults[i].name, str_defaults[i].value);
945 /* load autosave config */
946 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
947 if (source_file(filename) == -1) {
948 const char *def = DATADIR "/cmus/rc";
950 if (errno != ENOENT)
951 error_msg("loading %s: %s", filename, strerror(errno));
953 /* load defaults */
954 if (source_file(def) == -1)
955 die_errno("loading %s", def);
958 /* load optional static config */
959 snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
960 if (source_file(filename) == -1) {
961 if (errno != ENOENT)
962 error_msg("loading %s: %s", filename, strerror(errno));
966 void options_exit(void)
968 struct cmus_opt *opt;
969 struct filter_entry *filt;
970 char filename[512];
971 FILE *f;
972 int i;
974 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
975 f = fopen(filename, "w");
976 if (f == NULL) {
977 warn_errno("creating %s", filename);
978 return;
981 /* save options */
982 list_for_each_entry(opt, &option_head, node) {
983 char buf[OPTION_MAX_SIZE];
985 buf[0] = 0;
986 opt->get(opt->id, buf);
987 fprintf(f, "set %s=%s\n", opt->name, buf);
990 /* save key bindings */
991 for (i = 0; i < NR_CTXS; i++) {
992 struct binding *b = key_bindings[i];
994 while (b) {
995 fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
996 b = b->next;
1000 /* save filters */
1001 list_for_each_entry(filt, &filters_head, node)
1002 fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
1003 fprintf(f, "factivate");
1004 list_for_each_entry(filt, &filters_head, node) {
1005 switch (filt->act_stat) {
1006 case FS_YES:
1007 fprintf(f, " %s", filt->name);
1008 break;
1009 case FS_NO:
1010 fprintf(f, " !%s", filt->name);
1011 break;
1014 fprintf(f, "\n");
1016 fclose(f);