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