/* * 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 #include #include #else #include #include #include #endif #include #include #ifndef _plan9_ #include #endif #include #include #include #include #include #include #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); }