/**************************************************************************

    This file is part of Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Hector Peraza.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************/

#include <xclass/utils.h>
#include <xclass/OXWidgets.h>
#include <xclass/OXClient.h>
#include <xclass/OString.h>

#include <X11/cursorfont.h>
#include <X11/keysym.h>


//-----------------------------------------------------------------

OXMenuBar::OXMenuBar(const OXWindow *p, int w, int h,
		     unsigned int options) :
  OXHorizontalFrame(p, w, h, options | HORIZONTAL_FRAME) {

    _stick = True;
    _current = NULL;

    XGrabButton(GetDisplay(), Button1, AnyModifier, _id, True,
		ButtonPressMask | ButtonReleaseMask |
		EnterWindowMask,
		GrabModeAsync, GrabModeAsync, None, None);
}

OXMenuBar::~OXMenuBar() {
  SListFrameElt *ptr;
  OXMenuTitle *t;
  int keycode;
  const OXMainFrame *main = (OXMainFrame *) _GetMainFrame();
  
  // we should not access the _flist directly, but rather by way of an access method
  // (hence declaring _flist as private in OXCompositeFrame)
  for (ptr = _flist; ptr != NULL; ptr = ptr->next) {
    t = (OXMenuTitle *) ptr->frame;
    if ((keycode = t->GetHotKeyCode()) != 0)
      main->RemoveBind(this, keycode, Mod1Mask);
  }
}

void OXMenuBar::AddPopup(OHotString *s, OXPopupMenu *menu, OLayoutHints *l) {
  OXMenuTitle *t;
  int keycode;

  AddFrame(t = new OXMenuTitle(this, s, menu), l);

  if ((keycode = t->GetHotKeyCode()) != 0) {
    const OXMainFrame *main = (OXMainFrame *) _GetMainFrame();
    main->BindKey(this, keycode, Mod1Mask);
  }
}

int OXMenuBar::HandleButton(XButtonEvent *event) {
  SListFrameElt *ptr;

  // Here we are looking for a _child_ of a menu bar, so it can _ONLY_ be
  // a MenuTitle : BEWARE !

  OXMenuTitle *target;
  
  // We don't need to check the button number as XGrabButton will
  // only allow button1 events

  if (event->type == ButtonPress) {
    /*
    int dummy;
    Window wtarget;

    XTranslateCoordinates(GetDisplay(), _id, _id, event->x, event->y,
                          &dummy, &dummy, &wtarget);
    target = (OXMenuTitle*)_client->GetWindowById(wtarget);
    */
    target = (OXMenuTitle*)_client->GetWindowById(event->subwindow);

    _x0 = event->x;
    _y0 = event->y;

    if (target != NULL) {
      _stick = True;

      if (target != _current) {
        // Deactivate all others :
        for (ptr = _flist; ptr != NULL; ptr = ptr->next)
          ((OXMenuTitle*)ptr->frame)->SetState(False);

        target->SetState(True);
        _current = target;

        XGrabPointer(GetDisplay(), _id, True,
                     ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
                     GrabModeAsync, GrabModeAsync, None, _defaultCursor, CurrentTime);
      }
    }
  }
  
  if (event->type == ButtonRelease) {
    if (_stick) {
      _stick = False;
      return True;
    }
    XUngrabPointer(GetDisplay(), CurrentTime);

    for (ptr = _flist; ptr != NULL; ptr = ptr->next)
      ((OXMenuTitle*)ptr->frame)->SetState(False);

    if (_current != NULL) {
      target = _current; // tricky, because WaitFor
      _current = NULL;
      target->DoSendMessage();
    }
  }

  return True;
}

int OXMenuBar::HandleMotion(XMotionEvent *event) {
  SListFrameElt *ptr;
  int dummy;
  Window wtarget;
  OXMenuTitle *target;

  // use some threshold
  if (_stick &&
      (abs(event->x - _x0) < 5) &&
      (abs(event->y - _y0) < 5)) return True;

  _stick = False;

  XTranslateCoordinates(GetDisplay(), _id, _id, event->x, event->y,
                        &dummy, &dummy, &wtarget);
  target = (OXMenuTitle*)_client->GetWindowById(wtarget);

  if (target != NULL && target != _current) {
    // Deactivate all others :
    for (ptr = _flist; ptr != NULL; ptr = ptr->next)
      ((OXMenuTitle*)ptr->frame)->SetState(False);

    target->SetState(True);
    _stick = True;
    _current = target;
  }

  return True;
}

