/*
 * menu.cc
 *
 * Copyright (C) 1995, 1996, 1997, 1997, 1998, 1999, 2000, 2001, 2002 Kenichi Kourai
 * Copyright (C) 1999, 2000, 2001, 2002 Luiz Blanes
 *
 * 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 blwm; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/xpm.h>
#include "main.h"
#include "misc.h"
#include "blwm.h"
#include "menu.h"
#include "util.h"
#include "startmenu.h"
#include "blwmrc.h"
#include "timer.h"
#include "key.h"
#include "icon.h"
#include "callback.h"
#include "taskbar.h"
#include "paging.h"
#include "pixmap_image.h"
#include "bitmaps/folder16.xpm"
#include "bitmaps/icon16.xpm"
#include "bitmaps/next_black.xpm"
#include "bitmaps/next_white.xpm"
#include "bitmaps/check_black.xpm"
#include "bitmaps/check_white.xpm"
#include "bitmaps/selector_black.xpm"
#include "bitmaps/selector_white.xpm"

XContext	Menu::context;
Menu::MenuDir	Menu::fDir = Menu::RIGHT;
BlImage*	Menu::imgMenu[2];
BlImage*	Menu::imgNext[2];
BlImage*	Menu::imgCheck[2];
BlImage*	Menu::imgSelector[2];
BlImage 	*Menu::imgMenuBack, *Menu::imgActiveMenuBack;

Menu::Menu(MenuElem* mItem, XFontSet& menufs, Menu* par, Blwm* blwm,
	   int lMargin, int nMargin, int hMargin,
	   BlImage* imgDefFolder, BlImage* imgDefIcon)
: parent(par), leftMargin(lMargin), nameMargin(nMargin), hiMargin(hMargin),
  fs(menufs), blWm(blwm)
{
  int maxWidth = 0, width;
  Dim maxImgSize(0, 0);
  int i, sep = 0;
  XSetWindowAttributes attributes;
  unsigned long valueMask;
  Rect rect;

  iFocus = -1;            // no focus
  mapped = False;
  child = NULL;

  nitems = GetMenuItemNum(mItem);

  item = new Window[nitems];
  img = new BlImage*[nitems];
  name = new char*[nitems];
  scKey = new char[nitems];
  func = new FuncNumber[nitems];
  exec = new char*[nitems];
  next = new Menu*[nitems];
  childItem = new MenuElem*[nitems];

  imgWhiteNext = new BlImage*[nitems];
  imgBlackNext = new BlImage*[nitems];
  imgWhiteCheck = new BlImage*[nitems];
  imgBlackCheck = new BlImage*[nitems];
  imgWhiteSelector = new BlImage*[nitems];
  imgBlackSelector = new BlImage*[nitems];

  if (MenuImage)
    imgBack = new BlImage*[nitems];
  if (MenuActiveImage)
    imgActiveBack = new BlImage*[nitems];

  for (i = 0; i < nitems; i++) {
    ASSERT(mItem);

    func[i] = mItem->func;
    exec[i] = mItem->exec;
    name[i] = mItem->name;
    
    char* keyStr = strstr(name[i], "\\&");
    if (keyStr) {
      scKey[i] = *(keyStr + 2);
      if (scKey[i] >= 'a' && scKey[i] <= 'z')
	scKey[i] -= 0x20;
    }
    else
      scKey[i] = 0;

    if (mItem->func == Q_DIR) {
      childItem[i] = mItem->child;
      next[i] = NULL;  // menu creation is delayed
    }
    else {
      childItem[i] = NULL;
      next[i] = NULL;
    }

    if (mItem->func == Q_DIR) {
      if (strcmp(mItem->file, "") != 0) {
	img[i] = CreateImageFromFile(mItem->file, timer);
	if (img[i] == NULL && imgDefFolder)
	  img[i] = imgDefFolder->Duplicate();
      }
      else {
	if (imgDefFolder)
	  img[i] = imgDefFolder->Duplicate();
	else
	  img[i] = NULL;
      }
    }
    else {
      if (strcmp(mItem->file, "") != 0) {
	img[i] = CreateImageFromFile(mItem->file, timer);
	if (img[i] == NULL && imgDefIcon)
	  img[i] = imgDefIcon->Duplicate();
      }
      else {
	if (imgDefIcon)
	  img[i] = imgDefIcon->Duplicate();
	else
	  img[i] = NULL;
      }
    }
      
    imgWhiteNext[i] = imgNext[0]->Duplicate();
    imgBlackNext[i] = imgNext[1]->Duplicate();
    imgWhiteSelector[i] = imgSelector[0]->Duplicate();
    imgBlackSelector[i] = imgSelector[1]->Duplicate();
    imgWhiteCheck[i] = imgCheck[0]->Duplicate();
    imgBlackCheck[i] = imgCheck[1]->Duplicate();

    width = GetRealWidth(fs, name[i]);
    if (width > maxWidth)
      maxWidth = width;

    if (mItem->func != Q_SEPARATOR && img[i]) {
      Dim size = img[i]->GetSize();
      if (size.width > maxImgSize.width)
	maxImgSize.width = size.width;
      if (size.height > maxImgSize.height)
	maxImgSize.height = size.height;
    }

    mItem = mItem->next;
  }

  // for inheriting default images
  imgParentFolder = imgDefFolder;
  imgParentIcon = imgDefIcon;

  if (maxImgSize.width == 0)
    imageArea = leftMargin + 9 + nameMargin;  // no images
  else {
    imageArea = leftMargin + maxImgSize.width + nameMargin;
    if (imageArea < 14)
      imageArea = 14;
  }

  if (maxImgSize.height == 0)
    itemHeight = 16;  // no images
  else {
    itemHeight = maxImgSize.height + hiMargin * 2;
    if (itemHeight < 16)
      itemHeight = 16;
  }

  rc.width = imageArea + maxWidth + 24 + MenuFrameWidth * 2;

  /*
   * Create frame window.
   */
  attributes.save_under = DoesSaveUnders(ScreenOfDisplay(display, screen));
  attributes.background_pixel = MenuColor.pixel;
  attributes.event_mask = ExposureMask | KeyPressMask | LeaveWindowMask |
                          EnterWindowMask;
  valueMask = CWSaveUnder | CWBackPixel | CWEventMask;
  
  frame = XCreateWindow(display, root,
			0, 0, 1, 1,
			0, CopyFromParent, InputOutput, CopyFromParent,
			valueMask, &attributes);

  if (MenuImage)
    imgMenuBack->SetBackground(frame);

  valueMask = CWBackPixel | CWEventMask;

  /*
   * Create item windows for menu.
   */
  for (i = 0; i < nitems; i++) {
    if (func[i] != Q_SEPARATOR) {
      attributes.event_mask = ButtonPressMask | ButtonReleaseMask |
	                      EnterWindowMask |
			      OwnerGrabButtonMask | ExposureMask |
			      PointerMotionMask;
      rect.y = MenuFrameWidth + (i - sep) * itemHeight + sep * SeparatorHeight;
      rect.height = itemHeight;
    }
    else {
      attributes.event_mask = EnterWindowMask | OwnerGrabButtonMask |
	                      ExposureMask;
      rect.y = MenuFrameWidth + (i - sep) * itemHeight + sep * SeparatorHeight;
      rect.height = SeparatorHeight;
      sep++;
    }

    rect.x = MenuFrameWidth;
    rect.width = rc.width - MenuFrameWidth * 2;

    item[i] = XCreateWindow(display, frame,
			    rect.x, rect.y, rect.width, rect.height,
			    0, CopyFromParent, InputOutput, CopyFromParent,
			    valueMask, &attributes);

    if (MenuImage) {
      imgBack[i] = imgMenuBack->GetOffsetImage(Point(rect.x, rect.y));

      if (func[i] == Q_SEPARATOR)
	imgBack[i]->SetBackground(item[i]);
    }
    if (MenuActiveImage) {
      if (func[i] == Q_SEPARATOR)
	imgActiveBack[i] = NULL;
      else {
	Point pt(rect.x, rect.y);
	imgActiveBack[i] = imgActiveMenuBack->GetOffsetImage(pt);
      }
    }
  }

  rc.height = (nitems - sep) * itemHeight + sep * SeparatorHeight
    + MenuFrameWidth * 2;

  XResizeWindow(display, frame, rc.width, rc.height);

  XSaveContext(display, frame, context, (caddr_t)this);
  for (i = 0; i < nitems; i++)
    XSaveContext(display, item[i], context, (caddr_t)this);    
}

