1 /* 2 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2013, Rene Gollent, rene@gollent.com. 4 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 5 * All rights reserved. Distributed under the terms of the MIT License. 6 */ 7 8 #include "MainWindow.h" 9 10 #include <map> 11 12 #include <stdio.h> 13 14 #include <Alert.h> 15 #include <Autolock.h> 16 #include <Application.h> 17 #include <Button.h> 18 #include <Catalog.h> 19 #include <LayoutBuilder.h> 20 #include <MenuBar.h> 21 #include <MenuItem.h> 22 #include <Messenger.h> 23 #include <ScrollView.h> 24 #include <StringList.h> 25 #include <TabView.h> 26 27 #include <package/Context.h> 28 #include <package/manager/Exceptions.h> 29 #include <package/manager/RepositoryBuilder.h> 30 #include <package/RefreshRepositoryRequest.h> 31 #include <package/PackageRoster.h> 32 #include "package/RepositoryCache.h" 33 #include <package/solver/SolverPackage.h> 34 #include <package/solver/SolverProblem.h> 35 #include <package/solver/SolverProblemSolution.h> 36 #include <package/solver/SolverRepository.h> 37 #include <package/solver/SolverResult.h> 38 39 #include "AutoDeleter.h" 40 #include "AutoLocker.h" 41 #include "DecisionProvider.h" 42 #include "FilterView.h" 43 #include "JobStateListener.h" 44 #include "PackageInfoView.h" 45 #include "PackageListView.h" 46 #include "PackageManager.h" 47 #include "RatePackageWindow.h" 48 #include "UserLoginWindow.h" 49 50 51 #undef B_TRANSLATION_CONTEXT 52 #define B_TRANSLATION_CONTEXT "MainWindow" 53 54 55 enum { 56 MSG_MODEL_WORKER_DONE = 'mmwd', 57 MSG_REFRESH_DEPOTS = 'mrdp', 58 MSG_LOG_IN = 'lgin', 59 MSG_LOG_OUT = 'lgot', 60 MSG_AUTHORIZATION_CHANGED = 'athc', 61 MSG_PACKAGE_STATE_CHANGED = 'mpsc', 62 MSG_SHOW_SOURCE_PACKAGES = 'ssrc', 63 MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' 64 }; 65 66 67 using namespace BPackageKit; 68 using namespace BPackageKit::BManager::BPrivate; 69 70 71 typedef std::map<BString, PackageInfoRef> PackageInfoMap; 72 typedef std::map<BString, DepotInfo> DepotInfoMap; 73 74 75 struct RefreshWorkerParameters { 76 MainWindow* window; 77 bool forceRefresh; 78 79 RefreshWorkerParameters(MainWindow* window, bool forceRefresh) 80 : 81 window(window), 82 forceRefresh(forceRefresh) 83 { 84 } 85 }; 86 87 88 class MessageModelListener : public ModelListener { 89 public: 90 MessageModelListener(const BMessenger& messenger) 91 : 92 fMessenger(messenger) 93 { 94 } 95 96 virtual void AuthorizationChanged() 97 { 98 if (fMessenger.IsValid()) 99 fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED); 100 } 101 102 private: 103 BMessenger fMessenger; 104 }; 105 106 107 MainWindow::MainWindow(BRect frame, const BMessage& settings) 108 : 109 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"), 110 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 111 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 112 fModelListener(new MessageModelListener(BMessenger(this)), true), 113 fTerminating(false), 114 fSinglePackageMode(false), 115 fModelWorker(B_BAD_THREAD_ID) 116 { 117 BMenuBar* menuBar = new BMenuBar(B_TRANSLATE("Main Menu")); 118 _BuildMenu(menuBar); 119 120 fFilterView = new FilterView(); 121 fPackageListView = new PackageListView(fModel.Lock()); 122 fPackageInfoView = new PackageInfoView(fModel.Lock(), this); 123 124 fSplitView = new BSplitView(B_VERTICAL, 5.0f); 125 126 BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f) 127 .Add(menuBar) 128 .Add(fFilterView) 129 .AddSplit(fSplitView) 130 .AddGroup(B_VERTICAL) 131 .Add(fPackageListView) 132 .SetInsets( 133 B_USE_DEFAULT_SPACING, 0.0f, 134 B_USE_DEFAULT_SPACING, 0.0f) 135 .End() 136 .Add(fPackageInfoView) 137 .End() 138 ; 139 140 fSplitView->SetCollapsible(0, false); 141 fSplitView->SetCollapsible(1, false); 142 143 fModel.AddListener(fModelListener); 144 145 // Restore settings 146 BMessage columnSettings; 147 if (settings.FindMessage("column settings", &columnSettings) == B_OK) 148 fPackageListView->LoadState(&columnSettings); 149 150 bool showOption; 151 if (settings.FindBool("show develop packages", &showOption) == B_OK) 152 fModel.SetShowDevelopPackages(showOption); 153 if (settings.FindBool("show source packages", &showOption) == B_OK) 154 fModel.SetShowSourcePackages(showOption); 155 156 BString username; 157 if (settings.FindString("username", &username) == B_OK 158 && username.Length() > 0) { 159 fModel.SetUsername(username); 160 } 161 162 // start worker threads 163 BPackageRoster().StartWatching(this, 164 B_WATCH_PACKAGE_INSTALLATION_LOCATIONS); 165 166 _StartRefreshWorker(); 167 168 _InitWorkerThreads(); 169 } 170 171 172 MainWindow::MainWindow(BRect frame, const BMessage& settings, 173 const PackageInfoRef& package) 174 : 175 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"), 176 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 177 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 178 fLogOutItem(NULL), 179 fModelListener(new MessageModelListener(BMessenger(this)), true), 180 fTerminating(false), 181 fSinglePackageMode(true), 182 fModelWorker(B_BAD_THREAD_ID) 183 { 184 fFilterView = new FilterView(); 185 fPackageListView = new PackageListView(fModel.Lock()); 186 fPackageInfoView = new PackageInfoView(fModel.Lock(), this); 187 188 BLayoutBuilder::Group<>(this, B_VERTICAL) 189 .Add(fPackageInfoView) 190 .SetInsets(0, B_USE_WINDOW_INSETS, 0, 0) 191 ; 192 193 fModel.AddListener(fModelListener); 194 195 // Restore settings 196 BString username; 197 if (settings.FindString("username", &username) == B_OK 198 && username.Length() > 0) { 199 fModel.SetUsername(username); 200 } 201 202 fPackageInfoView->SetPackage(package); 203 204 _InitWorkerThreads(); 205 } 206 207 208 MainWindow::~MainWindow() 209 { 210 BPackageRoster().StopWatching(this); 211 212 fTerminating = true; 213 if (fModelWorker > 0) 214 wait_for_thread(fModelWorker, NULL); 215 216 delete_sem(fPendingActionsSem); 217 wait_for_thread(fPendingActionsWorker, NULL); 218 219 delete_sem(fPackageToPopulateSem); 220 wait_for_thread(fPopulatePackageWorker, NULL); 221 } 222 223 224 bool 225 MainWindow::QuitRequested() 226 { 227 BMessage settings; 228 StoreSettings(settings); 229 230 BMessage message(MSG_MAIN_WINDOW_CLOSED); 231 message.AddMessage("window settings", &settings); 232 233 be_app->PostMessage(&message); 234 235 return true; 236 } 237 238 239 void 240 MainWindow::MessageReceived(BMessage* message) 241 { 242 switch (message->what) { 243 case MSG_MODEL_WORKER_DONE: 244 { 245 fModelWorker = B_BAD_THREAD_ID; 246 _AdoptModel(); 247 fFilterView->AdoptModel(fModel); 248 break; 249 } 250 case B_SIMPLE_DATA: 251 case B_REFS_RECEIVED: 252 // TODO: ? 253 break; 254 255 case B_PACKAGE_UPDATE: 256 // TODO: We should do a more selective update depending on the 257 // "event", "location", and "change count" fields! 258 _StartRefreshWorker(false); 259 break; 260 261 case MSG_REFRESH_DEPOTS: 262 _StartRefreshWorker(true); 263 break; 264 265 case MSG_LOG_IN: 266 _OpenLoginWindow(BMessage()); 267 break; 268 269 case MSG_LOG_OUT: 270 fModel.SetUsername(""); 271 break; 272 273 case MSG_AUTHORIZATION_CHANGED: 274 _UpdateAuthorization(); 275 break; 276 277 case MSG_SHOW_SOURCE_PACKAGES: 278 { 279 BAutolock locker(fModel.Lock()); 280 fModel.SetShowSourcePackages(!fModel.ShowSourcePackages()); 281 } 282 _AdoptModel(); 283 break; 284 285 case MSG_SHOW_DEVELOP_PACKAGES: 286 { 287 BAutolock locker(fModel.Lock()); 288 fModel.SetShowDevelopPackages(!fModel.ShowDevelopPackages()); 289 } 290 _AdoptModel(); 291 break; 292 293 case MSG_PACKAGE_SELECTED: 294 { 295 BString title; 296 if (message->FindString("title", &title) == B_OK) { 297 int count = fVisiblePackages.CountItems(); 298 for (int i = 0; i < count; i++) { 299 const PackageInfoRef& package 300 = fVisiblePackages.ItemAtFast(i); 301 if (package.Get() != NULL && package->Title() == title) { 302 _AdoptPackage(package); 303 break; 304 } 305 } 306 } else { 307 _ClearPackage(); 308 } 309 break; 310 } 311 312 case MSG_CATEGORY_SELECTED: 313 { 314 BString name; 315 if (message->FindString("name", &name) != B_OK) 316 name = ""; 317 { 318 BAutolock locker(fModel.Lock()); 319 fModel.SetCategory(name); 320 } 321 _AdoptModel(); 322 break; 323 } 324 325 case MSG_FILTER_SELECTED: 326 { 327 BString name; 328 int32 value; 329 if (message->FindString("name", &name) != B_OK 330 || message->FindInt32("be:value", &value) != B_OK) { 331 break; 332 } 333 { 334 BAutolock locker(fModel.Lock()); 335 if (name == "available") { 336 fModel.SetShowAvailablePackages( 337 value == B_CONTROL_ON); 338 } else if (name == "installed") { 339 fModel.SetShowInstalledPackages( 340 value == B_CONTROL_ON); 341 } else if (name == "development") { 342 fModel.SetShowDevelopPackages( 343 value == B_CONTROL_ON); 344 } else if (name == "source code") { 345 fModel.SetShowSourcePackages( 346 value == B_CONTROL_ON); 347 } else { 348 break; 349 } 350 } 351 _AdoptModel(); 352 break; 353 } 354 355 case MSG_DEPOT_SELECTED: 356 { 357 BString name; 358 if (message->FindString("name", &name) != B_OK) 359 name = ""; 360 { 361 BAutolock locker(fModel.Lock()); 362 fModel.SetDepot(name); 363 } 364 _AdoptModel(); 365 break; 366 } 367 368 case MSG_SEARCH_TERMS_MODIFIED: 369 { 370 // TODO: Do this with a delay! 371 BString searchTerms; 372 if (message->FindString("search terms", &searchTerms) != B_OK) 373 searchTerms = ""; 374 { 375 BAutolock locker(fModel.Lock()); 376 fModel.SetSearchTerms(searchTerms); 377 } 378 _AdoptModel(); 379 break; 380 } 381 382 case MSG_PACKAGE_STATE_CHANGED: 383 { 384 PackageInfo* info; 385 if (message->FindPointer("package", (void**)&info) == B_OK) { 386 PackageInfoRef ref(info, true); 387 BAutolock locker(fModel.Lock()); 388 fModel.SetPackageState(ref, ref->State()); 389 } 390 break; 391 } 392 393 case MSG_RATE_PACKAGE: 394 _RatePackage(); 395 break; 396 397 default: 398 BWindow::MessageReceived(message); 399 break; 400 } 401 } 402 403 404 void 405 MainWindow::StoreSettings(BMessage& settings) const 406 { 407 if (fSinglePackageMode) 408 settings.AddRect("small window frame", Frame()); 409 else { 410 settings.AddRect("window frame", Frame()); 411 412 BMessage columnSettings; 413 fPackageListView->SaveState(&columnSettings); 414 415 settings.AddMessage("column settings", &columnSettings); 416 417 settings.AddBool("show develop packages", fModel.ShowDevelopPackages()); 418 settings.AddBool("show source packages", fModel.ShowSourcePackages()); 419 } 420 421 settings.AddString("username", fModel.Username()); 422 } 423 424 425 void 426 MainWindow::PackageChanged(const PackageInfoEvent& event) 427 { 428 if ((event.Changes() & PKG_CHANGED_STATE) != 0) { 429 PackageInfoRef ref(event.Package()); 430 BMessage message(MSG_PACKAGE_STATE_CHANGED); 431 message.AddPointer("package", ref.Get()); 432 ref.Detach(); 433 // reference needs to be released by MessageReceived(); 434 PostMessage(&message); 435 } 436 } 437 438 439 status_t 440 MainWindow::SchedulePackageActions(PackageActionList& list) 441 { 442 AutoLocker<BLocker> lock(&fPendingActionsLock); 443 for (int32 i = 0; i < list.CountItems(); i++) { 444 if (!fPendingActions.Add(list.ItemAtFast(i))) 445 return B_NO_MEMORY; 446 } 447 448 return release_sem_etc(fPendingActionsSem, list.CountItems(), 0); 449 } 450 451 452 Model* 453 MainWindow::GetModel() 454 { 455 return &fModel; 456 } 457 458 459 void 460 MainWindow::_BuildMenu(BMenuBar* menuBar) 461 { 462 BMenu* menu = new BMenu(B_TRANSLATE("Tools")); 463 menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"), 464 new BMessage(MSG_REFRESH_DEPOTS))); 465 menu->AddSeparatorItem(); 466 menu->AddItem(new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS), 467 new BMessage(MSG_LOG_IN))); 468 fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"), 469 new BMessage(MSG_LOG_OUT)); 470 menu->AddItem(fLogOutItem); 471 menuBar->AddItem(menu); 472 473 // menu = new BMenu(B_TRANSLATE("Options")); 474 // 475 // fShowDevelopPackagesItem = new BMenuItem( 476 // B_TRANSLATE("Show develop packages"), 477 // new BMessage(MSG_SHOW_DEVELOP_PACKAGES)); 478 // menu->AddItem(fShowDevelopPackagesItem); 479 // 480 // fShowSourcePackagesItem = new BMenuItem(B_TRANSLATE("Show source packages"), 481 // new BMessage(MSG_SHOW_SOURCE_PACKAGES)); 482 // menu->AddItem(fShowSourcePackagesItem); 483 // 484 // menuBar->AddItem(menu); 485 } 486 487 488 void 489 MainWindow::_InitWorkerThreads() 490 { 491 fPendingActionsSem = create_sem(0, "PendingPackageActions"); 492 if (fPendingActionsSem >= 0) { 493 fPendingActionsWorker = spawn_thread(&_PackageActionWorker, 494 "Planet Express", B_NORMAL_PRIORITY, this); 495 if (fPendingActionsWorker >= 0) 496 resume_thread(fPendingActionsWorker); 497 } 498 499 fPackageToPopulateSem = create_sem(0, "PopulatePackage"); 500 if (fPackageToPopulateSem >= 0) { 501 fPopulatePackageWorker = spawn_thread(&_PopulatePackageWorker, 502 "Package Populator", B_NORMAL_PRIORITY, this); 503 if (fPopulatePackageWorker >= 0) 504 resume_thread(fPopulatePackageWorker); 505 } 506 } 507 508 509 void 510 MainWindow::_AdoptModel() 511 { 512 fVisiblePackages = fModel.CreatePackageList(); 513 514 fPackageListView->Clear(); 515 for (int32 i = 0; i < fVisiblePackages.CountItems(); i++) { 516 BAutolock locker(fModel.Lock()); 517 fPackageListView->AddPackage(fVisiblePackages.ItemAtFast(i)); 518 } 519 520 BAutolock locker(fModel.Lock()); 521 // fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages()); 522 // fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages()); 523 fFilterView->AdoptCheckmarks(fModel); 524 } 525 526 527 void 528 MainWindow::_AdoptPackage(const PackageInfoRef& package) 529 { 530 fPackageInfoView->SetPackage(package); 531 532 // Trigger asynchronous package population from the web-app 533 { 534 AutoLocker<BLocker> lock(&fPackageToPopulateLock); 535 fPackageToPopulate = package; 536 } 537 release_sem_etc(fPackageToPopulateSem, 1, 0); 538 } 539 540 541 void 542 MainWindow::_ClearPackage() 543 { 544 fPackageInfoView->Clear(); 545 } 546 547 548 void 549 MainWindow::_RefreshRepositories(bool force) 550 { 551 BPackageRoster roster; 552 BStringList repositoryNames; 553 554 status_t result = roster.GetRepositoryNames(repositoryNames); 555 if (result != B_OK) 556 return; 557 558 DecisionProvider decisionProvider; 559 JobStateListener listener; 560 BContext context(decisionProvider, listener); 561 562 BRepositoryCache cache; 563 for (int32 i = 0; i < repositoryNames.CountStrings(); ++i) { 564 const BString& repoName = repositoryNames.StringAt(i); 565 BRepositoryConfig repoConfig; 566 result = roster.GetRepositoryConfig(repoName, &repoConfig); 567 if (result != B_OK) { 568 // TODO: notify user 569 continue; 570 } 571 572 if (roster.GetRepositoryCache(repoName, &cache) != B_OK || force) { 573 try { 574 BRefreshRepositoryRequest refreshRequest(context, repoConfig); 575 576 result = refreshRequest.Process(); 577 } catch (BFatalErrorException ex) { 578 BString message(B_TRANSLATE("An error occurred while " 579 "refreshing the repository: %error% (%details%)")); 580 message.ReplaceFirst("%error%", ex.Message()); 581 message.ReplaceFirst("%details%", ex.Details()); 582 _NotifyUser("Error", message.String()); 583 } catch (BException ex) { 584 BString message(B_TRANSLATE("An error occurred while " 585 "refreshing the repository: %error%")); 586 message.ReplaceFirst("%error%", ex.Message()); 587 _NotifyUser("Error", message.String()); 588 } 589 } 590 } 591 } 592 593 594 void 595 MainWindow::_RefreshPackageList() 596 { 597 BPackageRoster roster; 598 BStringList repositoryNames; 599 600 status_t result = roster.GetRepositoryNames(repositoryNames); 601 if (result != B_OK) 602 return; 603 604 DepotInfoMap depots; 605 for (int32 i = 0; i < repositoryNames.CountStrings(); i++) { 606 const BString& repoName = repositoryNames.StringAt(i); 607 depots[repoName] = DepotInfo(repoName); 608 } 609 610 PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME); 611 try { 612 manager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES 613 | PackageManager::B_ADD_REMOTE_REPOSITORIES); 614 } catch (BException ex) { 615 BString message(B_TRANSLATE("An error occurred while " 616 "initializing the package manager: %message%")); 617 message.ReplaceFirst("%message%", ex.Message()); 618 _NotifyUser("Error", message.String()); 619 return; 620 } 621 622 BObjectList<BSolverPackage> packages; 623 result = manager.Solver()->FindPackages("", 624 BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME 625 | BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION 626 | BSolver::B_FIND_IN_PROVIDES, 627 packages); 628 if (result != B_OK) { 629 BString message(B_TRANSLATE("An error occurred while " 630 "obtaining the package list: %message%")); 631 message.ReplaceFirst("%message%", strerror(result)); 632 _NotifyUser("Error", message.String()); 633 return; 634 } 635 636 if (packages.IsEmpty()) 637 return; 638 639 PackageInfoMap foundPackages; 640 // if a given package is installed locally, we will potentially 641 // get back multiple entries, one for each local installation 642 // location, and one for each remote repository the package 643 // is available in. The above map is used to ensure that in such 644 // cases we consolidate the information, rather than displaying 645 // duplicates 646 PackageInfoMap remotePackages; 647 // any package that we find in a remote repository goes in this map. 648 // this is later used to discern which packages came from a local 649 // installation only, as those must be handled a bit differently 650 // upon uninstallation, since we'd no longer be able to pull them 651 // down remotely. 652 BStringList systemFlaggedPackages; 653 // any packages flagged as a system package are added to this list. 654 // such packages cannot be uninstalled, nor can any of their deps. 655 PackageInfoMap systemInstalledPackages; 656 // any packages installed in system are added to this list. 657 // This is later used for dependency resolution of the actual 658 // system packages in order to compute the list of protected 659 // dependencies indicated above. 660 661 for (int32 i = 0; i < packages.CountItems(); i++) { 662 BSolverPackage* package = packages.ItemAt(i); 663 const BPackageInfo& repoPackageInfo = package->Info(); 664 PackageInfoRef modelInfo; 665 PackageInfoMap::iterator it = foundPackages.find( 666 repoPackageInfo.Name()); 667 if (it != foundPackages.end()) 668 modelInfo.SetTo(it->second); 669 else { 670 // Add new package info 671 modelInfo.SetTo(new(std::nothrow) PackageInfo(repoPackageInfo), 672 true); 673 674 if (modelInfo.Get() == NULL) 675 return; 676 677 foundPackages[repoPackageInfo.Name()] = modelInfo; 678 } 679 680 modelInfo->AddListener(this); 681 682 BSolverRepository* repository = package->Repository(); 683 if (dynamic_cast<BPackageManager::RemoteRepository*>(repository) 684 != NULL) { 685 depots[repository->Name()].AddPackage(modelInfo); 686 remotePackages[modelInfo->Title()] = modelInfo; 687 } else { 688 if (repository == static_cast<const BSolverRepository*>( 689 manager.SystemRepository())) { 690 modelInfo->AddInstallationLocation( 691 B_PACKAGE_INSTALLATION_LOCATION_SYSTEM); 692 if (!modelInfo->IsSystemPackage()) { 693 systemInstalledPackages[repoPackageInfo.FileName()] 694 = modelInfo; 695 } 696 } else if (repository == static_cast<const BSolverRepository*>( 697 manager.HomeRepository())) { 698 modelInfo->AddInstallationLocation( 699 B_PACKAGE_INSTALLATION_LOCATION_HOME); 700 } 701 } 702 703 if (modelInfo->IsSystemPackage()) 704 systemFlaggedPackages.Add(repoPackageInfo.FileName()); 705 } 706 707 BAutolock lock(fModel.Lock()); 708 709 fModel.Clear(); 710 711 // filter remote packages from the found list 712 // any packages remaining will be locally installed packages 713 // that weren't acquired from a repository 714 for (PackageInfoMap::iterator it = remotePackages.begin(); 715 it != remotePackages.end(); it++) { 716 foundPackages.erase(it->first); 717 } 718 719 if (!foundPackages.empty()) { 720 BString repoName = B_TRANSLATE("Local"); 721 depots[repoName] = DepotInfo(repoName); 722 DepotInfoMap::iterator depot = depots.find(repoName); 723 for (PackageInfoMap::iterator it = foundPackages.begin(); 724 it != foundPackages.end(); ++it) { 725 depot->second.AddPackage(it->second); 726 } 727 } 728 729 for (DepotInfoMap::iterator it = depots.begin(); it != depots.end(); it++) { 730 fModel.AddDepot(it->second); 731 } 732 733 // start retrieving package icons and average ratings 734 fModel.PopulateAllPackages(); 735 736 // compute the OS package dependencies 737 try { 738 // create the solver 739 BSolver* solver; 740 status_t error = BSolver::Create(solver); 741 if (error != B_OK) 742 throw BFatalErrorException(error, "Failed to create solver."); 743 744 ObjectDeleter<BSolver> solverDeleter(solver); 745 BPath systemPath; 746 error = find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &systemPath); 747 if (error != B_OK) { 748 throw BFatalErrorException(error, 749 "Unable to retrieve system packages directory."); 750 } 751 752 // add the "installed" repository with the given packages 753 BSolverRepository installedRepository; 754 { 755 BRepositoryBuilder installedRepositoryBuilder(installedRepository, 756 "installed"); 757 for (int32 i = 0; i < systemFlaggedPackages.CountStrings(); i++) { 758 BPath packagePath(systemPath); 759 packagePath.Append(systemFlaggedPackages.StringAt(i)); 760 installedRepositoryBuilder.AddPackage(packagePath.Path()); 761 } 762 installedRepositoryBuilder.AddToSolver(solver, true); 763 } 764 765 // add system repository 766 BSolverRepository systemRepository; 767 { 768 BRepositoryBuilder systemRepositoryBuilder(systemRepository, 769 "system"); 770 for (PackageInfoMap::iterator it = systemInstalledPackages.begin(); 771 it != systemInstalledPackages.end(); it++) { 772 BPath packagePath(systemPath); 773 packagePath.Append(it->first); 774 systemRepositoryBuilder.AddPackage(packagePath.Path()); 775 } 776 systemRepositoryBuilder.AddToSolver(solver, false); 777 } 778 779 // solve 780 error = solver->VerifyInstallation(); 781 if (error != B_OK) { 782 throw BFatalErrorException(error, "Failed to compute packages to " 783 "install."); 784 } 785 786 BSolverResult solverResult; 787 error = solver->GetResult(solverResult); 788 if (error != B_OK) { 789 throw BFatalErrorException(error, "Failed to retrieve system " 790 "package dependency list."); 791 } 792 793 for (int32 i = 0; const BSolverResultElement* element 794 = solverResult.ElementAt(i); i++) { 795 BSolverPackage* package = element->Package(); 796 if (element->Type() == BSolverResultElement::B_TYPE_INSTALL) { 797 PackageInfoMap::iterator it = systemInstalledPackages.find( 798 package->Info().FileName()); 799 if (it != systemInstalledPackages.end()) 800 it->second->SetSystemDependency(true); 801 } 802 } 803 } catch (BFatalErrorException ex) { 804 printf("Fatal exception occurred while resolving system dependencies: " 805 "%s, details: %s\n", strerror(ex.Error()), ex.Details().String()); 806 } catch (BNothingToDoException) { 807 // do nothing 808 } catch (BException ex) { 809 printf("Exception occurred while resolving system dependencies: %s\n", 810 ex.Message().String()); 811 } catch (...) { 812 printf("Unknown exception occurred while resolving system " 813 "dependencies.\n"); 814 } 815 } 816 817 818 void 819 MainWindow::_StartRefreshWorker(bool force) 820 { 821 if (fModelWorker != B_BAD_THREAD_ID) 822 return; 823 824 RefreshWorkerParameters* parameters = new(std::nothrow) 825 RefreshWorkerParameters(this, force); 826 if (parameters == NULL) 827 return; 828 829 ObjectDeleter<RefreshWorkerParameters> deleter(parameters); 830 fModelWorker = spawn_thread(&_RefreshModelThreadWorker, "model loader", 831 B_LOW_PRIORITY, parameters); 832 833 if (fModelWorker > 0) { 834 deleter.Detach(); 835 resume_thread(fModelWorker); 836 } 837 } 838 839 840 status_t 841 MainWindow::_RefreshModelThreadWorker(void* arg) 842 { 843 RefreshWorkerParameters* parameters 844 = reinterpret_cast<RefreshWorkerParameters*>(arg); 845 MainWindow* mainWindow = parameters->window; 846 ObjectDeleter<RefreshWorkerParameters> deleter(parameters); 847 848 BMessenger messenger(mainWindow); 849 850 mainWindow->_RefreshRepositories(parameters->forceRefresh); 851 852 if (mainWindow->fTerminating) 853 return B_OK; 854 855 mainWindow->_RefreshPackageList(); 856 857 messenger.SendMessage(MSG_MODEL_WORKER_DONE); 858 859 return B_OK; 860 } 861 862 863 status_t 864 MainWindow::_PackageActionWorker(void* arg) 865 { 866 MainWindow* window = reinterpret_cast<MainWindow*>(arg); 867 868 while (acquire_sem(window->fPendingActionsSem) == B_OK) { 869 PackageActionRef ref; 870 { 871 AutoLocker<BLocker> lock(&window->fPendingActionsLock); 872 ref = window->fPendingActions.ItemAt(0); 873 if (ref.Get() == NULL) 874 break; 875 window->fPendingActions.Remove(0); 876 } 877 878 ref->Perform(); 879 } 880 881 return 0; 882 } 883 884 885 status_t 886 MainWindow::_PopulatePackageWorker(void* arg) 887 { 888 MainWindow* window = reinterpret_cast<MainWindow*>(arg); 889 890 while (acquire_sem(window->fPackageToPopulateSem) == B_OK) { 891 PackageInfoRef package; 892 { 893 AutoLocker<BLocker> lock(&window->fPackageToPopulateLock); 894 package = window->fPackageToPopulate; 895 } 896 897 if (package.Get() != NULL) { 898 window->fModel.PopulatePackage(package, 899 Model::POPULATE_USER_RATINGS | Model::POPULATE_SCREEN_SHOTS 900 | Model::POPULATE_CHANGELOG); 901 } 902 } 903 904 return 0; 905 } 906 907 908 void 909 MainWindow::_NotifyUser(const char* title, const char* message) 910 { 911 BAlert* alert = new(std::nothrow) BAlert(title, message, 912 B_TRANSLATE("Close")); 913 914 if (alert != NULL) 915 alert->Go(); 916 } 917 918 919 void 920 MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage) 921 { 922 UserLoginWindow* window = new UserLoginWindow(this, 923 BRect(0, 0, 500, 400), fModel); 924 925 if (onSuccessMessage.what != 0) 926 window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage); 927 928 window->Show(); 929 } 930 931 932 void 933 MainWindow::_UpdateAuthorization() 934 { 935 BString username(fModel.Username()); 936 if (fLogOutItem != NULL) 937 fLogOutItem->SetEnabled(username.Length() > 0); 938 fFilterView->SetUsername(username); 939 } 940 941 942 void 943 MainWindow::_RatePackage() 944 { 945 if (fModel.Username().IsEmpty()) { 946 BAlert* alert = new(std::nothrow) BAlert( 947 B_TRANSLATE("Not logged in"), 948 B_TRANSLATE("You need to be logged into an account before you " 949 "can rate packages."), 950 B_TRANSLATE("Cancel"), 951 B_TRANSLATE("Login or Create account")); 952 953 if (alert == NULL) 954 return; 955 956 int32 choice = alert->Go(); 957 if (choice == 1) 958 _OpenLoginWindow(BMessage(MSG_RATE_PACKAGE)); 959 return; 960 } 961 962 // TODO: Allow only one RatePackageWindow 963 // TODO: Mechanism for remembering the window frame 964 RatePackageWindow* window = new RatePackageWindow(this, 965 BRect(0, 0, 500, 400), fModel); 966 window->SetPackage(fPackageInfoView->Package()); 967 window->Show(); 968 } 969