int OXMenuBar::HandleKey(XKeyEvent *event) {
  SListFrameElt *ptr;
  OXMenuTitle *target;
  
  if (event->type == KeyPress) {

    for (ptr = _flist; ptr != NULL; ptr = ptr->next) {
      target = (OXMenuTitle *) ptr->frame;
      if (event->keycode == target->GetHotKeyCode()) break;
    }
    if (ptr == NULL) target = NULL;

    if (target != NULL) {
      _stick = True;

      if (target != _current) {
        // Deactivate all others :
        for (ptr = _flist; ptr != NULL; ptr = ptr->next)
          ((OXMenuTitle*)ptr->frame)->SetState(False);

        target->SetState(True);
        _stick = True;
        _current = target;

        XGrabPointer(GetDisplay(), _id, True,
                     ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
                     GrabModeAsync, GrabModeAsync, None, _defaultCursor, CurrentTime);
      }
    }
  }
  
  if (event->type == KeyRelease) {
    if (_stick) {
      _stick = False;
      return True;
    }
    XUngrabPointer(GetDisplay(), CurrentTime);

    for (ptr = _flist; ptr != NULL; ptr = ptr->next)
      ((OXMenuTitle*)ptr->frame)->SetState(False);

    if (_current != NULL) {
      target = _current; /* tricky, because WaitFor */
      _current = NULL;
      target->DoSendMessage();
    }
  }

  return True;
}

//-----------------------------------------------------------------

OXPopupMenu::OXPopupMenu(const OXWindow *p, int w, int h,
			 unsigned int options)
  : OXFrame(p, w, h, options | OWN_BKGND) {
    XSetWindowAttributes wattr;
    unsigned long mask;

    _normGC       = _defaultGC;
    _selGC        = _defaultSelectedGC;
    _selbackGC    = _defaultSelectedBackgroundGC;
    _fontStruct   = _defaultFontStruct;
    _hifontStruct = _hilightFontStruct;

    _delay = NULL;

    _bw = 3;
    _h  = 6;
    _w  = 8;
    _xl = 16;
    _elist = NULL;
    _msgWindow = p;
    _stick = True;
    _current = NULL;
    _hasgrab = False;
    mask = CWOverrideRedirect | CWSaveUnder;
    wattr.override_redirect = True;
    wattr.save_under = True;
    
    XChangeWindowAttributes(GetDisplay(), _id, mask, &wattr);

    XSelectInput(GetDisplay(), _id, PointerMotionMask |
                                    EnterWindowMask | LeaveWindowMask |
                                    ExposureMask);
}

OXPopupMenu::~OXPopupMenu() {
  RemoveAllEntries();
  if (_delay) delete _delay;
}

void OXPopupMenu::AddEntry(OHotString *s, int ID, const OPicture *p) {
  OMenuEntry *nw, *ptr;
  int tw, pw = 0;

  nw = new OMenuEntry;

  nw->_label   = s;
  nw->_pic     = p;
  nw->_type    = MENU_ENTRY;
  nw->_entryID = ID;
  nw->_popup   = NULL;
  nw->_status  = MENU_ENABLE_MASK;
  nw->_ex  = 2;
  nw->_ey  = _h-2;
  nw->next = NULL;

  if (_elist != NULL) {
    for (ptr=_elist; ptr->next != NULL; ptr=ptr->next);
    ptr->next = nw;
  } else {
    _elist = nw;
  }

  tw = XTextWidth(_hifontStruct, s->GetString(), s->GetLength());
  if (p) {
    pw = p->GetWidth();
    if (pw+12 > _xl) { _w += pw+12-_xl; _xl = pw+12; }
  }

  _w = max(_w, tw + pw /*+8*/+18+12);
  _h += _hifontStruct->max_bounds.ascent +
        _hifontStruct->max_bounds.descent + 3;

  Resize(_w, _h);
}

