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',
73 MSG_AUTHORIZATION_CHANGED
= 'athc',
74 MSG_PACKAGE_CHANGED
= 'pchd',
76 MSG_SHOW_FEATURED_PACKAGES
= 'sofp',
77 MSG_SHOW_AVAILABLE_PACKAGES
= 'savl',
78 MSG_SHOW_INSTALLED_PACKAGES
= 'sins',
79 MSG_SHOW_SOURCE_PACKAGES
= 'ssrc',
80 MSG_SHOW_DEVELOP_PACKAGES
= 'sdvl'
84 using namespace BPackageKit
;
85 using namespace BPackageKit::BManager::BPrivate
;
88 typedef std::map
<BString
, PackageInfoRef
> PackageInfoMap
;
89 typedef std::map
<BString
, DepotInfo
> DepotInfoMap
;
92 struct RefreshWorkerParameters
{
96 RefreshWorkerParameters(MainWindow
* window
, bool forceRefresh
)
99 forceRefresh(forceRefresh
)
105 class MessageModelListener
: public ModelListener
{
107 MessageModelListener(const BMessenger
& messenger
)
109 fMessenger(messenger
)
113 virtual void AuthorizationChanged()
115 if (fMessenger
.IsValid())
116 fMessenger
.SendMessage(MSG_AUTHORIZATION_CHANGED
);
120 BMessenger fMessenger
;
124 MainWindow::MainWindow(const BMessage
& settings
)
126 BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
127 B_DOCUMENT_WINDOW_LOOK
, B_NORMAL_WINDOW_FEEL
,
128 B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
),
129 fScreenshotWindow(NULL
),
133 fModelListener(new MessageModelListener(BMessenger(this)), true),
135 fSinglePackageMode(false),
136 fModelWorker(B_BAD_THREAD_ID
)
138 BMenuBar
* menuBar
= new BMenuBar(B_TRANSLATE("Main Menu"));
141 BMenuBar
* userMenuBar
= new BMenuBar(B_TRANSLATE("User Menu"));
142 _BuildUserMenu(userMenuBar
);
143 set_small_font(userMenuBar
);
144 userMenuBar
->SetExplicitMaxSize(BSize(B_SIZE_UNSET
,
145 menuBar
->MaxSize().height
));
147 fFilterView
= new FilterView();
148 fFeaturedPackagesView
= new FeaturedPackagesView();
149 fPackageListView
= new PackageListView(fModel
.Lock());
150 fPackageInfoView
= new PackageInfoView(fModel
.Lock(), this);
152 fSplitView
= new BSplitView(B_VERTICAL
, 5.0f
);
154 BGroupView
* featuredPackagesGroup
= new BGroupView(B_VERTICAL
);
155 BStringView
* featuredPackagesTitle
= new BStringView(
156 "featured packages title", B_TRANSLATE("Featured packages"));
157 BFont
font(be_bold_font
);
158 font
.SetSize(font
.Size() * 1.3f
);
159 featuredPackagesTitle
->SetFont(&font
);
160 featuredPackagesGroup
->SetExplicitMaxSize(
161 BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
162 BLayoutBuilder::Group
<>(featuredPackagesGroup
)
163 .Add(featuredPackagesTitle
)
164 .Add(fFeaturedPackagesView
)
167 fWorkStatusView
= new WorkStatusView("work status");
168 fPackageListView
->AttachWorkStatusView(fWorkStatusView
);
170 BView
* listArea
= new BView("list area", 0);
171 fListLayout
= new BCardLayout();
172 listArea
->SetLayout(fListLayout
);
173 listArea
->AddChild(featuredPackagesGroup
);
174 listArea
->AddChild(fPackageListView
);
176 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0.0f
)
177 .AddGroup(B_HORIZONTAL
, 0.0f
)
179 .Add(userMenuBar
, 0.0f
)
182 .AddSplit(fSplitView
)
183 .AddGroup(B_VERTICAL
)
186 B_USE_DEFAULT_SPACING
, 0.0f
,
187 B_USE_DEFAULT_SPACING
, 0.0f
)
189 .Add(fPackageInfoView
)
191 .Add(fWorkStatusView
)
194 fSplitView
->SetCollapsible(0, false);
195 fSplitView
->SetCollapsible(1, false);
197 fModel
.AddListener(fModelListener
);
200 BMessage columnSettings
;
201 if (settings
.FindMessage("column settings", &columnSettings
) == B_OK
)
202 fPackageListView
->LoadState(&columnSettings
);
205 if (settings
.FindBool("show featured packages", &showOption
) == B_OK
)
206 fModel
.SetShowFeaturedPackages(showOption
);
207 if (settings
.FindBool("show available packages", &showOption
) == B_OK
)
208 fModel
.SetShowAvailablePackages(showOption
);
209 if (settings
.FindBool("show installed packages", &showOption
) == B_OK
)
210 fModel
.SetShowInstalledPackages(showOption
);
211 if (settings
.FindBool("show develop packages", &showOption
) == B_OK
)
212 fModel
.SetShowDevelopPackages(showOption
);
213 if (settings
.FindBool("show source packages", &showOption
) == B_OK
)
214 fModel
.SetShowSourcePackages(showOption
);
216 if (fModel
.ShowFeaturedPackages())
217 fListLayout
->SetVisibleItem((int32
)0);
219 fListLayout
->SetVisibleItem(1);
221 _RestoreUserName(settings
);
222 _RestoreWindowFrame(settings
);
224 atomic_set(&fPackagesToShowListID
, 0);
226 // start worker threads
227 BPackageRoster().StartWatching(this,
228 B_WATCH_PACKAGE_INSTALLATION_LOCATIONS
);
230 _StartRefreshWorker();
232 _InitWorkerThreads();
236 MainWindow::MainWindow(const BMessage
& settings
, const PackageInfoRef
& package
)
238 BWindow(BRect(50, 50, 650, 350), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
239 B_DOCUMENT_WINDOW_LOOK
, B_NORMAL_WINDOW_FEEL
,
240 B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
),
241 fWorkStatusView(NULL
),
242 fScreenshotWindow(NULL
),
246 fModelListener(new MessageModelListener(BMessenger(this)), true),
248 fSinglePackageMode(true),
249 fModelWorker(B_BAD_THREAD_ID
)
251 fFilterView
= new FilterView();
252 fPackageListView
= new PackageListView(fModel
.Lock());
253 fPackageInfoView
= new PackageInfoView(fModel
.Lock(), this);
255 BLayoutBuilder::Group
<>(this, B_VERTICAL
)
256 .Add(fPackageInfoView
)
257 .SetInsets(0, B_USE_WINDOW_INSETS
, 0, 0)
260 fModel
.AddListener(fModelListener
);
263 _RestoreUserName(settings
);
264 _RestoreWindowFrame(settings
);
266 fPackageInfoView
->SetPackage(package
);
268 _InitWorkerThreads();
272 MainWindow::~MainWindow()
274 BPackageRoster().StopWatching(this);
277 if (fModelWorker
>= 0)
278 wait_for_thread(fModelWorker
, NULL
);
280 delete_sem(fPendingActionsSem
);
281 if (fPendingActionsWorker
>= 0)
282 wait_for_thread(fPendingActionsWorker
, NULL
);
284 delete_sem(fPackageToPopulateSem
);
285 if (fPopulatePackageWorker
>= 0)
286 wait_for_thread(fPopulatePackageWorker
, NULL
);
288 delete_sem(fNewPackagesToShowSem
);
289 delete_sem(fShowPackagesAcknowledgeSem
);
290 if (fShowPackagesWorker
>= 0)
291 wait_for_thread(fShowPackagesWorker
, NULL
);
293 if (fScreenshotWindow
!= NULL
&& fScreenshotWindow
->Lock())
294 fScreenshotWindow
->Quit();
299 MainWindow::QuitRequested()
302 StoreSettings(settings
);
304 BMessage
message(MSG_MAIN_WINDOW_CLOSED
);
305 message
.AddMessage("window settings", &settings
);
307 be_app
->PostMessage(&message
);
314 MainWindow::MessageReceived(BMessage
* message
)
316 switch (message
->what
) {
317 case MSG_MODEL_WORKER_DONE
:
319 fModelWorker
= B_BAD_THREAD_ID
;
321 fFilterView
->AdoptModel(fModel
);
322 fWorkStatusView
->SetIdle();
326 case B_REFS_RECEIVED
:
330 case B_PACKAGE_UPDATE
:
331 // TODO: We should do a more selective update depending on the
332 // "event", "location", and "change count" fields!
333 _StartRefreshWorker(false);
336 case MSG_REFRESH_REPOS
:
337 _StartRefreshWorker(true);
340 case MSG_MANAGE_REPOS
:
341 be_roster
->Launch("application/x-vnd.Haiku-Repositories");
345 _OpenLoginWindow(BMessage());
349 fModel
.SetUsername("");
352 case MSG_AUTHORIZATION_CHANGED
:
353 _UpdateAuthorization();
356 case MSG_SHOW_FEATURED_PACKAGES
:
358 BAutolock
locker(fModel
.Lock());
359 fModel
.SetShowFeaturedPackages(
360 !fModel
.ShowFeaturedPackages());
365 case MSG_SHOW_AVAILABLE_PACKAGES
:
367 BAutolock
locker(fModel
.Lock());
368 fModel
.SetShowAvailablePackages(
369 !fModel
.ShowAvailablePackages());
374 case MSG_SHOW_INSTALLED_PACKAGES
:
376 BAutolock
locker(fModel
.Lock());
377 fModel
.SetShowInstalledPackages(
378 !fModel
.ShowInstalledPackages());
383 case MSG_SHOW_SOURCE_PACKAGES
:
385 BAutolock
locker(fModel
.Lock());
386 fModel
.SetShowSourcePackages(!fModel
.ShowSourcePackages());
391 case MSG_SHOW_DEVELOP_PACKAGES
:
393 BAutolock
locker(fModel
.Lock());
394 fModel
.SetShowDevelopPackages(!fModel
.ShowDevelopPackages());
399 case MSG_PACKAGE_SELECTED
:
402 if (message
->FindString("name", &name
) == B_OK
) {
403 BAutolock
locker(fModel
.Lock());
404 int count
= fVisiblePackages
.CountItems();
405 for (int i
= 0; i
< count
; i
++) {
406 const PackageInfoRef
& package
407 = fVisiblePackages
.ItemAtFast(i
);
408 if (package
.Get() != NULL
&& package
->Name() == name
) {
410 _AdoptPackage(package
);
420 case MSG_CATEGORY_SELECTED
:
423 if (message
->FindString("name", &name
) != B_OK
)
426 BAutolock
locker(fModel
.Lock());
427 fModel
.SetCategory(name
);
433 case MSG_DEPOT_SELECTED
:
436 if (message
->FindString("name", &name
) != B_OK
)
439 BAutolock
locker(fModel
.Lock());
440 fModel
.SetDepot(name
);
446 case MSG_SEARCH_TERMS_MODIFIED
:
448 // TODO: Do this with a delay!
450 if (message
->FindString("search terms", &searchTerms
) != B_OK
)
453 BAutolock
locker(fModel
.Lock());
454 fModel
.SetSearchTerms(searchTerms
);
460 case MSG_PACKAGE_CHANGED
:
463 if (message
->FindPointer("package", (void**)&info
) == B_OK
) {
464 PackageInfoRef
ref(info
, true);
466 if (message
->FindUInt32("changes", &changes
) != B_OK
)
468 if ((changes
& PKG_CHANGED_STATE
) != 0) {
469 BAutolock
locker(fModel
.Lock());
470 fModel
.SetPackageState(ref
, ref
->State());
473 // Asynchronous updates to the package information
474 // can mean that the package needs to be added or
475 // removed to/from the visible packages when the current
476 // filter parameters are re-evaluated on this package.
477 bool wasVisible
= fVisiblePackages
.Contains(ref
);
480 BAutolock
locker(fModel
.Lock());
481 // The package didn't get a chance yet to be in the
482 // visible package list
483 PackageList visiblePackages
= fModel
.CreatePackageList();
484 isVisible
= visiblePackages
.Contains(ref
);
486 // Transfer this single package, otherwise we miss
487 // other packages if they appear or disappear along
488 // with this one before receive a notification for
491 fVisiblePackages
.Add(ref
);
492 } else if (wasVisible
)
493 fVisiblePackages
.Remove(ref
);
496 if (wasVisible
!= isVisible
) {
498 fPackageListView
->RemovePackage(ref
);
499 fFeaturedPackagesView
->RemovePackage(ref
);
501 fPackageListView
->AddPackage(ref
);
502 if (ref
->IsProminent())
503 fFeaturedPackagesView
->AddPackage(ref
);
510 case MSG_RATE_PACKAGE
:
514 case MSG_SHOW_SCREENSHOT
:
518 case MSG_PACKAGE_WORKER_BUSY
:
521 status_t status
= message
->FindString("reason", &reason
);
524 if (!fSinglePackageMode
)
525 fWorkStatusView
->SetBusy(reason
);
529 case MSG_PACKAGE_WORKER_IDLE
:
530 if (!fSinglePackageMode
)
531 fWorkStatusView
->SetIdle();
534 case MSG_ADD_VISIBLE_PACKAGES
:
536 struct SemaphoreReleaser
{
537 SemaphoreReleaser(sem_id semaphore
)
539 fSemaphore(semaphore
)
542 ~SemaphoreReleaser() { release_sem(fSemaphore
); }
547 // Make sure acknowledge semaphore is released even on error,
548 // so the worker thread won't be blocked
549 SemaphoreReleaser
acknowledger(fShowPackagesAcknowledgeSem
);
551 int32 numPackages
= 0;
553 status_t status
= message
->GetInfo("package_ref", &unused
,
555 if (status
!= B_OK
|| numPackages
== 0)
559 status
= message
->FindInt32("list_id", &listID
);
562 if (listID
!= atomic_get(&fPackagesToShowListID
)) {
563 // list is outdated, ignore
567 for (int i
= 0; i
< numPackages
; i
++) {
568 PackageInfo
* packageRaw
= NULL
;
569 status
= message
->FindPointer("package_ref", i
,
570 (void**)&packageRaw
);
573 PackageInfoRef
package(packageRaw
, true);
575 fPackageListView
->AddPackage(package
);
576 if (package
->IsProminent())
577 fFeaturedPackagesView
->AddPackage(package
);
582 case MSG_UPDATE_SELECTED_PACKAGE
:
584 const PackageInfoRef
& selectedPackage
= fPackageInfoView
->Package();
585 fFeaturedPackagesView
->SelectPackage(selectedPackage
, true);
586 fPackageListView
->SelectPackage(selectedPackage
);
588 AutoLocker
<BLocker
> modelLocker(fModel
.Lock());
589 if (!fVisiblePackages
.Contains(fPackageInfoView
->Package()))
590 fPackageInfoView
->Clear();
595 BWindow::MessageReceived(message
);
602 MainWindow::StoreSettings(BMessage
& settings
) const
604 settings
.AddRect(_WindowFrameName(), Frame());
605 if (!fSinglePackageMode
) {
606 settings
.AddRect("window frame", Frame());
608 BMessage columnSettings
;
609 fPackageListView
->SaveState(&columnSettings
);
611 settings
.AddMessage("column settings", &columnSettings
);
613 settings
.AddBool("show featured packages",
614 fModel
.ShowFeaturedPackages());
615 settings
.AddBool("show available packages",
616 fModel
.ShowAvailablePackages());
617 settings
.AddBool("show installed packages",
618 fModel
.ShowInstalledPackages());
619 settings
.AddBool("show develop packages", fModel
.ShowDevelopPackages());
620 settings
.AddBool("show source packages", fModel
.ShowSourcePackages());
623 settings
.AddString("username", fModel
.Username());
628 MainWindow::PackageChanged(const PackageInfoEvent
& event
)
630 uint32 whatchedChanges
= PKG_CHANGED_STATE
| PKG_CHANGED_PROMINENCE
;
631 if ((event
.Changes() & whatchedChanges
) != 0) {
632 PackageInfoRef
ref(event
.Package());
633 BMessage
message(MSG_PACKAGE_CHANGED
);
634 message
.AddPointer("package", ref
.Get());
635 message
.AddUInt32("changes", event
.Changes());
637 // reference needs to be released by MessageReceived();
638 PostMessage(&message
);
644 MainWindow::SchedulePackageActions(PackageActionList
& list
)
646 AutoLocker
<BLocker
> lock(&fPendingActionsLock
);
647 for (int32 i
= 0; i
< list
.CountItems(); i
++) {
648 if (!fPendingActions
.Add(list
.ItemAtFast(i
)))
652 return release_sem_etc(fPendingActionsSem
, list
.CountItems(), 0);
657 MainWindow::GetModel()
664 MainWindow::_BuildMenu(BMenuBar
* menuBar
)
666 BMenu
* menu
= new BMenu(B_TRANSLATE("Tools"));
667 menu
->AddItem(new BMenuItem(B_TRANSLATE("Refresh repositories"),
668 new BMessage(MSG_REFRESH_REPOS
)));
669 menu
->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"
670 B_UTF8_ELLIPSIS
), new BMessage(MSG_MANAGE_REPOS
)));
672 menuBar
->AddItem(menu
);
674 menu
= new BMenu(B_TRANSLATE("Show"));
676 fShowFeaturedPackagesItem
= new BMenuItem(
677 B_TRANSLATE("Only featured packages"),
678 new BMessage(MSG_SHOW_FEATURED_PACKAGES
));
679 menu
->AddItem(fShowFeaturedPackagesItem
);
681 menu
->AddSeparatorItem();
683 fShowAvailablePackagesItem
= new BMenuItem(
684 B_TRANSLATE("Available packages"),
685 new BMessage(MSG_SHOW_AVAILABLE_PACKAGES
));
686 menu
->AddItem(fShowAvailablePackagesItem
);
688 fShowInstalledPackagesItem
= new BMenuItem(
689 B_TRANSLATE("Installed packages"),
690 new BMessage(MSG_SHOW_INSTALLED_PACKAGES
));
691 menu
->AddItem(fShowInstalledPackagesItem
);
693 menu
->AddSeparatorItem();
695 fShowDevelopPackagesItem
= new BMenuItem(
696 B_TRANSLATE("Develop packages"),
697 new BMessage(MSG_SHOW_DEVELOP_PACKAGES
));
698 menu
->AddItem(fShowDevelopPackagesItem
);
700 fShowSourcePackagesItem
= new BMenuItem(
701 B_TRANSLATE("Source packages"),
702 new BMessage(MSG_SHOW_SOURCE_PACKAGES
));
703 menu
->AddItem(fShowSourcePackagesItem
);
705 menuBar
->AddItem(menu
);
710 MainWindow::_BuildUserMenu(BMenuBar
* menuBar
)
712 fUserMenu
= new BMenu(B_TRANSLATE("Not logged in"));
714 fLogInItem
= new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS
),
715 new BMessage(MSG_LOG_IN
));
716 fUserMenu
->AddItem(fLogInItem
);
718 fLogOutItem
= new BMenuItem(B_TRANSLATE("Log out"),
719 new BMessage(MSG_LOG_OUT
));
720 fUserMenu
->AddItem(fLogOutItem
);
722 menuBar
->AddItem(fUserMenu
);
727 MainWindow::_RestoreUserName(const BMessage
& settings
)
730 if (settings
.FindString("username", &username
) == B_OK
731 && username
.Length() > 0) {
732 fModel
.SetUsername(username
);
738 MainWindow::_WindowFrameName() const
740 if (fSinglePackageMode
)
741 return "small window frame";
743 return "window frame";
748 MainWindow::_RestoreWindowFrame(const BMessage
& settings
)
750 BRect frame
= Frame();
753 bool fromSettings
= false;
754 if (settings
.FindRect(_WindowFrameName(), &windowFrame
) == B_OK
) {
757 } else if (!fSinglePackageMode
) {
758 // Resize to occupy a certain screen size
759 BRect screenFrame
= BScreen(this).Frame();
760 float width
= frame
.Width();
761 float height
= frame
.Height();
762 if (width
< screenFrame
.Width() * .666f
763 && height
< screenFrame
.Height() * .666f
) {
764 frame
.bottom
= frame
.top
+ screenFrame
.Height() * .666f
;
765 frame
.right
= frame
.left
766 + std::min(screenFrame
.Width() * .666f
, height
* 7 / 5);
770 MoveTo(frame
.LeftTop());
771 ResizeTo(frame
.Width(), frame
.Height());
781 MainWindow::_InitWorkerThreads()
783 fPendingActionsSem
= create_sem(0, "PendingPackageActions");
784 if (fPendingActionsSem
>= 0) {
785 fPendingActionsWorker
= spawn_thread(&_PackageActionWorker
,
786 "Planet Express", B_NORMAL_PRIORITY
, this);
787 if (fPendingActionsWorker
>= 0)
788 resume_thread(fPendingActionsWorker
);
790 fPendingActionsWorker
= -1;
792 fPackageToPopulateSem
= create_sem(0, "PopulatePackage");
793 if (fPackageToPopulateSem
>= 0) {
794 fPopulatePackageWorker
= spawn_thread(&_PopulatePackageWorker
,
795 "Package Populator", B_NORMAL_PRIORITY
, this);
796 if (fPopulatePackageWorker
>= 0)
797 resume_thread(fPopulatePackageWorker
);
799 fPopulatePackageWorker
= -1;
801 fNewPackagesToShowSem
= create_sem(0, "ShowPackages");
802 fShowPackagesAcknowledgeSem
= create_sem(0, "ShowPackagesAck");
803 if (fNewPackagesToShowSem
>= 0 && fShowPackagesAcknowledgeSem
>= 0) {
804 fShowPackagesWorker
= spawn_thread(&_PackagesToShowWorker
,
805 "Good news everyone", B_NORMAL_PRIORITY
, this);
806 if (fShowPackagesWorker
>= 0)
807 resume_thread(fShowPackagesWorker
);
809 fShowPackagesWorker
= -1;
814 MainWindow::_AdoptModel()
816 fVisiblePackages
= fModel
.CreatePackageList();
819 AutoLocker
<BLocker
> modelLocker(fModel
.Lock());
820 AutoLocker
<BLocker
> listLocker(fPackagesToShowListLock
);
821 fPackagesToShowList
= fVisiblePackages
;
822 atomic_add(&fPackagesToShowListID
, 1);
825 fFeaturedPackagesView
->Clear();
826 fPackageListView
->Clear();
828 release_sem(fNewPackagesToShowSem
);
830 BAutolock
locker(fModel
.Lock());
831 fShowFeaturedPackagesItem
->SetMarked(fModel
.ShowFeaturedPackages());
832 fShowFeaturedPackagesItem
->SetEnabled(fModel
.SearchTerms() == "");
833 fShowAvailablePackagesItem
->SetMarked(fModel
.ShowAvailablePackages());
834 fShowInstalledPackagesItem
->SetMarked(fModel
.ShowInstalledPackages());
835 fShowSourcePackagesItem
->SetMarked(fModel
.ShowSourcePackages());
836 fShowDevelopPackagesItem
->SetMarked(fModel
.ShowDevelopPackages());
838 if (fModel
.ShowFeaturedPackages() && fModel
.SearchTerms() == "")
839 fListLayout
->SetVisibleItem((int32
)0);
841 fListLayout
->SetVisibleItem((int32
)1);
846 MainWindow::_AdoptPackage(const PackageInfoRef
& package
)
849 BAutolock
locker(fModel
.Lock());
850 fPackageInfoView
->SetPackage(package
);
852 if (fFeaturedPackagesView
!= NULL
)
853 fFeaturedPackagesView
->SelectPackage(package
);
854 if (fPackageListView
!= NULL
)
855 fPackageListView
->SelectPackage(package
);
858 // Trigger asynchronous package population from the web-app
860 AutoLocker
<BLocker
> lock(&fPackageToPopulateLock
);
861 fPackageToPopulate
= package
;
863 release_sem_etc(fPackageToPopulateSem
, 1, 0);
868 MainWindow::_ClearPackage()
870 fPackageInfoView
->Clear();
875 MainWindow::_RefreshRepositories(bool force
)
877 if (fSinglePackageMode
)
880 BPackageRoster roster
;
881 BStringList repositoryNames
;
883 status_t result
= roster
.GetRepositoryNames(repositoryNames
);
887 DecisionProvider decisionProvider
;
888 JobStateListener listener
;
889 BContext
context(decisionProvider
, listener
);
891 BRepositoryCache cache
;
892 for (int32 i
= 0; i
< repositoryNames
.CountStrings(); ++i
) {
893 const BString
& repoName
= repositoryNames
.StringAt(i
);
894 BRepositoryConfig repoConfig
;
895 result
= roster
.GetRepositoryConfig(repoName
, &repoConfig
);
896 if (result
!= B_OK
) {
901 if (roster
.GetRepositoryCache(repoName
, &cache
) != B_OK
|| force
) {
903 BRefreshRepositoryRequest
refreshRequest(context
, repoConfig
);
905 result
= refreshRequest
.Process();
906 } catch (BFatalErrorException ex
) {
907 BString
message(B_TRANSLATE("An error occurred while "
908 "refreshing the repository: %error% (%details%)"));
909 message
.ReplaceFirst("%error%", ex
.Message());
910 message
.ReplaceFirst("%details%", ex
.Details());
911 _NotifyUser("Error", message
.String());
912 } catch (BException ex
) {
913 BString
message(B_TRANSLATE("An error occurred while "
914 "refreshing the repository: %error%"));
915 message
.ReplaceFirst("%error%", ex
.Message());
916 _NotifyUser("Error", message
.String());
924 MainWindow::_RefreshPackageList(bool force
)
926 if (fSinglePackageMode
)
929 BPackageRoster roster
;
930 BStringList repositoryNames
;
932 status_t result
= roster
.GetRepositoryNames(repositoryNames
);
937 for (int32 i
= 0; i
< repositoryNames
.CountStrings(); i
++) {
938 const BString
& repoName
= repositoryNames
.StringAt(i
);
939 DepotInfo depotInfo
= DepotInfo(repoName
);
941 BRepositoryConfig repoConfig
;
942 status_t getRepositoryConfigStatus
= roster
.GetRepositoryConfig(
943 repoName
, &repoConfig
);
945 if (getRepositoryConfigStatus
== B_OK
) {
946 depotInfo
.SetBaseURL(repoConfig
.BaseURL());
948 printf("unable to obtain the repository config for local "
949 "repository '%s'; %s\n",
950 repoName
.String(), strerror(getRepositoryConfigStatus
));
953 depots
[repoName
] = depotInfo
;
956 PackageManager
manager(B_PACKAGE_INSTALLATION_LOCATION_HOME
);
958 manager
.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
959 | PackageManager::B_ADD_REMOTE_REPOSITORIES
);
960 } catch (BException ex
) {
961 BString
message(B_TRANSLATE("An error occurred while "
962 "initializing the package manager: %message%"));
963 message
.ReplaceFirst("%message%", ex
.Message());
964 _NotifyUser("Error", message
.String());
968 BObjectList
<BSolverPackage
> packages
;
969 result
= manager
.Solver()->FindPackages("",
970 BSolver::B_FIND_CASE_INSENSITIVE
| BSolver::B_FIND_IN_NAME
971 | BSolver::B_FIND_IN_SUMMARY
| BSolver::B_FIND_IN_DESCRIPTION
972 | BSolver::B_FIND_IN_PROVIDES
,
974 if (result
!= B_OK
) {
975 BString
message(B_TRANSLATE("An error occurred while "
976 "obtaining the package list: %message%"));
977 message
.ReplaceFirst("%message%", strerror(result
));
978 _NotifyUser("Error", message
.String());
982 if (packages
.IsEmpty())
985 PackageInfoMap foundPackages
;
986 // if a given package is installed locally, we will potentially
987 // get back multiple entries, one for each local installation
988 // location, and one for each remote repository the package
989 // is available in. The above map is used to ensure that in such
990 // cases we consolidate the information, rather than displaying
992 PackageInfoMap remotePackages
;
993 // any package that we find in a remote repository goes in this map.
994 // this is later used to discern which packages came from a local
995 // installation only, as those must be handled a bit differently
996 // upon uninstallation, since we'd no longer be able to pull them
998 BStringList systemFlaggedPackages
;
999 // any packages flagged as a system package are added to this list.
1000 // such packages cannot be uninstalled, nor can any of their deps.
1001 PackageInfoMap systemInstalledPackages
;
1002 // any packages installed in system are added to this list.
1003 // This is later used for dependency resolution of the actual
1004 // system packages in order to compute the list of protected
1005 // dependencies indicated above.
1007 for (int32 i
= 0; i
< packages
.CountItems(); i
++) {
1008 BSolverPackage
* package
= packages
.ItemAt(i
);
1009 const BPackageInfo
& repoPackageInfo
= package
->Info();
1010 const BString repositoryName
= package
->Repository()->Name();
1011 PackageInfoRef modelInfo
;
1012 PackageInfoMap::iterator it
= foundPackages
.find(
1013 repoPackageInfo
.Name());
1014 if (it
!= foundPackages
.end())
1015 modelInfo
.SetTo(it
->second
);
1017 // Add new package info
1018 modelInfo
.SetTo(new(std::nothrow
) PackageInfo(repoPackageInfo
),
1021 if (modelInfo
.Get() == NULL
)
1024 modelInfo
->SetDepotName(repositoryName
);
1026 foundPackages
[repoPackageInfo
.Name()] = modelInfo
;
1029 modelInfo
->AddListener(this);
1031 BSolverRepository
* repository
= package
->Repository();
1032 if (dynamic_cast<BPackageManager::RemoteRepository
*>(repository
)
1034 depots
[repository
->Name()].AddPackage(modelInfo
);
1035 remotePackages
[modelInfo
->Name()] = modelInfo
;
1037 if (repository
== static_cast<const BSolverRepository
*>(
1038 manager
.SystemRepository())) {
1039 modelInfo
->AddInstallationLocation(
1040 B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
);
1041 if (!modelInfo
->IsSystemPackage()) {
1042 systemInstalledPackages
[repoPackageInfo
.FileName()]
1045 } else if (repository
== static_cast<const BSolverRepository
*>(
1046 manager
.HomeRepository())) {
1047 modelInfo
->AddInstallationLocation(
1048 B_PACKAGE_INSTALLATION_LOCATION_HOME
);
1052 if (modelInfo
->IsSystemPackage())
1053 systemFlaggedPackages
.Add(repoPackageInfo
.FileName());
1056 bool wasEmpty
= fModel
.Depots().IsEmpty();
1057 if (force
|| wasEmpty
)
1058 fModel
.StopPopulatingAllPackages();
1060 BAutolock
lock(fModel
.Lock());
1065 // filter remote packages from the found list
1066 // any packages remaining will be locally installed packages
1067 // that weren't acquired from a repository
1068 for (PackageInfoMap::iterator it
= remotePackages
.begin();
1069 it
!= remotePackages
.end(); it
++) {
1070 foundPackages
.erase(it
->first
);
1073 if (!foundPackages
.empty()) {
1074 BString repoName
= B_TRANSLATE("Local");
1075 depots
[repoName
] = DepotInfo(repoName
);
1076 DepotInfoMap::iterator depot
= depots
.find(repoName
);
1077 for (PackageInfoMap::iterator it
= foundPackages
.begin();
1078 it
!= foundPackages
.end(); ++it
) {
1079 depot
->second
.AddPackage(it
->second
);
1083 for (DepotInfoMap::iterator it
= depots
.begin(); it
!= depots
.end(); it
++) {
1084 if (fModel
.HasDepot(it
->second
.Name()))
1085 fModel
.SyncDepot(it
->second
);
1087 fModel
.AddDepot(it
->second
);
1090 fModel
.PopulateWebAppRepositoryCodes();
1092 // start retrieving package icons and average ratings
1093 if (force
|| wasEmpty
)
1094 fModel
.PopulateAllPackages();
1096 // compute the OS package dependencies
1098 // create the solver
1100 status_t error
= BSolver::Create(solver
);
1102 throw BFatalErrorException(error
, "Failed to create solver.");
1104 ObjectDeleter
<BSolver
> solverDeleter(solver
);
1106 error
= find_directory(B_SYSTEM_PACKAGES_DIRECTORY
, &systemPath
);
1107 if (error
!= B_OK
) {
1108 throw BFatalErrorException(error
,
1109 "Unable to retrieve system packages directory.");
1112 // add the "installed" repository with the given packages
1113 BSolverRepository installedRepository
;
1115 BRepositoryBuilder
installedRepositoryBuilder(installedRepository
,
1117 for (int32 i
= 0; i
< systemFlaggedPackages
.CountStrings(); i
++) {
1118 BPath
packagePath(systemPath
);
1119 packagePath
.Append(systemFlaggedPackages
.StringAt(i
));
1120 installedRepositoryBuilder
.AddPackage(packagePath
.Path());
1122 installedRepositoryBuilder
.AddToSolver(solver
, true);
1125 // add system repository
1126 BSolverRepository systemRepository
;
1128 BRepositoryBuilder
systemRepositoryBuilder(systemRepository
,
1130 for (PackageInfoMap::iterator it
= systemInstalledPackages
.begin();
1131 it
!= systemInstalledPackages
.end(); it
++) {
1132 BPath
packagePath(systemPath
);
1133 packagePath
.Append(it
->first
);
1134 systemRepositoryBuilder
.AddPackage(packagePath
.Path());
1136 systemRepositoryBuilder
.AddToSolver(solver
, false);
1140 error
= solver
->VerifyInstallation();
1141 if (error
!= B_OK
) {
1142 throw BFatalErrorException(error
, "Failed to compute packages to "
1146 BSolverResult solverResult
;
1147 error
= solver
->GetResult(solverResult
);
1148 if (error
!= B_OK
) {
1149 throw BFatalErrorException(error
, "Failed to retrieve system "
1150 "package dependency list.");
1153 for (int32 i
= 0; const BSolverResultElement
* element
1154 = solverResult
.ElementAt(i
); i
++) {
1155 BSolverPackage
* package
= element
->Package();
1156 if (element
->Type() == BSolverResultElement::B_TYPE_INSTALL
) {
1157 PackageInfoMap::iterator it
= systemInstalledPackages
.find(
1158 package
->Info().FileName());
1159 if (it
!= systemInstalledPackages
.end())
1160 it
->second
->SetSystemDependency(true);
1163 } catch (BFatalErrorException ex
) {
1164 printf("Fatal exception occurred while resolving system dependencies: "
1165 "%s, details: %s\n", strerror(ex
.Error()), ex
.Details().String());
1166 } catch (BNothingToDoException
) {
1168 } catch (BException ex
) {
1169 printf("Exception occurred while resolving system dependencies: %s\n",
1170 ex
.Message().String());
1172 printf("Unknown exception occurred while resolving system "
1179 MainWindow::_StartRefreshWorker(bool force
)
1181 if (fModelWorker
!= B_BAD_THREAD_ID
)
1184 RefreshWorkerParameters
* parameters
= new(std::nothrow
)
1185 RefreshWorkerParameters(this, force
);
1186 if (parameters
== NULL
)
1189 fWorkStatusView
->SetBusy(B_TRANSLATE("Refreshing..."));
1191 ObjectDeleter
<RefreshWorkerParameters
> deleter(parameters
);
1192 fModelWorker
= spawn_thread(&_RefreshModelThreadWorker
, "model loader",
1193 B_LOW_PRIORITY
, parameters
);
1195 if (fModelWorker
> 0) {
1197 resume_thread(fModelWorker
);
1203 MainWindow::_RefreshModelThreadWorker(void* arg
)
1205 RefreshWorkerParameters
* parameters
1206 = reinterpret_cast<RefreshWorkerParameters
*>(arg
);
1207 MainWindow
* mainWindow
= parameters
->window
;
1208 ObjectDeleter
<RefreshWorkerParameters
> deleter(parameters
);
1210 BMessenger
messenger(mainWindow
);
1212 mainWindow
->_RefreshRepositories(parameters
->forceRefresh
);
1214 if (mainWindow
->fTerminating
)
1217 mainWindow
->_RefreshPackageList(parameters
->forceRefresh
);
1219 messenger
.SendMessage(MSG_MODEL_WORKER_DONE
);
1226 MainWindow::_PackageActionWorker(void* arg
)
1228 MainWindow
* window
= reinterpret_cast<MainWindow
*>(arg
);
1230 while (acquire_sem(window
->fPendingActionsSem
) == B_OK
) {
1231 PackageActionRef ref
;
1233 AutoLocker
<BLocker
> lock(&window
->fPendingActionsLock
);
1234 ref
= window
->fPendingActions
.ItemAt(0);
1235 if (ref
.Get() == NULL
)
1237 window
->fPendingActions
.Remove(0);
1240 BMessenger
messenger(window
);
1241 BMessage
busyMessage(MSG_PACKAGE_WORKER_BUSY
);
1242 BString
text(ref
->Label());
1244 busyMessage
.AddString("reason", text
);
1246 messenger
.SendMessage(&busyMessage
);
1248 messenger
.SendMessage(MSG_PACKAGE_WORKER_IDLE
);
1256 MainWindow::_PopulatePackageWorker(void* arg
)
1258 MainWindow
* window
= reinterpret_cast<MainWindow
*>(arg
);
1260 while (acquire_sem(window
->fPackageToPopulateSem
) == B_OK
) {
1261 PackageInfoRef package
;
1263 AutoLocker
<BLocker
> lock(&window
->fPackageToPopulateLock
);
1264 package
= window
->fPackageToPopulate
;
1267 if (package
.Get() != NULL
) {
1268 window
->fModel
.PopulatePackage(package
,
1269 Model::POPULATE_USER_RATINGS
| Model::POPULATE_SCREEN_SHOTS
1270 | Model::POPULATE_CHANGELOG
);
1278 /* static */ status_t
1279 MainWindow::_PackagesToShowWorker(void* arg
)
1281 MainWindow
* window
= reinterpret_cast<MainWindow
*>(arg
);
1283 while (acquire_sem(window
->fNewPackagesToShowSem
) == B_OK
) {
1284 PackageList packageList
;
1287 AutoLocker
<BLocker
> lock(&window
->fPackagesToShowListLock
);
1288 packageList
= window
->fPackagesToShowList
;
1289 listID
= atomic_get(&window
->fPackagesToShowListID
);
1290 window
->fPackagesToShowList
.Clear();
1293 // Add packages to list views in batches of kPackagesPerUpdate so we
1294 // don't block the window thread for long with each iteration
1296 kPackagesPerUpdate
= 20
1298 uint32 packagesInMessage
= 0;
1299 BMessage
message(MSG_ADD_VISIBLE_PACKAGES
);
1300 BMessenger
messenger(window
);
1301 bool listIsOutdated
= false;
1303 for (int i
= 0; i
< packageList
.CountItems(); i
++) {
1304 const PackageInfoRef
& package
= packageList
.ItemAtFast(i
);
1306 if (packagesInMessage
>= kPackagesPerUpdate
) {
1307 if (listID
!= atomic_get(&window
->fPackagesToShowListID
)) {
1308 // The model was changed again in the meantime, and thus
1309 // our package list isn't current anymore. Send no further
1310 // messags based on this list and go back to start.
1311 listIsOutdated
= true;
1315 message
.AddInt32("list_id", listID
);
1316 messenger
.SendMessage(&message
);
1317 message
.MakeEmpty();
1318 packagesInMessage
= 0;
1320 // Don't spam the window's message queue, which would make it
1321 // unresponsive (i.e. allows UI messages to get in between our
1322 // messages). When it has processed the message we just sent,
1323 // it will let us know by releasing the semaphore.
1324 acquire_sem(window
->fShowPackagesAcknowledgeSem
);
1326 package
->AcquireReference();
1327 message
.AddPointer("package_ref", package
.Get());
1328 packagesInMessage
++;
1334 // Send remaining package infos, if any, which didn't make it into
1335 // the last message (count < kPackagesPerUpdate)
1336 if (packagesInMessage
> 0) {
1337 message
.AddInt32("list_id", listID
);
1338 messenger
.SendMessage(&message
);
1339 acquire_sem(window
->fShowPackagesAcknowledgeSem
);
1342 // Update selected package in list views
1343 messenger
.SendMessage(MSG_UPDATE_SELECTED_PACKAGE
);
1351 MainWindow::_NotifyUser(const char* title
, const char* message
)
1353 BAlert
* alert
= new(std::nothrow
) BAlert(title
, message
,
1354 B_TRANSLATE("Close"));
1362 MainWindow::_OpenLoginWindow(const BMessage
& onSuccessMessage
)
1364 UserLoginWindow
* window
= new UserLoginWindow(this,
1365 BRect(0, 0, 500, 400), fModel
);
1367 if (onSuccessMessage
.what
!= 0)
1368 window
->SetOnSuccessMessage(BMessenger(this), onSuccessMessage
);
1375 MainWindow::_UpdateAuthorization()
1377 BString
username(fModel
.Username());
1378 bool hasUser
= !username
.IsEmpty();
1380 if (fLogOutItem
!= NULL
)
1381 fLogOutItem
->SetEnabled(hasUser
);
1382 if (fLogInItem
!= NULL
) {
1384 fLogInItem
->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS
));
1386 fLogInItem
->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS
));
1389 if (fUserMenu
!= NULL
) {
1391 if (username
.Length() == 0) {
1392 label
= B_TRANSLATE("Not logged in");
1394 label
= B_TRANSLATE("Logged in as %User%");
1395 label
.ReplaceAll("%User%", username
);
1397 fUserMenu
->Superitem()->SetLabel(label
);
1403 MainWindow::_RatePackage()
1405 if (fModel
.Username().IsEmpty()) {
1406 BAlert
* alert
= new(std::nothrow
) BAlert(
1407 B_TRANSLATE("Not logged in"),
1408 B_TRANSLATE("You need to be logged into an account before you "
1409 "can rate packages."),
1410 B_TRANSLATE("Cancel"),
1411 B_TRANSLATE("Login or Create account"));
1416 int32 choice
= alert
->Go();
1418 _OpenLoginWindow(BMessage(MSG_RATE_PACKAGE
));
1422 // TODO: Allow only one RatePackageWindow
1423 // TODO: Mechanism for remembering the window frame
1424 RatePackageWindow
* window
= new RatePackageWindow(this,
1425 BRect(0, 0, 500, 400), fModel
);
1426 window
->SetPackage(fPackageInfoView
->Package());
1432 MainWindow::_ShowScreenshot()
1434 // TODO: Mechanism for remembering the window frame
1435 if (fScreenshotWindow
== NULL
)
1436 fScreenshotWindow
= new ScreenshotWindow(this, BRect(0, 0, 500, 400));
1438 if (fScreenshotWindow
->LockWithTimeout(1000) != B_OK
)
1441 fScreenshotWindow
->SetPackage(fPackageInfoView
->Package());
1443 if (fScreenshotWindow
->IsHidden())
1444 fScreenshotWindow
->Show();
1446 fScreenshotWindow
->Activate();
1448 fScreenshotWindow
->Unlock();