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