Menu::~Menu()
{
  int i;

  if (nitems == 0)
    return;

  XDeleteContext(display, frame, context);
  XDestroyWindow(display, frame);

  for (i = 0; i < nitems; i++) {
    XDeleteContext(display, item[i], context);
    XDestroyWindow(display, item[i]);

    BlImage::Destroy(img[i]);

    if (next[i])
      delete next[i];

    if (MenuImage) {
      if (imgBack[i])
	BlImage::Destroy(imgBack[i]);
    }
    if (MenuActiveImage) {
      if (imgActiveBack[i])
	BlImage::Destroy(imgActiveBack[i]);
    }
  }

  delete [] item;
  delete [] func;
  delete [] img;
  delete [] name;
  delete [] exec;
  delete [] next;

  if (MenuImage) {
    for (i = 0; i < nitems; i++)
      BlImage::Destroy(imgBack[i]);
    delete [] imgBack;
  }
  if (MenuActiveImage) {
    for (i = 0; i < nitems; i++)
      BlImage::Destroy(imgActiveBack[i]);
    delete [] imgActiveBack;
  }

  for (i = 0; i < nitems; i++) {
    BlImage::Destroy(imgWhiteNext[i]);
    BlImage::Destroy(imgBlackNext[i]);
    BlImage::Destroy(imgWhiteSelector[i]);
    BlImage::Destroy(imgBlackSelector[i]);
    BlImage::Destroy(imgWhiteCheck[i]);
    BlImage::Destroy(imgBlackCheck[i]);
  }

  delete [] imgWhiteNext;
  delete [] imgBlackNext;
  delete [] imgWhiteSelector;
  delete [] imgBlackSelector;
  delete [] imgWhiteCheck;
  delete [] imgBlackCheck;
}

