Added .draw_menu_exit to menu_input_s.
[cboard.git] / src / menu.c
blob426978b03db540ef29e220dddbe41c85165dd67e
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;
197 int nlen = 0;
198 int n = 0;
199 char *p;
201 if (!m->items)
202 return;
204 for (i = 0; m->items[i]; i++) {
205 y = strlen(m->items[i]->name);
207 if (nlen < y)
208 nlen = y;
211 for (i = m->top, y = 2; i < m->total && y < win->rows - 2; i++, y++) {
212 if (m->items[i]->value)
213 n = strlen(m->items[i]->value) + nlen;
215 if (n >= win->cols)
216 p = "Press ENTER";
217 else
218 p = m->items[i]->value;
220 if (i == m->selected)
221 wattron(win->w, CP_MENU_SELECTED);
222 else if (m->items[i]->selected)
223 wattron(win->w, CP_MENU_HIGHLIGHT);
225 if (!m->name_only)
226 mvwprintw(win->w, y, 1, "%*s - %-*s", nlen, m->items[i]->name,
227 win->cols - 5 - nlen, (m->items[i]->value) ? p : UNKNOWN);
228 else
229 mvwprintw(win->w, y, 1, "%-*s", nlen, m->items[i]->name);
231 if (i == m->selected)
232 wattroff(win->w, CP_MENU_SELECTED);
233 else if (m->items[i]->selected)
234 wattroff(win->w, CP_MENU_HIGHLIGHT);
238 int display_menu(WIN *win)
240 struct menu_input_s *m = win->data;
241 int i, n;
242 int key = 0;
243 char *p;
245 cbreak();
246 noecho();
247 keypad(win->w, TRUE);
248 nl();
250 if (m->keys) {
251 for (i = 0; m->keys[i]; i++) {
252 if (win->c == m->keys[i]->c) {
253 (*m->keys[i]->func)(m);
254 m->items = (*m->func)(win);
255 key = 1;
256 m->search[0] = 0;
257 goto end;
262 switch (win->c) {
263 case REFRESH_MENU:
264 m->items = (*m->func)(win);
265 pushkey = 0;
266 break;
267 case -1:
268 pushkey = 0;
269 goto done;
270 case KEY_HOME:
271 case KEY_END:
272 case KEY_UP:
273 case KEY_DOWN:
274 case KEY_NPAGE:
275 case KEY_PPAGE:
276 m->search[0] = 0;
277 break;
278 default:
279 if (!win->c)
280 break;
282 if (strlen(m->search) + 1 > sizeof(m->search) - 1)
283 m->search[0] = 0;
285 p = m->search;
287 while (*p)
288 p++;
290 *p++ = win->c;
291 *p = 0;
292 n = m->selected;
294 if (m->items) {
295 for (i = 0; m->items[i]; i++) {
296 if (strncasecmp(m->search, m->items[i]->name,
297 strlen(m->search)) == 0) {
298 m->selected = i;
299 break;
304 if (n == m->selected)
305 m->search[0] = 0;
308 end:
309 set_menu_vars(win->c, win->rows - 4, m->total - 1, &m->selected, &m->top);
310 fix_menu_vals(win);
311 draw_menu(win);
313 if (m->draw_exit_func)
314 (*m->draw_exit_func)(m);
316 return 1;
318 done:
319 win->data = m->data;
321 if (m->items) {
322 for (i = 0; m->items[i]; i++) {
323 if (!m->nofree)
324 free(m->items[i]->name);
326 if (!m->nofree && m->items[i]->value)
327 free(m->items[i]->value);
329 free(m->items[i]);
332 free(m->items);
335 if (m->keys) {
336 for (i = 0; m->keys[i]; i++)
337 free(m->keys[i]);
339 free(m->keys);
342 if (m->title)
343 free(m->title);
345 free(m);
346 return 0;
349 WIN *construct_menu(int rows, int cols, int y, int x, const char *title,
350 int name_only, menu_items *func, struct menu_key_s **keys, void *data,
351 window_exit_func *efunc)
353 WIN *win;
354 int h = 1, w = 1;
355 struct menu_input_s *m;
357 m = Calloc(1, sizeof(struct menu_input_s));
358 win = window_create(rows, cols, (y >= 0) ? y : CALCPOSY(h),
359 (x >= 0) ? x : CALCPOSX(w), display_menu, m, efunc);
360 m = win->data;
361 m->ystatic = y;
362 m->xstatic = x;
364 if (rows)
365 m->rstatic = 1;
367 if (cols)
368 m->cstatic = 1;
370 m->func = func;
371 m->keys = keys;
372 m->data = data;
373 m->name_only = name_only;
375 if (title)
376 m->title = strdup(title);
378 wbkgd(win->w, CP_MENU);
379 cbreak();
380 noecho();
381 keypad(win->w, TRUE);
382 nl();
383 m->items = (*m->func)(win);
384 fix_menu_vals(win);
385 (*win->func)(win);
386 return win;
389 void add_menu_key(struct menu_key_s ***dst, int c, menu_key func)
391 int n = 0;
392 struct menu_key_s **keys = *dst;
394 if (keys)
395 for (; keys[n]; n++);
397 keys = Realloc(keys, (n + 2) * sizeof(struct menu_key_s *));
398 keys[n] = Malloc(sizeof(struct menu_key_s));
399 keys[n]->c = c;
400 keys[n++]->func = func;
401 keys[n] = NULL;
402 *dst = keys;