xref: /haiku/src/apps/haikudepot/ui/MainWindow.cpp (revision 89b16bb4d245c3a6e55e0d8f3502324e6f31bcb4)
104c9d578SStephan Aßmus /*
2f7c2983eSAxel Dörfler  * Copyright 2015, Axel Dörfler, <axeld@pinc-software.de>.
304c9d578SStephan Aßmus  * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
404c9d578SStephan Aßmus  * Copyright 2013, Rene Gollent, rene@gollent.com.
504c9d578SStephan Aßmus  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
6*89b16bb4SAndrew Lindesay  * Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
7125d42d9SJulian Harnath  * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
804c9d578SStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
904c9d578SStephan Aßmus  */
1004c9d578SStephan Aßmus 
11f7c2983eSAxel Dörfler 
1204c9d578SStephan Aßmus #include "MainWindow.h"
1304c9d578SStephan Aßmus 
1404c9d578SStephan Aßmus #include <map>
153b17d8ddSAndrew Lindesay #include <vector>
1604c9d578SStephan Aßmus 
1704c9d578SStephan Aßmus #include <stdio.h>
1804c9d578SStephan Aßmus #include <Alert.h>
1904c9d578SStephan Aßmus #include <Autolock.h>
2004c9d578SStephan Aßmus #include <Application.h>
2104c9d578SStephan Aßmus #include <Button.h>
2204c9d578SStephan Aßmus #include <Catalog.h>
234b930ccaSStephan Aßmus #include <CardLayout.h>
2404c9d578SStephan Aßmus #include <LayoutBuilder.h>
2504c9d578SStephan Aßmus #include <MenuBar.h>
2604c9d578SStephan Aßmus #include <MenuItem.h>
2704c9d578SStephan Aßmus #include <Messenger.h>
285bf2b6ebSBrian Hill #include <Roster.h>
29f7c2983eSAxel Dörfler #include <Screen.h>
3004c9d578SStephan Aßmus #include <ScrollView.h>
3104c9d578SStephan Aßmus #include <StringList.h>
320a9c9029SStephan Aßmus #include <StringView.h>
3304c9d578SStephan Aßmus #include <TabView.h>
3404c9d578SStephan Aßmus 
353369e03dSAndrew Lindesay #include "AppUtils.h"
3604c9d578SStephan Aßmus #include "AutoDeleter.h"
3704c9d578SStephan Aßmus #include "AutoLocker.h"
3804c9d578SStephan Aßmus #include "DecisionProvider.h"
394b930ccaSStephan Aßmus #include "FeaturedPackagesView.h"
4004c9d578SStephan Aßmus #include "FilterView.h"
41d13c3d89SAndrew Lindesay #include "Logger.h"
4204c9d578SStephan Aßmus #include "PackageInfoView.h"
4304c9d578SStephan Aßmus #include "PackageListView.h"
4404c9d578SStephan Aßmus #include "PackageManager.h"
453369e03dSAndrew Lindesay #include "ProcessCoordinator.h"
463369e03dSAndrew Lindesay #include "ProcessCoordinatorFactory.h"
4704c9d578SStephan Aßmus #include "RatePackageWindow.h"
48e0a26dceSStephan Aßmus #include "support.h"
49d5ef985eSStephan Aßmus #include "ScreenshotWindow.h"
5004c9d578SStephan Aßmus #include "UserLoginWindow.h"
51125d42d9SJulian Harnath #include "WorkStatusView.h"
5204c9d578SStephan Aßmus 
5304c9d578SStephan Aßmus 
5404c9d578SStephan Aßmus #undef B_TRANSLATION_CONTEXT
5504c9d578SStephan Aßmus #define B_TRANSLATION_CONTEXT "MainWindow"
5604c9d578SStephan Aßmus 
5704c9d578SStephan Aßmus 
5804c9d578SStephan Aßmus enum {
593369e03dSAndrew Lindesay 	MSG_BULK_LOAD_DONE		= 'mmwd',
605bf2b6ebSBrian Hill 	MSG_REFRESH_REPOS			= 'mrrp',
615bf2b6ebSBrian Hill 	MSG_MANAGE_REPOS			= 'mmrp',
62d144f952SAugustin Cavalier 	MSG_SOFTWARE_UPDATER		= 'mswu',
6304c9d578SStephan Aßmus 	MSG_LOG_IN					= 'lgin',
648f03a0f9SStephan Aßmus 	MSG_LOG_OUT					= 'lgot',
658f03a0f9SStephan Aßmus 	MSG_AUTHORIZATION_CHANGED	= 'athc',
66*89b16bb4SAndrew Lindesay 	MSG_CATEGORIES_LIST_CHANGED	= 'clic',
67da651104SStephan Aßmus 	MSG_PACKAGE_CHANGED			= 'pchd',
683369e03dSAndrew Lindesay 	MSG_WORK_STATUS_CHANGE		= 'wsch',
693369e03dSAndrew Lindesay 	MSG_WORK_STATUS_CLEAR		= 'wscl',
70fdec4227SStephan Aßmus 
71e00a489bSAugustin Cavalier 	MSG_SHOW_FEATURED_PACKAGES	= 'sofp',
72fdec4227SStephan Aßmus 	MSG_SHOW_AVAILABLE_PACKAGES	= 'savl',
73fdec4227SStephan Aßmus 	MSG_SHOW_INSTALLED_PACKAGES	= 'sins',
7404c9d578SStephan Aßmus 	MSG_SHOW_SOURCE_PACKAGES	= 'ssrc',
7504c9d578SStephan Aßmus 	MSG_SHOW_DEVELOP_PACKAGES	= 'sdvl'
7604c9d578SStephan Aßmus };
7704c9d578SStephan Aßmus 
7804c9d578SStephan Aßmus 
7904c9d578SStephan Aßmus using namespace BPackageKit;
8004c9d578SStephan Aßmus using namespace BPackageKit::BManager::BPrivate;
8104c9d578SStephan Aßmus 
8204c9d578SStephan Aßmus 
8304c9d578SStephan Aßmus typedef std::map<BString, PackageInfoRef> PackageInfoMap;
8404c9d578SStephan Aßmus 
8504c9d578SStephan Aßmus 
8604c9d578SStephan Aßmus struct RefreshWorkerParameters {
8704c9d578SStephan Aßmus 	MainWindow* window;
8804c9d578SStephan Aßmus 	bool forceRefresh;
8904c9d578SStephan Aßmus 
9004c9d578SStephan Aßmus 	RefreshWorkerParameters(MainWindow* window, bool forceRefresh)
9104c9d578SStephan Aßmus 		:
9204c9d578SStephan Aßmus 		window(window),
9304c9d578SStephan Aßmus 		forceRefresh(forceRefresh)
9404c9d578SStephan Aßmus 	{
9504c9d578SStephan Aßmus 	}
9604c9d578SStephan Aßmus };
9704c9d578SStephan Aßmus 
9804c9d578SStephan Aßmus 
99*89b16bb4SAndrew Lindesay class MainWindowModelListener : public ModelListener {
1008f03a0f9SStephan Aßmus public:
101*89b16bb4SAndrew Lindesay 	MainWindowModelListener(const BMessenger& messenger)
1028f03a0f9SStephan Aßmus 		:
1038f03a0f9SStephan Aßmus 		fMessenger(messenger)
1048f03a0f9SStephan Aßmus 	{
1058f03a0f9SStephan Aßmus 	}
1068f03a0f9SStephan Aßmus 
1078f03a0f9SStephan Aßmus 	virtual void AuthorizationChanged()
1088f03a0f9SStephan Aßmus 	{
1098f03a0f9SStephan Aßmus 		if (fMessenger.IsValid())
1108f03a0f9SStephan Aßmus 			fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED);
1118f03a0f9SStephan Aßmus 	}
1128f03a0f9SStephan Aßmus 
113*89b16bb4SAndrew Lindesay 	virtual void CategoryListChanged()
114*89b16bb4SAndrew Lindesay 	{
115*89b16bb4SAndrew Lindesay 		if (fMessenger.IsValid())
116*89b16bb4SAndrew Lindesay 			fMessenger.SendMessage(MSG_CATEGORIES_LIST_CHANGED);
117*89b16bb4SAndrew Lindesay 	}
118*89b16bb4SAndrew Lindesay 
1198f03a0f9SStephan Aßmus private:
1208f03a0f9SStephan Aßmus 	BMessenger	fMessenger;
1218f03a0f9SStephan Aßmus };
1228f03a0f9SStephan Aßmus 
1238f03a0f9SStephan Aßmus 
124f7c2983eSAxel Dörfler MainWindow::MainWindow(const BMessage& settings)
12504c9d578SStephan Aßmus 	:
126f7c2983eSAxel Dörfler 	BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
12704c9d578SStephan Aßmus 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
12804c9d578SStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
129d5ef985eSStephan Aßmus 	fScreenshotWindow(NULL),
130e0a26dceSStephan Aßmus 	fUserMenu(NULL),
131e0a26dceSStephan Aßmus 	fLogInItem(NULL),
132e0a26dceSStephan Aßmus 	fLogOutItem(NULL),
133*89b16bb4SAndrew Lindesay 	fModelListener(new MainWindowModelListener(BMessenger(this)), true),
1343369e03dSAndrew Lindesay 	fBulkLoadProcessCoordinator(NULL),
1353369e03dSAndrew Lindesay 	fSinglePackageMode(false)
13604c9d578SStephan Aßmus {
137cbadcae1SHumdinger 	BMenuBar* menuBar = new BMenuBar("Main Menu");
13804c9d578SStephan Aßmus 	_BuildMenu(menuBar);
13904c9d578SStephan Aßmus 
140cbadcae1SHumdinger 	BMenuBar* userMenuBar = new BMenuBar("User Menu");
141e0a26dceSStephan Aßmus 	_BuildUserMenu(userMenuBar);
142e0a26dceSStephan Aßmus 	set_small_font(userMenuBar);
143e0a26dceSStephan Aßmus 	userMenuBar->SetExplicitMaxSize(BSize(B_SIZE_UNSET,
144e0a26dceSStephan Aßmus 		menuBar->MaxSize().height));
145e0a26dceSStephan Aßmus 
14604c9d578SStephan Aßmus 	fFilterView = new FilterView();
1474b930ccaSStephan Aßmus 	fFeaturedPackagesView = new FeaturedPackagesView();
14804c9d578SStephan Aßmus 	fPackageListView = new PackageListView(fModel.Lock());
14904c9d578SStephan Aßmus 	fPackageInfoView = new PackageInfoView(fModel.Lock(), this);
15004c9d578SStephan Aßmus 
15104c9d578SStephan Aßmus 	fSplitView = new BSplitView(B_VERTICAL, 5.0f);
15204c9d578SStephan Aßmus 
153125d42d9SJulian Harnath 	fWorkStatusView = new WorkStatusView("work status");
154125d42d9SJulian Harnath 	fPackageListView->AttachWorkStatusView(fWorkStatusView);
155125d42d9SJulian Harnath 
156e00a489bSAugustin Cavalier 	fListTabs = new TabView(BMessenger(this),
157e00a489bSAugustin Cavalier 		BMessage(MSG_SHOW_FEATURED_PACKAGES), "list tabs");
158e00a489bSAugustin Cavalier 	fListTabs->AddTab(fFeaturedPackagesView);
159e00a489bSAugustin Cavalier 	fListTabs->AddTab(fPackageListView);
1604b930ccaSStephan Aßmus 
16104c9d578SStephan Aßmus 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
162e0a26dceSStephan Aßmus 		.AddGroup(B_HORIZONTAL, 0.0f)
163e0a26dceSStephan Aßmus 			.Add(menuBar, 1.0f)
164e0a26dceSStephan Aßmus 			.Add(userMenuBar, 0.0f)
165e0a26dceSStephan Aßmus 		.End()
16604c9d578SStephan Aßmus 		.Add(fFilterView)
16704c9d578SStephan Aßmus 		.AddSplit(fSplitView)
16804c9d578SStephan Aßmus 			.AddGroup(B_VERTICAL)
169e00a489bSAugustin Cavalier 				.Add(fListTabs)
17004c9d578SStephan Aßmus 				.SetInsets(
17104c9d578SStephan Aßmus 					B_USE_DEFAULT_SPACING, 0.0f,
17204c9d578SStephan Aßmus 					B_USE_DEFAULT_SPACING, 0.0f)
17304c9d578SStephan Aßmus 			.End()
17404c9d578SStephan Aßmus 			.Add(fPackageInfoView)
17504c9d578SStephan Aßmus 		.End()
176125d42d9SJulian Harnath 		.Add(fWorkStatusView)
17704c9d578SStephan Aßmus 	;
17804c9d578SStephan Aßmus 
17904c9d578SStephan Aßmus 	fSplitView->SetCollapsible(0, false);
18004c9d578SStephan Aßmus 	fSplitView->SetCollapsible(1, false);
18104c9d578SStephan Aßmus 
1828f03a0f9SStephan Aßmus 	fModel.AddListener(fModelListener);
1838f03a0f9SStephan Aßmus 
18404c9d578SStephan Aßmus 	// Restore settings
18504c9d578SStephan Aßmus 	BMessage columnSettings;
18604c9d578SStephan Aßmus 	if (settings.FindMessage("column settings", &columnSettings) == B_OK)
18704c9d578SStephan Aßmus 		fPackageListView->LoadState(&columnSettings);
18804c9d578SStephan Aßmus 
18904c9d578SStephan Aßmus 	bool showOption;
1902997e05bSStephan Aßmus 	if (settings.FindBool("show featured packages", &showOption) == B_OK)
1912997e05bSStephan Aßmus 		fModel.SetShowFeaturedPackages(showOption);
1922997e05bSStephan Aßmus 	if (settings.FindBool("show available packages", &showOption) == B_OK)
1932997e05bSStephan Aßmus 		fModel.SetShowAvailablePackages(showOption);
1942997e05bSStephan Aßmus 	if (settings.FindBool("show installed packages", &showOption) == B_OK)
1952997e05bSStephan Aßmus 		fModel.SetShowInstalledPackages(showOption);
19604c9d578SStephan Aßmus 	if (settings.FindBool("show develop packages", &showOption) == B_OK)
19704c9d578SStephan Aßmus 		fModel.SetShowDevelopPackages(showOption);
19804c9d578SStephan Aßmus 	if (settings.FindBool("show source packages", &showOption) == B_OK)
19904c9d578SStephan Aßmus 		fModel.SetShowSourcePackages(showOption);
20004c9d578SStephan Aßmus 
2012997e05bSStephan Aßmus 	if (fModel.ShowFeaturedPackages())
202e00a489bSAugustin Cavalier 		fListTabs->Select(0);
2032997e05bSStephan Aßmus 	else
204e00a489bSAugustin Cavalier 		fListTabs->Select(1);
2052997e05bSStephan Aßmus 
206f7c2983eSAxel Dörfler 	_RestoreUserName(settings);
207f7c2983eSAxel Dörfler 	_RestoreWindowFrame(settings);
208f7c2983eSAxel Dörfler 
20972fff6d3SJulian Harnath 	atomic_set(&fPackagesToShowListID, 0);
21072fff6d3SJulian Harnath 
2118f03a0f9SStephan Aßmus 	// start worker threads
21204c9d578SStephan Aßmus 	BPackageRoster().StartWatching(this,
21304c9d578SStephan Aßmus 		B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
21404c9d578SStephan Aßmus 
2153369e03dSAndrew Lindesay 	_StartBulkLoad();
21604c9d578SStephan Aßmus 
21763975654SStephan Aßmus 	_InitWorkerThreads();
21804c9d578SStephan Aßmus }
21904c9d578SStephan Aßmus 
22004c9d578SStephan Aßmus 
221f7c2983eSAxel Dörfler MainWindow::MainWindow(const BMessage& settings, const PackageInfoRef& package)
2226d519fd1SStephan Aßmus 	:
223f7c2983eSAxel Dörfler 	BWindow(BRect(50, 50, 650, 350), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
2246d519fd1SStephan Aßmus 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
2256d519fd1SStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
226d78bfd8dSJulian Harnath 	fWorkStatusView(NULL),
227d5ef985eSStephan Aßmus 	fScreenshotWindow(NULL),
228e0a26dceSStephan Aßmus 	fUserMenu(NULL),
229e0a26dceSStephan Aßmus 	fLogInItem(NULL),
2306d519fd1SStephan Aßmus 	fLogOutItem(NULL),
231*89b16bb4SAndrew Lindesay 	fModelListener(new MainWindowModelListener(BMessenger(this)), true),
2323369e03dSAndrew Lindesay 	fBulkLoadProcessCoordinator(NULL),
2333369e03dSAndrew Lindesay 	fSinglePackageMode(true)
2346d519fd1SStephan Aßmus {
2356d519fd1SStephan Aßmus 	fFilterView = new FilterView();
2366d519fd1SStephan Aßmus 	fPackageListView = new PackageListView(fModel.Lock());
2376d519fd1SStephan Aßmus 	fPackageInfoView = new PackageInfoView(fModel.Lock(), this);
2386d519fd1SStephan Aßmus 
2396d519fd1SStephan Aßmus 	BLayoutBuilder::Group<>(this, B_VERTICAL)
2406d519fd1SStephan Aßmus 		.Add(fPackageInfoView)
2416d519fd1SStephan Aßmus 		.SetInsets(0, B_USE_WINDOW_INSETS, 0, 0)
2426d519fd1SStephan Aßmus 	;
2436d519fd1SStephan Aßmus 
2446d519fd1SStephan Aßmus 	fModel.AddListener(fModelListener);
2456d519fd1SStephan Aßmus 
2466d519fd1SStephan Aßmus 	// Restore settings
247f7c2983eSAxel Dörfler 	_RestoreUserName(settings);
248f7c2983eSAxel Dörfler 	_RestoreWindowFrame(settings);
2496d519fd1SStephan Aßmus 
2506d519fd1SStephan Aßmus 	fPackageInfoView->SetPackage(package);
25163975654SStephan Aßmus 
25263975654SStephan Aßmus 	_InitWorkerThreads();
2536d519fd1SStephan Aßmus }
2546d519fd1SStephan Aßmus 
2556d519fd1SStephan Aßmus 
25604c9d578SStephan Aßmus MainWindow::~MainWindow()
25704c9d578SStephan Aßmus {
25804c9d578SStephan Aßmus 	BPackageRoster().StopWatching(this);
25904c9d578SStephan Aßmus 
26004c9d578SStephan Aßmus 	delete_sem(fPendingActionsSem);
26105bffa62SStephan Aßmus 	if (fPendingActionsWorker >= 0)
26204c9d578SStephan Aßmus 		wait_for_thread(fPendingActionsWorker, NULL);
26304c9d578SStephan Aßmus 
26404c9d578SStephan Aßmus 	delete_sem(fPackageToPopulateSem);
26505bffa62SStephan Aßmus 	if (fPopulatePackageWorker >= 0)
26604c9d578SStephan Aßmus 		wait_for_thread(fPopulatePackageWorker, NULL);
267d5ef985eSStephan Aßmus 
26872fff6d3SJulian Harnath 	delete_sem(fNewPackagesToShowSem);
26972fff6d3SJulian Harnath 	delete_sem(fShowPackagesAcknowledgeSem);
27072fff6d3SJulian Harnath 	if (fShowPackagesWorker >= 0)
27172fff6d3SJulian Harnath 		wait_for_thread(fShowPackagesWorker, NULL);
27272fff6d3SJulian Harnath 
273d5ef985eSStephan Aßmus 	if (fScreenshotWindow != NULL && fScreenshotWindow->Lock())
274d5ef985eSStephan Aßmus 		fScreenshotWindow->Quit();
27504c9d578SStephan Aßmus }
27604c9d578SStephan Aßmus 
27704c9d578SStephan Aßmus 
27804c9d578SStephan Aßmus bool
27904c9d578SStephan Aßmus MainWindow::QuitRequested()
28004c9d578SStephan Aßmus {
28104c9d578SStephan Aßmus 	BMessage settings;
28204c9d578SStephan Aßmus 	StoreSettings(settings);
28304c9d578SStephan Aßmus 
28404c9d578SStephan Aßmus 	BMessage message(MSG_MAIN_WINDOW_CLOSED);
2853369e03dSAndrew Lindesay 	message.AddMessage(KEY_WINDOW_SETTINGS, &settings);
28604c9d578SStephan Aßmus 
28704c9d578SStephan Aßmus 	be_app->PostMessage(&message);
28804c9d578SStephan Aßmus 
2893369e03dSAndrew Lindesay 	_StopBulkLoad();
2903369e03dSAndrew Lindesay 
29104c9d578SStephan Aßmus 	return true;
29204c9d578SStephan Aßmus }
29304c9d578SStephan Aßmus 
29404c9d578SStephan Aßmus 
29504c9d578SStephan Aßmus void
29604c9d578SStephan Aßmus MainWindow::MessageReceived(BMessage* message)
29704c9d578SStephan Aßmus {
29804c9d578SStephan Aßmus 	switch (message->what) {
2993369e03dSAndrew Lindesay 		case MSG_BULK_LOAD_DONE:
3003369e03dSAndrew Lindesay 			_BulkLoadCompleteReceived();
30104c9d578SStephan Aßmus 			break;
30204c9d578SStephan Aßmus 		case B_SIMPLE_DATA:
30304c9d578SStephan Aßmus 		case B_REFS_RECEIVED:
30404c9d578SStephan Aßmus 			// TODO: ?
30504c9d578SStephan Aßmus 			break;
30604c9d578SStephan Aßmus 
30704c9d578SStephan Aßmus 		case B_PACKAGE_UPDATE:
30804c9d578SStephan Aßmus 			// TODO: We should do a more selective update depending on the
30904c9d578SStephan Aßmus 			// "event", "location", and "change count" fields!
3103369e03dSAndrew Lindesay 			_StartBulkLoad(false);
31104c9d578SStephan Aßmus 			break;
31204c9d578SStephan Aßmus 
3135bf2b6ebSBrian Hill 		case MSG_REFRESH_REPOS:
3143369e03dSAndrew Lindesay 			_StartBulkLoad(true);
3153369e03dSAndrew Lindesay 			break;
3163369e03dSAndrew Lindesay 
3173369e03dSAndrew Lindesay 		case MSG_WORK_STATUS_CHANGE:
3183369e03dSAndrew Lindesay 			_HandleWorkStatusChangeMessageReceived(message);
31904c9d578SStephan Aßmus 			break;
32004c9d578SStephan Aßmus 
3215bf2b6ebSBrian Hill 		case MSG_MANAGE_REPOS:
3225bf2b6ebSBrian Hill 			be_roster->Launch("application/x-vnd.Haiku-Repositories");
3235bf2b6ebSBrian Hill 			break;
3245bf2b6ebSBrian Hill 
325d144f952SAugustin Cavalier 		case MSG_SOFTWARE_UPDATER:
326d144f952SAugustin Cavalier 			be_roster->Launch("application/x-vnd.haiku-softwareupdater");
327d144f952SAugustin Cavalier 			break;
328d144f952SAugustin Cavalier 
32904c9d578SStephan Aßmus 		case MSG_LOG_IN:
3301bbda92dSStephan Aßmus 			_OpenLoginWindow(BMessage());
33104c9d578SStephan Aßmus 			break;
33204c9d578SStephan Aßmus 
3338f03a0f9SStephan Aßmus 		case MSG_LOG_OUT:
3348f03a0f9SStephan Aßmus 			fModel.SetUsername("");
3358f03a0f9SStephan Aßmus 			break;
3368f03a0f9SStephan Aßmus 
3378f03a0f9SStephan Aßmus 		case MSG_AUTHORIZATION_CHANGED:
3388f03a0f9SStephan Aßmus 			_UpdateAuthorization();
3398f03a0f9SStephan Aßmus 			break;
3408f03a0f9SStephan Aßmus 
341*89b16bb4SAndrew Lindesay 		case MSG_CATEGORIES_LIST_CHANGED:
342*89b16bb4SAndrew Lindesay 			fFilterView->AdoptModel(fModel);
343*89b16bb4SAndrew Lindesay 			break;
344*89b16bb4SAndrew Lindesay 
3454fade61fSStephan Aßmus 		case MSG_SHOW_FEATURED_PACKAGES:
346e00a489bSAugustin Cavalier 			// check to see if we aren't already on the current tab
347e00a489bSAugustin Cavalier 			if (fListTabs->Selection() ==
348e00a489bSAugustin Cavalier 					(fModel.ShowFeaturedPackages() ? 0 : 1))
349e00a489bSAugustin Cavalier 				break;
3504fade61fSStephan Aßmus 			{
3514fade61fSStephan Aßmus 				BAutolock locker(fModel.Lock());
3524fade61fSStephan Aßmus 				fModel.SetShowFeaturedPackages(
353e00a489bSAugustin Cavalier 					fListTabs->Selection() == 0);
3544fade61fSStephan Aßmus 			}
3554fade61fSStephan Aßmus 			_AdoptModel();
3564fade61fSStephan Aßmus 			break;
3574fade61fSStephan Aßmus 
358fdec4227SStephan Aßmus 		case MSG_SHOW_AVAILABLE_PACKAGES:
359fdec4227SStephan Aßmus 			{
360fdec4227SStephan Aßmus 				BAutolock locker(fModel.Lock());
361fdec4227SStephan Aßmus 				fModel.SetShowAvailablePackages(
362fdec4227SStephan Aßmus 					!fModel.ShowAvailablePackages());
363fdec4227SStephan Aßmus 			}
364fdec4227SStephan Aßmus 			_AdoptModel();
365fdec4227SStephan Aßmus 			break;
366fdec4227SStephan Aßmus 
367fdec4227SStephan Aßmus 		case MSG_SHOW_INSTALLED_PACKAGES:
368fdec4227SStephan Aßmus 			{
369fdec4227SStephan Aßmus 				BAutolock locker(fModel.Lock());
370fdec4227SStephan Aßmus 				fModel.SetShowInstalledPackages(
371fdec4227SStephan Aßmus 					!fModel.ShowInstalledPackages());
372fdec4227SStephan Aßmus 			}
373fdec4227SStephan Aßmus 			_AdoptModel();
374fdec4227SStephan Aßmus 			break;
375fdec4227SStephan Aßmus 
37604c9d578SStephan Aßmus 		case MSG_SHOW_SOURCE_PACKAGES:
37704c9d578SStephan Aßmus 			{
37804c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
37904c9d578SStephan Aßmus 				fModel.SetShowSourcePackages(!fModel.ShowSourcePackages());
38004c9d578SStephan Aßmus 			}
38104c9d578SStephan Aßmus 			_AdoptModel();
38204c9d578SStephan Aßmus 			break;
38304c9d578SStephan Aßmus 
38404c9d578SStephan Aßmus 		case MSG_SHOW_DEVELOP_PACKAGES:
38504c9d578SStephan Aßmus 			{
38604c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
38704c9d578SStephan Aßmus 				fModel.SetShowDevelopPackages(!fModel.ShowDevelopPackages());
38804c9d578SStephan Aßmus 			}
38904c9d578SStephan Aßmus 			_AdoptModel();
39004c9d578SStephan Aßmus 			break;
39104c9d578SStephan Aßmus 
392a9edb9bfSAndrew Lindesay 			// this may be triggered by, for example, a user rating being added
393a9edb9bfSAndrew Lindesay 			// or having been altered.
394a9edb9bfSAndrew Lindesay 		case MSG_SERVER_DATA_CHANGED:
395a9edb9bfSAndrew Lindesay 		{
396a9edb9bfSAndrew Lindesay 			BString name;
397a9edb9bfSAndrew Lindesay 			if (message->FindString("name", &name) == B_OK) {
398a9edb9bfSAndrew Lindesay 				BAutolock locker(fModel.Lock());
399a9edb9bfSAndrew Lindesay 				if (fPackageInfoView->Package()->Name() == name) {
400a9edb9bfSAndrew Lindesay 					_PopulatePackageAsync(true);
401a9edb9bfSAndrew Lindesay 				} else {
402a9edb9bfSAndrew Lindesay 					if (Logger::IsDebugEnabled()) {
403a9edb9bfSAndrew Lindesay 						printf("pkg [%s] is updated on the server, but is "
404a9edb9bfSAndrew Lindesay 							"not selected so will not be updated.\n",
405a9edb9bfSAndrew Lindesay 							name.String());
406a9edb9bfSAndrew Lindesay 					}
407a9edb9bfSAndrew Lindesay 				}
408a9edb9bfSAndrew Lindesay 			}
409a9edb9bfSAndrew Lindesay         	break;
410a9edb9bfSAndrew Lindesay         }
411a9edb9bfSAndrew Lindesay 
41204c9d578SStephan Aßmus 		case MSG_PACKAGE_SELECTED:
41304c9d578SStephan Aßmus 		{
41472992391SStephan Aßmus 			BString name;
41572992391SStephan Aßmus 			if (message->FindString("name", &name) == B_OK) {
41675ba9dbfSStephan Aßmus 				BAutolock locker(fModel.Lock());
41704c9d578SStephan Aßmus 				int count = fVisiblePackages.CountItems();
41804c9d578SStephan Aßmus 				for (int i = 0; i < count; i++) {
41904c9d578SStephan Aßmus 					const PackageInfoRef& package
42004c9d578SStephan Aßmus 						= fVisiblePackages.ItemAtFast(i);
42172992391SStephan Aßmus 					if (package.Get() != NULL && package->Name() == name) {
42275ba9dbfSStephan Aßmus 						locker.Unlock();
42304c9d578SStephan Aßmus 						_AdoptPackage(package);
42404c9d578SStephan Aßmus 						break;
42504c9d578SStephan Aßmus 					}
42604c9d578SStephan Aßmus 				}
42704c9d578SStephan Aßmus 			} else {
42804c9d578SStephan Aßmus 				_ClearPackage();
42904c9d578SStephan Aßmus 			}
43004c9d578SStephan Aßmus 			break;
43104c9d578SStephan Aßmus 		}
43204c9d578SStephan Aßmus 
43304c9d578SStephan Aßmus 		case MSG_CATEGORY_SELECTED:
43404c9d578SStephan Aßmus 		{
435*89b16bb4SAndrew Lindesay 			BString code;
436*89b16bb4SAndrew Lindesay 			if (message->FindString("code", &code) != B_OK)
437*89b16bb4SAndrew Lindesay 				code = "";
43804c9d578SStephan Aßmus 			{
43904c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
440*89b16bb4SAndrew Lindesay 				fModel.SetCategory(code);
44104c9d578SStephan Aßmus 			}
44204c9d578SStephan Aßmus 			_AdoptModel();
44304c9d578SStephan Aßmus 			break;
44404c9d578SStephan Aßmus 		}
44504c9d578SStephan Aßmus 
44604c9d578SStephan Aßmus 		case MSG_DEPOT_SELECTED:
44704c9d578SStephan Aßmus 		{
44804c9d578SStephan Aßmus 			BString name;
44904c9d578SStephan Aßmus 			if (message->FindString("name", &name) != B_OK)
45004c9d578SStephan Aßmus 				name = "";
45104c9d578SStephan Aßmus 			{
45204c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
45304c9d578SStephan Aßmus 				fModel.SetDepot(name);
45404c9d578SStephan Aßmus 			}
45504c9d578SStephan Aßmus 			_AdoptModel();
456b965ce70SHumdinger 			_UpdateAvailableRepositories();
45704c9d578SStephan Aßmus 			break;
45804c9d578SStephan Aßmus 		}
45904c9d578SStephan Aßmus 
46004c9d578SStephan Aßmus 		case MSG_SEARCH_TERMS_MODIFIED:
46104c9d578SStephan Aßmus 		{
46204c9d578SStephan Aßmus 			// TODO: Do this with a delay!
46304c9d578SStephan Aßmus 			BString searchTerms;
46404c9d578SStephan Aßmus 			if (message->FindString("search terms", &searchTerms) != B_OK)
46504c9d578SStephan Aßmus 				searchTerms = "";
46604c9d578SStephan Aßmus 			{
46704c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
46804c9d578SStephan Aßmus 				fModel.SetSearchTerms(searchTerms);
46904c9d578SStephan Aßmus 			}
47004c9d578SStephan Aßmus 			_AdoptModel();
47104c9d578SStephan Aßmus 			break;
47204c9d578SStephan Aßmus 		}
47304c9d578SStephan Aßmus 
474da651104SStephan Aßmus 		case MSG_PACKAGE_CHANGED:
47504c9d578SStephan Aßmus 		{
47604c9d578SStephan Aßmus 			PackageInfo* info;
47704c9d578SStephan Aßmus 			if (message->FindPointer("package", (void**)&info) == B_OK) {
47804c9d578SStephan Aßmus 				PackageInfoRef ref(info, true);
479da651104SStephan Aßmus 				uint32 changes;
480da651104SStephan Aßmus 				if (message->FindUInt32("changes", &changes) != B_OK)
481da651104SStephan Aßmus 					changes = 0;
482da651104SStephan Aßmus 				if ((changes & PKG_CHANGED_STATE) != 0) {
48304c9d578SStephan Aßmus 					BAutolock locker(fModel.Lock());
48404c9d578SStephan Aßmus 					fModel.SetPackageState(ref, ref->State());
48504c9d578SStephan Aßmus 				}
48657e51fffSStephan Aßmus 
48757e51fffSStephan Aßmus 				// Asynchronous updates to the package information
48857e51fffSStephan Aßmus 				// can mean that the package needs to be added or
48957e51fffSStephan Aßmus 				// removed to/from the visible packages when the current
49057e51fffSStephan Aßmus 				// filter parameters are re-evaluated on this package.
49157e51fffSStephan Aßmus 				bool wasVisible = fVisiblePackages.Contains(ref);
49257e51fffSStephan Aßmus 				bool isVisible;
49357e51fffSStephan Aßmus 				{
494da651104SStephan Aßmus 					BAutolock locker(fModel.Lock());
4950c6df5d5SStephan Aßmus 					// The package didn't get a chance yet to be in the
4960c6df5d5SStephan Aßmus 					// visible package list
49739f49f70SJulian Harnath 					isVisible = fModel.MatchesFilter(ref);
49857e51fffSStephan Aßmus 
49957e51fffSStephan Aßmus 					// Transfer this single package, otherwise we miss
50057e51fffSStephan Aßmus 					// other packages if they appear or disappear along
50157e51fffSStephan Aßmus 					// with this one before receive a notification for
50257e51fffSStephan Aßmus 					// them.
50357e51fffSStephan Aßmus 					if (isVisible) {
50457e51fffSStephan Aßmus 						fVisiblePackages.Add(ref);
50557e51fffSStephan Aßmus 					} else if (wasVisible)
50657e51fffSStephan Aßmus 						fVisiblePackages.Remove(ref);
50757e51fffSStephan Aßmus 				}
50857e51fffSStephan Aßmus 
50957e51fffSStephan Aßmus 				if (wasVisible != isVisible) {
51057e51fffSStephan Aßmus 					if (!isVisible) {
51157e51fffSStephan Aßmus 						fPackageListView->RemovePackage(ref);
51257e51fffSStephan Aßmus 						fFeaturedPackagesView->RemovePackage(ref);
51357e51fffSStephan Aßmus 					} else {
51457e51fffSStephan Aßmus 						fPackageListView->AddPackage(ref);
51557e51fffSStephan Aßmus 						if (ref->IsProminent())
516da651104SStephan Aßmus 							fFeaturedPackagesView->AddPackage(ref);
517da651104SStephan Aßmus 					}
518da651104SStephan Aßmus 				}
5194e6fbe03SJulian Harnath 
5204e6fbe03SJulian Harnath 				if (!fSinglePackageMode && (changes & PKG_CHANGED_STATE) != 0)
5214e6fbe03SJulian Harnath 					fWorkStatusView->PackageStatusChanged(ref);
52257e51fffSStephan Aßmus 			}
52304c9d578SStephan Aßmus 			break;
52404c9d578SStephan Aßmus 		}
52504c9d578SStephan Aßmus 
52604c9d578SStephan Aßmus 		case MSG_RATE_PACKAGE:
5271bbda92dSStephan Aßmus 			_RatePackage();
52804c9d578SStephan Aßmus 			break;
52904c9d578SStephan Aßmus 
530d5ef985eSStephan Aßmus 		case MSG_SHOW_SCREENSHOT:
531d5ef985eSStephan Aßmus 			_ShowScreenshot();
532d5ef985eSStephan Aßmus 			break;
533d5ef985eSStephan Aßmus 
534125d42d9SJulian Harnath 		case MSG_PACKAGE_WORKER_BUSY:
535125d42d9SJulian Harnath 		{
536125d42d9SJulian Harnath 			BString reason;
537125d42d9SJulian Harnath 			status_t status = message->FindString("reason", &reason);
538125d42d9SJulian Harnath 			if (status != B_OK)
539125d42d9SJulian Harnath 				break;
540d78bfd8dSJulian Harnath 			if (!fSinglePackageMode)
541125d42d9SJulian Harnath 				fWorkStatusView->SetBusy(reason);
542125d42d9SJulian Harnath 			break;
543125d42d9SJulian Harnath 		}
544125d42d9SJulian Harnath 
545125d42d9SJulian Harnath 		case MSG_PACKAGE_WORKER_IDLE:
546d78bfd8dSJulian Harnath 			if (!fSinglePackageMode)
547125d42d9SJulian Harnath 				fWorkStatusView->SetIdle();
548125d42d9SJulian Harnath 			break;
549125d42d9SJulian Harnath 
55072fff6d3SJulian Harnath 		case MSG_ADD_VISIBLE_PACKAGES:
55172fff6d3SJulian Harnath 		{
55272fff6d3SJulian Harnath 			struct SemaphoreReleaser {
55372fff6d3SJulian Harnath 				SemaphoreReleaser(sem_id semaphore)
55472fff6d3SJulian Harnath 					:
55572fff6d3SJulian Harnath 					fSemaphore(semaphore)
55672fff6d3SJulian Harnath 				{ }
55772fff6d3SJulian Harnath 
55872fff6d3SJulian Harnath 				~SemaphoreReleaser() { release_sem(fSemaphore); }
55972fff6d3SJulian Harnath 
56072fff6d3SJulian Harnath 				sem_id fSemaphore;
56172fff6d3SJulian Harnath 			};
56272fff6d3SJulian Harnath 
56372fff6d3SJulian Harnath 			// Make sure acknowledge semaphore is released even on error,
56472fff6d3SJulian Harnath 			// so the worker thread won't be blocked
56572fff6d3SJulian Harnath 			SemaphoreReleaser acknowledger(fShowPackagesAcknowledgeSem);
56672fff6d3SJulian Harnath 
56772fff6d3SJulian Harnath 			int32 numPackages = 0;
56872fff6d3SJulian Harnath 			type_code unused;
56972fff6d3SJulian Harnath 			status_t status = message->GetInfo("package_ref", &unused,
57072fff6d3SJulian Harnath 				&numPackages);
57172fff6d3SJulian Harnath 			if (status != B_OK || numPackages == 0)
57272fff6d3SJulian Harnath 				break;
57372fff6d3SJulian Harnath 
57472fff6d3SJulian Harnath 			int32 listID = 0;
57572fff6d3SJulian Harnath 			status = message->FindInt32("list_id", &listID);
57672fff6d3SJulian Harnath 			if (status != B_OK)
57772fff6d3SJulian Harnath 				break;
57872fff6d3SJulian Harnath 			if (listID != atomic_get(&fPackagesToShowListID)) {
57972fff6d3SJulian Harnath 				// list is outdated, ignore
58072fff6d3SJulian Harnath 				break;
58172fff6d3SJulian Harnath 			}
58272fff6d3SJulian Harnath 
58372fff6d3SJulian Harnath 			for (int i = 0; i < numPackages; i++) {
58472fff6d3SJulian Harnath 				PackageInfo* packageRaw = NULL;
58572fff6d3SJulian Harnath 				status = message->FindPointer("package_ref", i,
58672fff6d3SJulian Harnath 					(void**)&packageRaw);
58772fff6d3SJulian Harnath 				if (status != B_OK)
58872fff6d3SJulian Harnath 					break;
58972fff6d3SJulian Harnath 				PackageInfoRef package(packageRaw, true);
59072fff6d3SJulian Harnath 
59172fff6d3SJulian Harnath 				fPackageListView->AddPackage(package);
59272fff6d3SJulian Harnath 				if (package->IsProminent())
59372fff6d3SJulian Harnath 					fFeaturedPackagesView->AddPackage(package);
59472fff6d3SJulian Harnath 			}
59572fff6d3SJulian Harnath 			break;
59672fff6d3SJulian Harnath 		}
59772fff6d3SJulian Harnath 
59872fff6d3SJulian Harnath 		case MSG_UPDATE_SELECTED_PACKAGE:
59972fff6d3SJulian Harnath 		{
60072fff6d3SJulian Harnath 			const PackageInfoRef& selectedPackage = fPackageInfoView->Package();
60172fff6d3SJulian Harnath 			fFeaturedPackagesView->SelectPackage(selectedPackage, true);
60272fff6d3SJulian Harnath 			fPackageListView->SelectPackage(selectedPackage);
60372fff6d3SJulian Harnath 
60472fff6d3SJulian Harnath 			AutoLocker<BLocker> modelLocker(fModel.Lock());
60572fff6d3SJulian Harnath 			if (!fVisiblePackages.Contains(fPackageInfoView->Package()))
60672fff6d3SJulian Harnath 				fPackageInfoView->Clear();
60772fff6d3SJulian Harnath 			break;
60872fff6d3SJulian Harnath 		}
60972fff6d3SJulian Harnath 
61004c9d578SStephan Aßmus 		default:
61104c9d578SStephan Aßmus 			BWindow::MessageReceived(message);
61204c9d578SStephan Aßmus 			break;
61304c9d578SStephan Aßmus 	}
61404c9d578SStephan Aßmus }
61504c9d578SStephan Aßmus 
61604c9d578SStephan Aßmus 
61704c9d578SStephan Aßmus void
61804c9d578SStephan Aßmus MainWindow::StoreSettings(BMessage& settings) const
61904c9d578SStephan Aßmus {
620f7c2983eSAxel Dörfler 	settings.AddRect(_WindowFrameName(), Frame());
621f7c2983eSAxel Dörfler 	if (!fSinglePackageMode) {
62204c9d578SStephan Aßmus 		settings.AddRect("window frame", Frame());
62304c9d578SStephan Aßmus 
62404c9d578SStephan Aßmus 		BMessage columnSettings;
62504c9d578SStephan Aßmus 		fPackageListView->SaveState(&columnSettings);
62604c9d578SStephan Aßmus 
62704c9d578SStephan Aßmus 		settings.AddMessage("column settings", &columnSettings);
62804c9d578SStephan Aßmus 
6292997e05bSStephan Aßmus 		settings.AddBool("show featured packages",
6302997e05bSStephan Aßmus 			fModel.ShowFeaturedPackages());
6312997e05bSStephan Aßmus 		settings.AddBool("show available packages",
6322997e05bSStephan Aßmus 			fModel.ShowAvailablePackages());
6332997e05bSStephan Aßmus 		settings.AddBool("show installed packages",
6342997e05bSStephan Aßmus 			fModel.ShowInstalledPackages());
63504c9d578SStephan Aßmus 		settings.AddBool("show develop packages", fModel.ShowDevelopPackages());
63604c9d578SStephan Aßmus 		settings.AddBool("show source packages", fModel.ShowSourcePackages());
6376d519fd1SStephan Aßmus 	}
6388f03a0f9SStephan Aßmus 
6398f03a0f9SStephan Aßmus 	settings.AddString("username", fModel.Username());
64004c9d578SStephan Aßmus }
64104c9d578SStephan Aßmus 
64204c9d578SStephan Aßmus 
64304c9d578SStephan Aßmus void
64404c9d578SStephan Aßmus MainWindow::PackageChanged(const PackageInfoEvent& event)
64504c9d578SStephan Aßmus {
646a9edb9bfSAndrew Lindesay 	uint32 watchedChanges = PKG_CHANGED_STATE | PKG_CHANGED_PROMINENCE;
647a9edb9bfSAndrew Lindesay 	if ((event.Changes() & watchedChanges) != 0) {
64804c9d578SStephan Aßmus 		PackageInfoRef ref(event.Package());
649da651104SStephan Aßmus 		BMessage message(MSG_PACKAGE_CHANGED);
65004c9d578SStephan Aßmus 		message.AddPointer("package", ref.Get());
651da651104SStephan Aßmus 		message.AddUInt32("changes", event.Changes());
65204c9d578SStephan Aßmus 		ref.Detach();
65304c9d578SStephan Aßmus 			// reference needs to be released by MessageReceived();
65404c9d578SStephan Aßmus 		PostMessage(&message);
65504c9d578SStephan Aßmus 	}
65604c9d578SStephan Aßmus }
65704c9d578SStephan Aßmus 
65804c9d578SStephan Aßmus 
65904c9d578SStephan Aßmus status_t
66004c9d578SStephan Aßmus MainWindow::SchedulePackageActions(PackageActionList& list)
66104c9d578SStephan Aßmus {
66204c9d578SStephan Aßmus 	AutoLocker<BLocker> lock(&fPendingActionsLock);
66304c9d578SStephan Aßmus 	for (int32 i = 0; i < list.CountItems(); i++) {
66404c9d578SStephan Aßmus 		if (!fPendingActions.Add(list.ItemAtFast(i)))
66504c9d578SStephan Aßmus 			return B_NO_MEMORY;
66604c9d578SStephan Aßmus 	}
66704c9d578SStephan Aßmus 
66804c9d578SStephan Aßmus 	return release_sem_etc(fPendingActionsSem, list.CountItems(), 0);
66904c9d578SStephan Aßmus }
67004c9d578SStephan Aßmus 
67104c9d578SStephan Aßmus 
67204c9d578SStephan Aßmus Model*
67304c9d578SStephan Aßmus MainWindow::GetModel()
67404c9d578SStephan Aßmus {
67504c9d578SStephan Aßmus 	return &fModel;
67604c9d578SStephan Aßmus }
67704c9d578SStephan Aßmus 
67804c9d578SStephan Aßmus 
67904c9d578SStephan Aßmus void
68004c9d578SStephan Aßmus MainWindow::_BuildMenu(BMenuBar* menuBar)
68104c9d578SStephan Aßmus {
68204c9d578SStephan Aßmus 	BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
6833369e03dSAndrew Lindesay 	fRefreshRepositoriesItem = new BMenuItem(
6843369e03dSAndrew Lindesay 		B_TRANSLATE("Refresh repositories"), new BMessage(MSG_REFRESH_REPOS));
6853369e03dSAndrew Lindesay 	menu->AddItem(fRefreshRepositoriesItem);
686d7f78130SHumdinger 	menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"
687d7f78130SHumdinger 		B_UTF8_ELLIPSIS), new BMessage(MSG_MANAGE_REPOS)));
688d144f952SAugustin Cavalier 	menu->AddItem(new BMenuItem(B_TRANSLATE("Check for updates"
689d144f952SAugustin Cavalier 		B_UTF8_ELLIPSIS), new BMessage(MSG_SOFTWARE_UPDATER)));
690e0a26dceSStephan Aßmus 
69104c9d578SStephan Aßmus 	menuBar->AddItem(menu);
69204c9d578SStephan Aßmus 
693b965ce70SHumdinger 	fRepositoryMenu = new BMenu(B_TRANSLATE("Repositories"));
694b965ce70SHumdinger 	menuBar->AddItem(fRepositoryMenu);
695b965ce70SHumdinger 
6964fade61fSStephan Aßmus 	menu = new BMenu(B_TRANSLATE("Show"));
6974fade61fSStephan Aßmus 
698fdec4227SStephan Aßmus 	fShowAvailablePackagesItem = new BMenuItem(
6994fade61fSStephan Aßmus 		B_TRANSLATE("Available packages"),
700fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_AVAILABLE_PACKAGES));
701fdec4227SStephan Aßmus 	menu->AddItem(fShowAvailablePackagesItem);
702fdec4227SStephan Aßmus 
703fdec4227SStephan Aßmus 	fShowInstalledPackagesItem = new BMenuItem(
7044fade61fSStephan Aßmus 		B_TRANSLATE("Installed packages"),
705fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_INSTALLED_PACKAGES));
706fdec4227SStephan Aßmus 	menu->AddItem(fShowInstalledPackagesItem);
707fdec4227SStephan Aßmus 
708fdec4227SStephan Aßmus 	menu->AddSeparatorItem();
709fdec4227SStephan Aßmus 
710fdec4227SStephan Aßmus 	fShowDevelopPackagesItem = new BMenuItem(
7114fade61fSStephan Aßmus 		B_TRANSLATE("Develop packages"),
712fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_DEVELOP_PACKAGES));
713fdec4227SStephan Aßmus 	menu->AddItem(fShowDevelopPackagesItem);
714fdec4227SStephan Aßmus 
715fdec4227SStephan Aßmus 	fShowSourcePackagesItem = new BMenuItem(
7164fade61fSStephan Aßmus 		B_TRANSLATE("Source packages"),
717fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_SOURCE_PACKAGES));
718fdec4227SStephan Aßmus 	menu->AddItem(fShowSourcePackagesItem);
719fdec4227SStephan Aßmus 
720fdec4227SStephan Aßmus 	menuBar->AddItem(menu);
72104c9d578SStephan Aßmus }
72204c9d578SStephan Aßmus 
72304c9d578SStephan Aßmus 
72404c9d578SStephan Aßmus void
725e0a26dceSStephan Aßmus MainWindow::_BuildUserMenu(BMenuBar* menuBar)
726e0a26dceSStephan Aßmus {
727e0a26dceSStephan Aßmus 	fUserMenu = new BMenu(B_TRANSLATE("Not logged in"));
728e0a26dceSStephan Aßmus 
729e0a26dceSStephan Aßmus 	fLogInItem = new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS),
730e0a26dceSStephan Aßmus 		new BMessage(MSG_LOG_IN));
731e0a26dceSStephan Aßmus 	fUserMenu->AddItem(fLogInItem);
732e0a26dceSStephan Aßmus 
733e0a26dceSStephan Aßmus 	fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"),
734e0a26dceSStephan Aßmus 		new BMessage(MSG_LOG_OUT));
735e0a26dceSStephan Aßmus 	fUserMenu->AddItem(fLogOutItem);
736e0a26dceSStephan Aßmus 
737e0a26dceSStephan Aßmus 	menuBar->AddItem(fUserMenu);
738e0a26dceSStephan Aßmus }
739e0a26dceSStephan Aßmus 
740e0a26dceSStephan Aßmus 
741e0a26dceSStephan Aßmus void
742f7c2983eSAxel Dörfler MainWindow::_RestoreUserName(const BMessage& settings)
743f7c2983eSAxel Dörfler {
744f7c2983eSAxel Dörfler 	BString username;
745f7c2983eSAxel Dörfler 	if (settings.FindString("username", &username) == B_OK
746f7c2983eSAxel Dörfler 		&& username.Length() > 0) {
747f7c2983eSAxel Dörfler 		fModel.SetUsername(username);
748f7c2983eSAxel Dörfler 	}
749f7c2983eSAxel Dörfler }
750f7c2983eSAxel Dörfler 
751f7c2983eSAxel Dörfler 
752f7c2983eSAxel Dörfler const char*
753f7c2983eSAxel Dörfler MainWindow::_WindowFrameName() const
754f7c2983eSAxel Dörfler {
755f7c2983eSAxel Dörfler 	if (fSinglePackageMode)
756f7c2983eSAxel Dörfler 		return "small window frame";
757f7c2983eSAxel Dörfler 
758f7c2983eSAxel Dörfler 	return "window frame";
759f7c2983eSAxel Dörfler }
760f7c2983eSAxel Dörfler 
761f7c2983eSAxel Dörfler 
762f7c2983eSAxel Dörfler void
763f7c2983eSAxel Dörfler MainWindow::_RestoreWindowFrame(const BMessage& settings)
764f7c2983eSAxel Dörfler {
765f7c2983eSAxel Dörfler 	BRect frame = Frame();
766f7c2983eSAxel Dörfler 
767f7c2983eSAxel Dörfler 	BRect windowFrame;
768f7c2983eSAxel Dörfler 	bool fromSettings = false;
769f7c2983eSAxel Dörfler 	if (settings.FindRect(_WindowFrameName(), &windowFrame) == B_OK) {
770f7c2983eSAxel Dörfler 		frame = windowFrame;
771f7c2983eSAxel Dörfler 		fromSettings = true;
772f7c2983eSAxel Dörfler 	} else if (!fSinglePackageMode) {
773f7c2983eSAxel Dörfler 		// Resize to occupy a certain screen size
774f7c2983eSAxel Dörfler 		BRect screenFrame = BScreen(this).Frame();
775f7c2983eSAxel Dörfler 		float width = frame.Width();
776f7c2983eSAxel Dörfler 		float height = frame.Height();
777f7c2983eSAxel Dörfler 		if (width < screenFrame.Width() * .666f
778f7c2983eSAxel Dörfler 			&& height < screenFrame.Height() * .666f) {
779f7c2983eSAxel Dörfler 			frame.bottom = frame.top + screenFrame.Height() * .666f;
780f7c2983eSAxel Dörfler 			frame.right = frame.left
781f7c2983eSAxel Dörfler 				+ std::min(screenFrame.Width() * .666f, height * 7 / 5);
782f7c2983eSAxel Dörfler 		}
783f7c2983eSAxel Dörfler 	}
784f7c2983eSAxel Dörfler 
785f7c2983eSAxel Dörfler 	MoveTo(frame.LeftTop());
786f7c2983eSAxel Dörfler 	ResizeTo(frame.Width(), frame.Height());
787f7c2983eSAxel Dörfler 
788f7c2983eSAxel Dörfler 	if (fromSettings)
7898e96ec34SAxel Dörfler 		MoveOnScreen();
790f7c2983eSAxel Dörfler 	else
791f7c2983eSAxel Dörfler 		CenterOnScreen();
792f7c2983eSAxel Dörfler }
793f7c2983eSAxel Dörfler 
794f7c2983eSAxel Dörfler 
795f7c2983eSAxel Dörfler void
79663975654SStephan Aßmus MainWindow::_InitWorkerThreads()
79763975654SStephan Aßmus {
79863975654SStephan Aßmus 	fPendingActionsSem = create_sem(0, "PendingPackageActions");
79963975654SStephan Aßmus 	if (fPendingActionsSem >= 0) {
80063975654SStephan Aßmus 		fPendingActionsWorker = spawn_thread(&_PackageActionWorker,
80163975654SStephan Aßmus 			"Planet Express", B_NORMAL_PRIORITY, this);
80263975654SStephan Aßmus 		if (fPendingActionsWorker >= 0)
80363975654SStephan Aßmus 			resume_thread(fPendingActionsWorker);
80409f82905SStephan Aßmus 	} else
80509f82905SStephan Aßmus 		fPendingActionsWorker = -1;
80663975654SStephan Aßmus 
80763975654SStephan Aßmus 	fPackageToPopulateSem = create_sem(0, "PopulatePackage");
80863975654SStephan Aßmus 	if (fPackageToPopulateSem >= 0) {
80963975654SStephan Aßmus 		fPopulatePackageWorker = spawn_thread(&_PopulatePackageWorker,
81063975654SStephan Aßmus 			"Package Populator", B_NORMAL_PRIORITY, this);
81163975654SStephan Aßmus 		if (fPopulatePackageWorker >= 0)
81263975654SStephan Aßmus 			resume_thread(fPopulatePackageWorker);
81309f82905SStephan Aßmus 	} else
81409f82905SStephan Aßmus 		fPopulatePackageWorker = -1;
81572fff6d3SJulian Harnath 
81672fff6d3SJulian Harnath 	fNewPackagesToShowSem = create_sem(0, "ShowPackages");
81772fff6d3SJulian Harnath 	fShowPackagesAcknowledgeSem = create_sem(0, "ShowPackagesAck");
81872fff6d3SJulian Harnath 	if (fNewPackagesToShowSem >= 0 && fShowPackagesAcknowledgeSem >= 0) {
81972fff6d3SJulian Harnath 		fShowPackagesWorker = spawn_thread(&_PackagesToShowWorker,
82072fff6d3SJulian Harnath 			"Good news everyone", B_NORMAL_PRIORITY, this);
82172fff6d3SJulian Harnath 		if (fShowPackagesWorker >= 0)
82272fff6d3SJulian Harnath 			resume_thread(fShowPackagesWorker);
82372fff6d3SJulian Harnath 	} else
82472fff6d3SJulian Harnath 		fShowPackagesWorker = -1;
82563975654SStephan Aßmus }
82663975654SStephan Aßmus 
82763975654SStephan Aßmus 
82863975654SStephan Aßmus void
82904c9d578SStephan Aßmus MainWindow::_AdoptModel()
83004c9d578SStephan Aßmus {
83172fff6d3SJulian Harnath 	{
83272fff6d3SJulian Harnath 		AutoLocker<BLocker> modelLocker(fModel.Lock());
833cd002761SAndrew Lindesay 		fVisiblePackages = fModel.CreatePackageList();
83472fff6d3SJulian Harnath 		AutoLocker<BLocker> listLocker(fPackagesToShowListLock);
83572fff6d3SJulian Harnath 		fPackagesToShowList = fVisiblePackages;
83672fff6d3SJulian Harnath 		atomic_add(&fPackagesToShowListID, 1);
83772fff6d3SJulian Harnath 	}
83872fff6d3SJulian Harnath 
8394b930ccaSStephan Aßmus 	fFeaturedPackagesView->Clear();
84004c9d578SStephan Aßmus 	fPackageListView->Clear();
8414b930ccaSStephan Aßmus 
84272fff6d3SJulian Harnath 	release_sem(fNewPackagesToShowSem);
84304c9d578SStephan Aßmus 
84404c9d578SStephan Aßmus 	BAutolock locker(fModel.Lock());
845fdec4227SStephan Aßmus 	fShowAvailablePackagesItem->SetMarked(fModel.ShowAvailablePackages());
846fdec4227SStephan Aßmus 	fShowInstalledPackagesItem->SetMarked(fModel.ShowInstalledPackages());
847fdec4227SStephan Aßmus 	fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages());
848fdec4227SStephan Aßmus 	fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages());
849da651104SStephan Aßmus 
850b965ce70SHumdinger 	if (fModel.ShowFeaturedPackages())
851e00a489bSAugustin Cavalier 		fListTabs->Select(0);
8524fade61fSStephan Aßmus 	else
853e00a489bSAugustin Cavalier 		fListTabs->Select(1);
854b965ce70SHumdinger 
855b965ce70SHumdinger 	fFilterView->AdoptModel(fModel);
85604c9d578SStephan Aßmus }
85704c9d578SStephan Aßmus 
85804c9d578SStephan Aßmus 
85904c9d578SStephan Aßmus void
86004c9d578SStephan Aßmus MainWindow::_AdoptPackage(const PackageInfoRef& package)
86104c9d578SStephan Aßmus {
86275ba9dbfSStephan Aßmus 	{
86375ba9dbfSStephan Aßmus 		BAutolock locker(fModel.Lock());
86404c9d578SStephan Aßmus 		fPackageInfoView->SetPackage(package);
8651986ab9fSStephan Aßmus 
866664372abSStephan Aßmus 		if (fFeaturedPackagesView != NULL)
867664372abSStephan Aßmus 			fFeaturedPackagesView->SelectPackage(package);
8681986ab9fSStephan Aßmus 		if (fPackageListView != NULL)
8691986ab9fSStephan Aßmus 			fPackageListView->SelectPackage(package);
87075ba9dbfSStephan Aßmus 	}
87104c9d578SStephan Aßmus 
872a9edb9bfSAndrew Lindesay 	_PopulatePackageAsync(false);
87304c9d578SStephan Aßmus }
87404c9d578SStephan Aßmus 
87504c9d578SStephan Aßmus 
87604c9d578SStephan Aßmus void
87704c9d578SStephan Aßmus MainWindow::_ClearPackage()
87804c9d578SStephan Aßmus {
87904c9d578SStephan Aßmus 	fPackageInfoView->Clear();
88004c9d578SStephan Aßmus }
88104c9d578SStephan Aßmus 
88204c9d578SStephan Aßmus 
88304c9d578SStephan Aßmus void
8843369e03dSAndrew Lindesay MainWindow::_StopBulkLoad()
88504c9d578SStephan Aßmus {
8863369e03dSAndrew Lindesay 	AutoLocker<BLocker> lock(&fBulkLoadProcessCoordinatorLock);
887448f707cSStephan Aßmus 
8883369e03dSAndrew Lindesay 	if (fBulkLoadProcessCoordinator != NULL) {
8893369e03dSAndrew Lindesay 		printf("will stop full update process coordinator\n");
8903369e03dSAndrew Lindesay 		fBulkLoadProcessCoordinator->Stop();
89104c9d578SStephan Aßmus 	}
89204c9d578SStephan Aßmus }
89304c9d578SStephan Aßmus 
89404c9d578SStephan Aßmus 
89504c9d578SStephan Aßmus void
8963369e03dSAndrew Lindesay MainWindow::_StartBulkLoad(bool force)
89704c9d578SStephan Aßmus {
8983369e03dSAndrew Lindesay 	AutoLocker<BLocker> lock(&fBulkLoadProcessCoordinatorLock);
899448f707cSStephan Aßmus 
9003369e03dSAndrew Lindesay 	if (fBulkLoadProcessCoordinator == NULL) {
9013369e03dSAndrew Lindesay 		fBulkLoadProcessCoordinator
9023369e03dSAndrew Lindesay 			= ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
9033369e03dSAndrew Lindesay 				this,
9043369e03dSAndrew Lindesay 					// PackageInfoListener
9053369e03dSAndrew Lindesay 				this,
9063369e03dSAndrew Lindesay 					// ProcessCoordinatorListener
9073369e03dSAndrew Lindesay 				&fModel, force);
9083369e03dSAndrew Lindesay 		fBulkLoadProcessCoordinator->Start();
9093369e03dSAndrew Lindesay 		fRefreshRepositoriesItem->SetEnabled(false);
9103b17d8ddSAndrew Lindesay 	}
911ebb1a01cSAndrew Lindesay }
912ebb1a01cSAndrew Lindesay 
91304c9d578SStephan Aßmus 
9143369e03dSAndrew Lindesay /*! This method is called when there is some change in the bulk load process.
9153369e03dSAndrew Lindesay     A change may mean that a new process has started / stopped etc... or it
9163369e03dSAndrew Lindesay     may mean that the entire coordinator has finished.
9173369e03dSAndrew Lindesay */
91804c9d578SStephan Aßmus 
9193369e03dSAndrew Lindesay void
9203369e03dSAndrew Lindesay MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState)
9213369e03dSAndrew Lindesay {
9223369e03dSAndrew Lindesay 	AutoLocker<BLocker> lock(&fBulkLoadProcessCoordinatorLock);
92304c9d578SStephan Aßmus 
9243369e03dSAndrew Lindesay 	if (fBulkLoadProcessCoordinator == coordinatorState.Coordinator()) {
9253369e03dSAndrew Lindesay 		if (!coordinatorState.IsRunning())
9263369e03dSAndrew Lindesay 			_BulkLoadProcessCoordinatorFinished(coordinatorState);
92704c9d578SStephan Aßmus 		else {
9283369e03dSAndrew Lindesay 			_NotifyWorkStatusChange(coordinatorState.Message(),
9293369e03dSAndrew Lindesay 				coordinatorState.Progress());
9303369e03dSAndrew Lindesay 				// show the progress to the user.
931d13c3d89SAndrew Lindesay 		}
932d13c3d89SAndrew Lindesay 	} else {
9333369e03dSAndrew Lindesay 		if (Logger::IsInfoEnabled()) {
9343369e03dSAndrew Lindesay 			printf("unknown process coordinator changed\n");
9353b17d8ddSAndrew Lindesay 		}
936d13c3d89SAndrew Lindesay 	}
93704c9d578SStephan Aßmus }
93804c9d578SStephan Aßmus 
93904c9d578SStephan Aßmus 
94004c9d578SStephan Aßmus void
9413369e03dSAndrew Lindesay MainWindow::_BulkLoadProcessCoordinatorFinished(
9423369e03dSAndrew Lindesay 	ProcessCoordinatorState& coordinatorState)
94304c9d578SStephan Aßmus {
9443369e03dSAndrew Lindesay 	if (coordinatorState.ErrorStatus() != B_OK) {
9453369e03dSAndrew Lindesay 		AppUtils::NotifySimpleError(
9463369e03dSAndrew Lindesay 			B_TRANSLATE("Package Update Error"),
9473369e03dSAndrew Lindesay 			B_TRANSLATE("While updating package data, a problem has arisen "
9483369e03dSAndrew Lindesay 				"that may cause data to be outdated or missing from the "
9493369e03dSAndrew Lindesay 				"application's display.  Additional details regarding this "
9503369e03dSAndrew Lindesay 				"problem may be able to be obtained from the application "
9513369e03dSAndrew Lindesay 				"logs."));
95204c9d578SStephan Aßmus 	}
9533369e03dSAndrew Lindesay 	BMessenger messenger(this);
9543369e03dSAndrew Lindesay 	messenger.SendMessage(MSG_BULK_LOAD_DONE);
9553369e03dSAndrew Lindesay 	// it is safe to delete the coordinator here because it is already known
9563369e03dSAndrew Lindesay 	// that all of the processes have completed and their threads will have
9573369e03dSAndrew Lindesay 	// exited safely by this point.
9583369e03dSAndrew Lindesay 	delete fBulkLoadProcessCoordinator;
9593369e03dSAndrew Lindesay 	fBulkLoadProcessCoordinator = NULL;
9603369e03dSAndrew Lindesay 	fRefreshRepositoriesItem->SetEnabled(true);
96104c9d578SStephan Aßmus }
96204c9d578SStephan Aßmus 
96304c9d578SStephan Aßmus 
9643369e03dSAndrew Lindesay void
9653369e03dSAndrew Lindesay MainWindow::_BulkLoadCompleteReceived()
96604c9d578SStephan Aßmus {
9673369e03dSAndrew Lindesay 	_AdoptModel();
9683369e03dSAndrew Lindesay 	_UpdateAvailableRepositories();
9693369e03dSAndrew Lindesay 	fWorkStatusView->SetIdle();
9703369e03dSAndrew Lindesay }
97104c9d578SStephan Aßmus 
97204c9d578SStephan Aßmus 
9733369e03dSAndrew Lindesay /*! Sends off a message to the Window so that it can change the status view
9743369e03dSAndrew Lindesay     on the front-end in the UI thread.
9753369e03dSAndrew Lindesay */
97604c9d578SStephan Aßmus 
9773369e03dSAndrew Lindesay void
9783369e03dSAndrew Lindesay MainWindow::_NotifyWorkStatusChange(const BString& text, float progress)
9793369e03dSAndrew Lindesay {
9803369e03dSAndrew Lindesay 	BMessage message(MSG_WORK_STATUS_CHANGE);
98104c9d578SStephan Aßmus 
9823369e03dSAndrew Lindesay 	if (!text.IsEmpty())
9833369e03dSAndrew Lindesay 		message.AddString(KEY_WORK_STATUS_TEXT, text);
9843369e03dSAndrew Lindesay 	message.AddFloat(KEY_WORK_STATUS_PROGRESS, progress);
98504c9d578SStephan Aßmus 
9863369e03dSAndrew Lindesay 	this->PostMessage(&message, this);
9873369e03dSAndrew Lindesay }
98804c9d578SStephan Aßmus 
9893369e03dSAndrew Lindesay 
9903369e03dSAndrew Lindesay void
9913369e03dSAndrew Lindesay MainWindow::_HandleWorkStatusChangeMessageReceived(const BMessage* message)
9923369e03dSAndrew Lindesay {
9933369e03dSAndrew Lindesay 	BString text;
9943369e03dSAndrew Lindesay 	float progress;
9953369e03dSAndrew Lindesay 
9963369e03dSAndrew Lindesay 	if (message->FindString(KEY_WORK_STATUS_TEXT, &text) == B_OK)
9973369e03dSAndrew Lindesay 		fWorkStatusView->SetText(text);
9983369e03dSAndrew Lindesay 
9993369e03dSAndrew Lindesay 	if (message->FindFloat(KEY_WORK_STATUS_PROGRESS, &progress) == B_OK)
10003369e03dSAndrew Lindesay 		fWorkStatusView->SetProgress(progress);
100104c9d578SStephan Aßmus }
100204c9d578SStephan Aßmus 
100304c9d578SStephan Aßmus 
100404c9d578SStephan Aßmus status_t
100504c9d578SStephan Aßmus MainWindow::_PackageActionWorker(void* arg)
100604c9d578SStephan Aßmus {
100704c9d578SStephan Aßmus 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
100804c9d578SStephan Aßmus 
100904c9d578SStephan Aßmus 	while (acquire_sem(window->fPendingActionsSem) == B_OK) {
101004c9d578SStephan Aßmus 		PackageActionRef ref;
101104c9d578SStephan Aßmus 		{
101204c9d578SStephan Aßmus 			AutoLocker<BLocker> lock(&window->fPendingActionsLock);
101304c9d578SStephan Aßmus 			ref = window->fPendingActions.ItemAt(0);
101404c9d578SStephan Aßmus 			if (ref.Get() == NULL)
101504c9d578SStephan Aßmus 				break;
101604c9d578SStephan Aßmus 			window->fPendingActions.Remove(0);
101704c9d578SStephan Aßmus 		}
101804c9d578SStephan Aßmus 
1019125d42d9SJulian Harnath 		BMessenger messenger(window);
1020125d42d9SJulian Harnath 		BMessage busyMessage(MSG_PACKAGE_WORKER_BUSY);
1021125d42d9SJulian Harnath 		BString text(ref->Label());
1022bc622f6bSHumdinger 		text << B_UTF8_ELLIPSIS;
1023125d42d9SJulian Harnath 		busyMessage.AddString("reason", text);
1024125d42d9SJulian Harnath 
1025125d42d9SJulian Harnath 		messenger.SendMessage(&busyMessage);
102604c9d578SStephan Aßmus 		ref->Perform();
1027125d42d9SJulian Harnath 		messenger.SendMessage(MSG_PACKAGE_WORKER_IDLE);
102804c9d578SStephan Aßmus 	}
102904c9d578SStephan Aßmus 
103004c9d578SStephan Aßmus 	return 0;
103104c9d578SStephan Aßmus }
103204c9d578SStephan Aßmus 
103304c9d578SStephan Aßmus 
1034a9edb9bfSAndrew Lindesay /*! This method will cause the package to have its data refreshed from
1035a9edb9bfSAndrew Lindesay     the server application.  The refresh happens in the background; this method
1036a9edb9bfSAndrew Lindesay     is asynchronous.
1037a9edb9bfSAndrew Lindesay */
1038a9edb9bfSAndrew Lindesay 
1039a9edb9bfSAndrew Lindesay void
1040a9edb9bfSAndrew Lindesay MainWindow::_PopulatePackageAsync(bool forcePopulate)
1041a9edb9bfSAndrew Lindesay {
1042a9edb9bfSAndrew Lindesay 		// Trigger asynchronous package population from the web-app
1043a9edb9bfSAndrew Lindesay 	{
1044a9edb9bfSAndrew Lindesay 		AutoLocker<BLocker> lock(&fPackageToPopulateLock);
1045a9edb9bfSAndrew Lindesay 		fPackageToPopulate = fPackageInfoView->Package();
1046a9edb9bfSAndrew Lindesay 		fForcePopulatePackage = forcePopulate;
1047a9edb9bfSAndrew Lindesay 	}
1048a9edb9bfSAndrew Lindesay 	release_sem_etc(fPackageToPopulateSem, 1, 0);
1049a9edb9bfSAndrew Lindesay 
1050a9edb9bfSAndrew Lindesay 	if (Logger::IsDebugEnabled()) {
1051a9edb9bfSAndrew Lindesay 		printf("pkg [%s] will be updated from the server.\n",
1052a9edb9bfSAndrew Lindesay 			fPackageToPopulate->Name().String());
1053a9edb9bfSAndrew Lindesay 	}
1054a9edb9bfSAndrew Lindesay }
1055a9edb9bfSAndrew Lindesay 
1056a9edb9bfSAndrew Lindesay 
1057a9edb9bfSAndrew Lindesay /*! This method will run in the background.  The thread will block until there
1058a9edb9bfSAndrew Lindesay     is a package to be updated.  When the thread unblocks, it will update the
1059a9edb9bfSAndrew Lindesay     package with information from the server.
1060a9edb9bfSAndrew Lindesay */
1061a9edb9bfSAndrew Lindesay 
106204c9d578SStephan Aßmus status_t
106304c9d578SStephan Aßmus MainWindow::_PopulatePackageWorker(void* arg)
106404c9d578SStephan Aßmus {
106504c9d578SStephan Aßmus 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
106604c9d578SStephan Aßmus 
106704c9d578SStephan Aßmus 	while (acquire_sem(window->fPackageToPopulateSem) == B_OK) {
106804c9d578SStephan Aßmus 		PackageInfoRef package;
1069a9edb9bfSAndrew Lindesay 		bool force;
107004c9d578SStephan Aßmus 		{
107104c9d578SStephan Aßmus 			AutoLocker<BLocker> lock(&window->fPackageToPopulateLock);
107204c9d578SStephan Aßmus 			package = window->fPackageToPopulate;
1073a9edb9bfSAndrew Lindesay 			force = window->fForcePopulatePackage;
107404c9d578SStephan Aßmus 		}
107504c9d578SStephan Aßmus 
107604c9d578SStephan Aßmus 		if (package.Get() != NULL) {
1077a9edb9bfSAndrew Lindesay 			uint32 populateFlags = Model::POPULATE_USER_RATINGS
1078a9edb9bfSAndrew Lindesay 				| Model::POPULATE_SCREEN_SHOTS
1079a9edb9bfSAndrew Lindesay 				| Model::POPULATE_CHANGELOG;
1080a9edb9bfSAndrew Lindesay 
1081a9edb9bfSAndrew Lindesay 			if (force)
1082a9edb9bfSAndrew Lindesay 				populateFlags |= Model::POPULATE_FORCE;
1083a9edb9bfSAndrew Lindesay 
1084a9edb9bfSAndrew Lindesay 			window->fModel.PopulatePackage(package, populateFlags);
1085a9edb9bfSAndrew Lindesay 
1086a9edb9bfSAndrew Lindesay 			if (Logger::IsDebugEnabled()) {
1087a9edb9bfSAndrew Lindesay 				printf("populating package [%s]\n",
1088a9edb9bfSAndrew Lindesay 					package->Name().String());
1089a9edb9bfSAndrew Lindesay 			}
109004c9d578SStephan Aßmus 		}
109104c9d578SStephan Aßmus 	}
109204c9d578SStephan Aßmus 
109304c9d578SStephan Aßmus 	return 0;
109404c9d578SStephan Aßmus }
109504c9d578SStephan Aßmus 
109604c9d578SStephan Aßmus 
109772fff6d3SJulian Harnath /* static */ status_t
109872fff6d3SJulian Harnath MainWindow::_PackagesToShowWorker(void* arg)
109972fff6d3SJulian Harnath {
110072fff6d3SJulian Harnath 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
110172fff6d3SJulian Harnath 
110272fff6d3SJulian Harnath 	while (acquire_sem(window->fNewPackagesToShowSem) == B_OK) {
110372fff6d3SJulian Harnath 		PackageList packageList;
110472fff6d3SJulian Harnath 		int32 listID = 0;
110572fff6d3SJulian Harnath 		{
110672fff6d3SJulian Harnath 			AutoLocker<BLocker> lock(&window->fPackagesToShowListLock);
110772fff6d3SJulian Harnath 			packageList = window->fPackagesToShowList;
110872fff6d3SJulian Harnath 			listID = atomic_get(&window->fPackagesToShowListID);
110972fff6d3SJulian Harnath 			window->fPackagesToShowList.Clear();
111072fff6d3SJulian Harnath 		}
111172fff6d3SJulian Harnath 
111272fff6d3SJulian Harnath 		// Add packages to list views in batches of kPackagesPerUpdate so we
111372fff6d3SJulian Harnath 		// don't block the window thread for long with each iteration
111472fff6d3SJulian Harnath 		enum {
111572fff6d3SJulian Harnath 			kPackagesPerUpdate = 20
111672fff6d3SJulian Harnath 		};
111772fff6d3SJulian Harnath 		uint32 packagesInMessage = 0;
111872fff6d3SJulian Harnath 		BMessage message(MSG_ADD_VISIBLE_PACKAGES);
111972fff6d3SJulian Harnath 		BMessenger messenger(window);
112072fff6d3SJulian Harnath 		bool listIsOutdated = false;
112172fff6d3SJulian Harnath 
112272fff6d3SJulian Harnath 		for (int i = 0; i < packageList.CountItems(); i++) {
112372fff6d3SJulian Harnath 			const PackageInfoRef& package = packageList.ItemAtFast(i);
112472fff6d3SJulian Harnath 
112572fff6d3SJulian Harnath 			if (packagesInMessage >= kPackagesPerUpdate) {
112672fff6d3SJulian Harnath 				if (listID != atomic_get(&window->fPackagesToShowListID)) {
112772fff6d3SJulian Harnath 					// The model was changed again in the meantime, and thus
112872fff6d3SJulian Harnath 					// our package list isn't current anymore. Send no further
112972fff6d3SJulian Harnath 					// messags based on this list and go back to start.
113072fff6d3SJulian Harnath 					listIsOutdated = true;
113172fff6d3SJulian Harnath 					break;
113272fff6d3SJulian Harnath 				}
113372fff6d3SJulian Harnath 
113472fff6d3SJulian Harnath 				message.AddInt32("list_id", listID);
113572fff6d3SJulian Harnath 				messenger.SendMessage(&message);
113672fff6d3SJulian Harnath 				message.MakeEmpty();
113772fff6d3SJulian Harnath 				packagesInMessage = 0;
113872fff6d3SJulian Harnath 
113972fff6d3SJulian Harnath 				// Don't spam the window's message queue, which would make it
114072fff6d3SJulian Harnath 				// unresponsive (i.e. allows UI messages to get in between our
114172fff6d3SJulian Harnath 				// messages). When it has processed the message we just sent,
114272fff6d3SJulian Harnath 				// it will let us know by releasing the semaphore.
114372fff6d3SJulian Harnath 				acquire_sem(window->fShowPackagesAcknowledgeSem);
114472fff6d3SJulian Harnath 			}
114572fff6d3SJulian Harnath 			package->AcquireReference();
114672fff6d3SJulian Harnath 			message.AddPointer("package_ref", package.Get());
114772fff6d3SJulian Harnath 			packagesInMessage++;
114872fff6d3SJulian Harnath 		}
114972fff6d3SJulian Harnath 
115072fff6d3SJulian Harnath 		if (listIsOutdated)
115172fff6d3SJulian Harnath 			continue;
115272fff6d3SJulian Harnath 
115372fff6d3SJulian Harnath 		// Send remaining package infos, if any, which didn't make it into
115472fff6d3SJulian Harnath 		// the last message (count < kPackagesPerUpdate)
115572fff6d3SJulian Harnath 		if (packagesInMessage > 0) {
115672fff6d3SJulian Harnath 			message.AddInt32("list_id", listID);
115772fff6d3SJulian Harnath 			messenger.SendMessage(&message);
115872fff6d3SJulian Harnath 			acquire_sem(window->fShowPackagesAcknowledgeSem);
115972fff6d3SJulian Harnath 		}
116072fff6d3SJulian Harnath 
116172fff6d3SJulian Harnath 		// Update selected package in list views
116272fff6d3SJulian Harnath 		messenger.SendMessage(MSG_UPDATE_SELECTED_PACKAGE);
116372fff6d3SJulian Harnath 	}
116472fff6d3SJulian Harnath 
116572fff6d3SJulian Harnath 	return 0;
116672fff6d3SJulian Harnath }
116772fff6d3SJulian Harnath 
116872fff6d3SJulian Harnath 
116904c9d578SStephan Aßmus void
11701bbda92dSStephan Aßmus MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage)
117104c9d578SStephan Aßmus {
117204c9d578SStephan Aßmus 	UserLoginWindow* window = new UserLoginWindow(this,
117304c9d578SStephan Aßmus 		BRect(0, 0, 500, 400), fModel);
11741bbda92dSStephan Aßmus 
11751bbda92dSStephan Aßmus 	if (onSuccessMessage.what != 0)
11761bbda92dSStephan Aßmus 		window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage);
11771bbda92dSStephan Aßmus 
117804c9d578SStephan Aßmus 	window->Show();
117904c9d578SStephan Aßmus }
118004c9d578SStephan Aßmus 
11818f03a0f9SStephan Aßmus 
11828f03a0f9SStephan Aßmus void
11838f03a0f9SStephan Aßmus MainWindow::_UpdateAuthorization()
11848f03a0f9SStephan Aßmus {
11858f03a0f9SStephan Aßmus 	BString username(fModel.Username());
1186e0a26dceSStephan Aßmus 	bool hasUser = !username.IsEmpty();
1187e0a26dceSStephan Aßmus 
11886d519fd1SStephan Aßmus 	if (fLogOutItem != NULL)
1189e0a26dceSStephan Aßmus 		fLogOutItem->SetEnabled(hasUser);
1190e0a26dceSStephan Aßmus 	if (fLogInItem != NULL) {
1191e0a26dceSStephan Aßmus 		if (hasUser)
1192e0a26dceSStephan Aßmus 			fLogInItem->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS));
1193e0a26dceSStephan Aßmus 		else
1194e0a26dceSStephan Aßmus 			fLogInItem->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS));
1195e0a26dceSStephan Aßmus 	}
1196e0a26dceSStephan Aßmus 
1197e0a26dceSStephan Aßmus 	if (fUserMenu != NULL) {
1198e0a26dceSStephan Aßmus 		BString label;
1199e0a26dceSStephan Aßmus 		if (username.Length() == 0) {
1200e0a26dceSStephan Aßmus 			label = B_TRANSLATE("Not logged in");
1201e0a26dceSStephan Aßmus 		} else {
1202e0a26dceSStephan Aßmus 			label = B_TRANSLATE("Logged in as %User%");
1203e0a26dceSStephan Aßmus 			label.ReplaceAll("%User%", username);
1204e0a26dceSStephan Aßmus 		}
1205e0a26dceSStephan Aßmus 		fUserMenu->Superitem()->SetLabel(label);
1206e0a26dceSStephan Aßmus 	}
12078f03a0f9SStephan Aßmus }
12081bbda92dSStephan Aßmus 
12091bbda92dSStephan Aßmus 
12101bbda92dSStephan Aßmus void
1211b965ce70SHumdinger MainWindow::_UpdateAvailableRepositories()
1212b965ce70SHumdinger {
1213b965ce70SHumdinger 	fRepositoryMenu->RemoveItems(0, fRepositoryMenu->CountItems(), true);
1214b965ce70SHumdinger 
1215b965ce70SHumdinger 	fRepositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All repositories"),
1216b965ce70SHumdinger 		new BMessage(MSG_DEPOT_SELECTED)));
1217b965ce70SHumdinger 
1218b965ce70SHumdinger 	fRepositoryMenu->AddItem(new BSeparatorItem());
1219b965ce70SHumdinger 
1220b965ce70SHumdinger 	bool foundSelectedDepot = false;
1221b965ce70SHumdinger 	const DepotList& depots = fModel.Depots();
1222b965ce70SHumdinger 	for (int i = 0; i < depots.CountItems(); i++) {
1223b965ce70SHumdinger 		const DepotInfo& depot = depots.ItemAtFast(i);
1224d13c3d89SAndrew Lindesay 
1225d13c3d89SAndrew Lindesay 		if (depot.Name().Length() != 0) {
1226b965ce70SHumdinger 			BMessage* message = new BMessage(MSG_DEPOT_SELECTED);
1227b965ce70SHumdinger 			message->AddString("name", depot.Name());
1228b965ce70SHumdinger 			BMenuItem* item = new BMenuItem(depot.Name(), message);
1229b965ce70SHumdinger 			fRepositoryMenu->AddItem(item);
1230b965ce70SHumdinger 
1231b965ce70SHumdinger 			if (depot.Name() == fModel.Depot()) {
1232b965ce70SHumdinger 				item->SetMarked(true);
1233b965ce70SHumdinger 				foundSelectedDepot = true;
1234b965ce70SHumdinger 			}
1235b965ce70SHumdinger 		}
1236d13c3d89SAndrew Lindesay 	}
1237b965ce70SHumdinger 
1238b965ce70SHumdinger 	if (!foundSelectedDepot)
1239b965ce70SHumdinger 		fRepositoryMenu->ItemAt(0)->SetMarked(true);
1240b965ce70SHumdinger }
1241b965ce70SHumdinger 
1242b965ce70SHumdinger 
1243a9edb9bfSAndrew Lindesay bool
1244a9edb9bfSAndrew Lindesay MainWindow::_SelectedPackageHasWebAppRepositoryCode()
1245a9edb9bfSAndrew Lindesay {
1246a9edb9bfSAndrew Lindesay 	const PackageInfoRef& package = fPackageInfoView->Package();
12476ab878c0SAndrew Lindesay 	const BString depotName = package->DepotName();
1248a9edb9bfSAndrew Lindesay 
12496ab878c0SAndrew Lindesay 	if (depotName.IsEmpty()) {
12506ab878c0SAndrew Lindesay 		if (Logger::IsDebugEnabled()) {
12516ab878c0SAndrew Lindesay 			printf("the package [%s] has no depot name\n",
12526ab878c0SAndrew Lindesay 				package->Name().String());
12536ab878c0SAndrew Lindesay 		}
12546ab878c0SAndrew Lindesay 	} else {
12556ab878c0SAndrew Lindesay 		const DepotInfo* depot = fModel.DepotForName(depotName);
1256a9edb9bfSAndrew Lindesay 
12576ab878c0SAndrew Lindesay 		if (depot == NULL) {
12586ab878c0SAndrew Lindesay 			printf("the depot [%s] was not able to be found\n",
12596ab878c0SAndrew Lindesay 				depotName.String());
12606ab878c0SAndrew Lindesay 		} else {
12616ab878c0SAndrew Lindesay 			BString repositoryCode = depot->WebAppRepositoryCode();
1262a9edb9bfSAndrew Lindesay 
12636ab878c0SAndrew Lindesay 			if (repositoryCode.IsEmpty()) {
12646ab878c0SAndrew Lindesay 				printf("the depot [%s] has no web app repository code\n",
12656ab878c0SAndrew Lindesay 					depotName.String());
12666ab878c0SAndrew Lindesay 			} else {
12676ab878c0SAndrew Lindesay 				return true;
12686ab878c0SAndrew Lindesay 			}
12696ab878c0SAndrew Lindesay 		}
12706ab878c0SAndrew Lindesay 	}
12716ab878c0SAndrew Lindesay 
12726ab878c0SAndrew Lindesay 	return false;
1273a9edb9bfSAndrew Lindesay }
1274a9edb9bfSAndrew Lindesay 
1275a9edb9bfSAndrew Lindesay 
1276b965ce70SHumdinger void
12771bbda92dSStephan Aßmus MainWindow::_RatePackage()
12781bbda92dSStephan Aßmus {
1279a9edb9bfSAndrew Lindesay 	if (!_SelectedPackageHasWebAppRepositoryCode()) {
1280a9edb9bfSAndrew Lindesay 		BAlert* alert = new(std::nothrow) BAlert(
1281cbadcae1SHumdinger 			B_TRANSLATE("Rating not possible"),
1282cbadcae1SHumdinger 			B_TRANSLATE("This package doesn't seem to be on the HaikuDepot "
1283cbadcae1SHumdinger 				"Server, so it's not possible to create a new rating "
1284cbadcae1SHumdinger 				"or edit an existing rating."),
1285a9edb9bfSAndrew Lindesay 			B_TRANSLATE("OK"));
1286a9edb9bfSAndrew Lindesay 		alert->Go();
1287a9edb9bfSAndrew Lindesay     	return;
1288a9edb9bfSAndrew Lindesay 	}
1289a9edb9bfSAndrew Lindesay 
12901bbda92dSStephan Aßmus 	if (fModel.Username().IsEmpty()) {
12911bbda92dSStephan Aßmus 		BAlert* alert = new(std::nothrow) BAlert(
12921bbda92dSStephan Aßmus 			B_TRANSLATE("Not logged in"),
12931bbda92dSStephan Aßmus 			B_TRANSLATE("You need to be logged into an account before you "
12941bbda92dSStephan Aßmus 				"can rate packages."),
12951bbda92dSStephan Aßmus 			B_TRANSLATE("Cancel"),
12961bbda92dSStephan Aßmus 			B_TRANSLATE("Login or Create account"));
12971bbda92dSStephan Aßmus 
12981bbda92dSStephan Aßmus 		if (alert == NULL)
12991bbda92dSStephan Aßmus 			return;
13001bbda92dSStephan Aßmus 
13011bbda92dSStephan Aßmus 		int32 choice = alert->Go();
13021bbda92dSStephan Aßmus 		if (choice == 1)
13031bbda92dSStephan Aßmus 			_OpenLoginWindow(BMessage(MSG_RATE_PACKAGE));
13041bbda92dSStephan Aßmus 		return;
13051bbda92dSStephan Aßmus 	}
13061bbda92dSStephan Aßmus 
13071bbda92dSStephan Aßmus 	// TODO: Allow only one RatePackageWindow
13081bbda92dSStephan Aßmus 	// TODO: Mechanism for remembering the window frame
13091bbda92dSStephan Aßmus 	RatePackageWindow* window = new RatePackageWindow(this,
13101bbda92dSStephan Aßmus 		BRect(0, 0, 500, 400), fModel);
13111bbda92dSStephan Aßmus 	window->SetPackage(fPackageInfoView->Package());
13121bbda92dSStephan Aßmus 	window->Show();
13131bbda92dSStephan Aßmus }
1314d5ef985eSStephan Aßmus 
1315d5ef985eSStephan Aßmus 
1316d5ef985eSStephan Aßmus void
1317d5ef985eSStephan Aßmus MainWindow::_ShowScreenshot()
1318d5ef985eSStephan Aßmus {
1319d5ef985eSStephan Aßmus 	// TODO: Mechanism for remembering the window frame
1320d5ef985eSStephan Aßmus 	if (fScreenshotWindow == NULL)
1321d5ef985eSStephan Aßmus 		fScreenshotWindow = new ScreenshotWindow(this, BRect(0, 0, 500, 400));
1322d5ef985eSStephan Aßmus 
1323d5ef985eSStephan Aßmus 	if (fScreenshotWindow->LockWithTimeout(1000) != B_OK)
1324d5ef985eSStephan Aßmus 		return;
1325d5ef985eSStephan Aßmus 
1326d5ef985eSStephan Aßmus 	fScreenshotWindow->SetPackage(fPackageInfoView->Package());
1327d5ef985eSStephan Aßmus 
1328d5ef985eSStephan Aßmus 	if (fScreenshotWindow->IsHidden())
1329d5ef985eSStephan Aßmus 		fScreenshotWindow->Show();
1330d5ef985eSStephan Aßmus 	else
1331d5ef985eSStephan Aßmus 		fScreenshotWindow->Activate();
1332d5ef985eSStephan Aßmus 
1333d5ef985eSStephan Aßmus 	fScreenshotWindow->Unlock();
1334d5ef985eSStephan Aßmus }
1335