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(); 157 fPackageListView = new PackageListView(fModel.Lock()); 158 fPackageInfoView = new PackageInfoView(fModel.Lock(), 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.Lock(), 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 fModel.Lock()->Lock(); 830 const DepotList& depots = fModel.Depots(); 831 fModel.Lock()->Unlock(); 832 833 for (int32 d = 0; d < depots.CountItems(); d++) { 834 const DepotInfo& depot = depots.ItemAtFast(d); 835 const PackageList& packages = depot.Packages(); 836 for (int32 p = 0; p < packages.CountItems(); p++) 837 _AddRemovePackageFromLists(packages.ItemAtFast(p)); 838 } 839 840 _AdoptModelControls(); 841 } 842 843 844 void 845 MainWindow::_AddRemovePackageFromLists(const PackageInfoRef& package) 846 { 847 bool matches; 848 849 { 850 AutoLocker<BLocker> modelLocker(fModel.Lock()); 851 matches = fModel.MatchesFilter(package); 852 } 853 854 if (matches) { 855 if (package->IsProminent()) 856 fFeaturedPackagesView->AddPackage(package); 857 fPackageListView->AddPackage(package); 858 } else { 859 fFeaturedPackagesView->RemovePackage(package); 860 fPackageListView->RemovePackage(package); 861 } 862 } 863 864 865 void 866 MainWindow::_AdoptPackage(const PackageInfoRef& package) 867 { 868 { 869 BAutolock locker(fModel.Lock()); 870 fPackageInfoView->SetPackage(package); 871 872 if (fFeaturedPackagesView != NULL) 873 fFeaturedPackagesView->SelectPackage(package); 874 if (fPackageListView != NULL) 875 fPackageListView->SelectPackage(package); 876 } 877 878 _PopulatePackageAsync(false); 879 } 880 881 882 void 883 MainWindow::_ClearPackage() 884 { 885 fPackageInfoView->Clear(); 886 } 887 888 889 void 890 MainWindow::_StartBulkLoad(bool force) 891 { 892 if (fFeaturedPackagesView != NULL) 893 fFeaturedPackagesView->Clear(); 894 if (fPackageListView != NULL) 895 fPackageListView->Clear(); 896 fPackageInfoView->Clear(); 897 898 fRefreshRepositoriesItem->SetEnabled(false); 899 ProcessCoordinator* bulkLoadCoordinator = 900 ProcessCoordinatorFactory::CreateBulkLoadCoordinator( 901 this, 902 // PackageInfoListener 903 this, 904 // ProcessCoordinatorListener 905 &fModel, force); 906 _AddProcessCoordinator(bulkLoadCoordinator); 907 } 908 909 910 void 911 MainWindow::_BulkLoadCompleteReceived(status_t errorStatus) 912 { 913 if (errorStatus != B_OK) { 914 AppUtils::NotifySimpleError( 915 B_TRANSLATE("Package update error"), 916 B_TRANSLATE("While updating package data, a problem has arisen " 917 "that may cause data to be outdated or missing from the " 918 "application's display. Additional details regarding this " 919 "problem may be able to be obtained from the application " 920 "logs." 921 ALERT_MSG_LOGS_USER_GUIDE)); 922 } 923 924 fRefreshRepositoriesItem->SetEnabled(true); 925 _AdoptModel(); 926 _UpdateAvailableRepositories(); 927 928 // if after loading everything in, it transpires that there are no 929 // featured packages then the featured packages should be disabled 930 // and the user should be switched to the "all packages" view so 931 // that they are not presented with a blank window! 932 933 bool hasProminentPackages = fModel.HasAnyProminentPackages(); 934 fListTabs->TabAt(TAB_PROMINENT_PACKAGES)->SetEnabled(hasProminentPackages); 935 if (!hasProminentPackages 936 && fListTabs->Selection() == TAB_PROMINENT_PACKAGES) { 937 fModel.SetPackageListViewMode(ALL); 938 fListTabs->Select(TAB_ALL_PACKAGES); 939 } 940 } 941 942 943 void 944 MainWindow::_NotifyWorkStatusClear() 945 { 946 BMessage message(MSG_WORK_STATUS_CLEAR); 947 this->PostMessage(&message, this); 948 } 949 950 951 void 952 MainWindow::_HandleWorkStatusClear() 953 { 954 fWorkStatusView->SetText(""); 955 fWorkStatusView->SetIdle(); 956 } 957 958 959 /*! Sends off a message to the Window so that it can change the status view 960 on the front-end in the UI thread. 961 */ 962 963 void 964 MainWindow::_NotifyWorkStatusChange(const BString& text, float progress) 965 { 966 BMessage message(MSG_WORK_STATUS_CHANGE); 967 968 if (!text.IsEmpty()) 969 message.AddString(KEY_WORK_STATUS_TEXT, text); 970 message.AddFloat(KEY_WORK_STATUS_PROGRESS, progress); 971 972 this->PostMessage(&message, this); 973 } 974 975 976 void 977 MainWindow::_HandleWorkStatusChangeMessageReceived(const BMessage* message) 978 { 979 if (fWorkStatusView == NULL) 980 return; 981 982 BString text; 983 float progress; 984 985 if (message->FindString(KEY_WORK_STATUS_TEXT, &text) == B_OK) 986 fWorkStatusView->SetText(text); 987 988 if (message->FindFloat(KEY_WORK_STATUS_PROGRESS, &progress) == B_OK) 989 fWorkStatusView->SetProgress(progress); 990 } 991 992 993 status_t 994 MainWindow::_PackageActionWorker(void* arg) 995 { 996 MainWindow* window = reinterpret_cast<MainWindow*>(arg); 997 998 while (acquire_sem(window->fPendingActionsSem) == B_OK) { 999 PackageActionRef ref; 1000 { 1001 AutoLocker<BLocker> lock(&window->fPendingActionsLock); 1002 ref = window->fPendingActions.ItemAt(0); 1003 if (ref.Get() == NULL) 1004 break; 1005 window->fPendingActions.Remove(0); 1006 } 1007 1008 BMessenger messenger(window); 1009 BMessage busyMessage(MSG_PACKAGE_WORKER_BUSY); 1010 BString text(ref->Label()); 1011 text << B_UTF8_ELLIPSIS; 1012 busyMessage.AddString("reason", text); 1013 1014 messenger.SendMessage(&busyMessage); 1015 ref->Perform(); 1016 messenger.SendMessage(MSG_PACKAGE_WORKER_IDLE); 1017 } 1018 1019 return 0; 1020 } 1021 1022 1023 /*! This method will cause the package to have its data refreshed from 1024 the server application. The refresh happens in the background; this method 1025 is asynchronous. 1026 */ 1027 1028 void 1029 MainWindow::_PopulatePackageAsync(bool forcePopulate) 1030 { 1031 // Trigger asynchronous package population from the web-app 1032 { 1033 AutoLocker<BLocker> lock(&fPackageToPopulateLock); 1034 fPackageToPopulate = fPackageInfoView->Package(); 1035 fForcePopulatePackage = forcePopulate; 1036 } 1037 release_sem_etc(fPackageToPopulateSem, 1, 0); 1038 1039 HDDEBUG("pkg [%s] will be updated from the server.", 1040 fPackageToPopulate->Name().String()); 1041 } 1042 1043 1044 /*! This method will run in the background. The thread will block until there 1045 is a package to be updated. When the thread unblocks, it will update the 1046 package with information from the server. 1047 */ 1048 1049 status_t 1050 MainWindow::_PopulatePackageWorker(void* arg) 1051 { 1052 MainWindow* window = reinterpret_cast<MainWindow*>(arg); 1053 1054 while (acquire_sem(window->fPackageToPopulateSem) == B_OK) { 1055 PackageInfoRef package; 1056 bool force; 1057 { 1058 AutoLocker<BLocker> lock(&window->fPackageToPopulateLock); 1059 package = window->fPackageToPopulate; 1060 force = window->fForcePopulatePackage; 1061 } 1062 1063 if (package.Get() != NULL) { 1064 uint32 populateFlags = Model::POPULATE_USER_RATINGS 1065 | Model::POPULATE_SCREEN_SHOTS 1066 | Model::POPULATE_CHANGELOG; 1067 1068 if (force) 1069 populateFlags |= Model::POPULATE_FORCE; 1070 1071 window->fModel.PopulatePackage(package, populateFlags); 1072 1073 HDDEBUG("populating package [%s]", package->Name().String()); 1074 } 1075 } 1076 1077 return 0; 1078 } 1079 1080 1081 void 1082 MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage) 1083 { 1084 UserLoginWindow* window = new UserLoginWindow(this, 1085 BRect(0, 0, 500, 400), fModel); 1086 1087 if (onSuccessMessage.what != 0) 1088 window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage); 1089 1090 window->Show(); 1091 } 1092 1093 1094 void 1095 MainWindow::_StartUserVerify() 1096 { 1097 if (!fModel.Nickname().IsEmpty()) { 1098 _AddProcessCoordinator( 1099 ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator( 1100 this, 1101 // UserDetailVerifierListener 1102 this, 1103 // ProcessCoordinatorListener 1104 &fModel) ); 1105 } 1106 } 1107 1108 1109 void 1110 MainWindow::_UpdateAuthorization() 1111 { 1112 BString nickname(fModel.Nickname()); 1113 bool hasUser = !nickname.IsEmpty(); 1114 1115 if (fLogOutItem != NULL) 1116 fLogOutItem->SetEnabled(hasUser); 1117 if (fUsersUserUsageConditionsMenuItem != NULL) 1118 fUsersUserUsageConditionsMenuItem->SetEnabled(hasUser); 1119 if (fLogInItem != NULL) { 1120 if (hasUser) 1121 fLogInItem->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS)); 1122 else 1123 fLogInItem->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS)); 1124 } 1125 1126 if (fUserMenu != NULL) { 1127 BString label; 1128 if (hasUser) { 1129 label = B_TRANSLATE("Logged in as %User%"); 1130 label.ReplaceAll("%User%", nickname); 1131 } else { 1132 label = B_TRANSLATE("Not logged in"); 1133 } 1134 fUserMenu->Superitem()->SetLabel(label); 1135 } 1136 } 1137 1138 1139 void 1140 MainWindow::_UpdateAvailableRepositories() 1141 { 1142 fRepositoryMenu->RemoveItems(0, fRepositoryMenu->CountItems(), true); 1143 1144 fRepositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All repositories"), 1145 new BMessage(MSG_DEPOT_SELECTED))); 1146 1147 fRepositoryMenu->AddItem(new BSeparatorItem()); 1148 1149 bool foundSelectedDepot = false; 1150 const DepotList& depots = fModel.Depots(); 1151 for (int i = 0; i < depots.CountItems(); i++) { 1152 const DepotInfo& depot = depots.ItemAtFast(i); 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 BMenuItem(depot.Name(), message); 1158 fRepositoryMenu->AddItem(item); 1159 1160 if (depot.Name() == fModel.Depot()) { 1161 item->SetMarked(true); 1162 foundSelectedDepot = true; 1163 } 1164 } 1165 } 1166 1167 if (!foundSelectedDepot) 1168 fRepositoryMenu->ItemAt(0)->SetMarked(true); 1169 } 1170 1171 1172 bool 1173 MainWindow::_SelectedPackageHasWebAppRepositoryCode() 1174 { 1175 const PackageInfoRef& package = fPackageInfoView->Package(); 1176 const BString depotName = package->DepotName(); 1177 1178 if (depotName.IsEmpty()) { 1179 HDDEBUG("the package [%s] has no depot name", package->Name().String()); 1180 } else { 1181 const DepotInfo* depot = fModel.DepotForName(depotName); 1182 1183 if (depot == NULL) { 1184 HDINFO("the depot [%s] was not able to be found", 1185 depotName.String()); 1186 } else { 1187 BString repositoryCode = depot->WebAppRepositoryCode(); 1188 1189 if (repositoryCode.IsEmpty()) { 1190 HDINFO("the depot [%s] has no web app repository code", 1191 depotName.String()); 1192 } else 1193 return true; 1194 } 1195 } 1196 1197 return false; 1198 } 1199 1200 1201 void 1202 MainWindow::_RatePackage() 1203 { 1204 if (!_SelectedPackageHasWebAppRepositoryCode()) { 1205 BAlert* alert = new(std::nothrow) BAlert( 1206 B_TRANSLATE("Rating not possible"), 1207 B_TRANSLATE("This package doesn't seem to be on the HaikuDepot " 1208 "Server, so it's not possible to create a new rating " 1209 "or edit an existing rating."), 1210 B_TRANSLATE("OK")); 1211 alert->Go(); 1212 return; 1213 } 1214 1215 if (fModel.Nickname().IsEmpty()) { 1216 BAlert* alert = new(std::nothrow) BAlert( 1217 B_TRANSLATE("Not logged in"), 1218 B_TRANSLATE("You need to be logged into an account before you " 1219 "can rate packages."), 1220 B_TRANSLATE("Cancel"), 1221 B_TRANSLATE("Login or Create account")); 1222 1223 if (alert == NULL) 1224 return; 1225 1226 int32 choice = alert->Go(); 1227 if (choice == 1) 1228 _OpenLoginWindow(BMessage(MSG_RATE_PACKAGE)); 1229 return; 1230 } 1231 1232 // TODO: Allow only one RatePackageWindow 1233 // TODO: Mechanism for remembering the window frame 1234 RatePackageWindow* window = new RatePackageWindow(this, 1235 BRect(0, 0, 500, 400), fModel); 1236 window->SetPackage(fPackageInfoView->Package()); 1237 window->Show(); 1238 } 1239 1240 1241 void 1242 MainWindow::_ShowScreenshot() 1243 { 1244 // TODO: Mechanism for remembering the window frame 1245 if (fScreenshotWindow == NULL) 1246 fScreenshotWindow = new ScreenshotWindow(this, BRect(0, 0, 500, 400)); 1247 1248 if (fScreenshotWindow->LockWithTimeout(1000) != B_OK) 1249 return; 1250 1251 fScreenshotWindow->SetPackage(fPackageInfoView->Package()); 1252 1253 if (fScreenshotWindow->IsHidden()) 1254 fScreenshotWindow->Show(); 1255 else 1256 fScreenshotWindow->Activate(); 1257 1258 fScreenshotWindow->Unlock(); 1259 } 1260 1261 1262 void 1263 MainWindow::_ViewUserUsageConditions( 1264 UserUsageConditionsSelectionMode mode) 1265 { 1266 UserUsageConditionsWindow* window = new UserUsageConditionsWindow( 1267 fModel, mode); 1268 window->Show(); 1269 } 1270 1271 1272 void 1273 MainWindow::UserCredentialsFailed() 1274 { 1275 BString message = B_TRANSLATE("The password previously " 1276 "supplied for the user [%Nickname%] is not currently " 1277 "valid. The user will be logged-out of this application " 1278 "and you should login again with your updated password."); 1279 message.ReplaceAll("%Nickname%", fModel.Nickname()); 1280 1281 AppUtils::NotifySimpleError(B_TRANSLATE("Login issue"), 1282 message); 1283 1284 { 1285 AutoLocker<BLocker> locker(fModel.Lock()); 1286 fModel.SetNickname(""); 1287 } 1288 } 1289 1290 1291 /*! \brief This method is invoked from the UserDetailVerifierProcess on a 1292 background thread. For this reason it lodges a message into itself 1293 which can then be handled on the main thread. 1294 */ 1295 1296 void 1297 MainWindow::UserUsageConditionsNotLatest(const UserDetail& userDetail) 1298 { 1299 BMessage message(MSG_USER_USAGE_CONDITIONS_NOT_LATEST); 1300 BMessage detailsMessage; 1301 if (userDetail.Archive(&detailsMessage, true) != B_OK 1302 || message.AddMessage("userDetail", &detailsMessage) != B_OK) { 1303 HDERROR("unable to archive the user detail into a message"); 1304 } 1305 else 1306 BMessenger(this).SendMessage(&message); 1307 } 1308 1309 1310 void 1311 MainWindow::_HandleUserUsageConditionsNotLatest( 1312 const UserDetail& userDetail) 1313 { 1314 ToLatestUserUsageConditionsWindow* window = 1315 new ToLatestUserUsageConditionsWindow(this, fModel, userDetail); 1316 window->Show(); 1317 } 1318 1319 1320 void 1321 MainWindow::_AddProcessCoordinator(ProcessCoordinator* item) 1322 { 1323 AutoLocker<BLocker> lock(&fCoordinatorLock); 1324 1325 if (fCoordinator.Get() == NULL) { 1326 if (acquire_sem(fCoordinatorRunningSem) != B_OK) 1327 debugger("unable to acquire the process coordinator sem"); 1328 HDINFO("adding and starting a process coordinator [%s]", 1329 item->Name().String()); 1330 fCoordinator = BReference<ProcessCoordinator>(item); 1331 fCoordinator->Start(); 1332 } 1333 else { 1334 HDINFO("adding process coordinator [%s] to the queue", 1335 item->Name().String()); 1336 fCoordinatorQueue.push(item); 1337 } 1338 } 1339 1340 1341 void 1342 MainWindow::_SpinUntilProcessCoordinatorComplete() 1343 { 1344 while (true) { 1345 if (acquire_sem(fCoordinatorRunningSem) != B_OK) 1346 debugger("unable to acquire the process coordinator sem"); 1347 if (release_sem(fCoordinatorRunningSem) != B_OK) 1348 debugger("unable to release the process coordinator sem"); 1349 { 1350 AutoLocker<BLocker> lock(&fCoordinatorLock); 1351 if (fCoordinator.Get() == NULL) 1352 return; 1353 } 1354 } 1355 } 1356 1357 1358 void 1359 MainWindow::_StopProcessCoordinators() 1360 { 1361 HDINFO("will stop all process coordinators"); 1362 1363 { 1364 AutoLocker<BLocker> lock(&fCoordinatorLock); 1365 1366 while (!fCoordinatorQueue.empty()) { 1367 BReference<ProcessCoordinator> processCoordinator 1368 = fCoordinatorQueue.front(); 1369 HDINFO("will drop queued process coordinator [%s]", 1370 processCoordinator->Name().String()); 1371 fCoordinatorQueue.pop(); 1372 } 1373 1374 if (fCoordinator.Get() != NULL) { 1375 fCoordinator->Stop(); 1376 } 1377 } 1378 1379 HDINFO("will wait until the process coordinator has stopped"); 1380 1381 _SpinUntilProcessCoordinatorComplete(); 1382 1383 HDINFO("did stop all process coordinators"); 1384 } 1385 1386 1387 /*! This method is called when there is some change in the bulk load process 1388 or other process coordinator. 1389 A change may mean that a new process has started / stopped etc... or it 1390 may mean that the entire coordinator has finished. 1391 */ 1392 1393 void 1394 MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState) 1395 { 1396 AutoLocker<BLocker> lock(&fCoordinatorLock); 1397 1398 if (fCoordinator.Get() == coordinatorState.Coordinator()) { 1399 if (!coordinatorState.IsRunning()) { 1400 if (release_sem(fCoordinatorRunningSem) != B_OK) 1401 debugger("unable to release the process coordinator sem"); 1402 HDINFO("process coordinator [%s] did complete", 1403 fCoordinator->Name().String()); 1404 // complete the last one that just finished 1405 BMessage* message = fCoordinator->Message(); 1406 1407 if (message != NULL) { 1408 BMessenger messenger(this); 1409 message->AddInt64(KEY_ERROR_STATUS, 1410 (int64) fCoordinator->ErrorStatus()); 1411 messenger.SendMessage(message); 1412 } 1413 1414 fCoordinator = BReference<ProcessCoordinator>(NULL); 1415 // will delete the old process coordinator if it is not used 1416 // elsewhere. 1417 1418 // now schedule the next one. 1419 if (!fCoordinatorQueue.empty()) { 1420 if (acquire_sem(fCoordinatorRunningSem) != B_OK) 1421 debugger("unable to acquire the process coordinator sem"); 1422 fCoordinator = fCoordinatorQueue.front(); 1423 HDINFO("starting next process coordinator [%s]", 1424 fCoordinator->Name().String()); 1425 fCoordinatorQueue.pop(); 1426 fCoordinator->Start(); 1427 } 1428 else { 1429 _NotifyWorkStatusClear(); 1430 } 1431 } 1432 else { 1433 _NotifyWorkStatusChange(coordinatorState.Message(), 1434 coordinatorState.Progress()); 1435 // show the progress to the user. 1436 } 1437 } else 1438 HDINFO("! unknown process coordinator changed"); 1439 } 1440 1441 1442 static package_list_view_mode 1443 main_window_tab_to_package_list_view_mode(int32 tab) 1444 { 1445 if (tab == TAB_PROMINENT_PACKAGES) 1446 return PROMINENT; 1447 return ALL; 1448 } 1449 1450 1451 void 1452 MainWindow::_HandleChangePackageListViewMode() 1453 { 1454 package_list_view_mode tabMode = main_window_tab_to_package_list_view_mode( 1455 fListTabs->Selection()); 1456 package_list_view_mode modelMode = fModel.PackageListViewMode(); 1457 1458 if (tabMode != modelMode) { 1459 BAutolock locker(fModel.Lock()); 1460 fModel.SetPackageListViewMode(tabMode); 1461 } 1462 }