1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered 30 trademarks of Be Incorporated in the United States and other countries. Other 31 brand product names are registered trademarks or trademarks of their respective 32 holders. 33 All rights reserved. 34 */ 35 36 37 #include "DeskbarMenu.h" 38 39 #include <Debug.h> 40 #include <Bitmap.h> 41 #include <Catalog.h> 42 #include <Dragger.h> 43 #include <Locale.h> 44 #include <Menu.h> 45 #include <MenuItem.h> 46 #include <Roster.h> 47 48 #include "BarApp.h" 49 #include "BarView.h" 50 #include "DeskbarUtils.h" 51 #include "IconMenuItem.h" 52 #include "MountMenu.h" 53 #include "RecentItems.h" 54 #include "StatusView.h" 55 56 #include "tracker_private.h" 57 58 #undef B_TRANSLATION_CONTEXT 59 #define B_TRANSLATION_CONTEXT "DeskbarMenu" 60 61 #define ROSTER_SIG "application/x-vnd.Be-ROST" 62 63 #ifdef MOUNT_MENU_IN_DESKBAR 64 class DeskbarMountMenu : public BPrivate::MountMenu { 65 public: 66 DeskbarMountMenu(const char* name); 67 virtual bool AddDynamicItem(add_state s); 68 }; 69 #endif // MOUNT_MENU_IN_DESKBAR 70 71 //#define SHOW_RECENT_FIND_ITEMS 72 73 namespace BPrivate { 74 BMenu* TrackerBuildRecentFindItemsMenu(const char*); 75 } 76 77 using namespace BPrivate; 78 79 80 // #pragma mark - TDeskbarMenu 81 82 83 TDeskbarMenu::TDeskbarMenu(TBarView* barView) 84 : 85 BNavMenu("DeskbarMenu", B_REFS_RECEIVED, DefaultTarget()), 86 fAddState(kStart), 87 fBarView(barView) 88 { 89 } 90 91 92 void 93 TDeskbarMenu::AttachedToWindow() 94 { 95 if (fBarView != NULL && fBarView->LockLooper()) { 96 if (fBarView->Dragging()) { 97 SetTypesList(fBarView->CachedTypesList()); 98 SetTarget(BMessenger(fBarView)); 99 SetTrackingHookDeep(this, fBarView->MenuTrackingHook, 100 fBarView->GetTrackingHookData()); 101 fBarView->DragStart(); 102 } else { 103 SetTypesList(NULL); 104 SetTarget(DefaultTarget()); 105 SetTrackingHookDeep(this, NULL, NULL); 106 } 107 108 fBarView->UnlockLooper(); 109 } 110 111 BNavMenu::AttachedToWindow(); 112 } 113 114 115 void 116 TDeskbarMenu::DetachedFromWindow() 117 { 118 if (fBarView != NULL) { 119 BLooper* looper = fBarView->Looper(); 120 if (looper != NULL && looper->Lock()) { 121 fBarView->DragStop(); 122 looper->Unlock(); 123 } 124 } 125 126 // don't call BNavMenu::DetachedFromWindow 127 // it sets the TypesList to NULL 128 BMenu::DetachedFromWindow(); 129 } 130 131 132 bool 133 TDeskbarMenu::StartBuildingItemList() 134 { 135 RemoveItems(0, CountItems(), true); 136 fAddState = kStart; 137 return BNavMenu::StartBuildingItemList(); 138 } 139 140 141 void 142 TDeskbarMenu::DoneBuildingItemList() 143 { 144 if (fItemList->CountItems() <= 0) { 145 BMenuItem* item 146 = new BMenuItem(B_TRANSLATE("<Deskbar folder is empty>"), 0); 147 item->SetEnabled(false); 148 AddItem(item); 149 } else 150 BNavMenu::DoneBuildingItemList(); 151 } 152 153 154 bool 155 TDeskbarMenu::AddNextItem() 156 { 157 if (fAddState == kStart) 158 return AddStandardDeskbarMenuItems(); 159 160 TrackingHookData* data = fBarView->GetTrackingHookData(); 161 if (fAddState == kAddingRecents) { 162 static const char* recentTitle[] = { 163 B_TRANSLATE_MARK("Recent documents"), 164 B_TRANSLATE_MARK("Recent folders"), 165 B_TRANSLATE_MARK("Recent applications")}; 166 const int recentType[] = {kRecentDocuments, kRecentFolders, 167 kRecentApplications}; 168 const int recentTypes = 3; 169 TRecentsMenu* recentItem[recentTypes]; 170 171 bool enabled = false; 172 173 for (int i = 0; i < recentTypes; i++) { 174 recentItem[i] 175 = new TRecentsMenu(B_TRANSLATE_NOCOLLECT(recentTitle[i]), 176 fBarView, recentType[i]); 177 178 if (recentItem[i]) 179 enabled |= recentItem[i]->RecentsEnabled(); 180 } 181 if (enabled) { 182 AddSeparatorItem(); 183 184 for (int i = 0; i < recentTypes; i++) { 185 if (!recentItem[i]) 186 continue; 187 188 if (recentItem[i]->RecentsEnabled()) { 189 recentItem[i]->SetTypesList(TypesList()); 190 recentItem[i]->SetTarget(Target()); 191 AddItem(recentItem[i]); 192 } 193 194 if (data && fBarView && fBarView->Dragging()) { 195 recentItem[i]->InitTrackingHook(data->fTrackingHook, 196 &data->fTarget, data->fDragMessage); 197 } 198 } 199 } else { 200 for (int i = 0; i < recentTypes; i++) 201 delete recentItem[i]; 202 } 203 204 AddSeparatorItem(); 205 fAddState = kAddingDeskbarMenu; 206 return true; 207 } 208 209 if (fAddState == kAddingDeskbarMenu) { 210 // keep reentering and adding items 211 // until this returns false 212 bool done = BNavMenu::AddNextItem(); 213 BMenuItem* item = ItemAt(CountItems() - 1); 214 if (item) { 215 BNavMenu* menu = dynamic_cast<BNavMenu*>(item->Menu()); 216 if (menu) { 217 if (data && fBarView->Dragging()) { 218 menu->InitTrackingHook(data->fTrackingHook, 219 &data->fTarget, data->fDragMessage); 220 } else 221 menu->InitTrackingHook(0, NULL, NULL); 222 } 223 } 224 225 if (!done) 226 fAddState = kDone; 227 return done; 228 } 229 230 return false; 231 } 232 233 234 bool 235 TDeskbarMenu::AddStandardDeskbarMenuItems() 236 { 237 bool dragging = false; 238 if (fBarView) 239 dragging = fBarView->Dragging(); 240 241 BMenuItem* item; 242 243 // One of them is used if HAIKU_DISTRO_COMPATIBILITY_OFFICIAL, and the other if 244 // not. However, we want both of them to end up in the catalog, so we have to 245 // make them visible to collectcatkeys in either case. 246 B_TRANSLATE_MARK_VOID("About Haiku") 247 B_TRANSLATE_MARK_VOID("About this system") 248 249 item = new BMenuItem( 250 #ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL 251 B_TRANSLATE_NOCOLLECT("About Haiku") 252 #else 253 B_TRANSLATE_NOCOLLECT("About this system") 254 #endif 255 , new BMessage(kShowSplash)); 256 item->SetEnabled(!dragging); 257 AddItem(item); 258 259 static const char* kFindMenuItemStr 260 = B_TRANSLATE_MARK("Find" B_UTF8_ELLIPSIS); 261 262 #ifdef SHOW_RECENT_FIND_ITEMS 263 item = new BMenuItem( 264 TrackerBuildRecentFindItemsMenu(kFindMenuItemStr), 265 new BMessage(kFindButton)); 266 #else 267 item = new BMenuItem(B_TRANSLATE_NOCOLLECT(kFindMenuItemStr), 268 new BMessage(kFindButton)); 269 #endif 270 item->SetEnabled(!dragging); 271 AddItem(item); 272 273 item = new BMenuItem(B_TRANSLATE("Show replicants"), 274 new BMessage(kToggleDraggers)); 275 item->SetEnabled(!dragging); 276 item->SetMarked(BDragger::AreDraggersDrawn()); 277 AddItem(item); 278 279 static const char* kMountMenuStr = B_TRANSLATE_MARK("Mount"); 280 281 #ifdef MOUNT_MENU_IN_DESKBAR 282 DeskbarMountMenu* mountMenu = new DeskbarMountMenu( 283 B_TRANSLATE_NOCOLLECT(kMountMenuStr)); 284 mountMenu->SetEnabled(!dragging); 285 AddItem(mountMenu); 286 #endif 287 288 item = new BMenuItem(B_TRANSLATE("Deskbar preferences" B_UTF8_ELLIPSIS), 289 new BMessage(kConfigShow)); 290 item->SetTarget(be_app); 291 AddItem(item); 292 293 AddSeparatorItem(); 294 295 BMenu* shutdownMenu = new BMenu(B_TRANSLATE("Shutdown" B_UTF8_ELLIPSIS)); 296 297 item = new BMenuItem(B_TRANSLATE("Power off"), 298 new BMessage(kShutdownSystem)); 299 item->SetEnabled(!dragging); 300 shutdownMenu->AddItem(item); 301 302 item = new BMenuItem(B_TRANSLATE("Restart system"), 303 new BMessage(kRebootSystem)); 304 item->SetEnabled(!dragging); 305 shutdownMenu->AddItem(item); 306 307 B_TRANSLATE_MARK_VOID("Suspend"); 308 309 #ifdef APM_SUPPORT 310 if (_kapm_control_(APM_CHECK_ENABLED) == B_OK) { 311 item = new BMenuItem(B_TRANSLATE_NOCOLLECT("Suspend"), 312 new BMessage(kSuspendSystem)); 313 item->SetEnabled(!dragging); 314 shutdownMenu->AddItem(item); 315 } 316 #endif 317 318 shutdownMenu->SetTargetForItems(be_app); 319 320 BMessage* message = new BMessage(kShutdownSystem); 321 message->AddBool("confirm", true); 322 AddItem(new BMenuItem(shutdownMenu, message)); 323 324 fAddState = kAddingRecents; 325 326 return true; 327 } 328 329 330 void 331 TDeskbarMenu::ClearMenuBuildingState() 332 { 333 fAddState = kDone; 334 fMenuBuilt = false; 335 // force the menu to get rebuilt each time 336 BNavMenu::ClearMenuBuildingState(); 337 } 338 339 340 void 341 TDeskbarMenu::ResetTargets() 342 { 343 // This method does not recurse into submenus 344 // and does not affect menu items in submenus. 345 // (e.g. "Restart System" and "Power Off") 346 347 BNavMenu::ResetTargets(); 348 349 // if we are dragging, set the target to whatever was set 350 // else set it to the default (Tracker) 351 if (!fBarView->Dragging()) 352 SetTarget(DefaultTarget()); 353 354 // now set the target for the menuitems to the currently 355 // set target, which may or may not be tracker 356 SetTargetForItems(Target()); 357 358 for (int32 i = 0; ; i++) { 359 BMenuItem* item = ItemAt(i); 360 if (item == NULL) 361 break; 362 363 if (item->Message()) { 364 switch (item->Message()->what) { 365 case kFindButton: 366 item->SetTarget(BMessenger(kTrackerSignature)); 367 break; 368 369 case kShowSplash: 370 case kToggleDraggers: 371 case kConfigShow: 372 case kConfigQuit: 373 case kAlwaysTop: 374 case kExpandNewTeams: 375 case kHideLabels: 376 case kResizeTeamIcons: 377 case kSortRunningApps: 378 case kTrackerFirst: 379 case kRebootSystem: 380 case kSuspendSystem: 381 case kShutdownSystem: 382 case kRealignReplicants: 383 case kShowHideTime: 384 case kShowSeconds: 385 case kShowDayOfWeek: 386 case kShowTimeZone: 387 case kGetClockSettings: 388 item->SetTarget(be_app); 389 break; 390 } 391 } 392 } 393 } 394 395 396 BPoint 397 TDeskbarMenu::ScreenLocation() 398 { 399 bool vertical = fBarView->Vertical(); 400 int32 expando = fBarView->ExpandoState(); 401 bool left = fBarView->Left(); 402 BPoint point; 403 404 BRect rect = Supermenu()->Bounds(); 405 Supermenu()->ConvertToScreen(&rect); 406 407 if (vertical && expando && left) { 408 PRINT(("Left\n")); 409 point = rect.RightTop() + BPoint(0, 3); 410 } else if (vertical && expando && !left) { 411 PRINT(("Right\n")); 412 point = rect.LeftTop() - BPoint(Bounds().Width(), 0) + BPoint(0, 3); 413 } else 414 point = BMenu::ScreenLocation(); 415 416 return point; 417 } 418 419 420 /*static*/ 421 BMessenger 422 TDeskbarMenu::DefaultTarget() 423 { 424 // if Tracker is not available we target the BarApp 425 BMessenger target(kTrackerSignature); 426 if (target.IsValid()) 427 return target; 428 429 return BMessenger(be_app); 430 } 431 432 433 // #pragma mark - 434 435 436 TRecentsMenu::TRecentsMenu(const char* name, TBarView* bar, int32 which, 437 const char* signature, entry_ref* appRef) 438 : BNavMenu(name, B_REFS_RECEIVED, TDeskbarMenu::DefaultTarget()), 439 fWhich(which), 440 fAppRef(NULL), 441 fSignature(NULL), 442 fRecentsCount(0), 443 fRecentsEnabled(false), 444 fItemIndex(0), 445 fBarView(bar) 446 { 447 TBarApp* app = dynamic_cast<TBarApp*>(be_app); 448 if (app == NULL) 449 return; 450 451 switch (which) { 452 case kRecentDocuments: 453 fRecentsCount = app->Settings()->recentDocsCount; 454 fRecentsEnabled = app->Settings()->recentDocsEnabled; 455 break; 456 case kRecentApplications: 457 fRecentsCount = app->Settings()->recentAppsCount; 458 fRecentsEnabled = app->Settings()->recentAppsEnabled; 459 break; 460 case kRecentAppDocuments: 461 fRecentsCount = app->Settings()->recentDocsCount; 462 fRecentsEnabled = app->Settings()->recentDocsEnabled; 463 if (signature != NULL) 464 fSignature = strdup(signature); 465 if (appRef != NULL) 466 fAppRef = new entry_ref(*appRef); 467 break; 468 case kRecentFolders: 469 fRecentsCount = app->Settings()->recentFoldersCount; 470 fRecentsEnabled = app->Settings()->recentFoldersEnabled; 471 break; 472 } 473 } 474 475 476 TRecentsMenu::~TRecentsMenu() 477 { 478 delete fAppRef; 479 free(fSignature); 480 } 481 482 483 void 484 TRecentsMenu::DetachedFromWindow() 485 { 486 // BNavMenu::DetachedFromWindow sets the TypesList to NULL 487 BMenu::DetachedFromWindow(); 488 } 489 490 491 bool 492 TRecentsMenu::StartBuildingItemList() 493 { 494 RemoveItems(0, CountItems(), true); 495 496 // !! note: don't call inherited from here 497 // the navref is not set for this menu 498 // but it still needs to be a draggable navmenu 499 // simply return true so that AddNextItem is called 500 // 501 // return BNavMenu::StartBuildingItemList(); 502 return true; 503 } 504 505 506 bool 507 TRecentsMenu::AddNextItem() 508 { 509 if (fRecentsCount > 0 && fRecentsEnabled && AddRecents(fRecentsCount)) 510 return true; 511 512 fItemIndex = 0; 513 return false; 514 } 515 516 517 bool 518 TRecentsMenu::AddRecents(int32 count) 519 { 520 if (fItemIndex == 0) { 521 fRecentList.MakeEmpty(); 522 BRoster roster; 523 524 switch (fWhich) { 525 case kRecentDocuments: 526 roster.GetRecentDocuments(&fRecentList, count); 527 break; 528 case kRecentApplications: 529 roster.GetRecentApps(&fRecentList, count); 530 break; 531 case kRecentAppDocuments: 532 roster.GetRecentDocuments(&fRecentList, count, NULL, 533 fSignature); 534 break; 535 case kRecentFolders: 536 roster.GetRecentFolders(&fRecentList, count); 537 break; 538 default: 539 return false; 540 } 541 } 542 543 for (;;) { 544 entry_ref ref; 545 if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK) 546 break; 547 548 if (ref.name && strlen(ref.name) > 0) { 549 Model model(&ref, true); 550 551 if (fWhich != kRecentApplications) { 552 BMessage* message = new BMessage(B_REFS_RECEIVED); 553 if (fWhich == kRecentAppDocuments) { 554 // add application as handler 555 message->AddRef("handler", fAppRef); 556 } 557 558 ModelMenuItem* item = BNavMenu::NewModelItem(&model, 559 message, Target(), false, NULL, TypesList()); 560 561 if (item) 562 AddItem(item); 563 } else { 564 // The application items expand to a list of recent documents 565 // for that application - so they must be handled extra 566 BFile file(&ref, B_READ_ONLY); 567 char signature[B_MIME_TYPE_LENGTH]; 568 569 BAppFileInfo appInfo(&file); 570 if (appInfo.InitCheck() != B_OK 571 || appInfo.GetSignature(signature) != B_OK) 572 continue; 573 574 ModelMenuItem* item = NULL; 575 BMessage doc; 576 be_roster->GetRecentDocuments(&doc, 1, NULL, signature); 577 // ToDo: check if the documents do exist at all to 578 // avoid the creation of the submenu. 579 580 if (doc.CountNames(B_REF_TYPE) > 0) { 581 // create recents menu that will contain the recent docs of 582 // this app 583 TRecentsMenu* docs = new TRecentsMenu(model.Name(), 584 fBarView, kRecentAppDocuments, signature, &ref); 585 docs->SetTypesList(TypesList()); 586 docs->SetTarget(Target()); 587 588 item = new ModelMenuItem(&model, docs); 589 } else 590 item = new ModelMenuItem(&model, model.Name(), NULL); 591 592 if (item) { 593 // add refs-message so that the recent app can be launched 594 BMessage* msg = new BMessage(B_REFS_RECEIVED); 595 msg->AddRef("refs", &ref); 596 item->SetMessage(msg); 597 item->SetTarget(Target()); 598 599 AddItem(item); 600 } 601 } 602 603 // return true so that we know to reenter this list 604 return true; 605 } 606 } 607 608 // return false if we are done with this list 609 return false; 610 } 611 612 613 void 614 TRecentsMenu::DoneBuildingItemList() 615 { 616 // !! note: don't call inherited here 617 // the object list is not built 618 // and this list does not need to be sorted 619 // BNavMenu::DoneBuildingItemList(); 620 621 if (CountItems() > 0) 622 SetTargetForItems(Target()); 623 } 624 625 626 void 627 TRecentsMenu::ClearMenuBuildingState() 628 { 629 fMenuBuilt = false; 630 BNavMenu::ClearMenuBuildingState(); 631 } 632 633 634 void 635 TRecentsMenu::ResetTargets() 636 { 637 BNavMenu::ResetTargets(); 638 639 // if we are dragging, set the target to whatever was set 640 // else set it to the default (Tracker) 641 if (!fBarView->Dragging()) 642 SetTarget(TDeskbarMenu::DefaultTarget()); 643 644 // now set the target for the menuitems to the currently 645 // set target, which may or may not be tracker 646 SetTargetForItems(Target()); 647 } 648 649 650 // #pragma mark - DeskbarMountMenu 651 652 653 #ifdef MOUNT_MENU_IN_DESKBAR 654 DeskbarMountMenu::DeskbarMountMenu(const char* name) 655 : BPrivate::MountMenu(name) 656 { 657 } 658 659 660 bool 661 DeskbarMountMenu::AddDynamicItem(add_state s) 662 { 663 BPrivate::MountMenu::AddDynamicItem(s); 664 665 SetTargetForItems(BMessenger(kTrackerSignature)); 666 667 return false; 668 } 669 #endif // MOUNT_MENU_IN_DESKBAR 670