/*
 * MapMenu --
 *   Map menu.
 */
void Menu::MapMenu(int x, int y, int dir)
{
  if (GradMenuMap && mapped)
    XUnmapWindow(display, frame);

  XMapSubwindows(display, frame);
  XMoveWindow(display, frame, x, y);
  if (GradMenuMap)
    XResizeWindow(display, frame, 1, 1);
  XMapRaised(display, frame);

  // make this menu show gradually
  if (GradMenuMap) {
    // child menus expand only horizontaly
    if (parent)
      dir &= ~(GD_DOWN | GD_UP);

    int divide = ((rc.width + rc.height) / 2) * GradMenuMapSpeed / 100;
    if (divide == 0)
      divide = 1;

    for (int i = 1; i <= divide; i++) {
      Dim size(rc.width * i / divide, rc.height * i / divide);
      if (size.width == 0)
	size.width = 1;
      if (size.height == 0)
	size.height = 1;

      if (dir & GD_RIGHT) {
	if (dir & GD_DOWN) {
	  // right & down
	  XResizeWindow(display, frame, size.width, size.height);
	}
	else if (dir & GD_UP) {
	  // right & up
	  XMoveResizeWindow(display, frame,
			    x, y + rc.height - size.height,
			    size.width, size.height);
	}
	else {
	  // right
	  XResizeWindow(display, frame, size.width, rc.height);
	}
      }
      else if (dir & GD_LEFT) {
	if (dir & GD_DOWN) {
	  // left & down
	  XMoveResizeWindow(display, frame,
			    x + rc.width - size.width, y,
			    size.width, size.height);
	}
	else if (dir & GD_UP) {
	  // left & up
	  XMoveResizeWindow(display, frame,
			    x + rc.width - size.width,
			    y + rc.height - size.height,
			    size.width, size.height);
	}
	else {
	  // left
	  XMoveResizeWindow(display, frame,
			    x + rc.width - size.width, y,
			    size.width, rc.height);
	}
      }
      else if (dir & GD_DOWN) {
	// down
	XResizeWindow(display, frame, rc.width, size.height);
      }
      else {
	// up
	XMoveResizeWindow(display, frame,
			  x, y + rc.height - size.height,
			  rc.width, size.height);
      }

      XFlush(display);
      usleep(10000);
    }
  }

  mapped = True;
  fDir = RIGHT;
  rc.x = x;
  rc.y = y;

  ASSERT(blWm);

  /*
   * Take off the focus from the window when any menus are shown.
   */
  if (blWm->CheckMapped()) {
    if (blWm != rootBlwm)
      for (int i = 1; i < 4; i++)
	XGrabButton(display, i, 0, blWm->GetWin(), True, ButtonPressMask,
		    GrabModeAsync, GrabModeAsync, None, cursor[0]);
  }

  XSetInputFocus(display, frame, RevertToParent, CurrentTime);

  menuKey->GrabKeys(frame);
}

