Logo Search packages:      
Sourcecode: xaos version File versions  Download package

uimenu.c

/*
 *     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);
}

Generated by  Doxygen 1.6.0   Back to index