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