#include <FL/Fl.H>
#include "Fl_Screen_Driver.H"
#include "Fl_Window_Driver.H"
#include <FL/Fl_Menu_Window.H>
#include <FL/Fl_Menu_.H>
#include <FL/fl_draw.H>
#include <stdio.h>
#include "flstring.h"
int Fl_Menu_Item::size() const {
const Fl_Menu_Item* m = this;
int nest = 0;
for (;;) {
if (!m->text) {
if (!nest) return (int) (m-this+1);
nest--;
} else if (m->flags & FL_SUBMENU) {
nest++;
}
m++;
}
}
static const Fl_Menu_Item* next_visible_or_not(const Fl_Menu_Item* m) {
int nest = 0;
do {
if (!m->text) {
if (!nest) return m;
nest--;
} else if (m->flags&FL_SUBMENU) {
nest++;
}
m++;
}
while (nest);
return m;
}
const Fl_Menu_Item* Fl_Menu_Item::next(int n) const {
if (n < 0) return 0; const Fl_Menu_Item* m = this;
if (!m->visible()) n++;
while (n) {
m = next_visible_or_not(m);
if (m->visible() || !m->text) n--;
}
return m;
}
static const Fl_Menu_* button=0;
class menuwindow;
class window_with_items : public Fl_Menu_Window {
protected:
window_with_items(int X, int Y, int W, int H, const Fl_Menu_Item *m) :
Fl_Menu_Window(X, Y, W, H, 0) {
menu = m;
set_menu_window();
Fl_Window_Driver::driver(this)->set_popup_window();
end();
set_modal();
clear_border();
}
public:
const Fl_Menu_Item* menu;
virtual menuwindow* as_menuwindow() { return NULL; }
};
class menutitle : public window_with_items {
void draw() FL_OVERRIDE;
public:
menutitle(int X, int Y, int W, int H, const Fl_Menu_Item*, bool menubar = false);
bool in_menubar;
};
class menuwindow : public window_with_items {
friend class Fl_Window_Driver;
friend struct Fl_Menu_Item;
void draw() FL_OVERRIDE;
void drawentry(const Fl_Menu_Item*, int i, int erase);
int handle_part1(int);
int handle_part2(int e, int ret);
static Fl_Window *parent_;
static int display_height_;
public:
menutitle* title;
int handle(int) FL_OVERRIDE;
int itemheight; int numitems;
int selected;
int drawn_selected; int shortcutWidth;
menuwindow(const Fl_Menu_Item* m, int X, int Y, int W, int H,
const Fl_Menu_Item* picked, const Fl_Menu_Item* title,
int menubar = 0, int menubar_title = 0, int right_edge = 0);
~menuwindow();
void set_selected(int);
int find_selected(int mx, int my);
int titlex(int);
void autoscroll(int);
void position(int x, int y);
int is_inside(int x, int y);
menuwindow* as_menuwindow() FL_OVERRIDE { return this; }
int menubartitle;
menuwindow *origin;
int offset_y;
};
Fl_Window *menuwindow::parent_ = NULL;
int menuwindow::display_height_ = 0;
Fl_Window *Fl_Window_Driver::menu_parent(int *display_height) {
if (display_height) *display_height = menuwindow::display_height_;
return menuwindow::parent_;
}
static menuwindow *to_menuwindow(Fl_Window *win) {
if (!Fl_Window_Driver::driver(win)->popup_window() || !win->menu_window()) return NULL;
return ((window_with_items*)win)->as_menuwindow();
}
Fl_Window *Fl_Window_Driver::menu_leftorigin(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? mwin->origin : NULL);
}
Fl_Window *Fl_Window_Driver::menu_title(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? mwin->title : NULL);
}
int Fl_Window_Driver::menu_itemheight(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? mwin->itemheight : 0);
}
int Fl_Window_Driver::menu_bartitle(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? mwin->menubartitle : 0);
}
int Fl_Window_Driver::menu_selected(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? mwin->selected : -1);
}
int *Fl_Window_Driver::menu_offset_y(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? &(mwin->offset_y) : NULL);
}
bool Fl_Window_Driver::is_floating_title(Fl_Window *win) {
if (!Fl_Window_Driver::driver(win)->popup_window() || !win->menu_window()) return false;
Fl_Window *mwin = ((window_with_items*)win)->as_menuwindow();
return !mwin && !((menutitle*)win)->in_menubar;
}
void Fl_Window_Driver::scroll_to_selected_item(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
if (mwin && mwin->selected > 0) {
mwin->autoscroll(mwin->selected);
}
}
extern char fl_draw_shortcut;
int Fl_Menu_Item::measure(int* hp, const Fl_Menu_* m) const {
Fl_Label l;
l.value = text;
l.image = 0;
l.deimage = 0;
l.type = labeltype_;
l.font = labelsize_ || labelfont_ ? labelfont_ : (m ? m->textfont() : FL_HELVETICA);
l.size = labelsize_ ? labelsize_ : m ? m->textsize() : FL_NORMAL_SIZE;
l.color = FL_FOREGROUND_COLOR; l.h_margin_ = l.v_margin_ = l.spacing = 0;
fl_draw_shortcut = 1;
int w = 0; int h = 0;
l.measure(w, hp ? *hp : h);
fl_draw_shortcut = 0;
if (flags & (FL_MENU_TOGGLE|FL_MENU_RADIO)) w += FL_NORMAL_SIZE + 4;
return w;
}
void Fl_Menu_Item::draw(int x, int y, int w, int h, const Fl_Menu_* m,
int selected) const {
Fl_Label l;
l.value = text;
l.image = 0;
l.deimage = 0;
l.type = labeltype_;
l.font = labelsize_ || labelfont_ ? labelfont_ : (m ? m->textfont() : FL_HELVETICA);
l.size = labelsize_ ? labelsize_ : m ? m->textsize() : FL_NORMAL_SIZE;
l.color = labelcolor_ ? labelcolor_ : m ? m->textcolor() : int(FL_FOREGROUND_COLOR);
l.h_margin_ = l.v_margin_ = l.spacing = 0;
if (!active()) l.color = fl_inactive((Fl_Color)l.color);
if (selected) {
Fl_Color r = m ? m->selection_color() : FL_SELECTION_COLOR;
Fl_Boxtype b = m && m->down_box() ? m->down_box() : FL_FLAT_BOX;
l.color = fl_contrast((Fl_Color)labelcolor_, r);
if (selected == 2) { fl_draw_box(b, x, y, w, h, r);
x += 3;
w -= 8;
} else {
fl_draw_box(b, x+1, y-(Fl::menu_linespacing()-2)/2, w-2, h+(Fl::menu_linespacing()-2), r);
}
}
if (flags & (FL_MENU_TOGGLE|FL_MENU_RADIO)) {
int d = (h - FL_NORMAL_SIZE + 1) / 2;
int W = h - 2 * d;
Fl_Color check_color = labelcolor_;
if (Fl::is_scheme("gtk+"))
check_color = FL_SELECTION_COLOR;
check_color = fl_contrast(check_color, FL_BACKGROUND2_COLOR);
if (flags & FL_MENU_RADIO) {
fl_draw_box(FL_ROUND_DOWN_BOX, x+2, y+d, W, W, FL_BACKGROUND2_COLOR);
if (value()) {
int tW = (W - Fl::box_dw(FL_ROUND_DOWN_BOX)) / 2 + 1;
if ((W - tW) & 1) tW++; int td = (W - tW) / 2;
fl_draw_radio(x + td + 1, y + d + td - 1, tW + 2, check_color);
}
} else {
fl_draw_box(FL_DOWN_BOX, x+2, y+d, W, W, FL_BACKGROUND2_COLOR);
if (value()) {
fl_draw_check(Fl_Rect(x+3, y+d+1, W-2, W-2), check_color);
}
}
x += W + 3;
w -= W + 3;
}
if (!fl_draw_shortcut) fl_draw_shortcut = 1;
l.draw(x+3, y, w>6 ? w-6 : 0, h, FL_ALIGN_LEFT);
fl_draw_shortcut = 0;
}
menutitle::menutitle(int X, int Y, int W, int H, const Fl_Menu_Item* L, bool inbar) :
window_with_items(X, Y, W, H, L) {
in_menubar = inbar;
}
menuwindow::menuwindow(const Fl_Menu_Item* m, int X, int Y, int Wp, int Hp,
const Fl_Menu_Item* picked, const Fl_Menu_Item* t,
int menubar, int menubar_title, int right_edge)
: window_with_items(X, Y, Wp, Hp, m)
{
int scr_x, scr_y, scr_w, scr_h;
int tx = X, ty = Y;
menubartitle = menubar_title;
origin = NULL;
offset_y = 0;
int n = (Wp > 0 ? Fl::screen_num(X, Y) : -1);
Fl_Window_Driver::driver(this)->menu_window_area(scr_x, scr_y, scr_w, scr_h, n);
if (!right_edge || right_edge > scr_x+scr_w) right_edge = scr_x+scr_w;
if (m) m = m->first(); drawn_selected = -1;
if (button) {
Fl_Boxtype b = button->menu_box();
if (b==FL_NO_BOX)
b = button->box();
if (b==FL_NO_BOX)
b = FL_FLAT_BOX;
box(b);
} else {
box(FL_UP_BOX);
}
color(button && !Fl::scheme() ? button->color() : FL_GRAY);
selected = -1;
{
int j = 0;
if (m) for (const Fl_Menu_Item* m1=m; ; m1 = m1->next(), j++) {
if (picked) {
if (m1 == picked) {selected = j; picked = 0;}
else if (m1 > picked) {selected = j-1; picked = 0; Wp = Hp = 0;}
}
if (!m1->text) break;
}
numitems = j;}
if (menubar) {
itemheight = 0;
title = 0;
return;
}
itemheight = 1;
int hotKeysw = 0;
int hotModsw = 0;
int Wtitle = 0;
int Htitle = 0;
if (t) Wtitle = t->measure(&Htitle, button) + 12;
int W = 0;
if (m) for (; m->text; m = m->next()) {
int hh;
int w1 = m->measure(&hh, button);
if (hh+Fl::menu_linespacing()>itemheight) itemheight = hh+Fl::menu_linespacing();
if (m->flags&(FL_SUBMENU|FL_SUBMENU_POINTER))
w1 += FL_NORMAL_SIZE;
if (w1 > W) W = w1;
if (m->shortcut_) {
const char *k, *s = fl_shortcut_label(m->shortcut_, &k);
if (fl_utf_nb_char((const unsigned char*)k, (int) strlen(k))<=4) {
w1 = int(fl_width(s, (int) (k-s)));
if (w1 > hotModsw) hotModsw = w1;
w1 = int(fl_width(k))+4;
if (w1 > hotKeysw) hotKeysw = w1;
} else {
w1 = int(fl_width(s))+4;
if (w1 > (hotModsw+hotKeysw)) {
hotModsw = w1-hotKeysw;
}
}
}
}
shortcutWidth = hotKeysw;
if (selected >= 0 && !Wp) X -= W/2;
int BW = Fl::box_dx(box());
W += hotKeysw+hotModsw+2*BW+7;
if (Wp > W) W = Wp;
if (Wtitle > W) W = Wtitle;
if (X < scr_x) X = scr_x;
if (X > scr_x+scr_w-W) X = scr_x+scr_w-W;
x(X); w(W);
h((numitems ? itemheight*numitems-4 : 0)+2*BW+3);
if (selected >= 0) {
Y = Y+(Hp-itemheight)/2-selected*itemheight-BW;
} else {
Y = Y+Hp;
if (Y+h()>scr_y+scr_h && Y-h()>=scr_y) {
if (Hp>1) {
Y = Y-Hp-h();
} else if (t) {
Y = Y-itemheight-h()-Fl::box_dh(box());
} else {
Y = Y-h()+itemheight+Fl::box_dy(box());
}
if (t) {
if (menubar_title) {
Y = Y + Fl::menu_linespacing() - Fl::box_dw(button->box());
} else {
Y += 2*Htitle+2*BW+3;
}
}
}
}
if (m) y(Y); else {y(Y-2); w(1); h(1);}
if (t) {
if (menubar_title) {
int dy = Fl::box_dy(button->box())+1;
int ht = button->h()-dy*2;
title = new menutitle(tx, ty-ht-dy, Wtitle, ht, t, true);
} else {
int dy = 2;
int ht = Htitle+2*BW+3;
title = new menutitle(X, Y-ht-dy, Wtitle, ht, t);
}
} else {
title = 0;
}
}
menuwindow::~menuwindow() {
hide();
delete title;
}
void menuwindow::position(int X, int Y) {
if (title) {title->position(X, title->y()+Y-y());}
Fl_Menu_Window::position(X, Y);
}
void menuwindow::autoscroll(int n) {
int scr_y, scr_h;
int Y = y()+Fl::box_dx(box())+2+n*itemheight;
int xx, ww;
Fl_Window_Driver::driver(this)->menu_window_area(xx, scr_y, ww, scr_h, this->screen_num());
if (n==0 && Y <= scr_y + itemheight) {
Y = scr_y - Y + 10;
} else if (Y <= scr_y + itemheight) {
Y = scr_y - Y + 10 + itemheight;
} else {
Y = Y+itemheight-scr_h-scr_y;
if (Y < 0) return;
Y = -Y-10;
}
Fl_Window_Driver::driver(this)->reposition_menu_window(x(), y()+Y);
}
void menuwindow::drawentry(const Fl_Menu_Item* m, int n, int eraseit) {
if (!m) return;
int BW = Fl::box_dx(box());
int xx = BW;
int W = w();
int ww = W-2*BW-1;
int yy = BW+1+n*itemheight+Fl::menu_linespacing()/2-2;
int hh = itemheight - Fl::menu_linespacing();
if (eraseit && n != selected) {
fl_push_clip(xx+1, yy-(Fl::menu_linespacing()-2)/2, ww-2, hh+(Fl::menu_linespacing()-2));
draw_box(box(), 0, 0, w(), h(), button ? button->color() : color());
fl_pop_clip();
}
m->draw(xx, yy, ww, hh, button, n==selected);
if (m->submenu()) {
int sz = ((hh-2) & (-2)) + 1 ; if (sz > 13) sz = 13; int x1 = xx + ww - sz - 2; int y1 = yy + (hh-sz)/2 + 1;
fl_draw_arrow(Fl_Rect(x1, y1, sz, sz), FL_ARROW_SINGLE, FL_ORIENT_RIGHT, fl_color());
} else if (m->shortcut_) {
Fl_Font f = m->labelsize_ || m->labelfont_ ? (Fl_Font)m->labelfont_ :
button ? button->textfont() : FL_HELVETICA;
fl_font(f, m->labelsize_ ? m->labelsize_ :
button ? button->textsize() : FL_NORMAL_SIZE);
const char *k, *s = fl_shortcut_label(m->shortcut_, &k);
if (fl_utf_nb_char((const unsigned char*)k, (int) strlen(k))<=4) {
char *buf = (char*)malloc(k-s+1);
memcpy(buf, s, k-s); buf[k-s] = 0;
fl_draw(buf, xx, yy, ww-shortcutWidth, hh, FL_ALIGN_RIGHT);
fl_draw( k, xx+ww-shortcutWidth, yy, shortcutWidth, hh, FL_ALIGN_LEFT);
free(buf);
} else {
fl_draw(s, xx, yy, ww-4, hh, FL_ALIGN_RIGHT);
}
}
if (m->flags & FL_MENU_DIVIDER) {
fl_color(FL_DARK3);
fl_xyline(BW-1, yy+hh+(Fl::menu_linespacing()-2)/2, W-2*BW+2);
fl_color(FL_LIGHT3);
fl_xyline(BW-1, yy+hh+((Fl::menu_linespacing()-2)/2+1), W-2*BW+2);
}
}
void menutitle::draw() {
menu->draw(0, 0, w(), h(), button, 2);
}
void menuwindow::draw() {
if (damage() != FL_DAMAGE_CHILD) { if ( box() != FL_FLAT_BOX && ( Fl::is_scheme( "gtk+" ) ||
Fl::is_scheme( "plastic") || Fl::is_scheme( "gleam" ) )) {
fl_draw_box( FL_FLAT_BOX, 0, 0, w(), h(),
button ? button->color() : color());
}
fl_draw_box(box(), 0, 0, w(), h(), button ? button->color() : color());
if (menu) {
const Fl_Menu_Item* m; int j;
for (m=menu->first(), j=0; m->text; j++, m = m->next()) drawentry(m, j, 0);
}
} else {
if (damage() & FL_DAMAGE_CHILD && selected!=drawn_selected) { drawentry(menu->next(drawn_selected), drawn_selected, 1);
drawentry(menu->next(selected), selected, 1);
}
}
drawn_selected = selected;
}
void menuwindow::set_selected(int n) {
if (n != selected) {selected = n; damage(FL_DAMAGE_CHILD);}
}
int menuwindow::find_selected(int mx, int my) {
if (!menu || !menu->text) return -1;
mx -= x();
my -= y();
if (my < 0 || my >= h()) return -1;
if (!itemheight) { int xx = 3; int n = 0;
const Fl_Menu_Item* m = menu->first();
for (; ; m = m->next(), n++) {
if (!m->text) return -1;
xx += m->measure(0, button) + 16;
if (xx > mx) break;
}
return n;
}
if (mx < Fl::box_dx(box()) || mx >= w()) return -1;
int n = (my-Fl::box_dx(box())-1)/itemheight;
if (n < 0 || n>=numitems) return -1;
return n;
}
int menuwindow::titlex(int n) {
const Fl_Menu_Item* m;
int xx = 3;
for (m=menu->first(); n--; m = m->next()) xx += m->measure(0, button) + 16;
return xx;
}
int menuwindow::is_inside(int mx, int my) {
if ( mx < x_root() || mx >= x_root() + w() ||
my < y_root() || my >= y_root() + h()) {
return 0;
}
if (itemheight == 0 && find_selected(mx, my) == -1) {
return 0;
}
return 1;
}
#define INITIAL_STATE 0
#define PUSH_STATE 1
#define DONE_STATE 2
#define MENU_PUSH_STATE 3
struct menustate {
const Fl_Menu_Item* current_item; int menu_number; int item_number; menuwindow* p[20]; int nummenus;
int menubar; int state;
menuwindow* fakemenu; int is_inside(int mx, int my);
};
static menustate* p=0;
int menustate::is_inside(int mx, int my) {
int i;
for (i=nummenus-1; i>=0; i--) {
if (p[i]->is_inside(mx, my))
return 1;
}
return 0;
}
static inline void setitem(const Fl_Menu_Item* i, int m, int n) {
p->current_item = i;
p->menu_number = m;
p->item_number = n;
}
static void setitem(int m, int n) {
menustate &pp = *p;
pp.current_item = (n >= 0) ? pp.p[m]->menu->next(n) : 0;
pp.menu_number = m;
pp.item_number = n;
}
static int forward(int menu) { if (menu < 0)
menu = 0;
menustate &pp = *p;
menuwindow &m = *(pp.p[menu]);
int item = (menu == pp.menu_number) ? pp.item_number : m.selected;
bool wrapped = false;
do {
while (++item < m.numitems) {
const Fl_Menu_Item* m1 = m.menu->next(item);
if (m1->activevisible()) {setitem(m1, menu, item); return 1;}
}
if (wrapped) break;
item = -1;
wrapped = true;
}
while (Fl::event_key() != FL_Down);
return 0;
}
static int backward(int menu) { if (menu < 0)
menu = 0;
menustate &pp = *p;
menuwindow &m = *(pp.p[menu]);
int item = (menu == pp.menu_number) ? pp.item_number : m.selected;
bool wrapped = false;
do {
while (--item >= 0) {
const Fl_Menu_Item* m1 = m.menu->next(item);
if (m1->activevisible()) {setitem(m1, menu, item); return 1;}
}
if (wrapped) break;
item = m.numitems;
wrapped = true;
}
while (Fl::event_key() != FL_Up);
return 0;
}
int menuwindow::handle(int e) {
static int use_part2 = Fl::screen_driver()->need_menu_handle_part2();
int ret = handle_part1(e);
if (use_part2) ret = handle_part2(e, ret);
return ret;
}
int menuwindow::handle_part2(int e, int ret) {
menustate &pp = *p;
if (pp.state == DONE_STATE) {
hide();
if (pp.fakemenu) {
pp.fakemenu->hide();
if (pp.fakemenu->title)
pp.fakemenu->title->hide();
}
int i = pp.nummenus;
while (i>0) {
menuwindow *mw = pp.p[--i];
if (mw) {
mw->hide();
if (mw->title)
mw->title->hide();
}
}
}
return ret;
}
int menuwindow::handle_part1(int e) {
menustate &pp = *p;
switch (e) {
case FL_KEYBOARD:
switch (Fl::event_key()) {
case FL_BackSpace:
BACKTAB:
backward(pp.menu_number);
return 1;
case FL_Up:
if (pp.menubar && pp.menu_number == 0) {
} else if (backward(pp.menu_number)) {
} else if (pp.menubar && pp.menu_number==1) {
setitem(0, pp.p[0]->selected);
}
return 1;
case FL_Tab:
if (Fl::event_shift()) goto BACKTAB;
if (pp.menubar && pp.menu_number == 0) goto RIGHT;
case FL_Down:
if (pp.menu_number || !pp.menubar) {
forward(pp.menu_number);
} else if (pp.menu_number < pp.nummenus-1) {
forward(pp.menu_number+1);
}
return 1;
case FL_Right:
RIGHT:
if (pp.menubar && (pp.menu_number<=0 || (pp.menu_number == pp.nummenus-1)))
forward(0);
else if (pp.menu_number < pp.nummenus-1) forward(pp.menu_number+1);
return 1;
case FL_Left:
if (pp.menubar && pp.menu_number<=1) backward(0);
else if (pp.menu_number>0)
setitem(pp.menu_number-1, pp.p[pp.menu_number-1]->selected);
return 1;
case FL_Enter:
case FL_KP_Enter:
case ' ':
if ( pp.current_item
&& (!pp.menubar || pp.menu_number > 0)
&& pp.current_item->activevisible()
&& pp.current_item->submenu()
&& !pp.current_item->callback_)
{
goto RIGHT;
}
if (pp.current_item && !pp.current_item->activevisible())
return 1;
pp.state = DONE_STATE;
return 1;
case FL_Escape:
setitem(0, -1, 0);
pp.state = DONE_STATE;
return 1;
}
break;
case FL_SHORTCUT:
{
for (int mymenu = pp.nummenus; mymenu--;) {
menuwindow &mw = *(pp.p[mymenu]);
int item; const Fl_Menu_Item* m = mw.menu->find_shortcut(&item);
if (m) {
setitem(m, mymenu, item);
if (!m->submenu()) pp.state = DONE_STATE;
return 1;
}
}
}
break;
case FL_MOVE: {
static int use_part1_extra = Fl::screen_driver()->need_menu_handle_part1_extra();
if (use_part1_extra && pp.state == DONE_STATE) {
return 1; }
}
case FL_ENTER:
case FL_PUSH:
case FL_DRAG:
{
int mx = Fl::event_x_root();
int my = Fl::event_y_root();
int item=0; int mymenu = pp.nummenus-1;
if ((!pp.menubar || mymenu) && !pp.is_inside(mx, my)) {
setitem(0, -1, 0);
if (e==FL_PUSH)
pp.state = DONE_STATE;
return 1;
}
for (mymenu = pp.nummenus-1; ; mymenu--) {
item = pp.p[mymenu]->find_selected(mx, my);
if (item >= 0)
break;
if (mymenu <= 0) {
if (pp.menu_number==-1 && e==FL_PUSH) {
pp.state = DONE_STATE;
return 1;
}
if (pp.current_item && pp.menu_number==0 && !pp.current_item->submenu()) {
if (e==FL_PUSH) {
pp.state = DONE_STATE;
setitem(0, -1, 0);
}
return 1;
}
return 0;
}
}
setitem(mymenu, item);
if (e == FL_PUSH) {
if (pp.current_item && pp.current_item->submenu() && item != pp.p[mymenu]->selected && !pp.current_item->callback_) pp.state = MENU_PUSH_STATE;
else
pp.state = PUSH_STATE;
}
}
return 1;
case FL_RELEASE:
if ( !Fl::event_is_click()
|| pp.state == PUSH_STATE
|| (pp.menubar && pp.current_item && !pp.current_item->submenu()) ) {
#if 0#endif
if (!pp.current_item || (pp.current_item->activevisible() &&
(!pp.current_item->submenu() || pp.current_item->callback_ || (pp.menubar && pp.menu_number <= 0))))
pp.state = DONE_STATE;
}
return 1;
}
return Fl_Window::handle(e);
}
const Fl_Menu_Item* Fl_Menu_Item::pulldown(
int X, int Y, int W, int H,
const Fl_Menu_Item* initial_item,
const Fl_Menu_* pbutton,
const Fl_Menu_Item* title,
int menubar) const {
Fl_Group::current(0);
Fl_Widget_Tracker wp((Fl_Widget *)pbutton);
button = pbutton;
if (pbutton && pbutton->window()) {
menuwindow::parent_ = pbutton->top_window();
for (Fl_Window* w = pbutton->window(); w; w = w->window()) {
X += w->x();
Y += w->y();
}
} else {
X += Fl::event_x_root()-Fl::event_x();
Y += Fl::event_y_root()-Fl::event_y();
menuwindow::parent_ = Fl::first_window();
}
int XX, YY, WW;
Fl::screen_xywh(XX, YY, WW, menuwindow::display_height_, menuwindow::parent_->screen_num());
menuwindow mw(this, X, Y, W, H, initial_item, title, menubar);
Fl::grab(mw);
if (menuwindow::parent_)
menuwindow::parent_->cursor(FL_CURSOR_DEFAULT);
menustate pp; p = &pp;
pp.p[0] = &mw;
pp.nummenus = 1;
pp.menubar = menubar;
pp.state = INITIAL_STATE;
pp.fakemenu = 0;
if (initial_item && mw.selected >= 0) {
setitem(0, mw.selected);
goto STARTUP;
}
pp.current_item = 0; pp.menu_number = 0; pp.item_number = -1;
if (menubar) {
if (!mw.handle(FL_DRAG)) {
Fl::grab(0);
return 0;
}
}
initial_item = pp.current_item;
if (initial_item) {
if (menubar && !initial_item->activevisible()) { Fl::grab(0);
return NULL;
}
goto STARTUP;
}
for (;;) {
{
for (int k = menubar; k < pp.nummenus; k++) {
if (!pp.p[k]->shown()) {
if (pp.p[k]->title) pp.p[k]->title->show();
pp.p[k]->show();
}
}
}
{
const Fl_Menu_Item* oldi = pp.current_item;
Fl::wait();
if (pbutton && wp.deleted()) break;
if (pp.state == DONE_STATE) break; if (pp.current_item == oldi) continue;
}
if(pp.fakemenu) {delete pp.fakemenu; pp.fakemenu = 0;}
if (!pp.current_item) { pp.p[pp.nummenus-1]->set_selected(-1);
continue;
}
if(pp.fakemenu) {delete pp.fakemenu; pp.fakemenu = 0;}
initial_item = 0; pp.p[pp.menu_number]->autoscroll(pp.item_number);
STARTUP:
menuwindow& cw = *pp.p[pp.menu_number];
const Fl_Menu_Item* m = pp.current_item;
if (!m->activevisible()) { cw.set_selected(-1);
initial_item = 0; continue;
}
cw.set_selected(pp.item_number);
if (m==initial_item) initial_item=0; if (m->submenu()) {
const Fl_Menu_Item* title = m;
const Fl_Menu_Item* menutable;
if (m->flags&FL_SUBMENU) menutable = m+1;
else menutable = (Fl_Menu_Item*)(m)->user_data_;
int nX, nY;
if (!pp.menu_number && pp.menubar) { nX = cw.x() + cw.titlex(pp.item_number);
nY = cw.y() + cw.h();
initial_item = 0;
} else {
nX = cw.x() + cw.w();
nY = cw.y() + pp.item_number * cw.itemheight;
title = 0;
}
if (initial_item) { menuwindow* n = new menuwindow(menutable,X,Y,W,H,initial_item,title,0,0,cw.x());
pp.p[pp.nummenus++] = n;
if (pp.nummenus >= 2) pp.p[pp.nummenus-1]->origin = pp.p[pp.nummenus-2];
if (n->selected>=0) {
int dy = n->y()-nY;
int dx = n->x()-nX;
int waX, waY, waW, waH;
Fl_Window_Driver::driver(n)->menu_window_area(waX, waY, waW, waH, Fl::screen_num(X, Y));
for (int menu = 0; menu <= pp.menu_number; menu++) {
menuwindow* tt = pp.p[menu];
int nx = tt->x()+dx; if (nx < waX) {nx = waX; dx = -tt->x() + waX;}
int ny = tt->y()+dy; if (ny < waY) {ny = waY; dy = -tt->y() + waY;}
tt->position(nx, ny);
}
setitem(pp.nummenus-1, n->selected);
goto STARTUP;
}
} else if (pp.nummenus > pp.menu_number+1 &&
pp.p[pp.menu_number+1]->menu == menutable) {
while (pp.nummenus > pp.menu_number+2) delete pp.p[--pp.nummenus];
pp.p[pp.nummenus-1]->set_selected(-1);
} else {
while (pp.nummenus > pp.menu_number+1) delete pp.p[--pp.nummenus];
pp.p[pp.nummenus++]= new menuwindow(menutable, nX, nY,
title?1:0, 0, 0, title, 0, menubar,
(title ? 0 : cw.x()) );
if (pp.nummenus >= 2 && pp.p[pp.nummenus-2]->itemheight) {
pp.p[pp.nummenus-1]->origin = pp.p[pp.nummenus-2];
}
}
} else { while (pp.nummenus > pp.menu_number+1) delete pp.p[--pp.nummenus];
if (!pp.menu_number && pp.menubar) {
pp.fakemenu = new menuwindow(0,
cw.x()+cw.titlex(pp.item_number),
cw.y()+cw.h(), 0, 0,
0, m, 0, 1);
pp.fakemenu->title->show();
}
}
}
const Fl_Menu_Item* m = (pbutton && wp.deleted()) ? NULL : pp.current_item;
delete pp.fakemenu;
while (pp.nummenus>1) delete pp.p[--pp.nummenus];
mw.hide();
Fl::grab(0);
menuwindow::parent_ = NULL;
return m;
}
const Fl_Menu_Item* Fl_Menu_Item::popup(
int X, int Y,
const char* title,
const Fl_Menu_Item* picked,
const Fl_Menu_* menu_button
) const {
static Fl_Menu_Item dummy; dummy.text = title;
return pulldown(X, Y, 0, 0, picked, menu_button, title ? &dummy : 0);
}
static bool is_special_labeltype(uchar t) {
return t == _FL_MULTI_LABEL || t == _FL_ICON_LABEL || t == _FL_IMAGE_LABEL;
}
const Fl_Menu_Item* Fl_Menu_Item::find_shortcut(int* ip, const bool require_alt) const {
const Fl_Menu_Item* m = this;
if (m) for (int ii = 0; m->text; m = next_visible_or_not(m), ii++) {
if (m->active()) {
if (Fl::test_shortcut(m->shortcut_)
|| (!is_special_labeltype(m->labeltype_) && Fl_Widget::test_shortcut(m->text, require_alt))
|| (m->labeltype_ == _FL_MULTI_LABEL
&& !is_special_labeltype(((Fl_Multi_Label*)m->text)->typea)
&& Fl_Widget::test_shortcut(((Fl_Multi_Label*)m->text)->labela, require_alt))
|| (m->labeltype_ == _FL_MULTI_LABEL
&& !is_special_labeltype(((Fl_Multi_Label*)m->text)->typeb)
&& Fl_Widget::test_shortcut(((Fl_Multi_Label*)m->text)->labelb, require_alt))) {
if (ip) *ip=ii;
return m;
}
}
}
return 0;
}
const Fl_Menu_Item* Fl_Menu_Item::test_shortcut() const {
const Fl_Menu_Item* m = this;
const Fl_Menu_Item* ret = 0;
if (m) for (; m->text; m = next_visible_or_not(m)) {
if (m->active()) {
if (Fl::test_shortcut(m->shortcut_)) return m;
if (!ret && m->submenu()) {
const Fl_Menu_Item* s =
(m->flags&FL_SUBMENU) ? m+1:(const Fl_Menu_Item*)m->user_data_;
ret = s->test_shortcut();
}
}
}
return ret;
}