2 * Copyright 2001-2006, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (burton666@libero.it)
11 #include <Application.h>
14 #include <PopUpMenu.h>
19 #include <binary_compatibility/Interface.h>
22 struct popup_menu_data
{
39 BPopUpMenu::BPopUpMenu(const char* name
, bool radioMode
, bool labelFromMarked
,
51 SetLabelFromMarked(true);
55 BPopUpMenu::BPopUpMenu(BMessage
* archive
)
65 BPopUpMenu::~BPopUpMenu()
67 if (fTrackThread
>= 0) {
69 while (wait_for_thread(fTrackThread
, &status
) == B_INTERRUPTED
)
76 BPopUpMenu::Archive(BMessage
* data
, bool deep
) const
78 return BMenu::Archive(data
, deep
);
83 BPopUpMenu::Instantiate(BMessage
* data
)
85 if (validate_instantiation(data
, "BPopUpMenu"))
86 return new BPopUpMenu(data
);
93 BPopUpMenu::Go(BPoint where
, bool deliversMessage
, bool openAnyway
, bool async
)
95 return _Go(where
, deliversMessage
, openAnyway
, NULL
, async
);
100 BPopUpMenu::Go(BPoint where
, bool deliversMessage
, bool openAnyway
,
101 BRect clickToOpen
, bool async
)
103 return _Go(where
, deliversMessage
, openAnyway
, &clickToOpen
, async
);
108 BPopUpMenu::MessageReceived(BMessage
* message
)
110 BMenu::MessageReceived(message
);
115 BPopUpMenu::MouseDown(BPoint point
)
117 BView::MouseDown(point
);
122 BPopUpMenu::MouseUp(BPoint point
)
124 BView::MouseUp(point
);
129 BPopUpMenu::MouseMoved(BPoint point
, uint32 code
, const BMessage
* message
)
131 BView::MouseMoved(point
, code
, message
);
136 BPopUpMenu::AttachedToWindow()
138 BMenu::AttachedToWindow();
143 BPopUpMenu::DetachedFromWindow()
145 BMenu::DetachedFromWindow();
150 BPopUpMenu::FrameMoved(BPoint newPosition
)
152 BMenu::FrameMoved(newPosition
);
157 BPopUpMenu::FrameResized(float newWidth
, float newHeight
)
159 BMenu::FrameResized(newWidth
, newHeight
);
164 BPopUpMenu::ResolveSpecifier(BMessage
* message
, int32 index
,
165 BMessage
* specifier
, int32 form
, const char* property
)
167 return BMenu::ResolveSpecifier(message
, index
, specifier
, form
, property
);
172 BPopUpMenu::GetSupportedSuites(BMessage
* data
)
174 return BMenu::GetSupportedSuites(data
);
179 BPopUpMenu::Perform(perform_code code
, void* _data
)
182 case PERFORM_CODE_MIN_SIZE
:
183 ((perform_data_min_size
*)_data
)->return_value
184 = BPopUpMenu::MinSize();
186 case PERFORM_CODE_MAX_SIZE
:
187 ((perform_data_max_size
*)_data
)->return_value
188 = BPopUpMenu::MaxSize();
190 case PERFORM_CODE_PREFERRED_SIZE
:
191 ((perform_data_preferred_size
*)_data
)->return_value
192 = BPopUpMenu::PreferredSize();
194 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
195 ((perform_data_layout_alignment
*)_data
)->return_value
196 = BPopUpMenu::LayoutAlignment();
198 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
199 ((perform_data_has_height_for_width
*)_data
)->return_value
200 = BPopUpMenu::HasHeightForWidth();
202 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
204 perform_data_get_height_for_width
* data
205 = (perform_data_get_height_for_width
*)_data
;
206 BPopUpMenu::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
210 case PERFORM_CODE_SET_LAYOUT
:
212 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
213 BPopUpMenu::SetLayout(data
->layout
);
216 case PERFORM_CODE_LAYOUT_INVALIDATED
:
218 perform_data_layout_invalidated
* data
219 = (perform_data_layout_invalidated
*)_data
;
220 BPopUpMenu::LayoutInvalidated(data
->descendants
);
223 case PERFORM_CODE_DO_LAYOUT
:
225 BPopUpMenu::DoLayout();
230 return BMenu::Perform(code
, _data
);
235 BPopUpMenu::ResizeToPreferred()
237 BMenu::ResizeToPreferred();
242 BPopUpMenu::GetPreferredSize(float* _width
, float* _height
)
244 BMenu::GetPreferredSize(_width
, _height
);
249 BPopUpMenu::MakeFocus(bool state
)
251 BMenu::MakeFocus(state
);
256 BPopUpMenu::AllAttached()
258 BMenu::AllAttached();
263 BPopUpMenu::AllDetached()
265 BMenu::AllDetached();
270 BPopUpMenu::SetAsyncAutoDestruct(bool on
)
277 BPopUpMenu::AsyncAutoDestruct() const
279 return fAutoDestruct
;
284 BPopUpMenu::ScreenLocation()
286 // This case is when the BPopUpMenu is standalone
290 // This case is when the BPopUpMenu is inside a BMenuField
291 BMenuItem
* superItem
= Superitem();
292 BMenu
* superMenu
= Supermenu();
293 BMenuItem
* selectedItem
= FindItem(superItem
->Label());
294 BPoint point
= superItem
->Frame().LeftTop();
296 superMenu
->ConvertToScreen(&point
);
298 if (selectedItem
!= NULL
) {
299 while (selectedItem
->Menu() != this
300 && selectedItem
->Menu()->Superitem() != NULL
) {
301 selectedItem
= selectedItem
->Menu()->Superitem();
303 point
.y
-= selectedItem
->Frame().top
;
310 // #pragma mark - private methods
313 void BPopUpMenu::_ReservedPopUpMenu1() {}
314 void BPopUpMenu::_ReservedPopUpMenu2() {}
315 void BPopUpMenu::_ReservedPopUpMenu3() {}
319 BPopUpMenu::operator=(const BPopUpMenu
& other
)
326 BPopUpMenu::_Go(BPoint where
, bool autoInvoke
, bool startOpened
,
327 BRect
* _specialRect
, bool async
)
329 if (fTrackThread
>= B_OK
) {
330 // we already have an active menu, wait for it to go away before
333 while (wait_for_thread(fTrackThread
, &unused
) == B_INTERRUPTED
)
337 popup_menu_data
* data
= new (std::nothrow
) popup_menu_data
;
341 sem_id sem
= create_sem(0, "window close lock");
347 // Get a pointer to the window from which Go() was called
349 = dynamic_cast<BWindow
*>(BLooper::LooperForThread(find_thread(NULL
)));
350 data
->window
= window
;
352 // Asynchronous menu: we set the BWindow menu's semaphore
353 // and let BWindow block when needed
354 if (async
&& window
!= NULL
)
355 _set_menu_sem_(window
, sem
);
358 data
->autoInvoke
= autoInvoke
;
359 data
->useRect
= _specialRect
!= NULL
;
360 if (_specialRect
!= NULL
)
361 data
->rect
= *_specialRect
;
364 data
->startOpened
= startOpened
;
365 data
->selected
= NULL
;
368 // Spawn the tracking thread
369 fTrackThread
= spawn_thread(_thread_entry
, "popup", B_DISPLAY_PRIORITY
,
371 if (fTrackThread
< B_OK
) {
372 // Something went wrong. Cleanup and return NULL
374 if (async
&& window
!= NULL
)
375 _set_menu_sem_(window
, B_BAD_SEM_ID
);
380 resume_thread(fTrackThread
);
383 return _WaitMenu(data
);
391 BPopUpMenu::_thread_entry(void* menuData
)
393 popup_menu_data
* data
= static_cast<popup_menu_data
*>(menuData
);
394 BPopUpMenu
* menu
= data
->object
;
400 data
->selected
= menu
->_StartTrack(data
->where
, data
->autoInvoke
,
401 data
->startOpened
, rect
);
403 // Reset the window menu semaphore
404 if (data
->async
&& data
->window
)
405 _set_menu_sem_(data
->window
, B_BAD_SEM_ID
);
407 delete_sem(data
->lock
);
409 // Commit suicide if needed
410 if (data
->async
&& menu
->fAutoDestruct
) {
411 menu
->fTrackThread
= -1;
423 BPopUpMenu::_StartTrack(BPoint where
, bool autoInvoke
, bool startOpened
,
426 // I know, this doesn't look senseful, but don't be fooled,
427 // fUseWhere is used in ScreenLocation(), which is a virtual
428 // called by BMenu::Track()
432 // Determine when mouse-down-up will be taken as a 'press',
433 // rather than a 'click'
434 bigtime_t clickMaxTime
= 0;
435 get_click_speed(&clickMaxTime
);
436 clickMaxTime
+= system_time();
438 // Show the menu's window
441 BMenuItem
* result
= Track(startOpened
, _specialRect
);
443 // If it was a click, keep the menu open and tracking
444 if (system_time() <= clickMaxTime
)
445 result
= Track(true, _specialRect
);
446 if (result
!= NULL
&& autoInvoke
)
452 be_app
->ShowCursor();
459 BPopUpMenu::_WaitMenu(void* _data
)
461 popup_menu_data
* data
= static_cast<popup_menu_data
*>(_data
);
462 BWindow
* window
= data
->window
;
463 sem_id sem
= data
->lock
;
464 if (window
!= NULL
) {
465 while (acquire_sem_etc(sem
, 1, B_TIMEOUT
, 50000) != B_BAD_SEM_ID
)
466 window
->UpdateIfNeeded();
470 while (wait_for_thread(fTrackThread
, &unused
) == B_INTERRUPTED
)
475 BMenuItem
* selected
= data
->selected
;
476 // data->selected is filled by the tracking thread