void OXPopupMenu::AddSeparator() {
  OMenuEntry *nw, *ptr;

  nw = new OMenuEntry;

  nw->_label   = NULL;
  nw->_pic     = NULL;
  nw->_type    = MENU_SEPARATOR;
  nw->_entryID = -1;
  nw->_popup   = NULL;
  nw->_status  = MENU_ENABLE_MASK;
  nw->_ex  = 2;
  nw->_ey  = _h-2;
  nw->next = NULL;

  if (_elist != NULL) {
    for (ptr=_elist; ptr->next != NULL; ptr=ptr->next);
    ptr->next = nw;
  } else {
    _elist = nw;
  }

  _h += 4;

  Resize(_w, _h);
}

void OXPopupMenu::AddPopup(OHotString *s, OXPopupMenu *popup) {
  OMenuEntry *nw, *ptr;
  int tw;

  nw = new OMenuEntry;

  nw->_label   = s;
  nw->_pic     = NULL;
  nw->_type    = MENU_POPUP;
  nw->_entryID = -2;
  nw->_popup   = popup;
  nw->_status  = MENU_ENABLE_MASK;
  nw->_ex  = 2;
  nw->_ey  = _h-2;
  nw->next = NULL;

  if (_elist != NULL) {
    for (ptr=_elist; ptr->next != NULL; ptr=ptr->next);
    ptr->next = nw;
  } else {
    _elist = nw;
  }

  tw = XTextWidth(_hifontStruct, s->GetString(), s->GetLength());

  _w = max(_w, tw +8+18+12);
  _h += _hifontStruct->max_bounds.ascent +
        _hifontStruct->max_bounds.descent + 3;

  Resize(_w, _h);
}

void OXPopupMenu::RemoveEntry(int ID) {
  OMenuEntry *ptr, *ptr2;

  if (_elist == NULL) return;

  if (_elist->_entryID == ID) {
    ptr = _elist;
    _elist = _elist->next;
    delete ptr;
  } else {
    for (ptr = _elist;
        (ptr->next != NULL) && (ptr->next->_entryID != ID);
         ptr = ptr->next);
    if (ptr->next == NULL) return; // not found!
    ptr2 = ptr->next;
    ptr->next = ptr2->next;
    delete ptr2;
  }

  AdjustEntries();
}

void OXPopupMenu::RemoveAllEntries() {
  OMenuEntry *ptr, *next;

  ptr = _elist;
  while (ptr) {
    next = ptr->next;
    delete ptr;
    ptr = next;
  }
  _elist = NULL;
  _h = 6;
  _w = 8;
  _xl = 16;
  Resize(_w, _h);
}

void OXPopupMenu::AdjustEntries() {
  int tw, pw;
  OMenuEntry *ptr;

  _w = 8;
  _h = 6;
  _xl = 16;

  for (ptr=_elist; ptr!=NULL; ptr=ptr->next) {
    ptr->_ex = 2;
    ptr->_ey = _h-2;
    switch (ptr->_type) {
      case MENU_ENTRY:
        tw = XTextWidth(_hifontStruct, ptr->_label->GetString(),
                                       ptr->_label->GetLength());
        if (ptr->_pic) {
          pw = ptr->_pic->GetWidth();
          if (pw+12 > _xl) { _w += pw+12-_xl; _xl = pw+12; }
        }
        _w = max(_w, tw + pw /*+8*/+18+12);
        _h += _hifontStruct->max_bounds.ascent +
              _hifontStruct->max_bounds.descent + 3;
        break;

      case MENU_POPUP:
        tw = XTextWidth(_hifontStruct, ptr->_label->GetString(),
                                       ptr->_label->GetLength());
        _w = max(_w, tw +8+18+12);
        _h += _hifontStruct->max_bounds.ascent +
              _hifontStruct->max_bounds.descent + 3;
        break;

      case MENU_SEPARATOR:
        _h += 4;
        break;
    }
  }

  Resize(_w, _h);
}

void OXPopupMenu::PlaceMenu(int x, int y, int stick_mode, int grab_pointer) {
  int rx, ry;
  unsigned int rw, rh;
  unsigned int dummy;
  Window wdummy;
  
  _stick = stick_mode;
  _current = NULL;

  // Parent is root window for a popup menu:
  XGetGeometry(GetDisplay(), _parent->GetId(), &wdummy,
               &rx, &ry, &rw, &rh, &dummy, &dummy);

  if (x < 0) x = 0;
  if (x + _w > rw) x = rw - _w;
  if (y < 0) y = 0;
  if (y + _h > rh) y = rh - _h;

  Move(x, y);
  MapRaised();

  if (grab_pointer) {
    XGrabPointer(GetDisplay(), _id, True,
 		ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
		GrabModeAsync, GrabModeAsync, None, _defaultCursor, CurrentTime);
    _hasgrab = True;
  } else {
    _hasgrab = False;
  }
}

