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, 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.
12 #include "MainWindow.h"
20 #include <Application.h>
23 #include <CardLayout.h>
24 #include <LayoutBuilder.h>
27 #include <Messenger.h>
30 #include <ScrollView.h>
31 #include <StringList.h>
32 #include <StringView.h>
35 #include <package/Context.h>
36 #include <package/manager/Exceptions.h>
37 #include <package/manager/RepositoryBuilder.h>
38 #include <package/RefreshRepositoryRequest.h>
39 #include <package/PackageRoster.h>
40 #include "package/RepositoryCache.h"
41 #include <package/solver/SolverPackage.h>
42 #include <package/solver/SolverProblem.h>
43 #include <package/solver/SolverProblemSolution.h>
44 #include <package/solver/SolverRepository.h>
45 #include <package/solver/SolverResult.h>
47 #include "AutoDeleter.h"
48 #include "AutoLocker.h"
49 #include "DecisionProvider.h"
50 #include "FeaturedPackagesView.h"
51 #include "FilterView.h"
52 #include "JobStateListener.h"
53 #include "PackageInfoView.h"
54 #include "PackageListView.h"
55 #include "PackageManager.h"
56 #include "RatePackageWindow.h"
58 #include "ScreenshotWindow.h"
59 #include "UserLoginWindow.h"
60 #include "WorkStatusView.h"
63 #undef B_TRANSLATION_CONTEXT
64 #define B_TRANSLATION_CONTEXT "MainWindow"
68 MSG_MODEL_WORKER_DONE
= 'mmwd',
69 MSG_REFRESH_REPOS
= 'mrrp',
70 MSG_MANAGE_REPOS
= 'mmrp',
71 MSG_SOFTWARE_UPDATER
= 'mswu',
74 MSG_AUTHORIZATION_CHANGED
= 'athc',
75 MSG_PACKAGE_CHANGED
= 'pchd',
77 MSG_SHOW_FEATURED_PACKAGES
= 'sofp',
78 MSG_SHOW_AVAILABLE_PACKAGES
= 'savl',
79 MSG_SHOW_INSTALLED_PACKAGES
= 'sins',
80 MSG_SHOW_SOURCE_PACKAGES
= 'ssrc',
81 MSG_SHOW_DEVELOP_PACKAGES
= 'sdvl'
85 using namespace BPackageKit
;
86 using namespace BPackageKit::BManager::BPrivate
;
89 typedef std::map
<BString
, PackageInfoRef
> PackageInfoMap
;
90 typedef std::map
<BString
, DepotInfo
> DepotInfoMap
;
93 struct RefreshWorkerParameters
{
97 RefreshWorkerParameters(MainWindow
* window
, bool forceRefresh
)
100 forceRefresh(forceRefresh
)
106 class MessageModelListener
: public ModelListener
{
108 MessageModelListener(const BMessenger
& messenger
)
110 fMessenger(messenger
)
114 virtual void AuthorizationChanged()
116 if (fMessenger
.IsValid())
117 fMessenger
.SendMessage(MSG_AUTHORIZATION_CHANGED
);
121 BMessenger fMessenger
;
125 MainWindow::MainWindow(const BMessage
& settings
)
127 BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
128 B_DOCUMENT_WINDOW_LOOK
, B_NORMAL_WINDOW_FEEL
,
129 B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
),
130 fScreenshotWindow(NULL
),
134 fModelListener(new MessageModelListener(BMessenger(this)), true),
135 fBulkLoadStateMachine(&fModel
),
137 fSinglePackageMode(false),
138 fModelWorker(B_BAD_THREAD_ID
)
140 BMenuBar
* menuBar
= new BMenuBar(B_TRANSLATE("Main Menu"));
143 BMenuBar
* userMenuBar
= new BMenuBar(B_TRANSLATE("User Menu"));
144 _BuildUserMenu(userMenuBar
);
145 set_small_font(userMenuBar
);
146 userMenuBar
->SetExplicitMaxSize(BSize(B_SIZE_UNSET
,
147 menuBar
->MaxSize().height
));
149 fFilterView
= new FilterView();
150 fFeaturedPackagesView
= new FeaturedPackagesView();
151 fPackageListView
= new PackageListView(fModel
.Lock());
152 fPackageInfoView
= new PackageInfoView(fModel
.Lock(), this);
154 fSplitView
= new BSplitView(B_VERTICAL
, 5.0f
);
156 BGroupView
* featuredPackagesGroup
= new BGroupView(B_VERTICAL
);
157 BStringView
* featuredPackagesTitle
= new BStringView(
158 "featured packages title", B_TRANSLATE("Featured packages"));
159 BFont
font(be_bold_font
);
160 font
.SetSize(font
.Size() * 1.3f
);
161 featuredPackagesTitle
->SetFont(&font
);
162 featuredPackagesGroup
->SetExplicitMaxSize(
163 BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
164 BLayoutBuilder::Group
<>(featuredPackagesGroup
)
165 .Add(featuredPackagesTitle
)
166 .Add(fFeaturedPackagesView
)
169 fWorkStatusView
= new WorkStatusView("work status");
170 fPackageListView
->AttachWorkStatusView(fWorkStatusView
);
172 BView
* listArea
= new BView("list area", 0);
173 fListLayout
= new BCardLayout();
174 listArea
->SetLayout(fListLayout
);
175 listArea
->AddChild(featuredPackagesGroup
);
176 listArea
->AddChild(fPackageListView
);
178 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0.0f
)
179 .AddGroup(B_HORIZONTAL
, 0.0f
)
181 .Add(userMenuBar
, 0.0f
)
184 .AddSplit(fSplitView
)
185 .AddGroup(B_VERTICAL
)
188 B_USE_DEFAULT_SPACING
, 0.0f
,
189 B_USE_DEFAULT_SPACING
, 0.0f
)
191 .Add(fPackageInfoView
)
193 .Add(fWorkStatusView
)
196 fSplitView
->SetCollapsible(0, false);
197 fSplitView
->SetCollapsible(1, false);
199 fModel
.AddListener(fModelListener
);
202 BMessage columnSettings
;
203 if (settings
.FindMessage("column settings", &columnSettings
) == B_OK
)
204 fPackageListView
->LoadState(&columnSettings
);
207 if (settings
.FindBool("show featured packages", &showOption
) == B_OK
)
208 fModel
.SetShowFeaturedPackages(showOption
);
209 if (settings
.FindBool("show available packages", &showOption
) == B_OK
)
210 fModel
.SetShowAvailablePackages(showOption
);
211 if (settings
.FindBool("show installed packages", &showOption
) == B_OK
)
212 fModel
.SetShowInstalledPackages(showOption
);
213 if (settings
.FindBool("show develop packages", &showOption
) == B_OK
)
214 fModel
.SetShowDevelopPackages(showOption
);
215 if (settings
.FindBool("show source packages", &showOption
) == B_OK
)
216 fModel
.SetShowSourcePackages(showOption
);
218 if (fModel
.ShowFeaturedPackages())
219 fListLayout
->SetVisibleItem((int32
)0);
221 fListLayout
->SetVisibleItem(1);
223 _RestoreUserName(settings
);
224 _RestoreWindowFrame(settings
);
226 atomic_set(&fPackagesToShowListID
, 0);
228 // start worker threads
229 BPackageRoster().StartWatching(this,
230 B_WATCH_PACKAGE_INSTALLATION_LOCATIONS
);
232 _StartRefreshWorker();
234 _InitWorkerThreads();
238 MainWindow::MainWindow(const BMessage
& settings
, const PackageInfoRef
& package
)
240 BWindow(BRect(50, 50, 650, 350), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
241 B_DOCUMENT_WINDOW_LOOK
, B_NORMAL_WINDOW_FEEL
,
242 B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
),
243 fWorkStatusView(NULL
),
244 fScreenshotWindow(NULL
),
248 fModelListener(new MessageModelListener(BMessenger(this)), true),
249 fBulkLoadStateMachine(&fModel
),
251 fSinglePackageMode(true),
252 fModelWorker(B_BAD_THREAD_ID
)
254 fFilterView
= new FilterView();
255 fPackageListView
= new PackageListView(fModel
.Lock());
256 fPackageInfoView
= new PackageInfoView(fModel
.Lock(), this);
258 BLayoutBuilder::Group
<>(this, B_VERTICAL
)
259 .Add(fPackageInfoView
)
260 .SetInsets(0, B_USE_WINDOW_INSETS
, 0, 0)
263 fModel
.AddListener(fModelListener
);
266 _RestoreUserName(settings
);
267 _RestoreWindowFrame(settings
);
269 fPackageInfoView
->SetPackage(package
);
271 _InitWorkerThreads();
275 MainWindow::~MainWindow()
277 BPackageRoster().StopWatching(this);
280 if (fModelWorker
>= 0)
281 wait_for_thread(fModelWorker
, NULL
);
283 delete_sem(fPendingActionsSem
);
284 if (fPendingActionsWorker
>= 0)
285 wait_for_thread(fPendingActionsWorker
, NULL
);
287 delete_sem(fPackageToPopulateSem
);
288 if (fPopulatePackageWorker
>= 0)
289 wait_for_thread(fPopulatePackageWorker
, NULL
);
291 delete_sem(fNewPackagesToShowSem
);
292 delete_sem(fShowPackagesAcknowledgeSem
);
293 if (fShowPackagesWorker
>= 0)
294 wait_for_thread(fShowPackagesWorker
, NULL
);
296 if (fScreenshotWindow
!= NULL
&& fScreenshotWindow
->Lock())
297 fScreenshotWindow
->Quit();
302 MainWindow::QuitRequested()
305 StoreSettings(settings
);
307 BMessage
message(MSG_MAIN_WINDOW_CLOSED
);
308 message
.AddMessage("window settings", &settings
);
310 be_app
->PostMessage(&message
);
317 MainWindow::MessageReceived(BMessage
* message
)
319 switch (message
->what
) {
320 case MSG_MODEL_WORKER_DONE
:
322 fModelWorker
= B_BAD_THREAD_ID
;
324 fFilterView
->AdoptModel(fModel
);
325 fWorkStatusView
->SetIdle();
329 case B_REFS_RECEIVED
:
333 case B_PACKAGE_UPDATE
:
334 // TODO: We should do a more selective update depending on the
335 // "event", "location", and "change count" fields!
336 _StartRefreshWorker(false);
339 case MSG_REFRESH_REPOS
:
340 _StartRefreshWorker(true);
343 case MSG_MANAGE_REPOS
:
344 be_roster
->Launch("application/x-vnd.Haiku-Repositories");
347 case MSG_SOFTWARE_UPDATER
:
348 be_roster
->Launch("application/x-vnd.haiku-softwareupdater");
352 _OpenLoginWindow(BMessage());
356 fModel
.SetUsername("");
359 case MSG_AUTHORIZATION_CHANGED
:
360 _UpdateAuthorization();
363 case MSG_SHOW_FEATURED_PACKAGES
:
365 BAutolock
locker(fModel
.Lock());
366 fModel
.SetShowFeaturedPackages(
367 !fModel
.ShowFeaturedPackages());
372 case MSG_SHOW_AVAILABLE_PACKAGES
:
374 BAutolock
locker(fModel
.Lock());
375 fModel
.SetShowAvailablePackages(
376 !fModel
.ShowAvailablePackages());
381 case MSG_SHOW_INSTALLED_PACKAGES
:
383 BAutolock
locker(fModel
.Lock());
384 fModel
.SetShowInstalledPackages(
385 !fModel
.ShowInstalledPackages());
390 case MSG_SHOW_SOURCE_PACKAGES
:
392 BAutolock
locker(fModel
.Lock());
393 fModel
.SetShowSourcePackages(!fModel
.ShowSourcePackages());
398 case MSG_SHOW_DEVELOP_PACKAGES
:
400 BAutolock
locker(fModel
.Lock());
401 fModel
.SetShowDevelopPackages(!fModel
.ShowDevelopPackages());
406 case MSG_PACKAGE_SELECTED
:
409 if (message
->FindString("name", &name
) == B_OK
) {
410 BAutolock
locker(fModel
.Lock());
411 int count
= fVisiblePackages
.CountItems();
412 for (int i
= 0; i
< count
; i
++) {
413 const PackageInfoRef
& package
414 = fVisiblePackages
.ItemAtFast(i
);
415 if (package
.Get() != NULL
&& package
->Name() == name
) {
417 _AdoptPackage(package
);
427 case MSG_CATEGORY_SELECTED
:
430 if (message
->FindString("name", &name
) != B_OK
)
433 BAutolock
locker(fModel
.Lock());
434 fModel
.SetCategory(name
);
440 case MSG_DEPOT_SELECTED
:
443 if (message
->FindString("name", &name
) != B_OK
)
446 BAutolock
locker(fModel
.Lock());
447 fModel
.SetDepot(name
);
453 case MSG_SEARCH_TERMS_MODIFIED
:
455 // TODO: Do this with a delay!
457 if (message
->FindString("search terms", &searchTerms
) != B_OK
)
460 BAutolock
locker(fModel
.Lock());
461 fModel
.SetSearchTerms(searchTerms
);
467 case MSG_PACKAGE_CHANGED
:
470 if (message
->FindPointer("package", (void**)&info
) == B_OK
) {
471 PackageInfoRef
ref(info
, true);
473 if (message
->FindUInt32("changes", &changes
) != B_OK
)
475 if ((changes
& PKG_CHANGED_STATE
) != 0) {
476 BAutolock
locker(fModel
.Lock());
477 fModel
.SetPackageState(ref
, ref
->State());
480 // Asynchronous updates to the package information
481 // can mean that the package needs to be added or
482 // removed to/from the visible packages when the current
483 // filter parameters are re-evaluated on this package.
484 bool wasVisible
= fVisiblePackages
.Contains(ref
);
487 BAutolock
locker(fModel
.Lock());
488 // The package didn't get a chance yet to be in the
489 // visible package list
490 isVisible
= fModel
.MatchesFilter(ref
);
492 // Transfer this single package, otherwise we miss
493 // other packages if they appear or disappear along
494 // with this one before receive a notification for
497 fVisiblePackages
.Add(ref
);
498 } else if (wasVisible
)
499 fVisiblePackages
.Remove(ref
);
502 if (wasVisible
!= isVisible
) {
504 fPackageListView
->RemovePackage(ref
);
505 fFeaturedPackagesView
->RemovePackage(ref
);
507 fPackageListView
->AddPackage(ref
);
508 if (ref
->IsProminent())
509 fFeaturedPackagesView
->AddPackage(ref
);
513 if (!fSinglePackageMode
&& (changes
& PKG_CHANGED_STATE
) != 0)
514 fWorkStatusView
->PackageStatusChanged(ref
);
519 case MSG_RATE_PACKAGE
:
523 case MSG_SHOW_SCREENSHOT
:
527 case MSG_PACKAGE_WORKER_BUSY
:
530 status_t status
= message
->FindString("reason", &reason
);
533 if (!fSinglePackageMode
)
534 fWorkStatusView
->SetBusy(reason
);
538 case MSG_PACKAGE_WORKER_IDLE
:
539 if (!fSinglePackageMode
)
540 fWorkStatusView
->SetIdle();
543 case MSG_ADD_VISIBLE_PACKAGES
:
545 struct SemaphoreReleaser
{
546 SemaphoreReleaser(sem_id semaphore
)
548 fSemaphore(semaphore
)
551 ~SemaphoreReleaser() { release_sem(fSemaphore
); }
556 // Make sure acknowledge semaphore is released even on error,
557 // so the worker thread won't be blocked
558 SemaphoreReleaser
acknowledger(fShowPackagesAcknowledgeSem
);
560 int32 numPackages
= 0;
562 status_t status
= message
->GetInfo("package_ref", &unused
,
564 if (status
!= B_OK
|| numPackages
== 0)
568 status
= message
->FindInt32("list_id", &listID
);
571 if (listID
!= atomic_get(&fPackagesToShowListID
)) {
572 // list is outdated, ignore
576 for (int i
= 0; i
< numPackages
; i
++) {
577 PackageInfo
* packageRaw
= NULL
;
578 status
= message
->FindPointer("package_ref", i
,
579 (void**)&packageRaw
);
582 PackageInfoRef
package(packageRaw
, true);
584 fPackageListView
->AddPackage(package
);
585 if (package
->IsProminent())
586 fFeaturedPackagesView
->AddPackage(package
);
591 case MSG_UPDATE_SELECTED_PACKAGE
:
593 const PackageInfoRef
& selectedPackage
= fPackageInfoView
->Package();
594 fFeaturedPackagesView
->SelectPackage(selectedPackage
, true);
595 fPackageListView
->SelectPackage(selectedPackage
);
597 AutoLocker
<BLocker
> modelLocker(fModel
.Lock());
598 if (!fVisiblePackages
.Contains(fPackageInfoView
->Package()))
599 fPackageInfoView
->Clear();
604 BWindow::MessageReceived(message
);
611 MainWindow::StoreSettings(BMessage
& settings
) const
613 settings
.AddRect(_WindowFrameName(), Frame());
614 if (!fSinglePackageMode
) {
615 settings
.AddRect("window frame", Frame());
617 BMessage columnSettings
;
618 fPackageListView
->SaveState(&columnSettings
);
620 settings
.AddMessage("column settings", &columnSettings
);
622 settings
.AddBool("show featured packages",
623 fModel
.ShowFeaturedPackages());
624 settings
.AddBool("show available packages",
625 fModel
.ShowAvailablePackages());
626 settings
.AddBool("show installed packages",
627 fModel
.ShowInstalledPackages());
628 settings
.AddBool("show develop packages", fModel
.ShowDevelopPackages());
629 settings
.AddBool("show source packages", fModel
.ShowSourcePackages());
632 settings
.AddString("username", fModel
.Username());
637 MainWindow::PackageChanged(const PackageInfoEvent
& event
)
639 uint32 whatchedChanges
= PKG_CHANGED_STATE
| PKG_CHANGED_PROMINENCE
;
640 if ((event
.Changes() & whatchedChanges
) != 0) {
641 PackageInfoRef
ref(event
.Package());
642 BMessage
message(MSG_PACKAGE_CHANGED
);
643 message
.AddPointer("package", ref
.Get());
644 message
.AddUInt32("changes", event
.Changes());
646 // reference needs to be released by MessageReceived();
647 PostMessage(&message
);
653 MainWindow::SchedulePackageActions(PackageActionList
& list
)
655 AutoLocker
<BLocker
> lock(&fPendingActionsLock
);
656 for (int32 i
= 0; i
< list
.CountItems(); i
++) {
657 if (!fPendingActions
.Add(list
.ItemAtFast(i
)))
661 return release_sem_etc(fPendingActionsSem
, list
.CountItems(), 0);
666 MainWindow::GetModel()
673 MainWindow::_BuildMenu(BMenuBar
* menuBar
)
675 BMenu
* menu
= new BMenu(B_TRANSLATE("Tools"));
676 menu
->AddItem(new BMenuItem(B_TRANSLATE("Refresh repositories"),
677 new BMessage(MSG_REFRESH_REPOS
)));
678 menu
->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"
679 B_UTF8_ELLIPSIS
), new BMessage(MSG_MANAGE_REPOS
)));
680 menu
->AddItem(new BMenuItem(B_TRANSLATE("Check for updates"
681 B_UTF8_ELLIPSIS
), new BMessage(MSG_SOFTWARE_UPDATER
)));
683 menuBar
->AddItem(menu
);
685 menu
= new BMenu(B_TRANSLATE("Show"));
687 fShowFeaturedPackagesItem
= new BMenuItem(
688 B_TRANSLATE("Only featured packages"),
689 new BMessage(MSG_SHOW_FEATURED_PACKAGES
));
690 menu
->AddItem(fShowFeaturedPackagesItem
);
692 menu
->AddSeparatorItem();
694 fShowAvailablePackagesItem
= new BMenuItem(
695 B_TRANSLATE("Available packages"),
696 new BMessage(MSG_SHOW_AVAILABLE_PACKAGES
));
697 menu
->AddItem(fShowAvailablePackagesItem
);
699 fShowInstalledPackagesItem
= new BMenuItem(
700 B_TRANSLATE("Installed packages"),
701 new BMessage(MSG_SHOW_INSTALLED_PACKAGES
));
702 menu
->AddItem(fShowInstalledPackagesItem
);
704 menu
->AddSeparatorItem();
706 fShowDevelopPackagesItem
= new BMenuItem(
707 B_TRANSLATE("Develop packages"),
708 new BMessage(MSG_SHOW_DEVELOP_PACKAGES
));
709 menu
->AddItem(fShowDevelopPackagesItem
);
711 fShowSourcePackagesItem
= new BMenuItem(
712 B_TRANSLATE("Source packages"),
713 new BMessage(MSG_SHOW_SOURCE_PACKAGES
));
714 menu
->AddItem(fShowSourcePackagesItem
);
716 menuBar
->AddItem(menu
);
721 MainWindow::_BuildUserMenu(BMenuBar
* menuBar
)
723 fUserMenu
= new BMenu(B_TRANSLATE("Not logged in"));
725 fLogInItem
= new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS
),
726 new BMessage(MSG_LOG_IN
));
727 fUserMenu
->AddItem(fLogInItem
);
729 fLogOutItem
= new BMenuItem(B_TRANSLATE("Log out"),
730 new BMessage(MSG_LOG_OUT
));
731 fUserMenu
->AddItem(fLogOutItem
);
733 menuBar
->AddItem(fUserMenu
);
738 MainWindow::_RestoreUserName(const BMessage
& settings
)
741 if (settings
.FindString("username", &username
) == B_OK
742 && username
.Length() > 0) {
743 fModel
.SetUsername(username
);
749 MainWindow::_WindowFrameName() const
751 if (fSinglePackageMode
)
752 return "small window frame";
754 return "window frame";
759 MainWindow::_RestoreWindowFrame(const BMessage
& settings
)
761 BRect frame
= Frame();
764 bool fromSettings
= false;
765 if (settings
.FindRect(_WindowFrameName(), &windowFrame
) == B_OK
) {
768 } else if (!fSinglePackageMode
) {
769 // Resize to occupy a certain screen size
770 BRect screenFrame
= BScreen(this).Frame();
771 float width
= frame
.Width();
772 float height
= frame
.Height();
773 if (width
< screenFrame
.Width() * .666f
774 && height
< screenFrame
.Height() * .666f
) {
775 frame
.bottom
= frame
.top
+ screenFrame
.Height() * .666f
;
776 frame
.right
= frame
.left
777 + std::min(screenFrame
.Width() * .666f
, height
* 7 / 5);
781 MoveTo(frame
.LeftTop());
782 ResizeTo(frame
.Width(), frame
.Height());
792 MainWindow::_InitWorkerThreads()
794 fPendingActionsSem
= create_sem(0, "PendingPackageActions");
795 if (fPendingActionsSem
>= 0) {
796 fPendingActionsWorker
= spawn_thread(&_PackageActionWorker
,
797 "Planet Express", B_NORMAL_PRIORITY
, this);
798 if (fPendingActionsWorker
>= 0)
799 resume_thread(fPendingActionsWorker
);
801 fPendingActionsWorker
= -1;
803 fPackageToPopulateSem
= create_sem(0, "PopulatePackage");
804 if (fPackageToPopulateSem
>= 0) {
805 fPopulatePackageWorker
= spawn_thread(&_PopulatePackageWorker
,
806 "Package Populator", B_NORMAL_PRIORITY
, this);
807 if (fPopulatePackageWorker
>= 0)
808 resume_thread(fPopulatePackageWorker
);
810 fPopulatePackageWorker
= -1;
812 fNewPackagesToShowSem
= create_sem(0, "ShowPackages");
813 fShowPackagesAcknowledgeSem
= create_sem(0, "ShowPackagesAck");
814 if (fNewPackagesToShowSem
>= 0 && fShowPackagesAcknowledgeSem
>= 0) {
815 fShowPackagesWorker
= spawn_thread(&_PackagesToShowWorker
,
816 "Good news everyone", B_NORMAL_PRIORITY
, this);
817 if (fShowPackagesWorker
>= 0)
818 resume_thread(fShowPackagesWorker
);
820 fShowPackagesWorker
= -1;
825 MainWindow::_AdoptModel()
827 fVisiblePackages
= fModel
.CreatePackageList();
830 AutoLocker
<BLocker
> modelLocker(fModel
.Lock());
831 AutoLocker
<BLocker
> listLocker(fPackagesToShowListLock
);
832 fPackagesToShowList
= fVisiblePackages
;
833 atomic_add(&fPackagesToShowListID
, 1);
836 fFeaturedPackagesView
->Clear();
837 fPackageListView
->Clear();
839 release_sem(fNewPackagesToShowSem
);
841 BAutolock
locker(fModel
.Lock());
842 fShowFeaturedPackagesItem
->SetMarked(fModel
.ShowFeaturedPackages());
843 fShowFeaturedPackagesItem
->SetEnabled(fModel
.SearchTerms() == "");
844 fShowAvailablePackagesItem
->SetMarked(fModel
.ShowAvailablePackages());
845 fShowInstalledPackagesItem
->SetMarked(fModel
.ShowInstalledPackages());
846 fShowSourcePackagesItem
->SetMarked(fModel
.ShowSourcePackages());
847 fShowDevelopPackagesItem
->SetMarked(fModel
.ShowDevelopPackages());
849 if (fModel
.ShowFeaturedPackages() && fModel
.SearchTerms() == "")
850 fListLayout
->SetVisibleItem((int32
)0);
852 fListLayout
->SetVisibleItem((int32
)1);
857 MainWindow::_AdoptPackage(const PackageInfoRef
& package
)
860 BAutolock
locker(fModel
.Lock());
861 fPackageInfoView
->SetPackage(package
);
863 if (fFeaturedPackagesView
!= NULL
)
864 fFeaturedPackagesView
->SelectPackage(package
);
865 if (fPackageListView
!= NULL
)
866 fPackageListView
->SelectPackage(package
);
869 // Trigger asynchronous package population from the web-app
871 AutoLocker
<BLocker
> lock(&fPackageToPopulateLock
);
872 fPackageToPopulate
= package
;
874 release_sem_etc(fPackageToPopulateSem
, 1, 0);
879 MainWindow::_ClearPackage()
881 fPackageInfoView
->Clear();
886 MainWindow::_RefreshRepositories(bool force
)
888 if (fSinglePackageMode
)
891 BPackageRoster roster
;
892 BStringList repositoryNames
;
894 status_t result
= roster
.GetRepositoryNames(repositoryNames
);
898 DecisionProvider decisionProvider
;
899 JobStateListener listener
;
900 BContext
context(decisionProvider
, listener
);
902 BRepositoryCache cache
;
903 for (int32 i
= 0; i
< repositoryNames
.CountStrings(); ++i
) {
904 const BString
& repoName
= repositoryNames
.StringAt(i
);
905 BRepositoryConfig repoConfig
;
906 result
= roster
.GetRepositoryConfig(repoName
, &repoConfig
);
907 if (result
!= B_OK
) {
912 if (roster
.GetRepositoryCache(repoName
, &cache
) != B_OK
|| force
) {
914 BRefreshRepositoryRequest
refreshRequest(context
, repoConfig
);
916 result
= refreshRequest
.Process();
917 } catch (BFatalErrorException ex
) {
918 BString
message(B_TRANSLATE("An error occurred while "
919 "refreshing the repository: %error% (%details%)"));
920 message
.ReplaceFirst("%error%", ex
.Message());
921 message
.ReplaceFirst("%details%", ex
.Details());
922 _NotifyUser("Error", message
.String());
923 } catch (BException ex
) {
924 BString
message(B_TRANSLATE("An error occurred while "
925 "refreshing the repository: %error%"));
926 message
.ReplaceFirst("%error%", ex
.Message());
927 _NotifyUser("Error", message
.String());
935 MainWindow::_RefreshPackageList(bool force
)
937 if (fSinglePackageMode
)
940 BPackageRoster roster
;
941 BStringList repositoryNames
;
943 status_t result
= roster
.GetRepositoryNames(repositoryNames
);
948 for (int32 i
= 0; i
< repositoryNames
.CountStrings(); i
++) {
949 const BString
& repoName
= repositoryNames
.StringAt(i
);
950 DepotInfo depotInfo
= DepotInfo(repoName
);
952 BRepositoryConfig repoConfig
;
953 status_t getRepositoryConfigStatus
= roster
.GetRepositoryConfig(
954 repoName
, &repoConfig
);
956 if (getRepositoryConfigStatus
== B_OK
) {
957 depotInfo
.SetBaseURL(repoConfig
.BaseURL());
959 printf("unable to obtain the repository config for local "
960 "repository '%s'; %s\n",
961 repoName
.String(), strerror(getRepositoryConfigStatus
));
964 depots
[repoName
] = depotInfo
;
967 PackageManager
manager(B_PACKAGE_INSTALLATION_LOCATION_HOME
);
969 manager
.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
970 | PackageManager::B_ADD_REMOTE_REPOSITORIES
);
971 } catch (BException ex
) {
972 BString
message(B_TRANSLATE("An error occurred while "
973 "initializing the package manager: %message%"));
974 message
.ReplaceFirst("%message%", ex
.Message());
975 _NotifyUser("Error", message
.String());
979 BObjectList
<BSolverPackage
> packages
;
980 result
= manager
.Solver()->FindPackages("",
981 BSolver::B_FIND_CASE_INSENSITIVE
| BSolver::B_FIND_IN_NAME
982 | BSolver::B_FIND_IN_SUMMARY
| BSolver::B_FIND_IN_DESCRIPTION
983 | BSolver::B_FIND_IN_PROVIDES
,
985 if (result
!= B_OK
) {
986 BString
message(B_TRANSLATE("An error occurred while "
987 "obtaining the package list: %message%"));
988 message
.ReplaceFirst("%message%", strerror(result
));
989 _NotifyUser("Error", message
.String());
993 if (packages
.IsEmpty())
996 PackageInfoMap foundPackages
;
997 // if a given package is installed locally, we will potentially
998 // get back multiple entries, one for each local installation
999 // location, and one for each remote repository the package
1000 // is available in. The above map is used to ensure that in such
1001 // cases we consolidate the information, rather than displaying
1003 PackageInfoMap remotePackages
;
1004 // any package that we find in a remote repository goes in this map.
1005 // this is later used to discern which packages came from a local
1006 // installation only, as those must be handled a bit differently
1007 // upon uninstallation, since we'd no longer be able to pull them
1009 BStringList systemFlaggedPackages
;
1010 // any packages flagged as a system package are added to this list.
1011 // such packages cannot be uninstalled, nor can any of their deps.
1012 PackageInfoMap systemInstalledPackages
;
1013 // any packages installed in system are added to this list.
1014 // This is later used for dependency resolution of the actual
1015 // system packages in order to compute the list of protected
1016 // dependencies indicated above.
1018 for (int32 i
= 0; i
< packages
.CountItems(); i
++) {
1019 BSolverPackage
* package
= packages
.ItemAt(i
);
1020 const BPackageInfo
& repoPackageInfo
= package
->Info();
1021 const BString repositoryName
= package
->Repository()->Name();
1022 PackageInfoRef modelInfo
;
1023 PackageInfoMap::iterator it
= foundPackages
.find(
1024 repoPackageInfo
.Name());
1025 if (it
!= foundPackages
.end())
1026 modelInfo
.SetTo(it
->second
);
1028 // Add new package info
1029 modelInfo
.SetTo(new(std::nothrow
) PackageInfo(repoPackageInfo
),
1032 if (modelInfo
.Get() == NULL
)
1035 modelInfo
->SetDepotName(repositoryName
);
1037 foundPackages
[repoPackageInfo
.Name()] = modelInfo
;
1040 modelInfo
->AddListener(this);
1042 BSolverRepository
* repository
= package
->Repository();
1043 if (dynamic_cast<BPackageManager::RemoteRepository
*>(repository
)
1045 depots
[repository
->Name()].AddPackage(modelInfo
);
1046 remotePackages
[modelInfo
->Name()] = modelInfo
;
1048 if (repository
== static_cast<const BSolverRepository
*>(
1049 manager
.SystemRepository())) {
1050 modelInfo
->AddInstallationLocation(
1051 B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
);
1052 if (!modelInfo
->IsSystemPackage()) {
1053 systemInstalledPackages
[repoPackageInfo
.FileName()]
1056 } else if (repository
== static_cast<const BSolverRepository
*>(
1057 manager
.HomeRepository())) {
1058 modelInfo
->AddInstallationLocation(
1059 B_PACKAGE_INSTALLATION_LOCATION_HOME
);
1063 if (modelInfo
->IsSystemPackage())
1064 systemFlaggedPackages
.Add(repoPackageInfo
.FileName());
1067 bool wasEmpty
= fModel
.Depots().IsEmpty();
1068 if (force
|| wasEmpty
)
1069 fBulkLoadStateMachine
.Stop();
1071 BAutolock
lock(fModel
.Lock());
1076 // filter remote packages from the found list
1077 // any packages remaining will be locally installed packages
1078 // that weren't acquired from a repository
1079 for (PackageInfoMap::iterator it
= remotePackages
.begin();
1080 it
!= remotePackages
.end(); it
++) {
1081 foundPackages
.erase(it
->first
);
1084 if (!foundPackages
.empty()) {
1085 BString repoName
= B_TRANSLATE("Local");
1086 depots
[repoName
] = DepotInfo(repoName
);
1087 DepotInfoMap::iterator depot
= depots
.find(repoName
);
1088 for (PackageInfoMap::iterator it
= foundPackages
.begin();
1089 it
!= foundPackages
.end(); ++it
) {
1090 depot
->second
.AddPackage(it
->second
);
1094 for (DepotInfoMap::iterator it
= depots
.begin(); it
!= depots
.end(); it
++) {
1095 if (fModel
.HasDepot(it
->second
.Name()))
1096 fModel
.SyncDepot(it
->second
);
1098 fModel
.AddDepot(it
->second
);
1101 // start retrieving package icons and average ratings
1102 if (force
|| wasEmpty
) {
1103 fBulkLoadStateMachine
.Start();
1106 // compute the OS package dependencies
1108 // create the solver
1110 status_t error
= BSolver::Create(solver
);
1112 throw BFatalErrorException(error
, "Failed to create solver.");
1114 ObjectDeleter
<BSolver
> solverDeleter(solver
);
1116 error
= find_directory(B_SYSTEM_PACKAGES_DIRECTORY
, &systemPath
);
1117 if (error
!= B_OK
) {
1118 throw BFatalErrorException(error
,
1119 "Unable to retrieve system packages directory.");
1122 // add the "installed" repository with the given packages
1123 BSolverRepository installedRepository
;
1125 BRepositoryBuilder
installedRepositoryBuilder(installedRepository
,
1127 for (int32 i
= 0; i
< systemFlaggedPackages
.CountStrings(); i
++) {
1128 BPath
packagePath(systemPath
);
1129 packagePath
.Append(systemFlaggedPackages
.StringAt(i
));
1130 installedRepositoryBuilder
.AddPackage(packagePath
.Path());
1132 installedRepositoryBuilder
.AddToSolver(solver
, true);
1135 // add system repository
1136 BSolverRepository systemRepository
;
1138 BRepositoryBuilder
systemRepositoryBuilder(systemRepository
,
1140 for (PackageInfoMap::iterator it
= systemInstalledPackages
.begin();
1141 it
!= systemInstalledPackages
.end(); it
++) {
1142 BPath
packagePath(systemPath
);
1143 packagePath
.Append(it
->first
);
1144 systemRepositoryBuilder
.AddPackage(packagePath
.Path());
1146 systemRepositoryBuilder
.AddToSolver(solver
, false);
1150 error
= solver
->VerifyInstallation();
1151 if (error
!= B_OK
) {
1152 throw BFatalErrorException(error
, "Failed to compute packages to "
1156 BSolverResult solverResult
;
1157 error
= solver
->GetResult(solverResult
);
1158 if (error
!= B_OK
) {
1159 throw BFatalErrorException(error
, "Failed to retrieve system "
1160 "package dependency list.");
1163 for (int32 i
= 0; const BSolverResultElement
* element
1164 = solverResult
.ElementAt(i
); i
++) {
1165 BSolverPackage
* package
= element
->Package();
1166 if (element
->Type() == BSolverResultElement::B_TYPE_INSTALL
) {
1167 PackageInfoMap::iterator it
= systemInstalledPackages
.find(
1168 package
->Info().FileName());
1169 if (it
!= systemInstalledPackages
.end())
1170 it
->second
->SetSystemDependency(true);
1173 } catch (BFatalErrorException ex
) {
1174 printf("Fatal exception occurred while resolving system dependencies: "
1175 "%s, details: %s\n", strerror(ex
.Error()), ex
.Details().String());
1176 } catch (BNothingToDoException
) {
1178 } catch (BException ex
) {
1179 printf("Exception occurred while resolving system dependencies: %s\n",
1180 ex
.Message().String());
1182 printf("Unknown exception occurred while resolving system "
1189 MainWindow::_StartRefreshWorker(bool force
)
1191 if (fModelWorker
!= B_BAD_THREAD_ID
)
1194 RefreshWorkerParameters
* parameters
= new(std::nothrow
)
1195 RefreshWorkerParameters(this, force
);
1196 if (parameters
== NULL
)
1199 fWorkStatusView
->SetBusy(B_TRANSLATE("Refreshing..."));
1201 ObjectDeleter
<RefreshWorkerParameters
> deleter(parameters
);
1202 fModelWorker
= spawn_thread(&_RefreshModelThreadWorker
, "model loader",
1203 B_LOW_PRIORITY
, parameters
);
1205 if (fModelWorker
> 0) {
1207 resume_thread(fModelWorker
);
1213 MainWindow::_RefreshModelThreadWorker(void* arg
)
1215 RefreshWorkerParameters
* parameters
1216 = reinterpret_cast<RefreshWorkerParameters
*>(arg
);
1217 MainWindow
* mainWindow
= parameters
->window
;
1218 ObjectDeleter
<RefreshWorkerParameters
> deleter(parameters
);
1220 BMessenger
messenger(mainWindow
);
1222 mainWindow
->_RefreshRepositories(parameters
->forceRefresh
);
1224 if (mainWindow
->fTerminating
)
1227 mainWindow
->_RefreshPackageList(parameters
->forceRefresh
);
1229 messenger
.SendMessage(MSG_MODEL_WORKER_DONE
);
1236 MainWindow::_PackageActionWorker(void* arg
)
1238 MainWindow
* window
= reinterpret_cast<MainWindow
*>(arg
);
1240 while (acquire_sem(window
->fPendingActionsSem
) == B_OK
) {
1241 PackageActionRef ref
;
1243 AutoLocker
<BLocker
> lock(&window
->fPendingActionsLock
);
1244 ref
= window
->fPendingActions
.ItemAt(0);
1245 if (ref
.Get() == NULL
)
1247 window
->fPendingActions
.Remove(0);
1250 BMessenger
messenger(window
);
1251 BMessage
busyMessage(MSG_PACKAGE_WORKER_BUSY
);
1252 BString
text(ref
->Label());
1254 busyMessage
.AddString("reason", text
);
1256 messenger
.SendMessage(&busyMessage
);
1258 messenger
.SendMessage(MSG_PACKAGE_WORKER_IDLE
);
1266 MainWindow::_PopulatePackageWorker(void* arg
)
1268 MainWindow
* window
= reinterpret_cast<MainWindow
*>(arg
);
1270 while (acquire_sem(window
->fPackageToPopulateSem
) == B_OK
) {
1271 PackageInfoRef package
;
1273 AutoLocker
<BLocker
> lock(&window
->fPackageToPopulateLock
);
1274 package
= window
->fPackageToPopulate
;
1277 if (package
.Get() != NULL
) {
1278 window
->fModel
.PopulatePackage(package
,
1279 Model::POPULATE_USER_RATINGS
| Model::POPULATE_SCREEN_SHOTS
1280 | Model::POPULATE_CHANGELOG
);
1288 /* static */ status_t
1289 MainWindow::_PackagesToShowWorker(void* arg
)
1291 MainWindow
* window
= reinterpret_cast<MainWindow
*>(arg
);
1293 while (acquire_sem(window
->fNewPackagesToShowSem
) == B_OK
) {
1294 PackageList packageList
;
1297 AutoLocker
<BLocker
> lock(&window
->fPackagesToShowListLock
);
1298 packageList
= window
->fPackagesToShowList
;
1299 listID
= atomic_get(&window
->fPackagesToShowListID
);
1300 window
->fPackagesToShowList
.Clear();
1303 // Add packages to list views in batches of kPackagesPerUpdate so we
1304 // don't block the window thread for long with each iteration
1306 kPackagesPerUpdate
= 20
1308 uint32 packagesInMessage
= 0;
1309 BMessage
message(MSG_ADD_VISIBLE_PACKAGES
);
1310 BMessenger
messenger(window
);
1311 bool listIsOutdated
= false;
1313 for (int i
= 0; i
< packageList
.CountItems(); i
++) {
1314 const PackageInfoRef
& package
= packageList
.ItemAtFast(i
);
1316 if (packagesInMessage
>= kPackagesPerUpdate
) {
1317 if (listID
!= atomic_get(&window
->fPackagesToShowListID
)) {
1318 // The model was changed again in the meantime, and thus
1319 // our package list isn't current anymore. Send no further
1320 // messags based on this list and go back to start.
1321 listIsOutdated
= true;
1325 message
.AddInt32("list_id", listID
);
1326 messenger
.SendMessage(&message
);
1327 message
.MakeEmpty();
1328 packagesInMessage
= 0;
1330 // Don't spam the window's message queue, which would make it
1331 // unresponsive (i.e. allows UI messages to get in between our
1332 // messages). When it has processed the message we just sent,
1333 // it will let us know by releasing the semaphore.
1334 acquire_sem(window
->fShowPackagesAcknowledgeSem
);
1336 package
->AcquireReference();
1337 message
.AddPointer("package_ref", package
.Get());
1338 packagesInMessage
++;
1344 // Send remaining package infos, if any, which didn't make it into
1345 // the last message (count < kPackagesPerUpdate)
1346 if (packagesInMessage
> 0) {
1347 message
.AddInt32("list_id", listID
);
1348 messenger
.SendMessage(&message
);
1349 acquire_sem(window
->fShowPackagesAcknowledgeSem
);
1352 // Update selected package in list views
1353 messenger
.SendMessage(MSG_UPDATE_SELECTED_PACKAGE
);
1361 MainWindow::_NotifyUser(const char* title
, const char* message
)
1363 BAlert
* alert
= new(std::nothrow
) BAlert(title
, message
,
1364 B_TRANSLATE("Close"));
1372 MainWindow::_OpenLoginWindow(const BMessage
& onSuccessMessage
)
1374 UserLoginWindow
* window
= new UserLoginWindow(this,
1375 BRect(0, 0, 500, 400), fModel
);
1377 if (onSuccessMessage
.what
!= 0)
1378 window
->SetOnSuccessMessage(BMessenger(this), onSuccessMessage
);
1385 MainWindow::_UpdateAuthorization()
1387 BString
username(fModel
.Username());
1388 bool hasUser
= !username
.IsEmpty();
1390 if (fLogOutItem
!= NULL
)
1391 fLogOutItem
->SetEnabled(hasUser
);
1392 if (fLogInItem
!= NULL
) {
1394 fLogInItem
->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS
));
1396 fLogInItem
->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS
));
1399 if (fUserMenu
!= NULL
) {
1401 if (username
.Length() == 0) {
1402 label
= B_TRANSLATE("Not logged in");
1404 label
= B_TRANSLATE("Logged in as %User%");
1405 label
.ReplaceAll("%User%", username
);
1407 fUserMenu
->Superitem()->SetLabel(label
);
1413 MainWindow::_RatePackage()
1415 if (fModel
.Username().IsEmpty()) {
1416 BAlert
* alert
= new(std::nothrow
) BAlert(
1417 B_TRANSLATE("Not logged in"),
1418 B_TRANSLATE("You need to be logged into an account before you "
1419 "can rate packages."),
1420 B_TRANSLATE("Cancel"),
1421 B_TRANSLATE("Login or Create account"));
1426 int32 choice
= alert
->Go();
1428 _OpenLoginWindow(BMessage(MSG_RATE_PACKAGE
));
1432 // TODO: Allow only one RatePackageWindow
1433 // TODO: Mechanism for remembering the window frame
1434 RatePackageWindow
* window
= new RatePackageWindow(this,
1435 BRect(0, 0, 500, 400), fModel
);
1436 window
->SetPackage(fPackageInfoView
->Package());
1442 MainWindow::_ShowScreenshot()
1444 // TODO: Mechanism for remembering the window frame
1445 if (fScreenshotWindow
== NULL
)
1446 fScreenshotWindow
= new ScreenshotWindow(this, BRect(0, 0, 500, 400));
1448 if (fScreenshotWindow
->LockWithTimeout(1000) != B_OK
)
1451 fScreenshotWindow
->SetPackage(fPackageInfoView
->Package());
1453 if (fScreenshotWindow
->IsHidden())
1454 fScreenshotWindow
->Show();
1456 fScreenshotWindow
->Activate();
1458 fScreenshotWindow
->Unlock();