xref: /haiku/src/apps/haikudepot/ui/MainWindow.cpp (revision fdec4227f5fc60024af6f092e1ae14f6425938b9)
104c9d578SStephan Aßmus /*
204c9d578SStephan Aßmus  * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
304c9d578SStephan Aßmus  * Copyright 2013, Rene Gollent, rene@gollent.com.
404c9d578SStephan Aßmus  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
504c9d578SStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
604c9d578SStephan Aßmus  */
704c9d578SStephan Aßmus 
804c9d578SStephan Aßmus #include "MainWindow.h"
904c9d578SStephan Aßmus 
1004c9d578SStephan Aßmus #include <map>
1104c9d578SStephan Aßmus 
1204c9d578SStephan Aßmus #include <stdio.h>
1304c9d578SStephan Aßmus 
1404c9d578SStephan Aßmus #include <Alert.h>
1504c9d578SStephan Aßmus #include <Autolock.h>
1604c9d578SStephan Aßmus #include <Application.h>
1704c9d578SStephan Aßmus #include <Button.h>
1804c9d578SStephan Aßmus #include <Catalog.h>
1904c9d578SStephan Aßmus #include <LayoutBuilder.h>
2004c9d578SStephan Aßmus #include <MenuBar.h>
2104c9d578SStephan Aßmus #include <MenuItem.h>
2204c9d578SStephan Aßmus #include <Messenger.h>
2304c9d578SStephan Aßmus #include <ScrollView.h>
2404c9d578SStephan Aßmus #include <StringList.h>
2504c9d578SStephan Aßmus #include <TabView.h>
2604c9d578SStephan Aßmus 
2704c9d578SStephan Aßmus #include <package/Context.h>
2804c9d578SStephan Aßmus #include <package/manager/Exceptions.h>
2904c9d578SStephan Aßmus #include <package/manager/RepositoryBuilder.h>
3004c9d578SStephan Aßmus #include <package/RefreshRepositoryRequest.h>
3104c9d578SStephan Aßmus #include <package/PackageRoster.h>
3204c9d578SStephan Aßmus #include "package/RepositoryCache.h"
3304c9d578SStephan Aßmus #include <package/solver/SolverPackage.h>
3404c9d578SStephan Aßmus #include <package/solver/SolverProblem.h>
3504c9d578SStephan Aßmus #include <package/solver/SolverProblemSolution.h>
3604c9d578SStephan Aßmus #include <package/solver/SolverRepository.h>
3704c9d578SStephan Aßmus #include <package/solver/SolverResult.h>
3804c9d578SStephan Aßmus 
3904c9d578SStephan Aßmus #include "AutoDeleter.h"
4004c9d578SStephan Aßmus #include "AutoLocker.h"
4104c9d578SStephan Aßmus #include "DecisionProvider.h"
4204c9d578SStephan Aßmus #include "FilterView.h"
4304c9d578SStephan Aßmus #include "JobStateListener.h"
4404c9d578SStephan Aßmus #include "PackageInfoView.h"
4504c9d578SStephan Aßmus #include "PackageListView.h"
4604c9d578SStephan Aßmus #include "PackageManager.h"
4704c9d578SStephan Aßmus #include "RatePackageWindow.h"
48e0a26dceSStephan Aßmus #include "support.h"
4904c9d578SStephan Aßmus #include "UserLoginWindow.h"
5004c9d578SStephan Aßmus 
5104c9d578SStephan Aßmus 
5204c9d578SStephan Aßmus #undef B_TRANSLATION_CONTEXT
5304c9d578SStephan Aßmus #define B_TRANSLATION_CONTEXT "MainWindow"
5404c9d578SStephan Aßmus 
5504c9d578SStephan Aßmus 
5604c9d578SStephan Aßmus enum {
5704c9d578SStephan Aßmus 	MSG_MODEL_WORKER_DONE		= 'mmwd',
5804c9d578SStephan Aßmus 	MSG_REFRESH_DEPOTS			= 'mrdp',
5904c9d578SStephan Aßmus 	MSG_LOG_IN					= 'lgin',
608f03a0f9SStephan Aßmus 	MSG_LOG_OUT					= 'lgot',
618f03a0f9SStephan Aßmus 	MSG_AUTHORIZATION_CHANGED	= 'athc',
6204c9d578SStephan Aßmus 	MSG_PACKAGE_STATE_CHANGED	= 'mpsc',
63*fdec4227SStephan Aßmus 
64*fdec4227SStephan Aßmus 	MSG_SHOW_AVAILABLE_PACKAGES	= 'savl',
65*fdec4227SStephan Aßmus 	MSG_SHOW_INSTALLED_PACKAGES	= 'sins',
6604c9d578SStephan Aßmus 	MSG_SHOW_SOURCE_PACKAGES	= 'ssrc',
6704c9d578SStephan Aßmus 	MSG_SHOW_DEVELOP_PACKAGES	= 'sdvl'
6804c9d578SStephan Aßmus };
6904c9d578SStephan Aßmus 
7004c9d578SStephan Aßmus 
7104c9d578SStephan Aßmus using namespace BPackageKit;
7204c9d578SStephan Aßmus using namespace BPackageKit::BManager::BPrivate;
7304c9d578SStephan Aßmus 
7404c9d578SStephan Aßmus 
7504c9d578SStephan Aßmus typedef std::map<BString, PackageInfoRef> PackageInfoMap;
7604c9d578SStephan Aßmus typedef std::map<BString, DepotInfo> DepotInfoMap;
7704c9d578SStephan Aßmus 
7804c9d578SStephan Aßmus 
7904c9d578SStephan Aßmus struct RefreshWorkerParameters {
8004c9d578SStephan Aßmus 	MainWindow* window;
8104c9d578SStephan Aßmus 	bool forceRefresh;
8204c9d578SStephan Aßmus 
8304c9d578SStephan Aßmus 	RefreshWorkerParameters(MainWindow* window, bool forceRefresh)
8404c9d578SStephan Aßmus 		:
8504c9d578SStephan Aßmus 		window(window),
8604c9d578SStephan Aßmus 		forceRefresh(forceRefresh)
8704c9d578SStephan Aßmus 	{
8804c9d578SStephan Aßmus 	}
8904c9d578SStephan Aßmus };
9004c9d578SStephan Aßmus 
9104c9d578SStephan Aßmus 
928f03a0f9SStephan Aßmus class MessageModelListener : public ModelListener {
938f03a0f9SStephan Aßmus public:
948f03a0f9SStephan Aßmus 	MessageModelListener(const BMessenger& messenger)
958f03a0f9SStephan Aßmus 		:
968f03a0f9SStephan Aßmus 		fMessenger(messenger)
978f03a0f9SStephan Aßmus 	{
988f03a0f9SStephan Aßmus 	}
998f03a0f9SStephan Aßmus 
1008f03a0f9SStephan Aßmus 	virtual void AuthorizationChanged()
1018f03a0f9SStephan Aßmus 	{
1028f03a0f9SStephan Aßmus 		if (fMessenger.IsValid())
1038f03a0f9SStephan Aßmus 			fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED);
1048f03a0f9SStephan Aßmus 	}
1058f03a0f9SStephan Aßmus 
1068f03a0f9SStephan Aßmus private:
1078f03a0f9SStephan Aßmus 	BMessenger	fMessenger;
1088f03a0f9SStephan Aßmus };
1098f03a0f9SStephan Aßmus 
1108f03a0f9SStephan Aßmus 
11104c9d578SStephan Aßmus MainWindow::MainWindow(BRect frame, const BMessage& settings)
11204c9d578SStephan Aßmus 	:
11304c9d578SStephan Aßmus 	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
11404c9d578SStephan Aßmus 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
11504c9d578SStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
116e0a26dceSStephan Aßmus 	fUserMenu(NULL),
117e0a26dceSStephan Aßmus 	fLogInItem(NULL),
118e0a26dceSStephan Aßmus 	fLogOutItem(NULL),
1198f03a0f9SStephan Aßmus 	fModelListener(new MessageModelListener(BMessenger(this)), true),
12004c9d578SStephan Aßmus 	fTerminating(false),
1216d519fd1SStephan Aßmus 	fSinglePackageMode(false),
12204c9d578SStephan Aßmus 	fModelWorker(B_BAD_THREAD_ID)
12304c9d578SStephan Aßmus {
12404c9d578SStephan Aßmus 	BMenuBar* menuBar = new BMenuBar(B_TRANSLATE("Main Menu"));
12504c9d578SStephan Aßmus 	_BuildMenu(menuBar);
12604c9d578SStephan Aßmus 
127e0a26dceSStephan Aßmus 	BMenuBar* userMenuBar = new BMenuBar(B_TRANSLATE("User Menu"));
128e0a26dceSStephan Aßmus 	_BuildUserMenu(userMenuBar);
129e0a26dceSStephan Aßmus 	set_small_font(userMenuBar);
130e0a26dceSStephan Aßmus 	userMenuBar->SetExplicitMaxSize(BSize(B_SIZE_UNSET,
131e0a26dceSStephan Aßmus 		menuBar->MaxSize().height));
132e0a26dceSStephan Aßmus 
13304c9d578SStephan Aßmus 	fFilterView = new FilterView();
13404c9d578SStephan Aßmus 	fPackageListView = new PackageListView(fModel.Lock());
13504c9d578SStephan Aßmus 	fPackageInfoView = new PackageInfoView(fModel.Lock(), this);
13604c9d578SStephan Aßmus 
13704c9d578SStephan Aßmus 	fSplitView = new BSplitView(B_VERTICAL, 5.0f);
13804c9d578SStephan Aßmus 
13904c9d578SStephan Aßmus 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
140e0a26dceSStephan Aßmus 		.AddGroup(B_HORIZONTAL, 0.0f)
141e0a26dceSStephan Aßmus 			.Add(menuBar, 1.0f)
142e0a26dceSStephan Aßmus 			.Add(userMenuBar, 0.0f)
143e0a26dceSStephan Aßmus 		.End()
14404c9d578SStephan Aßmus 		.Add(fFilterView)
14504c9d578SStephan Aßmus 		.AddSplit(fSplitView)
14604c9d578SStephan Aßmus 			.AddGroup(B_VERTICAL)
14704c9d578SStephan Aßmus 				.Add(fPackageListView)
14804c9d578SStephan Aßmus 				.SetInsets(
14904c9d578SStephan Aßmus 					B_USE_DEFAULT_SPACING, 0.0f,
15004c9d578SStephan Aßmus 					B_USE_DEFAULT_SPACING, 0.0f)
15104c9d578SStephan Aßmus 			.End()
15204c9d578SStephan Aßmus 			.Add(fPackageInfoView)
15304c9d578SStephan Aßmus 		.End()
15404c9d578SStephan Aßmus 	;
15504c9d578SStephan Aßmus 
15604c9d578SStephan Aßmus 	fSplitView->SetCollapsible(0, false);
15704c9d578SStephan Aßmus 	fSplitView->SetCollapsible(1, false);
15804c9d578SStephan Aßmus 
1598f03a0f9SStephan Aßmus 	fModel.AddListener(fModelListener);
1608f03a0f9SStephan Aßmus 
16104c9d578SStephan Aßmus 	// Restore settings
16204c9d578SStephan Aßmus 	BMessage columnSettings;
16304c9d578SStephan Aßmus 	if (settings.FindMessage("column settings", &columnSettings) == B_OK)
16404c9d578SStephan Aßmus 		fPackageListView->LoadState(&columnSettings);
16504c9d578SStephan Aßmus 
16604c9d578SStephan Aßmus 	bool showOption;
16704c9d578SStephan Aßmus 	if (settings.FindBool("show develop packages", &showOption) == B_OK)
16804c9d578SStephan Aßmus 		fModel.SetShowDevelopPackages(showOption);
16904c9d578SStephan Aßmus 	if (settings.FindBool("show source packages", &showOption) == B_OK)
17004c9d578SStephan Aßmus 		fModel.SetShowSourcePackages(showOption);
17104c9d578SStephan Aßmus 
1728f03a0f9SStephan Aßmus 	BString username;
1738f03a0f9SStephan Aßmus 	if (settings.FindString("username", &username) == B_OK
1748f03a0f9SStephan Aßmus 		&& username.Length() > 0) {
1758f03a0f9SStephan Aßmus 		fModel.SetUsername(username);
1768f03a0f9SStephan Aßmus 	}
1778f03a0f9SStephan Aßmus 
1788f03a0f9SStephan Aßmus 	// start worker threads
17904c9d578SStephan Aßmus 	BPackageRoster().StartWatching(this,
18004c9d578SStephan Aßmus 		B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
18104c9d578SStephan Aßmus 
18204c9d578SStephan Aßmus 	_StartRefreshWorker();
18304c9d578SStephan Aßmus 
18463975654SStephan Aßmus 	_InitWorkerThreads();
18504c9d578SStephan Aßmus }
18604c9d578SStephan Aßmus 
18704c9d578SStephan Aßmus 
1886d519fd1SStephan Aßmus MainWindow::MainWindow(BRect frame, const BMessage& settings,
1896d519fd1SStephan Aßmus 	const PackageInfoRef& package)
1906d519fd1SStephan Aßmus 	:
1916d519fd1SStephan Aßmus 	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
1926d519fd1SStephan Aßmus 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
1936d519fd1SStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
194e0a26dceSStephan Aßmus 	fUserMenu(NULL),
195e0a26dceSStephan Aßmus 	fLogInItem(NULL),
1966d519fd1SStephan Aßmus 	fLogOutItem(NULL),
1976d519fd1SStephan Aßmus 	fModelListener(new MessageModelListener(BMessenger(this)), true),
1986d519fd1SStephan Aßmus 	fTerminating(false),
1996d519fd1SStephan Aßmus 	fSinglePackageMode(true),
2006d519fd1SStephan Aßmus 	fModelWorker(B_BAD_THREAD_ID)
2016d519fd1SStephan Aßmus {
2026d519fd1SStephan Aßmus 	fFilterView = new FilterView();
2036d519fd1SStephan Aßmus 	fPackageListView = new PackageListView(fModel.Lock());
2046d519fd1SStephan Aßmus 	fPackageInfoView = new PackageInfoView(fModel.Lock(), this);
2056d519fd1SStephan Aßmus 
2066d519fd1SStephan Aßmus 	BLayoutBuilder::Group<>(this, B_VERTICAL)
2076d519fd1SStephan Aßmus 		.Add(fPackageInfoView)
2086d519fd1SStephan Aßmus 		.SetInsets(0, B_USE_WINDOW_INSETS, 0, 0)
2096d519fd1SStephan Aßmus 	;
2106d519fd1SStephan Aßmus 
2116d519fd1SStephan Aßmus 	fModel.AddListener(fModelListener);
2126d519fd1SStephan Aßmus 
2136d519fd1SStephan Aßmus 	// Restore settings
2146d519fd1SStephan Aßmus 	BString username;
2156d519fd1SStephan Aßmus 	if (settings.FindString("username", &username) == B_OK
2166d519fd1SStephan Aßmus 		&& username.Length() > 0) {
2176d519fd1SStephan Aßmus 		fModel.SetUsername(username);
2186d519fd1SStephan Aßmus 	}
2196d519fd1SStephan Aßmus 
2206d519fd1SStephan Aßmus 	fPackageInfoView->SetPackage(package);
22163975654SStephan Aßmus 
22263975654SStephan Aßmus 	_InitWorkerThreads();
2236d519fd1SStephan Aßmus }
2246d519fd1SStephan Aßmus 
2256d519fd1SStephan Aßmus 
22604c9d578SStephan Aßmus MainWindow::~MainWindow()
22704c9d578SStephan Aßmus {
22804c9d578SStephan Aßmus 	BPackageRoster().StopWatching(this);
22904c9d578SStephan Aßmus 
23004c9d578SStephan Aßmus 	fTerminating = true;
23104c9d578SStephan Aßmus 	if (fModelWorker > 0)
23204c9d578SStephan Aßmus 		wait_for_thread(fModelWorker, NULL);
23304c9d578SStephan Aßmus 
23404c9d578SStephan Aßmus 	delete_sem(fPendingActionsSem);
23504c9d578SStephan Aßmus 	wait_for_thread(fPendingActionsWorker, NULL);
23604c9d578SStephan Aßmus 
23704c9d578SStephan Aßmus 	delete_sem(fPackageToPopulateSem);
23804c9d578SStephan Aßmus 	wait_for_thread(fPopulatePackageWorker, NULL);
23904c9d578SStephan Aßmus }
24004c9d578SStephan Aßmus 
24104c9d578SStephan Aßmus 
24204c9d578SStephan Aßmus bool
24304c9d578SStephan Aßmus MainWindow::QuitRequested()
24404c9d578SStephan Aßmus {
24504c9d578SStephan Aßmus 	BMessage settings;
24604c9d578SStephan Aßmus 	StoreSettings(settings);
24704c9d578SStephan Aßmus 
24804c9d578SStephan Aßmus 	BMessage message(MSG_MAIN_WINDOW_CLOSED);
24904c9d578SStephan Aßmus 	message.AddMessage("window settings", &settings);
25004c9d578SStephan Aßmus 
25104c9d578SStephan Aßmus 	be_app->PostMessage(&message);
25204c9d578SStephan Aßmus 
25304c9d578SStephan Aßmus 	return true;
25404c9d578SStephan Aßmus }
25504c9d578SStephan Aßmus 
25604c9d578SStephan Aßmus 
25704c9d578SStephan Aßmus void
25804c9d578SStephan Aßmus MainWindow::MessageReceived(BMessage* message)
25904c9d578SStephan Aßmus {
26004c9d578SStephan Aßmus 	switch (message->what) {
26104c9d578SStephan Aßmus 		case MSG_MODEL_WORKER_DONE:
26204c9d578SStephan Aßmus 		{
26304c9d578SStephan Aßmus 			fModelWorker = B_BAD_THREAD_ID;
26404c9d578SStephan Aßmus 			_AdoptModel();
26504c9d578SStephan Aßmus 			fFilterView->AdoptModel(fModel);
26604c9d578SStephan Aßmus 			break;
26704c9d578SStephan Aßmus 		}
26804c9d578SStephan Aßmus 		case B_SIMPLE_DATA:
26904c9d578SStephan Aßmus 		case B_REFS_RECEIVED:
27004c9d578SStephan Aßmus 			// TODO: ?
27104c9d578SStephan Aßmus 			break;
27204c9d578SStephan Aßmus 
27304c9d578SStephan Aßmus 		case B_PACKAGE_UPDATE:
27404c9d578SStephan Aßmus 			// TODO: We should do a more selective update depending on the
27504c9d578SStephan Aßmus 			// "event", "location", and "change count" fields!
27604c9d578SStephan Aßmus 			_StartRefreshWorker(false);
27704c9d578SStephan Aßmus 			break;
27804c9d578SStephan Aßmus 
27904c9d578SStephan Aßmus 		case MSG_REFRESH_DEPOTS:
28004c9d578SStephan Aßmus 			_StartRefreshWorker(true);
28104c9d578SStephan Aßmus 			break;
28204c9d578SStephan Aßmus 
28304c9d578SStephan Aßmus 		case MSG_LOG_IN:
2841bbda92dSStephan Aßmus 			_OpenLoginWindow(BMessage());
28504c9d578SStephan Aßmus 			break;
28604c9d578SStephan Aßmus 
2878f03a0f9SStephan Aßmus 		case MSG_LOG_OUT:
2888f03a0f9SStephan Aßmus 			fModel.SetUsername("");
2898f03a0f9SStephan Aßmus 			break;
2908f03a0f9SStephan Aßmus 
2918f03a0f9SStephan Aßmus 		case MSG_AUTHORIZATION_CHANGED:
2928f03a0f9SStephan Aßmus 			_UpdateAuthorization();
2938f03a0f9SStephan Aßmus 			break;
2948f03a0f9SStephan Aßmus 
295*fdec4227SStephan Aßmus 		case MSG_SHOW_AVAILABLE_PACKAGES:
296*fdec4227SStephan Aßmus 			{
297*fdec4227SStephan Aßmus 				BAutolock locker(fModel.Lock());
298*fdec4227SStephan Aßmus 				fModel.SetShowAvailablePackages(
299*fdec4227SStephan Aßmus 					!fModel.ShowAvailablePackages());
300*fdec4227SStephan Aßmus 			}
301*fdec4227SStephan Aßmus 			_AdoptModel();
302*fdec4227SStephan Aßmus 			break;
303*fdec4227SStephan Aßmus 
304*fdec4227SStephan Aßmus 		case MSG_SHOW_INSTALLED_PACKAGES:
305*fdec4227SStephan Aßmus 			{
306*fdec4227SStephan Aßmus 				BAutolock locker(fModel.Lock());
307*fdec4227SStephan Aßmus 				fModel.SetShowInstalledPackages(
308*fdec4227SStephan Aßmus 					!fModel.ShowInstalledPackages());
309*fdec4227SStephan Aßmus 			}
310*fdec4227SStephan Aßmus 			_AdoptModel();
311*fdec4227SStephan Aßmus 			break;
312*fdec4227SStephan Aßmus 
31304c9d578SStephan Aßmus 		case MSG_SHOW_SOURCE_PACKAGES:
31404c9d578SStephan Aßmus 			{
31504c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
31604c9d578SStephan Aßmus 				fModel.SetShowSourcePackages(!fModel.ShowSourcePackages());
31704c9d578SStephan Aßmus 			}
31804c9d578SStephan Aßmus 			_AdoptModel();
31904c9d578SStephan Aßmus 			break;
32004c9d578SStephan Aßmus 
32104c9d578SStephan Aßmus 		case MSG_SHOW_DEVELOP_PACKAGES:
32204c9d578SStephan Aßmus 			{
32304c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
32404c9d578SStephan Aßmus 				fModel.SetShowDevelopPackages(!fModel.ShowDevelopPackages());
32504c9d578SStephan Aßmus 			}
32604c9d578SStephan Aßmus 			_AdoptModel();
32704c9d578SStephan Aßmus 			break;
32804c9d578SStephan Aßmus 
32904c9d578SStephan Aßmus 		case MSG_PACKAGE_SELECTED:
33004c9d578SStephan Aßmus 		{
33104c9d578SStephan Aßmus 			BString title;
33204c9d578SStephan Aßmus 			if (message->FindString("title", &title) == B_OK) {
33304c9d578SStephan Aßmus 				int count = fVisiblePackages.CountItems();
33404c9d578SStephan Aßmus 				for (int i = 0; i < count; i++) {
33504c9d578SStephan Aßmus 					const PackageInfoRef& package
33604c9d578SStephan Aßmus 						= fVisiblePackages.ItemAtFast(i);
33704c9d578SStephan Aßmus 					if (package.Get() != NULL && package->Title() == title) {
33804c9d578SStephan Aßmus 						_AdoptPackage(package);
33904c9d578SStephan Aßmus 						break;
34004c9d578SStephan Aßmus 					}
34104c9d578SStephan Aßmus 				}
34204c9d578SStephan Aßmus 			} else {
34304c9d578SStephan Aßmus 				_ClearPackage();
34404c9d578SStephan Aßmus 			}
34504c9d578SStephan Aßmus 			break;
34604c9d578SStephan Aßmus 		}
34704c9d578SStephan Aßmus 
34804c9d578SStephan Aßmus 		case MSG_CATEGORY_SELECTED:
34904c9d578SStephan Aßmus 		{
35004c9d578SStephan Aßmus 			BString name;
35104c9d578SStephan Aßmus 			if (message->FindString("name", &name) != B_OK)
35204c9d578SStephan Aßmus 				name = "";
35304c9d578SStephan Aßmus 			{
35404c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
35504c9d578SStephan Aßmus 				fModel.SetCategory(name);
35604c9d578SStephan Aßmus 			}
35704c9d578SStephan Aßmus 			_AdoptModel();
35804c9d578SStephan Aßmus 			break;
35904c9d578SStephan Aßmus 		}
36004c9d578SStephan Aßmus 
36104c9d578SStephan Aßmus 		case MSG_DEPOT_SELECTED:
36204c9d578SStephan Aßmus 		{
36304c9d578SStephan Aßmus 			BString name;
36404c9d578SStephan Aßmus 			if (message->FindString("name", &name) != B_OK)
36504c9d578SStephan Aßmus 				name = "";
36604c9d578SStephan Aßmus 			{
36704c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
36804c9d578SStephan Aßmus 				fModel.SetDepot(name);
36904c9d578SStephan Aßmus 			}
37004c9d578SStephan Aßmus 			_AdoptModel();
37104c9d578SStephan Aßmus 			break;
37204c9d578SStephan Aßmus 		}
37304c9d578SStephan Aßmus 
37404c9d578SStephan Aßmus 		case MSG_SEARCH_TERMS_MODIFIED:
37504c9d578SStephan Aßmus 		{
37604c9d578SStephan Aßmus 			// TODO: Do this with a delay!
37704c9d578SStephan Aßmus 			BString searchTerms;
37804c9d578SStephan Aßmus 			if (message->FindString("search terms", &searchTerms) != B_OK)
37904c9d578SStephan Aßmus 				searchTerms = "";
38004c9d578SStephan Aßmus 			{
38104c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
38204c9d578SStephan Aßmus 				fModel.SetSearchTerms(searchTerms);
38304c9d578SStephan Aßmus 			}
38404c9d578SStephan Aßmus 			_AdoptModel();
38504c9d578SStephan Aßmus 			break;
38604c9d578SStephan Aßmus 		}
38704c9d578SStephan Aßmus 
38804c9d578SStephan Aßmus 		case MSG_PACKAGE_STATE_CHANGED:
38904c9d578SStephan Aßmus 		{
39004c9d578SStephan Aßmus 			PackageInfo* info;
39104c9d578SStephan Aßmus 			if (message->FindPointer("package", (void**)&info) == B_OK) {
39204c9d578SStephan Aßmus 				PackageInfoRef ref(info, true);
39304c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
39404c9d578SStephan Aßmus 				fModel.SetPackageState(ref, ref->State());
39504c9d578SStephan Aßmus 			}
39604c9d578SStephan Aßmus 			break;
39704c9d578SStephan Aßmus 		}
39804c9d578SStephan Aßmus 
39904c9d578SStephan Aßmus 		case MSG_RATE_PACKAGE:
4001bbda92dSStephan Aßmus 			_RatePackage();
40104c9d578SStephan Aßmus 			break;
40204c9d578SStephan Aßmus 
40304c9d578SStephan Aßmus 		default:
40404c9d578SStephan Aßmus 			BWindow::MessageReceived(message);
40504c9d578SStephan Aßmus 			break;
40604c9d578SStephan Aßmus 	}
40704c9d578SStephan Aßmus }
40804c9d578SStephan Aßmus 
40904c9d578SStephan Aßmus 
41004c9d578SStephan Aßmus void
41104c9d578SStephan Aßmus MainWindow::StoreSettings(BMessage& settings) const
41204c9d578SStephan Aßmus {
4136d519fd1SStephan Aßmus 	if (fSinglePackageMode)
4146d519fd1SStephan Aßmus 		settings.AddRect("small window frame", Frame());
4156d519fd1SStephan Aßmus 	else {
41604c9d578SStephan Aßmus 		settings.AddRect("window frame", Frame());
41704c9d578SStephan Aßmus 
41804c9d578SStephan Aßmus 		BMessage columnSettings;
41904c9d578SStephan Aßmus 		fPackageListView->SaveState(&columnSettings);
42004c9d578SStephan Aßmus 
42104c9d578SStephan Aßmus 		settings.AddMessage("column settings", &columnSettings);
42204c9d578SStephan Aßmus 
42304c9d578SStephan Aßmus 		settings.AddBool("show develop packages", fModel.ShowDevelopPackages());
42404c9d578SStephan Aßmus 		settings.AddBool("show source packages", fModel.ShowSourcePackages());
4256d519fd1SStephan Aßmus 	}
4268f03a0f9SStephan Aßmus 
4278f03a0f9SStephan Aßmus 	settings.AddString("username", fModel.Username());
42804c9d578SStephan Aßmus }
42904c9d578SStephan Aßmus 
43004c9d578SStephan Aßmus 
43104c9d578SStephan Aßmus void
43204c9d578SStephan Aßmus MainWindow::PackageChanged(const PackageInfoEvent& event)
43304c9d578SStephan Aßmus {
43404c9d578SStephan Aßmus 	if ((event.Changes() & PKG_CHANGED_STATE) != 0) {
43504c9d578SStephan Aßmus 		PackageInfoRef ref(event.Package());
43604c9d578SStephan Aßmus 		BMessage message(MSG_PACKAGE_STATE_CHANGED);
43704c9d578SStephan Aßmus 		message.AddPointer("package", ref.Get());
43804c9d578SStephan Aßmus 		ref.Detach();
43904c9d578SStephan Aßmus 			// reference needs to be released by MessageReceived();
44004c9d578SStephan Aßmus 		PostMessage(&message);
44104c9d578SStephan Aßmus 	}
44204c9d578SStephan Aßmus }
44304c9d578SStephan Aßmus 
44404c9d578SStephan Aßmus 
44504c9d578SStephan Aßmus status_t
44604c9d578SStephan Aßmus MainWindow::SchedulePackageActions(PackageActionList& list)
44704c9d578SStephan Aßmus {
44804c9d578SStephan Aßmus 	AutoLocker<BLocker> lock(&fPendingActionsLock);
44904c9d578SStephan Aßmus 	for (int32 i = 0; i < list.CountItems(); i++) {
45004c9d578SStephan Aßmus 		if (!fPendingActions.Add(list.ItemAtFast(i)))
45104c9d578SStephan Aßmus 			return B_NO_MEMORY;
45204c9d578SStephan Aßmus 	}
45304c9d578SStephan Aßmus 
45404c9d578SStephan Aßmus 	return release_sem_etc(fPendingActionsSem, list.CountItems(), 0);
45504c9d578SStephan Aßmus }
45604c9d578SStephan Aßmus 
45704c9d578SStephan Aßmus 
45804c9d578SStephan Aßmus Model*
45904c9d578SStephan Aßmus MainWindow::GetModel()
46004c9d578SStephan Aßmus {
46104c9d578SStephan Aßmus 	return &fModel;
46204c9d578SStephan Aßmus }
46304c9d578SStephan Aßmus 
46404c9d578SStephan Aßmus 
46504c9d578SStephan Aßmus void
46604c9d578SStephan Aßmus MainWindow::_BuildMenu(BMenuBar* menuBar)
46704c9d578SStephan Aßmus {
46804c9d578SStephan Aßmus 	BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
46904c9d578SStephan Aßmus 	menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"),
47004c9d578SStephan Aßmus 			new BMessage(MSG_REFRESH_DEPOTS)));
4718f03a0f9SStephan Aßmus 	menu->AddSeparatorItem();
472e0a26dceSStephan Aßmus 
47304c9d578SStephan Aßmus 	menuBar->AddItem(menu);
47404c9d578SStephan Aßmus 
475*fdec4227SStephan Aßmus 	menu = new BMenu(B_TRANSLATE("Options"));
476*fdec4227SStephan Aßmus 
477*fdec4227SStephan Aßmus 	fShowAvailablePackagesItem = new BMenuItem(
478*fdec4227SStephan Aßmus 		B_TRANSLATE("Show available packages"),
479*fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_AVAILABLE_PACKAGES));
480*fdec4227SStephan Aßmus 	menu->AddItem(fShowAvailablePackagesItem);
481*fdec4227SStephan Aßmus 
482*fdec4227SStephan Aßmus 	fShowInstalledPackagesItem = new BMenuItem(
483*fdec4227SStephan Aßmus 		B_TRANSLATE("Show installed packages"),
484*fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_INSTALLED_PACKAGES));
485*fdec4227SStephan Aßmus 	menu->AddItem(fShowInstalledPackagesItem);
486*fdec4227SStephan Aßmus 
487*fdec4227SStephan Aßmus 	menu->AddSeparatorItem();
488*fdec4227SStephan Aßmus 
489*fdec4227SStephan Aßmus 	fShowDevelopPackagesItem = new BMenuItem(
490*fdec4227SStephan Aßmus 		B_TRANSLATE("Show develop packages"),
491*fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_DEVELOP_PACKAGES));
492*fdec4227SStephan Aßmus 	menu->AddItem(fShowDevelopPackagesItem);
493*fdec4227SStephan Aßmus 
494*fdec4227SStephan Aßmus 	fShowSourcePackagesItem = new BMenuItem(
495*fdec4227SStephan Aßmus 		B_TRANSLATE("Show source packages"),
496*fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_SOURCE_PACKAGES));
497*fdec4227SStephan Aßmus 	menu->AddItem(fShowSourcePackagesItem);
498*fdec4227SStephan Aßmus 
499*fdec4227SStephan Aßmus 	menuBar->AddItem(menu);
50004c9d578SStephan Aßmus }
50104c9d578SStephan Aßmus 
50204c9d578SStephan Aßmus 
50304c9d578SStephan Aßmus void
504e0a26dceSStephan Aßmus MainWindow::_BuildUserMenu(BMenuBar* menuBar)
505e0a26dceSStephan Aßmus {
506e0a26dceSStephan Aßmus 	fUserMenu = new BMenu(B_TRANSLATE("Not logged in"));
507e0a26dceSStephan Aßmus 
508e0a26dceSStephan Aßmus 	fLogInItem = new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS),
509e0a26dceSStephan Aßmus 		new BMessage(MSG_LOG_IN));
510e0a26dceSStephan Aßmus 	fUserMenu->AddItem(fLogInItem);
511e0a26dceSStephan Aßmus 
512e0a26dceSStephan Aßmus 	fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"),
513e0a26dceSStephan Aßmus 		new BMessage(MSG_LOG_OUT));
514e0a26dceSStephan Aßmus 	fUserMenu->AddItem(fLogOutItem);
515e0a26dceSStephan Aßmus 
516e0a26dceSStephan Aßmus 	menuBar->AddItem(fUserMenu);
517e0a26dceSStephan Aßmus }
518e0a26dceSStephan Aßmus 
519e0a26dceSStephan Aßmus 
520e0a26dceSStephan Aßmus void
52163975654SStephan Aßmus MainWindow::_InitWorkerThreads()
52263975654SStephan Aßmus {
52363975654SStephan Aßmus 	fPendingActionsSem = create_sem(0, "PendingPackageActions");
52463975654SStephan Aßmus 	if (fPendingActionsSem >= 0) {
52563975654SStephan Aßmus 		fPendingActionsWorker = spawn_thread(&_PackageActionWorker,
52663975654SStephan Aßmus 			"Planet Express", B_NORMAL_PRIORITY, this);
52763975654SStephan Aßmus 		if (fPendingActionsWorker >= 0)
52863975654SStephan Aßmus 			resume_thread(fPendingActionsWorker);
52963975654SStephan Aßmus 	}
53063975654SStephan Aßmus 
53163975654SStephan Aßmus 	fPackageToPopulateSem = create_sem(0, "PopulatePackage");
53263975654SStephan Aßmus 	if (fPackageToPopulateSem >= 0) {
53363975654SStephan Aßmus 		fPopulatePackageWorker = spawn_thread(&_PopulatePackageWorker,
53463975654SStephan Aßmus 			"Package Populator", B_NORMAL_PRIORITY, this);
53563975654SStephan Aßmus 		if (fPopulatePackageWorker >= 0)
53663975654SStephan Aßmus 			resume_thread(fPopulatePackageWorker);
53763975654SStephan Aßmus 	}
53863975654SStephan Aßmus }
53963975654SStephan Aßmus 
54063975654SStephan Aßmus 
54163975654SStephan Aßmus void
54204c9d578SStephan Aßmus MainWindow::_AdoptModel()
54304c9d578SStephan Aßmus {
54404c9d578SStephan Aßmus 	fVisiblePackages = fModel.CreatePackageList();
54504c9d578SStephan Aßmus 
54604c9d578SStephan Aßmus 	fPackageListView->Clear();
54704c9d578SStephan Aßmus 	for (int32 i = 0; i < fVisiblePackages.CountItems(); i++) {
54804c9d578SStephan Aßmus 		BAutolock locker(fModel.Lock());
54904c9d578SStephan Aßmus 		fPackageListView->AddPackage(fVisiblePackages.ItemAtFast(i));
55004c9d578SStephan Aßmus 	}
55104c9d578SStephan Aßmus 
55204c9d578SStephan Aßmus 	BAutolock locker(fModel.Lock());
553*fdec4227SStephan Aßmus 	fShowAvailablePackagesItem->SetMarked(fModel.ShowAvailablePackages());
554*fdec4227SStephan Aßmus 	fShowInstalledPackagesItem->SetMarked(fModel.ShowInstalledPackages());
555*fdec4227SStephan Aßmus 	fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages());
556*fdec4227SStephan Aßmus 	fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages());
55704c9d578SStephan Aßmus }
55804c9d578SStephan Aßmus 
55904c9d578SStephan Aßmus 
56004c9d578SStephan Aßmus void
56104c9d578SStephan Aßmus MainWindow::_AdoptPackage(const PackageInfoRef& package)
56204c9d578SStephan Aßmus {
56304c9d578SStephan Aßmus 	fPackageInfoView->SetPackage(package);
56404c9d578SStephan Aßmus 
56504c9d578SStephan Aßmus 	// Trigger asynchronous package population from the web-app
56604c9d578SStephan Aßmus 	{
56704c9d578SStephan Aßmus 		AutoLocker<BLocker> lock(&fPackageToPopulateLock);
56804c9d578SStephan Aßmus 		fPackageToPopulate = package;
56904c9d578SStephan Aßmus 	}
57004c9d578SStephan Aßmus 	release_sem_etc(fPackageToPopulateSem, 1, 0);
57104c9d578SStephan Aßmus }
57204c9d578SStephan Aßmus 
57304c9d578SStephan Aßmus 
57404c9d578SStephan Aßmus void
57504c9d578SStephan Aßmus MainWindow::_ClearPackage()
57604c9d578SStephan Aßmus {
57704c9d578SStephan Aßmus 	fPackageInfoView->Clear();
57804c9d578SStephan Aßmus }
57904c9d578SStephan Aßmus 
58004c9d578SStephan Aßmus 
58104c9d578SStephan Aßmus void
58204c9d578SStephan Aßmus MainWindow::_RefreshRepositories(bool force)
58304c9d578SStephan Aßmus {
584448f707cSStephan Aßmus 	if (fSinglePackageMode)
585448f707cSStephan Aßmus 		return;
586448f707cSStephan Aßmus 
58704c9d578SStephan Aßmus 	BPackageRoster roster;
58804c9d578SStephan Aßmus 	BStringList repositoryNames;
58904c9d578SStephan Aßmus 
59004c9d578SStephan Aßmus 	status_t result = roster.GetRepositoryNames(repositoryNames);
59104c9d578SStephan Aßmus 	if (result != B_OK)
59204c9d578SStephan Aßmus 		return;
59304c9d578SStephan Aßmus 
59404c9d578SStephan Aßmus 	DecisionProvider decisionProvider;
59504c9d578SStephan Aßmus 	JobStateListener listener;
59604c9d578SStephan Aßmus 	BContext context(decisionProvider, listener);
59704c9d578SStephan Aßmus 
59804c9d578SStephan Aßmus 	BRepositoryCache cache;
59904c9d578SStephan Aßmus 	for (int32 i = 0; i < repositoryNames.CountStrings(); ++i) {
60004c9d578SStephan Aßmus 		const BString& repoName = repositoryNames.StringAt(i);
60104c9d578SStephan Aßmus 		BRepositoryConfig repoConfig;
60204c9d578SStephan Aßmus 		result = roster.GetRepositoryConfig(repoName, &repoConfig);
60304c9d578SStephan Aßmus 		if (result != B_OK) {
60404c9d578SStephan Aßmus 			// TODO: notify user
60504c9d578SStephan Aßmus 			continue;
60604c9d578SStephan Aßmus 		}
60704c9d578SStephan Aßmus 
60804c9d578SStephan Aßmus 		if (roster.GetRepositoryCache(repoName, &cache) != B_OK || force) {
60904c9d578SStephan Aßmus 			try {
61004c9d578SStephan Aßmus 				BRefreshRepositoryRequest refreshRequest(context, repoConfig);
61104c9d578SStephan Aßmus 
61204c9d578SStephan Aßmus 				result = refreshRequest.Process();
61304c9d578SStephan Aßmus 			} catch (BFatalErrorException ex) {
61404c9d578SStephan Aßmus 				BString message(B_TRANSLATE("An error occurred while "
61504c9d578SStephan Aßmus 					"refreshing the repository: %error% (%details%)"));
61604c9d578SStephan Aßmus  				message.ReplaceFirst("%error%", ex.Message());
61704c9d578SStephan Aßmus 				message.ReplaceFirst("%details%", ex.Details());
61804c9d578SStephan Aßmus 				_NotifyUser("Error", message.String());
61904c9d578SStephan Aßmus 			} catch (BException ex) {
62004c9d578SStephan Aßmus 				BString message(B_TRANSLATE("An error occurred while "
62104c9d578SStephan Aßmus 					"refreshing the repository: %error%"));
62204c9d578SStephan Aßmus 				message.ReplaceFirst("%error%", ex.Message());
62304c9d578SStephan Aßmus 				_NotifyUser("Error", message.String());
62404c9d578SStephan Aßmus 			}
62504c9d578SStephan Aßmus 		}
62604c9d578SStephan Aßmus 	}
62704c9d578SStephan Aßmus }
62804c9d578SStephan Aßmus 
62904c9d578SStephan Aßmus 
63004c9d578SStephan Aßmus void
63104c9d578SStephan Aßmus MainWindow::_RefreshPackageList()
63204c9d578SStephan Aßmus {
633448f707cSStephan Aßmus 	if (fSinglePackageMode)
634448f707cSStephan Aßmus 		return;
635448f707cSStephan Aßmus 
63604c9d578SStephan Aßmus 	BPackageRoster roster;
63704c9d578SStephan Aßmus 	BStringList repositoryNames;
63804c9d578SStephan Aßmus 
63904c9d578SStephan Aßmus 	status_t result = roster.GetRepositoryNames(repositoryNames);
64004c9d578SStephan Aßmus 	if (result != B_OK)
64104c9d578SStephan Aßmus 		return;
64204c9d578SStephan Aßmus 
64304c9d578SStephan Aßmus 	DepotInfoMap depots;
64404c9d578SStephan Aßmus 	for (int32 i = 0; i < repositoryNames.CountStrings(); i++) {
64504c9d578SStephan Aßmus 		const BString& repoName = repositoryNames.StringAt(i);
64604c9d578SStephan Aßmus 		depots[repoName] = DepotInfo(repoName);
64704c9d578SStephan Aßmus 	}
64804c9d578SStephan Aßmus 
64904c9d578SStephan Aßmus 	PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
65004c9d578SStephan Aßmus 	try {
65104c9d578SStephan Aßmus 		manager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
65204c9d578SStephan Aßmus 			| PackageManager::B_ADD_REMOTE_REPOSITORIES);
65304c9d578SStephan Aßmus 	} catch (BException ex) {
65404c9d578SStephan Aßmus 		BString message(B_TRANSLATE("An error occurred while "
65504c9d578SStephan Aßmus 			"initializing the package manager: %message%"));
65604c9d578SStephan Aßmus 		message.ReplaceFirst("%message%", ex.Message());
65704c9d578SStephan Aßmus 		_NotifyUser("Error", message.String());
65804c9d578SStephan Aßmus 		return;
65904c9d578SStephan Aßmus 	}
66004c9d578SStephan Aßmus 
66104c9d578SStephan Aßmus 	BObjectList<BSolverPackage> packages;
66204c9d578SStephan Aßmus 	result = manager.Solver()->FindPackages("",
66304c9d578SStephan Aßmus 		BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
66404c9d578SStephan Aßmus 			| BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
66504c9d578SStephan Aßmus 			| BSolver::B_FIND_IN_PROVIDES,
66604c9d578SStephan Aßmus 		packages);
66704c9d578SStephan Aßmus 	if (result != B_OK) {
66804c9d578SStephan Aßmus 		BString message(B_TRANSLATE("An error occurred while "
66904c9d578SStephan Aßmus 			"obtaining the package list: %message%"));
67004c9d578SStephan Aßmus 		message.ReplaceFirst("%message%", strerror(result));
67104c9d578SStephan Aßmus 		_NotifyUser("Error", message.String());
67204c9d578SStephan Aßmus 		return;
67304c9d578SStephan Aßmus 	}
67404c9d578SStephan Aßmus 
67504c9d578SStephan Aßmus 	if (packages.IsEmpty())
67604c9d578SStephan Aßmus 		return;
67704c9d578SStephan Aßmus 
67804c9d578SStephan Aßmus 	PackageInfoMap foundPackages;
67904c9d578SStephan Aßmus 		// if a given package is installed locally, we will potentially
68004c9d578SStephan Aßmus 		// get back multiple entries, one for each local installation
68104c9d578SStephan Aßmus 		// location, and one for each remote repository the package
68204c9d578SStephan Aßmus 		// is available in. The above map is used to ensure that in such
68304c9d578SStephan Aßmus 		// cases we consolidate the information, rather than displaying
68404c9d578SStephan Aßmus 		// duplicates
68504c9d578SStephan Aßmus 	PackageInfoMap remotePackages;
68604c9d578SStephan Aßmus 		// any package that we find in a remote repository goes in this map.
68704c9d578SStephan Aßmus 		// this is later used to discern which packages came from a local
68804c9d578SStephan Aßmus 		// installation only, as those must be handled a bit differently
68904c9d578SStephan Aßmus 		// upon uninstallation, since we'd no longer be able to pull them
69004c9d578SStephan Aßmus 		// down remotely.
69104c9d578SStephan Aßmus 	BStringList systemFlaggedPackages;
69204c9d578SStephan Aßmus 		// any packages flagged as a system package are added to this list.
69304c9d578SStephan Aßmus 		// such packages cannot be uninstalled, nor can any of their deps.
69404c9d578SStephan Aßmus 	PackageInfoMap systemInstalledPackages;
69504c9d578SStephan Aßmus 		// any packages installed in system are added to this list.
69604c9d578SStephan Aßmus 		// This is later used for dependency resolution of the actual
69704c9d578SStephan Aßmus 		// system packages in order to compute the list of protected
69804c9d578SStephan Aßmus 		// dependencies indicated above.
69904c9d578SStephan Aßmus 
70004c9d578SStephan Aßmus 	for (int32 i = 0; i < packages.CountItems(); i++) {
70104c9d578SStephan Aßmus 		BSolverPackage* package = packages.ItemAt(i);
70204c9d578SStephan Aßmus 		const BPackageInfo& repoPackageInfo = package->Info();
70304c9d578SStephan Aßmus 		PackageInfoRef modelInfo;
70404c9d578SStephan Aßmus 		PackageInfoMap::iterator it = foundPackages.find(
70504c9d578SStephan Aßmus 			repoPackageInfo.Name());
70604c9d578SStephan Aßmus 		if (it != foundPackages.end())
70704c9d578SStephan Aßmus 			modelInfo.SetTo(it->second);
70804c9d578SStephan Aßmus 		else {
70904c9d578SStephan Aßmus 			// Add new package info
7106d519fd1SStephan Aßmus 			modelInfo.SetTo(new(std::nothrow) PackageInfo(repoPackageInfo),
71104c9d578SStephan Aßmus 				true);
71204c9d578SStephan Aßmus 
71304c9d578SStephan Aßmus 			if (modelInfo.Get() == NULL)
71404c9d578SStephan Aßmus 				return;
71504c9d578SStephan Aßmus 
71604c9d578SStephan Aßmus 			foundPackages[repoPackageInfo.Name()] = modelInfo;
71704c9d578SStephan Aßmus 		}
71804c9d578SStephan Aßmus 
71904c9d578SStephan Aßmus 		modelInfo->AddListener(this);
72004c9d578SStephan Aßmus 
72104c9d578SStephan Aßmus 		BSolverRepository* repository = package->Repository();
72204c9d578SStephan Aßmus 		if (dynamic_cast<BPackageManager::RemoteRepository*>(repository)
72304c9d578SStephan Aßmus 				!= NULL) {
72404c9d578SStephan Aßmus 			depots[repository->Name()].AddPackage(modelInfo);
72504c9d578SStephan Aßmus 			remotePackages[modelInfo->Title()] = modelInfo;
72604c9d578SStephan Aßmus 		} else {
72704c9d578SStephan Aßmus 			if (repository == static_cast<const BSolverRepository*>(
72804c9d578SStephan Aßmus 					manager.SystemRepository())) {
72904c9d578SStephan Aßmus 				modelInfo->AddInstallationLocation(
73004c9d578SStephan Aßmus 					B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
73104c9d578SStephan Aßmus 				if (!modelInfo->IsSystemPackage()) {
73204c9d578SStephan Aßmus 					systemInstalledPackages[repoPackageInfo.FileName()]
73304c9d578SStephan Aßmus 						= modelInfo;
73404c9d578SStephan Aßmus 				}
73504c9d578SStephan Aßmus 			} else if (repository == static_cast<const BSolverRepository*>(
73604c9d578SStephan Aßmus 					manager.HomeRepository())) {
73704c9d578SStephan Aßmus 				modelInfo->AddInstallationLocation(
73804c9d578SStephan Aßmus 					B_PACKAGE_INSTALLATION_LOCATION_HOME);
73904c9d578SStephan Aßmus 			}
74004c9d578SStephan Aßmus 		}
74104c9d578SStephan Aßmus 
74204c9d578SStephan Aßmus 		if (modelInfo->IsSystemPackage())
74304c9d578SStephan Aßmus 			systemFlaggedPackages.Add(repoPackageInfo.FileName());
74404c9d578SStephan Aßmus 	}
74504c9d578SStephan Aßmus 
74604c9d578SStephan Aßmus 	BAutolock lock(fModel.Lock());
74704c9d578SStephan Aßmus 
74804c9d578SStephan Aßmus 	fModel.Clear();
74904c9d578SStephan Aßmus 
75004c9d578SStephan Aßmus 	// filter remote packages from the found list
75104c9d578SStephan Aßmus 	// any packages remaining will be locally installed packages
75204c9d578SStephan Aßmus 	// that weren't acquired from a repository
75304c9d578SStephan Aßmus 	for (PackageInfoMap::iterator it = remotePackages.begin();
75404c9d578SStephan Aßmus 			it != remotePackages.end(); it++) {
75504c9d578SStephan Aßmus 		foundPackages.erase(it->first);
75604c9d578SStephan Aßmus 	}
75704c9d578SStephan Aßmus 
75804c9d578SStephan Aßmus 	if (!foundPackages.empty()) {
75904c9d578SStephan Aßmus 		BString repoName = B_TRANSLATE("Local");
76004c9d578SStephan Aßmus 		depots[repoName] = DepotInfo(repoName);
76104c9d578SStephan Aßmus 		DepotInfoMap::iterator depot = depots.find(repoName);
76204c9d578SStephan Aßmus 		for (PackageInfoMap::iterator it = foundPackages.begin();
76304c9d578SStephan Aßmus 				it != foundPackages.end(); ++it) {
76404c9d578SStephan Aßmus 			depot->second.AddPackage(it->second);
76504c9d578SStephan Aßmus 		}
76604c9d578SStephan Aßmus 	}
76704c9d578SStephan Aßmus 
76804c9d578SStephan Aßmus 	for (DepotInfoMap::iterator it = depots.begin(); it != depots.end(); it++) {
76904c9d578SStephan Aßmus 		fModel.AddDepot(it->second);
77004c9d578SStephan Aßmus 	}
77104c9d578SStephan Aßmus 
77204c9d578SStephan Aßmus 	// start retrieving package icons and average ratings
77304c9d578SStephan Aßmus 	fModel.PopulateAllPackages();
77404c9d578SStephan Aßmus 
77504c9d578SStephan Aßmus 	// compute the OS package dependencies
77604c9d578SStephan Aßmus 	try {
77704c9d578SStephan Aßmus 		// create the solver
77804c9d578SStephan Aßmus 		BSolver* solver;
77904c9d578SStephan Aßmus 		status_t error = BSolver::Create(solver);
78004c9d578SStephan Aßmus 		if (error != B_OK)
78104c9d578SStephan Aßmus 			throw BFatalErrorException(error, "Failed to create solver.");
78204c9d578SStephan Aßmus 
78304c9d578SStephan Aßmus 		ObjectDeleter<BSolver> solverDeleter(solver);
78404c9d578SStephan Aßmus 		BPath systemPath;
78504c9d578SStephan Aßmus 		error = find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &systemPath);
78604c9d578SStephan Aßmus 		if (error != B_OK) {
78704c9d578SStephan Aßmus 			throw BFatalErrorException(error,
78804c9d578SStephan Aßmus 				"Unable to retrieve system packages directory.");
78904c9d578SStephan Aßmus 		}
79004c9d578SStephan Aßmus 
79104c9d578SStephan Aßmus 		// add the "installed" repository with the given packages
79204c9d578SStephan Aßmus 		BSolverRepository installedRepository;
79304c9d578SStephan Aßmus 		{
79404c9d578SStephan Aßmus 			BRepositoryBuilder installedRepositoryBuilder(installedRepository,
79504c9d578SStephan Aßmus 				"installed");
79604c9d578SStephan Aßmus 			for (int32 i = 0; i < systemFlaggedPackages.CountStrings(); i++) {
79704c9d578SStephan Aßmus 				BPath packagePath(systemPath);
79804c9d578SStephan Aßmus 				packagePath.Append(systemFlaggedPackages.StringAt(i));
79904c9d578SStephan Aßmus 				installedRepositoryBuilder.AddPackage(packagePath.Path());
80004c9d578SStephan Aßmus 			}
80104c9d578SStephan Aßmus 			installedRepositoryBuilder.AddToSolver(solver, true);
80204c9d578SStephan Aßmus 		}
80304c9d578SStephan Aßmus 
80404c9d578SStephan Aßmus 		// add system repository
80504c9d578SStephan Aßmus 		BSolverRepository systemRepository;
80604c9d578SStephan Aßmus 		{
80704c9d578SStephan Aßmus 			BRepositoryBuilder systemRepositoryBuilder(systemRepository,
80804c9d578SStephan Aßmus 				"system");
80904c9d578SStephan Aßmus 			for (PackageInfoMap::iterator it = systemInstalledPackages.begin();
81004c9d578SStephan Aßmus 					it != systemInstalledPackages.end(); it++) {
81104c9d578SStephan Aßmus 				BPath packagePath(systemPath);
81204c9d578SStephan Aßmus 				packagePath.Append(it->first);
81304c9d578SStephan Aßmus 				systemRepositoryBuilder.AddPackage(packagePath.Path());
81404c9d578SStephan Aßmus 			}
81504c9d578SStephan Aßmus 			systemRepositoryBuilder.AddToSolver(solver, false);
81604c9d578SStephan Aßmus 		}
81704c9d578SStephan Aßmus 
81804c9d578SStephan Aßmus 		// solve
81904c9d578SStephan Aßmus 		error = solver->VerifyInstallation();
82004c9d578SStephan Aßmus 		if (error != B_OK) {
82104c9d578SStephan Aßmus 			throw BFatalErrorException(error, "Failed to compute packages to "
82204c9d578SStephan Aßmus 				"install.");
82304c9d578SStephan Aßmus 		}
82404c9d578SStephan Aßmus 
82504c9d578SStephan Aßmus 		BSolverResult solverResult;
82604c9d578SStephan Aßmus 		error = solver->GetResult(solverResult);
82704c9d578SStephan Aßmus 		if (error != B_OK) {
82804c9d578SStephan Aßmus 			throw BFatalErrorException(error, "Failed to retrieve system "
82904c9d578SStephan Aßmus 				"package dependency list.");
83004c9d578SStephan Aßmus 		}
83104c9d578SStephan Aßmus 
83204c9d578SStephan Aßmus 		for (int32 i = 0; const BSolverResultElement* element
83304c9d578SStephan Aßmus 				= solverResult.ElementAt(i); i++) {
83404c9d578SStephan Aßmus 			BSolverPackage* package = element->Package();
83504c9d578SStephan Aßmus 			if (element->Type() == BSolverResultElement::B_TYPE_INSTALL) {
83604c9d578SStephan Aßmus 				PackageInfoMap::iterator it = systemInstalledPackages.find(
83704c9d578SStephan Aßmus 					package->Info().FileName());
83804c9d578SStephan Aßmus 				if (it != systemInstalledPackages.end())
83904c9d578SStephan Aßmus 					it->second->SetSystemDependency(true);
84004c9d578SStephan Aßmus 			}
84104c9d578SStephan Aßmus 		}
84204c9d578SStephan Aßmus 	} catch (BFatalErrorException ex) {
84304c9d578SStephan Aßmus 		printf("Fatal exception occurred while resolving system dependencies: "
84404c9d578SStephan Aßmus 			"%s, details: %s\n", strerror(ex.Error()), ex.Details().String());
84504c9d578SStephan Aßmus 	} catch (BNothingToDoException) {
84604c9d578SStephan Aßmus 		// do nothing
84704c9d578SStephan Aßmus 	} catch (BException ex) {
84804c9d578SStephan Aßmus 		printf("Exception occurred while resolving system dependencies: %s\n",
84904c9d578SStephan Aßmus 			ex.Message().String());
85004c9d578SStephan Aßmus 	} catch (...) {
85104c9d578SStephan Aßmus 		printf("Unknown exception occurred while resolving system "
85204c9d578SStephan Aßmus 			"dependencies.\n");
85304c9d578SStephan Aßmus 	}
85404c9d578SStephan Aßmus }
85504c9d578SStephan Aßmus 
85604c9d578SStephan Aßmus 
85704c9d578SStephan Aßmus void
85804c9d578SStephan Aßmus MainWindow::_StartRefreshWorker(bool force)
85904c9d578SStephan Aßmus {
86004c9d578SStephan Aßmus 	if (fModelWorker != B_BAD_THREAD_ID)
86104c9d578SStephan Aßmus 		return;
86204c9d578SStephan Aßmus 
86304c9d578SStephan Aßmus 	RefreshWorkerParameters* parameters = new(std::nothrow)
86404c9d578SStephan Aßmus 		RefreshWorkerParameters(this, force);
86504c9d578SStephan Aßmus 	if (parameters == NULL)
86604c9d578SStephan Aßmus 		return;
86704c9d578SStephan Aßmus 
86804c9d578SStephan Aßmus 	ObjectDeleter<RefreshWorkerParameters> deleter(parameters);
86904c9d578SStephan Aßmus 	fModelWorker = spawn_thread(&_RefreshModelThreadWorker, "model loader",
87004c9d578SStephan Aßmus 		B_LOW_PRIORITY, parameters);
87104c9d578SStephan Aßmus 
87204c9d578SStephan Aßmus 	if (fModelWorker > 0) {
87304c9d578SStephan Aßmus 		deleter.Detach();
87404c9d578SStephan Aßmus 		resume_thread(fModelWorker);
87504c9d578SStephan Aßmus 	}
87604c9d578SStephan Aßmus }
87704c9d578SStephan Aßmus 
87804c9d578SStephan Aßmus 
87904c9d578SStephan Aßmus status_t
88004c9d578SStephan Aßmus MainWindow::_RefreshModelThreadWorker(void* arg)
88104c9d578SStephan Aßmus {
88204c9d578SStephan Aßmus 	RefreshWorkerParameters* parameters
88304c9d578SStephan Aßmus 		= reinterpret_cast<RefreshWorkerParameters*>(arg);
88404c9d578SStephan Aßmus 	MainWindow* mainWindow = parameters->window;
88504c9d578SStephan Aßmus 	ObjectDeleter<RefreshWorkerParameters> deleter(parameters);
88604c9d578SStephan Aßmus 
88704c9d578SStephan Aßmus 	BMessenger messenger(mainWindow);
88804c9d578SStephan Aßmus 
88904c9d578SStephan Aßmus 	mainWindow->_RefreshRepositories(parameters->forceRefresh);
89004c9d578SStephan Aßmus 
89104c9d578SStephan Aßmus 	if (mainWindow->fTerminating)
89204c9d578SStephan Aßmus 		return B_OK;
89304c9d578SStephan Aßmus 
89404c9d578SStephan Aßmus 	mainWindow->_RefreshPackageList();
89504c9d578SStephan Aßmus 
89604c9d578SStephan Aßmus 	messenger.SendMessage(MSG_MODEL_WORKER_DONE);
89704c9d578SStephan Aßmus 
89804c9d578SStephan Aßmus 	return B_OK;
89904c9d578SStephan Aßmus }
90004c9d578SStephan Aßmus 
90104c9d578SStephan Aßmus 
90204c9d578SStephan Aßmus status_t
90304c9d578SStephan Aßmus MainWindow::_PackageActionWorker(void* arg)
90404c9d578SStephan Aßmus {
90504c9d578SStephan Aßmus 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
90604c9d578SStephan Aßmus 
90704c9d578SStephan Aßmus 	while (acquire_sem(window->fPendingActionsSem) == B_OK) {
90804c9d578SStephan Aßmus 		PackageActionRef ref;
90904c9d578SStephan Aßmus 		{
91004c9d578SStephan Aßmus 			AutoLocker<BLocker> lock(&window->fPendingActionsLock);
91104c9d578SStephan Aßmus 			ref = window->fPendingActions.ItemAt(0);
91204c9d578SStephan Aßmus 			if (ref.Get() == NULL)
91304c9d578SStephan Aßmus 				break;
91404c9d578SStephan Aßmus 			window->fPendingActions.Remove(0);
91504c9d578SStephan Aßmus 		}
91604c9d578SStephan Aßmus 
91704c9d578SStephan Aßmus 		ref->Perform();
91804c9d578SStephan Aßmus 	}
91904c9d578SStephan Aßmus 
92004c9d578SStephan Aßmus 	return 0;
92104c9d578SStephan Aßmus }
92204c9d578SStephan Aßmus 
92304c9d578SStephan Aßmus 
92404c9d578SStephan Aßmus status_t
92504c9d578SStephan Aßmus MainWindow::_PopulatePackageWorker(void* arg)
92604c9d578SStephan Aßmus {
92704c9d578SStephan Aßmus 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
92804c9d578SStephan Aßmus 
92904c9d578SStephan Aßmus 	while (acquire_sem(window->fPackageToPopulateSem) == B_OK) {
93004c9d578SStephan Aßmus 		PackageInfoRef package;
93104c9d578SStephan Aßmus 		{
93204c9d578SStephan Aßmus 			AutoLocker<BLocker> lock(&window->fPackageToPopulateLock);
93304c9d578SStephan Aßmus 			package = window->fPackageToPopulate;
93404c9d578SStephan Aßmus 		}
93504c9d578SStephan Aßmus 
93604c9d578SStephan Aßmus 		if (package.Get() != NULL) {
93704c9d578SStephan Aßmus 			window->fModel.PopulatePackage(package,
93804c9d578SStephan Aßmus 				Model::POPULATE_USER_RATINGS | Model::POPULATE_SCREEN_SHOTS
93904c9d578SStephan Aßmus 					| Model::POPULATE_CHANGELOG);
94004c9d578SStephan Aßmus 		}
94104c9d578SStephan Aßmus 	}
94204c9d578SStephan Aßmus 
94304c9d578SStephan Aßmus 	return 0;
94404c9d578SStephan Aßmus }
94504c9d578SStephan Aßmus 
94604c9d578SStephan Aßmus 
94704c9d578SStephan Aßmus void
94804c9d578SStephan Aßmus MainWindow::_NotifyUser(const char* title, const char* message)
94904c9d578SStephan Aßmus {
95004c9d578SStephan Aßmus 	BAlert* alert = new(std::nothrow) BAlert(title, message,
95104c9d578SStephan Aßmus 		B_TRANSLATE("Close"));
95204c9d578SStephan Aßmus 
95304c9d578SStephan Aßmus 	if (alert != NULL)
95404c9d578SStephan Aßmus 		alert->Go();
95504c9d578SStephan Aßmus }
95604c9d578SStephan Aßmus 
95704c9d578SStephan Aßmus 
95804c9d578SStephan Aßmus void
9591bbda92dSStephan Aßmus MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage)
96004c9d578SStephan Aßmus {
96104c9d578SStephan Aßmus 	UserLoginWindow* window = new UserLoginWindow(this,
96204c9d578SStephan Aßmus 		BRect(0, 0, 500, 400), fModel);
9631bbda92dSStephan Aßmus 
9641bbda92dSStephan Aßmus 	if (onSuccessMessage.what != 0)
9651bbda92dSStephan Aßmus 		window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage);
9661bbda92dSStephan Aßmus 
96704c9d578SStephan Aßmus 	window->Show();
96804c9d578SStephan Aßmus }
96904c9d578SStephan Aßmus 
9708f03a0f9SStephan Aßmus 
9718f03a0f9SStephan Aßmus void
9728f03a0f9SStephan Aßmus MainWindow::_UpdateAuthorization()
9738f03a0f9SStephan Aßmus {
9748f03a0f9SStephan Aßmus 	BString username(fModel.Username());
975e0a26dceSStephan Aßmus 	bool hasUser = !username.IsEmpty();
976e0a26dceSStephan Aßmus 
9776d519fd1SStephan Aßmus 	if (fLogOutItem != NULL)
978e0a26dceSStephan Aßmus 		fLogOutItem->SetEnabled(hasUser);
979e0a26dceSStephan Aßmus 	if (fLogInItem != NULL) {
980e0a26dceSStephan Aßmus 		if (hasUser)
981e0a26dceSStephan Aßmus 			fLogInItem->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS));
982e0a26dceSStephan Aßmus 		else
983e0a26dceSStephan Aßmus 			fLogInItem->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS));
984e0a26dceSStephan Aßmus 	}
985e0a26dceSStephan Aßmus 
986e0a26dceSStephan Aßmus 	if (fUserMenu != NULL) {
987e0a26dceSStephan Aßmus 		BString label;
988e0a26dceSStephan Aßmus 		if (username.Length() == 0) {
989e0a26dceSStephan Aßmus 			label = B_TRANSLATE("Not logged in");
990e0a26dceSStephan Aßmus 		} else {
991e0a26dceSStephan Aßmus 			label = B_TRANSLATE("Logged in as %User%");
992e0a26dceSStephan Aßmus 			label.ReplaceAll("%User%", username);
993e0a26dceSStephan Aßmus 		}
994e0a26dceSStephan Aßmus 		fUserMenu->Superitem()->SetLabel(label);
995e0a26dceSStephan Aßmus 	}
9968f03a0f9SStephan Aßmus }
9971bbda92dSStephan Aßmus 
9981bbda92dSStephan Aßmus 
9991bbda92dSStephan Aßmus void
10001bbda92dSStephan Aßmus MainWindow::_RatePackage()
10011bbda92dSStephan Aßmus {
10021bbda92dSStephan Aßmus 	if (fModel.Username().IsEmpty()) {
10031bbda92dSStephan Aßmus 		BAlert* alert = new(std::nothrow) BAlert(
10041bbda92dSStephan Aßmus 			B_TRANSLATE("Not logged in"),
10051bbda92dSStephan Aßmus 			B_TRANSLATE("You need to be logged into an account before you "
10061bbda92dSStephan Aßmus 				"can rate packages."),
10071bbda92dSStephan Aßmus 			B_TRANSLATE("Cancel"),
10081bbda92dSStephan Aßmus 			B_TRANSLATE("Login or Create account"));
10091bbda92dSStephan Aßmus 
10101bbda92dSStephan Aßmus 		if (alert == NULL)
10111bbda92dSStephan Aßmus 			return;
10121bbda92dSStephan Aßmus 
10131bbda92dSStephan Aßmus 		int32 choice = alert->Go();
10141bbda92dSStephan Aßmus 		if (choice == 1)
10151bbda92dSStephan Aßmus 			_OpenLoginWindow(BMessage(MSG_RATE_PACKAGE));
10161bbda92dSStephan Aßmus 		return;
10171bbda92dSStephan Aßmus 	}
10181bbda92dSStephan Aßmus 
10191bbda92dSStephan Aßmus 	// TODO: Allow only one RatePackageWindow
10201bbda92dSStephan Aßmus 	// TODO: Mechanism for remembering the window frame
10211bbda92dSStephan Aßmus 	RatePackageWindow* window = new RatePackageWindow(this,
10221bbda92dSStephan Aßmus 		BRect(0, 0, 500, 400), fModel);
10231bbda92dSStephan Aßmus 	window->SetPackage(fPackageInfoView->Package());
10241bbda92dSStephan Aßmus 	window->Show();
10251bbda92dSStephan Aßmus }
1026