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