This commit is contained in:
2026-03-29 14:01:52 +03:00
commit 0611279128
210 changed files with 60454 additions and 0 deletions

327
help.c Normal file
View File

@@ -0,0 +1,327 @@
/*
* Copyright 2008-2013 Various Authors
* Copyright 2006 <ft@bewatermyfriend.org>
*
* heavily based on filters.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "help.h"
#include "window.h"
#include "search.h"
#include "misc.h"
#include "xmalloc.h"
#include "keys.h"
#include "command_mode.h"
#include "ui_curses.h"
#include "options.h"
#include "cmdline.h"
#include <stdio.h>
struct window *help_win;
struct searchable *help_searchable;
static LIST_HEAD(help_head);
static struct list_head *bound_head;
static struct list_head *bound_tail;
static struct list_head *unbound_head;
static struct list_head *unbound_tail;
static inline void help_entry_to_iter(struct help_entry *e, struct iter *iter)
{
iter->data0 = &help_head;
iter->data1 = e;
iter->data2 = NULL;
}
static GENERIC_ITER_PREV(help_get_prev, struct help_entry, node)
static GENERIC_ITER_NEXT(help_get_next, struct help_entry, node)
static int help_search_get_current(void *data, struct iter *iter, enum search_direction dir)
{
return window_get_sel(help_win, iter);
}
static int help_search_matches(void *data, struct iter *iter, const char *text)
{
int matched = 0;
char **words = get_words(text);
if (words[0] != NULL) {
struct help_entry *ent;
int i;
ent = iter_to_help_entry(iter);
for (i = 0; ; i++) {
if (words[i] == NULL) {
window_set_sel(help_win, iter);
matched = 1;
break;
}
if (ent->type == HE_TEXT) {
if (!u_strcasestr(ent->text, words[i]))
break;
} else if (ent->type == HE_BOUND) {
if (!u_strcasestr(ent->binding->cmd, words[i]) &&
!u_strcasestr(ent->binding->key->name, words[i]))
break;
} else if (ent->type == HE_UNBOUND) {
if (!u_strcasestr(ent->command->name, words[i]))
break;
} else if (ent->type == HE_OPTION) {
if (!u_strcasestr(ent->option->name, words[i]))
break;
}
}
}
free_str_array(words);
return matched;
}
static const struct searchable_ops help_search_ops = {
.get_prev = help_get_prev,
.get_next = help_get_next,
.get_current = help_search_get_current,
.matches = help_search_matches
};
static void help_add_text(const char *s)
{
struct help_entry *ent;
ent = xnew(struct help_entry, 1);
ent->type = HE_TEXT;
ent->text = s;
list_add_tail(&ent->node, &help_head);
}
static void help_add_defaults(void)
{
struct cmus_opt *opt;
help_add_text("Keybindings");
help_add_text("-----------");
bound_head = help_head.prev;
help_add_text("");
help_add_text("Unbound Commands");
help_add_text("----------------");
unbound_head = help_head.prev;
help_add_text("");
help_add_text("Options");
help_add_text("-------");
list_for_each_entry(opt, &option_head, node) {
struct help_entry *ent = xnew(struct help_entry, 1);
ent->type = HE_OPTION;
ent->option = opt;
list_add_tail(&ent->node, &help_head);
}
bound_tail = bound_head->next;
unbound_tail = unbound_head->next;
}
void help_remove_unbound(struct command *cmd)
{
struct help_entry *ent;
struct iter i;
list_for_each_entry(ent, &help_head, node) {
if (ent->type != HE_UNBOUND)
continue;
if (ent->command == cmd) {
help_entry_to_iter(ent, &i);
window_row_vanishes(help_win, &i);
list_del(&ent->node);
free(ent);
return;
}
}
}
static void list_add_sorted(struct list_head *new, struct list_head *head,
struct list_head *tail,
int (*cmp)(struct list_head *, struct list_head *))
{
struct list_head *item = tail->prev;
while (item != head) {
if (cmp(new, item) >= 0)
break;
item = item->prev;
}
/* add after item */
list_add(new, item);
}
static int bound_cmp(struct list_head *ai, struct list_head *bi)
{
struct help_entry *a = container_of(ai, struct help_entry, node);
struct help_entry *b = container_of(bi, struct help_entry, node);
int ret = a->binding->ctx - b->binding->ctx;
if (!ret)
ret = strcmp(a->binding->key->name, b->binding->key->name);
return ret;
}
static int unbound_cmp(struct list_head *ai, struct list_head *bi)
{
struct help_entry *a = container_of(ai, struct help_entry, node);
struct help_entry *b = container_of(bi, struct help_entry, node);
return strcmp(a->command->name, b->command->name);
}
void help_add_unbound(struct command *cmd)
{
struct help_entry *ent;
ent = xnew(struct help_entry, 1);
ent->type = HE_UNBOUND;
ent->command = cmd;
list_add_sorted(&ent->node, unbound_head, unbound_tail, unbound_cmp);
}
void help_add_all_unbound(void)
{
int i;
for (i = 0; commands[i].name; ++i)
if (!commands[i].bc)
help_add_unbound(&commands[i]);
}
void help_select(void)
{
struct iter sel;
struct help_entry *ent;
char buf[OPTION_MAX_SIZE];
if (!window_get_sel(help_win, &sel))
return;
ent = iter_to_help_entry(&sel);
switch (ent->type) {
case HE_BOUND:
snprintf(buf, sizeof(buf), "bind -f %s %s %s",
key_context_names[ent->binding->ctx],
ent->binding->key->name,
ent->binding->cmd);
cmdline_set_text(buf);
enter_command_mode();
break;
case HE_UNBOUND:
snprintf(buf, sizeof(buf), "bind common <key> %s",
ent->command->name);
cmdline_set_text(buf);
enter_command_mode();
break;
case HE_OPTION:
snprintf(buf, sizeof(buf), "set %s=", ent->option->name);
size_t len = strlen(buf);
ent->option->get(ent->option->data, buf + len, sizeof(buf) - len);
cmdline_set_text(buf);
enter_command_mode();
break;
default:
break;
}
}
void help_toggle(void)
{
struct iter sel;
struct help_entry *ent;
if (!window_get_sel(help_win, &sel))
return;
ent = iter_to_help_entry(&sel);
switch (ent->type) {
case HE_OPTION:
if (ent->option->toggle) {
ent->option->toggle(ent->option->data);
help_win->changed = 1;
}
break;
default:
break;
}
}
void help_remove(void)
{
struct iter sel;
struct help_entry *ent;
if (!window_get_sel(help_win, &sel))
return;
ent = iter_to_help_entry(&sel);
switch (ent->type) {
case HE_BOUND:
if (yes_no_query("Remove selected binding? [y/N]") == UI_QUERY_ANSWER_YES)
key_unbind(key_context_names[ent->binding->ctx],
ent->binding->key->name, 0);
break;
default:
break;
}
}
void help_add_bound(const struct binding *bind)
{
struct help_entry *ent;
ent = xnew(struct help_entry, 1);
ent->type = HE_BOUND;
ent->binding = bind;
list_add_sorted(&ent->node, bound_head, bound_tail, bound_cmp);
}
void help_remove_bound(const struct binding *bind)
{
struct help_entry *ent;
struct iter i;
list_for_each_entry(ent, &help_head, node) {
if (ent->binding == bind) {
help_entry_to_iter(ent, &i);
window_row_vanishes(help_win, &i);
list_del(&ent->node);
free(ent);
return;
}
}
}
void help_init(void)
{
struct iter iter;
help_win = window_new(help_get_prev, help_get_next);
window_set_contents(help_win, &help_head);
window_changed(help_win);
help_add_defaults();
iter.data0 = &help_head;
iter.data1 = NULL;
iter.data2 = NULL;
help_searchable = searchable_new(NULL, &iter, &help_search_ops);
}
void help_exit(void)
{
searchable_free(help_searchable);
window_free(help_win);
}