int OXPopupMenu::EndMenu() {
  int ID;

  if (_delay) { delete _delay; _delay = NULL; }

  // destroy any cascaded child menus and get any ID back

  if (_current != NULL) {

    // deactivate the entry
    _current->_status &= ~MENU_ACTIVE_MASK;

    if (_current->_type == MENU_POPUP) {
      ID = _current->_popup->EndMenu();
    } else {
      // return the ID if the entry is enabled, otherwise -1
      if (_current->_status & MENU_ENABLE_MASK) {
        ID = _current->_entryID;
      } else {
        ID = -1;
      }
    }

  } else {
    // if no entry selected...
    ID = -1;
  }

  // then unmap itself
  UnmapWindow();
  return ID;
}

int OXPopupMenu::HandleButton(XButtonEvent *event) {
  int ID;
  
  if (event->type == ButtonRelease) {
    if (_stick) {
      _stick = False;
      return True;
      }
//    if (_current != NULL)
//      if (_current->_type == MENU_POPUP) return True;
    ID = EndMenu();
    if (_hasgrab) XUngrabPointer(GetDisplay(), CurrentTime);
    if (_current != NULL) {
      _current->_status &= ~MENU_ACTIVE_MASK;
      if (_current->_status & MENU_ENABLE_MASK)
        SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_MENU), ID, 0L);
    }
  }
  return True;
}

int OXPopupMenu::HandleCrossing(XCrossingEvent *event) {

  if (event->type == EnterNotify) {
    OMenuEntry *ptr;
    int h;

    for (ptr = _elist; ptr != NULL; ptr=ptr->next) {
      if (ptr->_type == MENU_SEPARATOR) {
        h = 4;
      } else {
        h = _fontStruct->max_bounds.ascent +
            _fontStruct->max_bounds.descent + 3;
      }
      if ((event->x >= ptr->_ex) && (event->x <= ptr->_ex+_w-10) &&
          (event->y >= ptr->_ey) && (event->y <= ptr->_ey+h)) break;
    }
    Activate(ptr);
  } else {
    Activate(NULL);
  }

  return True;
}

int OXPopupMenu::HandleMotion(XMotionEvent *event) {
  OMenuEntry *ptr;
  int h;
  
  OXFrame::HandleMotion(event);
  _stick = False; // be careful with this, use some threshold
  for (ptr = _elist; ptr != NULL; ptr=ptr->next) {
    if (ptr->_type == MENU_SEPARATOR) {
      h = 4;
    } else {
      h = _fontStruct->max_bounds.ascent +
          _fontStruct->max_bounds.descent + 3;
    }
    if ((event->x >= ptr->_ex) && (event->x <= ptr->_ex+_w-4) &&  //_w-10??
        (event->y >= ptr->_ey) && (event->y <= ptr->_ey+h)) break;
  }
  Activate(ptr);
  return True;
}

void OXPopupMenu::Activate(OMenuEntry *entry) {

  if (entry == _current) return;

  //-- Deactivate the current entry

  if (_current != NULL) {
    if (entry == NULL && _current->_type == MENU_POPUP) return;
    if (_current->_type == MENU_POPUP) _current->_popup->EndMenu();
    _current->_status &= ~MENU_ACTIVE_MASK;
    DrawEntry(_current);
  }

  if (_delay) { delete _delay; _delay = NULL; }

  //-- Activate the new one

  if (entry != NULL) {
    entry->_status |= MENU_ACTIVE_MASK;
    DrawEntry(entry);
    if (entry->_type == MENU_POPUP) {
      _delay = new OTimer(this, 350);
      /* this now goes to the timer handler routine...
      int ax, ay;
      Window wdummy;

      XTranslateCoordinates(GetDisplay(), _id, (entry->_popup->GetParent())->GetId(),
                            entry->_ex+_w, entry->_ey, &ax, &ay, &wdummy);

      entry->_popup->PlaceMenu(ax-5, ay-1, False, False);
      */
    } else if (entry->_type == MENU_ENTRY) {
      // test...
      SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_MENUSELECT),
                              entry->_entryID, 0L);
    }
  }

  _current = entry;
}

