1 /* 2 * Copyright 2015, Axel Dörfler, <axeld@pinc-software.de>. 3 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. 4 * Copyright 2013, Rene Gollent, rene@gollent.com. 5 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 6 * Copyright 2016-2021, Andrew Lindesay <apl@lindesay.co.nz>. 7 * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. 8 * All rights reserved. Distributed under the terms of the MIT License. 9 */ 10 11 12 #include "MainWindow.h" 13 14 #include <map> 15 #include <vector> 16 17 #include <stdio.h> 18 #include <Alert.h> 19 #include <Autolock.h> 20 #include <Application.h> 21 #include <Button.h> 22 #include <Catalog.h> 23 #include <CardLayout.h> 24 #include <LayoutBuilder.h> 25 #include <MenuBar.h> 26 #include <MenuItem.h> 27 #include <Messenger.h> 28 #include <Roster.h> 29 #include <Screen.h> 30 #include <ScrollView.h> 31 #include <StringList.h> 32 #include <StringView.h> 33 #include <TabView.h> 34 35 #include "AppUtils.h" 36 #include "AutoDeleter.h" 37 #include "AutoLocker.h" 38 #include "DecisionProvider.h" 39 #include "FeaturedPackagesView.h" 40 #include "FilterView.h" 41 #include "Logger.h" 42 #include "PackageInfoView.h" 43 #include "PackageListView.h" 44 #include "PackageManager.h" 45 #include "ProcessCoordinator.h" 46 #include "ProcessCoordinatorFactory.h" 47 #include "RatePackageWindow.h" 48 #include "support.h" 49 #include "ScreenshotWindow.h" 50 #include "SettingsWindow.h" 51 #include "ShuttingDownWindow.h" 52 #include "ToLatestUserUsageConditionsWindow.h" 53 #include "UserLoginWindow.h" 54 #include "UserUsageConditionsWindow.h" 55 #include "WorkStatusView.h" 56 57 58 #undef B_TRANSLATION_CONTEXT 59 #define B_TRANSLATION_CONTEXT "MainWindow" 60 61 62 enum { 63 MSG_REFRESH_REPOS = 'mrrp', 64 MSG_MANAGE_REPOS = 'mmrp', 65 MSG_SOFTWARE_UPDATER = 'mswu', 66 MSG_SETTINGS = 'stgs', 67 MSG_LOG_IN = 'lgin', 68 MSG_AUTHORIZATION_CHANGED = 'athc', 69 MSG_CATEGORIES_LIST_CHANGED = 'clic', 70 MSG_PACKAGE_CHANGED = 'pchd', 71 MSG_WORK_STATUS_CHANGE = 'wsch', 72 MSG_WORK_STATUS_CLEAR = 'wscl', 73 74 MSG_CHANGE_PACKAGE_LIST_VIEW_MODE = 'cplm', 75 MSG_SHOW_AVAILABLE_PACKAGES = 'savl', 76 MSG_SHOW_INSTALLED_PACKAGES = 'sins', 77 MSG_SHOW_SOURCE_PACKAGES = 'ssrc', 78 MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' 79 }; 80 81 #define KEY_ERROR_STATUS "errorStatus" 82 83 #define TAB_PROMINENT_PACKAGES 0 84 #define TAB_ALL_PACKAGES 1 85 86 using namespace BPackageKit; 87 using namespace BPackageKit::BManager::BPrivate; 88 89 90 typedef std::map<BString, PackageInfoRef> PackageInfoMap; 91 92 93 struct RefreshWorkerParameters { 94 MainWindow* window; 95 bool forceRefresh; 96 97 RefreshWorkerParameters(MainWindow* window, bool forceRefresh) 98 : 99 window(window), 100 forceRefresh(forceRefresh) 101 { 102 } 103 }; 104 105 106 class MainWindowModelListener : public ModelListener { 107 public: 108 MainWindowModelListener(const BMessenger& messenger) 109 : 110 fMessenger(messenger) 111 { 112 } 113 114 virtual void AuthorizationChanged() 115 { 116 if (fMessenger.IsValid()) 117 fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED); 118 } 119 120 virtual void CategoryListChanged() 121 { 122 if (fMessenger.IsValid()) 123 fMessenger.SendMessage(MSG_CATEGORIES_LIST_CHANGED); 124 } 125 126 private: 127 BMessenger fMessenger; 128 }; 129 130 131 MainWindow::MainWindow(const BMessage& settings) 132 : 133 BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"), 134 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 135 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 136 fScreenshotWindow(NULL), 137 fShuttingDownWindow(NULL), 138 fUserMenu(NULL), 139 fLogInItem(NULL), 140 fLogOutItem(NULL), 141 fUsersUserUsageConditionsMenuItem(NULL), 142 fModelListener(new MainWindowModelListener(BMessenger(this)), true), 143 fCoordinator(NULL), 144 fShouldCloseWhenNoProcessesToCoordinate(false), 145 fSinglePackageMode(false) 146 { 147 if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK) 148 debugger("unable to create the process coordinator semaphore"); 149 150 BMenuBar* menuBar = new BMenuBar("Main Menu"); 151 _BuildMenu(menuBar); 152 153 BMenuBar* userMenuBar = new BMenuBar("User Menu"); 154 _BuildUserMenu(userMenuBar); 155 set_small_font(userMenuBar); 156 userMenuBar->SetExplicitMaxSize(BSize(B_SIZE_UNSET, 157 menuBar->MaxSize().height)); 158 159 fFilterView = new FilterView(); 160 fFeaturedPackagesView = new FeaturedPackagesView(fModel); 161 fPackageListView = new PackageListView(&fModel); 162 fPackageInfoView = new PackageInfoView(&fModel, this); 163 164 fSplitView = new BSplitView(B_VERTICAL, 5.0f); 165 166 fWorkStatusView = new WorkStatusView("work status"); 167 fPackageListView->AttachWorkStatusView(fWorkStatusView); 168 169 fListTabs = new TabView(BMessenger(this), 170 BMessage(MSG_CHANGE_PACKAGE_LIST_VIEW_MODE), "list tabs"); 171 fListTabs->AddTab(fFeaturedPackagesView); 172 fListTabs->AddTab(fPackageListView); 173 174 BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f) 175 .AddGroup(B_HORIZONTAL, 0.0f) 176 .Add(menuBar, 1.0f) 177 .Add(userMenuBar, 0.0f) 178 .End() 179 .Add(fFilterView) 180 .AddSplit(fSplitView) 181 .AddGroup(B_VERTICAL) 182 .Add(fListTabs) 183 .SetInsets( 184 B_USE_DEFAULT_SPACING, 0.0f, 185 B_USE_DEFAULT_SPACING, 0.0f) 186 .End() 187 .Add(fPackageInfoView) 188 .End() 189 .Add(fWorkStatusView) 190 ; 191 192 fSplitView->SetCollapsible(0, false); 193 fSplitView->SetCollapsible(1, false); 194 195 fModel.AddListener(fModelListener); 196 197 BMessage columnSettings; 198 if (settings.FindMessage("column settings", &columnSettings) == B_OK) 199 fPackageListView->LoadState(&columnSettings); 200 201 _RestoreModelSettings(settings); 202 _MaybePromptCanShareAnonymousUserData(settings); 203 204 if (fModel.PackageListViewMode() == PROMINENT) 205 fListTabs->Select(TAB_PROMINENT_PACKAGES); 206 else 207 fListTabs->Select(TAB_ALL_PACKAGES); 208 209 _RestoreNickname(settings); 210 _UpdateAuthorization(); 211 _RestoreWindowFrame(settings); 212 213 // start worker threads 214 BPackageRoster().StartWatching(this, 215 B_WATCH_PACKAGE_INSTALLATION_LOCATIONS); 216 217 _InitWorkerThreads(); 218 _AdoptModel(); 219 _StartBulkLoad(); 220 } 221 222 223 MainWindow::MainWindow(const BMessage& settings, PackageInfoRef& package) 224 : 225 BWindow(BRect(50, 50, 650, 350), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"), 226 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 227 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 228 fFeaturedPackagesView(NULL), 229 fPackageListView(NULL), 230 fWorkStatusView(NULL), 231 fScreenshotWindow(NULL), 232 fShuttingDownWindow(NULL), 233 fUserMenu(NULL), 234 fLogInItem(NULL), 235 fLogOutItem(NULL), 236 fUsersUserUsageConditionsMenuItem(NULL), 237 fModelListener(new MainWindowModelListener(BMessenger(this)), true), 238 fCoordinator(NULL), 239 fShouldCloseWhenNoProcessesToCoordinate(false), 240 fSinglePackageMode(true) 241 { 242 if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK) 243 debugger("unable to create the process coordinator semaphore"); 244 245 fFilterView = new FilterView(); 246 fPackageInfoView = new PackageInfoView(&fModel, this); 247 fWorkStatusView = new WorkStatusView("work status"); 248 249 BLayoutBuilder::Group<>(this, B_VERTICAL) 250 .Add(fPackageInfoView) 251 .Add(fWorkStatusView) 252 .SetInsets(0, B_USE_WINDOW_INSETS, 0, 0) 253 ; 254 255 fModel.AddListener(fModelListener); 256 257 // add the single package into the model so that any internal 258 // business logic is able to find the package. 259 DepotInfoRef depot(new DepotInfo("single-pkg-depot"), true); 260 depot->AddPackage(package); 261 fModel.MergeOrAddDepot(depot); 262 263 // Restore settings 264 _RestoreNickname(settings); 265 _UpdateAuthorization(); 266 _RestoreWindowFrame(settings); 267 268 fPackageInfoView->SetPackage(package); 269 270 // start worker threads 271 BPackageRoster().StartWatching(this, 272 B_WATCH_PACKAGE_INSTALLATION_LOCATIONS); 273 274 _InitWorkerThreads(); 275 } 276 277 278 MainWindow::~MainWindow() 279 { 280 _SpinUntilProcessCoordinatorComplete(); 281 delete_sem(fCoordinatorRunningSem); 282 fCoordinatorRunningSem = 0; 283 284 BPackageRoster().StopWatching(this); 285 286 if (fScreenshotWindow != NULL) { 287 if (fScreenshotWindow->Lock()) 288 fScreenshotWindow->Quit(); 289 } 290 291 if (fShuttingDownWindow != NULL) { 292 if (fShuttingDownWindow->Lock()) 293 fShuttingDownWindow->Quit(); 294 } 295 } 296 297 298 bool 299 MainWindow::QuitRequested() 300 { 301 302 _StopProcessCoordinators(); 303 304 // If there are any processes in flight we need to be careful to make 305 // sure that they are cleanly completed before actually quitting. By 306 // turning on the `fShouldCloseWhenNoProcessesToCoordinate` flag, when 307 // the process coordination has completed then it will detect this and 308 // quit again. 309 310 { 311 AutoLocker<BLocker> lock(&fCoordinatorLock); 312 fShouldCloseWhenNoProcessesToCoordinate = true; 313 314 if (fCoordinator.IsSet()) { 315 HDINFO("a coordinator is running --> will wait before quitting..."); 316 317 if (fShuttingDownWindow == NULL) 318 fShuttingDownWindow = new ShuttingDownWindow(this); 319 fShuttingDownWindow->Show(); 320 321 return false; 322 } 323 } 324 325 BMessage settings; 326 StoreSettings(settings); 327 BMessage message(MSG_MAIN_WINDOW_CLOSED); 328 message.AddMessage(KEY_WINDOW_SETTINGS, &settings); 329 be_app->PostMessage(&message); 330 331 if (fShuttingDownWindow != NULL) { 332 if (fShuttingDownWindow->Lock()) 333 fShuttingDownWindow->Quit(); 334 fShuttingDownWindow = NULL; 335 } 336 337 return true; 338 } 339 340 341 void 342 MainWindow::MessageReceived(BMessage* message) 343 { 344 switch (message->what) { 345 case MSG_BULK_LOAD_DONE: 346 { 347 int64 errorStatus64; 348 if (message->FindInt64(KEY_ERROR_STATUS, &errorStatus64) == B_OK) 349 _BulkLoadCompleteReceived((status_t) errorStatus64); 350 else 351 HDERROR("expected [%s] value in message", KEY_ERROR_STATUS); 352 break; 353 } 354 case B_SIMPLE_DATA: 355 case B_REFS_RECEIVED: 356 // TODO: ? 357 break; 358 359 case B_PACKAGE_UPDATE: 360 _HandleExternalPackageUpdateMessageReceived(message); 361 break; 362 363 case MSG_REFRESH_REPOS: 364 _StartBulkLoad(true); 365 break; 366 367 case MSG_WORK_STATUS_CLEAR: 368 _HandleWorkStatusClear(); 369 break; 370 371 case MSG_WORK_STATUS_CHANGE: 372 _HandleWorkStatusChangeMessageReceived(message); 373 break; 374 375 case MSG_MANAGE_REPOS: 376 be_roster->Launch("application/x-vnd.Haiku-Repositories"); 377 break; 378 379 case MSG_SOFTWARE_UPDATER: 380 be_roster->Launch("application/x-vnd.haiku-softwareupdater"); 381 break; 382 383 case MSG_LOG_IN: 384 _OpenLoginWindow(BMessage()); 385 break; 386 387 case MSG_SETTINGS: 388 _OpenSettingsWindow(); 389 break; 390 391 case MSG_LOG_OUT: 392 fModel.SetNickname(""); 393 break; 394 395 case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS: 396 _ViewUserUsageConditions(LATEST); 397 break; 398 399 case MSG_VIEW_USERS_USER_USAGE_CONDITIONS: 400 _ViewUserUsageConditions(USER); 401 break; 402 403 case MSG_AUTHORIZATION_CHANGED: 404 _StartUserVerify(); 405 _UpdateAuthorization(); 406 break; 407 408 case MSG_CATEGORIES_LIST_CHANGED: 409 fFilterView->AdoptModel(fModel); 410 break; 411 412 case MSG_CHANGE_PACKAGE_LIST_VIEW_MODE: 413 _HandleChangePackageListViewMode(); 414 break; 415 416 case MSG_SHOW_AVAILABLE_PACKAGES: 417 { 418 BAutolock locker(fModel.Lock()); 419 fModel.SetShowAvailablePackages( 420 !fModel.ShowAvailablePackages()); 421 } 422 _AdoptModel(); 423 break; 424 425 case MSG_SHOW_INSTALLED_PACKAGES: 426 { 427 BAutolock locker(fModel.Lock()); 428 fModel.SetShowInstalledPackages( 429 !fModel.ShowInstalledPackages()); 430 } 431 _AdoptModel(); 432 break; 433 434 case MSG_SHOW_SOURCE_PACKAGES: 435 { 436 BAutolock locker(fModel.Lock()); 437 fModel.SetShowSourcePackages(!fModel.ShowSourcePackages()); 438 } 439 _AdoptModel(); 440 break; 441 442 case MSG_SHOW_DEVELOP_PACKAGES: 443 { 444 BAutolock locker(fModel.Lock()); 445 fModel.SetShowDevelopPackages(!fModel.ShowDevelopPackages()); 446 } 447 _AdoptModel(); 448 break; 449 450 // this may be triggered by, for example, a user rating being added 451 // or having been altered. 452 case MSG_SERVER_DATA_CHANGED: 453 { 454 BString name; 455 if (message->FindString("name", &name) == B_OK) { 456 BAutolock locker(fModel.Lock()); 457 if (fPackageInfoView->Package()->Name() == name) { 458 _PopulatePackageAsync(true); 459 } else { 460 HDDEBUG("pkg [%s] is updated on the server, but is " 461 "not selected so will not be updated.", 462 name.String()); 463 } 464 } 465 break; 466 } 467 468 case MSG_PACKAGE_SELECTED: 469 { 470 BString name; 471 if (message->FindString("name", &name) == B_OK) { 472 PackageInfoRef package; 473 { 474 BAutolock locker(fModel.Lock()); 475 package = fModel.PackageForName(name); 476 } 477 if (!package.IsSet() || name != package->Name()) 478 debugger("unable to find the named package"); 479 else { 480 _AdoptPackage(package); 481 _IncrementViewCounter(package); 482 } 483 } else { 484 _ClearPackage(); 485 } 486 break; 487 } 488 489 case MSG_CATEGORY_SELECTED: 490 { 491 BString code; 492 if (message->FindString("code", &code) != B_OK) 493 code = ""; 494 { 495 BAutolock locker(fModel.Lock()); 496 fModel.SetCategory(code); 497 } 498 _AdoptModel(); 499 break; 500 } 501 502 case MSG_DEPOT_SELECTED: 503 { 504 BString name; 505 if (message->FindString("name", &name) != B_OK) 506 name = ""; 507 { 508 BAutolock locker(fModel.Lock()); 509 fModel.SetDepot(name); 510 } 511 _AdoptModel(); 512 _UpdateAvailableRepositories(); 513 break; 514 } 515 516 case MSG_SEARCH_TERMS_MODIFIED: 517 { 518 // TODO: Do this with a delay! 519 BString searchTerms; 520 if (message->FindString("search terms", &searchTerms) != B_OK) 521 searchTerms = ""; 522 { 523 BAutolock locker(fModel.Lock()); 524 fModel.SetSearchTerms(searchTerms); 525 } 526 _AdoptModel(); 527 break; 528 } 529 530 case MSG_PACKAGE_CHANGED: 531 { 532 PackageInfo* info; 533 if (message->FindPointer("package", (void**)&info) == B_OK) { 534 PackageInfoRef ref(info, true); 535 fFeaturedPackagesView->BeginAddRemove(); 536 _AddRemovePackageFromLists(ref); 537 fFeaturedPackagesView->EndAddRemove(); 538 } 539 break; 540 } 541 542 case MSG_RATE_PACKAGE: 543 _RatePackage(); 544 break; 545 546 case MSG_SHOW_SCREENSHOT: 547 _ShowScreenshot(); 548 break; 549 550 case MSG_PACKAGE_WORKER_BUSY: 551 { 552 BString reason; 553 status_t status = message->FindString("reason", &reason); 554 if (status != B_OK) 555 break; 556 fWorkStatusView->SetBusy(reason); 557 break; 558 } 559 560 case MSG_PACKAGE_WORKER_IDLE: 561 fWorkStatusView->SetIdle(); 562 break; 563 564 case MSG_USER_USAGE_CONDITIONS_NOT_LATEST: 565 { 566 BMessage userDetailMsg; 567 if (message->FindMessage("userDetail", &userDetailMsg) != B_OK) { 568 debugger("expected the [userDetail] data to be carried in the " 569 "message."); 570 } 571 UserDetail userDetail(&userDetailMsg); 572 _HandleUserUsageConditionsNotLatest(userDetail); 573 break; 574 } 575 576 default: 577 BWindow::MessageReceived(message); 578 break; 579 } 580 } 581 582 583 static const char* 584 main_window_package_list_view_mode_str(package_list_view_mode mode) 585 { 586 if (mode == PROMINENT) 587 return "PROMINENT"; 588 return "ALL"; 589 } 590 591 592 static package_list_view_mode 593 main_window_str_to_package_list_view_mode(const BString& str) 594 { 595 if (str == "PROMINENT") 596 return PROMINENT; 597 return ALL; 598 } 599 600 601 void 602 MainWindow::StoreSettings(BMessage& settings) const 603 { 604 settings.AddRect(_WindowFrameName(), Frame()); 605 if (!fSinglePackageMode) { 606 settings.AddRect("window frame", Frame()); 607 608 BMessage columnSettings; 609 if (fPackageListView != NULL) 610 fPackageListView->SaveState(&columnSettings); 611 612 settings.AddMessage("column settings", &columnSettings); 613 614 settings.AddString(SETTING_PACKAGE_LIST_VIEW_MODE, 615 main_window_package_list_view_mode_str( 616 fModel.PackageListViewMode())); 617 settings.AddBool(SETTING_SHOW_AVAILABLE_PACKAGES, 618 fModel.ShowAvailablePackages()); 619 settings.AddBool(SETTING_SHOW_INSTALLED_PACKAGES, 620 fModel.ShowInstalledPackages()); 621 settings.AddBool(SETTING_SHOW_DEVELOP_PACKAGES, 622 fModel.ShowDevelopPackages()); 623 settings.AddBool(SETTING_SHOW_SOURCE_PACKAGES, 624 fModel.ShowSourcePackages()); 625 settings.AddBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA, 626 fModel.CanShareAnonymousUsageData()); 627 } 628 629 settings.AddString("username", fModel.Nickname()); 630 } 631 632 633 void 634 MainWindow::Consume(ProcessCoordinator *item) 635 { 636 _AddProcessCoordinator(item); 637 } 638 639 640 void 641 MainWindow::PackageChanged(const PackageInfoEvent& event) 642 { 643 uint32 watchedChanges = PKG_CHANGED_STATE | PKG_CHANGED_PROMINENCE; 644 if ((event.Changes() & watchedChanges) != 0) { 645 PackageInfoRef ref(event.Package()); 646 BMessage message(MSG_PACKAGE_CHANGED); 647 message.AddPointer("package", ref.Get()); 648 ref.Detach(); 649 // reference needs to be released by MessageReceived(); 650 PostMessage(&message); 651 } 652 } 653 654 655 void 656 MainWindow::_BuildMenu(BMenuBar* menuBar) 657 { 658 BMenu* menu = new BMenu(B_TRANSLATE_SYSTEM_NAME("HaikuDepot")); 659 fRefreshRepositoriesItem = new BMenuItem( 660 B_TRANSLATE("Refresh repositories"), new BMessage(MSG_REFRESH_REPOS)); 661 menu->AddItem(fRefreshRepositoriesItem); 662 menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories" 663 B_UTF8_ELLIPSIS), new BMessage(MSG_MANAGE_REPOS))); 664 menu->AddItem(new BMenuItem(B_TRANSLATE("Check for updates" 665 B_UTF8_ELLIPSIS), new BMessage(MSG_SOFTWARE_UPDATER))); 666 menu->AddSeparatorItem(); 667 menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), 668 new BMessage(MSG_SETTINGS), ',')); 669 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 670 new BMessage(B_QUIT_REQUESTED), 'Q')); 671 menuBar->AddItem(menu); 672 673 fRepositoryMenu = new BMenu(B_TRANSLATE("Repositories")); 674 menuBar->AddItem(fRepositoryMenu); 675 676 menu = new BMenu(B_TRANSLATE("Show")); 677 678 fShowAvailablePackagesItem = new BMenuItem( 679 B_TRANSLATE("Available packages"), 680 new BMessage(MSG_SHOW_AVAILABLE_PACKAGES)); 681 menu->AddItem(fShowAvailablePackagesItem); 682 683 fShowInstalledPackagesItem = new BMenuItem( 684 B_TRANSLATE("Installed packages"), 685 new BMessage(MSG_SHOW_INSTALLED_PACKAGES)); 686 menu->AddItem(fShowInstalledPackagesItem); 687 688 menu->AddSeparatorItem(); 689 690 fShowDevelopPackagesItem = new BMenuItem( 691 B_TRANSLATE("Develop packages"), 692 new BMessage(MSG_SHOW_DEVELOP_PACKAGES)); 693 menu->AddItem(fShowDevelopPackagesItem); 694 695 fShowSourcePackagesItem = new BMenuItem( 696 B_TRANSLATE("Source packages"), 697 new BMessage(MSG_SHOW_SOURCE_PACKAGES)); 698 menu->AddItem(fShowSourcePackagesItem); 699 700 menuBar->AddItem(menu); 701 } 702 703 704 void 705 MainWindow::_BuildUserMenu(BMenuBar* menuBar) 706 { 707 fUserMenu = new BMenu(B_TRANSLATE("Not logged in")); 708 709 fLogInItem = new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS), 710 new BMessage(MSG_LOG_IN)); 711 fUserMenu->AddItem(fLogInItem); 712 713 fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"), 714 new BMessage(MSG_LOG_OUT)); 715 fUserMenu->AddItem(fLogOutItem); 716 717 BMenuItem *latestUserUsageConditionsMenuItem = 718 new BMenuItem(B_TRANSLATE("View latest usage conditions" 719 B_UTF8_ELLIPSIS), 720 new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS)); 721 fUserMenu->AddItem(latestUserUsageConditionsMenuItem); 722 723 fUsersUserUsageConditionsMenuItem = 724 new BMenuItem(B_TRANSLATE("View agreed usage conditions" 725 B_UTF8_ELLIPSIS), 726 new BMessage(MSG_VIEW_USERS_USER_USAGE_CONDITIONS)); 727 fUserMenu->AddItem(fUsersUserUsageConditionsMenuItem); 728 729 menuBar->AddItem(fUserMenu); 730 } 731 732 733 void 734 MainWindow::_RestoreNickname(const BMessage& settings) 735 { 736 BString nickname; 737 if (settings.FindString("username", &nickname) == B_OK 738 && nickname.Length() > 0) { 739 fModel.SetNickname(nickname); 740 } 741 } 742 743 744 const char* 745 MainWindow::_WindowFrameName() const 746 { 747 if (fSinglePackageMode) 748 return "small window frame"; 749 750 return "window frame"; 751 } 752 753 754 void 755 MainWindow::_RestoreWindowFrame(const BMessage& settings) 756 { 757 BRect frame = Frame(); 758 759 BRect windowFrame; 760 bool fromSettings = false; 761 if (settings.FindRect(_WindowFrameName(), &windowFrame) == B_OK) { 762 frame = windowFrame; 763 fromSettings = true; 764 } else if (!fSinglePackageMode) { 765 // Resize to occupy a certain screen size 766 BRect screenFrame = BScreen(this).Frame(); 767 float width = frame.Width(); 768 float height = frame.Height(); 769 if (width < screenFrame.Width() * .666f 770 && height < screenFrame.Height() * .666f) { 771 frame.bottom = frame.top + screenFrame.Height() * .666f; 772 frame.right = frame.left 773 + std::min(screenFrame.Width() * .666f, height * 7 / 5); 774 } 775 } 776 777 MoveTo(frame.LeftTop()); 778 ResizeTo(frame.Width(), frame.Height()); 779 780 if (fromSettings) 781 MoveOnScreen(); 782 else 783 CenterOnScreen(); 784 } 785 786 787 void 788 MainWindow::_RestoreModelSettings(const BMessage& settings) 789 { 790 BString packageListViewMode; 791 if (settings.FindString(SETTING_PACKAGE_LIST_VIEW_MODE, 792 &packageListViewMode) == B_OK) { 793 fModel.SetPackageListViewMode( 794 main_window_str_to_package_list_view_mode(packageListViewMode)); 795 } 796 797 bool showOption; 798 if (settings.FindBool(SETTING_SHOW_AVAILABLE_PACKAGES, &showOption) == B_OK) 799 fModel.SetShowAvailablePackages(showOption); 800 if (settings.FindBool(SETTING_SHOW_INSTALLED_PACKAGES, &showOption) == B_OK) 801 fModel.SetShowInstalledPackages(showOption); 802 if (settings.FindBool(SETTING_SHOW_DEVELOP_PACKAGES, &showOption) == B_OK) 803 fModel.SetShowDevelopPackages(showOption); 804 if (settings.FindBool(SETTING_SHOW_SOURCE_PACKAGES, &showOption) == B_OK) 805 fModel.SetShowSourcePackages(showOption); 806 if (settings.FindBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA, 807 &showOption) == B_OK) { 808 fModel.SetCanShareAnonymousUsageData(showOption); 809 } 810 } 811 812 813 void 814 MainWindow::_MaybePromptCanShareAnonymousUserData(const BMessage& settings) 815 { 816 bool showOption; 817 if (settings.FindBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA, 818 &showOption) == B_NAME_NOT_FOUND) { 819 _PromptCanShareAnonymousUserData(); 820 } 821 } 822 823 824 void 825 MainWindow::_PromptCanShareAnonymousUserData() 826 { 827 BAlert* alert = new(std::nothrow) BAlert( 828 B_TRANSLATE("Sending anonymous usage data"), 829 B_TRANSLATE("Would it be acceptable to send anonymous usage data to the" 830 " HaikuDepotServer system from this computer? You can change your" 831 " preference in the \"Settings\" window later."), 832 B_TRANSLATE("No"), 833 B_TRANSLATE("Yes")); 834 835 int32 result = alert->Go(); 836 fModel.SetCanShareAnonymousUsageData(1 == result); 837 } 838 839 840 void 841 MainWindow::_InitWorkerThreads() 842 { 843 fPackageToPopulateSem = create_sem(0, "PopulatePackage"); 844 if (fPackageToPopulateSem >= 0) { 845 fPopulatePackageWorker = spawn_thread(&_PopulatePackageWorker, 846 "Package Populator", B_NORMAL_PRIORITY, this); 847 if (fPopulatePackageWorker >= 0) 848 resume_thread(fPopulatePackageWorker); 849 } else 850 fPopulatePackageWorker = -1; 851 } 852 853 854 void 855 MainWindow::_AdoptModelControls() 856 { 857 if (fSinglePackageMode) 858 return; 859 860 BAutolock locker(fModel.Lock()); 861 fShowAvailablePackagesItem->SetMarked(fModel.ShowAvailablePackages()); 862 fShowInstalledPackagesItem->SetMarked(fModel.ShowInstalledPackages()); 863 fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages()); 864 fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages()); 865 866 if (fModel.PackageListViewMode() == PROMINENT) 867 fListTabs->Select(TAB_PROMINENT_PACKAGES); 868 else 869 fListTabs->Select(TAB_ALL_PACKAGES); 870 871 fFilterView->AdoptModel(fModel); 872 } 873 874 875 void 876 MainWindow::_AdoptModel() 877 { 878 HDTRACE("adopting model to main window ui"); 879 880 if (fSinglePackageMode) 881 return; 882 883 std::vector<DepotInfoRef> depots = _CreateSnapshotOfDepots(); 884 std::vector<DepotInfoRef>::iterator it; 885 886 fFeaturedPackagesView->BeginAddRemove(); 887 888 for (it = depots.begin(); it != depots.end(); it++) { 889 DepotInfoRef depotInfoRef = *it; 890 for (int i = 0; i < depotInfoRef->CountPackages(); i++) { 891 PackageInfoRef package = depotInfoRef->PackageAtIndex(i); 892 _AddRemovePackageFromLists(package); 893 } 894 } 895 896 fFeaturedPackagesView->EndAddRemove(); 897 898 _AdoptModelControls(); 899 } 900 901 902 void 903 MainWindow::_AddRemovePackageFromLists(const PackageInfoRef& package) 904 { 905 bool matches; 906 907 { 908 AutoLocker<BLocker> modelLocker(fModel.Lock()); 909 matches = fModel.MatchesFilter(package); 910 } 911 912 if (matches) { 913 if (package->IsProminent()) 914 fFeaturedPackagesView->AddPackage(package); 915 fPackageListView->AddPackage(package); 916 } else { 917 fFeaturedPackagesView->RemovePackage(package); 918 fPackageListView->RemovePackage(package); 919 } 920 } 921 922 923 void 924 MainWindow::_IncrementViewCounter(const PackageInfoRef& package) 925 { 926 bool shouldIncrementViewCounter = false; 927 928 { 929 AutoLocker<BLocker> modelLocker(fModel.Lock()); 930 bool canShareAnonymousUsageData = fModel.CanShareAnonymousUsageData(); 931 if (canShareAnonymousUsageData && !package->Viewed()) { 932 package->SetViewed(); 933 shouldIncrementViewCounter = true; 934 } 935 } 936 937 if (shouldIncrementViewCounter) { 938 ProcessCoordinator* bulkLoadCoordinator = 939 ProcessCoordinatorFactory::CreateIncrementViewCounter( 940 &fModel, package); 941 _AddProcessCoordinator(bulkLoadCoordinator); 942 } 943 } 944 945 946 void 947 MainWindow::_AdoptPackage(const PackageInfoRef& package) 948 { 949 { 950 BAutolock locker(fModel.Lock()); 951 fPackageInfoView->SetPackage(package); 952 953 if (fFeaturedPackagesView != NULL) 954 fFeaturedPackagesView->SelectPackage(package); 955 if (fPackageListView != NULL) 956 fPackageListView->SelectPackage(package); 957 } 958 959 _PopulatePackageAsync(false); 960 } 961 962 963 void 964 MainWindow::_ClearPackage() 965 { 966 fPackageInfoView->Clear(); 967 } 968 969 970 void 971 MainWindow::_StartBulkLoad(bool force) 972 { 973 if (fFeaturedPackagesView != NULL) 974 fFeaturedPackagesView->Clear(); 975 if (fPackageListView != NULL) 976 fPackageListView->Clear(); 977 fPackageInfoView->Clear(); 978 979 fRefreshRepositoriesItem->SetEnabled(false); 980 ProcessCoordinator* bulkLoadCoordinator = 981 ProcessCoordinatorFactory::CreateBulkLoadCoordinator( 982 this, 983 // PackageInfoListener 984 &fModel, force); 985 _AddProcessCoordinator(bulkLoadCoordinator); 986 } 987 988 989 void 990 MainWindow::_BulkLoadCompleteReceived(status_t errorStatus) 991 { 992 if (errorStatus != B_OK) { 993 AppUtils::NotifySimpleError( 994 B_TRANSLATE("Package update error"), 995 B_TRANSLATE("While updating package data, a problem has arisen " 996 "that may cause data to be outdated or missing from the " 997 "application's display. Additional details regarding this " 998 "problem may be able to be obtained from the application " 999 "logs." 1000 ALERT_MSG_LOGS_USER_GUIDE)); 1001 } 1002 1003 fRefreshRepositoriesItem->SetEnabled(true); 1004 _AdoptModel(); 1005 _UpdateAvailableRepositories(); 1006 1007 // if after loading everything in, it transpires that there are no 1008 // featured packages then the featured packages should be disabled 1009 // and the user should be switched to the "all packages" view so 1010 // that they are not presented with a blank window! 1011 1012 bool hasProminentPackages = fModel.HasAnyProminentPackages(); 1013 fListTabs->TabAt(TAB_PROMINENT_PACKAGES)->SetEnabled(hasProminentPackages); 1014 if (!hasProminentPackages 1015 && fListTabs->Selection() == TAB_PROMINENT_PACKAGES) { 1016 fModel.SetPackageListViewMode(ALL); 1017 fListTabs->Select(TAB_ALL_PACKAGES); 1018 } 1019 } 1020 1021 1022 void 1023 MainWindow::_NotifyWorkStatusClear() 1024 { 1025 BMessage message(MSG_WORK_STATUS_CLEAR); 1026 this->PostMessage(&message, this); 1027 } 1028 1029 1030 void 1031 MainWindow::_HandleWorkStatusClear() 1032 { 1033 fWorkStatusView->SetText(""); 1034 fWorkStatusView->SetIdle(); 1035 } 1036 1037 1038 /*! Sends off a message to the Window so that it can change the status view 1039 on the front-end in the UI thread. 1040 */ 1041 1042 void 1043 MainWindow::_NotifyWorkStatusChange(const BString& text, float progress) 1044 { 1045 BMessage message(MSG_WORK_STATUS_CHANGE); 1046 1047 if (!text.IsEmpty()) 1048 message.AddString(KEY_WORK_STATUS_TEXT, text); 1049 message.AddFloat(KEY_WORK_STATUS_PROGRESS, progress); 1050 1051 this->PostMessage(&message, this); 1052 } 1053 1054 1055 void 1056 MainWindow::_HandleExternalPackageUpdateMessageReceived(const BMessage* message) 1057 { 1058 BStringList addedPackageNames; 1059 BStringList removedPackageNames; 1060 1061 if (message->FindStrings("added package names", 1062 &addedPackageNames) == B_OK) { 1063 addedPackageNames.Sort(); 1064 AutoLocker<BLocker> locker(fModel.Lock()); 1065 fModel.SetStateForPackagesByName(addedPackageNames, ACTIVATED); 1066 } 1067 else 1068 HDINFO("no [added package names] key in inbound message"); 1069 1070 if (message->FindStrings("removed package names", 1071 &removedPackageNames) == B_OK) { 1072 removedPackageNames.Sort(); 1073 AutoLocker<BLocker> locker(fModel.Lock()); 1074 fModel.SetStateForPackagesByName(addedPackageNames, UNINSTALLED); 1075 } else 1076 HDINFO("no [removed package names] key in inbound message"); 1077 } 1078 1079 1080 void 1081 MainWindow::_HandleWorkStatusChangeMessageReceived(const BMessage* message) 1082 { 1083 if (fWorkStatusView == NULL) 1084 return; 1085 1086 BString text; 1087 float progress; 1088 1089 if (message->FindString(KEY_WORK_STATUS_TEXT, &text) == B_OK) 1090 fWorkStatusView->SetText(text); 1091 1092 if (message->FindFloat(KEY_WORK_STATUS_PROGRESS, &progress) == B_OK) { 1093 if (progress < 0.0f) 1094 fWorkStatusView->SetBusy(); 1095 else 1096 fWorkStatusView->SetProgress(progress); 1097 } else { 1098 HDERROR("work status change missing progress on update message"); 1099 fWorkStatusView->SetProgress(0.0f); 1100 } 1101 } 1102 1103 1104 /*! This method will cause the package to have its data refreshed from 1105 the server application. The refresh happens in the background; this method 1106 is asynchronous. 1107 */ 1108 1109 void 1110 MainWindow::_PopulatePackageAsync(bool forcePopulate) 1111 { 1112 // Trigger asynchronous package population from the web-app 1113 { 1114 AutoLocker<BLocker> lock(&fPackageToPopulateLock); 1115 fPackageToPopulate = fPackageInfoView->Package(); 1116 fForcePopulatePackage = forcePopulate; 1117 } 1118 release_sem_etc(fPackageToPopulateSem, 1, 0); 1119 1120 HDDEBUG("pkg [%s] will be updated from the server.", 1121 fPackageToPopulate->Name().String()); 1122 } 1123 1124 1125 /*! This method will run in the background. The thread will block until there 1126 is a package to be updated. When the thread unblocks, it will update the 1127 package with information from the server. 1128 */ 1129 1130 status_t 1131 MainWindow::_PopulatePackageWorker(void* arg) 1132 { 1133 MainWindow* window = reinterpret_cast<MainWindow*>(arg); 1134 1135 while (acquire_sem(window->fPackageToPopulateSem) == B_OK) { 1136 PackageInfoRef package; 1137 bool force; 1138 { 1139 AutoLocker<BLocker> lock(&window->fPackageToPopulateLock); 1140 package = window->fPackageToPopulate; 1141 force = window->fForcePopulatePackage; 1142 } 1143 1144 if (package.IsSet()) { 1145 uint32 populateFlags = Model::POPULATE_USER_RATINGS 1146 | Model::POPULATE_SCREEN_SHOTS 1147 | Model::POPULATE_CHANGELOG; 1148 1149 if (force) 1150 populateFlags |= Model::POPULATE_FORCE; 1151 1152 window->fModel.PopulatePackage(package, populateFlags); 1153 1154 HDDEBUG("populating package [%s]", package->Name().String()); 1155 } 1156 } 1157 1158 return 0; 1159 } 1160 1161 1162 void 1163 MainWindow::_OpenSettingsWindow() 1164 { 1165 SettingsWindow* window = new SettingsWindow(this, &fModel); 1166 window->Show(); 1167 } 1168 1169 1170 void 1171 MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage) 1172 { 1173 UserLoginWindow* window = new UserLoginWindow(this, 1174 BRect(0, 0, 500, 400), fModel); 1175 1176 if (onSuccessMessage.what != 0) 1177 window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage); 1178 1179 window->Show(); 1180 } 1181 1182 1183 void 1184 MainWindow::_StartUserVerify() 1185 { 1186 if (!fModel.Nickname().IsEmpty()) { 1187 _AddProcessCoordinator( 1188 ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator( 1189 this, 1190 // UserDetailVerifierListener 1191 &fModel) ); 1192 } 1193 } 1194 1195 1196 void 1197 MainWindow::_UpdateAuthorization() 1198 { 1199 BString nickname(fModel.Nickname()); 1200 bool hasUser = !nickname.IsEmpty(); 1201 1202 if (fLogOutItem != NULL) 1203 fLogOutItem->SetEnabled(hasUser); 1204 if (fUsersUserUsageConditionsMenuItem != NULL) 1205 fUsersUserUsageConditionsMenuItem->SetEnabled(hasUser); 1206 if (fLogInItem != NULL) { 1207 if (hasUser) 1208 fLogInItem->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS)); 1209 else 1210 fLogInItem->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS)); 1211 } 1212 1213 if (fUserMenu != NULL) { 1214 BString label; 1215 if (hasUser) { 1216 label = B_TRANSLATE("Logged in as %User%"); 1217 label.ReplaceAll("%User%", nickname); 1218 } else { 1219 label = B_TRANSLATE("Not logged in"); 1220 } 1221 fUserMenu->Superitem()->SetLabel(label); 1222 } 1223 } 1224 1225 1226 void 1227 MainWindow::_UpdateAvailableRepositories() 1228 { 1229 fRepositoryMenu->RemoveItems(0, fRepositoryMenu->CountItems(), true); 1230 1231 fRepositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All repositories"), 1232 new BMessage(MSG_DEPOT_SELECTED))); 1233 1234 fRepositoryMenu->AddItem(new BSeparatorItem()); 1235 1236 bool foundSelectedDepot = false; 1237 std::vector<DepotInfoRef> depots = _CreateSnapshotOfDepots(); 1238 std::vector<DepotInfoRef>::iterator it; 1239 1240 for (it = depots.begin(); it != depots.end(); it++) { 1241 DepotInfoRef depot = *it; 1242 1243 if (depot->Name().Length() != 0) { 1244 BMessage* message = new BMessage(MSG_DEPOT_SELECTED); 1245 message->AddString("name", depot->Name()); 1246 BMenuItem* item = new(std::nothrow) BMenuItem(depot->Name(), message); 1247 1248 if (item == NULL) 1249 HDFATAL("memory exhaustion"); 1250 1251 fRepositoryMenu->AddItem(item); 1252 1253 if (depot->Name() == fModel.Depot()) { 1254 item->SetMarked(true); 1255 foundSelectedDepot = true; 1256 } 1257 } 1258 } 1259 1260 if (!foundSelectedDepot) 1261 fRepositoryMenu->ItemAt(0)->SetMarked(true); 1262 } 1263 1264 1265 bool 1266 MainWindow::_SelectedPackageHasWebAppRepositoryCode() 1267 { 1268 const PackageInfoRef& package = fPackageInfoView->Package(); 1269 const BString depotName = package->DepotName(); 1270 1271 if (depotName.IsEmpty()) { 1272 HDDEBUG("the package [%s] has no depot name", package->Name().String()); 1273 } else { 1274 const DepotInfo* depot = fModel.DepotForName(depotName); 1275 1276 if (depot == NULL) { 1277 HDINFO("the depot [%s] was not able to be found", 1278 depotName.String()); 1279 } else { 1280 BString repositoryCode = depot->WebAppRepositoryCode(); 1281 1282 if (repositoryCode.IsEmpty()) { 1283 HDINFO("the depot [%s] has no web app repository code", 1284 depotName.String()); 1285 } else 1286 return true; 1287 } 1288 } 1289 1290 return false; 1291 } 1292 1293 1294 void 1295 MainWindow::_RatePackage() 1296 { 1297 if (!_SelectedPackageHasWebAppRepositoryCode()) { 1298 BAlert* alert = new(std::nothrow) BAlert( 1299 B_TRANSLATE("Rating not possible"), 1300 B_TRANSLATE("This package doesn't seem to be on the HaikuDepot " 1301 "Server, so it's not possible to create a new rating " 1302 "or edit an existing rating."), 1303 B_TRANSLATE("OK")); 1304 alert->Go(); 1305 return; 1306 } 1307 1308 if (fModel.Nickname().IsEmpty()) { 1309 BAlert* alert = new(std::nothrow) BAlert( 1310 B_TRANSLATE("Not logged in"), 1311 B_TRANSLATE("You need to be logged into an account before you " 1312 "can rate packages."), 1313 B_TRANSLATE("Cancel"), 1314 B_TRANSLATE("Login or Create account")); 1315 1316 if (alert == NULL) 1317 return; 1318 1319 int32 choice = alert->Go(); 1320 if (choice == 1) 1321 _OpenLoginWindow(BMessage(MSG_RATE_PACKAGE)); 1322 return; 1323 } 1324 1325 // TODO: Allow only one RatePackageWindow 1326 // TODO: Mechanism for remembering the window frame 1327 RatePackageWindow* window = new RatePackageWindow(this, 1328 BRect(0, 0, 500, 400), fModel); 1329 window->SetPackage(fPackageInfoView->Package()); 1330 window->Show(); 1331 } 1332 1333 1334 void 1335 MainWindow::_ShowScreenshot() 1336 { 1337 // TODO: Mechanism for remembering the window frame 1338 if (fScreenshotWindow == NULL) 1339 fScreenshotWindow = new ScreenshotWindow(this, BRect(0, 0, 500, 400)); 1340 1341 if (fScreenshotWindow->LockWithTimeout(1000) != B_OK) 1342 return; 1343 1344 fScreenshotWindow->SetPackage(fPackageInfoView->Package()); 1345 1346 if (fScreenshotWindow->IsHidden()) 1347 fScreenshotWindow->Show(); 1348 else 1349 fScreenshotWindow->Activate(); 1350 1351 fScreenshotWindow->Unlock(); 1352 } 1353 1354 1355 void 1356 MainWindow::_ViewUserUsageConditions( 1357 UserUsageConditionsSelectionMode mode) 1358 { 1359 UserUsageConditionsWindow* window = new UserUsageConditionsWindow( 1360 fModel, mode); 1361 window->Show(); 1362 } 1363 1364 1365 void 1366 MainWindow::UserCredentialsFailed() 1367 { 1368 BString message = B_TRANSLATE("The password previously " 1369 "supplied for the user [%Nickname%] is not currently " 1370 "valid. The user will be logged-out of this application " 1371 "and you should login again with your updated password."); 1372 message.ReplaceAll("%Nickname%", fModel.Nickname()); 1373 1374 AppUtils::NotifySimpleError(B_TRANSLATE("Login issue"), 1375 message); 1376 1377 { 1378 AutoLocker<BLocker> locker(fModel.Lock()); 1379 fModel.SetNickname(""); 1380 } 1381 } 1382 1383 1384 /*! \brief This method is invoked from the UserDetailVerifierProcess on a 1385 background thread. For this reason it lodges a message into itself 1386 which can then be handled on the main thread. 1387 */ 1388 1389 void 1390 MainWindow::UserUsageConditionsNotLatest(const UserDetail& userDetail) 1391 { 1392 BMessage message(MSG_USER_USAGE_CONDITIONS_NOT_LATEST); 1393 BMessage detailsMessage; 1394 if (userDetail.Archive(&detailsMessage, true) != B_OK 1395 || message.AddMessage("userDetail", &detailsMessage) != B_OK) { 1396 HDERROR("unable to archive the user detail into a message"); 1397 } 1398 else 1399 BMessenger(this).SendMessage(&message); 1400 } 1401 1402 1403 void 1404 MainWindow::_HandleUserUsageConditionsNotLatest( 1405 const UserDetail& userDetail) 1406 { 1407 ToLatestUserUsageConditionsWindow* window = 1408 new ToLatestUserUsageConditionsWindow(this, fModel, userDetail); 1409 window->Show(); 1410 } 1411 1412 1413 void 1414 MainWindow::_AddProcessCoordinator(ProcessCoordinator* item) 1415 { 1416 AutoLocker<BLocker> lock(&fCoordinatorLock); 1417 1418 if (fShouldCloseWhenNoProcessesToCoordinate) { 1419 HDINFO("system shutting down --> new process coordinator [%s] rejected", 1420 item->Name().String()); 1421 delete item; 1422 return; 1423 } 1424 1425 item->SetListener(this); 1426 1427 if (!fCoordinator.IsSet()) { 1428 if (acquire_sem(fCoordinatorRunningSem) != B_OK) 1429 debugger("unable to acquire the process coordinator sem"); 1430 HDINFO("adding and starting a process coordinator [%s]", 1431 item->Name().String()); 1432 fCoordinator = BReference<ProcessCoordinator>(item); 1433 fCoordinator->Start(); 1434 } 1435 else { 1436 HDINFO("adding process coordinator [%s] to the queue", 1437 item->Name().String()); 1438 fCoordinatorQueue.push(item); 1439 } 1440 } 1441 1442 1443 void 1444 MainWindow::_SpinUntilProcessCoordinatorComplete() 1445 { 1446 while (true) { 1447 if (acquire_sem(fCoordinatorRunningSem) != B_OK) 1448 debugger("unable to acquire the process coordinator sem"); 1449 if (release_sem(fCoordinatorRunningSem) != B_OK) 1450 debugger("unable to release the process coordinator sem"); 1451 { 1452 AutoLocker<BLocker> lock(&fCoordinatorLock); 1453 if (!fCoordinator.IsSet()) 1454 return; 1455 } 1456 } 1457 } 1458 1459 1460 void 1461 MainWindow::_StopProcessCoordinators() 1462 { 1463 HDINFO("will stop all queued process coordinators"); 1464 AutoLocker<BLocker> lock(&fCoordinatorLock); 1465 1466 while (!fCoordinatorQueue.empty()) { 1467 BReference<ProcessCoordinator> processCoordinator 1468 = fCoordinatorQueue.front(); 1469 HDINFO("will drop queued process coordinator [%s]", 1470 processCoordinator->Name().String()); 1471 fCoordinatorQueue.pop(); 1472 } 1473 1474 if (fCoordinator.IsSet()) 1475 fCoordinator->RequestStop(); 1476 } 1477 1478 1479 /*! This method is called when there is some change in the bulk load process 1480 or other process coordinator. 1481 A change may mean that a new process has started / stopped etc... or it 1482 may mean that the entire coordinator has finished. 1483 */ 1484 1485 void 1486 MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState) 1487 { 1488 AutoLocker<BLocker> lock(&fCoordinatorLock); 1489 1490 if (fCoordinator.Get() == coordinatorState.Coordinator()) { 1491 if (!coordinatorState.IsRunning()) { 1492 if (release_sem(fCoordinatorRunningSem) != B_OK) 1493 debugger("unable to release the process coordinator sem"); 1494 HDINFO("process coordinator [%s] did complete", 1495 fCoordinator->Name().String()); 1496 // complete the last one that just finished 1497 BMessage* message = fCoordinator->Message(); 1498 1499 if (message != NULL) { 1500 BMessenger messenger(this); 1501 message->AddInt64(KEY_ERROR_STATUS, 1502 (int64) fCoordinator->ErrorStatus()); 1503 messenger.SendMessage(message); 1504 } 1505 1506 fCoordinator = BReference<ProcessCoordinator>(NULL); 1507 // will delete the old process coordinator if it is not used 1508 // elsewhere. 1509 1510 // now schedule the next one. 1511 if (!fCoordinatorQueue.empty()) { 1512 if (acquire_sem(fCoordinatorRunningSem) != B_OK) 1513 debugger("unable to acquire the process coordinator sem"); 1514 fCoordinator = fCoordinatorQueue.front(); 1515 HDINFO("starting next process coordinator [%s]", 1516 fCoordinator->Name().String()); 1517 fCoordinatorQueue.pop(); 1518 fCoordinator->Start(); 1519 } 1520 else { 1521 _NotifyWorkStatusClear(); 1522 if (fShouldCloseWhenNoProcessesToCoordinate) { 1523 HDINFO("no more processes to coord --> will quit"); 1524 BMessage message(B_QUIT_REQUESTED); 1525 PostMessage(&message); 1526 } 1527 } 1528 } 1529 else { 1530 _NotifyWorkStatusChange(coordinatorState.Message(), 1531 coordinatorState.Progress()); 1532 // show the progress to the user. 1533 } 1534 } else { 1535 _NotifyWorkStatusClear(); 1536 HDINFO("! unknown process coordinator changed"); 1537 } 1538 } 1539 1540 1541 static package_list_view_mode 1542 main_window_tab_to_package_list_view_mode(int32 tab) 1543 { 1544 if (tab == TAB_PROMINENT_PACKAGES) 1545 return PROMINENT; 1546 return ALL; 1547 } 1548 1549 1550 void 1551 MainWindow::_HandleChangePackageListViewMode() 1552 { 1553 package_list_view_mode tabMode = main_window_tab_to_package_list_view_mode( 1554 fListTabs->Selection()); 1555 package_list_view_mode modelMode = fModel.PackageListViewMode(); 1556 1557 if (tabMode != modelMode) { 1558 BAutolock locker(fModel.Lock()); 1559 fModel.SetPackageListViewMode(tabMode); 1560 } 1561 } 1562 1563 1564 std::vector<DepotInfoRef> 1565 MainWindow::_CreateSnapshotOfDepots() 1566 { 1567 std::vector<DepotInfoRef> result; 1568 BAutolock locker(fModel.Lock()); 1569 int32 countDepots = fModel.CountDepots(); 1570 for(int32 i = 0; i < countDepots; i++) 1571 result.push_back(fModel.DepotAtIndex(i)); 1572 return result; 1573 } 1574