/*
 * UnmapMenu --
 *   Unmap menu.
 */
void Menu::UnmapMenu()
{
  int i;

  timer->ResetTimeout(new Callback<Menu>(this, &Menu::PopupMenu));

  mapped = False;
  iFocus = -1;

  if (child != NULL)
    child->UnmapMenu();

  menuKey->UngrabKeys(frame);

  ASSERT(blWm);

  /*
   * Yield the focus.
   */
  if (blWm->CheckMapped()) {
    if (blWm != rootBlwm)
      for (i = 1; i < 4; i++)
	XUngrabButton(display, i, 0, blWm->GetWin());

    /*
     * Yield the focus to the parent, if any.
     */
    if (parent != NULL)
      XSetInputFocus(display, parent->frame, RevertToParent, CurrentTime);
    else
      Blwm::SetFocusToActiveWindow();
  }

  XUnmapWindow(display, frame);

  if (parent != NULL)
    parent->child = NULL;
}

/*
 * DrawMenu --
 *   Draw menu.
 */
void Menu::DrawMenu(Window win)
{
  int num;

  if (win == frame)
    DrawFrame();
  else {
    if ((num = FindItem(win)) != -1) {
      if (func[num] == Q_SEPARATOR)
	  DrawSeparator(num);
	else
	  SetMenuFocus(num);
    }
  }
}

/*
 * DrawFrame --
 *   Draw frame.
 */
void Menu::DrawFrame()
{
  XPoint xp[3];

  xp[0].x = rc.width - 2;
  xp[0].y = 0;
  xp[1].x = 0;
  xp[1].y = 0;
  xp[2].x = 0;
  xp[2].y = rc.height - 2;

  XSetForeground(display, ::gc, gray.pixel);
  XDrawLines(display, frame, ::gc, xp, 3, CoordModeOrigin);
  
  xp[0].x = rc.width - 1;
  xp[0].y = 0;
  xp[1].x = rc.width - 1;
  xp[1].y = rc.height - 1;
  xp[2].x = 0;
  xp[2].y = rc.height - 1;

  XSetForeground(display, ::gc, darkGrey.pixel);
  XDrawLines(display, frame, ::gc, xp, 3, CoordModeOrigin);

  xp[0].x = rc.width - 3;
  xp[0].y = 1;
  xp[1].x = 1;
  xp[1].y = 1;
  xp[2].x = 1;
  xp[2].y = rc.height - 3;

  XSetForeground(display, ::gc, white.pixel);
  XDrawLines(display, frame, ::gc, xp, 3, CoordModeOrigin);
  
  xp[0].x = rc.width - 2;
  xp[0].y = 1;
  xp[1].x = rc.width - 2;
  xp[1].y = rc.height - 2;
  xp[2].x = 1;
  xp[2].y = rc.height - 2;

  XSetForeground(display, ::gc, darkGray.pixel);
  XDrawLines(display, frame, ::gc, xp, 3, CoordModeOrigin);
}

/*
 * DrawSeparator --
 *   Draw the separator.
 */
void Menu::DrawSeparator(int num)
{
  ASSERT(num >= 0 && num < nitems);

  XSetForeground(display, ::gc, darkGray.pixel);
  XDrawLine(display, item[num], ::gc, 0, 2, rc.width-1, 2);

  XSetForeground(display, ::gc, white.pixel);
  XDrawLine(display, item[num], ::gc, 0, 3, rc.width-1, 3);
}

