Real men don't need the stupid silly help on top/bottom of the screen.
[apps/madmutt.git] / help.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #define HELP_C
11
12 #include <lib-lib/lib-lib.h>
13
14 #include <lib-ui/curses.h>
15
16 #include "mutt.h"
17 #include "charset.h"
18 #include "keymap.h"
19 #include "pager.h"
20
21 static struct binding_t *help_lookupFunction (int op, int menu)
22 {
23   int i;
24   struct binding_t *map;
25
26   if (menu != MENU_PAGER) {
27     /* first look in the generic map for the function */
28     for (i = 0; OpGeneric[i].name; i++)
29       if (OpGeneric[i].op == op)
30         return (&OpGeneric[i]);
31   }
32
33   if ((map = km_get_table (menu))) {
34     for (i = 0; map[i].name; i++)
35       if (map[i].op == op)
36         return (&map[i]);
37   }
38
39   return (NULL);
40 }
41
42 void mutt_make_help (char *d, ssize_t dlen, const char *txt, int menu, int op)
43 {
44   char buf[STRING];
45
46   if (km_expand_key (buf, sizeof (buf), km_find_func (menu, op)) ||
47       km_expand_key (buf, sizeof (buf), km_find_func (MENU_GENERIC, op)))
48     snprintf (d, dlen, "%s:%s", buf, txt);
49   else
50     d[0] = 0;
51 }
52
53 static int print_macro (FILE * f, int maxwidth, const char **macro)
54 {
55   int n = maxwidth;
56   wchar_t wc;
57   int w;
58   ssize_t k;
59   ssize_t len = m_strlen(*macro);
60   mbstate_t mbstate1, mbstate2;
61
62   p_clear(&mbstate1, 1);
63   p_clear(&mbstate2, 1);
64   for (; len && (k = mbrtowc (&wc, *macro, len, &mbstate1));
65        *macro += k, len -= k) {
66     if (k == -1 || k == -2) {
67       k = (k == -1) ? 1 : len;
68       wc = CharsetReplacement;
69     }
70     /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
71     if (iswprint(wc) && (w = wcwidth (wc)) >= 0) {
72       if (w > n)
73         break;
74       n -= w;
75       {
76         char buf[MB_LEN_MAX * 2];
77         ssize_t n1, n2;
78
79         if ((n1 = wcrtomb(buf, wc, &mbstate2)) != -1 &&
80             (n2 = wcrtomb(buf + n1, 0, &mbstate2)) != -1)
81           fputs (buf, f);
82       }
83     }
84     else if (wc < 0x20 || wc == 0x7f) {
85       if (2 > n)
86         break;
87       n -= 2;
88       if (wc == '\033')
89         fprintf (f, "\\e");
90       else if (wc == '\n')
91         fprintf (f, "\\n");
92       else if (wc == '\r')
93         fprintf (f, "\\r");
94       else if (wc == '\t')
95         fprintf (f, "\\t");
96       else
97         fprintf (f, "^%c", (char) ((wc + '@') & 0x7f));
98     }
99     else {
100       if (1 > n)
101         break;
102       n -= 1;
103       fprintf (f, "?");
104     }
105   }
106   return (maxwidth - n);
107 }
108
109 static int pad (FILE * f, int col, int i)
110 {
111   char fmt[8];
112
113   if (col < i) {
114     snprintf (fmt, sizeof (fmt), "%%-%ds", i - col);
115     fprintf (f, fmt, "");
116     return (i);
117   }
118   fputc (' ', f);
119   return (col + 1);
120 }
121
122 static void help_format_line (FILE * f, int ismacro,
123                               const char *t1, const char *t2, const char *t3)
124 {
125   int col;
126   int col_a, col_b;
127   int split;
128   int n;
129
130   fputs (t1, f);
131
132   /* don't try to press string into one line with less than 40 characters.
133      The double paranthesis avoid a gcc warning, sigh ... */
134   if ((split = COLS < 40)) {
135     col_a = col = 0;
136     col_b = LONG_STRING;
137     fputc ('\n', f);
138   }
139   else {
140     col_a = COLS > 83 ? (COLS - 32) >> 2 : 12;
141     col_b = COLS > 49 ? (COLS - 10) >> 1 : 19;
142     col = pad (f, m_strlen(t1), col_a);
143   }
144
145   if (ismacro > 0) {
146     fputs ("_\010M ", f);
147     col += 2;
148
149     if (!split) {
150       col += print_macro (f, col_b - col - 4, &t2);
151       if (m_strlen(t2) > col_b - col)
152         t2 = "...";
153     }
154   }
155
156   col += print_macro (f, col_b - col - 1, &t2);
157   if (split)
158     fputc ('\n', f);
159   else
160     col = pad (f, col, col_b);
161
162   if (split) {
163     print_macro (f, LONG_STRING, &t3);
164     fputc ('\n', f);
165   }
166   else {
167     while (*t3) {
168       n = COLS - col;
169
170       if (ismacro >= 0) {
171         t3 = vskipspaces(t3);
172
173         /* FIXME: this is completely wrong */
174         if ((n = m_strlen(t3)) > COLS - col) {
175           n = COLS - col;
176           for (col_a = n; col_a > 0 && t3[col_a] != ' '; col_a--);
177           if (col_a)
178             n = col_a;
179         }
180       }
181
182       print_macro (f, n, &t3);
183
184       if (*t3) {
185         n += col - COLS;
186         if (option (OPTMARKERS))
187           ++n;
188         col = pad (f, n, col_b);
189       }
190     }
191   }
192
193   fputc ('\n', f);
194 }
195
196 static void dump_menu (FILE * f, int menu)
197 {
198   struct keymap_t *map;
199   struct binding_t *b;
200   char buf[STRING];
201
202   /* browse through the keymap table */
203   for (map = Keymaps[menu]; map; map = map->next) {
204     if (map->op != OP_NULL) {
205       km_expand_key (buf, sizeof (buf), map);
206
207       if (map->op == OP_MACRO) {
208         if (map->descr == NULL)
209           help_format_line (f, -1, buf, "macro", map->macro);
210         else
211           help_format_line (f, 1, buf, map->macro, map->descr);
212       }
213       else {
214         b = help_lookupFunction (map->op, menu);
215         help_format_line (f, 0, buf, b ? b->name : "UNKNOWN",
216                      b ? _(HelpStrings[b->op]) :
217                      _("ERROR: please report this bug"));
218       }
219     }
220   }
221 }
222
223 static int is_bound (struct keymap_t *map, int op)
224 {
225   for (; map; map = map->next)
226     if (map->op == op)
227       return 1;
228   return 0;
229 }
230
231 static void dump_unbound (FILE * f,
232                           struct binding_t *funcs,
233                           struct keymap_t *map, struct keymap_t *aux)
234 {
235   int i;
236
237   for (i = 0; funcs[i].name; i++) {
238     if (!is_bound (map, funcs[i].op) &&
239         (!aux || !is_bound (aux, funcs[i].op)))
240       help_format_line (f, 0, funcs[i].name, "", _(HelpStrings[funcs[i].op]));
241   }
242 }
243
244 void mutt_help (int menu)
245 {
246     char tmp[_POSIX_PATH_MAX];
247     char buf[STRING];
248     const char *desc;
249     FILE *f;
250     struct binding_t *funcs;
251
252     funcs = km_get_table (menu);
253     desc = mutt_getnamebyvalue (menu, Menus);
254     if (!desc)
255         desc = _("<UNKNOWN>");
256
257     do {
258         f = m_tempfile(tmp, sizeof(tmp), NONULL(mod_core.tmpdir), NULL);
259         if (!f) {
260             mutt_perror(tmp);
261             return;
262         }
263
264         dump_menu(f, menu);
265         if (menu != MENU_EDITOR && menu != MENU_PAGER) {
266             fputs(_("\nGeneric bindings:\n\n"), f);
267             dump_menu(f, MENU_GENERIC);
268         }
269
270         fputs(_("\nUnbound functions:\n\n"), f);
271         if (funcs)
272             dump_unbound(f, funcs, Keymaps[menu], NULL);
273
274         if (menu != MENU_PAGER)
275             dump_unbound (f, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu]);
276
277         m_fclose(&f);
278
279         snprintf(buf, sizeof (buf), _("Help for %s"), desc);
280     } while (mutt_pager(buf, tmp,
281                         M_PAGER_RETWINCH | M_PAGER_MARKER | M_PAGER_NSKIP,
282                         NULL) == OP_REFORMAT_WINCH);
283 }
284
285 #undef HELP_C