1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku, Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: PopUpMenu.cpp 23 // Author: Marc Flerackers (mflerackers@androme.be) 24 // Stefano Ceccherini (burton666@libero.it) 25 // Description: BPopUpMenu represents a menu that pops up when you 26 // activate it. 27 //------------------------------------------------------------------------------ 28 #include <Application.h> 29 #include <Looper.h> 30 #include <MenuItem.h> 31 #include <PopUpMenu.h> 32 #include <Window.h> 33 34 35 struct popup_menu_data 36 { 37 BPopUpMenu *object; 38 BWindow *window; 39 BMenuItem *selected; 40 41 BPoint where; 42 BRect rect; 43 44 bool async; 45 bool autoInvoke; 46 bool startOpened; 47 bool useRect; 48 49 sem_id lock; 50 }; 51 52 53 BPopUpMenu::BPopUpMenu(const char *title, bool radioMode, bool autoRename, 54 menu_layout layout) 55 : 56 BMenu(title, layout), 57 fUseWhere(false), 58 fAutoDestruct(false), 59 fTrackThread(-1) 60 { 61 if (radioMode) 62 SetRadioMode(true); 63 64 if (autoRename) 65 SetLabelFromMarked(true); 66 } 67 68 69 BPopUpMenu::BPopUpMenu(BMessage *archive) 70 : 71 BMenu(archive), 72 fUseWhere(false), 73 fAutoDestruct(false), 74 fTrackThread(-1) 75 { 76 } 77 78 79 BPopUpMenu::~BPopUpMenu() 80 { 81 if (fTrackThread >= 0) { 82 status_t status; 83 while (wait_for_thread(fTrackThread, &status) == B_INTERRUPTED) 84 ; 85 86 } 87 } 88 89 90 status_t 91 BPopUpMenu::Archive(BMessage *data, bool deep) const 92 { 93 return BMenu::Archive(data, deep); 94 } 95 96 97 BArchivable * 98 BPopUpMenu::Instantiate(BMessage *data) 99 { 100 if (validate_instantiation(data, "BPopUpMenu")) 101 return new BPopUpMenu(data); 102 103 return NULL; 104 } 105 106 107 BMenuItem * 108 BPopUpMenu::Go(BPoint where, bool delivers_message, bool open_anyway, bool async) 109 { 110 return _go(where, delivers_message, open_anyway, NULL, async); 111 } 112 113 114 BMenuItem * 115 BPopUpMenu::Go(BPoint where, bool deliversMessage, bool openAnyway, 116 BRect clickToOpen, bool async) 117 { 118 return _go(where, deliversMessage, openAnyway, &clickToOpen, async); 119 } 120 121 122 void 123 BPopUpMenu::MessageReceived(BMessage *msg) 124 { 125 BMenu::MessageReceived(msg); 126 } 127 128 129 void 130 BPopUpMenu::MouseDown(BPoint point) 131 { 132 BView::MouseDown(point); 133 } 134 135 136 void 137 BPopUpMenu::MouseUp(BPoint point) 138 { 139 BView::MouseUp(point); 140 } 141 142 143 void 144 BPopUpMenu::MouseMoved(BPoint point, uint32 code, const BMessage *msg) 145 { 146 BView::MouseMoved(point, code, msg); 147 } 148 149 150 void 151 BPopUpMenu::AttachedToWindow() 152 { 153 BMenu::AttachedToWindow(); 154 } 155 156 157 void 158 BPopUpMenu::DetachedFromWindow() 159 { 160 BMenu::DetachedFromWindow(); 161 } 162 163 164 void 165 BPopUpMenu::FrameMoved(BPoint newPosition) 166 { 167 BMenu::FrameMoved(newPosition); 168 } 169 170 171 void 172 BPopUpMenu::FrameResized(float newWidth, float newHeight) 173 { 174 BMenu::FrameResized(newWidth, newHeight); 175 } 176 177 178 BHandler * 179 BPopUpMenu::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 180 int32 form, const char *property) 181 { 182 return BMenu::ResolveSpecifier(msg, index, specifier, form, property); 183 } 184 185 186 status_t 187 BPopUpMenu::GetSupportedSuites(BMessage *data) 188 { 189 return BMenu::GetSupportedSuites(data); 190 } 191 192 193 status_t 194 BPopUpMenu::Perform(perform_code d, void *arg) 195 { 196 return BMenu::Perform(d, arg); 197 } 198 199 200 void 201 BPopUpMenu::ResizeToPreferred() 202 { 203 BMenu::ResizeToPreferred(); 204 } 205 206 207 void 208 BPopUpMenu::GetPreferredSize(float *_width, float *_height) 209 { 210 BMenu::GetPreferredSize(_width, _height); 211 } 212 213 214 void 215 BPopUpMenu::MakeFocus(bool state) 216 { 217 BMenu::MakeFocus(state); 218 } 219 220 221 void 222 BPopUpMenu::AllAttached() 223 { 224 BMenu::AllAttached(); 225 } 226 227 228 void 229 BPopUpMenu::AllDetached() 230 { 231 BMenu::AllDetached(); 232 } 233 234 235 void 236 BPopUpMenu::SetAsyncAutoDestruct(bool state) 237 { 238 fAutoDestruct = state; 239 } 240 241 242 bool 243 BPopUpMenu::AsyncAutoDestruct() const 244 { 245 return fAutoDestruct; 246 } 247 248 249 BPoint 250 BPopUpMenu::ScreenLocation() 251 { 252 if (fUseWhere) 253 return fWhere; 254 255 BMenuItem *superItem = Superitem(); 256 BMenu *superMenu = Supermenu(); 257 BMenuItem *selectedItem = FindItem(superItem->Label()); 258 BPoint point = superItem->Frame().LeftTop(); 259 260 superMenu->ConvertToScreen(&point); 261 262 if (selectedItem != NULL) 263 point.y -= selectedItem->Frame().top; 264 265 return point; 266 } 267 268 269 // #pragma mark - 270 // private methods 271 272 273 void BPopUpMenu::_ReservedPopUpMenu1() {} 274 void BPopUpMenu::_ReservedPopUpMenu2() {} 275 void BPopUpMenu::_ReservedPopUpMenu3() {} 276 277 278 BPopUpMenu & 279 BPopUpMenu::operator=(const BPopUpMenu &) 280 { 281 return *this; 282 } 283 284 285 BMenuItem * 286 BPopUpMenu::_go(BPoint where, bool autoInvoke, bool startOpened, 287 BRect *_specialRect, bool async) 288 { 289 BMenuItem *selected = NULL; 290 291 // Can't use Window(), as the BPopUpMenu isn't attached 292 BLooper *looper = BLooper::LooperForThread(find_thread(NULL)); 293 BWindow *window = dynamic_cast<BWindow *>(looper); 294 295 if (window == NULL) 296 return NULL; 297 298 popup_menu_data *data = new popup_menu_data; 299 sem_id sem = create_sem(0, "window close lock"); 300 301 // Asynchronous menu: we set the BWindow menu's semaphore 302 // and let BWindow block when needed 303 if (async) { 304 data->window = window; 305 _set_menu_sem_(window, sem); 306 } 307 308 data->object = this; 309 data->autoInvoke = autoInvoke; 310 data->useRect = _specialRect != NULL; 311 if (_specialRect != NULL) 312 data->rect = *_specialRect; 313 data->async = async; 314 data->where = where; 315 data->startOpened = startOpened; 316 data->window = NULL; 317 data->selected = selected; 318 data->lock = sem; 319 320 // Spawn the tracking thread 321 thread_id thread = spawn_thread(entry, "popup", B_NORMAL_PRIORITY, data); 322 323 if (thread >= 0) 324 resume_thread(thread); 325 else { 326 // Something went wrong. Cleanup and return NULL 327 delete_sem(sem); 328 if (async) 329 _set_menu_sem_(window, B_NO_MORE_SEMS); 330 delete data; 331 return NULL; 332 } 333 334 // Synchronous menu: we block on the sem till 335 // the other thread deletes it. 336 if (!async) { 337 if (window) { 338 // TODO: usually it's not a good idea to check for a particular error 339 // code. Though here we just want to wait till the semaphore is deleted 340 // (it will return B_BAD_SEM_ID in that case), not provide locking or whatever. 341 while (acquire_sem_etc(sem, 1, B_TIMEOUT, 50000) != B_BAD_SEM_ID) 342 window->UpdateIfNeeded(); 343 } 344 345 status_t unused; 346 while (wait_for_thread(thread, &unused) == B_INTERRUPTED) 347 ; 348 349 selected = data->selected; 350 351 delete data; 352 } 353 354 return selected; 355 } 356 357 358 int32 359 BPopUpMenu::entry(void *arg) 360 { 361 popup_menu_data *data = static_cast<popup_menu_data *>(arg); 362 BPopUpMenu *menu = data->object; 363 364 BPoint where = data->where; 365 BRect *rect = NULL; 366 bool autoInvoke = data->autoInvoke; 367 bool startOpened = data->startOpened; 368 369 if (data->useRect) 370 rect = &data->rect; 371 372 BMenuItem *selected = menu->start_track(where, autoInvoke, 373 startOpened, rect); 374 375 // Put the selected item in the shared struct. 376 data->selected = selected; 377 378 delete_sem(data->lock); 379 380 // Reset the window menu semaphore 381 if (data->window) 382 _set_menu_sem_(data->window, B_NO_MORE_SEMS); 383 384 // Commit suicide if needed 385 if (menu->fAutoDestruct) 386 delete menu; 387 388 if (data->async) 389 delete data; 390 391 return 0; 392 } 393 394 395 BMenuItem * 396 BPopUpMenu::start_track(BPoint where, bool autoInvoke, 397 bool startOpened, BRect *_specialRect) 398 { 399 BMenuItem *result = NULL; 400 401 fWhere = where; 402 403 // I know, this doesn't look senseful, but don't be fooled, 404 // fUseWhere is used in ScreenLocation(), which is a virtual 405 // called by BMenu::Track() 406 fUseWhere = true; 407 408 // Show the menu's window 409 Show(); 410 411 // Wait some time then track the menu 412 snooze(50000); 413 result = Track(startOpened, _specialRect); 414 if (result != NULL && autoInvoke) 415 result->Invoke(); 416 417 fUseWhere = false; 418 419 Hide(); 420 421 return result; 422 } 423 424