/*
 * DrawMenuContents --
 *   Draw the contents of the menu.
 */
void Menu::DrawMenuContents(int num)
{
  XRectangle ink, log;
  int y;

  ASSERT(num >= 0 && num < nitems);

  /*
   * Draw the menu text.
   */
  XmbTextExtents(fs, name[num], strlen(name[num]), &ink, &log);
  y = (itemHeight - log.height) / 2 - log.y;

  if (num == iFocus) {
    if (IsSelectable(func[num])) {
      XSetForeground(display, gc, MenuStringActiveColor.pixel);
      DrawRealString(item[num], fs, gc, imageArea, y, name[num]);
    }
    else {
      XSetForeground(display, gc, darkGray.pixel);
      DrawRealString(item[num], fs, gc, imageArea, y, name[num]);
    }

    if (IsChecked(func[num]) && img[num] == NULL)
      imgWhiteCheck[num]->Display(item[num], Point(5, 6));

    if (IsSelected(func[num]) && img[num] == NULL)
      imgWhiteSelector[num]->Display(item[num], Point(4, 5));
    
    if (func[num] == Q_DIR)
      imgWhiteNext[num]->Display(item[num],
				 Point(rc.width - 14, itemHeight / 2 - 4));
  }
  else {
    if (IsSelectable(func[num])) {
      XSetForeground(display, gc, MenuStringColor.pixel);
      DrawRealString(item[num], fs, gc, imageArea, y, name[num]);
    }
    else {
      XSetForeground(display, gc, white.pixel);
      DrawRealString(item[num], fs, gc, imageArea + 1, y + 1, name[num]);
      XSetForeground(display, gc, darkGray.pixel);
      DrawRealString(item[num], fs, gc, imageArea, y, name[num]);
    }

    if (IsChecked(func[num]) && img[num] == NULL)
      imgBlackCheck[num]->Display(item[num], Point(5, 6));

    if (IsSelected(func[num]) && img[num] == NULL)
      imgBlackSelector[num]->Display(item[num], Point(4, 5));
    
    if (func[num] == Q_DIR)
      imgBlackNext[num]->Display(item[num],
				 Point(rc.width - 14, itemHeight / 2 - 4));
  }

  /*
   * Draw the menu pixmap, if neccesary.
   */
  if (img[num] && func[num] != Q_NONE) {
    int y = (itemHeight - img[num]->GetSize().height) / 2;

    img[num]->Display(item[num], Point(leftMargin, y));
  }
}

/*
 * SetMenuFocus --
 *   Give the focus to the menu item, and redraw.
 */
void Menu::SetMenuFocus(int num)
{
  ASSERT(num >= 0 && num < nitems);
  
  if (num == iFocus) {
    if (MenuImage)
      imgBack[num]->SetBackground(None);

    if (MenuActiveImage)
      imgActiveBack[num]->SetBackground(item[num]);
    else
      XSetWindowBackground(display, item[num], MenuActiveColor.pixel);
  }
  else {
    if (MenuActiveImage)
      imgActiveBack[num]->SetBackground(None);

    if (MenuImage)
      imgBack[num]->SetBackground(item[num]);
    else
      XSetWindowBackground(display, item[num], MenuColor.pixel);
  }

  XClearWindow(display, item[num]);

  DrawMenuContents(num);
}

/*
 * FindItem --
 *   Find the menu item corresponding to given window.
 */
int Menu::FindItem(Window win)
{
  for (int i = 0; i < nitems; i++)
    if (win == item[i])
      return i;
  
  return -1;
}

/*
 * CalcItemYPos --
 *   Calculate y position of the menu item.
 */
int Menu::CalcItemYPos(int num)
{
  int y = MenuFrameWidth;
  
  ASSERT(num < nitems);

  for (int i = 0; i < num; i++)
    if (func[i] != Q_SEPARATOR)
      y += itemHeight;
    else
      y += SeparatorHeight;

  return y;
}

/*
 * GetFixedMenuPos --
 *   Adjust the menu position.
 */
