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