Update copyright year to 2015.
[cboard.git] / src / menu.c
blobb9c72a3e6b37b445614b3c2db332a10f59ec0208
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2015 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 set_menu_vars(int c, int rows, int items, int *item, int *top)
42 int selected = *item;
43 int toppos = *top;
45 switch (c) {
46 case KEY_HOME:
47 selected = toppos = 0;
48 break;
49 case KEY_END:
50 selected = items;
51 toppos = items - rows + 1;
52 break;
53 case KEY_UP:
54 if (selected - 1 < 0) {
55 selected = items;
57 toppos = selected - rows + 1;
59 else {
60 selected--;
62 if (toppos && selected <= toppos)
63 toppos = selected;
65 break;
66 case KEY_DOWN:
67 if (selected + 1 > items )
68 selected = toppos = 0;
69 else {
70 selected++;
72 if (selected - toppos >= rows)
73 toppos++;
75 break;
76 case KEY_PPAGE:
77 selected -= rows;
79 if (selected < 0)
80 selected = 0;
82 toppos = selected - rows + 1;
84 if (toppos < 0)
85 toppos = 0;
86 break;
87 case KEY_NPAGE:
88 selected += rows;
90 if (selected > items)
91 selected = items;
93 toppos = selected - rows + 1;
95 if (toppos < 0)
96 toppos = 0;
97 break;
98 default:
99 if (selected == MAX_MENU_HEIGHT - 4)
100 toppos = 1;
101 else if (selected <= rows)
102 toppos = 0;
103 else {
104 if (selected - toppos > rows)
105 toppos = selected - rows + 1;
107 break;
110 if (toppos < 0)
111 toppos = 0;
113 if (selected >= items) {
114 selected = items;
115 toppos = selected - rows + 1;
118 if (toppos < 0)
119 toppos = 0;
121 *item = selected;
122 *top = toppos;
125 static void fix_menu_vals(WIN *win)
127 struct menu_input_s *m = win->data;
128 char buf[COLS - 4];
129 int i = 0;
130 wchar_t *wc;
131 size_t len;
132 int n, nlen = 0, vlen = 0;
134 for (i = 0; m->items[i]; i++);
135 m->total = i;
136 snprintf(buf, sizeof(buf), _("Item %i %s %i %s"), m->selected + 1,
137 _("of"), m->total, _("Type F1 for help"));
139 if (!m->cstatic) {
140 win->cols = 0;
142 for (i = 0; m->items[i]; i++) {
143 wc = str_to_wchar (m->items[i]->name);
144 n = wcslen (wc);
145 free (wc);
146 if (nlen < n)
147 nlen = n;
149 if (m->items[i]->value) {
150 wc = str_to_wchar (m->items[i]->value);
151 n = wcslen (wc);
152 if (vlen < n)
153 vlen = n;
155 n = vlen + nlen;
156 free (wc);
158 else {
159 char *s = _("empty value");
161 n = (!m->name_only) ? mblen(s, strlen (s)) + nlen : nlen;
164 if (win->cols < n)
165 win->cols = n;
169 if (!m->rstatic)
170 win->rows = i;
172 if (!m->rstatic && win->title)
173 win->rows++;
175 if (!m->cstatic)
176 win->cols += (!m->name_only) ? 5 : 2; // 2 box, 3 separator
178 if (!m->rstatic)
179 win->rows += 3; // 2 box, 1 prompt
181 if (!m->rstatic && win->rows > MAX_MENU_HEIGHT)
182 win->rows = MAX_MENU_HEIGHT;
184 wc = str_to_wchar (buf);
185 len = wcslen (wc);
186 free (wc);
188 if (win->cols < len)
189 win->cols = len + 2; // 2 box
191 if (win->cols > MAX_MENU_WIDTH)
192 win->cols = MAX_MENU_WIDTH;
194 wresize(win->w, win->rows, win->cols);
195 replace_panel(win->p, win->w);
196 move_panel(win->p, (m->ystatic == -1) ? CALCPOSY(win->rows) : m->ystatic,
197 (m->xstatic == -1) ? CALCPOSX(win->cols) : m->xstatic);
198 keypad(win->w, TRUE);
199 wmove(win->w, 0, 0);
200 wclrtobot(win->w);
201 window_draw_title(win->w, win->title, win->cols, CP_INPUT_TITLE,
202 CP_INPUT_BORDER);
203 window_draw_prompt(win->w, win->rows - 2, win->cols, buf, CP_INPUT_PROMPT);
206 static void draw_menu(WIN *win)
208 int i;
209 int y = 0;
210 struct menu_input_s *m = win->data;
212 if (!m->items)
213 return;
215 for (i = m->top, y = 2; m->items[i] && y < win->rows - 2; i++, y++) {
216 if (i == m->selected)
217 wattron(win->w, CP_MENU_SELECTED);
218 else if (m->items[i]->selected)
219 wattron(win->w, CP_MENU_HIGHLIGHT);
221 if (m->print_func) {
222 m->print_line = y;
223 m->item = m->items[i];
224 (*m->print_func)(win);
227 if (i == m->selected)
228 wattroff(win->w, CP_MENU_SELECTED);
229 else if (m->items[i]->selected)
230 wattroff(win->w, CP_MENU_HIGHLIGHT);
234 static int display_menu(WIN *win)
236 struct menu_input_s *m = win->data;
237 int i, n;
238 char *p;
240 cbreak();
241 noecho();
242 keypad(win->w, TRUE);
243 nl();
245 if (m->keys) {
246 for (i = 0; m->keys[i]; i++) {
247 if (win->c == m->keys[i]->c) {
248 (*m->keys[i]->func)(m);
249 m->items = (*m->func)(win);
250 m->search[0] = 0;
251 goto end;
256 switch (win->c) {
257 case REFRESH_MENU:
258 m->items = (*m->func)(win);
259 pushkey = 0;
260 break;
261 case -1:
262 pushkey = 0;
263 goto done;
264 case KEY_HOME:
265 case KEY_END:
266 case KEY_UP:
267 case KEY_DOWN:
268 case KEY_NPAGE:
269 case KEY_PPAGE:
270 m->search[0] = 0;
271 break;
272 default:
273 if (!win->c)
274 break;
276 if (strlen(m->search) + 1 > sizeof(m->search) - 1)
277 m->search[0] = 0;
279 p = m->search;
281 while (*p)
282 p++;
284 *p++ = win->c;
285 *p = 0;
286 n = m->selected;
288 if (m->items) {
289 for (i = 0; m->items[i]; i++) {
290 if (strncasecmp(m->search, m->items[i]->name,
291 strlen(m->search)) == 0) {
292 m->selected = i;
293 break;
298 if (n == m->selected)
299 m->search[0] = 0;
302 end:
303 set_menu_vars(win->c, win->rows - 4, m->total - 1, &m->selected, &m->top);
304 fix_menu_vals(win);
305 draw_menu(win);
307 if (m->draw_exit_func)
308 (*m->draw_exit_func)(m);
310 update_all(gp);
311 return 1;
313 done:
314 win->data = m->data;
316 if (m->items) {
317 for (i = 0; m->items[i]; i++) {
318 if (!m->nofree)
319 free(m->items[i]->name);
321 if (!m->nofree && m->items[i]->value)
322 free(m->items[i]->value);
324 free(m->items[i]);
327 free(m->items);
330 if (m->keys) {
331 for (i = 0; m->keys[i]; i++)
332 free(m->keys[i]);
334 free(m->keys);
337 free(m);
338 update_all(gp);
339 return 0;
342 WIN *construct_menu(int rows, int cols, int y, int x, const char *title,
343 int name_only, menu_items_fn *func,
344 struct menu_key_s **keys, void *data,
345 menu_print_func *pfunc, window_exit_func *efunc)
347 WIN *win;
348 struct menu_input_s *m;
350 m = Calloc(1, sizeof(struct menu_input_s));
351 win = window_create(title, (rows <= 0) ? 1 : rows, (cols <= 0) ? 1 : cols,
352 (y >= 0) ? y : 0, (x >= 0) ? x : 0, display_menu, m, efunc);
353 m = win->data;
354 m->ystatic = y;
355 m->xstatic = x;
357 if (rows > 0)
358 m->rstatic = 1;
360 if (cols > 0)
361 m->cstatic = 1;
363 m->print_func = pfunc;
364 m->func = func;
365 m->keys = keys;
366 m->data = data;
367 m->name_only = name_only;
368 wbkgd(win->w, CP_MENU);
369 cbreak();
370 noecho();
371 keypad(win->w, TRUE);
372 nl();
373 m->items = (*m->func)(win);
374 fix_menu_vals(win);
375 (*win->func)(win);
376 return win;
379 void add_menu_key(struct menu_key_s ***dst, wint_t c, menu_key func)
381 int n = 0;
382 struct menu_key_s **keys = *dst;
384 if (keys)
385 for (; keys[n]; n++);
387 keys = Realloc(keys, (n + 2) * sizeof(struct menu_key_s *));
388 keys[n] = Malloc(sizeof(struct menu_key_s));
389 keys[n]->c = c;
390 keys[n++]->func = func;
391 keys[n] = NULL;
392 *dst = keys;