#include "CtrlLib.h" #ifdef PLATFORM_WIN32 #include #endif NAMESPACE_UPP #define LLOG(x) // LOG(x) #define LTIMING(x) // RTIMING(x) class MenuItemBase : public Ctrl, public Bar::Item { public: virtual Bar::Item& Text(const char *text); virtual Bar::Item& Key(dword key); virtual Bar::Item& Image(const UPP::Image& img); virtual Bar::Item& Enable(bool _enable); virtual Bar::Item& Tip(const char *tip); virtual Bar::Item& Help(const char *help); virtual Bar::Item& Topic(const char *help); virtual Bar::Item& Description(const char *desc); virtual Bar::Item& Check(bool check); virtual Bar::Item& Radio(bool check); virtual String GetDesc() const; virtual dword GetAccessKeys() const; virtual void AssignAccessKeys(dword used); protected: enum { NOTHING, CHECK0, CHECK1, RADIO0, RADIO1 }; enum { NORMAL, HIGHLIGHT, PUSH }; String text; dword accel; int state; int leftgap; Font font; bool isenabled; byte type; byte accesskey; const MenuBar::Style *style; public: virtual void SyncState() = 0; void DrawMenuText(Draw& w, int x, int y, const String& s, Font f, bool enabled, bool hl, Color color = SColorMenuText); void PaintTopItem(Draw& w, int state); bool IsItemEnabled() const { return isenabled; } String GetText() const { return text; } MenuItemBase& LeftGap(int cx) { leftgap = cx; return *this; } MenuItemBase& SetFont(Font f) { font = f; return *this; } MenuItemBase& Style(const MenuBar::Style *s) { style = s; return *this; } Font GetFont() const { return font; } MenuItemBase(); }; void DrawMenuText(Draw& w, int x, int y, const String& s, Font f, bool enabled, bool highlight, int mn, Color color = SColorMenuText); class MenuItem : public MenuItemBase { public: virtual void Paint(Draw& w); virtual void MouseEnter(Point, dword); virtual void MouseLeave(); virtual Size GetMinSize() const; virtual void LeftUp(Point, dword); virtual void RightUp(Point, dword); virtual void GotFocus(); virtual void LostFocus(); virtual bool Key(dword key, int count); virtual bool HotKey(dword key); virtual void SyncState(); virtual Bar::Item& Image(const UPP::Image& img); private: UPP::Image licon, ricon; void SendHelpLine(); void ClearHelpLine(); protected: virtual int GetVisualState(); public: MenuItem& RightImage(const UPP::Image& img); }; class SubMenuBase { protected: MenuBar menu; Callback1 proc; MenuBar *parentmenu; void Pull(Ctrl *item, Point p, Size sz); public: virtual void Pull() = 0; void SetParent(MenuBar *menu) { parentmenu = menu; } void Set(Callback1 _submenu) { proc = _submenu; } Callback1 Get() { return proc; } SubMenuBase() { parentmenu = NULL; } }; class SubMenuItem : public MenuItem, public SubMenuBase { public: virtual void MouseEnter(Point, dword); virtual void MouseLeave(); virtual void GotFocus(); virtual bool HotKey(dword key); virtual bool Key(dword key, int count); virtual int GetVisualState(); virtual void Pull(); protected: enum { TIMEID_PULL = BarCtrl::TIMEID_COUNT, TIMEID_COUNT }; public: typedef SubMenuItem CLASSNAME; SubMenuItem(); }; class TopSubMenuItem : public MenuItemBase, public SubMenuBase { public: virtual void Paint(Draw& w); virtual void MouseEnter(Point, dword); virtual void MouseLeave(); virtual void LeftDown(Point, dword); virtual void SyncState(); virtual Size GetMinSize() const; virtual bool Key(dword key, int); virtual bool HotKey(dword key); virtual void Pull(); private: int GetState(); public: TopSubMenuItem() { NoInitFocus(); } }; class TopMenuItem : public MenuItemBase { public: virtual void Paint(Draw& w); virtual void MouseEnter(Point, dword); virtual void MouseLeave(); virtual void LeftUp(Point, dword); virtual void LeftDown(Point, dword); virtual void GotFocus(); virtual void LostFocus(); virtual bool Key(dword key, int count); virtual Size GetMinSize() const; virtual void SyncState(); static int GetStdHeight(Font font = StdFont()); private: int GetState(); public: TopMenuItem() { NoInitFocus(); } }; // ------------------------------------- MenuItemBase::MenuItemBase() { accel = 0; state = 0; isenabled = true; type = 0; font = StdFont(); leftgap = 16; accesskey = 0; NoWantFocus(); style = &MenuBar::StyleDefault(); } Bar::Item& MenuItemBase::Text(const char *s) { accesskey = ExtractAccessKey(s, text); Refresh(); return *this; } Bar::Item& MenuItemBase::Key(dword key) { if(key) { accel = key; Refresh(); } return *this; } Bar::Item& MenuItemBase::Image(const UPP::Image& img) { return *this; } Bar::Item& MenuItemBase::Check(bool check) { type = CHECK0 + check; return *this; } Bar::Item& MenuItemBase::Radio(bool check) { type = RADIO0 + check; return *this; } Bar::Item& MenuItemBase::Tip(const char *s) { return *this; } Bar::Item& MenuItemBase::Help(const char *s) { HelpLine(s); return *this; } Bar::Item& MenuItemBase::Description(const char *s) { Ctrl::Description(s); return *this; } Bar::Item& MenuItemBase::Topic(const char *s) { HelpTopic(s); return *this; } Bar::Item& MenuItemBase::Enable(bool en) { isenabled = en; Refresh(); return *this; } String MenuItemBase::GetDesc() const { return text; } dword MenuItemBase::GetAccessKeys() const { return AccessKeyBit(accesskey); } void MenuItemBase::AssignAccessKeys(dword used) { if(!accesskey) { accesskey = ChooseAccessKey(text, used); used |= AccessKeyBit(accesskey); } Ctrl::AssignAccessKeys(used); } void DrawMnemonicText(Draw& w, int x, int y, const String& s, Font font, Color color, int mnemonic) { int q = s.Find(ToUpper(mnemonic)); int e = s.Find(ToLower(mnemonic)); if(q < 0 || e >= 0 && e < q) q = e; w.DrawText(x, y, s, font, color); if(q < 0) return; FontInfo f = font.Info(); w.DrawRect(x + GetTextSize(~s, font, q).cx, y + f.GetAscent() + 1, f[s[q]], 1, SColorMark()); } void DrawMenuText(Draw& w, int x, int y, const String& s, Font f, bool enabled, bool hl, int mnemonic, Color color) { if(enabled) DrawMnemonicText(w, x, y, s, f, hl ? SColorHighlightText : color, mnemonic); else { if(GUI_GlobalStyle() >= GUISTYLE_XP) DrawMnemonicText(w, x, y, s, f, SColorDisabled, 0); else { DrawMnemonicText(w, x + 1, y + 1, s, f, SColorPaper, 0); DrawMnemonicText(w, x, y, s, f, SColorDisabled, 0); } } } void MenuItemBase::DrawMenuText(Draw& w, int x, int y, const String& s, Font f, bool enabled, bool hl, Color color) { UPP::DrawMenuText(w, x, y, s, f, enabled, hl, VisibleAccessKeys() ? accesskey : 0, color); } CH_STYLE(MenuBar, Style, StyleDefault) { topitem = item = SColorHighlight(); } void MenuItemBase::PaintTopItem(Draw& w, int state) { Size sz = GetSize(); if(GUI_GlobalStyle() >= GUISTYLE_XP) { if(state) ChPaint(w, 0, 0, sz.cx, sz.cy, style->topitem); else w.DrawRect(0, 0, sz.cx, sz.cy, SColorFace); String text = GetText(); Size isz = GetTextSize(text, StdFont()); DrawMenuText(w, 6, (sz.cy - isz.cy) / 2, text, GetFont(), IsItemEnabled(), state); } else { w.DrawRect(sz, SColorFace); static const ColorF b0[] = { (ColorF)1, SColorLight, SColorLight, SColorShadow, SColorShadow, }; static const ColorF b1[] = { (ColorF)1, SColorShadow, SColorShadow, SColorLight, SColorLight, }; String text = GetText(); Size isz = GetTextSize(text, StdFont()); DrawMenuText(w, 6, (sz.cy - isz.cy) / 2, text, GetFont(), IsItemEnabled(), false); if(state) DrawBorder(w, 0, 0, sz.cx, sz.cy, state == 2 ? b1 : b0); } } // ------------------------------------- Bar::Item& MenuItem::Image(const UPP::Image& img) { licon = img; Refresh(); return *this; } MenuItem& MenuItem::RightImage(const UPP::Image& img) { ricon = img; Refresh(); return *this; } void MenuItem::SendHelpLine() { BarCtrl::SendHelpLine(this); } void MenuItem::ClearHelpLine() { BarCtrl::ClearHelpLine(this); } void MenuItem::MouseEnter(Point, dword) { SetFocus(); Refresh(); SendHelpLine(); } void MenuItem::MouseLeave() { ClearHelpLine(); } void MenuItem::GotFocus() { Refresh(); SendHelpLine(); } void MenuItem::LostFocus() { Refresh(); ClearHelpLine(); } int MenuItem::GetVisualState() { return HasFocus() ? (HasMouse() && GetMouseLeft() || GetMouseRight()) ? PUSH : HIGHLIGHT : NORMAL; } void MenuItem::SyncState() { int s = GetVisualState(); if(s != state) { state = s; Refresh(); } } void MenuItem::Paint(Draw& w) { int q = text.Find('\t'); String txt, keydesc; if(accel) keydesc = GetKeyDesc(accel); if(q >= 0) { keydesc = text.Mid(q + 1); txt = text.Mid(0, q); } else txt = text; state = GetVisualState(); bool hl = state != NORMAL; Size sz = GetSize(); if(GUI_GlobalStyle() >= GUISTYLE_XP) if(hl) ChPaint(w, 0, 0, sz.cx, sz.cy, style->item); else w.DrawRect(0, 0, sz.cx, sz.cy, SColorMenu); else w.DrawRect(sz, hl ? SColorHighlight : SColorFace); UPP::Image li = licon; if(li.IsEmpty()) { switch(type) { case CHECK0: li = CtrlImg::MenuCheck0(); break; case CHECK1: li = CtrlImg::MenuCheck1(); break; case RADIO0: li = CtrlImg::MenuRadio0(); break; case RADIO1: li = CtrlImg::MenuRadio1(); break; } } Size isz = li.GetSize(); int iy = (sz.cy - isz.cy) / 2; bool chk = false; if(!licon.IsEmpty() && type) { chk = type == CHECK1 || type == RADIO1; if(GUI_GlobalStyle() >= GUISTYLE_XP) { if(chk && !hl) { DrawXPButton(w, RectC(0, iy - 2, isz.cx + 4, isz.cy + 4), BUTTON_EDGE|BUTTON_CHECKED); } } else { w.DrawRect(1, iy - 1, isz.cx + 2, isz.cy + 2, chk ? Blend(SColorFace, SColorLight) : SColorFace); DrawBorder(w, 0, iy - 2, isz.cx + 4, isz.cy + 4, chk ? ThinInsetBorder : ThinOutsetBorder); } } if(isenabled) DrawHighlightImage(w, 2, iy, li, hl || chk, true); else w.DrawImage(2, iy, DisabledImage(li)); int x = max(isz.cx, leftgap) + 6; isz = GetTextSize(text, StdFont()); DrawMenuText(w, x, (sz.cy - isz.cy) / 2, txt, font, isenabled, hl, SColorMenuText); isz = ricon.GetSize(); if(isenabled) DrawHighlightImage(w, sz.cx - isz.cx, (sz.cy - isz.cy) / 2, ricon, hl, true); else w.DrawImage(sz.cx - isz.cx, (sz.cy - isz.cy) / 2, DisabledImage(ricon)); x = sz.cx - max(isz.cx, 16) - 1; if(!IsEmpty(keydesc)) { isz = GetTextSize(keydesc, StdFont()); UPP::DrawMenuText(w, x - isz.cx - 2, (sz.cy - isz.cy) / 2, keydesc, font, isenabled, hl, 0, SColorMark()); } } Size MenuItem::GetMinSize() const { Size sz1 = GetTextSize(text, font); Size sz2(0, 0); if(accel) { sz2 = GetTextSize(GetKeyDesc(accel), font); sz2.cx += 12; } Size lsz = licon.GetSize(); Size rsz = ricon.GetSize(); return AddFrameSize(Size(max(lsz.cx, 16) + sz1.cx + max(sz2.cx, (rsz.cx ? 16 : 0)) + max(rsz.cx, 16) + 16, max(max(lsz.cy, rsz.cy) + 4, sz1.cy + 6))); } void MenuItem::LeftUp(Point, dword) { if(!isenabled) return; #ifdef PLATFORM_WIN32 #ifdef PLATFORM_WINCE PlaySound(L"MenuCommand", NULL, SND_ASYNC|SND_NODEFAULT); //TODO? #else PlaySound("MenuCommand", NULL, SND_ASYNC|SND_NODEFAULT); #endif #endif LLOG("Menu Item pre Action"); WhenAction(); LLOG("Menu Item post Action"); } void MenuItem::RightUp(Point p, dword w) { LeftUp(p, w); } bool MenuItem::HotKey(dword key) { if(isenabled && (key == accel || CompareAccessKey(accesskey, key) || key < 256 && IsAlNum((char)key) && CompareAccessKey(accesskey, ToUpper((char)key) + K_DELTA + K_ALT))) { LLOG("MenuItem::HotKey(" << key << ") -> SetFocus"); SetFocus(); Sync(); Sleep(50); WhenAction(); return true; } return false; } bool MenuItem::Key(dword key, int count) { if(key == K_ENTER && isenabled) { WhenAction(); return true; } return false; } // ---------------------------------------------------- void SubMenuBase::Pull(Ctrl *item, Point p, Size sz) { if(!item->IsOpen() || menu.IsOpen()) return; menu.Clear(); if(parentmenu) menu.SetStyle(*parentmenu->style); proc(menu); if(parentmenu) { parentmenu->SetActiveSubmenu(&menu, item); menu.SetParentMenu(parentmenu); } menu.PopUp(parentmenu, p, sz); if(parentmenu) parentmenu->doeffect = false; } // ---------------------------------------------------- SubMenuItem::SubMenuItem() { RightImage(CtrlImg::right_arrow()); } void SubMenuItem::GotFocus() { Refresh(); } void SubMenuItem::Pull() { Rect r = GetScreenRect(); Point p = r.TopRight(); p.x -= 3; p.y -= 2; SubMenuBase::Pull(this, p, Size(-r.Width(), 0)); if(parentmenu) parentmenu->SyncState(); } void SubMenuItem::MouseEnter(Point, dword) { SetFocus(); Refresh(); if(!menu.IsOpen() && isenabled) SetTimeCallback(400, THISBACK(Pull), TIMEID_PULL); } void SubMenuItem::MouseLeave() { if(HasFocus() && GetParent()) GetParent()->SetFocus(); KillTimeCallback(TIMEID_PULL); } int SubMenuItem::GetVisualState() { if(menu.IsOpen() && !GetParent()->HasFocusDeep()) return PUSH; return MenuItem::GetVisualState(); } bool SubMenuItem::Key(dword key, int count) { if(key == K_RIGHT || key == K_ENTER) { Pull(); return true; } return MenuItem::Key(key, count); } bool SubMenuItem::HotKey(dword key) { if(isenabled && (key == accel || CompareAccessKey(accesskey, key))) { Pull(); return true; } return false; } // ---------------------------------------------------- Size TopSubMenuItem::GetMinSize() const { return AddFrameSize(GetTextSize(text, font) + Size(12, 7)); } int TopSubMenuItem::GetState() { if(parentmenu && parentmenu->GetActiveSubmenu() == &menu) return PUSH; if(HasMouse() && GetParent() && !GetParent()->HasFocusDeep() && (!parentmenu || !parentmenu->GetActiveSubmenu() || parentmenu->GetActiveSubmenu() == &menu) || HasFocus()) return HIGHLIGHT; return NORMAL; } void TopSubMenuItem::Paint(Draw& w) { PaintTopItem(w, state = GetState()); } void TopSubMenuItem::Pull() { Rect r = GetScreenRect(); if(parentmenu && parentmenu->IsChild() && !parentmenu->submenu) parentmenu->SetupRestoreFocus(); Point p = r.BottomLeft(); if(GUI_GlobalStyle() >= GUISTYLE_XP) p.y -= 1; SubMenuBase::Pull(this, p, Size(r.Width(), -r.Height())); if(parentmenu) parentmenu->SyncState(); } void TopSubMenuItem::MouseEnter(Point, dword) { Refresh(); if(isenabled && parentmenu->GetActiveSubmenu()) Pull(); } bool TopSubMenuItem::Key(dword key, int) { if(isenabled && (key == K_ENTER || key == K_DOWN)) { Pull(); return true; } return false; } bool TopSubMenuItem::HotKey(dword key) { if(BarCtrl::Scan(proc, key)) return true; if(isenabled && (key == accel || CompareAccessKey(accesskey, key))) { Pull(); return true; } return false; } void TopSubMenuItem::MouseLeave() { Refresh(); } void TopSubMenuItem::LeftDown(Point, dword) { if(isenabled && !menu.IsOpen()) { Pull(); Refresh(); } } void TopSubMenuItem::SyncState() { int q = GetState(); if(q != state) { state = q; Refresh(); } } // ---------------------------------------------------- int TopMenuItem::GetState() { if(!IsEnabled()) return NORMAL; if(HasMouse() && GetMouseLeft() || GetMouseRight()) return PUSH; if(HasFocus() || HasMouse()) return HIGHLIGHT; return NORMAL; } void TopMenuItem::Paint(Draw& w) { PaintTopItem(w, state = GetState()); } void TopMenuItem::MouseEnter(Point, dword) { Refresh(); } void TopMenuItem::MouseLeave() { Refresh(); } void TopMenuItem::LeftUp(Point, dword) { if(!isenabled) return; WhenAction(); Refresh(); } void TopMenuItem::LeftDown(Point, dword) { Refresh(); } void TopMenuItem::GotFocus() { Refresh(); } void TopMenuItem::LostFocus() { Refresh(); } bool TopMenuItem::Key(dword key, int count) { if(isenabled && key == K_ENTER) { WhenAction(); return true; } return false; } Size TopMenuItem::GetMinSize() const { return AddFrameSize(GetTextSize(text, StdFont()) + Size(12, 7)); } int TopMenuItem::GetStdHeight(Font font) { return font.Info().GetHeight() + 7; } void TopMenuItem::SyncState() { if(state != GetState()) { state = GetState(); Refresh(); } } // ------------------------------------------- void MenuBar::Clear() { if(lock) return; lock++; SetActiveSubmenu(NULL, NULL); item.Clear(); BarCtrl::Clear(); lock--; } Bar::Item& MenuBar::AddItem(Callback cb) { LLOG("MenuBar::AddItem " << Name()); MenuItemBase *q; if(IsChild()) q = new TopMenuItem; else q = new MenuItem; item.Add(q); pane.Add(q, Null); q->SetFont(font); q->LeftGap(leftgap); q->Style(style); *q <<= THISBACK(CloseMenu); *q << cb; return *q; } Bar::Item& MenuBar::AddSubMenu(Callback1 proc) { LLOG("MenuBar::AddSubMenu " << Name()); SubMenuBase *w; MenuItemBase *q; if(IsChild()) { TopSubMenuItem *a = new TopSubMenuItem; q = a; w = a; } else { SubMenuItem *a = new SubMenuItem; q = a; w = a; } item.Add(q); pane.Add(q, Null); q->SetFont(font); q->LeftGap(leftgap); q->Style(style); w->SetParent(this); w->Set(proc); return *q; } void MenuBar::CloseMenu() { LLOG("CloseMenu"); MenuBar *q = GetLastSubmenu(); while(q && q->IsPopUp()) { if(q->IsOpen()) { q->doeffect = true; q->Close(); } MenuBar *w = q; q = q->parentmenu; w->submenu = w->parentmenu = NULL; } if(q) q->submenu = NULL; while(q) { q->SyncState(); q->doeffect = true; q = q->parentmenu; } } MenuBar *MenuBar::GetMasterMenu() { MenuBar *q = this; while(q->parentmenu) q = q->parentmenu; return q; } MenuBar *MenuBar::GetLastSubmenu() { MenuBar *q = this; while(q->submenu) q = q->submenu; return q; } void MenuBar::SetupRestoreFocus() { MenuBar *q = GetMasterMenu(); while(q) { if(q->HasFocusDeep()) return; q = q->submenu; } restorefocus = GetFocusCtrl(); LLOG("SetupRestoreFocus -> " << ::Name(restorefocus)); } void MenuBar::PostDeactivate() { MenuBar *q = GetLastSubmenu(); MenuBar *u = q; while(q && q->IsPopUp()) { if(q->IsOpen() && q->HasFocusDeep()) return; q = q->parentmenu; } if(u->IsPopUp()) { while(u->parentmenu && u->parentmenu->IsPopUp()) u = u->parentmenu; Ctrl *w = GetActiveCtrl(); while(w) { if(w == u) return; w = w->GetOwner(); } } CloseMenu(); } void MenuBar::Deactivate() { if(IsOpen()) SetTimeCallback(0, THISBACK(PostDeactivate), TIMEID_STOP); } void MenuBar::SyncState() { if(lock) return; for(int i = 0; i < item.GetCount(); i++) item[i].SyncState(); } void MenuBar::ChildGotFocus() { if(submenu && !submenuitem->HasFocusDeep() && !ExistsTimeCallback()) { if(submenu->IsOpen()) submenu->DelayedClose(); } KillDelayedClose(); SyncState(); } void MenuBar::ChildLostFocus() { SyncState(); } bool PullMenu(Ctrl *q) { if(q->IsEnabled()) { TopSubMenuItem *w = dynamic_cast(q); if(w) { w->Pull(); return true; } } return false; } static bool s_doaltkey; bool MenuKeyHook(Ctrl *ctrl, dword key, int count) { if(key != K_ALT_KEY && key != (K_ALT_KEY|K_KEYUP)) s_doaltkey = false; return false; } bool MenuStateHook(Ctrl *ctrl, int reason) { if(reason == Ctrl::ACTIVATE) s_doaltkey = false; return false; } INITBLOCK { Ctrl::InstallKeyHook(MenuKeyHook); Ctrl::InstallStateHook(MenuStateHook); } bool MenuBar::Key(dword key, int count) { bool horz = IsChild(); if((horz ? key == K_RIGHT : key == K_DOWN)) { Ctrl *ctrl = GetFocusChildDeep(); LLOG("MenuBar::Key(" << key << ") -> IterateFocusForward for " << ::Name(ctrl) << ", pane " << ::Name(&pane)); if(HasMouseDeep()) GetMouseCtrl()->Refresh(); if(ctrl && IterateFocusForward(ctrl, &pane, false, false, true)) return true; Ctrl *f = pane.GetFirstChild(); if(!f) return true; if(f->IsEnabled()) { f->SetFocus(); return true; } if(IterateFocusForward(pane.GetFirstChild(), &pane, false, false, true)) return true; } else if((horz ? key == K_LEFT : key == K_UP)) { Ctrl *ctrl = GetFocusChildDeep(); LLOG("MenuBar::Key(" << key << ") -> IterateFocusBackward for " << ::Name(ctrl) << ", pane " << ::Name(&pane)); if(HasMouseDeep()) GetMouseCtrl()->Refresh(); if(ctrl && IterateFocusBackward(ctrl, &pane, false, true)) return true; Ctrl *f = pane.GetLastChild(); if(!f) return true; if(f->IsEnabled()) { f->SetFocus(); return true; } if(IterateFocusBackward(pane.GetLastChild(), &pane, false, true)) return true; } else if(parentmenu && !parentmenu->IsChild() && key == K_LEFT || key == K_ESCAPE) { if(HasMouseDeep()) GetMouseCtrl()->Refresh(); if(parentmenu && parentmenu->submenu) parentmenu->submenuitem->SetFocus(); else if(IsChild() && HasFocusDeep()) { if(restorefocus) restorefocus->SetFocus(); doeffect = true; return true; } if(IsPopUp()) { SubmenuClose(); return true; } doeffect = true; } if(parentmenu && parentmenu->IsChild() && parentmenu->GetActiveSubmenu() && parentmenu->pane.GetFirstChild() && parentmenu->submenuitem) { Ctrl *smi = parentmenu->submenuitem; Ctrl *q = smi; if(key == K_RIGHT) for(;;) { q = q->GetNext(); if(!q) q = parentmenu->pane.GetFirstChild(); if(q == smi) break; if(PullMenu(q)) { SyncState(); return true; } } if(key == K_LEFT) for(;;) { q = q->GetPrev(); if(!q) q = parentmenu->pane.GetLastChild(); if(q == smi) break; if(PullMenu(q)) { SyncState(); return true; } } } return HotKey(key); } void MenuBar::LeftDown(Point, dword) { if(IsChild() && HasFocusDeep()) { if(restorefocus) restorefocus->SetFocus(); } WhenLeftClick(); } bool MenuBar::HotKey(dword key) { if(Ctrl::HotKey(key)) return true; if(IsChild()) { if((key == (K_ALT_KEY|K_KEYUP) || key == K_F10) && (submenu || HasFocusDeep())) { LLOG("CloseMenu()"); CloseMenu(); if(restorefocus) restorefocus->SetFocus(); s_doaltkey = false; return true; } if(key == K_ALT_KEY) { s_doaltkey = true; return true; } if((key == K_F10 || key == (K_ALT_KEY|K_KEYUP) && s_doaltkey) && !submenu && !HasFocusDeep() && GetTopWindow() && GetTopWindow()->IsForeground()) { SetupRestoreFocus(); for(Ctrl *q = pane.GetFirstChild(); q; q = q->GetNext()) if(q->SetFocus()) return true; } } return (key == K_LEFT || key == K_RIGHT) && parentmenu ? parentmenu->Key(key, 1) : false; } void MenuBar::SetActiveSubmenu(MenuBar *sm, Ctrl *item) { if(submenu && submenu != sm) { submenu->Close(); submenu->parentmenu = NULL; } if(submenuitem) submenuitem->Refresh(); submenu = sm; submenuitem = item; } void MenuBar::SubmenuClose() { if(parentmenu && parentmenu->GetActiveSubmenu() == this) parentmenu->SetActiveSubmenu(NULL, NULL); else Close(); parentmenu = NULL; Clear(); } void MenuBar::DelayedClose() { SetTimeCallback(400, THISBACK(SubmenuClose), TIMEID_SUBMENUCLOSE); } void MenuBar::KillDelayedClose() { KillTimeCallback(TIMEID_SUBMENUCLOSE); KillTimeCallback(TIMEID_STOP); } void MenuBar::Set(Callback1 menu) { if(lock) return; Clear(); lock++; menu(*this); SyncBar(); DistributeAccessKeys(); lock--; } void MenuBar::Post(Callback1 bar) { KillTimeCallback(TIMEID_POST); SetTimeCallback(0, THISBACK1(Set, bar), TIMEID_POST); } #ifdef PLATFORM_WIN32 DWORD WINAPI PlaySoundThread(LPVOID) { #ifdef PLATFORM_WINCE PlaySound(L"MenuPopup", NULL, SND_ASYNC|SND_NODEFAULT); #else PlaySound("MenuPopup", NULL, SND_ASYNC|SND_NODEFAULT); #endif return 0; } #endif static ColorF xpmenuborder[] = { (ColorF)3, &SColorShadow, &SColorShadow, &SColorShadow, &SColorShadow, &SColorMenu, &SColorMenu, &SColorMenu, &SColorMenu, &SColorMenu, &SColorMenu, &SColorMenu, &SColorMenu, }; GLOBAL_VARP(BorderFrame, XPMenuFrame, (xpmenuborder)); CtrlFrame& MenuFrame() { return GUI_GlobalStyle() >= GUISTYLE_XP ? (CtrlFrame&)XPMenuFrame() : (CtrlFrame&)OutsetFrame(); } void MenuBar::PopUp(Ctrl *owner, Point p, Size rsz) { bool szcx = true; bool szcy = true; bool szx = false; bool szy = false; if(parentmenu) { if(parentmenu->IsChild()) szcx = false; else szcy = false; WhenHelp = parentmenu->WhenHelp; } Rect r = GetWorkArea(); restorefocus = GetFocusCtrl(); LLOG("PopUp " << ::Name(this) << " set restorefocus:" << ::Name(restorefocus)); DistributeAccessKeys(); SetFrame(MenuFrame()); pane.SubMenu(); Size sz = pane.Repos(false, r.Height()); pane.RightPos(0, sz.cx).BottomPos(0, sz.cy); sz = AddFrameSize(sz); if(p.x + sz.cx > r.right) { p.x = max(p.x + rsz.cx - sz.cx, 0); szx = szcx; pane.LeftPos(0, sz.cx); } int pyy = p.y; if(p.y + sz.cy > r.bottom) { p.y = max(p.y + rsz.cy - sz.cy, 0); szy = szcy; pane.TopPos(0, sz.cy); } bool eff = parentmenu == NULL || parentmenu->doeffect; if(eff && GUI_PopUpEffect() == GUIEFFECT_SLIDE) SetRect(szx ? p.x + sz.cx : p.x, szy ? p.y + sz.cy : p.y, parentmenu ? sz.cx : 1, 1); else SetRect(p.x, p.y, sz.cx, sz.cy); #ifdef PLATFORM_WIN32 DWORD dummy; CreateThread(NULL, 0, PlaySoundThread, NULL, 0, &dummy); #endif doeffect = true; Ctrl::PopUp(owner, true, true, GUI_DropShadows(), !owner); #ifdef PLATFORM_X11 XSync(Xdisplay, false); ProcessEvents(); #endif if(eff) Animate(*this, p.x, p.y, sz.cx, sz.cy); } void MenuBar::Execute(Ctrl *owner, Point p) { if(IsEmpty()) return; PopUp(owner, p); EventLoop(this); } void MenuBar::Execute(Ctrl *owner, Callback1 proc, Point p) { MenuBar bar; proc(bar); bar.Execute(owner, p); } int MenuBar::GetStdHeight(Font font) { return TopMenuItem::GetStdHeight(font); } void MenuBar::CancelMode() { } MenuBar::MenuBar() { LLOG("MenuBar " << Name()); submenu = parentmenu = NULL; submenuitem = NULL; doeffect = true; font = StdFont(); leftgap = 16; lock = 0; SetStyle(StyleDefault()); } MenuBar::~MenuBar() { lock++; LLOG("~MenuBar " << Name()); LLOG("Parent menu " << ::Name(parentmenu)); if(parentmenu) parentmenu->SetActiveSubmenu(NULL, NULL); LLOG("~MenuBar 1"); } END_UPP_NAMESPACE