int OXPopupMenu::HandleTimer(OTimer *t) {
  if (t != _delay) return False;

  if (_current != NULL)
    if (_current->_type == MENU_POPUP) {
      int ax, ay;
      Window wdummy;

      XTranslateCoordinates(GetDisplay(), _id,
                            (_current->_popup->GetParent())->GetId(),
                            _current->_ex+_w, _current->_ey,
                            &ax, &ay, &wdummy);

      _current->_popup->PlaceMenu(ax-5, ay-1, False, False);
    }

  delete _delay;
  _delay = NULL;

  return True;
}

void OXPopupMenu::_DoRedraw() {
  OMenuEntry *ptr;
  
  OXFrame::_DoRedraw();
  for (ptr = _elist; ptr != NULL; ptr=ptr->next) {
    DrawEntry(ptr);
  }
}

void OXPopupMenu::DrawEntry(OMenuEntry *entry) {
  XFontStruct *font;
  XGCValues gcval;

  if (entry->_status & MENU_DEFAULT_MASK) {
    font = _hifontStruct;
    gcval.font = font->fid;
    XChangeGC(GetDisplay(), _normGC, GCFont, &gcval);
    XChangeGC(GetDisplay(), _selGC, GCFont, &gcval);
  } else {
    font = _fontStruct;
  }

  int tx = entry->_ex + _xl;
  int ty = entry->_ey + font->max_bounds.ascent;

  switch(entry->_type) {
  case MENU_POPUP:
  case MENU_ENTRY:
      if (entry->_status & MENU_ACTIVE_MASK) {
        XFillRectangle(GetDisplay(), _id, _selbackGC, entry->_ex+1, entry->_ey-1,
                       _w-6, font->max_bounds.ascent +
                             font->max_bounds.descent + 3);
        if (entry->_type == MENU_POPUP)
          DrawTrianglePattern(_selGC, _w-10, entry->_ey+3, _w-6, entry->_ey+11);
        if (entry->_status & MENU_CHECKED_MASK)
          DrawCheckMark(_selGC, 6, entry->_ey+3, 14, entry->_ey+11);
        if (entry->_status & MENU_RCHECKED_MASK)
          DrawRCheckMark(_selGC, 6, entry->_ey+3, 14, entry->_ey+11);
        if (entry->_pic != NULL)
          entry->_pic->Draw(GetDisplay(), _id, _selGC, 8, entry->_ey+1);
        entry->_label->Draw(GetDisplay(), _id,
                           (entry->_status & MENU_ENABLE_MASK) ? _selGC : _shadowGC,
                            tx, ty);
      } else {
        XFillRectangle(GetDisplay(), _id, _bckgndGC, entry->_ex+1, entry->_ey-1,
                       _w-6, font->max_bounds.ascent +
                             font->max_bounds.descent + 3);
        if (entry->_type == MENU_POPUP)
          DrawTrianglePattern(_normGC, _w-10, entry->_ey+3, _w-6, entry->_ey+11);
        if (entry->_status & MENU_CHECKED_MASK)
          DrawCheckMark(_normGC, 6, entry->_ey+3, 14, entry->_ey+11);
        if (entry->_status & MENU_RCHECKED_MASK)
          DrawRCheckMark(_normGC, 6, entry->_ey+3, 14, entry->_ey+11);
        if (entry->_pic != NULL)
          entry->_pic->Draw(GetDisplay(), _id, _normGC, 8, entry->_ey+1);
        if (entry->_status & MENU_ENABLE_MASK) {
          entry->_label->Draw(GetDisplay(), _id, _normGC, tx, ty);
        } else {
          entry->_label->Draw(GetDisplay(), _id, _hilightGC, tx+1, ty+1);
          entry->_label->Draw(GetDisplay(), _id, _shadowGC, tx, ty);
        }
      }
      break;

   case MENU_SEPARATOR:
      XDrawLine(GetDisplay(), _id, _shadowGC,  2, entry->_ey, _w-3, entry->_ey);
      XDrawLine(GetDisplay(), _id, _hilightGC, 2, entry->_ey+1, _w-3, entry->_ey+1);
      break;
   }

  // restore font
  if (entry->_status & MENU_DEFAULT_MASK) {
    gcval.font = _fontStruct->fid;
    XChangeGC(GetDisplay(), _normGC, GCFont, &gcval);
    XChangeGC(GetDisplay(), _selGC, GCFont, &gcval);
  }
}

