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