xref: /haiku/src/apps/haikudepot/ui/MainWindow.cpp (revision 363fc60bf3eb6fd69f0bc6f8448855eecfedfa9d)
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.
688af15cfSAndrew Lindesay  * Copyright 2016-2024, 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 #include "MainWindow.h"
1104c9d578SStephan Aßmus 
1204c9d578SStephan Aßmus #include <map>
133b17d8ddSAndrew Lindesay #include <vector>
1404c9d578SStephan Aßmus 
1504c9d578SStephan Aßmus #include <stdio.h>
1604c9d578SStephan Aßmus #include <Alert.h>
1704c9d578SStephan Aßmus #include <Autolock.h>
1804c9d578SStephan Aßmus #include <Application.h>
1904c9d578SStephan Aßmus #include <Button.h>
2004c9d578SStephan Aßmus #include <Catalog.h>
214b930ccaSStephan Aßmus #include <CardLayout.h>
2204c9d578SStephan Aßmus #include <LayoutBuilder.h>
2304c9d578SStephan Aßmus #include <MenuBar.h>
2404c9d578SStephan Aßmus #include <MenuItem.h>
2584674570SAndrew Lindesay #include <MessageRunner.h>
2604c9d578SStephan Aßmus #include <Messenger.h>
275bf2b6ebSBrian Hill #include <Roster.h>
28f7c2983eSAxel Dörfler #include <Screen.h>
2904c9d578SStephan Aßmus #include <ScrollView.h>
3004c9d578SStephan Aßmus #include <StringList.h>
310a9c9029SStephan Aßmus #include <StringView.h>
3204c9d578SStephan Aßmus #include <TabView.h>
3304c9d578SStephan Aßmus 
343369e03dSAndrew Lindesay #include "AppUtils.h"
3504c9d578SStephan Aßmus #include "AutoDeleter.h"
3604c9d578SStephan Aßmus #include "AutoLocker.h"
3704c9d578SStephan Aßmus #include "DecisionProvider.h"
384b930ccaSStephan Aßmus #include "FeaturedPackagesView.h"
3904c9d578SStephan Aßmus #include "FilterView.h"
40d13c3d89SAndrew Lindesay #include "Logger.h"
4104c9d578SStephan Aßmus #include "PackageInfoView.h"
4204c9d578SStephan Aßmus #include "PackageListView.h"
4304c9d578SStephan Aßmus #include "PackageManager.h"
443369e03dSAndrew Lindesay #include "ProcessCoordinator.h"
453369e03dSAndrew Lindesay #include "ProcessCoordinatorFactory.h"
4604c9d578SStephan Aßmus #include "RatePackageWindow.h"
47e0a26dceSStephan Aßmus #include "support.h"
48d5ef985eSStephan Aßmus #include "ScreenshotWindow.h"
49133ebab6SAndrew Lindesay #include "SettingsWindow.h"
5095c7b018SAndrew Lindesay #include "ShuttingDownWindow.h"
5110cd325cSAndrew Lindesay #include "ToLatestUserUsageConditionsWindow.h"
5204c9d578SStephan Aßmus #include "UserLoginWindow.h"
5301339a54SAndrew Lindesay #include "UserUsageConditionsWindow.h"
54125d42d9SJulian Harnath #include "WorkStatusView.h"
5504c9d578SStephan Aßmus 
5604c9d578SStephan Aßmus 
5704c9d578SStephan Aßmus #undef B_TRANSLATION_CONTEXT
5804c9d578SStephan Aßmus #define B_TRANSLATION_CONTEXT "MainWindow"
5904c9d578SStephan Aßmus 
6004c9d578SStephan Aßmus 
6104c9d578SStephan Aßmus enum {
625bf2b6ebSBrian Hill 	MSG_REFRESH_REPOS						= 'mrrp',
635bf2b6ebSBrian Hill 	MSG_MANAGE_REPOS						= 'mmrp',
64d144f952SAugustin Cavalier 	MSG_SOFTWARE_UPDATER					= 'mswu',
65133ebab6SAndrew Lindesay 	MSG_SETTINGS							= 'stgs',
6604c9d578SStephan Aßmus 	MSG_LOG_IN								= 'lgin',
678f03a0f9SStephan Aßmus 	MSG_AUTHORIZATION_CHANGED				= 'athc',
6889b16bb4SAndrew Lindesay 	MSG_CATEGORIES_LIST_CHANGED				= 'clic',
69da651104SStephan Aßmus 	MSG_PACKAGE_CHANGED						= 'pchd',
70409af934SAndrew Lindesay 	MSG_PROCESS_COORDINATOR_CHANGED			= 'pccd',
713369e03dSAndrew Lindesay 	MSG_WORK_STATUS_CHANGE					= 'wsch',
723369e03dSAndrew Lindesay 	MSG_WORK_STATUS_CLEAR					= 'wscl',
7384674570SAndrew Lindesay 	MSG_INCREMENT_VIEW_COUNTER				= 'icrv',
7488af15cfSAndrew Lindesay 	MSG_SCREENSHOT_CACHED					= 'ssca',
75fdec4227SStephan Aßmus 
76ccf707d0SAndrew Lindesay 	MSG_CHANGE_PACKAGE_LIST_VIEW_MODE		= 'cplm',
77fdec4227SStephan Aßmus 	MSG_SHOW_AVAILABLE_PACKAGES				= 'savl',
78fdec4227SStephan Aßmus 	MSG_SHOW_INSTALLED_PACKAGES				= 'sins',
7904c9d578SStephan Aßmus 	MSG_SHOW_SOURCE_PACKAGES				= 'ssrc',
8004c9d578SStephan Aßmus 	MSG_SHOW_DEVELOP_PACKAGES				= 'sdvl'
8104c9d578SStephan Aßmus };
8204c9d578SStephan Aßmus 
8310cd325cSAndrew Lindesay #define KEY_ERROR_STATUS				"errorStatus"
84ccf707d0SAndrew Lindesay 
8584674570SAndrew Lindesay const bigtime_t kIncrementViewCounterDelayMicros = 3 * 1000 * 1000;
8684674570SAndrew Lindesay 
87ccf707d0SAndrew Lindesay #define TAB_PROMINENT_PACKAGES	0
88ccf707d0SAndrew Lindesay #define TAB_ALL_PACKAGES		1
8904c9d578SStephan Aßmus 
9004c9d578SStephan Aßmus using namespace BPackageKit;
9104c9d578SStephan Aßmus using namespace BPackageKit::BManager::BPrivate;
9204c9d578SStephan Aßmus 
9304c9d578SStephan Aßmus 
9404c9d578SStephan Aßmus typedef std::map<BString, PackageInfoRef> PackageInfoMap;
9504c9d578SStephan Aßmus 
9604c9d578SStephan Aßmus 
9704c9d578SStephan Aßmus struct RefreshWorkerParameters {
9804c9d578SStephan Aßmus 	MainWindow* window;
9904c9d578SStephan Aßmus 	bool forceRefresh;
10004c9d578SStephan Aßmus 
10104c9d578SStephan Aßmus 	RefreshWorkerParameters(MainWindow* window, bool forceRefresh)
10204c9d578SStephan Aßmus 		:
10304c9d578SStephan Aßmus 		window(window),
10404c9d578SStephan Aßmus 		forceRefresh(forceRefresh)
10504c9d578SStephan Aßmus 	{
10604c9d578SStephan Aßmus 	}
10704c9d578SStephan Aßmus };
10804c9d578SStephan Aßmus 
10904c9d578SStephan Aßmus 
11089b16bb4SAndrew Lindesay class MainWindowModelListener : public ModelListener {
1118f03a0f9SStephan Aßmus public:
11289b16bb4SAndrew Lindesay 	MainWindowModelListener(const BMessenger& messenger)
1138f03a0f9SStephan Aßmus 		:
1148f03a0f9SStephan Aßmus 		fMessenger(messenger)
1158f03a0f9SStephan Aßmus 	{
1168f03a0f9SStephan Aßmus 	}
1178f03a0f9SStephan Aßmus 
1188f03a0f9SStephan Aßmus 	virtual void AuthorizationChanged()
1198f03a0f9SStephan Aßmus 	{
1208f03a0f9SStephan Aßmus 		if (fMessenger.IsValid())
1218f03a0f9SStephan Aßmus 			fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED);
1228f03a0f9SStephan Aßmus 	}
1238f03a0f9SStephan Aßmus 
12489b16bb4SAndrew Lindesay 	virtual void CategoryListChanged()
12589b16bb4SAndrew Lindesay 	{
12689b16bb4SAndrew Lindesay 		if (fMessenger.IsValid())
12789b16bb4SAndrew Lindesay 			fMessenger.SendMessage(MSG_CATEGORIES_LIST_CHANGED);
12889b16bb4SAndrew Lindesay 	}
12989b16bb4SAndrew Lindesay 
13088af15cfSAndrew Lindesay 	virtual void ScreenshotCached(const ScreenshotCoordinate& coordinate)
13188af15cfSAndrew Lindesay 	{
13288af15cfSAndrew Lindesay 		if (fMessenger.IsValid()) {
13388af15cfSAndrew Lindesay 			BMessage message(MSG_SCREENSHOT_CACHED);
13488af15cfSAndrew Lindesay 			if (coordinate.Archive(&message) != B_OK)
13588af15cfSAndrew Lindesay 				debugger("unable to serialize a screenshot coordinate");
13688af15cfSAndrew Lindesay 			fMessenger.SendMessage(&message);
13788af15cfSAndrew Lindesay 		}
13888af15cfSAndrew Lindesay 	}
13988af15cfSAndrew Lindesay 
1408f03a0f9SStephan Aßmus private:
1418f03a0f9SStephan Aßmus 	BMessenger	fMessenger;
1428f03a0f9SStephan Aßmus };
1438f03a0f9SStephan Aßmus 
1448f03a0f9SStephan Aßmus 
145409af934SAndrew Lindesay class MainWindowPackageInfoListener : public PackageInfoListener {
146409af934SAndrew Lindesay public:
147409af934SAndrew Lindesay 	MainWindowPackageInfoListener(MainWindow* mainWindow)
148409af934SAndrew Lindesay 		:
149409af934SAndrew Lindesay 		fMainWindow(mainWindow)
150409af934SAndrew Lindesay 	{
151409af934SAndrew Lindesay 	}
152409af934SAndrew Lindesay 
153409af934SAndrew Lindesay 	~MainWindowPackageInfoListener()
154409af934SAndrew Lindesay 	{
155409af934SAndrew Lindesay 	}
156409af934SAndrew Lindesay 
157409af934SAndrew Lindesay private:
158409af934SAndrew Lindesay 	// PackageInfoListener
159409af934SAndrew Lindesay 	virtual	void PackageChanged(const PackageInfoEvent& event)
160409af934SAndrew Lindesay 	{
161409af934SAndrew Lindesay 		fMainWindow->PackageChanged(event);
162409af934SAndrew Lindesay 	}
163409af934SAndrew Lindesay 
164409af934SAndrew Lindesay private:
165409af934SAndrew Lindesay 	MainWindow*	fMainWindow;
166409af934SAndrew Lindesay };
167409af934SAndrew Lindesay 
168409af934SAndrew Lindesay 
169f7c2983eSAxel Dörfler MainWindow::MainWindow(const BMessage& settings)
17004c9d578SStephan Aßmus 	:
171f7c2983eSAxel Dörfler 	BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
17204c9d578SStephan Aßmus 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
17304c9d578SStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
174d5ef985eSStephan Aßmus 	fScreenshotWindow(NULL),
17595c7b018SAndrew Lindesay 	fShuttingDownWindow(NULL),
176e0a26dceSStephan Aßmus 	fUserMenu(NULL),
177e0a26dceSStephan Aßmus 	fLogInItem(NULL),
178e0a26dceSStephan Aßmus 	fLogOutItem(NULL),
179976cadf1SAndrew Lindesay 	fUsersUserUsageConditionsMenuItem(NULL),
18089b16bb4SAndrew Lindesay 	fModelListener(new MainWindowModelListener(BMessenger(this)), true),
18110cd325cSAndrew Lindesay 	fCoordinator(NULL),
18295c7b018SAndrew Lindesay 	fShouldCloseWhenNoProcessesToCoordinate(false),
18384674570SAndrew Lindesay 	fSinglePackageMode(false),
18484674570SAndrew Lindesay 	fIncrementViewCounterDelayedRunner(NULL)
18504c9d578SStephan Aßmus {
18610cd325cSAndrew Lindesay 	if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK)
18710cd325cSAndrew Lindesay 		debugger("unable to create the process coordinator semaphore");
18810cd325cSAndrew Lindesay 
189409af934SAndrew Lindesay 	fPackageInfoListener = PackageInfoListenerRef(
190409af934SAndrew Lindesay 		new MainWindowPackageInfoListener(this), true);
191409af934SAndrew Lindesay 
192cbadcae1SHumdinger 	BMenuBar* menuBar = new BMenuBar("Main Menu");
19304c9d578SStephan Aßmus 	_BuildMenu(menuBar);
19404c9d578SStephan Aßmus 
195cbadcae1SHumdinger 	BMenuBar* userMenuBar = new BMenuBar("User Menu");
196e0a26dceSStephan Aßmus 	_BuildUserMenu(userMenuBar);
197e0a26dceSStephan Aßmus 	set_small_font(userMenuBar);
198e0a26dceSStephan Aßmus 	userMenuBar->SetExplicitMaxSize(BSize(B_SIZE_UNSET,
199e0a26dceSStephan Aßmus 		menuBar->MaxSize().height));
200e0a26dceSStephan Aßmus 
20104c9d578SStephan Aßmus 	fFilterView = new FilterView();
202f0e491d3SAndrew Lindesay 	fFeaturedPackagesView = new FeaturedPackagesView(fModel);
203f0e491d3SAndrew Lindesay 	fPackageListView = new PackageListView(&fModel);
204f0e491d3SAndrew Lindesay 	fPackageInfoView = new PackageInfoView(&fModel, this);
20504c9d578SStephan Aßmus 
20604c9d578SStephan Aßmus 	fSplitView = new BSplitView(B_VERTICAL, 5.0f);
20704c9d578SStephan Aßmus 
208125d42d9SJulian Harnath 	fWorkStatusView = new WorkStatusView("work status");
209125d42d9SJulian Harnath 	fPackageListView->AttachWorkStatusView(fWorkStatusView);
210125d42d9SJulian Harnath 
211e00a489bSAugustin Cavalier 	fListTabs = new TabView(BMessenger(this),
212ccf707d0SAndrew Lindesay 		BMessage(MSG_CHANGE_PACKAGE_LIST_VIEW_MODE), "list tabs");
213e00a489bSAugustin Cavalier 	fListTabs->AddTab(fFeaturedPackagesView);
214e00a489bSAugustin Cavalier 	fListTabs->AddTab(fPackageListView);
2154b930ccaSStephan Aßmus 
21604c9d578SStephan Aßmus 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
217e0a26dceSStephan Aßmus 		.AddGroup(B_HORIZONTAL, 0.0f)
218e0a26dceSStephan Aßmus 			.Add(menuBar, 1.0f)
219e0a26dceSStephan Aßmus 			.Add(userMenuBar, 0.0f)
220e0a26dceSStephan Aßmus 		.End()
22104c9d578SStephan Aßmus 		.Add(fFilterView)
22204c9d578SStephan Aßmus 		.AddSplit(fSplitView)
22304c9d578SStephan Aßmus 			.AddGroup(B_VERTICAL)
224e00a489bSAugustin Cavalier 				.Add(fListTabs)
22504c9d578SStephan Aßmus 				.SetInsets(
22604c9d578SStephan Aßmus 					B_USE_DEFAULT_SPACING, 0.0f,
22704c9d578SStephan Aßmus 					B_USE_DEFAULT_SPACING, 0.0f)
22804c9d578SStephan Aßmus 			.End()
22904c9d578SStephan Aßmus 			.Add(fPackageInfoView)
23004c9d578SStephan Aßmus 		.End()
231125d42d9SJulian Harnath 		.Add(fWorkStatusView)
23204c9d578SStephan Aßmus 	;
23304c9d578SStephan Aßmus 
23404c9d578SStephan Aßmus 	fSplitView->SetCollapsible(0, false);
23504c9d578SStephan Aßmus 	fSplitView->SetCollapsible(1, false);
23604c9d578SStephan Aßmus 
2378f03a0f9SStephan Aßmus 	fModel.AddListener(fModelListener);
2388f03a0f9SStephan Aßmus 
23904c9d578SStephan Aßmus 	BMessage columnSettings;
24004c9d578SStephan Aßmus 	if (settings.FindMessage("column settings", &columnSettings) == B_OK)
24104c9d578SStephan Aßmus 		fPackageListView->LoadState(&columnSettings);
24204c9d578SStephan Aßmus 
243ccf707d0SAndrew Lindesay 	_RestoreModelSettings(settings);
244133ebab6SAndrew Lindesay 	_MaybePromptCanShareAnonymousUserData(settings);
24504c9d578SStephan Aßmus 
246ccf707d0SAndrew Lindesay 	if (fModel.PackageListViewMode() == PROMINENT)
247ccf707d0SAndrew Lindesay 		fListTabs->Select(TAB_PROMINENT_PACKAGES);
2482997e05bSStephan Aßmus 	else
249ccf707d0SAndrew Lindesay 		fListTabs->Select(TAB_ALL_PACKAGES);
2502997e05bSStephan Aßmus 
251d2d4866dSAndrew Lindesay 	_RestoreNickname(settings);
25210cd325cSAndrew Lindesay 	_UpdateAuthorization();
253f7c2983eSAxel Dörfler 	_RestoreWindowFrame(settings);
254f7c2983eSAxel Dörfler 
2558f03a0f9SStephan Aßmus 	// start worker threads
25604c9d578SStephan Aßmus 	BPackageRoster().StartWatching(this,
25704c9d578SStephan Aßmus 		B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
25804c9d578SStephan Aßmus 
25963975654SStephan Aßmus 	_InitWorkerThreads();
2609883929bSAndrew Lindesay 	_AdoptModel();
2619883929bSAndrew Lindesay 	_StartBulkLoad();
26204c9d578SStephan Aßmus }
26304c9d578SStephan Aßmus 
26404c9d578SStephan Aßmus 
26575a70554SAndrew Lindesay /*!	This constructor is used when the application is loaded for the purpose of
26675a70554SAndrew Lindesay 	viewing an HPKG file.
26775a70554SAndrew Lindesay */
26875a70554SAndrew Lindesay 
269afe7affbSAndrew Lindesay MainWindow::MainWindow(const BMessage& settings, PackageInfoRef& package)
2706d519fd1SStephan Aßmus 	:
271f7c2983eSAxel Dörfler 	BWindow(BRect(50, 50, 650, 350), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
2726d519fd1SStephan Aßmus 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
2736d519fd1SStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
2749883929bSAndrew Lindesay 	fFeaturedPackagesView(NULL),
2759883929bSAndrew Lindesay 	fPackageListView(NULL),
276d78bfd8dSJulian Harnath 	fWorkStatusView(NULL),
277d5ef985eSStephan Aßmus 	fScreenshotWindow(NULL),
27895c7b018SAndrew Lindesay 	fShuttingDownWindow(NULL),
279e0a26dceSStephan Aßmus 	fUserMenu(NULL),
280e0a26dceSStephan Aßmus 	fLogInItem(NULL),
2816d519fd1SStephan Aßmus 	fLogOutItem(NULL),
282976cadf1SAndrew Lindesay 	fUsersUserUsageConditionsMenuItem(NULL),
28389b16bb4SAndrew Lindesay 	fModelListener(new MainWindowModelListener(BMessenger(this)), true),
28410cd325cSAndrew Lindesay 	fCoordinator(NULL),
28595c7b018SAndrew Lindesay 	fShouldCloseWhenNoProcessesToCoordinate(false),
28684674570SAndrew Lindesay 	fSinglePackageMode(true),
28784674570SAndrew Lindesay 	fIncrementViewCounterDelayedRunner(NULL)
2886d519fd1SStephan Aßmus {
28975a70554SAndrew Lindesay 	BString title = B_TRANSLATE("HaikuDepot - %PackageName% %PackageVersion%");
29075a70554SAndrew Lindesay 	title.ReplaceAll("%PackageName%", package->Name());
29175a70554SAndrew Lindesay 	title.ReplaceAll("%PackageVersion%", package->Version().ToString());
29275a70554SAndrew Lindesay 	SetTitle(title);
29375a70554SAndrew Lindesay 
29410cd325cSAndrew Lindesay 	if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK)
29510cd325cSAndrew Lindesay 		debugger("unable to create the process coordinator semaphore");
29610cd325cSAndrew Lindesay 
297409af934SAndrew Lindesay 	fPackageInfoListener = PackageInfoListenerRef(
298409af934SAndrew Lindesay 		new MainWindowPackageInfoListener(this), true);
299409af934SAndrew Lindesay 
3006d519fd1SStephan Aßmus 	fFilterView = new FilterView();
301f0e491d3SAndrew Lindesay 	fPackageInfoView = new PackageInfoView(&fModel, this);
30287016f8fSAndrew Lindesay 	fWorkStatusView = new WorkStatusView("work status");
3036d519fd1SStephan Aßmus 
3046d519fd1SStephan Aßmus 	BLayoutBuilder::Group<>(this, B_VERTICAL)
3056d519fd1SStephan Aßmus 		.Add(fPackageInfoView)
30687016f8fSAndrew Lindesay 		.Add(fWorkStatusView)
3076d519fd1SStephan Aßmus 		.SetInsets(0, B_USE_WINDOW_INSETS, 0, 0)
3086d519fd1SStephan Aßmus 	;
3096d519fd1SStephan Aßmus 
3106d519fd1SStephan Aßmus 	fModel.AddListener(fModelListener);
3116d519fd1SStephan Aßmus 
312afe7affbSAndrew Lindesay 	// add the single package into the model so that any internal
313afe7affbSAndrew Lindesay 	// business logic is able to find the package.
314afe7affbSAndrew Lindesay 	DepotInfoRef depot(new DepotInfo("single-pkg-depot"), true);
315afe7affbSAndrew Lindesay 	depot->AddPackage(package);
316afe7affbSAndrew Lindesay 	fModel.MergeOrAddDepot(depot);
317afe7affbSAndrew Lindesay 
3186d519fd1SStephan Aßmus 	// Restore settings
319d2d4866dSAndrew Lindesay 	_RestoreNickname(settings);
320ccf707d0SAndrew Lindesay 	_UpdateAuthorization();
321f7c2983eSAxel Dörfler 	_RestoreWindowFrame(settings);
3226d519fd1SStephan Aßmus 
3236d519fd1SStephan Aßmus 	fPackageInfoView->SetPackage(package);
32463975654SStephan Aßmus 
325afe7affbSAndrew Lindesay 	// start worker threads
326afe7affbSAndrew Lindesay 	BPackageRoster().StartWatching(this,
327afe7affbSAndrew Lindesay 		B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
328afe7affbSAndrew Lindesay 
32963975654SStephan Aßmus 	_InitWorkerThreads();
3306d519fd1SStephan Aßmus }
3316d519fd1SStephan Aßmus 
3326d519fd1SStephan Aßmus 
33304c9d578SStephan Aßmus MainWindow::~MainWindow()
33404c9d578SStephan Aßmus {
33510cd325cSAndrew Lindesay 	_SpinUntilProcessCoordinatorComplete();
33610cd325cSAndrew Lindesay 	delete_sem(fCoordinatorRunningSem);
33710cd325cSAndrew Lindesay 	fCoordinatorRunningSem = 0;
33810cd325cSAndrew Lindesay 
33904c9d578SStephan Aßmus 	BPackageRoster().StopWatching(this);
34004c9d578SStephan Aßmus 
34195c7b018SAndrew Lindesay 	if (fScreenshotWindow != NULL) {
34295c7b018SAndrew Lindesay 		if (fScreenshotWindow->Lock())
343d5ef985eSStephan Aßmus 			fScreenshotWindow->Quit();
34404c9d578SStephan Aßmus 	}
34504c9d578SStephan Aßmus 
34695c7b018SAndrew Lindesay 	if (fShuttingDownWindow != NULL) {
34795c7b018SAndrew Lindesay 		if (fShuttingDownWindow->Lock())
34895c7b018SAndrew Lindesay 			fShuttingDownWindow->Quit();
34995c7b018SAndrew Lindesay 	}
350d5562247SAugustin Cavalier 
351a3866854SAugustin Cavalier 	delete_sem(fPackageToPopulateSem);
352a3866854SAugustin Cavalier 	wait_for_thread(fPopulatePackageWorker, NULL);
353a3866854SAugustin Cavalier 
3544329a83fSAugustin Cavalier 	// We must clear the model early to release references.
355d5562247SAugustin Cavalier 	fModel.Clear();
35695c7b018SAndrew Lindesay }
35795c7b018SAndrew Lindesay 
35804c9d578SStephan Aßmus 
35904c9d578SStephan Aßmus bool
36004c9d578SStephan Aßmus MainWindow::QuitRequested()
36104c9d578SStephan Aßmus {
36204c9d578SStephan Aßmus 
36310cd325cSAndrew Lindesay 	_StopProcessCoordinators();
3643369e03dSAndrew Lindesay 
36595c7b018SAndrew Lindesay 	// If there are any processes in flight we need to be careful to make
36695c7b018SAndrew Lindesay 	// sure that they are cleanly completed before actually quitting.  By
36795c7b018SAndrew Lindesay 	// turning on the `fShouldCloseWhenNoProcessesToCoordinate` flag, when
36895c7b018SAndrew Lindesay 	// the process coordination has completed then it will detect this and
36995c7b018SAndrew Lindesay 	// quit again.
37095c7b018SAndrew Lindesay 
37195c7b018SAndrew Lindesay 	{
37295c7b018SAndrew Lindesay 		AutoLocker<BLocker> lock(&fCoordinatorLock);
37395c7b018SAndrew Lindesay 		fShouldCloseWhenNoProcessesToCoordinate = true;
37495c7b018SAndrew Lindesay 
375409af934SAndrew Lindesay 		if (fCoordinator != NULL) {
37695c7b018SAndrew Lindesay 			HDINFO("a coordinator is running --> will wait before quitting...");
37795c7b018SAndrew Lindesay 
37895c7b018SAndrew Lindesay 			if (fShuttingDownWindow == NULL)
37995c7b018SAndrew Lindesay 				fShuttingDownWindow = new ShuttingDownWindow(this);
38095c7b018SAndrew Lindesay 			fShuttingDownWindow->Show();
38195c7b018SAndrew Lindesay 
38295c7b018SAndrew Lindesay 			return false;
38395c7b018SAndrew Lindesay 		}
38495c7b018SAndrew Lindesay 	}
38595c7b018SAndrew Lindesay 
38695c7b018SAndrew Lindesay 	BMessage settings;
38795c7b018SAndrew Lindesay 	StoreSettings(settings);
38895c7b018SAndrew Lindesay 	BMessage message(MSG_MAIN_WINDOW_CLOSED);
38995c7b018SAndrew Lindesay 	message.AddMessage(KEY_WINDOW_SETTINGS, &settings);
39095c7b018SAndrew Lindesay 	be_app->PostMessage(&message);
39195c7b018SAndrew Lindesay 
39295c7b018SAndrew Lindesay 	if (fShuttingDownWindow != NULL) {
39395c7b018SAndrew Lindesay 		if (fShuttingDownWindow->Lock())
39495c7b018SAndrew Lindesay 			fShuttingDownWindow->Quit();
39595c7b018SAndrew Lindesay 		fShuttingDownWindow = NULL;
39695c7b018SAndrew Lindesay 	}
39795c7b018SAndrew Lindesay 
39804c9d578SStephan Aßmus 	return true;
39904c9d578SStephan Aßmus }
40004c9d578SStephan Aßmus 
40104c9d578SStephan Aßmus 
40204c9d578SStephan Aßmus void
40304c9d578SStephan Aßmus MainWindow::MessageReceived(BMessage* message)
40404c9d578SStephan Aßmus {
40504c9d578SStephan Aßmus 	switch (message->what) {
4063369e03dSAndrew Lindesay 		case MSG_BULK_LOAD_DONE:
40710cd325cSAndrew Lindesay 		{
40810cd325cSAndrew Lindesay 			int64 errorStatus64;
40910cd325cSAndrew Lindesay 			if (message->FindInt64(KEY_ERROR_STATUS, &errorStatus64) == B_OK)
41010cd325cSAndrew Lindesay 				_BulkLoadCompleteReceived((status_t) errorStatus64);
41110cd325cSAndrew Lindesay 			else
412fa5c8097SAndrew Lindesay 				HDERROR("expected [%s] value in message", KEY_ERROR_STATUS);
41304c9d578SStephan Aßmus 			break;
41410cd325cSAndrew Lindesay 		}
41504c9d578SStephan Aßmus 		case B_SIMPLE_DATA:
41604c9d578SStephan Aßmus 		case B_REFS_RECEIVED:
41704c9d578SStephan Aßmus 			// TODO: ?
41804c9d578SStephan Aßmus 			break;
41904c9d578SStephan Aßmus 
42004c9d578SStephan Aßmus 		case B_PACKAGE_UPDATE:
421f32d5c5cSAndrew Lindesay 			_HandleExternalPackageUpdateMessageReceived(message);
42204c9d578SStephan Aßmus 			break;
42304c9d578SStephan Aßmus 
4245bf2b6ebSBrian Hill 		case MSG_REFRESH_REPOS:
4253369e03dSAndrew Lindesay 			_StartBulkLoad(true);
4263369e03dSAndrew Lindesay 			break;
4273369e03dSAndrew Lindesay 
42810cd325cSAndrew Lindesay 		case MSG_WORK_STATUS_CLEAR:
42910cd325cSAndrew Lindesay 			_HandleWorkStatusClear();
43010cd325cSAndrew Lindesay 			break;
43110cd325cSAndrew Lindesay 
4323369e03dSAndrew Lindesay 		case MSG_WORK_STATUS_CHANGE:
4333369e03dSAndrew Lindesay 			_HandleWorkStatusChangeMessageReceived(message);
43404c9d578SStephan Aßmus 			break;
43504c9d578SStephan Aßmus 
4365bf2b6ebSBrian Hill 		case MSG_MANAGE_REPOS:
4375bf2b6ebSBrian Hill 			be_roster->Launch("application/x-vnd.Haiku-Repositories");
4385bf2b6ebSBrian Hill 			break;
4395bf2b6ebSBrian Hill 
440d144f952SAugustin Cavalier 		case MSG_SOFTWARE_UPDATER:
441d144f952SAugustin Cavalier 			be_roster->Launch("application/x-vnd.haiku-softwareupdater");
442d144f952SAugustin Cavalier 			break;
443d144f952SAugustin Cavalier 
44404c9d578SStephan Aßmus 		case MSG_LOG_IN:
4451bbda92dSStephan Aßmus 			_OpenLoginWindow(BMessage());
44604c9d578SStephan Aßmus 			break;
44704c9d578SStephan Aßmus 
448133ebab6SAndrew Lindesay 		case MSG_SETTINGS:
449133ebab6SAndrew Lindesay 			_OpenSettingsWindow();
450133ebab6SAndrew Lindesay 			break;
451133ebab6SAndrew Lindesay 
4528f03a0f9SStephan Aßmus 		case MSG_LOG_OUT:
453d2d4866dSAndrew Lindesay 			fModel.SetNickname("");
4548f03a0f9SStephan Aßmus 			break;
4558f03a0f9SStephan Aßmus 
45601339a54SAndrew Lindesay 		case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS:
457d2d4866dSAndrew Lindesay 			_ViewUserUsageConditions(LATEST);
458d2d4866dSAndrew Lindesay 			break;
459d2d4866dSAndrew Lindesay 
460d2d4866dSAndrew Lindesay 		case MSG_VIEW_USERS_USER_USAGE_CONDITIONS:
461d2d4866dSAndrew Lindesay 			_ViewUserUsageConditions(USER);
46201339a54SAndrew Lindesay 			break;
46301339a54SAndrew Lindesay 
4648f03a0f9SStephan Aßmus 		case MSG_AUTHORIZATION_CHANGED:
46510cd325cSAndrew Lindesay 			_StartUserVerify();
4668f03a0f9SStephan Aßmus 			_UpdateAuthorization();
4678f03a0f9SStephan Aßmus 			break;
4688f03a0f9SStephan Aßmus 
46988af15cfSAndrew Lindesay 		case MSG_SCREENSHOT_CACHED:
47088af15cfSAndrew Lindesay 			_HandleScreenshotCached(message);
47188af15cfSAndrew Lindesay 			break;
47288af15cfSAndrew Lindesay 
47389b16bb4SAndrew Lindesay 		case MSG_CATEGORIES_LIST_CHANGED:
47489b16bb4SAndrew Lindesay 			fFilterView->AdoptModel(fModel);
47589b16bb4SAndrew Lindesay 			break;
47689b16bb4SAndrew Lindesay 
477ccf707d0SAndrew Lindesay 		case MSG_CHANGE_PACKAGE_LIST_VIEW_MODE:
478ccf707d0SAndrew Lindesay 			_HandleChangePackageListViewMode();
4794fade61fSStephan Aßmus 			break;
4804fade61fSStephan Aßmus 
481fdec4227SStephan Aßmus 		case MSG_SHOW_AVAILABLE_PACKAGES:
482fdec4227SStephan Aßmus 			{
483fdec4227SStephan Aßmus 				BAutolock locker(fModel.Lock());
484*363fc60bSAndrew Lindesay 				PackageFilterModel* filterModel = fModel.PackageFilter();
485*363fc60bSAndrew Lindesay 				filterModel->SetShowAvailablePackages(!filterModel->ShowAvailablePackages());
486fdec4227SStephan Aßmus 			}
487fdec4227SStephan Aßmus 			_AdoptModel();
488fdec4227SStephan Aßmus 			break;
489fdec4227SStephan Aßmus 
490fdec4227SStephan Aßmus 		case MSG_SHOW_INSTALLED_PACKAGES:
491fdec4227SStephan Aßmus 			{
492fdec4227SStephan Aßmus 				BAutolock locker(fModel.Lock());
493*363fc60bSAndrew Lindesay 				PackageFilterModel* filterModel = fModel.PackageFilter();
494*363fc60bSAndrew Lindesay 				filterModel->SetShowInstalledPackages(!filterModel->ShowInstalledPackages());
495fdec4227SStephan Aßmus 			}
496fdec4227SStephan Aßmus 			_AdoptModel();
497fdec4227SStephan Aßmus 			break;
498fdec4227SStephan Aßmus 
49904c9d578SStephan Aßmus 		case MSG_SHOW_SOURCE_PACKAGES:
50004c9d578SStephan Aßmus 			{
50104c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
502*363fc60bSAndrew Lindesay 				PackageFilterModel* filterModel = fModel.PackageFilter();
503*363fc60bSAndrew Lindesay 				filterModel->SetShowSourcePackages(!filterModel->ShowSourcePackages());
50404c9d578SStephan Aßmus 			}
50504c9d578SStephan Aßmus 			_AdoptModel();
50604c9d578SStephan Aßmus 			break;
50704c9d578SStephan Aßmus 
50804c9d578SStephan Aßmus 		case MSG_SHOW_DEVELOP_PACKAGES:
50904c9d578SStephan Aßmus 			{
51004c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
511*363fc60bSAndrew Lindesay 				PackageFilterModel* filterModel = fModel.PackageFilter();
512*363fc60bSAndrew Lindesay 				filterModel->SetShowDevelopPackages(!filterModel->ShowDevelopPackages());
51304c9d578SStephan Aßmus 			}
51404c9d578SStephan Aßmus 			_AdoptModel();
51504c9d578SStephan Aßmus 			break;
51604c9d578SStephan Aßmus 
517a9edb9bfSAndrew Lindesay 			// this may be triggered by, for example, a user rating being added
518a9edb9bfSAndrew Lindesay 			// or having been altered.
519a9edb9bfSAndrew Lindesay 		case MSG_SERVER_DATA_CHANGED:
520a9edb9bfSAndrew Lindesay 		{
521a9edb9bfSAndrew Lindesay 			BString name;
522a9edb9bfSAndrew Lindesay 			if (message->FindString("name", &name) == B_OK) {
523a9edb9bfSAndrew Lindesay 				BAutolock locker(fModel.Lock());
524a9edb9bfSAndrew Lindesay 				if (fPackageInfoView->Package()->Name() == name) {
525a9edb9bfSAndrew Lindesay 					_PopulatePackageAsync(true);
526a9edb9bfSAndrew Lindesay 				} else {
527f96d1f4dSAndrew Lindesay 					HDDEBUG("pkg [%s] is updated on the server, but is "
528f96d1f4dSAndrew Lindesay 						"not selected so will not be updated.",
529fa5c8097SAndrew Lindesay 						name.String());
530a9edb9bfSAndrew Lindesay 				}
531a9edb9bfSAndrew Lindesay 			}
532a9edb9bfSAndrew Lindesay 			break;
533a9edb9bfSAndrew Lindesay 		}
534a9edb9bfSAndrew Lindesay 
53584674570SAndrew Lindesay 		case MSG_INCREMENT_VIEW_COUNTER:
53684674570SAndrew Lindesay 			_HandleIncrementViewCounter(message);
53784674570SAndrew Lindesay 			break;
53884674570SAndrew Lindesay 
53904c9d578SStephan Aßmus 		case MSG_PACKAGE_SELECTED:
54004c9d578SStephan Aßmus 		{
54172992391SStephan Aßmus 			BString name;
54272992391SStephan Aßmus 			if (message->FindString("name", &name) == B_OK) {
543c4199192SAndrew Lindesay 				PackageInfoRef package;
544c4199192SAndrew Lindesay 				{
54575ba9dbfSStephan Aßmus 					BAutolock locker(fModel.Lock());
546c4199192SAndrew Lindesay 					package = fModel.PackageForName(name);
547c4199192SAndrew Lindesay 				}
5486f07feadSAndrew Lindesay 				if (!package.IsSet() || name != package->Name())
549c4199192SAndrew Lindesay 					debugger("unable to find the named package");
550133ebab6SAndrew Lindesay 				else {
55104c9d578SStephan Aßmus 					_AdoptPackage(package);
55284674570SAndrew Lindesay 					_SetupDelayedIncrementViewCounter(package);
553133ebab6SAndrew Lindesay 				}
55404c9d578SStephan Aßmus 			} else {
55504c9d578SStephan Aßmus 				_ClearPackage();
55604c9d578SStephan Aßmus 			}
55704c9d578SStephan Aßmus 			break;
55804c9d578SStephan Aßmus 		}
55904c9d578SStephan Aßmus 
56004c9d578SStephan Aßmus 		case MSG_CATEGORY_SELECTED:
56104c9d578SStephan Aßmus 		{
56289b16bb4SAndrew Lindesay 			BString code;
56389b16bb4SAndrew Lindesay 			if (message->FindString("code", &code) != B_OK)
56489b16bb4SAndrew Lindesay 				code = "";
56504c9d578SStephan Aßmus 			{
56604c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
567*363fc60bSAndrew Lindesay 				fModel.PackageFilter()->SetCategory(code);
56804c9d578SStephan Aßmus 			}
56904c9d578SStephan Aßmus 			_AdoptModel();
57004c9d578SStephan Aßmus 			break;
57104c9d578SStephan Aßmus 		}
57204c9d578SStephan Aßmus 
57304c9d578SStephan Aßmus 		case MSG_DEPOT_SELECTED:
57404c9d578SStephan Aßmus 		{
57504c9d578SStephan Aßmus 			BString name;
57604c9d578SStephan Aßmus 			if (message->FindString("name", &name) != B_OK)
57704c9d578SStephan Aßmus 				name = "";
57804c9d578SStephan Aßmus 			{
57904c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
580*363fc60bSAndrew Lindesay 				fModel.PackageFilter()->SetDepotName(name);
58104c9d578SStephan Aßmus 			}
58204c9d578SStephan Aßmus 			_AdoptModel();
583b965ce70SHumdinger 			_UpdateAvailableRepositories();
58404c9d578SStephan Aßmus 			break;
58504c9d578SStephan Aßmus 		}
58604c9d578SStephan Aßmus 
58704c9d578SStephan Aßmus 		case MSG_SEARCH_TERMS_MODIFIED:
58804c9d578SStephan Aßmus 		{
58904c9d578SStephan Aßmus 			// TODO: Do this with a delay!
59004c9d578SStephan Aßmus 			BString searchTerms;
59104c9d578SStephan Aßmus 			if (message->FindString("search terms", &searchTerms) != B_OK)
59204c9d578SStephan Aßmus 				searchTerms = "";
59304c9d578SStephan Aßmus 			{
59404c9d578SStephan Aßmus 				BAutolock locker(fModel.Lock());
595*363fc60bSAndrew Lindesay 				fModel.PackageFilter()->SetSearchTerms(searchTerms);
59604c9d578SStephan Aßmus 			}
59704c9d578SStephan Aßmus 			_AdoptModel();
59804c9d578SStephan Aßmus 			break;
59904c9d578SStephan Aßmus 		}
60004c9d578SStephan Aßmus 
601da651104SStephan Aßmus 		case MSG_PACKAGE_CHANGED:
60204c9d578SStephan Aßmus 		{
60304c9d578SStephan Aßmus 			PackageInfo* info;
60404c9d578SStephan Aßmus 			if (message->FindPointer("package", (void**)&info) == B_OK) {
60504c9d578SStephan Aßmus 				PackageInfoRef ref(info, true);
6060b69420bSAndrew Lindesay 				fFeaturedPackagesView->BeginAddRemove();
607c4199192SAndrew Lindesay 				_AddRemovePackageFromLists(ref);
6080b69420bSAndrew Lindesay 				fFeaturedPackagesView->EndAddRemove();
60910cd325cSAndrew Lindesay 			}
61004c9d578SStephan Aßmus 			break;
61104c9d578SStephan Aßmus 		}
61204c9d578SStephan Aßmus 
613409af934SAndrew Lindesay 		case MSG_PROCESS_COORDINATOR_CHANGED:
614409af934SAndrew Lindesay 		{
615409af934SAndrew Lindesay 			ProcessCoordinatorState state(message);
616409af934SAndrew Lindesay 			_HandleProcessCoordinatorChanged(state);
617409af934SAndrew Lindesay 			break;
618409af934SAndrew Lindesay 		}
619409af934SAndrew Lindesay 
62004c9d578SStephan Aßmus 		case MSG_RATE_PACKAGE:
6211bbda92dSStephan Aßmus 			_RatePackage();
62204c9d578SStephan Aßmus 			break;
62304c9d578SStephan Aßmus 
624d5ef985eSStephan Aßmus 		case MSG_SHOW_SCREENSHOT:
625d5ef985eSStephan Aßmus 			_ShowScreenshot();
626d5ef985eSStephan Aßmus 			break;
627d5ef985eSStephan Aßmus 
628125d42d9SJulian Harnath 		case MSG_PACKAGE_WORKER_BUSY:
629125d42d9SJulian Harnath 		{
630125d42d9SJulian Harnath 			BString reason;
631125d42d9SJulian Harnath 			status_t status = message->FindString("reason", &reason);
632125d42d9SJulian Harnath 			if (status != B_OK)
633125d42d9SJulian Harnath 				break;
634125d42d9SJulian Harnath 			fWorkStatusView->SetBusy(reason);
635125d42d9SJulian Harnath 			break;
636125d42d9SJulian Harnath 		}
637125d42d9SJulian Harnath 
638125d42d9SJulian Harnath 		case MSG_PACKAGE_WORKER_IDLE:
639125d42d9SJulian Harnath 			fWorkStatusView->SetIdle();
640125d42d9SJulian Harnath 			break;
641125d42d9SJulian Harnath 
64210cd325cSAndrew Lindesay 		case MSG_USER_USAGE_CONDITIONS_NOT_LATEST:
64310cd325cSAndrew Lindesay 		{
64410cd325cSAndrew Lindesay 			BMessage userDetailMsg;
64510cd325cSAndrew Lindesay 			if (message->FindMessage("userDetail", &userDetailMsg) != B_OK) {
64610cd325cSAndrew Lindesay 				debugger("expected the [userDetail] data to be carried in the "
64710cd325cSAndrew Lindesay 					"message.");
64810cd325cSAndrew Lindesay 			}
64910cd325cSAndrew Lindesay 			UserDetail userDetail(&userDetailMsg);
65010cd325cSAndrew Lindesay 			_HandleUserUsageConditionsNotLatest(userDetail);
65110cd325cSAndrew Lindesay 			break;
65210cd325cSAndrew Lindesay 		}
65310cd325cSAndrew Lindesay 
65404c9d578SStephan Aßmus 		default:
65504c9d578SStephan Aßmus 			BWindow::MessageReceived(message);
65604c9d578SStephan Aßmus 			break;
65704c9d578SStephan Aßmus 	}
65804c9d578SStephan Aßmus }
65904c9d578SStephan Aßmus 
66004c9d578SStephan Aßmus 
661ccf707d0SAndrew Lindesay static const char*
662ccf707d0SAndrew Lindesay main_window_package_list_view_mode_str(package_list_view_mode mode)
663ccf707d0SAndrew Lindesay {
664ccf707d0SAndrew Lindesay 	if (mode == PROMINENT)
665ccf707d0SAndrew Lindesay 		return "PROMINENT";
666ccf707d0SAndrew Lindesay 	return "ALL";
667ccf707d0SAndrew Lindesay }
668ccf707d0SAndrew Lindesay 
669ccf707d0SAndrew Lindesay 
670ccf707d0SAndrew Lindesay static package_list_view_mode
671ccf707d0SAndrew Lindesay main_window_str_to_package_list_view_mode(const BString& str)
672ccf707d0SAndrew Lindesay {
673ccf707d0SAndrew Lindesay 	if (str == "PROMINENT")
674ccf707d0SAndrew Lindesay 		return PROMINENT;
675ccf707d0SAndrew Lindesay 	return ALL;
676ccf707d0SAndrew Lindesay }
677ccf707d0SAndrew Lindesay 
678ccf707d0SAndrew Lindesay 
67904c9d578SStephan Aßmus void
6804b347fccSAndrew Lindesay MainWindow::StoreSettings(BMessage& settings)
68104c9d578SStephan Aßmus {
682f7c2983eSAxel Dörfler 	settings.AddRect(_WindowFrameName(), Frame());
683f7c2983eSAxel Dörfler 	if (!fSinglePackageMode) {
68404c9d578SStephan Aßmus 		settings.AddRect("window frame", Frame());
68504c9d578SStephan Aßmus 
68604c9d578SStephan Aßmus 		BMessage columnSettings;
6879883929bSAndrew Lindesay 		if (fPackageListView != NULL)
68804c9d578SStephan Aßmus 			fPackageListView->SaveState(&columnSettings);
68904c9d578SStephan Aßmus 
69004c9d578SStephan Aßmus 		settings.AddMessage("column settings", &columnSettings);
69104c9d578SStephan Aßmus 
692133ebab6SAndrew Lindesay 		settings.AddString(SETTING_PACKAGE_LIST_VIEW_MODE,
693ccf707d0SAndrew Lindesay 			main_window_package_list_view_mode_str(
694ccf707d0SAndrew Lindesay 				fModel.PackageListViewMode()));
695*363fc60bSAndrew Lindesay 
696133ebab6SAndrew Lindesay 		settings.AddBool(SETTING_SHOW_AVAILABLE_PACKAGES,
697*363fc60bSAndrew Lindesay 			fModel.PackageFilter()->ShowAvailablePackages());
698133ebab6SAndrew Lindesay 		settings.AddBool(SETTING_SHOW_INSTALLED_PACKAGES,
699*363fc60bSAndrew Lindesay 			fModel.PackageFilter()->ShowInstalledPackages());
700133ebab6SAndrew Lindesay 		settings.AddBool(SETTING_SHOW_DEVELOP_PACKAGES,
701*363fc60bSAndrew Lindesay 			fModel.PackageFilter()->ShowDevelopPackages());
702133ebab6SAndrew Lindesay 		settings.AddBool(SETTING_SHOW_SOURCE_PACKAGES,
703*363fc60bSAndrew Lindesay 			fModel.PackageFilter()->ShowSourcePackages());
704*363fc60bSAndrew Lindesay 
705133ebab6SAndrew Lindesay 		settings.AddBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA,
706133ebab6SAndrew Lindesay 			fModel.CanShareAnonymousUsageData());
7076d519fd1SStephan Aßmus 	}
7088f03a0f9SStephan Aßmus 
709d2d4866dSAndrew Lindesay 	settings.AddString("username", fModel.Nickname());
71004c9d578SStephan Aßmus }
71104c9d578SStephan Aßmus 
71204c9d578SStephan Aßmus 
71304c9d578SStephan Aßmus void
714a18f873fSAndrew Lindesay MainWindow::Consume(ProcessCoordinator *item)
715a18f873fSAndrew Lindesay {
716a18f873fSAndrew Lindesay 	_AddProcessCoordinator(item);
717a18f873fSAndrew Lindesay }
718a18f873fSAndrew Lindesay 
719a18f873fSAndrew Lindesay 
720a18f873fSAndrew Lindesay void
72104c9d578SStephan Aßmus MainWindow::PackageChanged(const PackageInfoEvent& event)
72204c9d578SStephan Aßmus {
723a9edb9bfSAndrew Lindesay 	uint32 watchedChanges = PKG_CHANGED_STATE | PKG_CHANGED_PROMINENCE;
724a9edb9bfSAndrew Lindesay 	if ((event.Changes() & watchedChanges) != 0) {
72504c9d578SStephan Aßmus 		PackageInfoRef ref(event.Package());
726da651104SStephan Aßmus 		BMessage message(MSG_PACKAGE_CHANGED);
72704c9d578SStephan Aßmus 		message.AddPointer("package", ref.Get());
72804c9d578SStephan Aßmus 		ref.Detach();
72904c9d578SStephan Aßmus 			// reference needs to be released by MessageReceived();
73004c9d578SStephan Aßmus 		PostMessage(&message);
73104c9d578SStephan Aßmus 	}
73204c9d578SStephan Aßmus }
73304c9d578SStephan Aßmus 
73404c9d578SStephan Aßmus 
73504c9d578SStephan Aßmus void
73604c9d578SStephan Aßmus MainWindow::_BuildMenu(BMenuBar* menuBar)
73704c9d578SStephan Aßmus {
7380b6928f1SHumdinger 	BMenu* menu = new BMenu(B_TRANSLATE_SYSTEM_NAME("HaikuDepot"));
7393369e03dSAndrew Lindesay 	fRefreshRepositoriesItem = new BMenuItem(
7403369e03dSAndrew Lindesay 		B_TRANSLATE("Refresh repositories"), new BMessage(MSG_REFRESH_REPOS));
7413369e03dSAndrew Lindesay 	menu->AddItem(fRefreshRepositoriesItem);
742d7f78130SHumdinger 	menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"
743d7f78130SHumdinger 		B_UTF8_ELLIPSIS), new BMessage(MSG_MANAGE_REPOS)));
744d144f952SAugustin Cavalier 	menu->AddItem(new BMenuItem(B_TRANSLATE("Check for updates"
745d144f952SAugustin Cavalier 		B_UTF8_ELLIPSIS), new BMessage(MSG_SOFTWARE_UPDATER)));
7460b6928f1SHumdinger 	menu->AddSeparatorItem();
7470b6928f1SHumdinger 	menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS),
748e1ae57abSJoachim Mairböck 		new BMessage(MSG_SETTINGS), ','));
7490b6928f1SHumdinger 	menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
7500b6928f1SHumdinger 		new BMessage(B_QUIT_REQUESTED), 'Q'));
75104c9d578SStephan Aßmus 	menuBar->AddItem(menu);
75204c9d578SStephan Aßmus 
753b965ce70SHumdinger 	fRepositoryMenu = new BMenu(B_TRANSLATE("Repositories"));
754b965ce70SHumdinger 	menuBar->AddItem(fRepositoryMenu);
755b965ce70SHumdinger 
7564fade61fSStephan Aßmus 	menu = new BMenu(B_TRANSLATE("Show"));
7574fade61fSStephan Aßmus 
758fdec4227SStephan Aßmus 	fShowAvailablePackagesItem = new BMenuItem(
7594fade61fSStephan Aßmus 		B_TRANSLATE("Available packages"),
760fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_AVAILABLE_PACKAGES));
761fdec4227SStephan Aßmus 	menu->AddItem(fShowAvailablePackagesItem);
762fdec4227SStephan Aßmus 
763fdec4227SStephan Aßmus 	fShowInstalledPackagesItem = new BMenuItem(
7644fade61fSStephan Aßmus 		B_TRANSLATE("Installed packages"),
765fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_INSTALLED_PACKAGES));
766fdec4227SStephan Aßmus 	menu->AddItem(fShowInstalledPackagesItem);
767fdec4227SStephan Aßmus 
768fdec4227SStephan Aßmus 	menu->AddSeparatorItem();
769fdec4227SStephan Aßmus 
770fdec4227SStephan Aßmus 	fShowDevelopPackagesItem = new BMenuItem(
7714fade61fSStephan Aßmus 		B_TRANSLATE("Develop packages"),
772fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_DEVELOP_PACKAGES));
773fdec4227SStephan Aßmus 	menu->AddItem(fShowDevelopPackagesItem);
774fdec4227SStephan Aßmus 
775fdec4227SStephan Aßmus 	fShowSourcePackagesItem = new BMenuItem(
7764fade61fSStephan Aßmus 		B_TRANSLATE("Source packages"),
777fdec4227SStephan Aßmus 		new BMessage(MSG_SHOW_SOURCE_PACKAGES));
778fdec4227SStephan Aßmus 	menu->AddItem(fShowSourcePackagesItem);
779fdec4227SStephan Aßmus 
780fdec4227SStephan Aßmus 	menuBar->AddItem(menu);
78104c9d578SStephan Aßmus }
78204c9d578SStephan Aßmus 
78304c9d578SStephan Aßmus 
78404c9d578SStephan Aßmus void
785e0a26dceSStephan Aßmus MainWindow::_BuildUserMenu(BMenuBar* menuBar)
786e0a26dceSStephan Aßmus {
787e0a26dceSStephan Aßmus 	fUserMenu = new BMenu(B_TRANSLATE("Not logged in"));
788e0a26dceSStephan Aßmus 
789e0a26dceSStephan Aßmus 	fLogInItem = new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS),
790e0a26dceSStephan Aßmus 		new BMessage(MSG_LOG_IN));
791e0a26dceSStephan Aßmus 	fUserMenu->AddItem(fLogInItem);
792e0a26dceSStephan Aßmus 
793e0a26dceSStephan Aßmus 	fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"),
794e0a26dceSStephan Aßmus 		new BMessage(MSG_LOG_OUT));
795e0a26dceSStephan Aßmus 	fUserMenu->AddItem(fLogOutItem);
796e0a26dceSStephan Aßmus 
7970c82f64bSAndrew Lindesay 	BMenuItem *latestUserUsageConditionsMenuItem =
7981448ced0SHumdinger 		new BMenuItem(B_TRANSLATE("View latest usage conditions"
79901339a54SAndrew Lindesay 			B_UTF8_ELLIPSIS),
80001339a54SAndrew Lindesay 			new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS));
8010c82f64bSAndrew Lindesay 	fUserMenu->AddItem(latestUserUsageConditionsMenuItem);
80201339a54SAndrew Lindesay 
803d2d4866dSAndrew Lindesay 	fUsersUserUsageConditionsMenuItem =
804d2d4866dSAndrew Lindesay 		new BMenuItem(B_TRANSLATE("View agreed usage conditions"
805d2d4866dSAndrew Lindesay 			B_UTF8_ELLIPSIS),
806d2d4866dSAndrew Lindesay 			new BMessage(MSG_VIEW_USERS_USER_USAGE_CONDITIONS));
807d2d4866dSAndrew Lindesay 	fUserMenu->AddItem(fUsersUserUsageConditionsMenuItem);
808d2d4866dSAndrew Lindesay 
809e0a26dceSStephan Aßmus 	menuBar->AddItem(fUserMenu);
810e0a26dceSStephan Aßmus }
811e0a26dceSStephan Aßmus 
812e0a26dceSStephan Aßmus 
813e0a26dceSStephan Aßmus void
814d2d4866dSAndrew Lindesay MainWindow::_RestoreNickname(const BMessage& settings)
815f7c2983eSAxel Dörfler {
816d2d4866dSAndrew Lindesay 	BString nickname;
817d2d4866dSAndrew Lindesay 	if (settings.FindString("username", &nickname) == B_OK
818d2d4866dSAndrew Lindesay 		&& nickname.Length() > 0) {
819d2d4866dSAndrew Lindesay 		fModel.SetNickname(nickname);
820f7c2983eSAxel Dörfler 	}
821f7c2983eSAxel Dörfler }
822f7c2983eSAxel Dörfler 
823f7c2983eSAxel Dörfler 
824f7c2983eSAxel Dörfler const char*
825f7c2983eSAxel Dörfler MainWindow::_WindowFrameName() const
826f7c2983eSAxel Dörfler {
827f7c2983eSAxel Dörfler 	if (fSinglePackageMode)
828f7c2983eSAxel Dörfler 		return "small window frame";
829f7c2983eSAxel Dörfler 
830f7c2983eSAxel Dörfler 	return "window frame";
831f7c2983eSAxel Dörfler }
832f7c2983eSAxel Dörfler 
833f7c2983eSAxel Dörfler 
834f7c2983eSAxel Dörfler void
835f7c2983eSAxel Dörfler MainWindow::_RestoreWindowFrame(const BMessage& settings)
836f7c2983eSAxel Dörfler {
837f7c2983eSAxel Dörfler 	BRect frame = Frame();
838f7c2983eSAxel Dörfler 
839f7c2983eSAxel Dörfler 	BRect windowFrame;
840f7c2983eSAxel Dörfler 	bool fromSettings = false;
841f7c2983eSAxel Dörfler 	if (settings.FindRect(_WindowFrameName(), &windowFrame) == B_OK) {
842f7c2983eSAxel Dörfler 		frame = windowFrame;
843f7c2983eSAxel Dörfler 		fromSettings = true;
844f7c2983eSAxel Dörfler 	} else if (!fSinglePackageMode) {
845f7c2983eSAxel Dörfler 		// Resize to occupy a certain screen size
846f7c2983eSAxel Dörfler 		BRect screenFrame = BScreen(this).Frame();
847f7c2983eSAxel Dörfler 		float width = frame.Width();
848f7c2983eSAxel Dörfler 		float height = frame.Height();
849f7c2983eSAxel Dörfler 		if (width < screenFrame.Width() * .666f
850f7c2983eSAxel Dörfler 			&& height < screenFrame.Height() * .666f) {
851f7c2983eSAxel Dörfler 			frame.bottom = frame.top + screenFrame.Height() * .666f;
852f7c2983eSAxel Dörfler 			frame.right = frame.left
853f7c2983eSAxel Dörfler 				+ std::min(screenFrame.Width() * .666f, height * 7 / 5);
854f7c2983eSAxel Dörfler 		}
855f7c2983eSAxel Dörfler 	}
856f7c2983eSAxel Dörfler 
857f7c2983eSAxel Dörfler 	MoveTo(frame.LeftTop());
858f7c2983eSAxel Dörfler 	ResizeTo(frame.Width(), frame.Height());
859f7c2983eSAxel Dörfler 
860f7c2983eSAxel Dörfler 	if (fromSettings)
8618e96ec34SAxel Dörfler 		MoveOnScreen();
862f7c2983eSAxel Dörfler 	else
863f7c2983eSAxel Dörfler 		CenterOnScreen();
864f7c2983eSAxel Dörfler }
865f7c2983eSAxel Dörfler 
866f7c2983eSAxel Dörfler 
867f7c2983eSAxel Dörfler void
868ccf707d0SAndrew Lindesay MainWindow::_RestoreModelSettings(const BMessage& settings)
869ccf707d0SAndrew Lindesay {
870ccf707d0SAndrew Lindesay 	BString packageListViewMode;
871133ebab6SAndrew Lindesay 	if (settings.FindString(SETTING_PACKAGE_LIST_VIEW_MODE,
872ccf707d0SAndrew Lindesay 			&packageListViewMode) == B_OK) {
873ccf707d0SAndrew Lindesay 		fModel.SetPackageListViewMode(
874ccf707d0SAndrew Lindesay 			main_window_str_to_package_list_view_mode(packageListViewMode));
875ccf707d0SAndrew Lindesay 	}
876ccf707d0SAndrew Lindesay 
877ccf707d0SAndrew Lindesay 	bool showOption;
878*363fc60bSAndrew Lindesay 
879133ebab6SAndrew Lindesay 	if (settings.FindBool(SETTING_SHOW_AVAILABLE_PACKAGES, &showOption) == B_OK)
880*363fc60bSAndrew Lindesay 		fModel.PackageFilter()->SetShowAvailablePackages(showOption);
881133ebab6SAndrew Lindesay 	if (settings.FindBool(SETTING_SHOW_INSTALLED_PACKAGES, &showOption) == B_OK)
882*363fc60bSAndrew Lindesay 		fModel.PackageFilter()->SetShowInstalledPackages(showOption);
883133ebab6SAndrew Lindesay 	if (settings.FindBool(SETTING_SHOW_DEVELOP_PACKAGES, &showOption) == B_OK)
884*363fc60bSAndrew Lindesay 		fModel.PackageFilter()->SetShowDevelopPackages(showOption);
885133ebab6SAndrew Lindesay 	if (settings.FindBool(SETTING_SHOW_SOURCE_PACKAGES, &showOption) == B_OK)
886*363fc60bSAndrew Lindesay 		fModel.PackageFilter()->SetShowSourcePackages(showOption);
887*363fc60bSAndrew Lindesay 
888133ebab6SAndrew Lindesay 	if (settings.FindBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA,
889133ebab6SAndrew Lindesay 			&showOption) == B_OK) {
890133ebab6SAndrew Lindesay 		fModel.SetCanShareAnonymousUsageData(showOption);
891133ebab6SAndrew Lindesay 	}
892133ebab6SAndrew Lindesay }
893133ebab6SAndrew Lindesay 
894133ebab6SAndrew Lindesay 
895133ebab6SAndrew Lindesay void
896133ebab6SAndrew Lindesay MainWindow::_MaybePromptCanShareAnonymousUserData(const BMessage& settings)
897133ebab6SAndrew Lindesay {
898133ebab6SAndrew Lindesay 	bool showOption;
899133ebab6SAndrew Lindesay 	if (settings.FindBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA,
900133ebab6SAndrew Lindesay 			&showOption) == B_NAME_NOT_FOUND) {
901133ebab6SAndrew Lindesay 		_PromptCanShareAnonymousUserData();
902133ebab6SAndrew Lindesay 	}
903133ebab6SAndrew Lindesay }
904133ebab6SAndrew Lindesay 
905133ebab6SAndrew Lindesay 
906133ebab6SAndrew Lindesay void
907133ebab6SAndrew Lindesay MainWindow::_PromptCanShareAnonymousUserData()
908133ebab6SAndrew Lindesay {
909133ebab6SAndrew Lindesay 	BAlert* alert = new(std::nothrow) BAlert(
910133ebab6SAndrew Lindesay 		B_TRANSLATE("Sending anonymous usage data"),
911133ebab6SAndrew Lindesay 		B_TRANSLATE("Would it be acceptable to send anonymous usage data to the"
912133ebab6SAndrew Lindesay 			" HaikuDepotServer system from this computer? You can change your"
913133ebab6SAndrew Lindesay 			" preference in the \"Settings\" window later."),
914133ebab6SAndrew Lindesay 		B_TRANSLATE("No"),
915133ebab6SAndrew Lindesay 		B_TRANSLATE("Yes"));
916133ebab6SAndrew Lindesay 
917133ebab6SAndrew Lindesay 	int32 result = alert->Go();
918133ebab6SAndrew Lindesay 	fModel.SetCanShareAnonymousUsageData(1 == result);
919ccf707d0SAndrew Lindesay }
920ccf707d0SAndrew Lindesay 
921ccf707d0SAndrew Lindesay 
922ccf707d0SAndrew Lindesay void
92363975654SStephan Aßmus MainWindow::_InitWorkerThreads()
92463975654SStephan Aßmus {
92563975654SStephan Aßmus 	fPackageToPopulateSem = create_sem(0, "PopulatePackage");
92663975654SStephan Aßmus 	if (fPackageToPopulateSem >= 0) {
92763975654SStephan Aßmus 		fPopulatePackageWorker = spawn_thread(&_PopulatePackageWorker,
92863975654SStephan Aßmus 			"Package Populator", B_NORMAL_PRIORITY, this);
92963975654SStephan Aßmus 		if (fPopulatePackageWorker >= 0)
93063975654SStephan Aßmus 			resume_thread(fPopulatePackageWorker);
93109f82905SStephan Aßmus 	} else
93209f82905SStephan Aßmus 		fPopulatePackageWorker = -1;
93363975654SStephan Aßmus }
93463975654SStephan Aßmus 
93563975654SStephan Aßmus 
93663975654SStephan Aßmus void
937c4199192SAndrew Lindesay MainWindow::_AdoptModelControls()
93804c9d578SStephan Aßmus {
9399883929bSAndrew Lindesay 	if (fSinglePackageMode)
9409883929bSAndrew Lindesay 		return;
9419883929bSAndrew Lindesay 
94204c9d578SStephan Aßmus 	BAutolock locker(fModel.Lock());
943*363fc60bSAndrew Lindesay 	fShowAvailablePackagesItem->SetMarked(fModel.PackageFilter()->ShowAvailablePackages());
944*363fc60bSAndrew Lindesay 	fShowInstalledPackagesItem->SetMarked(fModel.PackageFilter()->ShowInstalledPackages());
945*363fc60bSAndrew Lindesay 	fShowSourcePackagesItem->SetMarked(fModel.PackageFilter()->ShowSourcePackages());
946*363fc60bSAndrew Lindesay 	fShowDevelopPackagesItem->SetMarked(fModel.PackageFilter()->ShowDevelopPackages());
947da651104SStephan Aßmus 
948ccf707d0SAndrew Lindesay 	if (fModel.PackageListViewMode() == PROMINENT)
949ccf707d0SAndrew Lindesay 		fListTabs->Select(TAB_PROMINENT_PACKAGES);
9504fade61fSStephan Aßmus 	else
951ccf707d0SAndrew Lindesay 		fListTabs->Select(TAB_ALL_PACKAGES);
952b965ce70SHumdinger 
953b965ce70SHumdinger 	fFilterView->AdoptModel(fModel);
95404c9d578SStephan Aßmus }
95504c9d578SStephan Aßmus 
95604c9d578SStephan Aßmus 
95704c9d578SStephan Aßmus void
958c4199192SAndrew Lindesay MainWindow::_AdoptModel()
959c4199192SAndrew Lindesay {
960fa5c8097SAndrew Lindesay 	HDTRACE("adopting model to main window ui");
961c4199192SAndrew Lindesay 
962c4199192SAndrew Lindesay 	if (fSinglePackageMode)
963c4199192SAndrew Lindesay 		return;
964c4199192SAndrew Lindesay 
96505880d13SAndrew Lindesay 	std::vector<DepotInfoRef> depots = _CreateSnapshotOfDepots();
96605880d13SAndrew Lindesay 	std::vector<DepotInfoRef>::iterator it;
9670b69420bSAndrew Lindesay 
9680b69420bSAndrew Lindesay 	fFeaturedPackagesView->BeginAddRemove();
9690b69420bSAndrew Lindesay 
97005880d13SAndrew Lindesay 	for (it = depots.begin(); it != depots.end(); it++) {
97105880d13SAndrew Lindesay 		DepotInfoRef depotInfoRef = *it;
972d75b4d61SAndrew Lindesay 		for (int i = 0; i < depotInfoRef->CountPackages(); i++) {
973d75b4d61SAndrew Lindesay 			PackageInfoRef package = depotInfoRef->PackageAtIndex(i);
974d75b4d61SAndrew Lindesay 			_AddRemovePackageFromLists(package);
975d75b4d61SAndrew Lindesay 		}
976c4199192SAndrew Lindesay 	}
977c4199192SAndrew Lindesay 
9780b69420bSAndrew Lindesay 	fFeaturedPackagesView->EndAddRemove();
9790b69420bSAndrew Lindesay 
980c4199192SAndrew Lindesay 	_AdoptModelControls();
981c4199192SAndrew Lindesay }
982c4199192SAndrew Lindesay 
983c4199192SAndrew Lindesay 
984c4199192SAndrew Lindesay void
985c4199192SAndrew Lindesay MainWindow::_AddRemovePackageFromLists(const PackageInfoRef& package)
986c4199192SAndrew Lindesay {
987c4199192SAndrew Lindesay 	bool matches;
988c4199192SAndrew Lindesay 
989c4199192SAndrew Lindesay 	{
990c4199192SAndrew Lindesay 		AutoLocker<BLocker> modelLocker(fModel.Lock());
991*363fc60bSAndrew Lindesay 		matches = fModel.PackageFilter()->Filter()->AcceptsPackage(package);
992c4199192SAndrew Lindesay 	}
993c4199192SAndrew Lindesay 
994c4199192SAndrew Lindesay 	if (matches) {
995c4199192SAndrew Lindesay 		if (package->IsProminent())
996c4199192SAndrew Lindesay 			fFeaturedPackagesView->AddPackage(package);
997c4199192SAndrew Lindesay 		fPackageListView->AddPackage(package);
998c4199192SAndrew Lindesay 	} else {
999c4199192SAndrew Lindesay 		fFeaturedPackagesView->RemovePackage(package);
1000c4199192SAndrew Lindesay 		fPackageListView->RemovePackage(package);
1001c4199192SAndrew Lindesay 	}
1002c4199192SAndrew Lindesay }
1003c4199192SAndrew Lindesay 
1004c4199192SAndrew Lindesay 
1005c4199192SAndrew Lindesay void
100684674570SAndrew Lindesay MainWindow::_SetupDelayedIncrementViewCounter(const PackageInfoRef package) {
100784674570SAndrew Lindesay 	if (fIncrementViewCounterDelayedRunner != NULL) {
100884674570SAndrew Lindesay 		fIncrementViewCounterDelayedRunner->SetCount(0);
100984674570SAndrew Lindesay 		delete fIncrementViewCounterDelayedRunner;
101084674570SAndrew Lindesay 	}
101184674570SAndrew Lindesay 	BMessage message(MSG_INCREMENT_VIEW_COUNTER);
101284674570SAndrew Lindesay 	message.SetString("name", package->Name());
101384674570SAndrew Lindesay 	fIncrementViewCounterDelayedRunner =
101484674570SAndrew Lindesay 		new BMessageRunner(BMessenger(this), &message,
101584674570SAndrew Lindesay 			kIncrementViewCounterDelayMicros, 1);
101684674570SAndrew Lindesay 	if (fIncrementViewCounterDelayedRunner->InitCheck()
101784674570SAndrew Lindesay 			!= B_OK) {
101884674570SAndrew Lindesay 		HDERROR("unable to init the increment view counter");
101984674570SAndrew Lindesay 	}
102084674570SAndrew Lindesay }
102184674570SAndrew Lindesay 
102284674570SAndrew Lindesay 
102384674570SAndrew Lindesay void
102484674570SAndrew Lindesay MainWindow::_HandleIncrementViewCounter(const BMessage* message)
102584674570SAndrew Lindesay {
102684674570SAndrew Lindesay 	BString name;
102784674570SAndrew Lindesay 	if (message->FindString("name", &name) == B_OK) {
102884674570SAndrew Lindesay 		const PackageInfoRef& viewedPackage =
102984674570SAndrew Lindesay 			fPackageInfoView->Package();
103084674570SAndrew Lindesay 		if (viewedPackage.IsSet()) {
103184674570SAndrew Lindesay 			if (viewedPackage->Name() == name)
103284674570SAndrew Lindesay 				_IncrementViewCounter(viewedPackage);
103384674570SAndrew Lindesay 			else
103484674570SAndrew Lindesay 				HDINFO("incr. view counter; name mismatch");
103584674570SAndrew Lindesay 		} else
103684674570SAndrew Lindesay 			HDINFO("incr. view counter; no viewed pkg");
103784674570SAndrew Lindesay 	} else
103884674570SAndrew Lindesay 		HDERROR("incr. view counter; no name");
103984674570SAndrew Lindesay 	fIncrementViewCounterDelayedRunner->SetCount(0);
104084674570SAndrew Lindesay 	delete fIncrementViewCounterDelayedRunner;
104184674570SAndrew Lindesay 	fIncrementViewCounterDelayedRunner = NULL;
104284674570SAndrew Lindesay }
104384674570SAndrew Lindesay 
104484674570SAndrew Lindesay 
104584674570SAndrew Lindesay void
1046409af934SAndrew Lindesay MainWindow::_IncrementViewCounter(const PackageInfoRef package)
1047133ebab6SAndrew Lindesay {
1048133ebab6SAndrew Lindesay 	bool shouldIncrementViewCounter = false;
1049133ebab6SAndrew Lindesay 
1050133ebab6SAndrew Lindesay 	{
1051133ebab6SAndrew Lindesay 		AutoLocker<BLocker> modelLocker(fModel.Lock());
1052133ebab6SAndrew Lindesay 		bool canShareAnonymousUsageData = fModel.CanShareAnonymousUsageData();
1053133ebab6SAndrew Lindesay 		if (canShareAnonymousUsageData && !package->Viewed()) {
1054133ebab6SAndrew Lindesay 			package->SetViewed();
1055133ebab6SAndrew Lindesay 			shouldIncrementViewCounter = true;
1056133ebab6SAndrew Lindesay 		}
1057133ebab6SAndrew Lindesay 	}
1058133ebab6SAndrew Lindesay 
1059133ebab6SAndrew Lindesay 	if (shouldIncrementViewCounter) {
1060467ffecdSAugustin Cavalier 		ProcessCoordinator* incrementViewCoordinator =
1061133ebab6SAndrew Lindesay 			ProcessCoordinatorFactory::CreateIncrementViewCounter(
1062133ebab6SAndrew Lindesay 				&fModel, package);
1063467ffecdSAugustin Cavalier 		_AddProcessCoordinator(incrementViewCoordinator);
1064133ebab6SAndrew Lindesay 	}
1065133ebab6SAndrew Lindesay }
1066133ebab6SAndrew Lindesay 
1067133ebab6SAndrew Lindesay 
1068133ebab6SAndrew Lindesay void
106904c9d578SStephan Aßmus MainWindow::_AdoptPackage(const PackageInfoRef& package)
107004c9d578SStephan Aßmus {
107175ba9dbfSStephan Aßmus 	{
107275ba9dbfSStephan Aßmus 		BAutolock locker(fModel.Lock());
107304c9d578SStephan Aßmus 		fPackageInfoView->SetPackage(package);
10741986ab9fSStephan Aßmus 
1075664372abSStephan Aßmus 		if (fFeaturedPackagesView != NULL)
1076664372abSStephan Aßmus 			fFeaturedPackagesView->SelectPackage(package);
10771986ab9fSStephan Aßmus 		if (fPackageListView != NULL)
10781986ab9fSStephan Aßmus 			fPackageListView->SelectPackage(package);
107975ba9dbfSStephan Aßmus 	}
108004c9d578SStephan Aßmus 
1081a9edb9bfSAndrew Lindesay 	_PopulatePackageAsync(false);
108204c9d578SStephan Aßmus }
108304c9d578SStephan Aßmus 
108404c9d578SStephan Aßmus 
108504c9d578SStephan Aßmus void
108604c9d578SStephan Aßmus MainWindow::_ClearPackage()
108704c9d578SStephan Aßmus {
108804c9d578SStephan Aßmus 	fPackageInfoView->Clear();
108904c9d578SStephan Aßmus }
109004c9d578SStephan Aßmus 
109104c9d578SStephan Aßmus 
109204c9d578SStephan Aßmus void
10933369e03dSAndrew Lindesay MainWindow::_StartBulkLoad(bool force)
109404c9d578SStephan Aßmus {
1095c4199192SAndrew Lindesay 	if (fFeaturedPackagesView != NULL)
1096c4199192SAndrew Lindesay 		fFeaturedPackagesView->Clear();
1097c4199192SAndrew Lindesay 	if (fPackageListView != NULL)
1098c4199192SAndrew Lindesay 		fPackageListView->Clear();
1099c4199192SAndrew Lindesay 	fPackageInfoView->Clear();
1100c4199192SAndrew Lindesay 
110110cd325cSAndrew Lindesay 	fRefreshRepositoriesItem->SetEnabled(false);
110210cd325cSAndrew Lindesay 	ProcessCoordinator* bulkLoadCoordinator =
110310cd325cSAndrew Lindesay 		ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
1104409af934SAndrew Lindesay 			fPackageInfoListener, &fModel, force);
110510cd325cSAndrew Lindesay 	_AddProcessCoordinator(bulkLoadCoordinator);
110604c9d578SStephan Aßmus }
110704c9d578SStephan Aßmus 
110804c9d578SStephan Aßmus 
110904c9d578SStephan Aßmus void
111010cd325cSAndrew Lindesay MainWindow::_BulkLoadCompleteReceived(status_t errorStatus)
111104c9d578SStephan Aßmus {
111210cd325cSAndrew Lindesay 	if (errorStatus != B_OK) {
11133369e03dSAndrew Lindesay 		AppUtils::NotifySimpleError(
11141448ced0SHumdinger 			B_TRANSLATE("Package update error"),
11153369e03dSAndrew Lindesay 			B_TRANSLATE("While updating package data, a problem has arisen "
11163369e03dSAndrew Lindesay 				"that may cause data to be outdated or missing from the "
11173369e03dSAndrew Lindesay 				"application's display. Additional details regarding this "
11183369e03dSAndrew Lindesay 				"problem may be able to be obtained from the application "
1119b21c9447SAndrew Lindesay 				"logs."
1120b21c9447SAndrew Lindesay 				ALERT_MSG_LOGS_USER_GUIDE));
112104c9d578SStephan Aßmus 	}
112210cd325cSAndrew Lindesay 
11233369e03dSAndrew Lindesay 	fRefreshRepositoriesItem->SetEnabled(true);
112410cd325cSAndrew Lindesay 	_AdoptModel();
112510cd325cSAndrew Lindesay 	_UpdateAvailableRepositories();
1126ccf707d0SAndrew Lindesay 
1127ccf707d0SAndrew Lindesay 	// if after loading everything in, it transpires that there are no
1128ccf707d0SAndrew Lindesay 	// featured packages then the featured packages should be disabled
1129ccf707d0SAndrew Lindesay 	// and the user should be switched to the "all packages" view so
1130ccf707d0SAndrew Lindesay 	// that they are not presented with a blank window!
1131ccf707d0SAndrew Lindesay 
1132ccf707d0SAndrew Lindesay 	bool hasProminentPackages = fModel.HasAnyProminentPackages();
1133ccf707d0SAndrew Lindesay 	fListTabs->TabAt(TAB_PROMINENT_PACKAGES)->SetEnabled(hasProminentPackages);
1134ccf707d0SAndrew Lindesay 	if (!hasProminentPackages
1135ccf707d0SAndrew Lindesay 			&& fListTabs->Selection() == TAB_PROMINENT_PACKAGES) {
1136ccf707d0SAndrew Lindesay 		fModel.SetPackageListViewMode(ALL);
1137ccf707d0SAndrew Lindesay 		fListTabs->Select(TAB_ALL_PACKAGES);
1138ccf707d0SAndrew Lindesay 	}
113904c9d578SStephan Aßmus }
114004c9d578SStephan Aßmus 
114104c9d578SStephan Aßmus 
11423369e03dSAndrew Lindesay void
114310cd325cSAndrew Lindesay MainWindow::_NotifyWorkStatusClear()
114404c9d578SStephan Aßmus {
114510cd325cSAndrew Lindesay 	BMessage message(MSG_WORK_STATUS_CLEAR);
114610cd325cSAndrew Lindesay 	this->PostMessage(&message, this);
114710cd325cSAndrew Lindesay }
114810cd325cSAndrew Lindesay 
114910cd325cSAndrew Lindesay 
115010cd325cSAndrew Lindesay void
115110cd325cSAndrew Lindesay MainWindow::_HandleWorkStatusClear()
115210cd325cSAndrew Lindesay {
115310cd325cSAndrew Lindesay 	fWorkStatusView->SetText("");
11543369e03dSAndrew Lindesay 	fWorkStatusView->SetIdle();
11553369e03dSAndrew Lindesay }
115604c9d578SStephan Aßmus 
115704c9d578SStephan Aßmus 
11583369e03dSAndrew Lindesay /*! Sends off a message to the Window so that it can change the status view
11593369e03dSAndrew Lindesay     on the front-end in the UI thread.
11603369e03dSAndrew Lindesay */
116104c9d578SStephan Aßmus 
11623369e03dSAndrew Lindesay void
11633369e03dSAndrew Lindesay MainWindow::_NotifyWorkStatusChange(const BString& text, float progress)
11643369e03dSAndrew Lindesay {
11653369e03dSAndrew Lindesay 	BMessage message(MSG_WORK_STATUS_CHANGE);
116604c9d578SStephan Aßmus 
11673369e03dSAndrew Lindesay 	if (!text.IsEmpty())
11683369e03dSAndrew Lindesay 		message.AddString(KEY_WORK_STATUS_TEXT, text);
11693369e03dSAndrew Lindesay 	message.AddFloat(KEY_WORK_STATUS_PROGRESS, progress);
117004c9d578SStephan Aßmus 
11713369e03dSAndrew Lindesay 	this->PostMessage(&message, this);
11723369e03dSAndrew Lindesay }
117304c9d578SStephan Aßmus 
11743369e03dSAndrew Lindesay 
1175f32d5c5cSAndrew Lindesay void
1176f32d5c5cSAndrew Lindesay MainWindow::_HandleExternalPackageUpdateMessageReceived(const BMessage* message)
1177f32d5c5cSAndrew Lindesay {
1178f32d5c5cSAndrew Lindesay 	BStringList addedPackageNames;
1179f32d5c5cSAndrew Lindesay 	BStringList removedPackageNames;
1180f32d5c5cSAndrew Lindesay 
1181f32d5c5cSAndrew Lindesay 	if (message->FindStrings("added package names",
1182f32d5c5cSAndrew Lindesay 			&addedPackageNames) == B_OK) {
1183f32d5c5cSAndrew Lindesay 		addedPackageNames.Sort();
11840ebb9177SAndrew Lindesay 		AutoLocker<BLocker> locker(fModel.Lock());
11850ebb9177SAndrew Lindesay 		fModel.SetStateForPackagesByName(addedPackageNames, ACTIVATED);
1186f32d5c5cSAndrew Lindesay 	}
1187f32d5c5cSAndrew Lindesay 	else
1188f32d5c5cSAndrew Lindesay 		HDINFO("no [added package names] key in inbound message");
1189f32d5c5cSAndrew Lindesay 
1190f32d5c5cSAndrew Lindesay 	if (message->FindStrings("removed package names",
1191f32d5c5cSAndrew Lindesay 			&removedPackageNames) == B_OK) {
1192f32d5c5cSAndrew Lindesay 		removedPackageNames.Sort();
11930ebb9177SAndrew Lindesay 		AutoLocker<BLocker> locker(fModel.Lock());
11940ebb9177SAndrew Lindesay 		fModel.SetStateForPackagesByName(addedPackageNames, UNINSTALLED);
1195f32d5c5cSAndrew Lindesay 	} else
1196f32d5c5cSAndrew Lindesay 		HDINFO("no [removed package names] key in inbound message");
1197f32d5c5cSAndrew Lindesay }
1198f32d5c5cSAndrew Lindesay 
1199f32d5c5cSAndrew Lindesay 
12003369e03dSAndrew Lindesay void
12013369e03dSAndrew Lindesay MainWindow::_HandleWorkStatusChangeMessageReceived(const BMessage* message)
12023369e03dSAndrew Lindesay {
12039883929bSAndrew Lindesay 	if (fWorkStatusView == NULL)
12049883929bSAndrew Lindesay 		return;
12059883929bSAndrew Lindesay 
12063369e03dSAndrew Lindesay 	BString text;
12073369e03dSAndrew Lindesay 	float progress;
12083369e03dSAndrew Lindesay 
12093369e03dSAndrew Lindesay 	if (message->FindString(KEY_WORK_STATUS_TEXT, &text) == B_OK)
12103369e03dSAndrew Lindesay 		fWorkStatusView->SetText(text);
12113369e03dSAndrew Lindesay 
1212a18f873fSAndrew Lindesay 	if (message->FindFloat(KEY_WORK_STATUS_PROGRESS, &progress) == B_OK) {
1213a18f873fSAndrew Lindesay 		if (progress < 0.0f)
1214a18f873fSAndrew Lindesay 			fWorkStatusView->SetBusy();
1215a18f873fSAndrew Lindesay 		else
12163369e03dSAndrew Lindesay 			fWorkStatusView->SetProgress(progress);
1217a18f873fSAndrew Lindesay 	} else {
1218a18f873fSAndrew Lindesay 		HDERROR("work status change missing progress on update message");
1219a18f873fSAndrew Lindesay 		fWorkStatusView->SetProgress(0.0f);
122004c9d578SStephan Aßmus 	}
122104c9d578SStephan Aßmus }
122204c9d578SStephan Aßmus 
122304c9d578SStephan Aßmus 
1224a9edb9bfSAndrew Lindesay /*! This method will cause the package to have its data refreshed from
1225a9edb9bfSAndrew Lindesay     the server application.  The refresh happens in the background; this method
1226a9edb9bfSAndrew Lindesay     is asynchronous.
1227a9edb9bfSAndrew Lindesay */
1228a9edb9bfSAndrew Lindesay 
1229a9edb9bfSAndrew Lindesay void
1230a9edb9bfSAndrew Lindesay MainWindow::_PopulatePackageAsync(bool forcePopulate)
1231a9edb9bfSAndrew Lindesay {
1232a9edb9bfSAndrew Lindesay 		// Trigger asynchronous package population from the web-app
1233a9edb9bfSAndrew Lindesay 	{
1234a9edb9bfSAndrew Lindesay 		AutoLocker<BLocker> lock(&fPackageToPopulateLock);
1235a9edb9bfSAndrew Lindesay 		fPackageToPopulate = fPackageInfoView->Package();
1236a9edb9bfSAndrew Lindesay 		fForcePopulatePackage = forcePopulate;
1237a9edb9bfSAndrew Lindesay 	}
1238a9edb9bfSAndrew Lindesay 	release_sem_etc(fPackageToPopulateSem, 1, 0);
1239a9edb9bfSAndrew Lindesay 
1240f96d1f4dSAndrew Lindesay 	HDDEBUG("pkg [%s] will be updated from the server.",
1241fa5c8097SAndrew Lindesay 		fPackageToPopulate->Name().String());
1242a9edb9bfSAndrew Lindesay }
1243a9edb9bfSAndrew Lindesay 
1244a9edb9bfSAndrew Lindesay 
1245a9edb9bfSAndrew Lindesay /*! This method will run in the background.  The thread will block until there
1246a9edb9bfSAndrew Lindesay     is a package to be updated.  When the thread unblocks, it will update the
1247a9edb9bfSAndrew Lindesay     package with information from the server.
1248a9edb9bfSAndrew Lindesay */
1249a9edb9bfSAndrew Lindesay 
125004c9d578SStephan Aßmus status_t
125104c9d578SStephan Aßmus MainWindow::_PopulatePackageWorker(void* arg)
125204c9d578SStephan Aßmus {
125304c9d578SStephan Aßmus 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
125404c9d578SStephan Aßmus 
125504c9d578SStephan Aßmus 	while (acquire_sem(window->fPackageToPopulateSem) == B_OK) {
125604c9d578SStephan Aßmus 		PackageInfoRef package;
1257a9edb9bfSAndrew Lindesay 		bool force;
125804c9d578SStephan Aßmus 		{
125904c9d578SStephan Aßmus 			AutoLocker<BLocker> lock(&window->fPackageToPopulateLock);
126004c9d578SStephan Aßmus 			package = window->fPackageToPopulate;
1261a9edb9bfSAndrew Lindesay 			force = window->fForcePopulatePackage;
126204c9d578SStephan Aßmus 		}
126304c9d578SStephan Aßmus 
1264779ab335SX512 		if (package.IsSet()) {
1265a9edb9bfSAndrew Lindesay 			uint32 populateFlags = Model::POPULATE_USER_RATINGS
1266a9edb9bfSAndrew Lindesay 				| Model::POPULATE_CHANGELOG;
1267a9edb9bfSAndrew Lindesay 
1268a9edb9bfSAndrew Lindesay 			if (force)
1269a9edb9bfSAndrew Lindesay 				populateFlags |= Model::POPULATE_FORCE;
1270a9edb9bfSAndrew Lindesay 
1271a9edb9bfSAndrew Lindesay 			window->fModel.PopulatePackage(package, populateFlags);
1272a9edb9bfSAndrew Lindesay 
1273fa5c8097SAndrew Lindesay 			HDDEBUG("populating package [%s]", package->Name().String());
127404c9d578SStephan Aßmus 		}
127504c9d578SStephan Aßmus 	}
127604c9d578SStephan Aßmus 
127704c9d578SStephan Aßmus 	return 0;
127804c9d578SStephan Aßmus }
127904c9d578SStephan Aßmus 
128004c9d578SStephan Aßmus 
128104c9d578SStephan Aßmus void
1282133ebab6SAndrew Lindesay MainWindow::_OpenSettingsWindow()
1283133ebab6SAndrew Lindesay {
1284133ebab6SAndrew Lindesay 	SettingsWindow* window = new SettingsWindow(this, &fModel);
1285133ebab6SAndrew Lindesay 	window->Show();
1286133ebab6SAndrew Lindesay }
1287133ebab6SAndrew Lindesay 
1288133ebab6SAndrew Lindesay 
1289133ebab6SAndrew Lindesay void
12901bbda92dSStephan Aßmus MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage)
129104c9d578SStephan Aßmus {
129204c9d578SStephan Aßmus 	UserLoginWindow* window = new UserLoginWindow(this,
129304c9d578SStephan Aßmus 		BRect(0, 0, 500, 400), fModel);
12941bbda92dSStephan Aßmus 
12951bbda92dSStephan Aßmus 	if (onSuccessMessage.what != 0)
12961bbda92dSStephan Aßmus 		window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage);
12971bbda92dSStephan Aßmus 
129804c9d578SStephan Aßmus 	window->Show();
129904c9d578SStephan Aßmus }
130004c9d578SStephan Aßmus 
13018f03a0f9SStephan Aßmus 
13028f03a0f9SStephan Aßmus void
130310cd325cSAndrew Lindesay MainWindow::_StartUserVerify()
130410cd325cSAndrew Lindesay {
130510cd325cSAndrew Lindesay 	if (!fModel.Nickname().IsEmpty()) {
130610cd325cSAndrew Lindesay 		_AddProcessCoordinator(
130710cd325cSAndrew Lindesay 			ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
130810cd325cSAndrew Lindesay 				this,
130910cd325cSAndrew Lindesay 					// UserDetailVerifierListener
131010cd325cSAndrew Lindesay 				&fModel) );
131110cd325cSAndrew Lindesay 	}
131210cd325cSAndrew Lindesay }
131310cd325cSAndrew Lindesay 
131410cd325cSAndrew Lindesay 
131510cd325cSAndrew Lindesay void
13168f03a0f9SStephan Aßmus MainWindow::_UpdateAuthorization()
13178f03a0f9SStephan Aßmus {
1318d2d4866dSAndrew Lindesay 	BString nickname(fModel.Nickname());
1319d2d4866dSAndrew Lindesay 	bool hasUser = !nickname.IsEmpty();
1320e0a26dceSStephan Aßmus 
13216d519fd1SStephan Aßmus 	if (fLogOutItem != NULL)
1322e0a26dceSStephan Aßmus 		fLogOutItem->SetEnabled(hasUser);
1323d2d4866dSAndrew Lindesay 	if (fUsersUserUsageConditionsMenuItem != NULL)
1324d2d4866dSAndrew Lindesay 		fUsersUserUsageConditionsMenuItem->SetEnabled(hasUser);
1325e0a26dceSStephan Aßmus 	if (fLogInItem != NULL) {
1326e0a26dceSStephan Aßmus 		if (hasUser)
1327e0a26dceSStephan Aßmus 			fLogInItem->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS));
1328e0a26dceSStephan Aßmus 		else
1329e0a26dceSStephan Aßmus 			fLogInItem->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS));
1330e0a26dceSStephan Aßmus 	}
1331e0a26dceSStephan Aßmus 
1332e0a26dceSStephan Aßmus 	if (fUserMenu != NULL) {
1333e0a26dceSStephan Aßmus 		BString label;
1334d2d4866dSAndrew Lindesay 		if (hasUser) {
1335e0a26dceSStephan Aßmus 			label = B_TRANSLATE("Logged in as %User%");
1336d2d4866dSAndrew Lindesay 			label.ReplaceAll("%User%", nickname);
1337d2d4866dSAndrew Lindesay 		} else {
1338d2d4866dSAndrew Lindesay 			label = B_TRANSLATE("Not logged in");
1339e0a26dceSStephan Aßmus 		}
1340e0a26dceSStephan Aßmus 		fUserMenu->Superitem()->SetLabel(label);
1341e0a26dceSStephan Aßmus 	}
13428f03a0f9SStephan Aßmus }
13431bbda92dSStephan Aßmus 
13441bbda92dSStephan Aßmus 
13451bbda92dSStephan Aßmus void
1346b965ce70SHumdinger MainWindow::_UpdateAvailableRepositories()
1347b965ce70SHumdinger {
1348b965ce70SHumdinger 	fRepositoryMenu->RemoveItems(0, fRepositoryMenu->CountItems(), true);
1349b965ce70SHumdinger 
1350b965ce70SHumdinger 	fRepositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All repositories"),
1351b965ce70SHumdinger 		new BMessage(MSG_DEPOT_SELECTED)));
1352b965ce70SHumdinger 
1353b965ce70SHumdinger 	fRepositoryMenu->AddItem(new BSeparatorItem());
1354b965ce70SHumdinger 
1355b965ce70SHumdinger 	bool foundSelectedDepot = false;
135605880d13SAndrew Lindesay 	std::vector<DepotInfoRef> depots = _CreateSnapshotOfDepots();
135705880d13SAndrew Lindesay 	std::vector<DepotInfoRef>::iterator it;
1358d13c3d89SAndrew Lindesay 
135905880d13SAndrew Lindesay 	for (it = depots.begin(); it != depots.end(); it++) {
136005880d13SAndrew Lindesay 		DepotInfoRef depot = *it;
136105880d13SAndrew Lindesay 
136205880d13SAndrew Lindesay 		if (depot->Name().Length() != 0) {
1363b965ce70SHumdinger 			BMessage* message = new BMessage(MSG_DEPOT_SELECTED);
136405880d13SAndrew Lindesay 			message->AddString("name", depot->Name());
136505880d13SAndrew Lindesay 			BMenuItem* item = new(std::nothrow) BMenuItem(depot->Name(), message);
136605880d13SAndrew Lindesay 
136705880d13SAndrew Lindesay 			if (item == NULL)
136805880d13SAndrew Lindesay 				HDFATAL("memory exhaustion");
136905880d13SAndrew Lindesay 
1370b965ce70SHumdinger 			fRepositoryMenu->AddItem(item);
1371b965ce70SHumdinger 
1372*363fc60bSAndrew Lindesay 			if (depot->Name() == fModel.PackageFilter()->DepotName()) {
1373b965ce70SHumdinger 				item->SetMarked(true);
1374b965ce70SHumdinger 				foundSelectedDepot = true;
1375b965ce70SHumdinger 			}
1376b965ce70SHumdinger 		}
1377d13c3d89SAndrew Lindesay 	}
1378b965ce70SHumdinger 
1379b965ce70SHumdinger 	if (!foundSelectedDepot)
1380b965ce70SHumdinger 		fRepositoryMenu->ItemAt(0)->SetMarked(true);
1381b965ce70SHumdinger }
1382b965ce70SHumdinger 
1383b965ce70SHumdinger 
1384a9edb9bfSAndrew Lindesay bool
1385a9edb9bfSAndrew Lindesay MainWindow::_SelectedPackageHasWebAppRepositoryCode()
1386a9edb9bfSAndrew Lindesay {
1387a9edb9bfSAndrew Lindesay 	const PackageInfoRef& package = fPackageInfoView->Package();
13886ab878c0SAndrew Lindesay 	const BString depotName = package->DepotName();
1389a9edb9bfSAndrew Lindesay 
13906ab878c0SAndrew Lindesay 	if (depotName.IsEmpty()) {
1391fa5c8097SAndrew Lindesay 		HDDEBUG("the package [%s] has no depot name", package->Name().String());
13926ab878c0SAndrew Lindesay 	} else {
13936ab878c0SAndrew Lindesay 		const DepotInfo* depot = fModel.DepotForName(depotName);
1394a9edb9bfSAndrew Lindesay 
13956ab878c0SAndrew Lindesay 		if (depot == NULL) {
1396f96d1f4dSAndrew Lindesay 			HDINFO("the depot [%s] was not able to be found",
1397fa5c8097SAndrew Lindesay 				depotName.String());
13986ab878c0SAndrew Lindesay 		} else {
13996ab878c0SAndrew Lindesay 			BString repositoryCode = depot->WebAppRepositoryCode();
1400a9edb9bfSAndrew Lindesay 
14016ab878c0SAndrew Lindesay 			if (repositoryCode.IsEmpty()) {
1402f96d1f4dSAndrew Lindesay 				HDINFO("the depot [%s] has no web app repository code",
1403fa5c8097SAndrew Lindesay 					depotName.String());
1404fa5c8097SAndrew Lindesay 			} else
14056ab878c0SAndrew Lindesay 				return true;
14066ab878c0SAndrew Lindesay 		}
14076ab878c0SAndrew Lindesay 	}
14086ab878c0SAndrew Lindesay 
14096ab878c0SAndrew Lindesay 	return false;
1410a9edb9bfSAndrew Lindesay }
1411a9edb9bfSAndrew Lindesay 
1412a9edb9bfSAndrew Lindesay 
1413b965ce70SHumdinger void
14141bbda92dSStephan Aßmus MainWindow::_RatePackage()
14151bbda92dSStephan Aßmus {
1416a9edb9bfSAndrew Lindesay 	if (!_SelectedPackageHasWebAppRepositoryCode()) {
1417a9edb9bfSAndrew Lindesay 		BAlert* alert = new(std::nothrow) BAlert(
1418cbadcae1SHumdinger 			B_TRANSLATE("Rating not possible"),
1419cbadcae1SHumdinger 			B_TRANSLATE("This package doesn't seem to be on the HaikuDepot "
1420cbadcae1SHumdinger 				"Server, so it's not possible to create a new rating "
1421cbadcae1SHumdinger 				"or edit an existing rating."),
1422a9edb9bfSAndrew Lindesay 			B_TRANSLATE("OK"));
1423a9edb9bfSAndrew Lindesay 		alert->Go();
1424a9edb9bfSAndrew Lindesay     	return;
1425a9edb9bfSAndrew Lindesay 	}
1426a9edb9bfSAndrew Lindesay 
1427d2d4866dSAndrew Lindesay 	if (fModel.Nickname().IsEmpty()) {
14281bbda92dSStephan Aßmus 		BAlert* alert = new(std::nothrow) BAlert(
14291bbda92dSStephan Aßmus 			B_TRANSLATE("Not logged in"),
14301bbda92dSStephan Aßmus 			B_TRANSLATE("You need to be logged into an account before you "
14311bbda92dSStephan Aßmus 				"can rate packages."),
14321bbda92dSStephan Aßmus 			B_TRANSLATE("Cancel"),
14331bbda92dSStephan Aßmus 			B_TRANSLATE("Login or Create account"));
14341bbda92dSStephan Aßmus 
14351bbda92dSStephan Aßmus 		if (alert == NULL)
14361bbda92dSStephan Aßmus 			return;
14371bbda92dSStephan Aßmus 
14381bbda92dSStephan Aßmus 		int32 choice = alert->Go();
14391bbda92dSStephan Aßmus 		if (choice == 1)
14401bbda92dSStephan Aßmus 			_OpenLoginWindow(BMessage(MSG_RATE_PACKAGE));
14411bbda92dSStephan Aßmus 		return;
14421bbda92dSStephan Aßmus 	}
14431bbda92dSStephan Aßmus 
14441bbda92dSStephan Aßmus 	// TODO: Allow only one RatePackageWindow
14451bbda92dSStephan Aßmus 	// TODO: Mechanism for remembering the window frame
14461bbda92dSStephan Aßmus 	RatePackageWindow* window = new RatePackageWindow(this,
14471bbda92dSStephan Aßmus 		BRect(0, 0, 500, 400), fModel);
14481bbda92dSStephan Aßmus 	window->SetPackage(fPackageInfoView->Package());
14491bbda92dSStephan Aßmus 	window->Show();
14501bbda92dSStephan Aßmus }
1451d5ef985eSStephan Aßmus 
1452d5ef985eSStephan Aßmus 
1453d5ef985eSStephan Aßmus void
1454d5ef985eSStephan Aßmus MainWindow::_ShowScreenshot()
1455d5ef985eSStephan Aßmus {
1456d5ef985eSStephan Aßmus 	// TODO: Mechanism for remembering the window frame
1457d5ef985eSStephan Aßmus 	if (fScreenshotWindow == NULL)
145888af15cfSAndrew Lindesay 		fScreenshotWindow = new ScreenshotWindow(this, BRect(0, 0, 500, 400), &fModel);
1459d5ef985eSStephan Aßmus 
1460d5ef985eSStephan Aßmus 	if (fScreenshotWindow->LockWithTimeout(1000) != B_OK)
1461d5ef985eSStephan Aßmus 		return;
1462d5ef985eSStephan Aßmus 
1463d5ef985eSStephan Aßmus 	fScreenshotWindow->SetPackage(fPackageInfoView->Package());
1464d5ef985eSStephan Aßmus 
1465d5ef985eSStephan Aßmus 	if (fScreenshotWindow->IsHidden())
1466d5ef985eSStephan Aßmus 		fScreenshotWindow->Show();
1467d5ef985eSStephan Aßmus 	else
1468d5ef985eSStephan Aßmus 		fScreenshotWindow->Activate();
1469d5ef985eSStephan Aßmus 
1470d5ef985eSStephan Aßmus 	fScreenshotWindow->Unlock();
1471d5ef985eSStephan Aßmus }
147201339a54SAndrew Lindesay 
147301339a54SAndrew Lindesay 
147401339a54SAndrew Lindesay void
1475d2d4866dSAndrew Lindesay MainWindow::_ViewUserUsageConditions(
1476d2d4866dSAndrew Lindesay 	UserUsageConditionsSelectionMode mode)
147701339a54SAndrew Lindesay {
147801339a54SAndrew Lindesay 	UserUsageConditionsWindow* window = new UserUsageConditionsWindow(
1479d2d4866dSAndrew Lindesay 		fModel, mode);
148001339a54SAndrew Lindesay 	window->Show();
148101339a54SAndrew Lindesay }
148210cd325cSAndrew Lindesay 
148310cd325cSAndrew Lindesay 
148410cd325cSAndrew Lindesay void
148510cd325cSAndrew Lindesay MainWindow::UserCredentialsFailed()
148610cd325cSAndrew Lindesay {
148710cd325cSAndrew Lindesay 	BString message = B_TRANSLATE("The password previously "
148810cd325cSAndrew Lindesay 		"supplied for the user [%Nickname%] is not currently "
148910cd325cSAndrew Lindesay 		"valid. The user will be logged-out of this application "
149010cd325cSAndrew Lindesay 		"and you should login again with your updated password.");
149110cd325cSAndrew Lindesay 	message.ReplaceAll("%Nickname%", fModel.Nickname());
149210cd325cSAndrew Lindesay 
149310cd325cSAndrew Lindesay 	AppUtils::NotifySimpleError(B_TRANSLATE("Login issue"),
149410cd325cSAndrew Lindesay 		message);
149510cd325cSAndrew Lindesay 
149610cd325cSAndrew Lindesay 	{
149710cd325cSAndrew Lindesay 		AutoLocker<BLocker> locker(fModel.Lock());
149810cd325cSAndrew Lindesay 		fModel.SetNickname("");
149910cd325cSAndrew Lindesay 	}
150010cd325cSAndrew Lindesay }
150110cd325cSAndrew Lindesay 
150210cd325cSAndrew Lindesay 
150310cd325cSAndrew Lindesay /*! \brief This method is invoked from the UserDetailVerifierProcess on a
150410cd325cSAndrew Lindesay 		   background thread.  For this reason it lodges a message into itself
150510cd325cSAndrew Lindesay 		   which can then be handled on the main thread.
150610cd325cSAndrew Lindesay */
150710cd325cSAndrew Lindesay 
150810cd325cSAndrew Lindesay void
150910cd325cSAndrew Lindesay MainWindow::UserUsageConditionsNotLatest(const UserDetail& userDetail)
151010cd325cSAndrew Lindesay {
151110cd325cSAndrew Lindesay 	BMessage message(MSG_USER_USAGE_CONDITIONS_NOT_LATEST);
151210cd325cSAndrew Lindesay 	BMessage detailsMessage;
151310cd325cSAndrew Lindesay 	if (userDetail.Archive(&detailsMessage, true) != B_OK
151410cd325cSAndrew Lindesay 			|| message.AddMessage("userDetail", &detailsMessage) != B_OK) {
1515fa5c8097SAndrew Lindesay 		HDERROR("unable to archive the user detail into a message");
151610cd325cSAndrew Lindesay 	}
151710cd325cSAndrew Lindesay 	else
151810cd325cSAndrew Lindesay 		BMessenger(this).SendMessage(&message);
151910cd325cSAndrew Lindesay }
152010cd325cSAndrew Lindesay 
152110cd325cSAndrew Lindesay 
152210cd325cSAndrew Lindesay void
152310cd325cSAndrew Lindesay MainWindow::_HandleUserUsageConditionsNotLatest(
152410cd325cSAndrew Lindesay 	const UserDetail& userDetail)
152510cd325cSAndrew Lindesay {
152610cd325cSAndrew Lindesay 	ToLatestUserUsageConditionsWindow* window =
152710cd325cSAndrew Lindesay 		new ToLatestUserUsageConditionsWindow(this, fModel, userDetail);
152810cd325cSAndrew Lindesay 	window->Show();
152910cd325cSAndrew Lindesay }
153010cd325cSAndrew Lindesay 
153110cd325cSAndrew Lindesay 
153210cd325cSAndrew Lindesay void
153310cd325cSAndrew Lindesay MainWindow::_AddProcessCoordinator(ProcessCoordinator* item)
153410cd325cSAndrew Lindesay {
153510cd325cSAndrew Lindesay 	AutoLocker<BLocker> lock(&fCoordinatorLock);
153610cd325cSAndrew Lindesay 
153795c7b018SAndrew Lindesay 	if (fShouldCloseWhenNoProcessesToCoordinate) {
153895c7b018SAndrew Lindesay 		HDINFO("system shutting down --> new process coordinator [%s] rejected",
153995c7b018SAndrew Lindesay 			item->Name().String());
154095c7b018SAndrew Lindesay 		return;
154195c7b018SAndrew Lindesay 	}
154295c7b018SAndrew Lindesay 
1543a18f873fSAndrew Lindesay 	item->SetListener(this);
1544a18f873fSAndrew Lindesay 
1545409af934SAndrew Lindesay 	if (fCoordinator == NULL) {
154610cd325cSAndrew Lindesay 		if (acquire_sem(fCoordinatorRunningSem) != B_OK)
154710cd325cSAndrew Lindesay 			debugger("unable to acquire the process coordinator sem");
1548f96d1f4dSAndrew Lindesay 		HDINFO("adding and starting a process coordinator [%s]",
1549fa5c8097SAndrew Lindesay 			item->Name().String());
1550409af934SAndrew Lindesay 		delete fCoordinator;
1551409af934SAndrew Lindesay 		fCoordinator = item;
155210cd325cSAndrew Lindesay 		fCoordinator->Start();
15537d7de135SAugustin Cavalier 	} else {
1554f96d1f4dSAndrew Lindesay 		HDINFO("adding process coordinator [%s] to the queue",
155510cd325cSAndrew Lindesay 			item->Name().String());
155610cd325cSAndrew Lindesay 		fCoordinatorQueue.push(item);
155710cd325cSAndrew Lindesay 	}
155810cd325cSAndrew Lindesay }
155910cd325cSAndrew Lindesay 
156010cd325cSAndrew Lindesay 
156110cd325cSAndrew Lindesay void
156210cd325cSAndrew Lindesay MainWindow::_SpinUntilProcessCoordinatorComplete()
156310cd325cSAndrew Lindesay {
156410cd325cSAndrew Lindesay 	while (true) {
156510cd325cSAndrew Lindesay 		if (acquire_sem(fCoordinatorRunningSem) != B_OK)
156610cd325cSAndrew Lindesay 			debugger("unable to acquire the process coordinator sem");
156710cd325cSAndrew Lindesay 		if (release_sem(fCoordinatorRunningSem) != B_OK)
156810cd325cSAndrew Lindesay 			debugger("unable to release the process coordinator sem");
156910cd325cSAndrew Lindesay 		{
157010cd325cSAndrew Lindesay 			AutoLocker<BLocker> lock(&fCoordinatorLock);
1571409af934SAndrew Lindesay 			if (fCoordinator == NULL)
157210cd325cSAndrew Lindesay 				return;
157310cd325cSAndrew Lindesay 		}
157410cd325cSAndrew Lindesay 	}
157510cd325cSAndrew Lindesay }
157610cd325cSAndrew Lindesay 
157710cd325cSAndrew Lindesay 
157810cd325cSAndrew Lindesay void
157910cd325cSAndrew Lindesay MainWindow::_StopProcessCoordinators()
158010cd325cSAndrew Lindesay {
158195c7b018SAndrew Lindesay 	HDINFO("will stop all queued process coordinators");
158210cd325cSAndrew Lindesay 	AutoLocker<BLocker> lock(&fCoordinatorLock);
158310cd325cSAndrew Lindesay 
158410cd325cSAndrew Lindesay 	while (!fCoordinatorQueue.empty()) {
1585409af934SAndrew Lindesay 		ProcessCoordinator* processCoordinator
1586f96d1f4dSAndrew Lindesay 			= fCoordinatorQueue.front();
1587f96d1f4dSAndrew Lindesay 		HDINFO("will drop queued process coordinator [%s]",
1588fa5c8097SAndrew Lindesay 			processCoordinator->Name().String());
158910cd325cSAndrew Lindesay 		fCoordinatorQueue.pop();
1590409af934SAndrew Lindesay 		delete processCoordinator;
159110cd325cSAndrew Lindesay 	}
159210cd325cSAndrew Lindesay 
1593409af934SAndrew Lindesay 	if (fCoordinator != NULL)
159495c7b018SAndrew Lindesay 		fCoordinator->RequestStop();
159510cd325cSAndrew Lindesay }
159610cd325cSAndrew Lindesay 
159710cd325cSAndrew Lindesay 
159810cd325cSAndrew Lindesay /*! This method is called when there is some change in the bulk load process
159910cd325cSAndrew Lindesay 	or other process coordinator.
160010cd325cSAndrew Lindesay 	A change may mean that a new process has started / stopped etc... or it
160110cd325cSAndrew Lindesay 	may mean that the entire coordinator has finished.
160210cd325cSAndrew Lindesay */
160310cd325cSAndrew Lindesay 
160410cd325cSAndrew Lindesay void
160510cd325cSAndrew Lindesay MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState)
160610cd325cSAndrew Lindesay {
1607409af934SAndrew Lindesay 	BMessage message(MSG_PROCESS_COORDINATOR_CHANGED);
1608409af934SAndrew Lindesay 	if (coordinatorState.Archive(&message, true) != B_OK) {
1609409af934SAndrew Lindesay 		HDFATAL("unable to archive message when the process coordinator"
1610409af934SAndrew Lindesay 			" has changed");
1611409af934SAndrew Lindesay 	}
1612409af934SAndrew Lindesay 	BMessenger(this).SendMessage(&message);
1613409af934SAndrew Lindesay }
1614409af934SAndrew Lindesay 
1615409af934SAndrew Lindesay 
1616409af934SAndrew Lindesay void
1617409af934SAndrew Lindesay MainWindow::_HandleProcessCoordinatorChanged(ProcessCoordinatorState& coordinatorState)
1618409af934SAndrew Lindesay {
161910cd325cSAndrew Lindesay 	AutoLocker<BLocker> lock(&fCoordinatorLock);
162010cd325cSAndrew Lindesay 
1621409af934SAndrew Lindesay 	if (fCoordinator->Identifier()
1622409af934SAndrew Lindesay 			== coordinatorState.ProcessCoordinatorIdentifier()) {
162310cd325cSAndrew Lindesay 		if (!coordinatorState.IsRunning()) {
162410cd325cSAndrew Lindesay 			if (release_sem(fCoordinatorRunningSem) != B_OK)
162510cd325cSAndrew Lindesay 				debugger("unable to release the process coordinator sem");
1626f96d1f4dSAndrew Lindesay 			HDINFO("process coordinator [%s] did complete",
1627fa5c8097SAndrew Lindesay 				fCoordinator->Name().String());
162810cd325cSAndrew Lindesay 			// complete the last one that just finished
162910cd325cSAndrew Lindesay 			BMessage* message = fCoordinator->Message();
163010cd325cSAndrew Lindesay 
163110cd325cSAndrew Lindesay 			if (message != NULL) {
163210cd325cSAndrew Lindesay 				BMessenger messenger(this);
163310cd325cSAndrew Lindesay 				message->AddInt64(KEY_ERROR_STATUS,
163410cd325cSAndrew Lindesay 					(int64) fCoordinator->ErrorStatus());
163510cd325cSAndrew Lindesay 				messenger.SendMessage(message);
163610cd325cSAndrew Lindesay 			}
163710cd325cSAndrew Lindesay 
16383538133fSAndrew Lindesay 			HDDEBUG("process coordinator report;\n---\n%s\n----",
16393538133fSAndrew Lindesay 				fCoordinator->LogReport().String());
16403538133fSAndrew Lindesay 
1641409af934SAndrew Lindesay 			delete fCoordinator;
1642409af934SAndrew Lindesay 			fCoordinator = NULL;
164310cd325cSAndrew Lindesay 
164410cd325cSAndrew Lindesay 			// now schedule the next one.
164510cd325cSAndrew Lindesay 			if (!fCoordinatorQueue.empty()) {
164610cd325cSAndrew Lindesay 				if (acquire_sem(fCoordinatorRunningSem) != B_OK)
164710cd325cSAndrew Lindesay 					debugger("unable to acquire the process coordinator sem");
164810cd325cSAndrew Lindesay 				fCoordinator = fCoordinatorQueue.front();
1649f96d1f4dSAndrew Lindesay 				HDINFO("starting next process coordinator [%s]",
165010cd325cSAndrew Lindesay 					fCoordinator->Name().String());
165110cd325cSAndrew Lindesay 				fCoordinatorQueue.pop();
165210cd325cSAndrew Lindesay 				fCoordinator->Start();
165310cd325cSAndrew Lindesay 			}
165410cd325cSAndrew Lindesay 			else {
165510cd325cSAndrew Lindesay 				_NotifyWorkStatusClear();
165695c7b018SAndrew Lindesay 				if (fShouldCloseWhenNoProcessesToCoordinate) {
165795c7b018SAndrew Lindesay 					HDINFO("no more processes to coord --> will quit");
165895c7b018SAndrew Lindesay 					BMessage message(B_QUIT_REQUESTED);
165995c7b018SAndrew Lindesay 					PostMessage(&message);
166095c7b018SAndrew Lindesay 				}
166110cd325cSAndrew Lindesay 			}
166210cd325cSAndrew Lindesay 		}
166310cd325cSAndrew Lindesay 		else {
166410cd325cSAndrew Lindesay 			_NotifyWorkStatusChange(coordinatorState.Message(),
166510cd325cSAndrew Lindesay 				coordinatorState.Progress());
166610cd325cSAndrew Lindesay 				// show the progress to the user.
166710cd325cSAndrew Lindesay 		}
166895c7b018SAndrew Lindesay 	} else {
166995c7b018SAndrew Lindesay 		_NotifyWorkStatusClear();
1670fa5c8097SAndrew Lindesay 		HDINFO("! unknown process coordinator changed");
167110cd325cSAndrew Lindesay 	}
167295c7b018SAndrew Lindesay }
1673ccf707d0SAndrew Lindesay 
1674ccf707d0SAndrew Lindesay 
1675ccf707d0SAndrew Lindesay static package_list_view_mode
1676ccf707d0SAndrew Lindesay main_window_tab_to_package_list_view_mode(int32 tab)
1677ccf707d0SAndrew Lindesay {
1678ccf707d0SAndrew Lindesay 	if (tab == TAB_PROMINENT_PACKAGES)
1679ccf707d0SAndrew Lindesay 		return PROMINENT;
1680ccf707d0SAndrew Lindesay 	return ALL;
1681ccf707d0SAndrew Lindesay }
1682ccf707d0SAndrew Lindesay 
1683ccf707d0SAndrew Lindesay 
1684ccf707d0SAndrew Lindesay void
1685ccf707d0SAndrew Lindesay MainWindow::_HandleChangePackageListViewMode()
1686ccf707d0SAndrew Lindesay {
1687ccf707d0SAndrew Lindesay 	package_list_view_mode tabMode = main_window_tab_to_package_list_view_mode(
1688ccf707d0SAndrew Lindesay 		fListTabs->Selection());
1689ccf707d0SAndrew Lindesay 	package_list_view_mode modelMode = fModel.PackageListViewMode();
1690ccf707d0SAndrew Lindesay 
1691ccf707d0SAndrew Lindesay 	if (tabMode != modelMode) {
1692ccf707d0SAndrew Lindesay 		BAutolock locker(fModel.Lock());
1693ccf707d0SAndrew Lindesay 		fModel.SetPackageListViewMode(tabMode);
1694ccf707d0SAndrew Lindesay 	}
1695ccf707d0SAndrew Lindesay }
169605880d13SAndrew Lindesay 
169705880d13SAndrew Lindesay 
169805880d13SAndrew Lindesay std::vector<DepotInfoRef>
169905880d13SAndrew Lindesay MainWindow::_CreateSnapshotOfDepots()
170005880d13SAndrew Lindesay {
170105880d13SAndrew Lindesay 	std::vector<DepotInfoRef> result;
170205880d13SAndrew Lindesay 	BAutolock locker(fModel.Lock());
170305880d13SAndrew Lindesay 	int32 countDepots = fModel.CountDepots();
170405880d13SAndrew Lindesay 	for(int32 i = 0; i < countDepots; i++)
170505880d13SAndrew Lindesay 		result.push_back(fModel.DepotAtIndex(i));
170605880d13SAndrew Lindesay 	return result;
170705880d13SAndrew Lindesay }
170888af15cfSAndrew Lindesay 
170988af15cfSAndrew Lindesay 
171088af15cfSAndrew Lindesay /*! This will get invoked in the case that a screenshot has been cached
171188af15cfSAndrew Lindesay     and so could now be loaded by some UI element. This method will then
171288af15cfSAndrew Lindesay     signal to other UI elements that they could load a screenshot should
171388af15cfSAndrew Lindesay     they have been waiting for it.
171488af15cfSAndrew Lindesay */
171588af15cfSAndrew Lindesay 
171688af15cfSAndrew Lindesay void
171788af15cfSAndrew Lindesay MainWindow::_HandleScreenshotCached(const BMessage* message)
171888af15cfSAndrew Lindesay {
171988af15cfSAndrew Lindesay 	ScreenshotCoordinate coordinate(message);
172088af15cfSAndrew Lindesay 	fPackageInfoView->HandleScreenshotCached(coordinate);
172188af15cfSAndrew Lindesay }
1722