Start with empty scene as last resort option.
[calfbox.git] / menu.c
blob73fa5e4ba4b2054a85de235a6f4f46cfb75723f5
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010 Krzysztof Foltman
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 3 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, see <http://www.gnu.org/licenses/>.
19 #include "menu.h"
20 #include "menuitem.h"
21 #include "ui.h"
23 #include <assert.h>
24 #include <glib.h>
25 #include <malloc.h>
26 #include <ncurses.h>
27 #include <string.h>
29 struct cbox_menu
31 GPtrArray *items;
32 GStringChunk *strings;
35 static int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch);
36 static int cbox_menu_page_on_idle(struct cbox_ui_page *p);
38 struct cbox_menu *cbox_menu_new()
40 struct cbox_menu *menu = malloc(sizeof(struct cbox_menu));
42 menu->items = g_ptr_array_new();
43 menu->strings = g_string_chunk_new(100);
44 return menu;
47 struct cbox_menu_item *cbox_menu_add_item(struct cbox_menu *menu, struct cbox_menu_item *item)
49 g_ptr_array_add(menu->items, item);
52 void cbox_menu_destroy(struct cbox_menu *menu)
54 int i;
56 for (i = 0; i < menu->items->len; i++)
57 cbox_menu_item_destroy(g_ptr_array_index(menu->items, i));
59 g_ptr_array_free(menu->items, TRUE);
60 g_string_chunk_free(menu->strings);
65 gchar *cbox_menu_item_value_format(const struct cbox_menu_item *item, void *context)
67 switch(item->type)
69 case menu_item_static:
70 if (item->extras)
71 return ((struct cbox_menu_item_extras_static *)(item->extras))->format_value(item, context);
72 else
73 return g_strdup_printf("");
74 case menu_item_submenu:
75 return g_strdup_printf("...");
76 case menu_item_command:
77 return g_strdup_printf("<cmd>");
78 default:
79 if (!item->value)
80 return g_strdup_printf("(null)");
81 switch(item->type)
83 case menu_item_value_int:
84 return g_strdup_printf(((struct cbox_menu_item_extras_int *)(item->extras))->fmt, *(int *)item->value);
85 case menu_item_value_double:
86 return g_strdup_printf(((struct cbox_menu_item_extras_double *)(item->extras))->fmt, *(double *)item->value);
87 case menu_item_value_enum:
88 return g_strdup_printf("<enum>%d", *(int *)item->value);
89 default:
90 return g_strdup_printf("");
93 assert(0);
94 return NULL;
98 void cbox_menu_state_size(struct cbox_menu_state *menu_state)
100 struct cbox_menu *menu = menu_state->menu;
101 int i;
102 menu_state->size.label_width = 0;
103 menu_state->size.value_width = 0;
104 menu_state->size.height = 0;
105 menu_state->yspace = getmaxy(menu_state->window) - 2;
107 for (i = 0; i < menu->items->len; i++)
109 struct cbox_menu_item *item = g_ptr_array_index(menu->items, i);
111 item->x = 1;
112 item->y = 1 + menu_state->size.height;
113 item->item_class->measure(item, menu_state);
117 void cbox_menu_state_draw(struct cbox_menu_state *menu_state)
119 struct cbox_menu *menu = menu_state->menu;
120 int i, x, y;
122 werase(menu_state->window);
123 box(menu_state->window, 0, 0);
124 for (i = 0; i < menu->items->len; i++)
126 struct cbox_menu_item *item = g_ptr_array_index(menu->items, i);
127 gchar *str = item->item_class->format_value(item, menu_state);
128 item->item_class->draw(item, menu_state, str, menu_state->cursor == i);
129 g_free(str);
131 wrefresh(menu_state->window);
134 static void cbox_menu_page_draw(struct cbox_ui_page *p)
136 struct cbox_menu_page *mp = p->user_data;
137 struct cbox_menu_state *st = mp->state;
138 cbox_menu_state_size(st);
139 cbox_menu_state_draw(st);
142 static int cbox_menu_is_item_enabled(struct cbox_menu *menu, int item)
144 assert(item >= 0 && item < menu->items->len);
146 return ((struct cbox_menu_item *)g_ptr_array_index(menu->items, item))->item_class->on_key != NULL;
149 struct cbox_menu_state *cbox_menu_state_new(struct cbox_menu_page *page, struct cbox_menu *menu, WINDOW *window, void *context)
151 struct cbox_menu_state *st = malloc(sizeof(struct cbox_menu_state));
152 st->page = page;
153 st->menu = menu;
154 st->cursor = 0;
155 st->yoffset = 0;
156 st->window = window;
157 st->context = context;
158 st->caller = NULL;
159 st->menu_is_temporary = 0;
161 while(st->cursor < menu->items->len - 1 && !cbox_menu_is_item_enabled(menu, st->cursor))
162 st->cursor++;
164 return st;
167 int cbox_menu_page_on_idle(struct cbox_ui_page *p)
169 struct cbox_menu_page *mp = p->user_data;
170 struct cbox_menu_state *st = mp->state;
171 cbox_menu_state_size(st);
172 cbox_menu_state_draw(st);
173 return 0;
176 int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch)
178 struct cbox_menu_page *mp = p->user_data;
179 struct cbox_menu_state *st = mp->state;
180 struct cbox_menu *menu = st->menu;
181 struct cbox_menu_item *item = NULL;
182 int pos = st->cursor;
183 int res = 0;
184 if (st->cursor >= 0 && st->cursor < menu->items->len)
185 item = g_ptr_array_index(menu->items, st->cursor);
187 if (ch == 27)
188 return ch;
190 if (item->item_class->on_key)
192 res = item->item_class->on_key(item, st, ch);
193 st = mp->state;
194 if (res < 0)
196 cbox_menu_state_size(st);
197 cbox_menu_state_draw(st);
198 return 0;
202 if (res > 0)
203 return res;
205 switch(ch)
207 case 12:
208 wclear(st->window);
209 return 0;
210 case 27:
211 return ch;
212 case KEY_UP:
213 case KEY_END:
214 pos = ch == KEY_END ? menu->items->len - 1 : st->cursor - 1;
215 while(pos >= 0 && !cbox_menu_is_item_enabled(menu, pos))
216 pos--;
217 if (pos >= 0)
218 st->cursor = pos;
219 if (ch == KEY_END)
221 st->yoffset = st->size.height - st->yspace;
222 if (st->yoffset < 0)
223 st->yoffset = 0;
225 else
226 if (pos >= 0 && pos < menu->items->len)
228 int npos = st->cursor;
229 int count = 0;
230 // show up to 2 disabled items above
231 while(npos >= 1 && !cbox_menu_is_item_enabled(menu, npos - 1) && count < 2)
233 npos--;
234 count++;
236 item = g_ptr_array_index(menu->items, npos);
237 if (item->y < 1 + st->yoffset)
238 st->yoffset = item->y - 1;
240 cbox_menu_state_draw(st);
241 return 0;
242 case KEY_HOME:
243 case KEY_DOWN:
244 pos = ch == KEY_HOME ? 0 : st->cursor + 1;
245 while(pos < menu->items->len && !cbox_menu_is_item_enabled(menu, pos))
246 pos++;
247 if (pos < menu->items->len)
248 st->cursor = pos;
249 if (ch == KEY_HOME)
250 st->yoffset = 0;
251 else if (pos >= 0 && pos < menu->items->len)
253 item = g_ptr_array_index(menu->items, st->cursor);
254 if (item->y - 1 - st->yoffset >= st->yspace)
255 st->yoffset = item->y - st->yspace;
257 cbox_menu_state_draw(st);
258 return 0;
260 return 0;
263 void cbox_menu_state_destroy(struct cbox_menu_state *st)
265 free(st);
268 struct cbox_menu_page *cbox_menu_page_new()
270 struct cbox_menu_page *page = malloc(sizeof(struct cbox_menu_page));
271 page->state = NULL;
272 page->page.user_data = page;
273 page->page.draw = cbox_menu_page_draw;
274 page->page.on_key = cbox_menu_page_on_key;
275 page->page.on_idle = cbox_menu_page_on_idle;
276 return page;
279 void cbox_menu_page_destroy(struct cbox_menu_page *p)
281 free(p);