Release v.5.0.0
[screen.git] / src / help.c
blob53b6fad98a2183db11f3a32c8f6b09a0f7851b6b
1 /* Copyright (c) 2010
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
4 * Copyright (c) 2008, 2009
5 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
6 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
7 * Micah Cowan (micah@cowan.name)
8 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
9 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
10 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
11 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
12 * Copyright (c) 1987 Oliver Laumann
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3, or (at your option)
17 * any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program (see the file COPYING); if not, see
26 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
29 ****************************************************************
32 #include "config.h"
34 #include "help.h"
36 #include <stdint.h>
37 #include <stdbool.h>
38 #include <sys/types.h>
40 #include "screen.h"
42 #include "misc.h"
43 #include "list_generic.h"
44 #include "process.h"
46 char version[60]; /* initialised by main() */
48 static void PadStr(char *, int, int, int);
51 ** Here come the help page routines
54 static void HelpProcess(char **, size_t *);
55 static void HelpAbort(void);
56 static void HelpRedisplayLine(int, int, int, int);
57 static void add_key_to_buf(char *, int);
58 static void AddAction(struct action *, int, int);
59 static int helppage(void);
61 struct helpdata {
62 char *class;
63 struct action *ktabp;
64 int maxrow, grow, numcols, numrows, num_names;
65 int numskip, numpages;
66 int command_search, command_bindings;
67 int refgrow, refcommand_search;
68 int inter, mcom, mkey;
69 int nact[RC_LAST + 1];
72 #define MAXKLEN 256
74 static const struct LayFuncs HelpLf = {
75 HelpProcess,
76 HelpAbort,
77 HelpRedisplayLine,
78 DefClearLine,
79 DefResize,
80 DefRestore,
81 NULL
84 void display_help(char *class, struct action *ktabp)
86 int i, n, key, mcom, mkey, l;
87 struct helpdata *helpdata;
88 int used[RC_LAST + 1];
90 if (flayer->l_height < 6) {
91 LMsg(0, "Window height too small for help page");
92 return;
94 if (InitOverlayPage(sizeof(struct helpdata), &HelpLf, 0))
95 return;
97 helpdata = (struct helpdata *)flayer->l_data;
98 helpdata->class = class;
99 helpdata->ktabp = ktabp;
100 helpdata->num_names = helpdata->command_bindings = 0;
101 helpdata->command_search = 0;
102 for (n = 0; n <= RC_LAST; n++)
103 used[n] = 0;
104 mcom = 0;
105 mkey = 0;
106 for (key = 0; key < 256 + KMAP_KEYS; key++) {
107 n = ktabp[key].nr;
108 if (n == RC_ILLEGAL)
109 continue;
110 if (ktabp[key].args == noargs) {
111 used[n] += (key <= ' ' || key == 0x7f) ? 3 : (key > 0x7f) ? 5 : 2;
112 } else
113 helpdata->command_bindings++;
115 for (n = i = 0; n <= RC_LAST; n++)
116 if (used[n]) {
117 l = strlen(comms[n].name);
118 if (l > mcom)
119 mcom = l;
120 if (used[n] > mkey)
121 mkey = used[n];
122 helpdata->nact[i++] = n;
124 helpdata->num_names = i;
126 if (mkey > MAXKLEN)
127 mkey = MAXKLEN;
128 helpdata->numcols = flayer->l_width / (mcom + mkey + 1);
129 if (helpdata->numcols == 0) {
130 HelpAbort();
131 LMsg(0, "Width too small");
132 return;
134 helpdata->inter = (flayer->l_width - (mcom + mkey) * helpdata->numcols) / (helpdata->numcols + 1);
135 if (helpdata->inter <= 0)
136 helpdata->inter = 1;
137 helpdata->mcom = mcom;
138 helpdata->mkey = mkey;
139 helpdata->numrows = (helpdata->num_names + helpdata->numcols - 1) / helpdata->numcols;
140 helpdata->numskip = flayer->l_height - 5 - (2 + helpdata->numrows);
141 while (helpdata->numskip < 0)
142 helpdata->numskip += flayer->l_height - 5;
143 helpdata->numskip %= flayer->l_height - 5;
144 if (helpdata->numskip > flayer->l_height / 3 || helpdata->numskip > helpdata->command_bindings)
145 helpdata->numskip = 1;
146 helpdata->maxrow = 2 + helpdata->numrows + helpdata->numskip + helpdata->command_bindings;
147 helpdata->grow = 0;
149 helpdata->numpages = (helpdata->maxrow + flayer->l_height - 6) / (flayer->l_height - 5);
150 flayer->l_x = 0;
151 flayer->l_y = flayer->l_height - 1;
152 helppage();
155 static void HelpProcess(char **ppbuf, size_t *plen)
157 bool done = false;
159 while (!done && *plen > 0) {
160 switch (**ppbuf) {
161 case ' ':
162 if (helppage() == 0)
163 break;
164 /* FALLTHROUGH */
165 case '\r':
166 case '\n':
167 done = true;
168 break;
169 default:
170 break;
172 ++*ppbuf;
173 --*plen;
175 if (done)
176 HelpAbort();
179 static void HelpAbort(void)
181 LAY_CALL_UP(LRefreshAll(flayer, 0));
182 ExitOverlayPage();
185 static int helppage(void)
187 struct helpdata *helpdata;
188 int col, crow, n, key, x;
189 char buf[MAXKLEN], Esc_buf[5], cbuf[512];
190 struct action *ktabp;
192 helpdata = (struct helpdata *)flayer->l_data;
194 ktabp = helpdata->ktabp;
195 if (helpdata->grow >= helpdata->maxrow)
196 return -1;
197 helpdata->refgrow = helpdata->grow;
198 helpdata->refcommand_search = helpdata->command_search;
200 /* Clear the help screen */
201 LClearAll(flayer, 0);
203 sprintf(cbuf, "Screen key bindings, page %d of %d.", helpdata->grow / (flayer->l_height - 5) + 1,
204 helpdata->numpages);
205 centerline(cbuf, 0);
206 crow = 2;
208 *Esc_buf = '\0';
209 *buf = '\0';
210 /* XXX fix escape character */
211 if (flayer->l_cvlist && flayer->l_cvlist->c_display) {
212 add_key_to_buf(buf, flayer->l_cvlist->c_display->d_user->u_MetaEsc);
213 add_key_to_buf(Esc_buf, flayer->l_cvlist->c_display->d_user->u_Esc);
214 } else {
215 strncpy(Esc_buf, "??", 5);
216 strncpy(buf, "??", 256);
219 for (; crow < flayer->l_height - 3; crow++) {
220 if (helpdata->grow < 1) {
221 if (ktabp == ktab)
222 sprintf(cbuf, "Command key: %s Literal %s: %s", Esc_buf, Esc_buf, buf);
223 else
224 sprintf(cbuf, "Command class: '%.80s'", helpdata->class);
225 centerline(cbuf, crow);
226 helpdata->grow++;
227 } else if (helpdata->grow >= 2 && helpdata->grow - 2 < helpdata->numrows) {
228 x = 0;
229 for (col = 0;
230 col < helpdata->numcols
231 && (n = helpdata->numrows * col + (helpdata->grow - 2)) < helpdata->num_names; col++) {
232 x += helpdata->inter - !col;
233 n = helpdata->nact[n];
234 buf[0] = '\0';
235 for (key = 0; key < 256 + KMAP_KEYS; key++)
236 if (ktabp[key].nr == n && ktabp[key].args == noargs
237 && strlen(buf) < ARRAY_SIZE(buf) - 7) {
238 strcat(buf, " ");
239 add_key_to_buf(buf, key);
241 PadStr(comms[n].name, helpdata->mcom, x, crow);
242 x += helpdata->mcom;
243 PadStr(buf, helpdata->mkey, x, crow);
244 x += helpdata->mkey;
246 helpdata->grow++;
247 } else if (helpdata->grow - 2 - helpdata->numrows >= helpdata->numskip
248 && helpdata->grow - 2 - helpdata->numrows - helpdata->numskip < helpdata->command_bindings) {
249 while ((n = ktabp[helpdata->command_search].nr) == RC_ILLEGAL
250 || ktabp[helpdata->command_search].args == noargs) {
251 if (++helpdata->command_search >= 256 + KMAP_KEYS)
252 return -1;
254 buf[0] = '\0';
255 add_key_to_buf(buf, helpdata->command_search);
256 PadStr(buf, 5, 0, crow);
257 AddAction(&ktabp[helpdata->command_search++], 5, crow);
258 helpdata->grow++;
259 } else
260 helpdata->grow++;
262 sprintf(cbuf, "[Press Space %s Return to end.]", helpdata->grow < helpdata->maxrow ? "for next page;" : "or");
263 centerline(cbuf, flayer->l_height - 2);
264 LaySetCursor();
265 return 0;
268 static void AddAction(struct action *act, int x, int y)
270 char buf[256];
271 int del, l;
272 char *bp, *cp, **pp;
273 int *lp, ll;
274 int fr;
275 struct mchar mchar_dol;
277 mchar_dol = mchar_blank;
278 mchar_dol.image = '$';
280 fr = flayer->l_width - 1 - x;
281 if (fr <= 0)
282 return;
283 l = strlen(comms[act->nr].name);
285 if (l + 1 > fr)
286 l = fr - 1;
287 PadStr(comms[act->nr].name, l, x, y);
288 x += l;
289 fr -= l + 1;
290 LPutChar(flayer, fr ? &mchar_blank : &mchar_dol, x++, y);
292 pp = act->args;
293 lp = act->argl;
294 while (pp && (cp = *pp) != NULL) {
295 del = 0;
296 bp = buf;
297 ll = *lp++;
298 if (!ll || (strchr(cp, ' ') != NULL)) {
299 if (strchr(cp, '\'') != NULL)
300 *bp++ = del = '"';
301 else
302 *bp++ = del = '\'';
304 while (ll-- && bp < buf + 250)
305 bp += AddXChar(bp, *(unsigned char *)cp++);
306 if (del)
307 *bp++ = del;
308 *bp = 0;
309 if ((fr -= (bp - buf) + 1) < 0) {
310 fr += bp - buf;
311 if (fr > 0)
312 PadStr(buf, fr, x, y);
313 if (fr == 0)
314 LPutChar(flayer, &mchar_dol, x, y);
315 return;
317 PadStr(buf, strlen(buf), x, y);
318 x += strlen(buf);
319 pp++;
320 if (*pp)
321 LPutChar(flayer, fr ? &mchar_blank : &mchar_dol, x++, y);
325 static void add_key_to_buf(char *buf, int key)
327 buf += strlen(buf);
328 if (key < 0)
329 strncpy(buf, "unset", 6);
330 else if (key == ' ')
331 strncpy(buf, "sp", 3);
332 else if (key >= 256) {
333 key = key - 256 + T_CAPS;
334 buf[0] = ':';
335 buf[1] = term[key].tcname[0];
336 buf[2] = term[key].tcname[1];
337 buf[3] = ':';
338 buf[4] = 0;
339 } else
340 buf[AddXChar(buf, key)] = 0;
343 static void HelpRedisplayLine(int y, int xs, int xe, int isblank)
345 if (y < 0) {
346 struct helpdata *helpdata;
348 helpdata = (struct helpdata *)flayer->l_data;
349 helpdata->grow = helpdata->refgrow;
350 helpdata->command_search = helpdata->refcommand_search;
351 helppage();
352 return;
354 if (y != 0 && y != flayer->l_height - 1)
355 return;
356 if (!isblank)
357 LClearArea(flayer, xs, y, xe, y, 0, 0);
362 ** The bindkey help page
366 static void BindkeyProcess(char **, size_t *);
367 static void BindkeyAbort(void);
368 static void BindkeyRedisplayLine(int, int, int, int);
369 static void bindkeypage(void);
371 struct bindkeydata {
372 char *title;
373 struct action *tab;
374 int pos;
375 int last;
376 int page;
377 int pages;
380 static const struct LayFuncs BindkeyLf = {
381 BindkeyProcess,
382 BindkeyAbort,
383 BindkeyRedisplayLine,
384 DefClearLine,
385 DefResize,
386 DefRestore,
387 NULL
390 void display_bindkey(char *title, struct action *tab)
392 struct bindkeydata *bindkeydata;
393 int i, n;
395 if (flayer->l_height < 6) {
396 LMsg(0, "Window height too small for bindkey page");
397 return;
399 if (InitOverlayPage(sizeof(struct bindkeydata), &BindkeyLf, 0))
400 return;
402 bindkeydata = (struct bindkeydata *)flayer->l_data;
403 bindkeydata->title = title;
404 bindkeydata->tab = tab;
406 n = 0;
407 for (i = 0; i < KMAP_KEYS + KMAP_AKEYS + kmap_extn; i++) {
408 if (tab[i].nr != RC_ILLEGAL)
409 n++;
411 bindkeydata->pos = 0;
412 bindkeydata->page = 1;
413 bindkeydata->pages = (n + flayer->l_height - 6) / (flayer->l_height - 5);
414 if (bindkeydata->pages == 0)
415 bindkeydata->pages = 1;
416 flayer->l_x = 0;
417 flayer->l_y = flayer->l_height - 1;
418 bindkeypage();
421 static void BindkeyAbort(void)
423 LAY_CALL_UP(LRefreshAll(flayer, 0));
424 ExitOverlayPage();
427 static void bindkeypage(void)
429 struct bindkeydata *bindkeydata;
430 struct kmap_ext *kme;
431 char tbuf[256];
432 int del, i, y, sl;
433 struct action *act;
434 char *xch, *s, *p;
436 bindkeydata = (struct bindkeydata *)flayer->l_data;
438 LClearAll(flayer, 0);
440 sprintf(tbuf, "%s key bindings, page %d of %d.", bindkeydata->title, bindkeydata->page, bindkeydata->pages);
441 centerline(tbuf, 0);
442 y = 2;
443 for (i = bindkeydata->pos; i < KMAP_KEYS + KMAP_AKEYS + kmap_extn && y < flayer->l_height - 3; i++) {
444 p = tbuf;
445 xch = " ";
446 if (i < KMAP_KEYS) {
447 act = &bindkeydata->tab[i];
448 if (act->nr == RC_ILLEGAL)
449 continue;
450 del = *p++ = ':';
451 s = term[i + T_CAPS].tcname;
452 sl = s ? strlen(s) : 0;
453 } else if (i < KMAP_KEYS + KMAP_AKEYS) {
454 act = &bindkeydata->tab[i];
455 if (act->nr == RC_ILLEGAL)
456 continue;
457 del = *p++ = ':';
458 s = term[i + (T_CAPS - T_OCAPS + T_CURSOR)].tcname;
459 sl = s ? strlen(s) : 0;
460 xch = "[A]";
461 } else {
462 kme = kmap_exts + (i - (KMAP_KEYS + KMAP_AKEYS));
463 del = 0;
464 s = kme->str;
465 sl = kme->fl & ~KMAP_NOTIMEOUT;
466 if ((kme->fl & KMAP_NOTIMEOUT) != 0)
467 xch = "[T]";
468 act = bindkeydata->tab == dmtab ? &kme->dm : bindkeydata->tab == mmtab ? &kme->mm : &kme->um;
469 if (act->nr == RC_ILLEGAL)
470 continue;
472 while (sl-- > 0)
473 p += AddXChar(p, *(unsigned char *)s++);
474 if (del)
475 *p++ = del;
476 *p++ = ' ';
477 while (p < tbuf + 15)
478 *p++ = ' ';
479 sprintf(p, "%s -> ", xch);
480 p += 7;
481 if (p - tbuf > flayer->l_width - 1) {
482 tbuf[flayer->l_width - 2] = '$';
483 tbuf[flayer->l_width - 1] = 0;
485 PadStr(tbuf, strlen(tbuf), 0, y);
486 AddAction(act, strlen(tbuf), y);
487 y++;
489 y++;
490 bindkeydata->last = i;
491 sprintf(tbuf, "[Press Space %s Return to end.]",
492 bindkeydata->page < bindkeydata->pages ? "for next page;" : "or");
493 centerline(tbuf, flayer->l_height - 2);
494 LaySetCursor();
497 static void BindkeyProcess(char **ppbuf, size_t *plen)
499 int done = 0;
500 struct bindkeydata *bindkeydata;
502 bindkeydata = (struct bindkeydata *)flayer->l_data;
503 while (!done && *plen > 0) {
504 switch (**ppbuf) {
505 case ' ':
506 if (bindkeydata->page < bindkeydata->pages) {
507 bindkeydata->pos = bindkeydata->last;
508 bindkeydata->page++;
509 bindkeypage();
510 break;
512 /* FALLTHROUGH */
513 case '\r':
514 case '\n':
515 done = 1;
516 break;
517 default:
518 break;
520 ++*ppbuf;
521 --*plen;
523 if (done)
524 BindkeyAbort();
527 static void BindkeyRedisplayLine(int y, int xs, int xe, int isblank)
529 if (y < 0) {
530 bindkeypage();
531 return;
533 if (y != 0 && y != flayer->l_height - 1)
534 return;
535 if (!isblank)
536 LClearArea(flayer, xs, y, xe, y, 0, 0);
541 ** The zmodem active page
545 static void ZmodemRedisplayLine(int, int, int, int);
546 static int ZmodemResize(int, int);
548 static const struct LayFuncs ZmodemLf = {
549 DefProcess,
550 NULL,
551 ZmodemRedisplayLine,
552 DefClearLine,
553 ZmodemResize,
554 DefRestore,
555 NULL
558 static int ZmodemResize(int wi, int he)
560 flayer->l_width = wi;
561 flayer->l_height = he;
562 flayer->l_x = flayer->l_width > 32 ? 32 : 0;
563 return 0;
566 static void ZmodemRedisplayLine(int y, int xs, int xe, int isblank)
568 DefRedisplayLine(y, xs, xe, isblank);
569 if (y == 0 && xs == 0)
570 LPutStr(flayer, "Zmodem active on another display", flayer->l_width > 32 ? 32 : flayer->l_width,
571 &mchar_blank, 0, 0);
574 void ZmodemPage(void)
576 if (InitOverlayPage(1, &ZmodemLf, 1))
577 return;
578 LRefreshAll(flayer, 0);
579 flayer->l_x = flayer->l_width > 32 ? 32 : 0;
580 flayer->l_y = 0;
583 static void PadStr(char *str, int n, int x, int y)
585 int l;
587 l = strlen(str);
588 if (l > n)
589 l = n;
590 LPutStr(flayer, str, l, &mchar_blank, x, y);
591 if (l < n)
592 LPutStr(flayer, (char *)blank, n - l, &mchar_blank, x + l, y);