void OXPopupMenu::DrawBorder() {
  XDrawLine (GetDisplay(), _id, _bckgndGC, 0, 0, _w-2, 0);
  XDrawLine (GetDisplay(), _id, _bckgndGC, 0, 0, 0, _h-2);
  XDrawLine (GetDisplay(), _id, _hilightGC, 1, 1, _w-3, 1);
  XDrawLine (GetDisplay(), _id, _hilightGC, 1, 1, 1, _h-3);
    
  XDrawLine (GetDisplay(), _id, _shadowGC,  1, _h-2, _w-2, _h-2);
  XDrawLine (GetDisplay(), _id, _shadowGC,  _w-2, _h-2, _w-2, 1);
  XDrawLine (GetDisplay(), _id, _blackGC, 0, _h-1, _w-1, _h-1);
  XDrawLine (GetDisplay(), _id, _blackGC, _w-1, _h-1, _w-1, 0);
}

void OXPopupMenu::DrawTrianglePattern(GC gc, int l, int t, int r, int b) {
  XPoint points[3];

  int m = (t + b) >> 1;

  points[0].x = l;
  points[0].y = t;
  points[1].x = l;
  points[1].y = b;
  points[2].x = r;
  points[2].y = m;

  XFillPolygon(GetDisplay(), _id, gc, points, 3, Convex, CoordModeOrigin);
}

void OXPopupMenu::DrawCheckMark(GC gc, int l, int t, int r, int b) {
  XSegment seg[6];

  t = (t + b - 8) >> 1; ++t;

  seg[0].x1 = 1+l; seg[0].y1 = 3+t; seg[0].x2 = 3+l; seg[0].y2 = 5+t;
  seg[1].x1 = 1+l; seg[1].y1 = 4+t; seg[1].x2 = 3+l; seg[1].y2 = 6+t;
  seg[2].x1 = 1+l; seg[2].y1 = 5+t; seg[2].x2 = 3+l; seg[2].y2 = 7+t;
  seg[3].x1 = 3+l; seg[3].y1 = 5+t; seg[3].x2 = 7+l; seg[3].y2 = 1+t;
  seg[4].x1 = 3+l; seg[4].y1 = 6+t; seg[4].x2 = 7+l; seg[4].y2 = 2+t;
  seg[5].x1 = 3+l; seg[5].y1 = 7+t; seg[5].x2 = 7+l; seg[5].y2 = 3+t;

  XDrawSegments(GetDisplay(), _id, gc, seg, 6);
//  XCopyPlane(GetDisplay(), _checkmark, _id, gc, 0, 0, 13, 13, l, t, 1);
}

void OXPopupMenu::DrawRCheckMark(GC gc, int l, int t, int r, int b) {
  XSegment seg[5];

  t = (t + b - 5) >> 1; ++t;
  l = (l + r - 5) >> 1; ++l;

  seg[0].x1 = 1+l; seg[0].y1 = 0+t; seg[0].x2 = 3+l; seg[0].y2 = 0+t;
  seg[1].x1 = 0+l; seg[1].y1 = 1+t; seg[1].x2 = 4+l; seg[1].y2 = 1+t;
  seg[2].x1 = 0+l; seg[2].y1 = 2+t; seg[2].x2 = 4+l; seg[2].y2 = 2+t;
  seg[3].x1 = 0+l; seg[3].y1 = 3+t; seg[3].x2 = 4+l; seg[3].y2 = 3+t;
  seg[4].x1 = 1+l; seg[4].y1 = 4+t; seg[4].x2 = 3+l; seg[4].y2 = 4+t;

  XDrawSegments(GetDisplay(), _id, gc, seg, 5);
//  XCopyPlane(GetDisplay(), _radiomark, _id, gc, 0, 0, 12, 12, l, t, 1);
}


//---- set default entry (default entries are drawn with bold text)

