You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
8.0 KiB
289 lines
8.0 KiB
3 years ago
|
/*
|
||
|
Calf Box, an open source musical instrument.
|
||
|
Copyright (C) 2010 Krzysztof Foltman
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "menu.h"
|
||
|
#include "menuitem.h"
|
||
|
#include "ui.h"
|
||
|
|
||
|
#if USE_NCURSES
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <glib.h>
|
||
|
#include <malloc.h>
|
||
|
#include <ncurses.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
struct cbox_menu
|
||
|
{
|
||
|
GPtrArray *items;
|
||
|
GStringChunk *strings;
|
||
|
};
|
||
|
|
||
|
static int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch);
|
||
|
static int cbox_menu_page_on_idle(struct cbox_ui_page *p);
|
||
|
|
||
|
struct cbox_menu *cbox_menu_new()
|
||
|
{
|
||
|
struct cbox_menu *menu = malloc(sizeof(struct cbox_menu));
|
||
|
|
||
|
menu->items = g_ptr_array_new();
|
||
|
menu->strings = g_string_chunk_new(100);
|
||
|
return menu;
|
||
|
}
|
||
|
|
||
|
struct cbox_menu_item *cbox_menu_add_item(struct cbox_menu *menu, struct cbox_menu_item *item)
|
||
|
{
|
||
|
g_ptr_array_add(menu->items, item);
|
||
|
return item;
|
||
|
}
|
||
|
|
||
|
void cbox_menu_destroy(struct cbox_menu *menu)
|
||
|
{
|
||
|
guint i;
|
||
|
|
||
|
for (i = 0; i < menu->items->len; i++)
|
||
|
cbox_menu_item_destroy(g_ptr_array_index(menu->items, i));
|
||
|
|
||
|
g_ptr_array_free(menu->items, TRUE);
|
||
|
g_string_chunk_free(menu->strings);
|
||
|
free(menu);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
gchar *cbox_menu_item_value_format(const struct cbox_menu_item *item, void *context)
|
||
|
{
|
||
|
switch(item->type)
|
||
|
{
|
||
|
case menu_item_static:
|
||
|
if (item->extras)
|
||
|
return ((struct cbox_menu_item_extras_static *)(item->extras))->format_value(item, context);
|
||
|
else
|
||
|
return g_strdup_printf("");
|
||
|
case menu_item_submenu:
|
||
|
return g_strdup_printf("...");
|
||
|
case menu_item_command:
|
||
|
return g_strdup_printf("<cmd>");
|
||
|
default:
|
||
|
if (!item->value)
|
||
|
return g_strdup_printf("(null)");
|
||
|
switch(item->type)
|
||
|
{
|
||
|
case menu_item_value_int:
|
||
|
return g_strdup_printf(((struct cbox_menu_item_extras_int *)(item->extras))->fmt, *(int *)item->value);
|
||
|
case menu_item_value_double:
|
||
|
return g_strdup_printf(((struct cbox_menu_item_extras_double *)(item->extras))->fmt, *(double *)item->value);
|
||
|
case menu_item_value_enum:
|
||
|
return g_strdup_printf("<enum>%d", *(int *)item->value);
|
||
|
default:
|
||
|
return g_strdup_printf("");
|
||
|
}
|
||
|
}
|
||
|
assert(0);
|
||
|
return NULL;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
void cbox_menu_state_size(struct cbox_menu_state *menu_state)
|
||
|
{
|
||
|
struct cbox_menu *menu = menu_state->menu;
|
||
|
guint i;
|
||
|
menu_state->size.label_width = 0;
|
||
|
menu_state->size.value_width = 0;
|
||
|
menu_state->size.height = 0;
|
||
|
menu_state->yspace = getmaxy(menu_state->window) - 2;
|
||
|
|
||
|
for (i = 0; i < menu->items->len; i++)
|
||
|
{
|
||
|
struct cbox_menu_item *item = g_ptr_array_index(menu->items, i);
|
||
|
|
||
|
item->x = 1;
|
||
|
item->y = 1 + menu_state->size.height;
|
||
|
item->item_class->measure(item, menu_state);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void cbox_menu_state_draw(struct cbox_menu_state *menu_state)
|
||
|
{
|
||
|
struct cbox_menu *menu = menu_state->menu;
|
||
|
guint i;
|
||
|
|
||
|
werase(menu_state->window);
|
||
|
box(menu_state->window, 0, 0);
|
||
|
for (i = 0; i < menu->items->len; i++)
|
||
|
{
|
||
|
struct cbox_menu_item *item = g_ptr_array_index(menu->items, i);
|
||
|
gchar *str = item->item_class->format_value(item, menu_state);
|
||
|
item->item_class->draw(item, menu_state, str, menu_state->cursor == i);
|
||
|
g_free(str);
|
||
|
}
|
||
|
wrefresh(menu_state->window);
|
||
|
}
|
||
|
|
||
|
static void cbox_menu_page_draw(struct cbox_ui_page *p)
|
||
|
{
|
||
|
struct cbox_menu_page *mp = p->user_data;
|
||
|
struct cbox_menu_state *st = mp->state;
|
||
|
cbox_menu_state_size(st);
|
||
|
cbox_menu_state_draw(st);
|
||
|
}
|
||
|
|
||
|
static int cbox_menu_is_item_enabled(struct cbox_menu *menu, unsigned int item)
|
||
|
{
|
||
|
assert(item < menu->items->len);
|
||
|
|
||
|
return ((struct cbox_menu_item *)g_ptr_array_index(menu->items, item))->item_class->on_key != NULL;
|
||
|
}
|
||
|
|
||
|
struct cbox_menu_state *cbox_menu_state_new(struct cbox_menu_page *page, struct cbox_menu *menu, WINDOW *window, void *context)
|
||
|
{
|
||
|
struct cbox_menu_state *st = malloc(sizeof(struct cbox_menu_state));
|
||
|
st->page = page;
|
||
|
st->menu = menu;
|
||
|
st->cursor = 0;
|
||
|
st->yoffset = 0;
|
||
|
st->window = window;
|
||
|
st->context = context;
|
||
|
st->caller = NULL;
|
||
|
st->menu_is_temporary = 0;
|
||
|
|
||
|
while(st->cursor < menu->items->len - 1 && !cbox_menu_is_item_enabled(menu, st->cursor))
|
||
|
st->cursor++;
|
||
|
|
||
|
return st;
|
||
|
}
|
||
|
|
||
|
int cbox_menu_page_on_idle(struct cbox_ui_page *p)
|
||
|
{
|
||
|
struct cbox_menu_page *mp = p->user_data;
|
||
|
struct cbox_menu_state *st = mp->state;
|
||
|
cbox_menu_state_size(st);
|
||
|
cbox_menu_state_draw(st);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int cbox_menu_page_on_key(struct cbox_ui_page *p, int ch)
|
||
|
{
|
||
|
struct cbox_menu_page *mp = p->user_data;
|
||
|
struct cbox_menu_state *st = mp->state;
|
||
|
struct cbox_menu *menu = st->menu;
|
||
|
struct cbox_menu_item *item = NULL;
|
||
|
int pos = st->cursor;
|
||
|
int res = 0;
|
||
|
if (st->cursor >= 0 && st->cursor < menu->items->len)
|
||
|
item = g_ptr_array_index(menu->items, st->cursor);
|
||
|
|
||
|
if (ch == 27)
|
||
|
return ch;
|
||
|
|
||
|
if (item->item_class->on_key)
|
||
|
{
|
||
|
res = item->item_class->on_key(item, st, ch);
|
||
|
st = mp->state;
|
||
|
if (res < 0)
|
||
|
{
|
||
|
cbox_menu_state_size(st);
|
||
|
cbox_menu_state_draw(st);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (res > 0)
|
||
|
return res;
|
||
|
|
||
|
switch(ch)
|
||
|
{
|
||
|
case 12:
|
||
|
wclear(st->window);
|
||
|
return 0;
|
||
|
case 27:
|
||
|
return ch;
|
||
|
case KEY_UP:
|
||
|
case KEY_END:
|
||
|
pos = ch == KEY_END ? menu->items->len - 1 : st->cursor - 1;
|
||
|
while(pos >= 0 && !cbox_menu_is_item_enabled(menu, pos))
|
||
|
pos--;
|
||
|
if (pos >= 0)
|
||
|
st->cursor = pos;
|
||
|
if (ch == KEY_END)
|
||
|
{
|
||
|
st->yoffset = st->size.height - st->yspace;
|
||
|
if (st->yoffset < 0)
|
||
|
st->yoffset = 0;
|
||
|
}
|
||
|
else
|
||
|
if (pos >= 0 && (guint)pos < menu->items->len)
|
||
|
{
|
||
|
int npos = st->cursor;
|
||
|
int count = 0;
|
||
|
// show up to 2 disabled items above
|
||
|
while(npos >= 1 && !cbox_menu_is_item_enabled(menu, npos - 1) && count < 2)
|
||
|
{
|
||
|
npos--;
|
||
|
count++;
|
||
|
}
|
||
|
item = g_ptr_array_index(menu->items, npos);
|
||
|
if (item->y < 1 + st->yoffset)
|
||
|
st->yoffset = item->y - 1;
|
||
|
}
|
||
|
cbox_menu_state_draw(st);
|
||
|
return 0;
|
||
|
case KEY_HOME:
|
||
|
case KEY_DOWN:
|
||
|
pos = ch == KEY_HOME ? 0 : st->cursor + 1;
|
||
|
while(pos < (int)menu->items->len && !cbox_menu_is_item_enabled(menu, pos))
|
||
|
pos++;
|
||
|
if (pos < (int)menu->items->len)
|
||
|
st->cursor = pos;
|
||
|
if (ch == KEY_HOME)
|
||
|
st->yoffset = 0;
|
||
|
else if (pos >= 0 && pos < (int)menu->items->len)
|
||
|
{
|
||
|
item = g_ptr_array_index(menu->items, st->cursor);
|
||
|
if (item->y - 1 - st->yoffset >= st->yspace)
|
||
|
st->yoffset = item->y - st->yspace;
|
||
|
}
|
||
|
cbox_menu_state_draw(st);
|
||
|
return 0;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void cbox_menu_state_destroy(struct cbox_menu_state *st)
|
||
|
{
|
||
|
free(st);
|
||
|
}
|
||
|
|
||
|
struct cbox_menu_page *cbox_menu_page_new()
|
||
|
{
|
||
|
struct cbox_menu_page *page = malloc(sizeof(struct cbox_menu_page));
|
||
|
page->state = NULL;
|
||
|
page->page.user_data = page;
|
||
|
page->page.draw = cbox_menu_page_draw;
|
||
|
page->page.on_key = cbox_menu_page_on_key;
|
||
|
page->page.on_idle = cbox_menu_page_on_idle;
|
||
|
return page;
|
||
|
}
|
||
|
|
||
|
void cbox_menu_page_destroy(struct cbox_menu_page *p)
|
||
|
{
|
||
|
free(p);
|
||
|
}
|
||
|
|
||
|
#endif
|