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