home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / Fl_Menu.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-09  |  21.6 KB  |  733 lines

  1. //
  2. // "$Id: Fl_Menu.cxx,v 1.18.2.2 1999/08/09 06:19:33 bill Exp $"
  3. //
  4. // Menu code for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-1999 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  24. //
  25.  
  26. // Warning: this menu code is quite a mess!
  27.  
  28. // This file contains code for implementing Fl_Menu_Item, and for
  29. // methods for bringing up popup menu hierarchies without using the
  30. // Fl_Menu_ widget.
  31.  
  32. #include <FL/Fl.H>
  33. #include <FL/Fl_Menu_Window.H>
  34. #include <FL/Fl_Menu_.H>
  35. #include <FL/fl_draw.H>
  36.  
  37. int Fl_Menu_Item::size() const {
  38.   const Fl_Menu_Item* m = this;
  39.   int nest = 0;
  40.   for (;;) {
  41.     if (!m->text) {
  42.       if (!nest) return (m-this+1);
  43.       nest--;
  44.     } else if (m->flags & FL_SUBMENU) {
  45.       nest++;
  46.     }
  47.     m++;
  48.   }
  49. }
  50.  
  51. const Fl_Menu_Item* Fl_Menu_Item::next(int n) const {
  52.   if (n < 0) return 0; // this is so selected==-1 returns NULL
  53.   const Fl_Menu_Item* m = this;
  54.   int nest = 0;
  55.   while (n>0) {
  56.     if (!m->text) {
  57.       if (!nest) return m;
  58.       nest--;
  59.     } else if (m->flags&FL_SUBMENU) {
  60.       nest++;
  61.     }
  62.     m++;
  63.     if (!nest && m->visible()) n--;
  64.   }
  65.   return m;
  66. }
  67.  
  68. // appearance of current menus are pulled from this parent widget:
  69. static const Fl_Menu_* button;
  70.  
  71. ////////////////////////////////////////////////////////////////
  72.  
  73. // tiny window for title of menu:
  74. class menutitle : public Fl_Menu_Window {
  75.   void draw();
  76. public:
  77.   const Fl_Menu_Item* menu;
  78.   menutitle(int X, int Y, int W, int H, const Fl_Menu_Item*);
  79. };
  80.  
  81. // each vertical menu has one of these:
  82. class menuwindow : public Fl_Menu_Window {
  83.   void draw();
  84.   void drawentry(const Fl_Menu_Item*, int i, int erase);
  85. public:
  86.   menutitle* title;
  87.   int handle(int);
  88.   int itemheight;    // zero == menubar
  89.   int numitems;
  90.   int selected;
  91.   int drawn_selected;    // last redraw has this selected
  92.   const Fl_Menu_Item* menu;
  93.   menuwindow(const Fl_Menu_Item* m, int X, int Y, int W, int H,
  94.          const Fl_Menu_Item* picked, const Fl_Menu_Item* title,
  95.          int menubar = 0, int menubar_title = 0);
  96.   ~menuwindow();
  97.   void set_selected(int);
  98.   int find_selected(int mx, int my);
  99.   int titlex(int);
  100.   void autoscroll(int);
  101.   void position(int x, int y);
  102. };
  103.  
  104. #define LEADING 4 // extra vertical leading
  105.  
  106. extern char fl_draw_shortcut;
  107.  
  108. // width of label, including effect of & characters:
  109. int Fl_Menu_Item::measure(int* hp, const Fl_Menu_* m) const {
  110.   Fl_Label l;
  111.   l.value = text;
  112.   l.type = labeltype_;
  113.   l.font = labelsize_ ? labelfont_ : uchar(m ? m->textfont() : FL_HELVETICA);
  114.   l.size = labelsize_ ? labelsize_ : m ? m->textsize() : FL_NORMAL_SIZE;
  115.   l.color = labelcolor_;
  116.   fl_draw_shortcut = 1;
  117.   int w = 0; int h = 0; l.measure(w, hp ? *hp : h);
  118.   fl_draw_shortcut = 0;
  119.   if (flags & (FL_MENU_TOGGLE|FL_MENU_RADIO)) w += 14;
  120.   return w;
  121. }
  122.  
  123. void Fl_Menu_Item::draw(int x, int y, int w, int h, const Fl_Menu_* m,
  124.             int selected) const {
  125.   Fl_Label l;
  126.   l.value = text;
  127.   l.type = labeltype_;
  128.   l.font = labelsize_ ? labelfont_ : uchar(m ? m->textfont() : FL_HELVETICA);
  129.   l.size = labelsize_ ? labelsize_ : m ? m->textsize() : FL_NORMAL_SIZE;
  130.   l.color = !active() ? inactive((Fl_Color)labelcolor_) : (Fl_Color)labelcolor_;
  131.   Fl_Color color = m ? m->color() : FL_GRAY;
  132.   if (selected) {
  133.     Fl_Color r = m ? m->selection_color() : FL_SELECTION_COLOR;
  134.     Fl_Boxtype b = m && m->down_box() ? m->down_box() : FL_FLAT_BOX;
  135.     if (contrast(r,color)!=r) { // back compatability boxtypes
  136.       if (selected == 2) { // menu title
  137.     r = color;
  138.     b = m ? m->box() : FL_UP_BOX;
  139.       } else {
  140.     r = (Fl_Color)(FL_COLOR_CUBE-1); // white
  141.     l.color = contrast((Fl_Color)labelcolor_, r);
  142.       }
  143.     } else {
  144.       l.color = contrast((Fl_Color)labelcolor_, r);
  145.     }
  146.     if (selected == 2) { // menu title
  147.       fl_draw_box(b, x, y, w, h, r);
  148.       x += 3;
  149.       w -= 8;
  150.     } else {
  151.       fl_draw_box(b, x+1, y-1, w-2, h+2, r);
  152.     }
  153.   }
  154.  
  155.   if (flags & (FL_MENU_TOGGLE|FL_MENU_RADIO)) {
  156.     int y1 = y+(h-14)/2;
  157.     fl_color(FL_DARK3);
  158.     if (flags & FL_MENU_RADIO) {
  159.       fl_line(x+2, y1+7, x+8, y1+1, x+14, y1+7);
  160.       if (selected) {
  161.     fl_color(color); 
  162.     fl_polygon(x+3, y1+7, x+8, y1+2, x+13, y1+7, x+8, y1+12);
  163.       }
  164.       fl_color(FL_LIGHT3); fl_line(x+14, y1+7, x+8, y1+13, x+2, y1+7);
  165.       if (value()) {
  166.     fl_color(FL_BLACK); 
  167.     fl_polygon(x+4, y1+7, x+8, y1+3, x+12, y1+7, x+8, y1+11);
  168.       }
  169.     } else {
  170.       fl_yxline(x+3, y1+11, y1+2, x+12);
  171.       if (selected) {fl_color(color); fl_rectf(x+4, y1+3, 9, 9);}
  172.       fl_color(FL_LIGHT3); fl_xyline(x+4, y1+12, x+13, y1+3);
  173.       if (value()) {fl_color(FL_BLACK); fl_rectf(x+5, y1+4, 7, 7);}
  174.     }
  175.     x += 14; w -= 14;
  176.   }
  177.  
  178.   if (!fl_draw_shortcut) fl_draw_shortcut = 1;
  179.   l.draw(x+3, y, w>6 ? w-6 : 0, h, FL_ALIGN_LEFT);
  180.   fl_draw_shortcut = 0;
  181. }
  182.  
  183. menutitle::menutitle(int X, int Y, int W, int H, const Fl_Menu_Item* L) :
  184.   Fl_Menu_Window(X, Y, W, H, 0) {
  185.   end();
  186.   set_modal();
  187.   clear_border();
  188.   menu = L;
  189.   if (L->labelcolor_) clear_overlay();
  190. }
  191.  
  192. menuwindow::menuwindow(const Fl_Menu_Item* m, int X, int Y, int Wp, int Hp,
  193.                const Fl_Menu_Item* picked, const Fl_Menu_Item* t, 
  194.                int menubar, int menubar_title)
  195.   : Fl_Menu_Window(X, Y, Wp, Hp, 0)
  196. {
  197.   end();
  198.   set_modal();
  199.   clear_border();
  200.   menu = m;
  201.   drawn_selected = -1;
  202.   box(button && button->box() ? button->box() : FL_UP_BOX);
  203.   color(button ? button->color() : FL_GRAY);
  204.   selected = -1;
  205.   {int i = 0;
  206.   if (m) for (const Fl_Menu_Item* m1=m; ; m1 = m1->next(), i++) {
  207.     if (picked) {
  208.       if (m1 == picked) {selected = i; picked = 0;}
  209.       else if (m1 > picked) {selected = i-1; picked = 0; Wp = Hp = 0;}
  210.     }
  211.     if (!m1->text) break;
  212.   }
  213.   numitems = i;}
  214.  
  215.   if (menubar) {
  216.     itemheight = 0;
  217.     title = 0;
  218.     return;
  219.   }
  220.  
  221.   itemheight = 1;
  222.  
  223.   int hotKeysw = 0;
  224.   int Wtitle = 0;
  225.   int Htitle = 0;
  226.   if (t) Wtitle = t->measure(&Htitle, button) + 12;
  227.   int W = 0;
  228.   if (m) for (; m->text; m = m->next()) {
  229.     int h; int w1 = m->measure(&h, button);
  230.     if (h+LEADING>itemheight) itemheight = h+LEADING;
  231.     if (m->flags&(FL_SUBMENU|FL_SUBMENU_POINTER)) w1 += 14;
  232.     if (w1 > W) W = w1;
  233.     if (m->shortcut_) {
  234.       w1 = int(fl_width(fl_shortcut_label(m->shortcut_))) + 8;
  235.       if (w1 > hotKeysw) hotKeysw = w1;
  236.     }
  237.     if (m->labelcolor_) clear_overlay();
  238.   }
  239.   if (selected >= 0 && !Wp) X -= W/2;
  240.   int BW = Fl::box_dx(box());
  241.   W += hotKeysw+2*BW+7;
  242.   if (Wp > W) W = Wp;
  243.   if (Wtitle > W) W = Wtitle;
  244.  
  245.   if (!Wp) {if (X < 0) X = 0; if (X > Fl::w()-W) X= Fl::w()-W;}
  246.   x(X); w(W);
  247.   h((numitems ? itemheight*numitems-LEADING : 0)+2*BW+5);
  248.   if (selected >= 0)
  249.     Y = Y+(Hp-itemheight)/2-selected*itemheight-BW+1;
  250.   else
  251.     Y = Y+Hp;
  252.   if (m) y(Y-1); else {y(Y-3); w(1); h(1);}
  253.  
  254.   if (t) {
  255.     int ht = menubar_title ? button->h()-6 : Htitle+2*BW+3;
  256.     title = new menutitle(X, Y-ht-3, Wtitle, ht, t);
  257.   } else
  258.     title = 0;
  259. }
  260.  
  261. menuwindow::~menuwindow() {
  262.   delete title;
  263. }
  264.  
  265. void menuwindow::position(int X, int Y) {
  266.   if (title) {title->position(X, title->y()+Y-y());}
  267.   Fl_Menu_Window::position(X, Y);
  268.   // x(X); y(Y); // don't wait for response from X
  269. }
  270.  
  271. // scroll so item i is visible on screen
  272. void menuwindow::autoscroll(int i) {
  273.   int Y = y()+Fl::box_dx(box())+2+i*itemheight;
  274.   if (Y <= Fl::y()) Y = Fl::y()-Y+10;
  275.   else {
  276.     Y = Y+itemheight-Fl::h()-Fl::y();
  277.     if (Y < 0) return;
  278.     Y = -Y-10;
  279.   }
  280.   Fl_Menu_Window::position(x(), y()+Y);
  281.   // y(y()+Y); // don't wait for response from X
  282. }
  283.  
  284. ////////////////////////////////////////////////////////////////
  285.  
  286. void menuwindow::drawentry(const Fl_Menu_Item* m, int i, int erase) {
  287.   if (!m) return; // this happens if -1 is selected item and redrawn
  288.  
  289.   int BW = Fl::box_dx(box());
  290.   int x = BW;
  291.   int W = this->w();
  292.   int w = W-2*BW-1;
  293.   int y = BW+2+i*itemheight;
  294.   int h = itemheight - LEADING;
  295.  
  296.   if (erase && i != selected) {
  297.     fl_color(button ? button->color() : FL_GRAY);
  298.     fl_rectf(x+1, y-1, w-2, h+2);
  299.   }
  300.  
  301.   m->draw(x, y, w, h, button, i==selected);
  302.  
  303.   // the shortcuts and arrows assumme fl_color() was left set by draw():
  304.   if (m->submenu()) {
  305.     int y1 = y+(h-14)/2;
  306.     fl_polygon(x+w-13, y1+2, x+w-13, y1+2+10, x+w-3, y1+2+5);
  307.   } else if (m->shortcut_) {
  308.     Fl_Font f = button ? button->textfont() : FL_HELVETICA;
  309.     fl_font(f, button ? button->textsize() : FL_NORMAL_SIZE);
  310.     fl_draw(fl_shortcut_label(m->shortcut_), x, y, w-3, h, FL_ALIGN_RIGHT);
  311.   }
  312.  
  313.   if (m->flags & FL_MENU_DIVIDER) {
  314.     fl_color(FL_DARK3);
  315.     fl_xyline(BW-1, y+h+1, W-2*BW+2);
  316.     fl_color(FL_LIGHT3);
  317.     fl_xyline(BW-1, y+h+2, W-2*BW+2);
  318.   }
  319.  
  320. }
  321.  
  322. void menutitle::draw() {
  323.   menu->draw(0, 0, w(), h(), button, 2);
  324. }
  325.  
  326. void menuwindow::draw() {
  327.  
  328.   if (damage() != FL_DAMAGE_CHILD) {    // complete redraw
  329.     fl_draw_box(box(), 0, 0, w(), h(), color());
  330.     if (menu) {
  331.       const Fl_Menu_Item* m; int i;
  332.       for (m=menu, i=0; m->text; i++, m = m->next()) drawentry(m, i, 0);
  333.     }
  334.   } else {
  335.     if (damage() & FL_DAMAGE_CHILD && selected!=drawn_selected) { // change selection
  336.       drawentry(menu->next(drawn_selected), drawn_selected, 1);
  337.       drawentry(menu->next(selected), selected, 1);
  338.     }
  339.   }        
  340.   drawn_selected = selected;
  341. }
  342.  
  343. void menuwindow::set_selected(int i) {
  344.   if (i != selected) {selected = i; damage(FL_DAMAGE_CHILD);}
  345. }
  346.  
  347. ////////////////////////////////////////////////////////////////
  348.  
  349. int menuwindow::find_selected(int mx, int my) {
  350.   if (!menu || !menu->text) return -1;
  351.   mx -= x();
  352.   my -= y();
  353.   if (my < 0 || my >= h()) return -1;
  354.   if (!itemheight) { // menubar
  355.     int x = 3; int i = 0;
  356.     const Fl_Menu_Item* m = menu;
  357.     for (; ; m = m->next(), i++) {
  358.       if (!m->text) return -1;
  359.       x += m->measure(0, button) + 16;
  360.       if (x > mx) break;
  361.     }
  362.     return i;
  363.   }
  364.   if (mx < Fl::box_dx(box()) || mx >= w()) return -1;
  365.   int i = (my-Fl::box_dx(box())-1)/itemheight;
  366.   if (i < 0 || i>=numitems) return -1;
  367.   return i;
  368. }
  369.  
  370. // return horizontal position for item i in a menubar:
  371. int menuwindow::titlex(int i) {
  372.   const Fl_Menu_Item* m;
  373.   int x = 3;
  374.   for (m=menu; i--; m = m->next()) x += m->measure(0, button) + 16;
  375.   return x;
  376. }
  377.  
  378. // match shortcuts & label shortcuts, don't search submenus:
  379. // returns menu item and index
  380. const Fl_Menu_Item* Fl_Menu_Item::find_shortcut(int* ip) const {
  381.   const Fl_Menu_Item* m1 = this;
  382.   for (int ii = 0; m1 && m1->text; m1 = m1->next(1), ii++) {
  383.     if (m1->activevisible() &&
  384.     (Fl::test_shortcut(m1->shortcut_)
  385.      || Fl_Widget::test_shortcut(m1->text))) {if (ip) *ip=ii; return m1;}
  386.   }
  387.   return 0;
  388. }
  389.  
  390. ////////////////////////////////////////////////////////////////
  391. // Fl_Menu_Item::popup(...)
  392.  
  393. // Because Fl::grab() is done, all events go to one of the menu windows.
  394. // But the handle method needs to look at all of them to find out
  395. // what item the user is pointing at.  And it needs a whole lot
  396. // of other state variables to determine what is going on with
  397. // the currently displayed menus.
  398. // So the main loop (handlemenu()) puts all the state in a structure
  399. // and puts a pointer to it in a static location, so the handle()
  400. // on menus can refer to it and alter it.  The handle() method
  401. // changes variables in this state to indicate what item is
  402. // picked, but does not actually alter the display, instead the
  403. // main loop does that.  This is because the X mapping and unmapping
  404. // of windows is slow, and we don't want to fall behind the events.
  405.  
  406. // values for menustate.state:
  407. #define INITIAL_STATE 0    // no mouse up or down since popup() called
  408. #define PUSH_STATE 1    // mouse has been pushed on a normal item
  409. #define DONE_STATE 2    // exit the popup, the current item was picked
  410. #define MENU_PUSH_STATE 3 // mouse has been pushed on a menu title
  411.  
  412. struct menustate {
  413.   const Fl_Menu_Item* current_item; // what mouse is pointing at
  414.   int menu_number; // which menu it is in
  415.   int item_number; // which item in that menu, -1 if none
  416.   menuwindow* p[20]; // pointers to menus
  417.   int nummenus;
  418.   int menubar; // if true p[0] is a menubar
  419.   int state;
  420. };
  421. static menustate* p;
  422.  
  423. static inline void setitem(const Fl_Menu_Item* i, int m, int n) {
  424.   p->current_item = i;
  425.   p->menu_number = m;
  426.   p->item_number = n;
  427. }
  428.  
  429. static void setitem(int m, int n) {
  430.   menustate &p = *(::p);
  431.   p.current_item = (n >= 0) ? p.p[m]->menu->next(n) : 0;
  432.   p.menu_number = m;
  433.   p.item_number = n;
  434. }
  435.  
  436. static int forward(int menu) { // go to next item in menu menu if possible
  437.   menustate &p = *(::p);
  438.   menuwindow &m = *(p.p[menu]);
  439.   int item = (menu == p.menu_number) ? p.item_number : m.selected;
  440.   while (++item < m.numitems) {
  441.     const Fl_Menu_Item* m1 = m.menu->next(item);
  442.     if (m1->activevisible()) {setitem(m1, menu, item); return 1;}
  443.   }
  444.   return 0;
  445. }
  446.  
  447. static int backward(int menu) { // previous item in menu menu if possible
  448.   menustate &p = *(::p);
  449.   menuwindow &m = *(p.p[menu]);
  450.   int item = (menu == p.menu_number) ? p.item_number : m.selected;
  451.   if (item < 0) item = m.numitems;
  452.   while (--item >= 0) {
  453.     const Fl_Menu_Item* m1 = m.menu->next(item);
  454.     if (m1->activevisible()) {setitem(m1, menu, item); return 1;}
  455.   }
  456.   return 0;
  457. }
  458.  
  459. int menuwindow::handle(int e) {
  460.   menustate &p = *(::p);
  461.   switch (e) {
  462.   case FL_KEYBOARD:
  463.     switch (Fl::event_key()) {
  464.     case FL_Tab:
  465.       if (Fl::event_shift()&FL_SHIFT) goto BACKTAB;
  466.     case ' ':
  467.       if (!forward(p.menu_number)) {p.item_number = -1; forward(p.menu_number);}
  468.       return 1;
  469.     case FL_BackSpace:
  470.     case 0xFE20: // backtab
  471.     BACKTAB:
  472.       if (!backward(p.menu_number)) {p.item_number = -1;backward(p.menu_number);}
  473.       return 1;
  474.     case FL_Up:
  475.       if (p.menubar && p.menu_number == 0) ;
  476.       else if (backward(p.menu_number));
  477.       else if (p.menubar && p.menu_number==1) setitem(0, p.p[0]->selected);
  478.       return 1;
  479.     case FL_Down:
  480.       if (p.menu_number || !p.menubar) forward(p.menu_number);
  481.       else if (p.menu_number < p.nummenus-1) forward(p.menu_number+1);
  482.       return 1;
  483.     case FL_Right:
  484.       if (p.menubar && (p.menu_number<=0 || p.menu_number==1 && p.nummenus==2))
  485.     forward(0);
  486.       else if (p.menu_number < p.nummenus-1) forward(p.menu_number+1);
  487.       return 1;
  488.     case FL_Left:
  489.       if (p.menubar && p.menu_number<=1) backward(0);
  490.       else if (p.menu_number>0)
  491.     setitem(p.menu_number-1, p.p[p.menu_number-1]->selected);
  492.       return 1;
  493.     case FL_Enter:
  494.       p.state = DONE_STATE;
  495.       return 1;
  496.     case FL_Escape:
  497.       setitem(0, -1, 0);
  498.       p.state = DONE_STATE;
  499.       return 1;
  500.     }
  501.     break;
  502.   case FL_SHORTCUT: {
  503.     for (int menu = p.nummenus; menu--;) {
  504.       menuwindow &mw = *(p.p[menu]);
  505.       int item; const Fl_Menu_Item* m = mw.menu->find_shortcut(&item);
  506.       if (m) {
  507.     setitem(m, menu, item);
  508.     if (!m->submenu()) p.state = DONE_STATE;
  509.     return 1;
  510.       }
  511.     }} break;
  512.   case FL_PUSH:
  513.   //case FL_MOVE:
  514.   case FL_DRAG: {
  515.     int mx = Fl::event_x_root();
  516.     int my = Fl::event_y_root();
  517.     int item=0; int menu;
  518.     for (menu = p.nummenus-1; ; menu--) {
  519.       item = p.p[menu]->find_selected(mx, my);
  520.       if (item >= 0) break;
  521.       if (menu <= 0) break;
  522.     }
  523.     setitem(menu, item);
  524.     if (e == FL_PUSH) {
  525.       if (p.current_item && p.current_item->submenu() // this is a menu title
  526.       && item != p.p[menu]->selected // and it is not already on
  527.       && !p.current_item->callback_) // and it does not have a callback
  528.     p.state = MENU_PUSH_STATE;
  529.       else
  530.     p.state = PUSH_STATE;
  531.     }} return 1;
  532.   case FL_RELEASE:
  533.     // do nothing if they try to pick inactive items
  534.     if (p.current_item && !p.current_item->activevisible()) return 1;
  535.     // Mouse must either be held down/dragged some, or this must be
  536.     // the second click (not the one that popped up the menu):
  537.     if (!Fl::event_is_click() || p.state == PUSH_STATE ||
  538.     p.menubar && p.current_item && !p.current_item->submenu() // button
  539.     ) {
  540. #if 0 // makes the check/radio items leave the menu up
  541.       const Fl_Menu_Item* m = p.current_item;
  542.       if (m && button && (m->flags & (FL_MENU_TOGGLE|FL_MENU_RADIO))) {
  543.     ((Fl_Menu_*)button)->picked(m);
  544.     p.p[p.menu_number]->redraw();
  545.       } else
  546. #endif
  547.     p.state = DONE_STATE;
  548.     }
  549.     return 1;
  550.   }
  551.   return Fl_Window::handle(e);
  552. }
  553.  
  554. const Fl_Menu_Item* Fl_Menu_Item::pulldown(
  555.     int X, int Y, int W, int H,
  556.     const Fl_Menu_Item* initial_item,
  557.     const Fl_Menu_* pbutton,
  558.     const Fl_Menu_Item* t,
  559.     int menubar) const
  560. {
  561.   Fl_Group::current(0); // fix possible user error...
  562.  
  563.   button = pbutton;
  564.   if (pbutton) {
  565.     for (Fl_Window* w = pbutton->window(); w; w = w->window()) {
  566.       X += w->x();
  567.       Y += w->y();
  568.     }
  569.   } else {
  570.     X += Fl::event_x_root()-Fl::event_x();
  571.     Y += Fl::event_y_root()-Fl::event_y();
  572.   }
  573.   menuwindow mw(this, X, Y, W, H, initial_item, t, menubar);
  574.   Fl::grab(mw);
  575.   menustate p; ::p = &p;
  576.   p.p[0] = &mw;
  577.   p.nummenus = 1;
  578.   p.menubar = menubar;
  579.   p.state = INITIAL_STATE;
  580.  
  581.   menuwindow* fakemenu = 0; // kludge for buttons in menubar
  582.  
  583.   // preselected item, pop up submenus if necessary:
  584.   if (initial_item && mw.selected >= 0) {
  585.     setitem(0, mw.selected);
  586.     goto STARTUP;
  587.   }
  588.  
  589.   p.current_item = 0; p.menu_number = 0; p.item_number = -1;
  590.   if (menubar) mw.handle(FL_DRAG); // find the initial menu
  591.   initial_item = p.current_item;
  592.   if (initial_item) goto STARTUP;
  593.  
  594.   // the main loop, runs until p.state goes to DONE_STATE:
  595.   for (;;) {
  596.  
  597.     // make sure all the menus are shown:
  598.     {for (int k = menubar; k < p.nummenus; k++)
  599.       if (!p.p[k]->shown()) {
  600.     if (p.p[k]->title) p.p[k]->title->show();
  601.     p.p[k]->show();
  602.       }
  603.     }
  604.  
  605.     // get events:
  606.     {const Fl_Menu_Item* oldi = p.current_item;
  607.     Fl::wait();
  608.     if (p.state == DONE_STATE) break; // done.
  609.     if (p.current_item == oldi) continue;}
  610.     // only do rest if item changes:
  611.  
  612.     delete fakemenu; fakemenu = 0; // turn off "menubar button"
  613.  
  614.     if (!p.current_item) { // pointing at nothing
  615.       // turn off selection in deepest menu, but don't erase other menus:
  616.       p.p[p.nummenus-1]->set_selected(-1);
  617.       continue;
  618.     }
  619.  
  620.     delete fakemenu; fakemenu = 0;
  621.     initial_item = 0; // stop the startup code
  622.     p.p[p.menu_number]->autoscroll(p.item_number);
  623.  
  624.   STARTUP:
  625.     menuwindow& cw = *p.p[p.menu_number];
  626.     const Fl_Menu_Item* m = p.current_item;
  627.     if (!m->activevisible()) { // pointing at inactive item
  628.       cw.set_selected(-1);
  629.       initial_item = 0; // turn off startup code
  630.       continue;
  631.     }
  632.     cw.set_selected(p.item_number);
  633.  
  634.     if (m==initial_item) initial_item=0; // stop the startup code if item found
  635.     if (m->submenu()) {
  636.       const Fl_Menu_Item* title = m;
  637.       const Fl_Menu_Item* menutable;
  638.       if (m->flags&FL_SUBMENU) menutable = m+1;
  639.       else menutable = (Fl_Menu_Item*)(m)->user_data_;
  640.       // figure out where new menu goes:
  641.       int nX, nY;
  642.       if (!p.menu_number && p.menubar) {    // menu off a menubar:
  643.     nX = cw.x() + cw.titlex(p.item_number);
  644.     nY = cw.y() + cw.h();
  645.     initial_item = 0;
  646.       } else {
  647.     nX = cw.x() + cw.w();
  648.     nY = cw.y() + 1 + p.item_number * cw.itemheight;
  649.     title = 0;
  650.       }
  651.       if (initial_item) { // bring up submenu containing initial item:
  652.     menuwindow* n = new menuwindow(menutable,X,Y,W,H,initial_item,title);
  653.     p.p[p.nummenus++] = n;
  654.     // move all earlier menus to line up with this new one:
  655.     if (n->selected>=0) {
  656.       int dy = n->y()-nY;
  657.       int dx = n->x()-nX;
  658.       for (int menu = 0; menu <= p.menu_number; menu++) {
  659.         menuwindow* t = p.p[menu];
  660.         int nx = t->x()+dx; if (nx < 0) {nx = 0; dx = -t->x();}
  661.         int ny = t->y()+dy+1; if (ny < 0) {ny = 0; dy = -t->y()-1;}
  662.         t->position(nx, ny);
  663.       }
  664.       setitem(p.nummenus-1, n->selected);
  665.       goto STARTUP;
  666.     }
  667.       } else if (p.nummenus > p.menu_number+1 &&
  668.          p.p[p.menu_number+1]->menu == menutable) {
  669.     // the menu is already up:
  670.     while (p.nummenus > p.menu_number+2) delete p.p[--p.nummenus];
  671.     p.p[p.nummenus-1]->set_selected(-1);
  672.       } else {
  673.     // delete all the old menus and create new one:
  674.     while (p.nummenus > p.menu_number+1) delete p.p[--p.nummenus];
  675.     p.p[p.nummenus++]= new menuwindow(menutable, nX, nY,
  676.                       title?1:0, 0, 0, title, 0, menubar);
  677.       }
  678.     } else { // !m->submenu():
  679.       while (p.nummenus > p.menu_number+1) delete p.p[--p.nummenus];
  680.       if (!p.menu_number && p.menubar) {
  681.     // kludge so "menubar buttons" turn "on" by using menu title:
  682.     fakemenu = new menuwindow(0,
  683.                   cw.x()+cw.titlex(p.item_number),
  684.                   cw.y()+cw.h(), 0, 0,
  685.                   0, m, 0, 1);
  686.     fakemenu->title->show();
  687.       }
  688.     }
  689.   }
  690.   const Fl_Menu_Item* m = p.current_item;
  691.   delete fakemenu;
  692.   while (p.nummenus>1) delete p.p[--p.nummenus];
  693.   mw.hide();
  694.   Fl::release();
  695.   return m;
  696. }
  697.  
  698. const Fl_Menu_Item*
  699. Fl_Menu_Item::popup(
  700.   int X, int Y,
  701.   const char* title,
  702.   const Fl_Menu_Item* picked,
  703.   const Fl_Menu_* button
  704.   ) const
  705. {
  706.   static Fl_Menu_Item dummy; // static so it is all zeros
  707.   dummy.text = title;
  708.   return pulldown(X, Y, 0, 0, picked, button, title ? &dummy : 0);
  709. }
  710.  
  711. const Fl_Menu_Item* Fl_Menu_Item::test_shortcut() const {
  712.   const Fl_Menu_Item* m = this;
  713.   const Fl_Menu_Item* ret = 0;
  714.   if (m) for (; m->text; m = m->next()) {
  715.     if (m->activevisible()) {
  716.       // return immediately any match of an item in top level menu:
  717.       if (Fl::test_shortcut(m->shortcut_)) return m;
  718.       // if (Fl_Widget::test_shortcut(m->text)) return m;
  719.       // only return matches from lower menu if nothing found in top menu:
  720.       if (!ret && m->submenu()) {
  721.     const Fl_Menu_Item* s =
  722.       (m->flags&FL_SUBMENU) ? m+1:(const Fl_Menu_Item*)m->user_data_;
  723.     ret = s->test_shortcut();
  724.       }
  725.     }
  726.   }
  727.   return ret;
  728. }
  729.  
  730. //
  731. // End of "$Id: Fl_Menu.cxx,v 1.18.2.2 1999/08/09 06:19:33 bill Exp $".
  732. //
  733.