1c7023acdSAxel Dörfler /* 2c7023acdSAxel Dörfler * Copyright 2001-2006, Haiku, Inc. 3c7023acdSAxel Dörfler * Distributed under the terms of the MIT License. 4c7023acdSAxel Dörfler * 5c7023acdSAxel Dörfler * Authors: 6c7023acdSAxel Dörfler * Marc Flerackers (mflerackers@androme.be) 7c7023acdSAxel Dörfler * Stefano Ceccherini (burton666@libero.it) 8c7023acdSAxel Dörfler */ 9c7023acdSAxel Dörfler 10c7023acdSAxel Dörfler //! BMenuWindow is a custom BWindow for BMenus. 11c7023acdSAxel Dörfler 12446b8c19SStefano Ceccherini #include <Menu.h> 13446b8c19SStefano Ceccherini 142c11ec31SStefano Ceccherini #include <MenuPrivate.h> 152c11ec31SStefano Ceccherini #include <MenuWindow.h> 16c7023acdSAxel Dörfler #include <WindowPrivate.h> 17c7023acdSAxel Dörfler 18e411c658SStefano Ceccherini 192628e60cSAxel Dörfler namespace BPrivate { 202628e60cSAxel Dörfler 212628e60cSAxel Dörfler class BMenuScroller : public BView { 222628e60cSAxel Dörfler public: 23*3616859aSStefano Ceccherini BMenuScroller(BRect frame); 242628e60cSAxel Dörfler 25*3616859aSStefano Ceccherini bool IsEnabled() const; 26*3616859aSStefano Ceccherini void SetEnabled(const bool &enabled); 272628e60cSAxel Dörfler private: 28*3616859aSStefano Ceccherini bool fEnabled; 292628e60cSAxel Dörfler }; 302628e60cSAxel Dörfler 31*3616859aSStefano Ceccherini 322628e60cSAxel Dörfler class BMenuFrame : public BView { 332628e60cSAxel Dörfler public: 342628e60cSAxel Dörfler BMenuFrame(BMenu *menu); 352628e60cSAxel Dörfler 362628e60cSAxel Dörfler virtual void AttachedToWindow(); 372628e60cSAxel Dörfler virtual void DetachedFromWindow(); 382628e60cSAxel Dörfler virtual void Draw(BRect updateRect); 392628e60cSAxel Dörfler 402628e60cSAxel Dörfler private: 412628e60cSAxel Dörfler friend class BMenuWindow; 422628e60cSAxel Dörfler 432628e60cSAxel Dörfler BMenu *fMenu; 442628e60cSAxel Dörfler }; 452628e60cSAxel Dörfler 46*3616859aSStefano Ceccherini 47*3616859aSStefano Ceccherini class UpperScroller : public BMenuScroller { 48*3616859aSStefano Ceccherini public: 49*3616859aSStefano Ceccherini UpperScroller(BRect frame); 50*3616859aSStefano Ceccherini virtual void Draw(BRect updateRect); 51*3616859aSStefano Ceccherini }; 52*3616859aSStefano Ceccherini 53*3616859aSStefano Ceccherini class LowerScroller : public BMenuScroller { 54*3616859aSStefano Ceccherini public: 55*3616859aSStefano Ceccherini LowerScroller(BRect frame); 56*3616859aSStefano Ceccherini virtual void Draw(BRect updateRect); 57*3616859aSStefano Ceccherini }; 58*3616859aSStefano Ceccherini 592628e60cSAxel Dörfler } // namespace BPrivate 602628e60cSAxel Dörfler 61*3616859aSStefano Ceccherini 622628e60cSAxel Dörfler using namespace BPrivate; 632628e60cSAxel Dörfler 642628e60cSAxel Dörfler 6527cc2508SStefano Ceccherini const int kScrollerHeight = 10; 66c7338938SStefano Ceccherini const int kScrollStep = 19; 675b752875SStefano Ceccherini 682628e60cSAxel Dörfler 69*3616859aSStefano Ceccherini BMenuScroller::BMenuScroller(BRect frame) 70c7338938SStefano Ceccherini : BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS), 71*3616859aSStefano Ceccherini fEnabled(false) 7227cc2508SStefano Ceccherini { 7327cc2508SStefano Ceccherini SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 7427cc2508SStefano Ceccherini } 7527cc2508SStefano Ceccherini 7627cc2508SStefano Ceccherini 77c7338938SStefano Ceccherini bool 78*3616859aSStefano Ceccherini BMenuScroller::IsEnabled() const 7927cc2508SStefano Ceccherini { 80*3616859aSStefano Ceccherini return fEnabled; 8127cc2508SStefano Ceccherini } 8227cc2508SStefano Ceccherini 8327cc2508SStefano Ceccherini 8427cc2508SStefano Ceccherini void 85*3616859aSStefano Ceccherini BMenuScroller::SetEnabled(const bool &enabled) 86*3616859aSStefano Ceccherini { 87*3616859aSStefano Ceccherini fEnabled = enabled; 88*3616859aSStefano Ceccherini } 89*3616859aSStefano Ceccherini 90*3616859aSStefano Ceccherini 91*3616859aSStefano Ceccherini // #pragma mark - 92*3616859aSStefano Ceccherini 93*3616859aSStefano Ceccherini 94*3616859aSStefano Ceccherini UpperScroller::UpperScroller(BRect frame) 95*3616859aSStefano Ceccherini : 96*3616859aSStefano Ceccherini BMenuScroller(frame) 97*3616859aSStefano Ceccherini { 98*3616859aSStefano Ceccherini } 99*3616859aSStefano Ceccherini 100*3616859aSStefano Ceccherini 101*3616859aSStefano Ceccherini void 102*3616859aSStefano Ceccherini UpperScroller::Draw(BRect updateRect) 10327cc2508SStefano Ceccherini { 10427cc2508SStefano Ceccherini SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 10527cc2508SStefano Ceccherini float middle = Bounds().right / 2; 10627cc2508SStefano Ceccherini 10727cc2508SStefano Ceccherini // Draw the upper arrow. 108*3616859aSStefano Ceccherini if (IsEnabled()) 10927cc2508SStefano Ceccherini SetHighColor(0, 0, 0); 11027cc2508SStefano Ceccherini else 11127cc2508SStefano Ceccherini SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 11227cc2508SStefano Ceccherini B_DARKEN_2_TINT)); 11327cc2508SStefano Ceccherini 114*3616859aSStefano Ceccherini FillRect(Bounds(), B_SOLID_LOW); 11527cc2508SStefano Ceccherini 11627cc2508SStefano Ceccherini FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3), 11727cc2508SStefano Ceccherini BPoint(middle + 5, (kScrollerHeight / 2) + 2), 11827cc2508SStefano Ceccherini BPoint(middle - 5, (kScrollerHeight / 2) + 2)); 11927cc2508SStefano Ceccherini } 12027cc2508SStefano Ceccherini 121*3616859aSStefano Ceccherini 122*3616859aSStefano Ceccherini // #pragma mark - 123*3616859aSStefano Ceccherini 124*3616859aSStefano Ceccherini 125*3616859aSStefano Ceccherini LowerScroller::LowerScroller(BRect frame) 126*3616859aSStefano Ceccherini : 127*3616859aSStefano Ceccherini BMenuScroller(frame) 128*3616859aSStefano Ceccherini { 129*3616859aSStefano Ceccherini } 130*3616859aSStefano Ceccherini 131*3616859aSStefano Ceccherini 132*3616859aSStefano Ceccherini void 133*3616859aSStefano Ceccherini LowerScroller::Draw(BRect updateRect) 134*3616859aSStefano Ceccherini { 135*3616859aSStefano Ceccherini SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 136*3616859aSStefano Ceccherini 137*3616859aSStefano Ceccherini BRect frame = Bounds(); 13827cc2508SStefano Ceccherini // Draw the lower arrow. 139*3616859aSStefano Ceccherini if (IsEnabled()) 14027cc2508SStefano Ceccherini SetHighColor(0, 0, 0); 14127cc2508SStefano Ceccherini else 14227cc2508SStefano Ceccherini SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 14327cc2508SStefano Ceccherini B_DARKEN_2_TINT)); 14427cc2508SStefano Ceccherini 145*3616859aSStefano Ceccherini FillRect(Bounds(), B_SOLID_LOW); 14627cc2508SStefano Ceccherini 147*3616859aSStefano Ceccherini float middle = Bounds().right / 2; 148*3616859aSStefano Ceccherini 149*3616859aSStefano Ceccherini FillTriangle(BPoint(middle, frame.bottom - (kScrollerHeight / 2) + 3), 150*3616859aSStefano Ceccherini BPoint(middle + 5, frame.bottom - (kScrollerHeight / 2) - 2), 151*3616859aSStefano Ceccherini BPoint(middle - 5, frame.bottom - (kScrollerHeight / 2) - 2)); 15227cc2508SStefano Ceccherini } 15327cc2508SStefano Ceccherini 15427cc2508SStefano Ceccherini 15527cc2508SStefano Ceccherini // #pragma mark - 1565b752875SStefano Ceccherini 1575b752875SStefano Ceccherini 1582628e60cSAxel Dörfler BMenuFrame::BMenuFrame(BMenu *menu) 1592628e60cSAxel Dörfler : BView(BRect(0, 0, 1, 1), "menu frame", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), 1602628e60cSAxel Dörfler fMenu(menu) 1612628e60cSAxel Dörfler { 1622628e60cSAxel Dörfler } 1632628e60cSAxel Dörfler 1642628e60cSAxel Dörfler 1652628e60cSAxel Dörfler void 1662628e60cSAxel Dörfler BMenuFrame::AttachedToWindow() 1672628e60cSAxel Dörfler { 1682628e60cSAxel Dörfler BView::AttachedToWindow(); 1692628e60cSAxel Dörfler 1702628e60cSAxel Dörfler if (fMenu != NULL) 1712628e60cSAxel Dörfler AddChild(fMenu); 1722628e60cSAxel Dörfler 1732628e60cSAxel Dörfler ResizeTo(Window()->Bounds().Width(), Window()->Bounds().Height()); 1742628e60cSAxel Dörfler if (fMenu != NULL) { 1752628e60cSAxel Dörfler BFont font; 1762628e60cSAxel Dörfler fMenu->GetFont(&font); 1772628e60cSAxel Dörfler SetFont(&font); 1782628e60cSAxel Dörfler } 1792628e60cSAxel Dörfler } 1802628e60cSAxel Dörfler 1812628e60cSAxel Dörfler 1822628e60cSAxel Dörfler void 1832628e60cSAxel Dörfler BMenuFrame::DetachedFromWindow() 1842628e60cSAxel Dörfler { 1852628e60cSAxel Dörfler if (fMenu != NULL) 1862628e60cSAxel Dörfler RemoveChild(fMenu); 1872628e60cSAxel Dörfler } 1882628e60cSAxel Dörfler 1892628e60cSAxel Dörfler 1902628e60cSAxel Dörfler void 1912628e60cSAxel Dörfler BMenuFrame::Draw(BRect updateRect) 1922628e60cSAxel Dörfler { 1932628e60cSAxel Dörfler if (fMenu != NULL && fMenu->CountItems() == 0) { 1942628e60cSAxel Dörfler // TODO: Review this as it's a bit hacky. 1952628e60cSAxel Dörfler // Menu has a size of 0, 0, since there are no items in it. 1962628e60cSAxel Dörfler // So the BMenuFrame class has to fake it and draw an empty item. 1972628e60cSAxel Dörfler // Note that we can't add a real "empty" item because then we couldn't 1982628e60cSAxel Dörfler // tell if the item was added by us or not. 1992628e60cSAxel Dörfler // See also BMenu::UpdateWindowViewSize() 2002628e60cSAxel Dörfler SetHighColor(ui_color(B_MENU_BACKGROUND_COLOR)); 2012628e60cSAxel Dörfler SetLowColor(HighColor()); 2022628e60cSAxel Dörfler FillRect(updateRect); 2032628e60cSAxel Dörfler 2042628e60cSAxel Dörfler font_height height; 2052628e60cSAxel Dörfler GetFontHeight(&height); 2062628e60cSAxel Dörfler SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DISABLED_LABEL_TINT)); 2072628e60cSAxel Dörfler BPoint where((Bounds().Width() - fMenu->StringWidth(kEmptyMenuLabel)) / 2, ceilf(height.ascent + 1)); 2082628e60cSAxel Dörfler DrawString(kEmptyMenuLabel, where); 2092628e60cSAxel Dörfler } 2102628e60cSAxel Dörfler 2112628e60cSAxel Dörfler SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 2122628e60cSAxel Dörfler BRect bounds(Bounds()); 2132628e60cSAxel Dörfler 2142628e60cSAxel Dörfler StrokeLine(BPoint(bounds.right, bounds.top), 2152628e60cSAxel Dörfler BPoint(bounds.right, bounds.bottom - 1)); 2162628e60cSAxel Dörfler StrokeLine(BPoint(bounds.left + 1, bounds.bottom), 2172628e60cSAxel Dörfler BPoint(bounds.right, bounds.bottom)); 2182628e60cSAxel Dörfler } 2192628e60cSAxel Dörfler 2202628e60cSAxel Dörfler 221*3616859aSStefano Ceccherini 2222628e60cSAxel Dörfler // #pragma mark - 2232628e60cSAxel Dörfler 2242628e60cSAxel Dörfler 2254185bd8bSStefano Ceccherini BMenuWindow::BMenuWindow(const char *name) 2261664b981SStefano Ceccherini // The window will be resized by BMenu, so just pass a dummy rect 227c7023acdSAxel Dörfler : BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel, 228c7023acdSAxel Dörfler B_NOT_ZOOMABLE | B_AVOID_FOCUS), 229*3616859aSStefano Ceccherini fMenu(NULL), 230*3616859aSStefano Ceccherini fMenuFrame(NULL), 231*3616859aSStefano Ceccherini fUpperScroller(NULL), 232*3616859aSStefano Ceccherini fLowerScroller(NULL) 233446b8c19SStefano Ceccherini { 234446b8c19SStefano Ceccherini } 235446b8c19SStefano Ceccherini 236446b8c19SStefano Ceccherini 237446b8c19SStefano Ceccherini BMenuWindow::~BMenuWindow() 238446b8c19SStefano Ceccherini { 239446b8c19SStefano Ceccherini } 2405aa032f1SStefano Ceccherini 2415aa032f1SStefano Ceccherini 24261ba5a32SStefano Ceccherini void 2434185bd8bSStefano Ceccherini BMenuWindow::AttachMenu(BMenu *menu) 2444185bd8bSStefano Ceccherini { 24527cc2508SStefano Ceccherini if (fMenuFrame) 24627cc2508SStefano Ceccherini debugger("BMenuWindow: a menu is already attached!"); 2474185bd8bSStefano Ceccherini if (menu != NULL) { 24827cc2508SStefano Ceccherini fMenuFrame = new BMenuFrame(menu); 24927cc2508SStefano Ceccherini AddChild(fMenuFrame); 25007dc2c69SAxel Dörfler menu->MakeFocus(true); 251*3616859aSStefano Ceccherini fMenu = menu; 2524185bd8bSStefano Ceccherini } 2534185bd8bSStefano Ceccherini } 2544185bd8bSStefano Ceccherini 2554185bd8bSStefano Ceccherini 2564185bd8bSStefano Ceccherini void 2574185bd8bSStefano Ceccherini BMenuWindow::DetachMenu() 25861ba5a32SStefano Ceccherini { 25927cc2508SStefano Ceccherini DetachScrollers(); 260*3616859aSStefano Ceccherini if (fMenuFrame) { 26127cc2508SStefano Ceccherini RemoveChild(fMenuFrame); 26227cc2508SStefano Ceccherini delete fMenuFrame; 26327cc2508SStefano Ceccherini fMenuFrame = NULL; 264*3616859aSStefano Ceccherini fMenu = NULL; 26527cc2508SStefano Ceccherini } 26627cc2508SStefano Ceccherini } 26727cc2508SStefano Ceccherini 26827cc2508SStefano Ceccherini 26927cc2508SStefano Ceccherini void 27027cc2508SStefano Ceccherini BMenuWindow::AttachScrollers() 27127cc2508SStefano Ceccherini { 27227cc2508SStefano Ceccherini // We want to attach a scroller only if there's a menu frame already 27327cc2508SStefano Ceccherini // existing. 274*3616859aSStefano Ceccherini if (!fMenu || !fMenuFrame) 27527cc2508SStefano Ceccherini return; 27627cc2508SStefano Ceccherini 277*3616859aSStefano Ceccherini fMenu->MakeFocus(true); 27827cc2508SStefano Ceccherini 279*3616859aSStefano Ceccherini BRect frame = Bounds(); 280*3616859aSStefano Ceccherini fUpperScroller = new UpperScroller(BRect(0, 0, frame.right, kScrollerHeight)); 281*3616859aSStefano Ceccherini AddChild(fUpperScroller); 282*3616859aSStefano Ceccherini fLowerScroller = new LowerScroller(BRect(0, frame.bottom - kScrollerHeight, frame.right, frame.bottom)); 283*3616859aSStefano Ceccherini AddChild(fLowerScroller); 284*3616859aSStefano Ceccherini 285*3616859aSStefano Ceccherini fUpperScroller->SetEnabled(false); 286*3616859aSStefano Ceccherini fLowerScroller->SetEnabled(true); 28727cc2508SStefano Ceccherini 28827cc2508SStefano Ceccherini fMenuFrame->ResizeBy(0, -2 * kScrollerHeight); 28927cc2508SStefano Ceccherini fMenuFrame->MoveBy(0, kScrollerHeight); 290*3616859aSStefano Ceccherini 291*3616859aSStefano Ceccherini fMenuFrame->Bounds().PrintToStream(); 292*3616859aSStefano Ceccherini fValue = 0; 293*3616859aSStefano Ceccherini fLimit = fMenu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight); 29427cc2508SStefano Ceccherini } 29527cc2508SStefano Ceccherini 29627cc2508SStefano Ceccherini 29727cc2508SStefano Ceccherini void 29827cc2508SStefano Ceccherini BMenuWindow::DetachScrollers() 29927cc2508SStefano Ceccherini { 30027cc2508SStefano Ceccherini // BeOS doesn't remember the position where the last scrolling ended, 30127cc2508SStefano Ceccherini // so we just scroll back to the beginning. 302*3616859aSStefano Ceccherini fMenu->ScrollTo(0, 0); 30327cc2508SStefano Ceccherini 304*3616859aSStefano Ceccherini if (fLowerScroller) { 305*3616859aSStefano Ceccherini RemoveChild(fLowerScroller); 306*3616859aSStefano Ceccherini delete fLowerScroller; 307*3616859aSStefano Ceccherini fLowerScroller = NULL; 308*3616859aSStefano Ceccherini } 30927cc2508SStefano Ceccherini 310*3616859aSStefano Ceccherini if (fUpperScroller) { 311*3616859aSStefano Ceccherini RemoveChild(fUpperScroller); 312*3616859aSStefano Ceccherini delete fUpperScroller; 313*3616859aSStefano Ceccherini fUpperScroller = NULL; 314*3616859aSStefano Ceccherini } 31561ba5a32SStefano Ceccherini } 316c7338938SStefano Ceccherini 317c7338938SStefano Ceccherini 318c7338938SStefano Ceccherini bool 319c7338938SStefano Ceccherini BMenuWindow::CheckForScrolling(BPoint cursor) 320c7338938SStefano Ceccherini { 321*3616859aSStefano Ceccherini if (!fMenuFrame) 322c7338938SStefano Ceccherini return false; 323c7338938SStefano Ceccherini 324*3616859aSStefano Ceccherini return _Scroll(cursor); 325*3616859aSStefano Ceccherini } 326*3616859aSStefano Ceccherini 327*3616859aSStefano Ceccherini 328*3616859aSStefano Ceccherini bool 329*3616859aSStefano Ceccherini BMenuWindow::_Scroll(BPoint cursor) 330*3616859aSStefano Ceccherini { 331*3616859aSStefano Ceccherini ConvertFromScreen(&cursor); 332*3616859aSStefano Ceccherini 333*3616859aSStefano Ceccherini BRect lowerFrame; 334*3616859aSStefano Ceccherini BRect upperFrame; 335*3616859aSStefano Ceccherini if (fLowerScroller) 336*3616859aSStefano Ceccherini lowerFrame = fLowerScroller->Frame(); 337*3616859aSStefano Ceccherini if (fUpperScroller) 338*3616859aSStefano Ceccherini upperFrame = fUpperScroller->Frame(); 339*3616859aSStefano Ceccherini 340*3616859aSStefano Ceccherini if (fLowerScroller && fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor)) { 341*3616859aSStefano Ceccherini if (fValue == 0) { 342*3616859aSStefano Ceccherini fUpperScroller->SetEnabled(true); 343*3616859aSStefano Ceccherini fUpperScroller->Invalidate(); 344*3616859aSStefano Ceccherini } 345*3616859aSStefano Ceccherini 346*3616859aSStefano Ceccherini if (fValue + kScrollStep >= fLimit) { 347*3616859aSStefano Ceccherini // If we reached the limit, we don't want to scroll a whole 348*3616859aSStefano Ceccherini // 'step' if not needed. 349*3616859aSStefano Ceccherini fMenu->ScrollBy(0, fLimit - fValue); 350*3616859aSStefano Ceccherini fValue = fLimit; 351*3616859aSStefano Ceccherini fLowerScroller->SetEnabled(false); 352*3616859aSStefano Ceccherini fLowerScroller->Invalidate(); 353*3616859aSStefano Ceccherini 354*3616859aSStefano Ceccherini } else { 355*3616859aSStefano Ceccherini fMenu->ScrollBy(0, kScrollStep); 356*3616859aSStefano Ceccherini fValue += kScrollStep; 357*3616859aSStefano Ceccherini } 358*3616859aSStefano Ceccherini } else if (fUpperScroller && fUpperScroller->IsEnabled() && upperFrame.Contains(cursor)) { 359*3616859aSStefano Ceccherini if (fValue == fLimit) { 360*3616859aSStefano Ceccherini fLowerScroller->SetEnabled(true); 361*3616859aSStefano Ceccherini fLowerScroller->Invalidate(); 362*3616859aSStefano Ceccherini } 363*3616859aSStefano Ceccherini 364*3616859aSStefano Ceccherini if (fValue - kScrollStep <= 0) { 365*3616859aSStefano Ceccherini fMenu->ScrollBy(0, -fValue); 366*3616859aSStefano Ceccherini fValue = 0; 367*3616859aSStefano Ceccherini fUpperScroller->SetEnabled(false); 368*3616859aSStefano Ceccherini fUpperScroller->Invalidate(); 369*3616859aSStefano Ceccherini 370*3616859aSStefano Ceccherini } else { 371*3616859aSStefano Ceccherini fMenu->ScrollBy(0, -kScrollStep); 372*3616859aSStefano Ceccherini fValue -= kScrollStep; 373*3616859aSStefano Ceccherini } 374*3616859aSStefano Ceccherini } else { 375*3616859aSStefano Ceccherini return false; 376*3616859aSStefano Ceccherini } 377*3616859aSStefano Ceccherini 378*3616859aSStefano Ceccherini snooze(10000); 379*3616859aSStefano Ceccherini 380*3616859aSStefano Ceccherini return true; 381c7338938SStefano Ceccherini } 382c7338938SStefano Ceccherini 383