Handle streams separately in tree_add_track()
[cmus.git] / help.c
blob6364669a68c48d148ff74a61411e6010d283c7d2
1 /*
2 * help.c, heavily based on filters.c
3 * (c) 2006, <ft@bewatermyfriend.org>
4 */
6 #include "help.h"
7 #include "window.h"
8 #include "search.h"
9 #include "misc.h"
10 #include "xmalloc.h"
11 #include "keys.h"
12 #include "command_mode.h"
13 #include "ui_curses.h"
14 #include "options.h"
15 #include "cmdline.h"
17 #include <stdio.h>
19 struct window *help_win;
20 struct searchable *help_searchable;
22 static LIST_HEAD(help_head);
23 static struct list_head *bound_head;
24 static struct list_head *bound_tail;
25 static struct list_head *unbound_head;
26 static struct list_head *unbound_tail;
28 static inline void help_entry_to_iter(struct help_entry *e, struct iter *iter)
30 iter->data0 = &help_head;
31 iter->data1 = e;
32 iter->data2 = NULL;
35 static GENERIC_ITER_PREV(help_get_prev, struct help_entry, node)
36 static GENERIC_ITER_NEXT(help_get_next, struct help_entry, node)
38 static int help_search_get_current(void *data, struct iter *iter)
40 return window_get_sel(help_win, iter);
43 static int help_search_matches(void *data, struct iter *iter, const char *text)
45 int matched = 0;
46 char **words = get_words(text);
48 if (words[0] != NULL) {
49 struct help_entry *ent;
50 int i;
52 ent = iter_to_help_entry(iter);
53 for (i = 0; ; i++) {
54 if (words[i] == NULL) {
55 window_set_sel(help_win, iter);
56 matched = 1;
57 break;
59 if (ent->type == HE_TEXT) {
60 if (!u_strcasestr(ent->text, words[i]))
61 break;
62 } else if (ent->type == HE_BOUND) {
63 if (!u_strcasestr(ent->binding->cmd, words[i]) &&
64 !u_strcasestr(ent->binding->key->name, words[i]))
65 break;
66 } else if (ent->type == HE_UNBOUND) {
67 if (!u_strcasestr(ent->command->name, words[i]))
68 break;
69 } else if (ent->type == HE_OPTION) {
70 if (!u_strcasestr(ent->option->name, words[i]))
71 break;
75 free_str_array(words);
76 return matched;
79 static const struct searchable_ops help_search_ops = {
80 .get_prev = help_get_prev,
81 .get_next = help_get_next,
82 .get_current = help_search_get_current,
83 .matches = help_search_matches
86 static void help_add_text(const char *s)
88 struct help_entry *ent;
89 ent = xnew(struct help_entry, 1);
90 ent->type = HE_TEXT;
91 ent->text = s;
92 list_add_tail(&ent->node, &help_head);
95 static void help_add_defaults(void)
97 struct cmus_opt *opt;
99 help_add_text("Keybindings");
100 help_add_text("-----------");
101 bound_head = help_head.prev;
102 help_add_text("");
103 help_add_text("Unbound Commands");
104 help_add_text("----------------");
105 unbound_head = help_head.prev;
106 help_add_text("");
107 help_add_text("Options");
108 help_add_text("-------");
110 list_for_each_entry(opt, &option_head, node) {
111 struct help_entry *ent = xnew(struct help_entry, 1);
113 ent->type = HE_OPTION;
114 ent->option = opt;
115 list_add_tail(&ent->node, &help_head);
118 bound_tail = bound_head->next;
119 unbound_tail = unbound_head->next;
122 void help_remove_unbound(struct command *cmd)
124 struct help_entry *ent;
125 struct iter i;
126 list_for_each_entry(ent, &help_head, node) {
127 if (ent->type != HE_UNBOUND)
128 continue;
129 if (ent->command == cmd) {
130 help_entry_to_iter(ent, &i);
131 window_row_vanishes(help_win, &i);
132 list_del(&ent->node);
133 free(ent);
134 return;
139 static void list_add_sorted(struct list_head *new, struct list_head *head,
140 struct list_head *tail,
141 int (*cmp)(struct list_head *, struct list_head *))
143 struct list_head *item = tail->prev;
145 while (item != head) {
146 if (cmp(new, item) >= 0)
147 break;
148 item = item->prev;
150 /* add after item */
151 list_add(new, item);
154 static int bound_cmp(struct list_head *ai, struct list_head *bi)
156 struct help_entry *a = container_of(ai, struct help_entry, node);
157 struct help_entry *b = container_of(bi, struct help_entry, node);
158 int ret = a->binding->ctx - b->binding->ctx;
160 if (!ret)
161 ret = strcmp(a->binding->key->name, b->binding->key->name);
162 return ret;
165 static int unbound_cmp(struct list_head *ai, struct list_head *bi)
167 struct help_entry *a = container_of(ai, struct help_entry, node);
168 struct help_entry *b = container_of(bi, struct help_entry, node);
170 return strcmp(a->command->name, b->command->name);
173 void help_add_unbound(struct command *cmd)
175 struct help_entry *ent;
177 ent = xnew(struct help_entry, 1);
178 ent->type = HE_UNBOUND;
179 ent->command = cmd;
180 list_add_sorted(&ent->node, unbound_head, unbound_tail, unbound_cmp);
183 void help_add_all_unbound(void)
185 int i;
186 for (i = 0; commands[i].name; ++i)
187 if (!commands[i].bc)
188 help_add_unbound(&commands[i]);
191 void help_select(void)
193 struct iter sel;
194 struct help_entry *ent;
195 char buf[512];
197 if (!window_get_sel(help_win, &sel))
198 return;
200 ent = iter_to_help_entry(&sel);
201 switch (ent->type) {
202 case HE_BOUND:
203 snprintf(buf, sizeof(buf), "bind -f %s %s %s",
204 key_context_names[ent->binding->ctx],
205 ent->binding->key->name,
206 ent->binding->cmd);
207 cmdline_set_text(buf);
208 enter_command_mode();
209 break;
210 case HE_OPTION:
211 snprintf(buf, sizeof(buf), "set %s=", ent->option->name);
212 ent->option->get(ent->option->id, buf + strlen(buf));
213 cmdline_set_text(buf);
214 enter_command_mode();
215 break;
216 default:
217 break;
221 void help_toggle(void)
223 struct iter sel;
224 struct help_entry *ent;
226 if (!window_get_sel(help_win, &sel))
227 return;
229 ent = iter_to_help_entry(&sel);
230 switch (ent->type) {
231 case HE_OPTION:
232 if (ent->option->toggle) {
233 ent->option->toggle(ent->option->id);
234 help_win->changed = 1;
236 break;
237 default:
238 break;
242 void help_remove(void)
244 struct iter sel;
245 struct help_entry *ent;
247 if (!window_get_sel(help_win, &sel))
248 return;
250 ent = iter_to_help_entry(&sel);
251 switch (ent->type) {
252 case HE_BOUND:
253 if (yes_no_query("Remove selected binding? [y/N]"))
254 key_unbind(key_context_names[ent->binding->ctx],
255 ent->binding->key->name, 0);
256 break;
257 default:
258 break;
262 void help_add_bound(const struct binding *bind)
264 struct help_entry *ent;
265 ent = xnew(struct help_entry, 1);
266 ent->type = HE_BOUND;
267 ent->binding = bind;
268 list_add_sorted(&ent->node, bound_head, bound_tail, bound_cmp);
271 void help_remove_bound(const struct binding *bind)
273 struct help_entry *ent;
274 struct iter i;
275 list_for_each_entry(ent, &help_head, node) {
276 if (ent->binding == bind) {
277 help_entry_to_iter(ent, &i);
278 window_row_vanishes(help_win, &i);
279 list_del(&ent->node);
280 free(ent);
281 return;
286 void help_init(void)
288 struct iter iter;
290 help_win = window_new(help_get_prev, help_get_next);
291 window_set_contents(help_win, &help_head);
292 window_changed(help_win);
293 help_add_defaults();
295 iter.data0 = &help_head;
296 iter.data1 = NULL;
297 iter.data2 = NULL;
298 help_searchable = searchable_new(NULL, &iter, &help_search_ops);
301 void help_exit(void)
303 searchable_free(help_searchable);
304 window_free(help_win);