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 case kShowHideTime: 392 case kShowSeconds: 393 case kShowDayOfWeek: 394 case kShowTimeZone: 395 case kGetClockSettings: 396 item->SetTarget(be_app); 397 break; 398 } 399 } 400 } 401 } 402 403 404 BPoint 405 TDeskbarMenu::ScreenLocation() 406 { 407 bool vertical = fBarView->Vertical(); 408 int32 expando = (fBarView->State() == kExpandoState); 409 BPoint point; 410 411 BRect rect = Supermenu()->Bounds(); 412 Supermenu()->ConvertToScreen(&rect); 413 414 if (expando && vertical && fBarView->Left()) { 415 PRINT(("Left\n")); 416 point = rect.RightTop() + BPoint(0, 3); 417 } else if (expando && vertical && !fBarView->Left()) { 418 PRINT(("Right\n")); 419 point = rect.LeftTop() - BPoint(Bounds().Width(), 0) + BPoint(0, 3); 420 } else 421 point = BMenu::ScreenLocation(); 422 423 return point; 424 } 425 426 427 /*static*/ 428 BMessenger 429 TDeskbarMenu::DefaultTarget() 430 { 431 // if Tracker is not available we target the BarApp 432 BMessenger target(kTrackerSignature); 433 if (target.IsValid()) 434 return target; 435 436 return BMessenger(be_app); 437 } 438 439 440 // #pragma mark - 441 442 443 TRecentsMenu::TRecentsMenu(const char* name, TBarView* bar, int32 which, 444 const char* signature, entry_ref* appRef) 445 : BNavMenu(name, B_REFS_RECEIVED, TDeskbarMenu::DefaultTarget()), 446 fWhich(which), 447 fAppRef(NULL), 448 fSignature(NULL), 449 fRecentsCount(0), 450 fRecentsEnabled(false), 451 fItemIndex(0), 452 fBarView(bar) 453 { 454 TBarApp* app = dynamic_cast<TBarApp*>(be_app); 455 if (app == NULL) 456 return; 457 458 switch (which) { 459 case kRecentDocuments: 460 fRecentsCount = app->Settings()->recentDocsCount; 461 fRecentsEnabled = app->Settings()->recentDocsEnabled; 462 break; 463 case kRecentApplications: 464 fRecentsCount = app->Settings()->recentAppsCount; 465 fRecentsEnabled = app->Settings()->recentAppsEnabled; 466 break; 467 case kRecentAppDocuments: 468 fRecentsCount = app->Settings()->recentDocsCount; 469 fRecentsEnabled = app->Settings()->recentDocsEnabled; 470 if (signature != NULL) 471 fSignature = strdup(signature); 472 if (appRef != NULL) 473 fAppRef = new entry_ref(*appRef); 474 break; 475 case kRecentFolders: 476 fRecentsCount = app->Settings()->recentFoldersCount; 477 fRecentsEnabled = app->Settings()->recentFoldersEnabled; 478 break; 479 } 480 } 481 482 483 TRecentsMenu::~TRecentsMenu() 484 { 485 delete fAppRef; 486 free(fSignature); 487 } 488 489 490 void 491 TRecentsMenu::DetachedFromWindow() 492 { 493 // BNavMenu::DetachedFromWindow sets the TypesList to NULL 494 BMenu::DetachedFromWindow(); 495 } 496 497 498 bool 499 TRecentsMenu::StartBuildingItemList() 500 { 501 RemoveItems(0, CountItems(), true); 502 503 // !! note: don't call inherited from here 504 // the navref is not set for this menu 505 // but it still needs to be a draggable navmenu 506 // simply return true so that AddNextItem is called 507 // 508 // return BNavMenu::StartBuildingItemList(); 509 return true; 510 } 511 512 513 bool 514 TRecentsMenu::AddNextItem() 515 { 516 if (fRecentsCount > 0 && fRecentsEnabled && AddRecents(fRecentsCount)) 517 return true; 518 519 fItemIndex = 0; 520 return false; 521 } 522 523 524 bool 525 TRecentsMenu::AddRecents(int32 count) 526 { 527 if (fItemIndex == 0) { 528 fRecentList.MakeEmpty(); 529 BRoster roster; 530 531 switch (fWhich) { 532 case kRecentDocuments: 533 roster.GetRecentDocuments(&fRecentList, count); 534 break; 535 case kRecentApplications: 536 roster.GetRecentApps(&fRecentList, count); 537 break; 538 case kRecentAppDocuments: 539 roster.GetRecentDocuments(&fRecentList, count, NULL, 540 fSignature); 541 break; 542 case kRecentFolders: 543 roster.GetRecentFolders(&fRecentList, count); 544 break; 545 default: 546 return false; 547 } 548 } 549 550 for (;;) { 551 entry_ref ref; 552 if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK) 553 break; 554 555 if (ref.name && strlen(ref.name) > 0) { 556 Model model(&ref, true); 557 558 if (fWhich != kRecentApplications) { 559 BMessage* message = new BMessage(B_REFS_RECEIVED); 560 if (fWhich == kRecentAppDocuments) { 561 // add application as handler 562 message->AddRef("handler", fAppRef); 563 } 564 565 ModelMenuItem* item = BNavMenu::NewModelItem(&model, 566 message, Target(), false, NULL, TypesList()); 567 568 if (item) 569 AddItem(item); 570 } else { 571 // The application items expand to a list of recent documents 572 // for that application - so they must be handled extra 573 BFile file(&ref, B_READ_ONLY); 574 char signature[B_MIME_TYPE_LENGTH]; 575 576 BAppFileInfo appInfo(&file); 577 if (appInfo.InitCheck() != B_OK 578 || appInfo.GetSignature(signature) != B_OK) 579 continue; 580 581 ModelMenuItem* item = NULL; 582 BMessage doc; 583 be_roster->GetRecentDocuments(&doc, 1, NULL, signature); 584 // ToDo: check if the documents do exist at all to 585 // avoid the creation of the submenu. 586 587 if (doc.CountNames(B_REF_TYPE) > 0) { 588 // create recents menu that will contain the recent docs of 589 // this app 590 TRecentsMenu* docs = new TRecentsMenu(model.Name(), 591 fBarView, kRecentAppDocuments, signature, &ref); 592 docs->SetTypesList(TypesList()); 593 docs->SetTarget(Target()); 594 595 item = new ModelMenuItem(&model, docs); 596 } else 597 item = new ModelMenuItem(&model, model.Name(), NULL); 598 599 if (item) { 600 // add refs-message so that the recent app can be launched 601 BMessage* msg = new BMessage(B_REFS_RECEIVED); 602 msg->AddRef("refs", &ref); 603 item->SetMessage(msg); 604 item->SetTarget(Target()); 605 606 AddItem(item); 607 } 608 } 609 610 // return true so that we know to reenter this list 611 return true; 612 } 613 } 614 615 // return false if we are done with this list 616 return false; 617 } 618 619 620 void 621 TRecentsMenu::DoneBuildingItemList() 622 { 623 // !! note: don't call inherited here 624 // the object list is not built 625 // and this list does not need to be sorted 626 // BNavMenu::DoneBuildingItemList(); 627 628 if (CountItems() > 0) 629 SetTargetForItems(Target()); 630 } 631 632 633 void 634 TRecentsMenu::ClearMenuBuildingState() 635 { 636 fMenuBuilt = false; 637 BNavMenu::ClearMenuBuildingState(); 638 } 639 640 641 void 642 TRecentsMenu::ResetTargets() 643 { 644 BNavMenu::ResetTargets(); 645 646 // if we are dragging, set the target to whatever was set 647 // else set it to the default (Tracker) 648 if (!fBarView->Dragging()) 649 SetTarget(TDeskbarMenu::DefaultTarget()); 650 651 // now set the target for the menuitems to the currently 652 // set target, which may or may not be tracker 653 SetTargetForItems(Target()); 654 } 655 656 657 //***************************************************************************** 658 // #pragma mark - 659 660 661 #ifdef MOUNT_MENU_IN_DESKBAR 662 663 DeskbarMountMenu::DeskbarMountMenu(const char* name) 664 : BPrivate::MountMenu(name) 665 { 666 SetFont(be_plain_font); 667 } 668 669 670 bool 671 DeskbarMountMenu::AddDynamicItem(add_state s) 672 { 673 BPrivate::MountMenu::AddDynamicItem(s); 674 675 SetTargetForItems(BMessenger(kTrackerSignature)); 676 677 return false; 678 } 679 680 #endif 681