Added .data to userdata_s.
[cboard.git] / src / menu.c
blob925f66922e7620e32a8f580b8ca4f845990627e8
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 draw_menu(win);
246 cbreak();
247 noecho();
248 keypad(win->w, TRUE);
249 nl();
251 if (m->keys) {
252 for (i = 0; m->keys[i]; i++) {
253 if (win->c == m->keys[i]->c) {
254 (*m->keys[i]->func)(m);
255 m->items = (*m->func)(win);
256 key = 1;
257 m->search[0] = 0;
258 goto end;
263 switch (win->c) {
264 case REFRESH_MENU:
265 m->items = (*m->func)(win);
266 pushkey = 0;
267 break;
268 case -1:
269 pushkey = 0;
270 goto done;
271 case KEY_HOME:
272 case KEY_END:
273 case KEY_UP:
274 case KEY_DOWN:
275 case KEY_NPAGE:
276 case KEY_PPAGE:
277 m->search[0] = 0;
278 break;
279 default:
280 if (strlen(m->search) + 1 > sizeof(m->search) - 1)
281 m->search[0] = 0;
283 p = m->search;
285 while (*p)
286 p++;
288 *p++ = win->c;
289 *p = 0;
290 n = m->selected;
292 if (m->items) {
293 for (i = 0; m->items[i]; i++) {
294 if (strncasecmp(m->search, m->items[i]->name,
295 strlen(m->search)) == 0) {
296 m->selected = i;
297 break;
302 if (n == m->selected)
303 m->search[0] = 0;
306 end:
307 set_menu_vars(win->c, win->rows - 4, m->total - 1, &m->selected, &m->top);
308 fix_menu_vals(win);
309 draw_menu(win);
310 return 1;
312 done:
313 win->data = m->data;
315 if (m->items) {
316 for (i = 0; m->items[i]; i++) {
317 if (!m->nofree)
318 free(m->items[i]->name);
320 if (!m->nofree && m->items[i]->value)
321 free(m->items[i]->value);
323 free(m->items[i]);
326 free(m->items);
329 if (m->keys) {
330 for (i = 0; m->keys[i]; i++)
331 free(m->keys[i]);
333 free(m->keys);
336 if (m->title)
337 free(m->title);
339 free(m);
340 return 0;
343 WIN *construct_menu(int rows, int cols, int y, int x, const char *title,
344 int name_only, menu_items *func, struct menu_key_s **keys, void *data,
345 window_exit_func *efunc)
347 WIN *win;
348 int h = 1, w = 1;
349 struct menu_input_s *m;
351 m = Calloc(1, sizeof(struct menu_input_s));
352 win = window_create(rows, cols, (y >= 0) ? y : CALCPOSY(h),
353 (x >= 0) ? x : CALCPOSX(w), display_menu, m, efunc);
354 m = win->data;
355 m->ystatic = y;
356 m->xstatic = x;
358 if (rows)
359 m->rstatic = 1;
361 if (cols)
362 m->cstatic = 1;
364 m->func = func;
365 m->keys = keys;
366 m->data = data;
367 m->name_only = name_only;
369 if (title)
370 m->title = strdup(title);
372 wbkgd(win->w, CP_MENU);
373 cbreak();
374 noecho();
375 keypad(win->w, TRUE);
376 nl();
377 m->items = (*m->func)(win);
378 fix_menu_vals(win);
379 (*win->func)(win);
380 return win;
383 void add_menu_key(struct menu_key_s ***dst, int c, menu_key func)
385 int n = 0;
386 struct menu_key_s **keys = *dst;
388 if (keys)
389 for (; keys[n]; n++);
391 keys = Realloc(keys, (n + 2) * sizeof(struct menu_key_s *));
392 keys[n] = Malloc(sizeof(struct menu_key_s));
393 keys[n]->c = c;
394 keys[n++]->func = func;
395 keys[n] = NULL;
396 *dst = keys;