Point Menu::GetFixedMenuPos(const Point& pt, int& dir)
{
  Point ptNew;
  Rect rcRoot = rootBlwm->GetRect();

  if (pt.x + rc.width > rcRoot.width - 1) {
    ptNew.x = pt.x - rc.width;
    dir = GD_LEFT;
  }
  else {
    ptNew.x = pt.x;
    dir = GD_RIGHT;
  }
  if (pt.y + rc.height > rcRoot.height - 1) {
    ptNew.y = rcRoot.height - rc.height;
    dir |= GD_UP;
  }
  else {
    ptNew.y = pt.y;
    dir |= GD_DOWN;
  }

  return ptNew;
}

/*
 * PopupMenu --
 *   Pop down previous child menu, if any, and pop up new child menu.
 */
void Menu::PopupMenu()
{
  Menu* child = GetChildMenu();

  if (child)
    child->UnmapMenu();

  ExecFunction(Q_DIR, delayMenuNum);
}

/*
 * PopdownMenu --
 *   Pop down previous child menu.
 */
void Menu::PopdownMenu()
{
  UnmapMenu();
}  

void Menu::Leave(Window win)
{
  XEvent ev;

  if (child) {
    if (XCheckTypedWindowEvent(display, child->frame, EnterNotify, &ev)) {
      child->Enter(child->frame);
      return;
    }
    else
      child->UnmapMenu();
  }
  else
    timer->ResetTimeout(new Callback<Menu>(this, &Menu::PopupMenu));

  int pFocus = iFocus;

  if (iFocus != -1) {
    iFocus = -1;
    SetMenuFocus(pFocus);
  }
}

/*
 * Enter --
 *   Process enter event.
 */
void Menu::Enter(Window win)
{
  if (!CheckMapped() || win == frame)
    return;

  int num, pFocus;

  num = FindItem(win);

  if (iFocus != num) {
    if (iFocus != -1) {
      pFocus = iFocus;
      iFocus = -1;
      SetMenuFocus(pFocus);
    }
    if (func[num] != Q_SEPARATOR) {
      iFocus = num;
      SetMenuFocus(num);
    }

    timer->ResetTimeout(new Callback<Menu>(this, &Menu::PopupMenu));

    /*
     * Delay that child menu popups.
     */
    if (func[num] == Q_DIR) {
      delayMenuNum = num;
      if (child)
	timer->SetTimeout(MenuDelayTime2,
			  new Callback<Menu>(this, &Menu::PopupMenu));
      else
	timer->SetTimeout(MenuDelayTime,
			  new Callback<Menu>(this, &Menu::PopupMenu));
    }
    else {
      if (child)
	timer->SetTimeout(MenuDelayTime,
			  new Callback<Menu>(child, &Menu::PopdownMenu));
    }
  }
}

/*
 * Button1Press --
 *   Process the press of button1(mouse left button).
 */
void Menu::Button1Press(Window win) 
{
  int num;

  num = FindItem(win);
  ASSERT(num >= 0 && num < nitems);

  if (func[num] == Q_DIR) {
    timer->ForceTimeout(new Callback<Menu>(this, &Menu::PopupMenu));

    /*
     * When you operate a menu by both key and mouse, and when child menu
     * is not still be popuped. ???
     */
    if (child != next[num]) {
      Enter(win);
      timer->ForceTimeout(new Callback<Menu>(this, &Menu::PopupMenu));
    }
  }
}

/*
 * Button1Release --
 *   Process the release of button1(mouse left button).
 */
void Menu::Button1Release(Window win)
{
  int num;

  num = FindItem(win);
  ASSERT(num >= 0 && num < nitems);

  if (IsSelectable(func[num])) {
    ASSERT(blWm);
    /*
     * Erase all menus.
     */
    if (func[num] != Q_DIR) {
      UnmapAllMenus();
      ExecFunction(func[num], num);
    }
  }
}

/*
 * PointerMotion --
 *   Process the motion of mouse.
 */
void Menu::PointerMotion(Window win)
{
  int num;
  
  // the mouse pointer is outside of this menu
  if (iFocus == -1)
    return;

  num = FindItem(win);
  if (num != iFocus && func[num] != Q_SEPARATOR)
    Enter(win);
}

