Sort playlist file for library views in same order as view 2
[cmus.git] / options.c
blob8628e82a940a74ab56b6453fa2352baabd74cdf0
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 NULL
192 static const char **parse_sort_keys(const char *value)
194 const char **keys;
195 const char *s, *e;
196 int size = 4;
197 int pos = 0;
199 size = 4;
200 keys = xnew(const char *, size);
202 s = value;
203 while (1) {
204 char buf[32];
205 int i, len;
207 while (*s == ' ')
208 s++;
210 e = s;
211 while (*e && *e != ' ')
212 e++;
214 len = e - s;
215 if (len == 0)
216 break;
217 if (len > 31)
218 len = 31;
220 memcpy(buf, s, len);
221 buf[len] = 0;
222 s = e;
224 for (i = 0; ; i++) {
225 if (valid_sort_keys[i] == NULL) {
226 error_msg("invalid sort key '%s'", buf);
227 free(keys);
228 return NULL;
231 if (strcmp(buf, valid_sort_keys[i]) == 0)
232 break;
235 if (pos == size - 1) {
236 size *= 2;
237 keys = xrenew(const char *, keys, size);
239 keys[pos++] = valid_sort_keys[i];
241 keys[pos] = NULL;
242 return keys;
245 static void get_lib_sort(unsigned int id, char *buf)
247 strcpy(buf, lib_editable.sort_str);
250 static void set_lib_sort(unsigned int id, const char *buf)
252 const char **keys = parse_sort_keys(buf);
254 if (keys)
255 editable_set_sort_keys(&lib_editable, keys);
258 static void get_pl_sort(unsigned int id, char *buf)
260 strcpy(buf, pl_editable.sort_str);
263 static void set_pl_sort(unsigned int id, const char *buf)
265 const char **keys = parse_sort_keys(buf);
267 if (keys)
268 editable_set_sort_keys(&pl_editable, keys);
271 static void get_output_plugin(unsigned int id, char *buf)
273 char *value = player_get_op();
275 if (value)
276 strcpy(buf, value);
277 free(value);
280 static void set_output_plugin(unsigned int id, const char *buf)
282 if (ui_initialized) {
283 player_set_op(buf);
284 } else {
285 /* must set it later manually */
286 output_plugin = xstrdup(buf);
290 static void get_passwd(unsigned int id, char *buf)
292 if (server_password)
293 strcpy(buf, server_password);
296 static void set_passwd(unsigned int id, const char *buf)
298 int len = strlen(buf);
300 if (len == 0) {
301 free(server_password);
302 server_password = NULL;
303 } else if (len < 6) {
304 error_msg("unsafe password");
305 } else {
306 free(server_password);
307 server_password = xstrdup(buf);
311 static void get_softvol_state(unsigned int id, char *buf)
313 sprintf(buf, "%d %d", soft_vol_l, soft_vol_r);
316 static void set_softvol_state(unsigned int id, const char *buf)
318 char buffer[OPTION_MAX_SIZE];
319 char *ptr;
320 long int l, r;
322 strcpy(buffer, buf);
323 ptr = strchr(buffer, ' ');
324 if (!ptr)
325 goto err;
326 while (*ptr == ' ')
327 *ptr++ = 0;
329 if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
330 goto err;
331 if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
332 goto err;
334 if (soft_vol) {
335 /* avoid race condition */
336 player_set_volume(l, r);
337 } else {
338 /* can't use player_set_volume(), no race condition */
339 soft_vol_l = l;
340 soft_vol_r = r;
342 return;
343 err:
344 error_msg("two integers in range 0..100 expected");
347 static void get_status_display_program(unsigned int id, char *buf)
349 if (status_display_program)
350 strcpy(buf, status_display_program);
353 static void set_status_display_program(unsigned int id, const char *buf)
355 free(status_display_program);
356 status_display_program = NULL;
357 if (buf[0])
358 status_display_program = xstrdup(buf);
361 /* }}} */
363 /* callbacks for toggle options {{{ */
365 static void get_auto_reshuffle(unsigned int id, char *buf)
367 strcpy(buf, bool_names[auto_reshuffle]);
370 static void set_auto_reshuffle(unsigned int id, const char *buf)
372 parse_bool(buf, &auto_reshuffle);
375 static void toggle_auto_reshuffle(unsigned int id)
377 auto_reshuffle ^= 1;
380 static void get_continue(unsigned int id, char *buf)
382 strcpy(buf, bool_names[player_cont]);
385 static void set_continue(unsigned int id, const char *buf)
387 if (!parse_bool(buf, &player_cont))
388 return;
389 update_statusline();
392 static void toggle_continue(unsigned int id)
394 player_cont ^= 1;
395 update_statusline();
398 static void get_confirm_run(unsigned int id, char *buf)
400 strcpy(buf, bool_names[confirm_run]);
403 static void set_confirm_run(unsigned int id, const char *buf)
405 parse_bool(buf, &confirm_run);
408 static void toggle_confirm_run(unsigned int id)
410 confirm_run ^= 1;
413 const char * const view_names[NR_VIEWS + 1] = {
414 "tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
417 static void get_play_library(unsigned int id, char *buf)
419 strcpy(buf, bool_names[play_library]);
422 static void set_play_library(unsigned int id, const char *buf)
424 if (!parse_bool(buf, &play_library))
425 return;
426 update_statusline();
429 static void toggle_play_library(unsigned int id)
431 play_library ^= 1;
432 update_statusline();
435 static void get_play_sorted(unsigned int id, char *buf)
437 strcpy(buf, bool_names[play_sorted]);
440 static void set_play_sorted(unsigned int id, const char *buf)
442 int tmp;
444 if (!parse_bool(buf, &tmp))
445 return;
447 play_sorted = tmp;
448 update_statusline();
451 static void toggle_play_sorted(unsigned int id)
453 editable_lock();
454 play_sorted = play_sorted ^ 1;
456 /* shuffle would override play_sorted... */
457 if (play_sorted) {
458 /* play_sorted makes no sense in playlist */
459 play_library = 1;
460 shuffle = 0;
463 editable_unlock();
464 update_statusline();
467 const char * const aaa_mode_names[] = {
468 "all", "artist", "album", NULL
471 static void get_aaa_mode(unsigned int id, char *buf)
473 strcpy(buf, aaa_mode_names[aaa_mode]);
476 static void set_aaa_mode(unsigned int id, const char *buf)
478 int tmp;
480 if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
481 return;
483 aaa_mode = tmp;
484 update_statusline();
487 static void toggle_aaa_mode(unsigned int id)
489 editable_lock();
491 /* aaa mode makes no sense in playlist */
492 play_library = 1;
494 aaa_mode++;
495 aaa_mode %= 3;
496 editable_unlock();
497 update_statusline();
500 static void get_repeat(unsigned int id, char *buf)
502 strcpy(buf, bool_names[repeat]);
505 static void set_repeat(unsigned int id, const char *buf)
507 if (!parse_bool(buf, &repeat))
508 return;
509 update_statusline();
512 static void toggle_repeat(unsigned int id)
514 repeat ^= 1;
515 update_statusline();
518 static void get_show_hidden(unsigned int id, char *buf)
520 strcpy(buf, bool_names[show_hidden]);
523 static void set_show_hidden(unsigned int id, const char *buf)
525 if (!parse_bool(buf, &show_hidden))
526 return;
527 browser_reload();
530 static void toggle_show_hidden(unsigned int id)
532 show_hidden ^= 1;
533 browser_reload();
536 static void get_show_remaining_time(unsigned int id, char *buf)
538 strcpy(buf, bool_names[show_remaining_time]);
541 static void set_show_remaining_time(unsigned int id, const char *buf)
543 if (!parse_bool(buf, &show_remaining_time))
544 return;
545 update_statusline();
548 static void toggle_show_remaining_time(unsigned int id)
550 show_remaining_time ^= 1;
551 update_statusline();
554 static void get_set_term_title(unsigned int id, char *buf)
556 strcpy(buf, bool_names[set_term_title]);
559 static void set_set_term_title(unsigned int id, const char *buf)
561 parse_bool(buf, &set_term_title);
564 static void toggle_set_term_title(unsigned int id)
566 set_term_title ^= 1;
569 static void get_shuffle(unsigned int id, char *buf)
571 strcpy(buf, bool_names[shuffle]);
574 static void set_shuffle(unsigned int id, const char *buf)
576 if (!parse_bool(buf, &shuffle))
577 return;
578 update_statusline();
581 static void toggle_shuffle(unsigned int id)
583 shuffle ^= 1;
584 update_statusline();
587 static void get_softvol(unsigned int id, char *buf)
589 strcpy(buf, bool_names[soft_vol]);
592 static void set_softvol(unsigned int id, const char *buf)
594 int soft;
596 if (!parse_bool(buf, &soft))
597 return;
598 player_set_soft_vol(soft);
601 static void toggle_softvol(unsigned int id)
603 player_set_soft_vol(soft_vol ^ 1);
606 /* }}} */
608 /* special callbacks (id set) {{{ */
610 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
611 "default",
612 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
613 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
614 NULL
617 static void get_color(unsigned int id, char *buf)
619 int val;
621 val = colors[id];
622 if (val < 16) {
623 strcpy(buf, color_enum_names[val + 1]);
624 } else {
625 buf_int(buf, val);
629 static void set_color(unsigned int id, const char *buf)
631 int color;
633 if (!parse_enum(buf, -1, 255, color_enum_names, &color))
634 return;
636 colors[id] = color;
637 update_colors();
638 update_full();
641 static char **id_to_fmt(enum format_id id)
643 switch (id) {
644 case FMT_CURRENT_ALT:
645 return &current_alt_format;
646 case FMT_PLAYLIST_ALT:
647 return &list_win_alt_format;
648 case FMT_TITLE_ALT:
649 return &window_title_alt_format;
650 case FMT_TRACKWIN_ALT:
651 return &track_win_alt_format;
652 case FMT_CURRENT:
653 return &current_format;
654 case FMT_PLAYLIST:
655 return &list_win_format;
656 case FMT_TITLE:
657 return &window_title_format;
658 case FMT_TRACKWIN:
659 return &track_win_format;
661 return NULL;
664 static void get_format(unsigned int id, char *buf)
666 char **fmtp = id_to_fmt(id);
668 strcpy(buf, *fmtp);
671 static void set_format(unsigned int id, const char *buf)
673 char **fmtp = id_to_fmt(id);
675 if (!format_valid(buf)) {
676 error_msg("invalid format string");
677 return;
679 free(*fmtp);
680 *fmtp = xstrdup(buf);
682 update_full();
685 /* }}} */
687 #define DN(name) { #name, get_ ## name, set_ ## name, NULL },
688 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name },
690 static const struct {
691 const char *name;
692 opt_get_cb get;
693 opt_set_cb set;
694 opt_toggle_cb toggle;
695 } simple_options[] = {
696 DT(aaa_mode)
697 DT(auto_reshuffle)
698 DN(buffer_seconds)
699 DT(confirm_run)
700 DT(continue)
701 DN(id3_default_charset)
702 DN(lib_sort)
703 DN(output_plugin)
704 DN(passwd)
705 DN(pl_sort)
706 DT(play_library)
707 DT(play_sorted)
708 DT(repeat)
709 DT(show_hidden)
710 DT(show_remaining_time)
711 DT(set_term_title)
712 DT(shuffle)
713 DT(softvol)
714 DN(softvol_state)
715 DN(status_display_program)
716 { NULL, NULL, NULL, NULL }
719 static const char * const color_names[NR_COLORS] = {
720 "color_cmdline_bg",
721 "color_cmdline_fg",
722 "color_error",
723 "color_info",
724 "color_separator",
725 "color_statusline_bg",
726 "color_statusline_fg",
727 "color_titleline_bg",
728 "color_titleline_fg",
729 "color_win_bg",
730 "color_win_cur",
731 "color_win_cur_sel_bg",
732 "color_win_cur_sel_fg",
733 "color_win_dir",
734 "color_win_fg",
735 "color_win_inactive_cur_sel_bg",
736 "color_win_inactive_cur_sel_fg",
737 "color_win_inactive_sel_bg",
738 "color_win_inactive_sel_fg",
739 "color_win_sel_bg",
740 "color_win_sel_fg",
741 "color_win_title_bg",
742 "color_win_title_fg"
745 /* default values for the variables which we must initialize but
746 * can't do it statically */
747 static const struct {
748 const char *name;
749 const char *value;
750 } str_defaults[] = {
751 { "altformat_current", " %F " },
752 { "altformat_playlist", " %f%= %d " },
753 { "altformat_title", "%f" },
754 { "altformat_trackwin", " %f%= %d " },
755 { "format_current", " %a - %l - %02n. %t%= %y " },
756 { "format_playlist", " %-20a %02n. %t%= %y %d " },
757 { "format_title", "%a - %l - %t (%y)" },
758 { "format_trackwin", " %02n. %t%= %y %d " },
760 { "lib_sort" , "artist album discnumber tracknumber title filename" },
761 { "pl_sort", "" },
762 { "id3_default_charset","ISO-8859-1" },
763 { NULL, NULL }
766 LIST_HEAD(option_head);
767 int nr_options = 0;
769 void option_add(const char *name, unsigned int id, opt_get_cb get,
770 opt_set_cb set, opt_toggle_cb toggle)
772 struct cmus_opt *opt = xnew(struct cmus_opt, 1);
773 struct list_head *item;
775 opt->name = name;
776 opt->id = id;
777 opt->get = get;
778 opt->set = set;
779 opt->toggle = toggle;
781 item = option_head.next;
782 while (item != &option_head) {
783 struct cmus_opt *o = container_of(item, struct cmus_opt, node);
785 if (strcmp(name, o->name) < 0)
786 break;
787 item = item->next;
789 /* add before item */
790 list_add_tail(&opt->node, item);
791 nr_options++;
794 struct cmus_opt *option_find(const char *name)
796 struct cmus_opt *opt;
798 list_for_each_entry(opt, &option_head, node) {
799 if (strcmp(name, opt->name) == 0)
800 return opt;
802 error_msg("no such option %s", name);
803 return NULL;
806 void option_set(const char *name, const char *value)
808 struct cmus_opt *opt = option_find(name);
810 if (opt)
811 opt->set(opt->id, value);
814 static void get_op_option(unsigned int id, char *buf)
816 char *val = NULL;
818 player_get_op_option(id, &val);
819 if (val) {
820 strcpy(buf, val);
821 free(val);
825 static void set_op_option(unsigned int id, const char *buf)
827 int rc = player_set_op_option(id, buf);
829 if (rc) {
830 char *msg = op_get_error_msg(rc, "setting option");
831 error_msg("%s", msg);
832 free(msg);
836 /* id is ((plugin_index << 16) | option_index) */
837 static void add_op_option(unsigned int id, const char *name)
839 option_add(xstrdup(name), id, get_op_option, set_op_option, NULL);
842 void options_add(void)
844 int i;
846 /* add options */
848 for (i = 0; simple_options[i].name; i++)
849 option_add(simple_options[i].name, 0, simple_options[i].get,
850 simple_options[i].set, simple_options[i].toggle);
852 for (i = 0; i < NR_FMTS; i++)
853 option_add(str_defaults[i].name, i, get_format, set_format, NULL);
855 for (i = 0; i < NR_COLORS; i++)
856 option_add(color_names[i], i, get_color, set_color, NULL);
858 player_for_each_op_option(add_op_option);
861 static int handle_line(void *data, const char *line)
863 run_command(line);
864 return 0;
867 int source_file(const char *filename)
869 return file_for_each_line(filename, handle_line, NULL);
872 void options_load(void)
874 char filename[512];
875 int i;
877 display_errors = 1;
879 /* initialize those that can't be statically initialized */
880 for (i = 0; str_defaults[i].name; i++)
881 option_set(str_defaults[i].name, str_defaults[i].value);
883 /* load autosave config */
884 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
885 if (source_file(filename) == -1) {
886 const char *def = DATADIR "/cmus/rc";
888 if (errno != ENOENT)
889 warn_errno("loading %s", filename);
891 /* load defaults */
892 if (source_file(def) == -1)
893 die_errno("loading %s", def);
896 /* load optional static config */
897 snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
898 if (source_file(filename) == -1) {
899 if (errno != ENOENT)
900 warn_errno("loading %s", filename);
904 void options_exit(void)
906 struct cmus_opt *opt;
907 struct filter_entry *filt;
908 char filename[512];
909 FILE *f;
910 int i;
912 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
913 f = fopen(filename, "w");
914 if (f == NULL) {
915 warn_errno("creating %s", filename);
916 return;
919 /* save options */
920 list_for_each_entry(opt, &option_head, node) {
921 char buf[OPTION_MAX_SIZE];
923 buf[0] = 0;
924 opt->get(opt->id, buf);
925 fprintf(f, "set %s=%s\n", opt->name, buf);
928 /* save key bindings */
929 for (i = 0; i < NR_CTXS; i++) {
930 struct binding *b = key_bindings[i];
932 while (b) {
933 fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
934 b = b->next;
938 /* save filters */
939 list_for_each_entry(filt, &filters_head, node)
940 fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
941 fprintf(f, "factivate");
942 list_for_each_entry(filt, &filters_head, node) {
943 switch (filt->act_stat) {
944 case FS_YES:
945 fprintf(f, " %s", filt->name);
946 break;
947 case FS_NO:
948 fprintf(f, " !%s", filt->name);
949 break;
952 fprintf(f, "\n");
954 fclose(f);