tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / MenuBar.cpp
blob194cfcf19c0fe7211e8fa1703115057d22c0b34d
1 /*
2 * Copyright 2001-2009, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (burton666@libero.it)
8 * Stephan Aßmus <superstippi@gmx.de>
9 */
12 #include <MenuBar.h>
14 #include <math.h>
16 #include <Application.h>
17 #include <Autolock.h>
18 #include <ControlLook.h>
19 #include <LayoutUtils.h>
20 #include <MenuItem.h>
21 #include <Window.h>
23 #include <AppMisc.h>
24 #include <binary_compatibility/Interface.h>
25 #include <MenuPrivate.h>
26 #include <TokenSpace.h>
27 #include <InterfaceDefs.h>
29 #include "BMCPrivate.h"
32 using BPrivate::gDefaultTokens;
35 struct menubar_data {
36 BMenuBar* menuBar;
37 int32 menuIndex;
39 bool sticky;
40 bool showMenu;
42 bool useRect;
43 BRect rect;
47 BMenuBar::BMenuBar(BRect frame, const char* name, uint32 resizingMode,
48 menu_layout layout, bool resizeToFit)
50 BMenu(frame, name, resizingMode, B_WILL_DRAW | B_FRAME_EVENTS
51 | B_FULL_UPDATE_ON_RESIZE, layout, resizeToFit),
52 fBorder(B_BORDER_FRAME),
53 fTrackingPID(-1),
54 fPrevFocusToken(-1),
55 fMenuSem(-1),
56 fLastBounds(NULL),
57 fTracking(false)
59 _InitData(layout);
63 BMenuBar::BMenuBar(const char* name, menu_layout layout, uint32 flags)
65 BMenu(BRect(), name, B_FOLLOW_NONE,
66 flags | B_WILL_DRAW | B_FRAME_EVENTS | B_SUPPORTS_LAYOUT,
67 layout, false),
68 fBorder(B_BORDER_FRAME),
69 fTrackingPID(-1),
70 fPrevFocusToken(-1),
71 fMenuSem(-1),
72 fLastBounds(NULL),
73 fTracking(false)
75 _InitData(layout);
79 BMenuBar::BMenuBar(BMessage* archive)
81 BMenu(archive),
82 fBorder(B_BORDER_FRAME),
83 fTrackingPID(-1),
84 fPrevFocusToken(-1),
85 fMenuSem(-1),
86 fLastBounds(NULL),
87 fTracking(false)
89 int32 border;
91 if (archive->FindInt32("_border", &border) == B_OK)
92 SetBorder((menu_bar_border)border);
94 menu_layout layout = B_ITEMS_IN_COLUMN;
95 archive->FindInt32("_layout", (int32*)&layout);
97 _InitData(layout);
101 BMenuBar::~BMenuBar()
103 if (fTracking) {
104 status_t dummy;
105 wait_for_thread(fTrackingPID, &dummy);
108 delete fLastBounds;
112 BArchivable*
113 BMenuBar::Instantiate(BMessage* data)
115 if (validate_instantiation(data, "BMenuBar"))
116 return new BMenuBar(data);
118 return NULL;
122 status_t
123 BMenuBar::Archive(BMessage* data, bool deep) const
125 status_t err = BMenu::Archive(data, deep);
127 if (err < B_OK)
128 return err;
130 if (Border() != B_BORDER_FRAME)
131 err = data->AddInt32("_border", Border());
133 return err;
137 // #pragma mark -
140 void
141 BMenuBar::AttachedToWindow()
143 _Install(Window());
144 Window()->SetKeyMenuBar(this);
146 BMenu::AttachedToWindow();
148 *fLastBounds = Bounds();
152 void
153 BMenuBar::DetachedFromWindow()
155 BMenu::DetachedFromWindow();
159 void
160 BMenuBar::AllAttached()
162 BMenu::AllAttached();
166 void
167 BMenuBar::AllDetached()
169 BMenu::AllDetached();
173 void
174 BMenuBar::WindowActivated(bool state)
176 BView::WindowActivated(state);
180 void
181 BMenuBar::MakeFocus(bool state)
183 BMenu::MakeFocus(state);
187 // #pragma mark -
190 void
191 BMenuBar::ResizeToPreferred()
193 BMenu::ResizeToPreferred();
197 void
198 BMenuBar::GetPreferredSize(float* width, float* height)
200 BMenu::GetPreferredSize(width, height);
204 BSize
205 BMenuBar::MinSize()
207 return BMenu::MinSize();
211 BSize
212 BMenuBar::MaxSize()
214 BSize size = BMenu::MaxSize();
215 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
216 BSize(B_SIZE_UNLIMITED, size.height));
220 BSize
221 BMenuBar::PreferredSize()
223 return BMenu::PreferredSize();
227 void
228 BMenuBar::FrameMoved(BPoint newPosition)
230 BMenu::FrameMoved(newPosition);
234 void
235 BMenuBar::FrameResized(float newWidth, float newHeight)
237 // invalidate right border
238 if (newWidth != fLastBounds->Width()) {
239 BRect rect(min_c(fLastBounds->right, newWidth), 0,
240 max_c(fLastBounds->right, newWidth), newHeight);
241 Invalidate(rect);
244 // invalidate bottom border
245 if (newHeight != fLastBounds->Height()) {
246 BRect rect(0, min_c(fLastBounds->bottom, newHeight) - 1,
247 newWidth, max_c(fLastBounds->bottom, newHeight));
248 Invalidate(rect);
251 fLastBounds->Set(0, 0, newWidth, newHeight);
253 BMenu::FrameResized(newWidth, newHeight);
257 // #pragma mark -
260 void
261 BMenuBar::Show()
263 BView::Show();
267 void
268 BMenuBar::Hide()
270 BView::Hide();
274 void
275 BMenuBar::Draw(BRect updateRect)
277 if (_RelayoutIfNeeded()) {
278 Invalidate();
279 return;
282 BRect rect(Bounds());
283 rgb_color base = LowColor();
284 uint32 flags = 0;
286 be_control_look->DrawBorder(this, rect, updateRect, base,
287 B_PLAIN_BORDER, flags, BControlLook::B_BOTTOM_BORDER);
289 be_control_look->DrawMenuBarBackground(this, rect, updateRect, base);
291 _DrawItems(updateRect);
295 // #pragma mark -
298 void
299 BMenuBar::MessageReceived(BMessage* msg)
301 BMenu::MessageReceived(msg);
305 void
306 BMenuBar::MouseDown(BPoint where)
308 if (fTracking)
309 return;
311 uint32 buttons;
312 GetMouse(&where, &buttons);
314 BWindow* window = Window();
315 if (!window->IsActive() || !window->IsFront()) {
316 if ((mouse_mode() == B_FOCUS_FOLLOWS_MOUSE)
317 || ((mouse_mode() == B_CLICK_TO_FOCUS_MOUSE)
318 && ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0))) {
319 // right-click to bring-to-front and send-to-back
320 // (might cause some regressions in FFM)
321 window->Activate();
322 window->UpdateIfNeeded();
326 StartMenuBar(-1, false, false);
330 void
331 BMenuBar::MouseUp(BPoint where)
333 BView::MouseUp(where);
337 // #pragma mark -
340 BHandler*
341 BMenuBar::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
342 int32 form, const char* property)
344 return BMenu::ResolveSpecifier(msg, index, specifier, form, property);
348 status_t
349 BMenuBar::GetSupportedSuites(BMessage* data)
351 return BMenu::GetSupportedSuites(data);
355 // #pragma mark -
358 void
359 BMenuBar::SetBorder(menu_bar_border border)
361 fBorder = border;
365 menu_bar_border
366 BMenuBar::Border() const
368 return fBorder;
372 // #pragma mark -
375 status_t
376 BMenuBar::Perform(perform_code code, void* _data)
378 switch (code) {
379 case PERFORM_CODE_MIN_SIZE:
380 ((perform_data_min_size*)_data)->return_value
381 = BMenuBar::MinSize();
382 return B_OK;
384 case PERFORM_CODE_MAX_SIZE:
385 ((perform_data_max_size*)_data)->return_value
386 = BMenuBar::MaxSize();
387 return B_OK;
389 case PERFORM_CODE_PREFERRED_SIZE:
390 ((perform_data_preferred_size*)_data)->return_value
391 = BMenuBar::PreferredSize();
392 return B_OK;
394 case PERFORM_CODE_LAYOUT_ALIGNMENT:
395 ((perform_data_layout_alignment*)_data)->return_value
396 = BMenuBar::LayoutAlignment();
397 return B_OK;
399 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
400 ((perform_data_has_height_for_width*)_data)->return_value
401 = BMenuBar::HasHeightForWidth();
402 return B_OK;
404 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
406 perform_data_get_height_for_width* data
407 = (perform_data_get_height_for_width*)_data;
408 BMenuBar::GetHeightForWidth(data->width, &data->min, &data->max,
409 &data->preferred);
410 return B_OK;
413 case PERFORM_CODE_SET_LAYOUT:
415 perform_data_set_layout* data = (perform_data_set_layout*)_data;
416 BMenuBar::SetLayout(data->layout);
417 return B_OK;
420 case PERFORM_CODE_LAYOUT_INVALIDATED:
422 perform_data_layout_invalidated* data
423 = (perform_data_layout_invalidated*)_data;
424 BMenuBar::LayoutInvalidated(data->descendants);
425 return B_OK;
428 case PERFORM_CODE_DO_LAYOUT:
430 BMenuBar::DoLayout();
431 return B_OK;
435 return BMenu::Perform(code, _data);
439 // #pragma mark -
442 void BMenuBar::_ReservedMenuBar1() {}
443 void BMenuBar::_ReservedMenuBar2() {}
444 void BMenuBar::_ReservedMenuBar3() {}
445 void BMenuBar::_ReservedMenuBar4() {}
448 BMenuBar &
449 BMenuBar::operator=(const BMenuBar &)
451 return *this;
455 // #pragma mark -
458 void
459 BMenuBar::StartMenuBar(int32 menuIndex, bool sticky, bool showMenu,
460 BRect* specialRect)
462 if (fTracking)
463 return;
465 BWindow* window = Window();
466 if (window == NULL)
467 debugger("MenuBar must be added to a window before it can be used.");
469 BAutolock lock(window);
470 if (!lock.IsLocked())
471 return;
473 fPrevFocusToken = -1;
474 fTracking = true;
476 // We are called from the window's thread,
477 // so let's call MenusBeginning() directly
478 window->MenusBeginning();
480 fMenuSem = create_sem(0, "window close sem");
481 _set_menu_sem_(window, fMenuSem);
483 fTrackingPID = spawn_thread(_TrackTask, "menu_tracking",
484 B_DISPLAY_PRIORITY, NULL);
485 if (fTrackingPID >= 0) {
486 menubar_data data;
487 data.menuBar = this;
488 data.menuIndex = menuIndex;
489 data.sticky = sticky;
490 data.showMenu = showMenu;
491 data.useRect = specialRect != NULL;
492 if (data.useRect)
493 data.rect = *specialRect;
495 resume_thread(fTrackingPID);
496 send_data(fTrackingPID, 0, &data, sizeof(data));
497 } else {
498 fTracking = false;
499 _set_menu_sem_(window, B_NO_MORE_SEMS);
500 delete_sem(fMenuSem);
505 /*static*/ int32
506 BMenuBar::_TrackTask(void* arg)
508 menubar_data data;
509 thread_id id;
510 receive_data(&id, &data, sizeof(data));
512 BMenuBar* menuBar = data.menuBar;
513 if (data.useRect)
514 menuBar->fExtraRect = &data.rect;
515 menuBar->_SetStickyMode(data.sticky);
517 int32 action;
518 menuBar->_Track(&action, data.menuIndex, data.showMenu);
520 menuBar->fTracking = false;
521 menuBar->fExtraRect = NULL;
523 // We aren't the BWindow thread, so don't call MenusEnded() directly
524 BWindow* window = menuBar->Window();
525 window->PostMessage(_MENUS_DONE_);
527 _set_menu_sem_(window, B_BAD_SEM_ID);
528 delete_sem(menuBar->fMenuSem);
529 menuBar->fMenuSem = B_BAD_SEM_ID;
531 return 0;
535 BMenuItem*
536 BMenuBar::_Track(int32* action, int32 startIndex, bool showMenu)
538 // TODO: Cleanup, merge some "if" blocks if possible
539 BMenuItem* item = NULL;
540 fState = MENU_STATE_TRACKING;
541 fChosenItem = NULL;
542 // we will use this for keyboard selection
544 BPoint where;
545 uint32 buttons;
546 if (LockLooper()) {
547 if (startIndex != -1) {
548 be_app->ObscureCursor();
549 _SelectItem(ItemAt(startIndex), true, false);
551 GetMouse(&where, &buttons);
552 UnlockLooper();
555 while (fState != MENU_STATE_CLOSED) {
556 bigtime_t snoozeAmount = 40000;
557 if (!LockLooper())
558 break;
560 item = dynamic_cast<_BMCMenuBar_*>(this) != NULL ? ItemAt(0)
561 : _HitTestItems(where, B_ORIGIN);
563 if (_OverSubmenu(fSelected, ConvertToScreen(where))
564 || fState == MENU_STATE_KEY_TO_SUBMENU) {
565 // call _Track() from the selected sub-menu when the mouse cursor
566 // is over its window
567 BMenu* submenu = fSelected->Submenu();
568 UnlockLooper();
569 snoozeAmount = 30000;
570 submenu->_SetStickyMode(_IsStickyMode());
571 int localAction;
572 fChosenItem = submenu->_Track(&localAction);
574 // The mouse could have meen moved since the last time we
575 // checked its position, or buttons might have been pressed.
576 // Unfortunately our child menus don't tell
577 // us the new position.
578 // TODO: Maybe have a shared struct between all menus
579 // where to store the current mouse position ?
580 // (Or just use the BView mouse hooks)
581 BPoint newWhere;
582 if (LockLooper()) {
583 GetMouse(&newWhere, &buttons);
584 UnlockLooper();
587 // Needed to make BMenuField child menus "sticky"
588 // (see ticket #953)
589 if (localAction == MENU_STATE_CLOSED) {
590 if (fExtraRect != NULL && fExtraRect->Contains(where)
591 && point_distance(newWhere, where) < 9) {
592 // 9 = 3 pixels ^ 2 (since point_distance() returns the
593 // square of the distance)
594 _SetStickyMode(true);
595 fExtraRect = NULL;
596 } else
597 fState = MENU_STATE_CLOSED;
599 if (!LockLooper())
600 break;
601 } else if (item != NULL) {
602 if (item->Submenu() != NULL && item != fSelected) {
603 if (item->Submenu()->Window() == NULL) {
604 // open the menu if it's not opened yet
605 _SelectItem(item);
606 } else {
607 // Menu was already opened, close it and bail
608 _SelectItem(NULL);
609 fState = MENU_STATE_CLOSED;
610 fChosenItem = NULL;
612 } else {
613 // No submenu, just select the item
614 _SelectItem(item);
616 } else if (item == NULL && fSelected != NULL
617 && !_IsStickyMode() && Bounds().Contains(where)) {
618 _SelectItem(NULL);
619 fState = MENU_STATE_TRACKING;
622 UnlockLooper();
624 if (fState != MENU_STATE_CLOSED) {
625 BPoint newWhere = where;
626 uint32 newButtons = buttons;
628 do {
629 // If user doesn't move the mouse or change buttons loop
630 // here so that we don't interfere with keyboard menu
631 // navigation
632 snooze(snoozeAmount);
633 if (!LockLooper())
634 break;
636 GetMouse(&newWhere, &newButtons);
637 UnlockLooper();
638 } while (newWhere == where && newButtons == buttons
639 && fState == MENU_STATE_TRACKING);
641 if (newButtons != 0 && _IsStickyMode()) {
642 if (item == NULL || (item->Submenu() != NULL
643 && item->Submenu()->Window() != NULL)) {
644 // clicked outside the menu bar or on item with already
645 // open sub menu
646 fState = MENU_STATE_CLOSED;
647 } else
648 _SetStickyMode(false);
649 } else if (newButtons == 0 && !_IsStickyMode()) {
650 if ((fSelected != NULL && fSelected->Submenu() == NULL)
651 || item == NULL) {
652 // clicked on an item without a submenu or clicked and
653 // released the mouse button outside the menu bar
654 fChosenItem = fSelected;
655 fState = MENU_STATE_CLOSED;
656 } else
657 _SetStickyMode(true);
659 where = newWhere;
660 buttons = newButtons;
664 if (LockLooper()) {
665 if (fSelected != NULL)
666 _SelectItem(NULL);
668 if (fChosenItem != NULL)
669 fChosenItem->Invoke();
671 _RestoreFocus();
672 UnlockLooper();
675 if (_IsStickyMode())
676 _SetStickyMode(false);
678 _DeleteMenuWindow();
680 if (action != NULL)
681 *action = fState;
683 return fChosenItem;
687 void
688 BMenuBar::_StealFocus()
690 // We already stole the focus, don't do anything
691 if (fPrevFocusToken != -1)
692 return;
694 BWindow* window = Window();
695 if (window != NULL && window->Lock()) {
696 BView* focusView = window->CurrentFocus();
697 if (focusView != NULL && focusView != this)
698 fPrevFocusToken = _get_object_token_(focusView);
699 MakeFocus();
700 window->Unlock();
705 void
706 BMenuBar::_RestoreFocus()
708 BWindow* window = Window();
709 if (window != NULL && window->Lock()) {
710 BHandler* handler = NULL;
711 if (fPrevFocusToken != -1
712 && gDefaultTokens.GetToken(fPrevFocusToken, B_HANDLER_TOKEN,
713 (void**)&handler) == B_OK) {
714 BView* view = dynamic_cast<BView*>(handler);
715 if (view != NULL && view->Window() == window)
716 view->MakeFocus();
717 } else if (IsFocus())
718 MakeFocus(false);
720 fPrevFocusToken = -1;
721 window->Unlock();
726 void
727 BMenuBar::_InitData(menu_layout layout)
729 fLastBounds = new BRect(Bounds());
730 SetItemMargins(8, 2, 8, 2);
731 _SetIgnoreHidden(true);