1 /*
2  * Calcurse - text-based organizer
3  *
4  * Copyright (c) 2004-2020 calcurse Development Team <misc@calcurse.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  *      - Redistributions of source code must retain the above
12  *        copyright notice, this list of conditions and the
13  *        following disclaimer.
14  *
15  *      - Redistributions in binary form must reproduce the above
16  *        copyright notice, this list of conditions and the
17  *        following disclaimer in the documentation and/or other
18  *        materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Send your feedback or comments to : misc@calcurse.org
33  * Calcurse home page : http://calcurse.org
34  *
35  */
36 
37 #include <string.h>
38 #include <math.h>
39 
40 #include "calcurse.h"
41 
42 #define MAXKEYVAL   KEY_MAX	/* ncurses defines KEY_MAX as maximum key value */
43 
44 struct keydef_s {
45 	const char *label;
46 	const char *binding;
47 	const char *sb_label;
48 };
49 
50 static llist_t keys[NBKEYS];
51 static enum key actions[MAXKEYVAL];
52 
53 struct key_ext {
54 	int ch;
55 	enum key action;
56 };
57 
58 llist_t actions_ext;
59 
60 #define gettext_noop(s) s
61 static struct keydef_s keydef[NBKEYS] = {
62 	{ "generic-cancel", "ESC", gettext_noop("Cancel") },
63 	{ "generic-select", "SPC", gettext_noop("Select") },
64 	{ "generic-credits", "@", gettext_noop("Credits") },
65 	{ "generic-help", "?", gettext_noop("Help") },
66 	{ "generic-quit", "q Q", gettext_noop("Quit") },
67 	{ "generic-save", "s S ^S", gettext_noop("Save") },
68 	{ "generic-reload", "R", gettext_noop("Reload") },
69 	{ "generic-copy", "c", gettext_noop("Copy") },
70 	{ "generic-paste", "p ^V", gettext_noop("Paste") },
71 	{ "generic-change-view", "TAB", gettext_noop("Chg Win") },
72 	{ "generic-import", "i I", gettext_noop("Import") },
73 	{ "generic-export", "x X", gettext_noop("Export") },
74 	{ "generic-goto", "g G", gettext_noop("Go to") },
75 	{ "generic-other-cmd", "o O", gettext_noop("OtherCmd") },
76 	{ "generic-config-menu", "C", gettext_noop("Config") },
77 	{ "generic-redraw", "^R", gettext_noop("Redraw") },
78 	{ "generic-add-appt", "^A", gettext_noop("Add Appt") },
79 	{ "generic-add-todo", "^T", gettext_noop("Add Todo") },
80 	{ "generic-prev-day", "T ^H", gettext_noop("-1 Day") },
81 	{ "generic-next-day", "t ^L", gettext_noop("+1 Day") },
82 	{ "generic-prev-week", "W ^K", gettext_noop("-1 Week") },
83 	{ "generic-next-week", "w", gettext_noop("+1 Week") },
84 	{ "generic-prev-month", "M", gettext_noop("-1 Month") },
85 	{ "generic-next-month", "m", gettext_noop("+1 Month") },
86 	{ "generic-prev-year", "Y", gettext_noop("-1 Year") },
87 	{ "generic-next-year", "y", gettext_noop("+1 Year") },
88 	{ "generic-scroll-down", "^N", gettext_noop("Nxt View") },
89 	{ "generic-scroll-up", "^P", gettext_noop("Prv View") },
90 	{ "generic-goto-today", "^G", gettext_noop("Today") },
91 	{ "generic-command", ":", gettext_noop("Command") },
92 
93 	{ "move-right", "l L RGT", gettext_noop("Right") },
94 	{ "move-left", "h H LFT", gettext_noop("Left") },
95 	{ "move-down", "j J DWN", gettext_noop("Down") },
96 	{ "move-up", "k K UP", gettext_noop("Up") },
97 	{ "start-of-week", "0", gettext_noop("beg Week") },
98 	{ "end-of-week", "$", gettext_noop("end Week") },
99 	{ "add-item", "a A", gettext_noop("Add Item") },
100 	{ "del-item", "d D", gettext_noop("Del Item") },
101 	{ "edit-item", "e E", gettext_noop("Edit Itm") },
102 	{ "view-item", "v V RET", gettext_noop("View") },
103 	{ "pipe-item", "|", gettext_noop("Pipe") },
104 	{ "flag-item", "!", gettext_noop("Flag Itm") },
105 	{ "repeat", "r", gettext_noop("Repeat") },
106 	{ "edit-note", "n N", gettext_noop("EditNote") },
107 	{ "view-note", ">", gettext_noop("ViewNote") },
108 	{ "raise-priority", "+", gettext_noop("Prio.+") },
109 	{ "lower-priority", "-", gettext_noop("Prio.-") }
110 };
111 
112 /*
113  * Table of cached keynames indexed by key codes.
114  */
115 static char *keynames[KEY_MAX];
116 
dump_intro(FILE * fd)117 static void dump_intro(FILE * fd)
118 {
119 	const char *intro =
120 	    _("#\n"
121 	      "# Calcurse keys configuration file\n#\n"
122 	      "# In this file the keybindings used by Calcurse are defined.\n"
123 	      "# It is generated automatically by Calcurse and is maintained\n"
124 	      "# via the key configuration menu of the interactive user\n"
125 	      "# interface. It should not be edited directly.\n");
126 
127 	fprintf(fd, "%s\n", intro);
128 }
129 
keys_init(void)130 void keys_init(void)
131 {
132 	int i;
133 	const char *cp;
134 
135 	for (i = 0; i < MAXKEYVAL; i++)
136 		actions[i] = KEY_UNDEF;
137 	LLIST_INIT(&actions_ext);
138 	for (i = 0; i < NBKEYS; i++)
139 		LLIST_INIT(&keys[i]);
140 
141 	/* Initialization of the keynames table. */
142 	for (i = 0; i < KEY_MAX; i++)
143 		keynames[i] = "";
144 
145 	 /* Insertion of ncurses names in the ASCII range ...  */
146 	for (i = 1; i < 128; i++)
147 		if ((cp = keyname(i)))
148 			keynames[i] = mem_strdup(cp);
149 	/* ... and for the ncurses escape keys (pseudokeys). */
150 	for (i = KEY_MIN; i < KEY_MAX; i++)
151 		if ((cp = keyname(i)))
152 			keynames[i] = mem_strdup(cp);
153 
154 	/* Replace some with calcurse short forms. */
155 	keynames[TAB] =		"TAB";
156 	keynames[RETURN] =	"RET";
157 	keynames[ESCAPE] =	"ESC";
158 	keynames[SPACE] =	"SPC";
159 	keynames[KEY_UP] =	"UP";
160 	keynames[KEY_DOWN] =	"DWN";
161 	keynames[KEY_LEFT] =	"LFT";
162 	keynames[KEY_RIGHT] =	"RGT";
163 	keynames[KEY_HOME] =	"HOM";
164 	keynames[KEY_END] =	"END";
165 	keynames[KEY_NPAGE] =	"PgD";
166 	keynames[KEY_PPAGE] =	"PgU";
167 	keynames[KEY_IC] =	"INS";
168 	keynames[KEY_DC] =	"DEL";
169 	keynames[KEY_F(1)] =	"F1";
170 	keynames[KEY_F(2)] =	"F2";
171 	keynames[KEY_F(3)] =	"F3";
172 	keynames[KEY_F(4)] =	"F4";
173 	keynames[KEY_F(5)] =	"F5";
174 	keynames[KEY_F(6)] =	"F6";
175 	keynames[KEY_F(7)] =	"F7";
176 	keynames[KEY_F(8)] =	"F8";
177 	keynames[KEY_F(9)] =	"F9";
178 	keynames[KEY_F(10)] =	"F10";
179 	keynames[KEY_F(11)] =	"F11";
180 	keynames[KEY_F(12)] =	"F12";
181 }
182 
key_free(char * s)183 static void key_free(char *s)
184 {
185 	mem_free(s);
186 }
187 
keys_free(void)188 void keys_free(void)
189 {
190 	int i;
191 
192 	for (i = 0; i < NBKEYS; i++) {
193 		LLIST_FREE_INNER(&keys[i], key_free);
194 		LLIST_FREE(&keys[i]);
195 	}
196 }
197 
keys_dump_defaults(char * file)198 void keys_dump_defaults(char *file)
199 {
200 	FILE *fd;
201 	int i;
202 
203 	fd = fopen(file, "w");
204 	EXIT_IF(fd == NULL,
205 		_("FATAL ERROR: could not create default keys file."));
206 
207 	dump_intro(fd);
208 	for (i = 0; i < NBKEYS; i++)
209 		fprintf(fd, "%s  %s\n", keydef[i].label,
210 			keydef[i].binding);
211 	file_close(fd, __FILE_POS__);
212 }
213 
keys_get_label(enum key key)214 const char *keys_get_label(enum key key)
215 {
216 	EXIT_IF(key < 0
217 		|| key > NBKEYS,
218 		_("FATAL ERROR: key value out of bounds"));
219 
220 	return keydef[key].label;
221 }
222 
key_ext_hasch(struct key_ext * k,void * cbdata)223 static int key_ext_hasch(struct key_ext *k, void *cbdata)
224 {
225 	return (k->ch == *((int *)cbdata));
226 }
227 
keys_get_action(int pressed)228 enum key keys_get_action(int pressed)
229 {
230 	if (pressed < 0) {
231 		return -1;
232 	} else if (pressed > MAXKEYVAL) {
233 		llist_item_t *i = LLIST_FIND_FIRST(&actions_ext, &pressed,
234 						   key_ext_hasch);
235 		if (!i)
236 			return KEY_UNDEF;
237 
238 		struct key_ext *k = LLIST_GET_DATA(i);
239 		return k->action;
240 	} else {
241 		return actions[pressed];
242 	}
243 }
244 
keys_wgetch(WINDOW * win)245 int keys_wgetch(WINDOW *win)
246 {
247 	int ch, i;
248 	char buf[UTF8_MAXLEN];
249 
250 	ch = wgetch(win);
251 	if (ch == ERR)
252 		return ch;
253 
254 	/* Handle curses pseudo characters. */
255 	if (ch >= KEY_MIN)
256 		return ch;
257 
258 	/* Handle 1-byte UTF-8 characters. */
259 	if (UTF8_LENGTH(ch) == 1)
260 		return ch;
261 
262 	/*
263 	 * Map multibyte UTF-8 characters to code point values
264 	 * and add KEY_MAX to avoid the curses range.
265 	 */
266 	buf[0] = ch;
267 	for (i = 1; i < UTF8_LENGTH(buf[0]); i++)
268 		buf[i] = wgetch(win);
269 	return utf8_decode(buf) + KEY_MAX;
270 }
271 
keys_wait_for_any_key(WINDOW * win)272 void keys_wait_for_any_key(WINDOW *win)
273 {
274 	keys_wgetch(win);
275 }
276 
keys_get(WINDOW * win,int * count,int * reg)277 enum key keys_get(WINDOW *win, int *count, int *reg)
278 {
279 	int ch = '0';
280 
281 	if (count && reg) {
282 		*count = 0;
283 		*reg = 0;
284 		do {
285 			*count = *count * 10 + ch - '0';
286 			ch = keys_wgetch(win);
287 		}
288 		while ((ch == '0' && *count > 0)
289 		       || (ch >= '1' && ch <= '9'));
290 
291 		if (*count == 0)
292 			*count = 1;
293 
294 		if (ch == '"') {
295 			ch = keys_wgetch(win);
296 			if (ch >= '1' && ch <= '9') {
297 				*reg = ch - '1' + 1;
298 			} else if (ch >= 'a' && ch <= 'z') {
299 				*reg = ch - 'a' + 10;
300 			}
301 			ch = keys_wgetch(win);
302 		}
303 	} else {
304 		ch = keys_wgetch(win);
305 	}
306 
307 	switch (ch) {
308 	case KEY_RESIZE:
309 		return KEY_RESIZE;
310 	default:
311 		return keys_get_action(ch);
312 	}
313 }
314 
add_key_str(enum key action,int key)315 static void add_key_str(enum key action, int key)
316 {
317 	if (action > NBKEYS)
318 		return;
319 
320 	LLIST_ADD(&keys[action], keys_int2str(key));
321 }
322 
keys_assign_binding(int key,enum key action)323 int keys_assign_binding(int key, enum key action)
324 {
325 	if (key < 0)
326 		return 1;
327 	if (key > KEY_MAX) {
328 		llist_item_t *i = LLIST_FIND_FIRST(&actions_ext, &key, key_ext_hasch);
329 		if (i)
330 			return 1;
331 		struct key_ext *k = mem_malloc(sizeof(struct key_ext));
332 		k->ch = key;
333 		k->action = action;
334 		LLIST_ADD(&actions_ext, k);
335 	} else {
336 		if (actions[key] != KEY_UNDEF)
337 			return 1;
338 		actions[key] = action;
339 	}
340 	add_key_str(action, key);
341 	return 0;
342 }
343 
del_key_str(enum key action,int key)344 static void del_key_str(enum key action, int key)
345 {
346 	llist_item_t *i;
347 	char *oldstr = keys_int2str(key);;
348 
349 	if (action > NBKEYS)
350 		return;
351 
352 	LLIST_FOREACH(&keys[action], i) {
353 		if (strcmp(LLIST_GET_DATA(i), oldstr) == 0) {
354 			LLIST_REMOVE(&keys[action], i);
355 			goto cleanup;
356 		}
357 	}
358 
359 cleanup:
360 	mem_free(oldstr);
361 }
362 
keys_remove_binding(int key,enum key action)363 void keys_remove_binding(int key, enum key action)
364 {
365 	if (key < 0)
366 	       return;
367 
368 	if (key <= MAXKEYVAL) {
369 		actions[key] = KEY_UNDEF;
370 	} else {
371 		llist_item_t *i = LLIST_FIND_FIRST(&actions_ext, &key,
372 						   key_ext_hasch);
373 		if (i) {
374 			struct key_ext *k = LLIST_GET_DATA(i);
375 			LLIST_REMOVE(&actions_ext, i);
376 			mem_free(k);
377 		}
378 	}
379 
380 	del_key_str(action, key);
381 }
382 
keys_str2int(const char * key)383 int keys_str2int(const char *key)
384 {
385 	if (!key)
386 		return -1;
387 
388 	/* For backwards compatibility. */
389 	if (strcmp(key, "^J") == 0)
390 		return RETURN;
391 	else if (strcmp(key, "KEY_HOME") == 0)
392 		return KEY_HOME;
393 	else if (strcmp(key, "KEY_END") == 0)
394 		return KEY_END;
395 
396 
397 	/* Lookup in the keynames table. */
398 	for (int i = 1; i < 128; i++)
399 		if (strcmp(key, keynames[i]) == 0)
400 			return i;
401 	for (int i = KEY_MIN; i < KEY_MAX; i++)
402 		if (strcmp(key, keynames[i]) == 0)
403 			return i;
404 
405 	/* UTF-8 multibyte keys. */
406 	return utf8_decode(key) + KEY_MAX;
407 }
408 
keys_int2str(int key)409 char *keys_int2str(int key)
410 {
411 	char *res;
412 
413 	if (key < KEY_MAX) {
414 		if (strcmp(keynames[key], "") == 0)
415 			return NULL;
416 		else
417 			return mem_strdup(keynames[key]);
418 	} else {
419 		asprintf(&res, "%s", utf8_encode(key - KEY_MAX));
420 		return res;
421 	}
422 }
423 
keys_action_count_keys(enum key action)424 int keys_action_count_keys(enum key action)
425 {
426 	llist_item_t *i;
427 	int n = 0;
428 
429 	LLIST_FOREACH(&keys[action], i)
430 	    n++;
431 
432 	return n;
433 }
434 
keys_action_firstkey(enum key action)435 const char *keys_action_firstkey(enum key action)
436 {
437 	const char *s = LLIST_GET_DATA(LLIST_FIRST(&keys[action]));
438 	return (s != NULL) ? s : "XXX";
439 }
440 
keys_action_nkey(enum key action,int keynum)441 const char *keys_action_nkey(enum key action, int keynum)
442 {
443 	return LLIST_GET_DATA(LLIST_NTH(&keys[action], keynum));
444 }
445 
keys_action_allkeys(enum key action)446 char *keys_action_allkeys(enum key action)
447 {
448 	llist_item_t *i;
449 	static char keystr[BUFSIZ];
450 	int keystrlen = 0;
451 	int entrylen;
452 
453 	if (!LLIST_FIRST(&keys[action]))
454 		return NULL;
455 
456 	keystr[0] = '\0';
457 	LLIST_FOREACH(&keys[action], i) {
458 		entrylen = strlen(LLIST_GET_DATA(i)) + 1;
459 		if (keystrlen + entrylen >= BUFSIZ)
460 			break;
461 		memcpy(keystr + keystrlen, LLIST_GET_DATA(i), entrylen - 1);
462 		keystr[keystrlen + entrylen - 1] = ' ';
463 		keystrlen += entrylen;
464 	}
465 
466 	keystr[keystrlen] = '\0';
467 	return keystr;
468 }
469 
470 /* Need this to display keys properly inside status bar. */
keys_format_label(char ** s,char * key,int width)471 static unsigned keys_format_label(char **s, char *key, int width)
472 {
473 	*s = mem_strdup(key);
474 	utf8_chop(*s, width);
475 	return utf8_strwidth(*s);
476 }
477 
478 void
keys_display_bindings_bar(WINDOW * win,int * bindings,int count,int page_base,int page_size)479 keys_display_bindings_bar(WINDOW * win, int *bindings, int count,
480 			  int page_base, int page_size)
481 {
482 	page_size = MIN(page_size, count - page_base);
483 
484 	/* Padding between two key bindings. */
485 	const int padding =
486 	    (col * 2) / page_size - (KEYS_KEYLEN + KEYS_LABELEN + 1);
487 	/* Total length of a key binding (including padding). */
488 	const int cmd_len = KEYS_KEYLEN + KEYS_LABELEN + 1 + padding;
489 
490 	int i;
491 
492 	wins_erase_status_bar();
493 	for (i = 0; i < page_size && page_base + i < count; i++) {
494 		/* Location of key and label. */
495 		const int key_pos_x = (i / 2) * cmd_len;
496 		const int key_pos_y = i % 2;
497 		const int label_pos_x = key_pos_x + KEYS_KEYLEN + 1;
498 		const int label_pos_y = key_pos_y;
499 
500 		char key[UTF8_MAXLEN + 1], *fmtkey;
501 		unsigned dpywidth, shift_x;
502 
503 		int binding_key;
504 
505 		if (i < page_size - 1 || page_base + i == count - 1)
506 			binding_key = bindings[page_base + i];
507 		else
508 			binding_key = KEY_GENERIC_OTHER_CMD;
509 
510 		const char *label;
511 
512 		if (binding_key < NBKEYS) {
513 			strncpy(key, keys_action_firstkey(binding_key), UTF8_MAXLEN);
514 			key[UTF8_MAXLEN] = '\0';
515 			label = gettext(keydef[binding_key].sb_label);
516 		} else {
517 			switch (binding_key) {
518 			case KEY_CONFIGMENU_GENERAL:
519 				strcpy(key, "g");
520 				label = _("General");
521 				break;
522 			case KEY_CONFIGMENU_LAYOUT:
523 				strcpy(key, "l");
524 				label = _("Layout");
525 				break;
526 			case KEY_CONFIGMENU_SIDEBAR:
527 				strcpy(key, "s");
528 				label = _("Sidebar");
529 				break;
530 			case KEY_CONFIGMENU_COLOR:
531 				strcpy(key, "c");
532 				label = _("Color");
533 				break;
534 			case KEY_CONFIGMENU_NOTIFY:
535 				strcpy(key, "n");
536 				label = _("Notify");
537 				break;
538 			case KEY_CONFIGMENU_KEYS:
539 				strcpy(key, "k");
540 				label = _("Keys");
541 				break;
542 			default:
543 				strcpy(key, "?");
544 				label = _("Unknown");
545 				break;
546 			}
547 		}
548 
549 		custom_apply_attr(win, ATTR_HIGHEST);
550 		dpywidth = keys_format_label(&fmtkey, key, KEYS_KEYLEN);
551 		shift_x = KEYS_KEYLEN - dpywidth;
552 		mvwaddstr(win, key_pos_y, key_pos_x + shift_x, fmtkey);
553 		mem_free(fmtkey);
554 		custom_remove_attr(win, ATTR_HIGHEST);
555 		mvwaddstr(win, label_pos_y, label_pos_x, label);
556 	}
557 	wnoutrefresh(win);
558 }
559 
560 /*
561  * Display information about the given key.
562  * (could not add the keys descriptions to keydef variable, because of i18n).
563  */
keys_popup_info(enum key key)564 void keys_popup_info(enum key key)
565 {
566 	char *info[NBKEYS];
567 	WINDOW *infowin;
568 
569 	info[KEY_GENERIC_CANCEL] = _("Cancel the ongoing action.");
570 	info[KEY_GENERIC_SELECT] = _("Select the highlighted item.");
571 	info[KEY_GENERIC_CREDITS] =
572 	    _("Print general information about calcurse's authors, license, etc.");
573 	info[KEY_GENERIC_HELP] =
574 	    _("Display hints whenever some help screens are available.");
575 	info[KEY_GENERIC_QUIT] =
576 	    _("Exit from the current menu, or quit calcurse.");
577 	info[KEY_GENERIC_SAVE] = _("Save calcurse data.");
578 	info[KEY_GENERIC_RELOAD] = _("Reload appointments and todo items.");
579 	info[KEY_GENERIC_COPY] =
580 	    _("Copy the item that is currently selected.");
581 	info[KEY_GENERIC_PASTE] =
582 	    _("Paste an item at the current position.");
583 	info[KEY_GENERIC_CHANGE_VIEW] =
584 	    _("Select next panel in calcurse main screen.");
585 	info[KEY_GENERIC_IMPORT] = _("Import data from an external file.");
586 	info[KEY_GENERIC_EXPORT] = _("Export data to a new file format.");
587 	info[KEY_GENERIC_GOTO] = _("Select the day to go to.");
588 	info[KEY_GENERIC_OTHER_CMD] =
589 	    _("Show next possible actions inside status bar.");
590 	info[KEY_GENERIC_CONFIG_MENU] = _("Enter the configuration menu.");
591 	info[KEY_GENERIC_REDRAW] = _("Redraw calcurse's screen.");
592 	info[KEY_GENERIC_ADD_APPT] =
593 	    _("Add an appointment, whichever panel is currently selected.");
594 	info[KEY_GENERIC_ADD_TODO] =
595 	    _("Add a todo item, whichever panel is currently selected.");
596 	info[KEY_GENERIC_PREV_DAY] =
597 	    _("Move to previous day in calendar, whichever panel is currently "
598 	     "selected.");
599 	info[KEY_GENERIC_NEXT_DAY] =
600 	    _("Move to next day in calendar, whichever panel is currently selected.");
601 	info[KEY_GENERIC_PREV_WEEK] =
602 	    _("Move to previous week in calendar, whichever panel is currently "
603 	     "selected");
604 	info[KEY_GENERIC_NEXT_WEEK] =
605 	    _("Move to next week in calendar, whichever panel is currently selected.");
606 	info[KEY_GENERIC_PREV_MONTH] =
607 	    _("Move to previous month in calendar, whichever panel is currently "
608 	     "selected");
609 	info[KEY_GENERIC_NEXT_MONTH] =
610 	    _("Move to next month in calendar, whichever panel is currently "
611 	     "selected.");
612 	info[KEY_GENERIC_PREV_YEAR] =
613 	    _("Move to previous year in calendar, whichever panel is currently "
614 	     "selected");
615 	info[KEY_GENERIC_NEXT_YEAR] =
616 	    _("Move to next year in calendar, whichever panel is currently selected.");
617 	info[KEY_GENERIC_SCROLL_DOWN] =
618 	    _("Scroll window down (e.g. when displaying text inside a popup window).");
619 	info[KEY_GENERIC_SCROLL_UP] =
620 	    _("Scroll window up (e.g. when displaying text inside a popup window).");
621 	info[KEY_GENERIC_GOTO_TODAY] =
622 	    _("Go to today, whichever panel is selected.");
623 	info[KEY_GENERIC_CMD] = _("Enter command mode.");
624 	info[KEY_MOVE_RIGHT] = _("Move to the right.");
625 	info[KEY_MOVE_LEFT] = _("Move to the left.");
626 	info[KEY_MOVE_DOWN] = _("Move down.");
627 	info[KEY_MOVE_UP] = _("Move up.");
628 	info[KEY_START_OF_WEEK] =
629 	    _("Select the first day of the current week when inside the calendar "
630 	     "panel.");
631 	info[KEY_END_OF_WEEK] =
632 	    _("Select the last day of the current week when inside the calendar "
633 	     "panel.");
634 	info[KEY_ADD_ITEM] =
635 	    _("Add an item to the currently selected panel.");
636 	info[KEY_DEL_ITEM] = _("Delete the currently selected item.");
637 	info[KEY_EDIT_ITEM] = _("Edit the currently seleted item.");
638 	info[KEY_VIEW_ITEM] =
639 	    _("Display the currently selected item inside a popup window.");
640 	info[KEY_FLAG_ITEM] =
641 	    _("Flag the currently selected item as important.");
642 	info[KEY_REPEAT_ITEM] = _("Repeat an item");
643 	info[KEY_PIPE_ITEM] =
644 	    _("Pipe the currently selected item to an external program.");
645 	info[KEY_EDIT_NOTE] =
646 	    _("Attach (or edit if one exists) a note to the currently selected item");
647 	info[KEY_VIEW_NOTE] =
648 	    _("View the note attached to the currently selected item.");
649 	info[KEY_RAISE_PRIORITY] =
650 	    _("Raise a task priority inside the todo panel.");
651 	info[KEY_LOWER_PRIORITY] =
652 	    _("Lower a task priority inside the todo panel.");
653 
654 	if (key > NBKEYS)
655 		return;
656 
657 #define WINROW 10
658 #define WINCOL (col - 4)
659 	infowin =
660 	    popup(WINROW, WINCOL, (row - WINROW) / 2, (col - WINCOL) / 2,
661 		  keydef[key].label, info[key], 1);
662 	keys_get(infowin, NULL, NULL);
663 	delwin(infowin);
664 #undef WINROW
665 #undef WINCOL
666 }
667 
keys_save_bindings(FILE * fd)668 void keys_save_bindings(FILE * fd)
669 {
670 	int i;
671 	char *action;
672 
673 	EXIT_IF(fd == NULL, _("FATAL ERROR: null file pointer."));
674 	dump_intro(fd);
675 	for (i = 0; i < NBKEYS; i++) {
676 		action = keys_action_allkeys(i);
677 		if (action)
678 			fprintf(fd, "%s  %s\n", keydef[i].label, action);
679 	}
680 }
681 
keys_check_missing_bindings(void)682 int keys_check_missing_bindings(void)
683 {
684 	int i;
685 
686 	for (i = 0; i < NBKEYS; i++) {
687 		if (!LLIST_FIRST(&keys[i]))
688 			return 1;
689 	}
690 	return 0;
691 }
692 
keys_fill_missing(void)693 void keys_fill_missing(void)
694 {
695 	int i;
696 
697 	for (i = 0; i < NBKEYS; i++) {
698 		if (!LLIST_FIRST(&keys[i])) {
699 			char *p, tmpbuf[BUFSIZ];
700 
701 			strncpy(tmpbuf, keydef[i].binding, BUFSIZ);
702 			tmpbuf[BUFSIZ - 1] = '\0';
703 			p = tmpbuf;
704 			for (;;) {
705 				char key_ch[BUFSIZ];
706 
707 				while (*p == ' ')
708 					p++;
709 				if (sscanf(p, "%s", key_ch) == 1) {
710 					int ch, used;
711 
712 					ch = keys_str2int(key_ch);
713 					used = keys_assign_binding(ch, i);
714 					if (used)
715 						WARN_MSG(_("When adding default key for \"%s\", "
716 							  "\"%s\" was already assigned!"),
717 							 keydef[i].label,
718 							 key_ch);
719 					p += strlen(key_ch);
720 				} else {
721 					break;
722 				}
723 			}
724 		}
725 	}
726 }
727