void Menu::ExecShortCutKey(char key)
{
  for (int i = 0; i < nitems; i++) {
    if (key >= 'a' && key <= 'z')
      key -= 0x20;
    if (scKey[i] == key)
      if (IsSelectable(func[i])) {
	if (func[i] != Q_DIR)
	  UnmapAllMenus();
	else {
	  if (iFocus != i) {
	    if (iFocus != -1) {
	      int pFocus = iFocus;
	      iFocus = -1;
	      SetMenuFocus(pFocus);
	    }
	    iFocus = i;
	    SetMenuFocus(i);
	  }
	}
 	BlFunction::execFunction(func[i], this, i);
      }
  }
}

Bool Menu::IsChecked(FuncNumber fn)
{
  switch (fn) {
  case Q_TOGGLE_ONTOP:
    if (blWm->CheckFlags(ONTOP))
      return True;
    break;
    
  case Q_TOGGLE_STICKY:
    if (blWm->CheckFlags(STICKY))
      return True;
    break;

  case Q_TOGGLE_AUTOHIDE:
    if (TaskbarAutoHide)
      return True;
    break;
    
  case Q_TOGGLE_TASKBAR:
    if (UseTaskbar && enableTaskbar)
      return True;
    break;

  case Q_TOGGLE_PAGER:
    if (UsePager && enablePager)
      return True;
    break;

  default:
    break;
  }

  return False;
}

Bool Menu::IsSelected(FuncNumber fn)
{
  switch (fn) {
  case Q_BOTTOM:
    if (UseTaskbar)
      if (taskBar->GetPos() == Taskbar::BOTTOM)
	return True;
    break;

  case Q_TOP:
    if (UseTaskbar)
      if (taskBar->GetPos() == Taskbar::TOP)
	return True;
    break;

  case Q_LEFT:
    if (UseTaskbar)
      if (taskBar->GetPos() == Taskbar::LEFT)
	return True;
    break;

  case Q_RIGHT:
    if (UseTaskbar)
      if (taskBar->GetPos() == Taskbar::RIGHT)
	return True;
    break;

  default:
    break;
  }

  return False;
 }
 
 /*
  * IsSelectable --
  *   Return True if the menu item is selectable.
  */
 Bool Menu::IsSelectable(FuncNumber fn)
 {
   ASSERT(blWm);
 
   switch (fn) {
   case Q_NONE:
     return False;
     
   case Q_RESTORE:
     if (blWm->CheckStatus(MINIMIZE_WINDOW | MAXIMIZE_WINDOW))
       return True;
     return False;
 
   case Q_MOVE:
   case Q_RESIZE:
     if (blWm->CheckStatus(MINIMIZE_WINDOW | MAXIMIZE_WINDOW))
       return False;
     return True;
 
   case Q_MINIMIZE:
     if (blWm->CheckStatus(MINIMIZE_WINDOW) || blWm->CheckFlags(NO_TBUTTON))
       return False;
     return True;
 
   case Q_MAXIMIZE:
     if (blWm->CheckStatus(MAXIMIZE_WINDOW) &&
 	!blWm->CheckStatus(MINIMIZE_WINDOW))
       return False;
     return True;
 
   case Q_SEPARATOR:
     return False;
 
   case Q_BOTTOM:
   case Q_TOP:
   case Q_LEFT:
   case Q_RIGHT:
     if (UseTaskbar && !DisableDesktopChange)
       return True;
     else
       return False;
 
   case Q_LEFT_PAGING:
     {
       Rect rcRoot = rootBlwm->GetRect();
       Rect rcVirt = paging->GetVirtRect();
       if (paging->origin.x > rcVirt.x * rcRoot.width)
 	return True;
       else
 	return False;
     }
 
   case Q_RIGHT_PAGING:
     {
       Rect rcRoot = rootBlwm->GetRect();
       Rect rcVirt = paging->GetVirtRect();
       if (paging->origin.x < (rcVirt.x + rcVirt.width - 1) * rcRoot.width)
 	return True;
       else
 	return False;
     }
 
   case Q_UP_PAGING:
     {
       Rect rcRoot = rootBlwm->GetRect();
       Rect rcVirt = paging->GetVirtRect();
       if (paging->origin.y > rcVirt.y * rcRoot.height)
 	return True;
       else
 	return False;
     }
     
   case Q_DOWN_PAGING:
     {
       Rect rcRoot = rootBlwm->GetRect();
       Rect rcVirt = paging->GetVirtRect();
       if (paging->origin.y < (rcVirt.y + rcVirt.height - 1) * rcRoot.height)
 	return True;
       else
 	return False;
     }
 
   default:
     return True;
  }
}
  void Menu::Initialize()
{
  Menu::context = XUniqueContext();

  /*
   * create images for default folder
   */
  imgMenu[0] = new PixmapImage(folder16);
  imgMenu[1] = new PixmapImage(icon16);

  /*
   * create pixmap for next mark
   */
  imgNext[0] = new PixmapImage(next_white);
  imgNext[1] = new PixmapImage(next_black);

  /*
   * create pixmap for check mark
   */
  imgCheck[0] = new PixmapImage(check_white);
  imgCheck[1] = new PixmapImage(check_black);

  /*
   * create pixmap for selector
   */
  imgSelector[0] = new PixmapImage(selector_white);
  imgSelector[1] = new PixmapImage(selector_black);

  if (MenuImage) {
    imgMenuBack = CreateImageFromFile(MenuImage, timer);
    if (imgMenuBack == NULL) {
      delete [] MenuImage;
      MenuImage = NULL;
    }
  }
  if (MenuActiveImage) {
    imgActiveMenuBack = CreateImageFromFile(MenuActiveImage, timer);
    if (imgActiveMenuBack == NULL) {
      delete [] MenuActiveImage;
      MenuActiveImage = NULL;
    }
  }
}

