Add description field to macros.
[cboard.git] / src / menu.c
blob981746129d93e5040151ed25f63aca3aab3dbf47
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2018 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
28 #ifdef HAVE_STRINGS_H
29 #include <strings.h>
30 #endif
32 #include "common.h"
33 #include "conf.h"
34 #include "colors.h"
35 #include "strings.h"
36 #include "misc.h"
37 #include "window.h"
38 #include "menu.h"
40 static void
41 set_menu_vars (int c, int rows, int items, int *item, int *top)
43 int selected = *item;
44 int toppos = *top;
46 switch (c)
48 case KEY_HOME:
49 selected = toppos = 0;
50 break;
51 case KEY_END:
52 selected = items;
53 toppos = items - rows + 1;
54 break;
55 case KEY_UP:
56 if (selected - 1 < 0)
58 selected = items;
60 toppos = selected - rows + 1;
62 else
64 selected--;
66 if (toppos && selected <= toppos)
67 toppos = selected;
69 break;
70 case KEY_DOWN:
71 if (selected + 1 > items)
72 selected = toppos = 0;
73 else
75 selected++;
77 if (selected - toppos >= rows)
78 toppos++;
80 break;
81 case KEY_PPAGE:
82 selected -= rows;
84 if (selected < 0)
85 selected = 0;
87 toppos = selected - rows + 1;
89 if (toppos < 0)
90 toppos = 0;
91 break;
92 case KEY_NPAGE:
93 selected += rows;
95 if (selected > items)
96 selected = items;
98 toppos = selected - rows + 1;
100 if (toppos < 0)
101 toppos = 0;
102 break;
103 default:
104 if (selected == MAX_MENU_HEIGHT - 4)
105 toppos = 1;
106 else if (selected <= rows)
107 toppos = 0;
108 else
110 if (selected - toppos > rows)
111 toppos = selected - rows + 1;
113 break;
116 if (toppos < 0)
117 toppos = 0;
119 if (selected >= items)
121 selected = items;
122 toppos = selected - rows + 1;
125 if (toppos < 0)
126 toppos = 0;
128 *item = selected;
129 *top = toppos;
132 static void
133 fix_menu_vals (WIN * win)
135 struct menu_input_s *m = win->data;
136 char buf[COLS - 4];
137 int i = 0;
138 wchar_t *wc;
139 size_t len;
140 int n, nlen = 0, vlen = 0;
142 for (i = 0; m->items[i]; i++);
143 m->total = i;
144 snprintf (buf, sizeof (buf), _("Item %i %s %i %s"), m->selected + 1,
145 _("of"), m->total, _("Type F1 for help"));
147 if (!m->cstatic)
149 win->cols = 0;
151 for (i = 0; m->items[i]; i++)
153 wc = str_to_wchar (m->items[i]->name);
154 n = wcslen (wc);
155 free (wc);
156 if (nlen < n)
157 nlen = n;
159 if (m->items[i]->value)
161 wc = str_to_wchar (m->items[i]->value);
162 n = wcslen (wc);
163 if (vlen < n)
164 vlen = n;
166 n = vlen + nlen;
167 free (wc);
169 else
171 char *s = _("empty value");
173 n = (!m->name_only) ? mblen (s, strlen (s)) + nlen : nlen;
176 if (win->cols < n)
177 win->cols = n;
181 if (!m->rstatic)
182 win->rows = i;
184 if (!m->rstatic && win->title)
185 win->rows++;
187 if (!m->cstatic)
188 win->cols += (!m->name_only) ? 5 : 2; // 2 box, 3 separator
190 if (!m->rstatic)
191 win->rows += 3; // 2 box, 1 prompt
193 if (!m->rstatic && win->rows > MAX_MENU_HEIGHT)
194 win->rows = MAX_MENU_HEIGHT;
196 wc = str_to_wchar (buf);
197 len = wcslen (wc);
198 free (wc);
200 if (win->cols < len)
201 win->cols = len + 2; // 2 box
203 if (win->cols > MAX_MENU_WIDTH)
204 win->cols = MAX_MENU_WIDTH;
206 wresize (win->w, win->rows, win->cols);
207 replace_panel (win->p, win->w);
208 move_panel (win->p, (m->ystatic == -1) ? CALCPOSY (win->rows) : m->ystatic,
209 (m->xstatic == -1) ? CALCPOSX (win->cols) : m->xstatic);
210 keypad (win->w, TRUE);
211 wmove (win->w, 0, 0);
212 wclrtobot (win->w);
213 window_draw_title (win->w, win->title, win->cols, CP_INPUT_TITLE,
214 CP_INPUT_BORDER);
215 window_draw_prompt (win->w, win->rows - 2, win->cols, buf, CP_INPUT_PROMPT);
218 void
219 redraw_menu (WIN * win)
221 int i;
222 int y = 0;
223 struct menu_input_s *m = win->data;
225 if (!m->items)
226 return;
228 fix_menu_vals (win);
230 for (i = m->top, y = 2; m->items[i] && y < win->rows - 2; i++, y++)
232 if (i == m->selected)
233 wattron (win->w, CP_MENU_SELECTED);
234 else if (m->items[i]->selected)
235 wattron (win->w, CP_MENU_HIGHLIGHT);
237 if (m->print_func)
239 m->print_line = y;
240 m->item = m->items[i];
241 (*m->print_func) (win);
244 if (i == m->selected)
245 wattroff (win->w, CP_MENU_SELECTED);
246 else if (m->items[i]->selected)
247 wattroff (win->w, CP_MENU_HIGHLIGHT);
251 static int
252 display_menu (WIN * win)
254 struct menu_input_s *m = win->data;
255 int i, n;
256 char *p;
258 cbreak ();
259 noecho ();
260 keypad (win->w, TRUE);
261 nl ();
263 if (m->keys)
265 for (i = 0; m->keys[i]; i++)
267 if (win->c == m->keys[i]->c)
269 (*m->keys[i]->func) (m);
270 m->items = (*m->func) (win);
271 m->search[0] = 0;
272 goto end;
277 switch (win->c)
279 case REFRESH_MENU:
280 m->items = (*m->func) (win);
281 pushkey = 0;
282 break;
283 case -1:
284 pushkey = 0;
285 goto done;
286 case KEY_HOME:
287 case KEY_END:
288 case KEY_UP:
289 case KEY_DOWN:
290 case KEY_NPAGE:
291 case KEY_PPAGE:
292 m->search[0] = 0;
293 break;
294 default:
295 if (!win->c)
296 break;
298 if (strlen (m->search) + 1 > sizeof (m->search) - 1)
299 m->search[0] = 0;
301 p = m->search;
303 while (*p)
304 p++;
306 *p++ = win->c;
307 *p = 0;
308 n = m->selected;
310 if (m->items)
312 for (i = 0; m->items[i]; i++)
314 if (strncasecmp (m->search, m->items[i]->name,
315 strlen (m->search)) == 0)
317 m->selected = i;
318 break;
323 if (n == m->selected)
324 m->search[0] = 0;
327 end:
328 set_menu_vars (win->c, win->rows - 4, m->total - 1, &m->selected, &m->top);
329 redraw_menu (win);
331 if (m->draw_exit_func)
332 (*m->draw_exit_func) (m);
334 update_all (gp);
335 return 1;
337 done:
338 win->data = m->data;
340 if (m->items)
342 for (i = 0; m->items[i]; i++)
344 if (!m->nofree)
345 free (m->items[i]->name);
347 if (!m->nofree && m->items[i]->value)
348 free (m->items[i]->value);
350 free (m->items[i]);
353 free (m->items);
356 if (m->keys)
358 for (i = 0; m->keys[i]; i++)
359 free (m->keys[i]);
361 free (m->keys);
364 free (m);
365 update_all (gp);
366 return 0;
369 static void
370 menu_resize_func (WIN *w)
372 struct menu_input_s *m = w->data;
374 w->rows = m->total >= LINES - 5 ? LINES - 1 : m->total + 5;
375 w->cols = w->cols > COLS - 2 ? COLS - 2 : w->cols;
376 wresize (w->w, w->rows, w->cols);
377 move_panel (w->p, CALCPOSY (w->rows), CALCPOSX (w->cols));
378 wclear (w->w);
379 redraw_menu (w);
382 WIN *
383 construct_menu (int rows, int cols, int y, int x, const char *title,
384 int name_only, menu_items_fn * func,
385 struct menu_key_s ** keys, void *data,
386 menu_print_func * pfunc, window_exit_func * efunc,
387 window_resize_func *rfunc)
389 WIN *win;
390 struct menu_input_s *m;
392 m = Calloc (1, sizeof (struct menu_input_s));
393 win = window_create (title, (rows <= 0) ? 1 : rows, (cols <= 0) ? 1 : cols,
394 (y >= 0) ? y : 0, (x >= 0) ? x : 0, display_menu, m,
395 efunc, rfunc ? rfunc : menu_resize_func);
396 m = win->data;
397 m->ystatic = y;
398 m->xstatic = x;
400 if (rows > 0)
401 m->rstatic = 1;
403 if (cols > 0)
404 m->cstatic = 1;
406 m->print_func = pfunc;
407 m->func = func;
408 m->keys = keys;
409 m->data = data;
410 m->name_only = name_only;
411 wbkgd (win->w, CP_MENU);
412 cbreak ();
413 noecho ();
414 keypad (win->w, TRUE);
415 nl ();
416 m->items = (*m->func) (win);
417 fix_menu_vals (win);
418 (*win->func) (win);
419 return win;
422 void
423 add_menu_key (struct menu_key_s ***dst, wint_t c, menu_key func)
425 int n = 0;
426 struct menu_key_s **keys = *dst;
428 if (keys)
429 for (; keys[n]; n++);
431 keys = Realloc (keys, (n + 2) * sizeof (struct menu_key_s *));
432 keys[n] = Malloc (sizeof (struct menu_key_s));
433 keys[n]->c = c;
434 keys[n++]->func = func;
435 keys[n] = NULL;
436 *dst = keys;