diff options
Diffstat (limited to 'src/ui/uimenu.c')
-rw-r--r-- | src/ui/uimenu.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/src/ui/uimenu.c b/src/ui/uimenu.c new file mode 100644 index 0000000..b920c48 --- /dev/null +++ b/src/ui/uimenu.c @@ -0,0 +1,572 @@ +/* + * XaoS, a fast portable realtime fractal zoomer + * Copyright (C) 1996 by + * + * Jan Hubicka (hubicka@paru.cas.cz) + * Thomas Marsh (tmarsh@austin.ibm.com) + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#undef _EFENCE_ +#ifdef _plan9_ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#else +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#endif +#include <config.h> +#include <fconfig.h> +#ifndef _plan9_ +#include <assert.h> +#endif +#include <filter.h> +#include <ui_helper.h> +#include <ui.h> +#include <timers.h> +#include <xmenu.h> +#include <grlib.h> +#include "uiint.h" + + +#define NMENUS 20 +struct ui_menuitems { + int x, y, width, height; + CONST menuitem *item; + int separator; +}; +static struct ui_menu { + int x, y, width, height; + CONST char *name; + CONST char *fullname; + int namewidth; + int selected; + int n; + int flags; + struct ui_menuitems *items; + struct uih_window *window; + tl_timer *timer; +} *ui_menus[NMENUS]; +int ui_nmenus; + +#define MENU_HORIZONTAL 1 +#define MENU_PRESSED 2 +#define MENU_AUTOHIDE 4 + +#define SUBMENUWIDTH xtextwidth(uih->font, ">") +#define MENUPAUSE xtextwidth(uih->font, "X") +#define MENUWIDTH(a) (xtextwidth(uih->font, "w")+MENUPAUSE+xtextwidth(uih->font, a)+SUBMENUWIDTH) +#define HMENUWIDTH(a) (xtextwidth(uih->font, a)+xtextwidth(uih->font, " ")) + +#define SEPARATORSIZE 6 +static struct ui_menuitems *ui_getmenuitems(CONST char *name, int *width1, + int *height1, int *n1, + int horizontal) +{ + CONST menuitem *item; + int nseparators = 0; + int i; + int width = 0; + int n; + struct ui_menuitems *items; + for (n = 0; (item = menu_item(name, n)) != NULL; n++) + if (item->type == MENU_SEPARATOR) + nseparators++; + n -= nseparators; + *n1 = n; + *height1 = n * xtextheight(uih->font) + nseparators * SEPARATORSIZE; + items = + (struct ui_menuitems *) malloc(n * sizeof(struct ui_menuitems)); + nseparators = 0; + for (i = 0; i < n; i++) { + int w; + int sbehind = -1; + nseparators--; + do { + nseparators++; + sbehind++; + item = menu_item(name, i + nseparators); + } + while (item->type == MENU_SEPARATOR); + items[i].item = item; + items[i].separator = sbehind; + if (horizontal) { + w = HMENUWIDTH(items[i].item->name); + if (items[i].item->key) { + char c[10]; + sprintf(c, "(%s)", items[i].item->key); + w += xtextwidth(uih->font, c); + } + } else { + w = MENUWIDTH(items[i].item->name); + if (items[i].item->key) { + char c[10]; + sprintf(c, " %s ", items[i].item->key); + w += xtextwidth(uih->font, c); + } + } + items[i].width = w; + items[i].height = xtextheight(uih->font) + 1; + if (w > width) + width = w; + } + *width1 = width; + return (items); +} + +static void +ui_menusize(uih_context * c, int *x, int *y, int *w, int *h, void *data) +{ + struct ui_menu *m = (struct ui_menu *) data; + *x = m->x; + *y = m->y; + *w = m->width; + *h = m->height; +} + +static void ui_drawmenu(uih_context * c, void *data) +{ + struct ui_menu *m = (struct ui_menu *) data; + int i; + int width1 = xtextwidth(c->font, "w"); + char s[2]; + s[1] = 0; + if (!(m->flags & MENU_HORIZONTAL)) + xprint(c->image, c->font, m->x + (m->width - m->namewidth) / 2, + m->y + BORDERWIDTH, m->fullname, SELCOLOR(c), + BGCOLOR(c), 0); + for (i = 0; i < m->n; i++) { + int color = (i == m->selected ? SELCOLOR(c) : FGCOLOR(c)); + int pressed = 0; + if ((uih->palette->type & BITMAPS) && i == m->selected) { + pressed = TEXT_PRESSED; + color = BGCOLOR(c); + xrectangle(uih->image, m->items[i].x, m->items[i].y, + m->items[i].width, m->items[i].height, FGCOLOR(c)); + } + if (!(m->flags & MENU_HORIZONTAL)) { + if (m->items[i].separator) { + xhline(c->image, m->x + 5, + m->items[i].y - 2 - SEPARATORSIZE / 2, + m->width - 10, BGCOLOR(c)); + xhline(c->image, m->x + 5, + m->items[i].y - 1 - SEPARATORSIZE / 2, + m->width - 10, LIGHTGRAYCOLOR(c)); + } + if (i < 10) + s[0] = '0' + (i == 9 ? 0 : i + 1); + else + s[0] = 'A' + (i - 10); + xprint(c->image, c->font, m->items[i].x, m->items[i].y, s, + color, BGCOLOR(c), pressed); + if (menu_enabled(m->items[i].item, uih)) { + xprint(c->image, c->font, m->items[i].x + width1, + m->items[i].y, "X", color, BGCOLOR(c), + pressed); + } + xprint(c->image, c->font, m->items[i].x + width1 + MENUPAUSE, + m->items[i].y, m->items[i].item->name, + color, BGCOLOR(c), pressed); + if (m->items[i].item->key) { + char ch[20]; + sprintf(ch, " %s ", m->items[i].item->key); + xprint(c->image, c->font, + m->items[i].x + m->items[i].width - SUBMENUWIDTH - + xtextwidth(uih->font, ch), m->items[i].y, ch, + LIGHTGRAYCOLOR(c), BGCOLOR(c), + pressed); + } + if (m->items[i].item->type == MENU_SUBMENU) + xprint(c->image, c->font, + m->items[i].x + m->items[i].width - SUBMENUWIDTH, + m->items[i].y, ">", color, BGCOLOR(c), + pressed); + } else { + xprint(c->image, c->font, m->items[i].x, m->items[i].y, + m->items[i].item->name, color, BGCOLOR(c), + pressed); + if (m->items[i].item->key) { + char ch[20]; + sprintf(ch, "%s", m->items[i].item->key); + xprint(c->image, c->font, + m->items[i].x + xtextwidth(uih->font, + m->items[i].item->name) + + 2, m->items[i].y, ch, + LIGHTGRAYCOLOR(c), BGCOLOR(c), pressed); + } + } + } +} + +static struct ui_menu *ui_buildmenu(CONST char *name, int x, int y, + int flags) +{ + int shift = 0; + int width, height; + int textheight = xtextheight(uih->font); + struct ui_menu *menu; + int i; + menu = (struct ui_menu *) malloc(sizeof(*menu)); + menu->timer = tl_create_timer(); + tl_reset_timer(menu->timer); + menu->flags = flags; + menu->items = + ui_getmenuitems(name, &width, &height, &menu->n, + flags & MENU_HORIZONTAL); + menu->selected = -1; + menu->fullname = menu_fullname(name); + menu->name = name; + menu->namewidth = xtextwidth(uih->font, menu->fullname); + if (!(menu->flags & MENU_HORIZONTAL)) { + if (menu->namewidth > width) + width = menu->namewidth; + width += 2 * BORDERWIDTH; + height += 2 * BORDERHEIGHT + xtextheight(uih->font); + if (x + width > uih->image->width) + x = uih->image->width - width; + if (y + height > uih->image->height) + y = uih->image->height - height; + if (x < 0) + x = 0; + if (y < 0) + y = 0; + shift = 0; + for (i = 0; i < menu->n; i++) { + shift += menu->items[i].separator * SEPARATORSIZE; + menu->items[i].x = x + BORDERWIDTH; + menu->items[i].y = + y + BORDERWIDTH + textheight * (i + 1) + shift; + menu->items[i].width = width - 2 * BORDERWIDTH; + menu->items[i].height = textheight; + } + } else { + int line = 0; + int xpos = BORDERWIDTH; + x = 0, width = uih->image->width; + for (i = 0; i < menu->n; i++) { + if (xpos + 2 * BORDERWIDTH + menu->items[i].width > + uih->image->width) + xpos = BORDERWIDTH, line++; + menu->items[i].x = xpos; + menu->items[i].y = y + BORDERWIDTH + line * textheight; + xpos += menu->items[i].width; + menu->items[i].height = textheight; + } + height = (line + 1) * textheight + 2 * BORDERWIDTH; + } + menu->selected = -1; + menu->window = + uih_registerw(uih, ui_menusize, ui_drawmenu, menu, DRAWBORDER); + uih->display = 1; + menu->x = x; + menu->y = y; + menu->width = width; + menu->height = height; + return (menu); +} + +static void ui_closemenu(struct ui_menu *menu) +{ + free(menu->items); + tl_free_timer(menu->timer); + uih_removew(uih, menu->window); + uih->display = 1; + free(menu); +} + +static void ui_openmenu(CONST char *name, int x, int y, int flags) +{ + if (ui_nogui) { + printf("menu \"%s\"\n", name); + return; + } + if (driver->gui_driver && driver->gui_driver->menu) { + driver->gui_driver->menu(uih, name); + return; + } + if (ui_nmenus > NMENUS) + return; + ui_menus[ui_nmenus] = ui_buildmenu(name, x, y, flags); + ui_nmenus++; + ui_updatestarts(); +} + +static void ui_closetopmenu(void) +{ + if (!ui_nmenus) + return; + ui_nmenus--; + ui_closemenu(ui_menus[ui_nmenus]); + ui_updatestarts(); +} + +void ui_closemenus(void) +{ + while (ui_nmenus) + ui_closetopmenu(); +} + +void ui_menu(CONST char *m) +{ + int mousex, mousey, buttons; + driver->getmouse(&mousex, &mousey, &buttons); + ui_openmenu(m, mousex, mousey, 0); +} + +static void ui_menupress(int number) +{ + CONST menuitem *item; + if (number >= ui_menus[ui_nmenus - 1]->n) + return; + ui_menus[ui_nmenus - 1]->selected = number; + item = ui_menus[ui_nmenus - 1]->items[number].item; + if (item != NULL) { + uih->display = 1; + if (item->type == MENU_SUBMENU) { + int flags = 0; + int mousex, mousey, buttons; + driver->getmouse(&mousex, &mousey, &buttons); + if (buttons & BUTTON1) + flags |= MENU_PRESSED; + if ((ui_menus[ui_nmenus - 1]->flags & MENU_HORIZONTAL)) + ui_openmenu(item->shortname, + ui_menus[ui_nmenus - 1]->items[number].x, + ui_menus[ui_nmenus - 1]->items[number].y + + ui_menus[ui_nmenus - 1]->items[number].height, + flags); + else + ui_openmenu(item->shortname, + ui_menus[ui_nmenus - 1]->items[number].x + + ui_menus[ui_nmenus - 1]->items[number].width, + ui_menus[ui_nmenus - 1]->items[number].y, + flags); + } else + ui_menuactivate(item, NULL); + } +} + +int ui_menumouse(int x, int y, int mousebuttons, int flags) +{ + if (ui_nmenus) { + struct ui_menu *m = ui_menus[ui_nmenus - 1]; + int place = -1; + int inmenu = 0; + if (x >= m->x && y >= m->y && x <= m->x + m->width + && y <= m->y + m->height) { + int i; + for (i = 0; i < m->n; i++) { + if (x >= m->items[i].x && y >= m->items[i].y + && x <= m->items[i].x + m->items[i].width + && y <= m->items[i].y + m->items[i].height) { + place = i; + break; + } + } + inmenu = 1; + } else { + if (ui_nmenus > 1) { + struct ui_menu *m2 = ui_menus[ui_nmenus - 2]; + int i; + i = m2->selected; + if (x >= m2->items[i].x && y >= m2->items[i].y + && x <= m2->items[i].x + m2->items[i].width + && y <= m2->items[i].y + m2->items[i].height) + inmenu = 1; + } + } + if ((m->flags & MENU_AUTOHIDE) && !inmenu) { + ui_closetopmenu(); + return (ui_menumouse(x, y, mousebuttons, flags)); + } + if (flags & MOUSE_MOVE && m->selected != place) + m->selected = place, uih->display = 1; + if (m->flags & MENU_PRESSED) { + if (inmenu && place >= 0 && (m->flags & MENU_HORIZONTAL) + && (flags & MOUSE_DRAG) + && m->items[place].item->type == MENU_SUBMENU) { + ui_menupress(place); + return 1; + } else if (inmenu && place >= 0 && (flags & MOUSE_DRAG) + && m->items[place].item->type == MENU_SUBMENU + && x > + m->items[place].x + m->items[place].width - + 2 * SUBMENUWIDTH) { + ui_menupress(place); + return 1; + } + if (flags & MOUSE_RELEASE || !(flags & MOUSE_DRAG)) { + if (tl_lookup_timer(m->timer) < 300000) { + m->flags &= ~MENU_PRESSED; + return 1; + } + if (!inmenu || place < 0) { + ui_closetopmenu(); + return (ui_menumouse(x, y, mousebuttons, flags)); + } + ui_menupress(place); + return 1; + } + if (!inmenu) { /*Trace all menus back and look, if user selected some */ + int nmenu; + for (nmenu = ui_nmenus - 2; nmenu > -1; nmenu--) { + struct ui_menu *m2 = ui_menus[nmenu]; + if (x > m2->x && y > m2->y && x < m2->x + m2->width + && y < m2->y + m2->height) { + ui_closetopmenu(); + m2->flags |= MENU_PRESSED; + return (ui_menumouse(x, y, mousebuttons, flags)); + } + } + } + } else if (flags & MOUSE_PRESS) { + if (!inmenu || place < 0) { + ui_closetopmenu(); + return (ui_menumouse(x, y, mousebuttons, flags)); + } + ui_menupress(place); + } + return (1); + } else { + if (!ui_nogui && + (!driver->gui_driver || !driver->gui_driver->setrootmenu) && + (flags & MOUSE_MOVE) && y < xtextheight(uih->font) + 1 + && !(mousebuttons)) + ui_openmenu(uih->menuroot, 0, 0, + MENU_HORIZONTAL | MENU_AUTOHIDE); + } + return (0); +} + +int ui_menukey(int key) +{ + int k; + if (!ui_nmenus) { + if (key == '\n' || key == 13) { + ui_closemenus(); + ui_openmenu(uih->menuroot, 0, 0, MENU_HORIZONTAL); + return 1; + } + return 0; + } else { + struct ui_menu *menu = ui_menus[ui_nmenus - 1]; + switch (key) { + case '\n': + case 13: + if (menu->selected >= 0) + ui_menupress(menu->selected); + return 1; + case 'h': + { + CONST menuitem *item = menu->items[menu->selected].item; + ui_closemenus(); + if (menu->selected >= 0) { + ui_help(item->shortname); + } else + ui_help(menu->name); + return 1; + } + case UIKEY_LEFT: + if (menu->flags & MENU_HORIZONTAL) { + if (menu->selected == -1) + menu->selected = 0; + else + menu->selected--; + if (menu->selected < 0) + menu->selected = menu->n - 1; + uih->display = 1; + } else if (ui_nmenus == 2 + && ui_menus[0]->flags & MENU_HORIZONTAL) { + ui_closetopmenu(); + ui_menus[0]->selected--; + if (ui_menus[0]->selected < 0) + ui_menus[0]->selected = ui_menus[0]->n - 1; + ui_menupress(ui_menus[0]->selected); + } else + ui_closetopmenu(); + return 1; + case UIKEY_RIGHT: + if (menu->flags & MENU_HORIZONTAL) { + if (menu->selected == -1) + menu->selected = 0; + else + menu->selected++; + menu->selected %= menu->n; + uih->display = 1; + } else if (menu->selected >= 0 + && menu->items[menu->selected].item->type == + MENU_SUBMENU) { + ui_menupress(menu->selected); + } else if (ui_nmenus == 2 + && ui_menus[0]->flags & MENU_HORIZONTAL) { + ui_closetopmenu(); + ui_menus[0]->selected++; + ui_menus[0]->selected %= ui_menus[0]->n; + ui_menupress(ui_menus[0]->selected); + } + return 1; + case UIKEY_DOWN: + if (menu->flags & MENU_HORIZONTAL) { + if (menu->selected >= 0) + ui_menupress(ui_menus[0]->selected); + } else { + if (menu->selected == -1) + menu->selected = 0; + else + menu->selected++; + menu->selected %= menu->n; + uih->display = 1; + } + return 1; + case UIKEY_ESC: + ui_closetopmenu(); + return 1; + case UIKEY_UP: + if (menu->flags & MENU_HORIZONTAL) { + ui_closetopmenu(); + } else { + if (menu->selected == -1) + menu->selected = 0; + else + menu->selected--; + if (menu->selected < 0) + menu->selected = menu->n - 1; + uih->display = 1; + } + return 1; + } + if (tolower(key) >= 'a' + && tolower(key) - 'a' < ui_menus[ui_nmenus - 1]->n - 10) { + ui_menupress(tolower(key) - 'a' + 10); + return 1; + } + if (key >= '0' && key <= '9') { + k = key - '1'; + if (k == -1) + k = 9; + ui_menupress(k); + return 1; + } + return 0; + } +} + +int ui_menuwidth(void) +{ + if (ui_nmenus && (ui_menus[0]->flags & MENU_HORIZONTAL)) + return (ui_menus[0]->height); + return (0); +} |