/*
 * UnmapAllMenus --
 *   Unmap all menus (blwm ctrl menu, startmenu, icon menu and taskbar menu).
 *   Only a menu in these menus is mapped.
 */
void Menu::UnmapAllMenus(Bool hideTaskbar)
{
  if (Blwm::focusBlwm && Blwm::focusBlwm->CheckMenuMapped())
    Blwm::focusBlwm->UnmapMenu();
  else if (::ctrlMenu->CheckMapped())
    ::ctrlMenu->UnmapMenu(); // for NOFOCUS window
  else if (startMenu && startMenu->CheckMapped())
    startMenu->UnmapMenu();
  else if (Icon::focusIcon && Icon::focusIcon->CheckMenuMapped())
    Icon::focusIcon->UnmapMenu();
  else if (UseTaskbar && taskBar->CheckMenuMapped())
    taskBar->UnmapMenu();

  if (UseTaskbar)
    if (hideTaskbar && TaskbarAutoHide && !taskBar->IsHiding())
      taskBar->HideTaskbar();
}

/*
 * CheckAnyMenusMapped --
 *   check whether any menus are mapped.
 */
Bool Menu::CheckAnyMenusMapped()
{
  if ((Blwm::focusBlwm && Blwm::focusBlwm->CheckMenuMapped()) ||
      ::ctrlMenu->CheckMapped() ||
      (startMenu && startMenu->CheckMapped()) ||
      (Icon::focusIcon && Icon::focusIcon->CheckMenuMapped()) ||
      (UseTaskbar && taskBar->CheckMenuMapped()))
    return True;
  
  return False;
}

/*
 * GetMappedMenuFrame --
 *   get the frame of a mapped menu.
 */
Window Menu::GetMappedMenuFrame()
{
  if (Blwm::focusBlwm && Blwm::focusBlwm->CheckMenuMapped())
    return Blwm::focusBlwm->ctrlMenu->GetFrameWin();
  else if (::ctrlMenu->CheckMapped())
    return ::ctrlMenu->GetFrameWin();
  else if (startMenu && startMenu->CheckMapped())
    return startMenu->GetFrameWin();
  else if (Icon::focusIcon && Icon::focusIcon->CheckMenuMapped())
    return Icon::ctrlMenu->GetFrameWin();
  else if (UseTaskbar && taskBar->CheckMenuMapped())
    return taskBar->GetMenu()->GetFrameWin();

  return None;
}


syntax highlighted by Code2HTML, v. 0.9.1