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