btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / interface / PopUpMenu.cpp
blob7b94b380ab6d98a32a45d0ffd3fee9c646bb08b9
1 /*
2 * Copyright 2001-2006, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (burton666@libero.it)
8 */
11 #include <Application.h>
12 #include <Looper.h>
13 #include <MenuItem.h>
14 #include <PopUpMenu.h>
15 #include <Window.h>
17 #include <new>
19 #include <binary_compatibility/Interface.h>
22 struct popup_menu_data {
23 BPopUpMenu* object;
24 BWindow* window;
25 BMenuItem* selected;
27 BPoint where;
28 BRect rect;
30 bool async;
31 bool autoInvoke;
32 bool startOpened;
33 bool useRect;
35 sem_id lock;
39 BPopUpMenu::BPopUpMenu(const char* name, bool radioMode, bool labelFromMarked,
40 menu_layout layout)
42 BMenu(name, layout),
43 fUseWhere(false),
44 fAutoDestruct(false),
45 fTrackThread(-1)
47 if (radioMode)
48 SetRadioMode(true);
50 if (labelFromMarked)
51 SetLabelFromMarked(true);
55 BPopUpMenu::BPopUpMenu(BMessage* archive)
57 BMenu(archive),
58 fUseWhere(false),
59 fAutoDestruct(false),
60 fTrackThread(-1)
65 BPopUpMenu::~BPopUpMenu()
67 if (fTrackThread >= 0) {
68 status_t status;
69 while (wait_for_thread(fTrackThread, &status) == B_INTERRUPTED)
75 status_t
76 BPopUpMenu::Archive(BMessage* data, bool deep) const
78 return BMenu::Archive(data, deep);
82 BArchivable*
83 BPopUpMenu::Instantiate(BMessage* data)
85 if (validate_instantiation(data, "BPopUpMenu"))
86 return new BPopUpMenu(data);
88 return NULL;
92 BMenuItem*
93 BPopUpMenu::Go(BPoint where, bool deliversMessage, bool openAnyway, bool async)
95 return _Go(where, deliversMessage, openAnyway, NULL, async);
99 BMenuItem*
100 BPopUpMenu::Go(BPoint where, bool deliversMessage, bool openAnyway,
101 BRect clickToOpen, bool async)
103 return _Go(where, deliversMessage, openAnyway, &clickToOpen, async);
107 void
108 BPopUpMenu::MessageReceived(BMessage* message)
110 BMenu::MessageReceived(message);
114 void
115 BPopUpMenu::MouseDown(BPoint point)
117 BView::MouseDown(point);
121 void
122 BPopUpMenu::MouseUp(BPoint point)
124 BView::MouseUp(point);
128 void
129 BPopUpMenu::MouseMoved(BPoint point, uint32 code, const BMessage* message)
131 BView::MouseMoved(point, code, message);
135 void
136 BPopUpMenu::AttachedToWindow()
138 BMenu::AttachedToWindow();
142 void
143 BPopUpMenu::DetachedFromWindow()
145 BMenu::DetachedFromWindow();
149 void
150 BPopUpMenu::FrameMoved(BPoint newPosition)
152 BMenu::FrameMoved(newPosition);
156 void
157 BPopUpMenu::FrameResized(float newWidth, float newHeight)
159 BMenu::FrameResized(newWidth, newHeight);
163 BHandler*
164 BPopUpMenu::ResolveSpecifier(BMessage* message, int32 index,
165 BMessage* specifier, int32 form, const char* property)
167 return BMenu::ResolveSpecifier(message, index, specifier, form, property);
171 status_t
172 BPopUpMenu::GetSupportedSuites(BMessage* data)
174 return BMenu::GetSupportedSuites(data);
178 status_t
179 BPopUpMenu::Perform(perform_code code, void* _data)
181 switch (code) {
182 case PERFORM_CODE_MIN_SIZE:
183 ((perform_data_min_size*)_data)->return_value
184 = BPopUpMenu::MinSize();
185 return B_OK;
186 case PERFORM_CODE_MAX_SIZE:
187 ((perform_data_max_size*)_data)->return_value
188 = BPopUpMenu::MaxSize();
189 return B_OK;
190 case PERFORM_CODE_PREFERRED_SIZE:
191 ((perform_data_preferred_size*)_data)->return_value
192 = BPopUpMenu::PreferredSize();
193 return B_OK;
194 case PERFORM_CODE_LAYOUT_ALIGNMENT:
195 ((perform_data_layout_alignment*)_data)->return_value
196 = BPopUpMenu::LayoutAlignment();
197 return B_OK;
198 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
199 ((perform_data_has_height_for_width*)_data)->return_value
200 = BPopUpMenu::HasHeightForWidth();
201 return B_OK;
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,
207 &data->preferred);
208 return B_OK;
210 case PERFORM_CODE_SET_LAYOUT:
212 perform_data_set_layout* data = (perform_data_set_layout*)_data;
213 BPopUpMenu::SetLayout(data->layout);
214 return B_OK;
216 case PERFORM_CODE_LAYOUT_INVALIDATED:
218 perform_data_layout_invalidated* data
219 = (perform_data_layout_invalidated*)_data;
220 BPopUpMenu::LayoutInvalidated(data->descendants);
221 return B_OK;
223 case PERFORM_CODE_DO_LAYOUT:
225 BPopUpMenu::DoLayout();
226 return B_OK;
230 return BMenu::Perform(code, _data);
234 void
235 BPopUpMenu::ResizeToPreferred()
237 BMenu::ResizeToPreferred();
241 void
242 BPopUpMenu::GetPreferredSize(float* _width, float* _height)
244 BMenu::GetPreferredSize(_width, _height);
248 void
249 BPopUpMenu::MakeFocus(bool state)
251 BMenu::MakeFocus(state);
255 void
256 BPopUpMenu::AllAttached()
258 BMenu::AllAttached();
262 void
263 BPopUpMenu::AllDetached()
265 BMenu::AllDetached();
269 void
270 BPopUpMenu::SetAsyncAutoDestruct(bool on)
272 fAutoDestruct = on;
276 bool
277 BPopUpMenu::AsyncAutoDestruct() const
279 return fAutoDestruct;
283 BPoint
284 BPopUpMenu::ScreenLocation()
286 // This case is when the BPopUpMenu is standalone
287 if (fUseWhere)
288 return fWhere;
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;
306 return point;
310 // #pragma mark - private methods
313 void BPopUpMenu::_ReservedPopUpMenu1() {}
314 void BPopUpMenu::_ReservedPopUpMenu2() {}
315 void BPopUpMenu::_ReservedPopUpMenu3() {}
318 BPopUpMenu&
319 BPopUpMenu::operator=(const BPopUpMenu& other)
321 return *this;
325 BMenuItem*
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
331 // spawning another
332 status_t unused;
333 while (wait_for_thread(fTrackThread, &unused) == B_INTERRUPTED)
337 popup_menu_data* data = new (std::nothrow) popup_menu_data;
338 if (data == NULL)
339 return NULL;
341 sem_id sem = create_sem(0, "window close lock");
342 if (sem < B_OK) {
343 delete data;
344 return NULL;
347 // Get a pointer to the window from which Go() was called
348 BWindow* window
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);
357 data->object = this;
358 data->autoInvoke = autoInvoke;
359 data->useRect = _specialRect != NULL;
360 if (_specialRect != NULL)
361 data->rect = *_specialRect;
362 data->async = async;
363 data->where = where;
364 data->startOpened = startOpened;
365 data->selected = NULL;
366 data->lock = sem;
368 // Spawn the tracking thread
369 fTrackThread = spawn_thread(_thread_entry, "popup", B_DISPLAY_PRIORITY,
370 data);
371 if (fTrackThread < B_OK) {
372 // Something went wrong. Cleanup and return NULL
373 delete_sem(sem);
374 if (async && window != NULL)
375 _set_menu_sem_(window, B_BAD_SEM_ID);
376 delete data;
377 return NULL;
380 resume_thread(fTrackThread);
382 if (!async)
383 return _WaitMenu(data);
385 return 0;
389 /* static */
390 int32
391 BPopUpMenu::_thread_entry(void* menuData)
393 popup_menu_data* data = static_cast<popup_menu_data*>(menuData);
394 BPopUpMenu* menu = data->object;
395 BRect* rect = NULL;
397 if (data->useRect)
398 rect = &data->rect;
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;
412 delete menu;
415 if (data->async)
416 delete data;
418 return 0;
422 BMenuItem*
423 BPopUpMenu::_StartTrack(BPoint where, bool autoInvoke, bool startOpened,
424 BRect* _specialRect)
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()
429 fWhere = where;
430 fUseWhere = true;
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
439 Show();
440 snooze(50000);
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)
447 result->Invoke();
449 fUseWhere = false;
451 Hide();
452 be_app->ShowCursor();
454 return result;
458 BMenuItem*
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();
469 status_t unused;
470 while (wait_for_thread(fTrackThread, &unused) == B_INTERRUPTED)
473 fTrackThread = -1;
475 BMenuItem* selected = data->selected;
476 // data->selected is filled by the tracking thread
478 delete data;
480 return selected;