tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / MenuWindow.cpp
blob9523f671785ad61df15cca9ebe4dd224d6028046
1 /*
2 * Copyright 2001-2009, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (stefano.ceccherini@gmail.com)
8 */
10 //! BMenuWindow is a custom BWindow for BMenus.
12 #include <MenuWindow.h>
14 #include <ControlLook.h>
15 #include <Debug.h>
16 #include <Menu.h>
17 #include <MenuItem.h>
19 #include <MenuPrivate.h>
20 #include <WindowPrivate.h>
23 namespace BPrivate {
25 class BMenuScroller : public BView {
26 public:
27 BMenuScroller(BRect frame);
29 bool IsEnabled() const;
30 void SetEnabled(bool enabled);
32 private:
33 bool fEnabled;
37 class BMenuFrame : public BView {
38 public:
39 BMenuFrame(BMenu* menu);
41 virtual void AttachedToWindow();
42 virtual void DetachedFromWindow();
43 virtual void Draw(BRect updateRect);
45 private:
46 friend class BMenuWindow;
48 BMenu* fMenu;
52 class UpperScroller : public BMenuScroller {
53 public:
54 UpperScroller(BRect frame);
56 virtual void Draw(BRect updateRect);
60 class LowerScroller : public BMenuScroller {
61 public:
62 LowerScroller(BRect frame);
64 virtual void Draw(BRect updateRect);
68 } // namespace BPrivate
71 using namespace BPrivate;
74 const int kScrollerHeight = 12;
77 BMenuScroller::BMenuScroller(BRect frame)
79 BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS),
80 fEnabled(false)
82 SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
86 bool
87 BMenuScroller::IsEnabled() const
89 return fEnabled;
93 void
94 BMenuScroller::SetEnabled(bool enabled)
96 fEnabled = enabled;
100 // #pragma mark -
103 UpperScroller::UpperScroller(BRect frame)
105 BMenuScroller(frame)
110 void
111 UpperScroller::Draw(BRect updateRect)
113 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
114 float middle = Bounds().right / 2;
116 // Draw the upper arrow.
117 if (IsEnabled())
118 SetHighColor(0, 0, 0);
119 else {
120 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
121 B_DARKEN_2_TINT));
124 FillRect(Bounds(), B_SOLID_LOW);
126 FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3),
127 BPoint(middle + 5, (kScrollerHeight / 2) + 2),
128 BPoint(middle - 5, (kScrollerHeight / 2) + 2));
132 // #pragma mark -
135 LowerScroller::LowerScroller(BRect frame)
137 BMenuScroller(frame)
142 void
143 LowerScroller::Draw(BRect updateRect)
145 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
147 BRect frame = Bounds();
148 // Draw the lower arrow.
149 if (IsEnabled())
150 SetHighColor(0, 0, 0);
151 else {
152 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
153 B_DARKEN_2_TINT));
156 FillRect(frame, B_SOLID_LOW);
158 float middle = Bounds().right / 2;
160 FillTriangle(BPoint(middle, frame.bottom - (kScrollerHeight / 2) + 3),
161 BPoint(middle + 5, frame.bottom - (kScrollerHeight / 2) - 2),
162 BPoint(middle - 5, frame.bottom - (kScrollerHeight / 2) - 2));
166 // #pragma mark -
169 BMenuFrame::BMenuFrame(BMenu *menu)
171 BView(BRect(0, 0, 1, 1), "menu frame", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
172 fMenu(menu)
177 void
178 BMenuFrame::AttachedToWindow()
180 BView::AttachedToWindow();
182 if (fMenu != NULL)
183 AddChild(fMenu);
185 ResizeTo(Window()->Bounds().Width(), Window()->Bounds().Height());
186 if (fMenu != NULL) {
187 BFont font;
188 fMenu->GetFont(&font);
189 SetFont(&font);
194 void
195 BMenuFrame::DetachedFromWindow()
197 if (fMenu != NULL)
198 RemoveChild(fMenu);
202 void
203 BMenuFrame::Draw(BRect updateRect)
205 if (fMenu != NULL && fMenu->CountItems() == 0) {
206 if (be_control_look != NULL) {
207 BRect rect(Bounds());
208 be_control_look->DrawMenuBackground(this, rect, updateRect,
209 ui_color(B_MENU_BACKGROUND_COLOR));
210 SetDrawingMode(B_OP_OVER);
211 } else {
212 // TODO: Review this as it's a bit hacky.
213 // Menu has a size of 0, 0, since there are no items in it.
214 // So the BMenuFrame class has to fake it and draw an empty item.
215 // Note that we can't add a real "empty" item because then we
216 // couldn't tell if the item was added by us or not.
217 // See also BMenu::UpdateWindowViewSize()
218 SetHighColor(ui_color(B_MENU_BACKGROUND_COLOR));
219 SetLowColor(HighColor());
220 FillRect(updateRect);
223 font_height height;
224 GetFontHeight(&height);
225 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
226 B_DISABLED_LABEL_TINT));
227 BPoint where(
228 (Bounds().Width() - fMenu->StringWidth(kEmptyMenuLabel)) / 2,
229 ceilf(height.ascent + 1));
230 DrawString(kEmptyMenuLabel, where);
233 if (be_control_look != NULL)
234 return;
236 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
237 B_DARKEN_2_TINT));
238 BRect bounds(Bounds());
240 StrokeLine(BPoint(bounds.right, bounds.top),
241 BPoint(bounds.right, bounds.bottom - 1));
242 StrokeLine(BPoint(bounds.left + 1, bounds.bottom),
243 BPoint(bounds.right, bounds.bottom));
248 // #pragma mark -
251 BMenuWindow::BMenuWindow(const char *name)
252 // The window will be resized by BMenu, so just pass a dummy rect
254 BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
255 B_NOT_MOVABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_AVOID_FOCUS
256 | kAcceptKeyboardFocusFlag),
257 fMenu(NULL),
258 fMenuFrame(NULL),
259 fUpperScroller(NULL),
260 fLowerScroller(NULL),
261 fScrollStep(19)
263 SetSizeLimits(2, 10000, 2, 10000);
267 BMenuWindow::~BMenuWindow()
269 DetachMenu();
273 void
274 BMenuWindow::DispatchMessage(BMessage *message, BHandler *handler)
276 BWindow::DispatchMessage(message, handler);
280 void
281 BMenuWindow::AttachMenu(BMenu *menu)
283 if (fMenuFrame)
284 debugger("BMenuWindow: a menu is already attached!");
285 if (menu != NULL) {
286 fMenuFrame = new BMenuFrame(menu);
287 AddChild(fMenuFrame);
288 menu->MakeFocus(true);
289 fMenu = menu;
294 void
295 BMenuWindow::DetachMenu()
297 DetachScrollers();
298 if (fMenuFrame) {
299 RemoveChild(fMenuFrame);
300 delete fMenuFrame;
301 fMenuFrame = NULL;
302 fMenu = NULL;
307 void
308 BMenuWindow::AttachScrollers()
310 // We want to attach a scroller only if there's a
311 // menu frame already existing.
312 if (!fMenu || !fMenuFrame)
313 return;
315 fMenu->MakeFocus(true);
317 BRect frame = Bounds();
319 if (fUpperScroller == NULL) {
320 fUpperScroller = new UpperScroller(
321 BRect(0, 0, frame.right, kScrollerHeight - 1));
322 AddChild(fUpperScroller);
325 if (fLowerScroller == NULL) {
326 fLowerScroller = new LowerScroller(
327 BRect(0, frame.bottom - kScrollerHeight + 1, frame.right,
328 frame.bottom));
329 AddChild(fLowerScroller);
332 fUpperScroller->SetEnabled(false);
333 fLowerScroller->SetEnabled(true);
335 fMenuFrame->ResizeBy(0, -2 * kScrollerHeight);
336 fMenuFrame->MoveBy(0, kScrollerHeight);
338 fValue = 0;
339 fLimit = fMenu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight);
343 void
344 BMenuWindow::DetachScrollers()
346 // BeOS doesn't remember the position where the last scrolling ended,
347 // so we just scroll back to the beginning.
348 if (fMenu)
349 fMenu->ScrollTo(0, 0);
351 if (fLowerScroller) {
352 RemoveChild(fLowerScroller);
353 delete fLowerScroller;
354 fLowerScroller = NULL;
357 if (fUpperScroller) {
358 RemoveChild(fUpperScroller);
359 delete fUpperScroller;
360 fUpperScroller = NULL;
365 void
366 BMenuWindow::SetSmallStep(float step)
368 fScrollStep = step;
372 void
373 BMenuWindow::GetSteps(float* _smallStep, float* _largeStep) const
375 if (_smallStep != NULL)
376 *_smallStep = fScrollStep;
377 if (_largeStep != NULL) {
378 if (fMenuFrame != NULL)
379 *_largeStep = fMenuFrame->Bounds().Height() - fScrollStep;
380 else
381 *_largeStep = fScrollStep * 2;
386 bool
387 BMenuWindow::HasScrollers() const
389 return fMenuFrame != NULL && fUpperScroller != NULL
390 && fLowerScroller != NULL;
394 bool
395 BMenuWindow::CheckForScrolling(const BPoint &cursor)
397 if (!fMenuFrame || !fUpperScroller || !fLowerScroller)
398 return false;
400 return _Scroll(cursor);
404 bool
405 BMenuWindow::TryScrollBy(const float& step)
407 if (!fMenuFrame || !fUpperScroller || !fLowerScroller)
408 return false;
410 _ScrollBy(step);
411 return true;
415 bool
416 BMenuWindow::TryScrollTo(const float& where)
418 if (!fMenuFrame || !fUpperScroller || !fLowerScroller)
419 return false;
421 _ScrollBy(where - fValue);
422 return true;
426 bool
427 BMenuWindow::_Scroll(const BPoint& where)
429 ASSERT((fLowerScroller != NULL));
430 ASSERT((fUpperScroller != NULL));
432 const BPoint cursor = ConvertFromScreen(where);
433 const BRect &lowerFrame = fLowerScroller->Frame();
434 const BRect &upperFrame = fUpperScroller->Frame();
436 int32 delta = 0;
437 if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor))
438 delta = 1;
439 else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor))
440 delta = -1;
442 if (delta == 0)
443 return false;
445 float smallStep;
446 GetSteps(&smallStep, NULL);
447 _ScrollBy(smallStep * delta);
449 snooze(5000);
451 return true;
455 void
456 BMenuWindow::_ScrollBy(const float& step)
458 if (step > 0) {
459 if (fValue == 0) {
460 fUpperScroller->SetEnabled(true);
461 fUpperScroller->Invalidate();
464 if (fValue + step >= fLimit) {
465 // If we reached the limit, only scroll to the end
466 fMenu->ScrollBy(0, fLimit - fValue);
467 fValue = fLimit;
468 fLowerScroller->SetEnabled(false);
469 fLowerScroller->Invalidate();
470 } else {
471 fMenu->ScrollBy(0, step);
472 fValue += step;
474 } else if (step < 0) {
475 if (fValue == fLimit) {
476 fLowerScroller->SetEnabled(true);
477 fLowerScroller->Invalidate();
480 if (fValue + step <= 0) {
481 fMenu->ScrollBy(0, -fValue);
482 fValue = 0;
483 fUpperScroller->SetEnabled(false);
484 fUpperScroller->Invalidate();
485 } else {
486 fMenu->ScrollBy(0, step);
487 fValue += step;