void OXPopupMenu::SetDefaultEntry(int ID) {
  OMenuEntry *ptr;
  
  for (ptr = _elist; ptr != NULL; ptr=ptr->next) {
    if (ptr->_entryID == ID)
      ptr->_status |= MENU_DEFAULT_MASK;
    else
      ptr->_status &= ~MENU_DEFAULT_MASK;
  }
}


//---- enable/disable entry (disabled entries appear in a sunken relief)

void OXPopupMenu::EnableEntry(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) { ptr->_status |= MENU_ENABLE_MASK; break; }
}

void OXPopupMenu::DisableEntry(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) { ptr->_status &= ~MENU_ENABLE_MASK; break; }
}

int OXPopupMenu::IsEntryEnabled(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) return (ptr->_status & MENU_ENABLE_MASK);
  return 0;
}


//---- check/uncheck entry

void OXPopupMenu::CheckEntry(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) { ptr->_status |= MENU_CHECKED_MASK; break; }
}

void OXPopupMenu::UnCheckEntry(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) { ptr->_status &= ~MENU_CHECKED_MASK; break; }
}

int OXPopupMenu::IsEntryChecked(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) return (ptr->_status & MENU_CHECKED_MASK);
  return 0;
}


//---- radio-select entry (note that they cannot be unselected,
//                         the selection must be moved to another entry instead)

void OXPopupMenu::RCheckEntry(int ID, int IDfirst, int IDlast) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID)
      ptr->_status |= MENU_RCHECKED_MASK;
    else
      if(ptr->_entryID >= IDfirst && ptr->_entryID <= IDlast) 
        ptr->_status &= ~MENU_RCHECKED_MASK;
}

int OXPopupMenu::IsEntryRChecked(int ID) {
  OMenuEntry *ptr;

  for (ptr = _elist; ptr != NULL; ptr=ptr->next)
    if(ptr->_entryID == ID) return (ptr->_status & MENU_RCHECKED_MASK);
  return 0;
}


//-----------------------------------------------------------------

OXMenuTitle::OXMenuTitle(const OXWindow *p, OHotString *s, OXPopupMenu *menu, 
	      GC norm, XFontStruct *font, unsigned int options)
  : OXFrame(p, 1, 1, options) {
    int tw, hotchar;
 
    _label = s;
    _menu = menu;
    _fontStruct = font;
    _selGC = _defaultSelectedGC;
    _selbackGC = _defaultSelectedBackgroundGC;
    _normGC = norm;
    _state = BUTTON_UP;
    _ID = -1;

    if ((hotchar = s->GetHotChar()) != 0)
      _hkeycode = XKeysymToKeycode(GetDisplay(), hotchar);
    else
      _hkeycode = 0;

    tw = XTextWidth(_fontStruct, _label->GetString(), _label->GetLength());
  
    Resize(tw + 8, _fontStruct->max_bounds.ascent +
                   _fontStruct->max_bounds.descent + 7);
} 

void OXMenuTitle::SetState(int state) {
  _state = state;
  if (state) {
    if (_menu != NULL) {
      int ax, ay;
      Window wdummy;

      XTranslateCoordinates(GetDisplay(), _id, (_menu->GetParent())->GetId(),
			0, 0, &ax, &ay, &wdummy);

      // Place the menu just under the window :
      _menu->PlaceMenu(ax-1, ay+_h, True, False); //True);
    }
  } else {
    if (_menu != NULL) {
      _ID = _menu->EndMenu();
    }
  }
  _options &= ~(SUNKEN_FRAME | RAISED_FRAME);
  _client->NeedRedraw(this);
}

void OXMenuTitle::_DoRedraw() {
  int x, y;

  OXFrame::_DoRedraw();
  x = 4; y = 4;
  if (_state) {
    XFillRectangle(GetDisplay(), _id, _selbackGC, 0, 0, _w, _h);
    _label->Draw(GetDisplay(), _id, _selGC, x, y + _fontStruct->max_bounds.ascent);
  } else {
    XClearWindow(GetDisplay(), _id);
    _label->Draw(GetDisplay(), _id, _normGC, x, y + _fontStruct->max_bounds.ascent);
  }
}

void OXMenuTitle::DoSendMessage() {
  if (_menu != NULL)
    if (_ID != -1)
      SendMessage(_menu->_msgWindow, MK_MSG(XC_COMMAND, CM_MENU), _ID, 0L);
}
