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