In case of running out of prefetch pipes, truncate the sample to the preloaded part.
[calfbox.git] / menu.c
blob9e41a5b7050799e0f463ac8e4c4abf7a8fccb27a
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);
50 return item;
53 void cbox_menu_destroy(struct cbox_menu *menu)
55 int i;
57 for (i = 0; i < menu->items->len; i++)
58 cbox_menu_item_destroy(g_ptr_array_index(menu->items, i));
60 g_ptr_array_free(menu->items, TRUE);
61 g_string_chunk_free(menu->strings);
62 free(menu);
67 gchar *cbox_menu_item_value_format(const struct cbox_menu_item *item, void *context)
69 switch(item->type)
71 case menu_item_static:
72 if (item->extras)
73 return ((struct cbox_menu_item_extras_static *)(item->extras))->format_value(item, context);
74 else
75 return g_strdup_printf("");
76 case menu_item_submenu:
77 return g_strdup_printf("...");
78 case menu_item_command:
79 return g_strdup_printf("<cmd>");
80 default:
81 if (!item->value)
82 return g_strdup_printf("(null)");
83 switch(item->type)
85 case menu_item_value_int:
86 return g_strdup_printf(((struct cbox_menu_item_extras_int *)(item->extras))->fmt, *(int *)item->value);
87 case menu_item_value_double:
88 return g_strdup_printf(((struct cbox_menu_item_extras_double *)(item->extras))->fmt, *(double *)item->value);
89 case menu_item_value_enum:
90 return g_strdup_printf("<enum>%d", *(int *)item->value);
91 default:
92 return g_strdup_printf("");
95 assert(0);
96 return NULL;
100 void cbox_menu_state_size(struct cbox_menu_state *menu_state)
102 struct cbox_menu *menu = menu_state->menu;
103 int i;
104 menu_state->size.label_width = 0;
105 menu_state->size.value_width = 0;
106 menu_state->size.height = 0;
107 menu_state->yspace = getmaxy(menu_state->window) - 2;
109 for (i = 0; i < menu->items->len; i++)
111 struct cbox_menu_item *item = g_ptr_array_index(menu->items, i);
113 item->x = 1;
114 item->y = 1 + menu_state->size.height;
115 item->item_class->measure(item, menu_state);
119 void cbox_menu_state_draw(struct cbox_menu_state *menu_state)
121 struct cbox_menu *menu = menu_state->menu;
122 int i;
124 werase(menu_state->window);
125 box(menu_state->window, 0, 0);
126 for (i = 0; i < menu->items->len; i++)
128 struct cbox_menu_item *item = g_ptr_array_index(menu->items, i);
129 gchar *str = item->item_class->format_value(item, menu_state);
130 item->item_class->draw(item, menu_state, str, menu_state->cursor == i);
131 g_free(str);
133 wrefresh(menu_state->window);
136 static void cbox_menu_page_draw(struct cbox_ui_page *p)
138 struct cbox_menu_page *mp = p->user_data;
139 struct cbox_menu_state *st = mp->state;
140 cbox_menu_state_size(st);
141 cbox_menu_state_draw(st);
144 static int cbox_menu_is_item_enabled(struct cbox_menu *menu, int item)
146 assert(item >= 0 && item < menu->items->len);
148 return ((struct cbox_menu_item *)g_ptr_array_index(menu->items, item))->item_class->on_key != NULL;
151 struct cbox_menu_state *cbox_menu_state_new(struct cbox_menu_page *page, struct cbox_menu *menu, WINDOW *window, void *context)
153 struct cbox_menu_state *st = malloc(sizeof(struct cbox_menu_state));
154 st->page = page;
155 st->menu = menu;
156 st->cursor = 0;
157 st->yoffset = 0;
158 st->window = window;
159 st->context = context;
160 st->caller = NULL;
161 st->menu_is_temporary = 0;
163 while(st->cursor < menu->items->len - 1 && !cbox_menu_is_item_enabled(menu, st->cursor))
164 st->cursor++;
166 return st;
169 int cbox_menu_page_on_idle(struct cbox_ui_page *p)
171 struct cbox_menu_page *mp = p->user_data;
172 struct cbox_menu_state *st = mp->state;
173 cbox_menu_state_size(st);
174 cbox_menu_state_draw(st);
175 return 0;
178 int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch)
180 struct cbox_menu_page *mp = p->user_data;
181 struct cbox_menu_state *st = mp->state;
182 struct cbox_menu *menu = st->menu;
183 struct cbox_menu_item *item = NULL;
184 int pos = st->cursor;
185 int res = 0;
186 if (st->cursor >= 0 && st->cursor < menu->items->len)
187 item = g_ptr_array_index(menu->items, st->cursor);
189 if (ch == 27)
190 return ch;
192 if (item->item_class->on_key)
194 res = item->item_class->on_key(item, st, ch);
195 st = mp->state;
196 if (res < 0)
198 cbox_menu_state_size(st);
199 cbox_menu_state_draw(st);
200 return 0;
204 if (res > 0)
205 return res;
207 switch(ch)
209 case 12:
210 wclear(st->window);
211 return 0;
212 case 27:
213 return ch;
214 case KEY_UP:
215 case KEY_END:
216 pos = ch == KEY_END ? menu->items->len - 1 : st->cursor - 1;
217 while(pos >= 0 && !cbox_menu_is_item_enabled(menu, pos))
218 pos--;
219 if (pos >= 0)
220 st->cursor = pos;
221 if (ch == KEY_END)
223 st->yoffset = st->size.height - st->yspace;
224 if (st->yoffset < 0)
225 st->yoffset = 0;
227 else
228 if (pos >= 0 && pos < menu->items->len)
230 int npos = st->cursor;
231 int count = 0;
232 // show up to 2 disabled items above
233 while(npos >= 1 && !cbox_menu_is_item_enabled(menu, npos - 1) && count < 2)
235 npos--;
236 count++;
238 item = g_ptr_array_index(menu->items, npos);
239 if (item->y < 1 + st->yoffset)
240 st->yoffset = item->y - 1;
242 cbox_menu_state_draw(st);
243 return 0;
244 case KEY_HOME:
245 case KEY_DOWN:
246 pos = ch == KEY_HOME ? 0 : st->cursor + 1;
247 while(pos < menu->items->len && !cbox_menu_is_item_enabled(menu, pos))
248 pos++;
249 if (pos < menu->items->len)
250 st->cursor = pos;
251 if (ch == KEY_HOME)
252 st->yoffset = 0;
253 else if (pos >= 0 && pos < menu->items->len)
255 item = g_ptr_array_index(menu->items, st->cursor);
256 if (item->y - 1 - st->yoffset >= st->yspace)
257 st->yoffset = item->y - st->yspace;
259 cbox_menu_state_draw(st);
260 return 0;
262 return 0;
265 void cbox_menu_state_destroy(struct cbox_menu_state *st)
267 free(st);
270 struct cbox_menu_page *cbox_menu_page_new()
272 struct cbox_menu_page *page = malloc(sizeof(struct cbox_menu_page));
273 page->state = NULL;
274 page->page.user_data = page;
275 page->page.draw = cbox_menu_page_draw;
276 page->page.on_key = cbox_menu_page_on_key;
277 page->page.on_idle = cbox_menu_page_on_idle;
278 return page;
281 void cbox_menu_page_destroy(struct cbox_menu_page *p)
283 free(p);