Each menu has it's own print function pointer.
[cboard.git] / src / menu.c
blobd04bd542ebc77ae289abcbd330dd434a1000dba8
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2006 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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #ifdef HAVE_NCURSES_H
29 #include <ncurses.h>
30 #endif
32 #ifdef HAVE_PANEL_H
33 #include <panel.h>
34 #endif
36 #include "chess.h"
37 #include "conf.h"
38 #include "colors.h"
39 #include "strings.h"
40 #include "misc.h"
41 #include "window.h"
42 #include "menu.h"
44 void set_menu_vars(int c, int rows, int items, int *item, int *top)
46 int selected = *item;
47 int toppos = *top;
49 switch (c) {
50 case KEY_HOME:
51 selected = toppos = 0;
52 break;
53 case KEY_END:
54 selected = items;
55 toppos = items - rows + 1;
56 break;
57 case KEY_UP:
58 if (selected - 1 < 0) {
59 selected = items;
61 toppos = selected - rows + 1;
63 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 {
74 selected++;
76 if (selected - toppos >= rows)
77 toppos++;
79 break;
80 case KEY_PPAGE:
81 selected -= rows;
83 if (selected < 0)
84 selected = 0;
86 toppos = selected - rows + 1;
88 if (toppos < 0)
89 toppos = 0;
90 break;
91 case KEY_NPAGE:
92 selected += rows;
94 if (selected > items)
95 selected = items;
97 toppos = selected - rows + 1;
99 if (toppos < 0)
100 toppos = 0;
101 break;
102 default:
103 if (selected == MAX_MENU_HEIGHT - 4)
104 toppos = 1;
105 else if (selected <= rows)
106 toppos = 0;
107 else
108 toppos = selected - rows + 1;
109 break;
112 if (toppos < 0)
113 toppos = 0;
115 if (selected > items) {
116 selected = items;
117 toppos = selected - rows + 1;
120 if (toppos < 0)
121 toppos = 0;
123 *item = selected;
124 *top = toppos;
127 static void fix_menu_vals(WIN *win)
129 struct menu_input_s *m = win->data;
130 char buf[COLS - 4];
131 int i = 0, n;
132 int nlen = 0;
134 if (!m->cstatic) {
135 win->cols = 0;
137 for (i = 0; m->items[i]; i++) {
138 n = strlen(m->items[i]->name);
140 if (nlen < n)
141 nlen = n;
143 if (m->items[i]->value)
144 n = strlen(m->items[i]->value) + nlen;
145 else
146 n = (!m->name_only) ? strlen(UNKNOWN) + nlen : nlen;
148 if (win->cols < n)
149 win->cols = n;
153 if (!m->rstatic)
154 win->rows = i;
156 for (i = 0; m->items[i]; i++);
157 m->total = i;
159 if (!m->rstatic && m->title)
160 win->rows++;
162 if (!m->cstatic)
163 win->cols += (!m->name_only) ? 5 : 2; // 2 box, 3 separator
165 if (!m->rstatic)
166 win->rows += 3; // 2 box, 1 prompt
168 if (!m->rstatic && win->rows > MAX_MENU_HEIGHT)
169 win->rows = MAX_MENU_HEIGHT;
171 snprintf(buf, sizeof(buf), "Item %i %s %i %s", m->selected + 1,
172 N_OF_N_STR, m->total, HELP_PROMPT);
174 if (win->cols < strlen(buf))
175 win->cols = strlen(buf) + 2; // 2 box
177 if (win->cols > MAX_MENU_WIDTH)
178 win->cols = MAX_MENU_WIDTH;
180 wresize(win->w, win->rows, win->cols);
181 replace_panel(win->p, win->w);
182 move_panel(win->p, (m->ystatic == -1) ? CALCPOSY(win->rows) : m->ystatic,
183 (m->xstatic == -1) ? CALCPOSX(win->cols) : m->xstatic);
184 keypad(win->w, TRUE);
185 wmove(win->w, 0, 0);
186 wclrtobot(win->w);
187 window_draw_title(win->w, m->title, win->cols, CP_INPUT_TITLE,
188 CP_INPUT_BORDER);
189 window_draw_prompt(win->w, win->rows - 2, win->cols, buf, CP_INPUT_PROMPT);
192 static void draw_menu(WIN *win)
194 int i;
195 int y = 0;
196 struct menu_input_s *m = win->data;
198 if (!m->items)
199 return;
201 for (i = m->top, y = 2; m->items[i] && y < win->rows - 2; i++, y++) {
202 if (i == m->selected)
203 wattron(win->w, CP_MENU_SELECTED);
204 else if (m->items[i]->selected)
205 wattron(win->w, CP_MENU_HIGHLIGHT);
207 if (m->print_func) {
208 m->print_line = y;
209 m->item = m->items[i];
210 (*m->print_func)(win);
213 if (i == m->selected)
214 wattroff(win->w, CP_MENU_SELECTED);
215 else if (m->items[i]->selected)
216 wattroff(win->w, CP_MENU_HIGHLIGHT);
220 int display_menu(WIN *win)
222 struct menu_input_s *m = win->data;
223 int i, n;
224 int key = 0;
225 char *p;
227 cbreak();
228 noecho();
229 keypad(win->w, TRUE);
230 nl();
232 if (m->keys) {
233 for (i = 0; m->keys[i]; i++) {
234 if (win->c == m->keys[i]->c) {
235 (*m->keys[i]->func)(m);
236 m->items = (*m->func)(win);
237 key = 1;
238 m->search[0] = 0;
239 goto end;
244 switch (win->c) {
245 case REFRESH_MENU:
246 m->items = (*m->func)(win);
247 pushkey = 0;
248 break;
249 case -1:
250 pushkey = 0;
251 goto done;
252 case KEY_HOME:
253 case KEY_END:
254 case KEY_UP:
255 case KEY_DOWN:
256 case KEY_NPAGE:
257 case KEY_PPAGE:
258 m->search[0] = 0;
259 break;
260 default:
261 if (!win->c)
262 break;
264 if (strlen(m->search) + 1 > sizeof(m->search) - 1)
265 m->search[0] = 0;
267 p = m->search;
269 while (*p)
270 p++;
272 *p++ = win->c;
273 *p = 0;
274 n = m->selected;
276 if (m->items) {
277 for (i = 0; m->items[i]; i++) {
278 if (strncasecmp(m->search, m->items[i]->name,
279 strlen(m->search)) == 0) {
280 m->selected = i;
281 break;
286 if (n == m->selected)
287 m->search[0] = 0;
290 end:
291 set_menu_vars(win->c, win->rows - 4, m->total - 1, &m->selected, &m->top);
292 fix_menu_vals(win);
293 draw_menu(win);
295 if (m->draw_exit_func)
296 (*m->draw_exit_func)(m);
298 return 1;
300 done:
301 win->data = m->data;
303 if (m->items) {
304 for (i = 0; m->items[i]; i++) {
305 if (!m->nofree)
306 free(m->items[i]->name);
308 if (!m->nofree && m->items[i]->value)
309 free(m->items[i]->value);
311 free(m->items[i]);
314 free(m->items);
317 if (m->keys) {
318 for (i = 0; m->keys[i]; i++)
319 free(m->keys[i]);
321 free(m->keys);
324 if (m->title)
325 free(m->title);
327 free(m);
328 return 0;
331 WIN *construct_menu(int rows, int cols, int y, int x, const char *title,
332 int name_only, menu_items *func, struct menu_key_s **keys, void *data,
333 menu_print_func *pfunc, window_exit_func *efunc)
335 WIN *win;
336 int h = 1, w = 1;
337 struct menu_input_s *m;
339 m = Calloc(1, sizeof(struct menu_input_s));
340 win = window_create(rows, cols, (y >= 0) ? y : CALCPOSY(h),
341 (x >= 0) ? x : CALCPOSX(w), display_menu, m, efunc);
342 m = win->data;
343 m->ystatic = y;
344 m->xstatic = x;
346 if (rows)
347 m->rstatic = 1;
349 if (cols)
350 m->cstatic = 1;
352 m->print_func = pfunc;
353 m->func = func;
354 m->keys = keys;
355 m->data = data;
356 m->name_only = name_only;
358 if (title)
359 m->title = strdup(title);
361 wbkgd(win->w, CP_MENU);
362 cbreak();
363 noecho();
364 keypad(win->w, TRUE);
365 nl();
366 m->items = (*m->func)(win);
367 fix_menu_vals(win);
368 (*win->func)(win);
369 return win;
372 void add_menu_key(struct menu_key_s ***dst, int c, menu_key func)
374 int n = 0;
375 struct menu_key_s **keys = *dst;
377 if (keys)
378 for (; keys[n]; n++);
380 keys = Realloc(keys, (n + 2) * sizeof(struct menu_key_s *));
381 keys[n] = Malloc(sizeof(struct menu_key_s));
382 keys[n]->c = c;
383 keys[n++]->func = func;
384 keys[n] = NULL;
385 *dst = keys;