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