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