Model.cpp revision 4fade61f
16d367ba4SStephan Aßmus/*
25bd5f161SStephan Aßmus * Copyright 2013-2014, Stephan A��mus <superstippi@gmx.de>.
3634c2c68SAxel Dörfler * Copyright 2014, Axel D��rfler <axeld@pinc-software.de>.
46d367ba4SStephan Aßmus * All rights reserved. Distributed under the terms of the MIT License.
56d367ba4SStephan Aßmus */
66d367ba4SStephan Aßmus
76d367ba4SStephan Aßmus#include "Model.h"
86d367ba4SStephan Aßmus
9726db742SStephan Aßmus#include <ctime>
10634c2c68SAxel Dörfler#include <stdarg.h>
116d367ba4SStephan Aßmus#include <stdio.h>
12726db742SStephan Aßmus#include <time.h>
136d367ba4SStephan Aßmus
147eec822dSStephan Aßmus#include <Autolock.h>
15f75ec10dSStephan Aßmus#include <Catalog.h>
16726db742SStephan Aßmus#include <Directory.h>
17726db742SStephan Aßmus#include <Entry.h>
18726db742SStephan Aßmus#include <File.h>
1989ec84a4SStephan Aßmus#include <KeyStore.h>
206e48c81bSStephan Aßmus#include <LocaleRoster.h>
21e3fdc894SStephan Aßmus#include <Message.h>
22726db742SStephan Aßmus#include <Path.h>
23f75ec10dSStephan Aßmus
24f75ec10dSStephan Aßmus
25f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
26f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
27f75ec10dSStephan Aßmus
286d367ba4SStephan Aßmus
2989ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3089ec84a4SStephan Aßmus
3189ec84a4SStephan Aßmus
328f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
338f03a0f9SStephan Aßmus{
348f03a0f9SStephan Aßmus}
354abd2b71SStephan Aßmus
364abd2b71SStephan Aßmus
378f03a0f9SStephan AßmusModelListener::~ModelListener()
384abd2b71SStephan Aßmus{
394abd2b71SStephan Aßmus}
404abd2b71SStephan Aßmus
414abd2b71SStephan Aßmus
428f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
438f03a0f9SStephan Aßmus
448f03a0f9SStephan Aßmus
454abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
464abd2b71SStephan Aßmuspublic:
47779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
484abd2b71SStephan Aßmus	{
494abd2b71SStephan Aßmus		return true;
504abd2b71SStephan Aßmus	}
514abd2b71SStephan Aßmus};
524abd2b71SStephan Aßmus
534abd2b71SStephan Aßmus
544abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
554abd2b71SStephan Aßmuspublic:
564abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
574abd2b71SStephan Aßmus		:
584abd2b71SStephan Aßmus		fDepot(depot)
594abd2b71SStephan Aßmus	{
604abd2b71SStephan Aßmus	}
611f67148fSRene Gollent
62779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
634abd2b71SStephan Aßmus	{
644abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
654abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
664abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
674abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
684abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
694abd2b71SStephan Aßmus		// expensive as is.
70634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
714abd2b71SStephan Aßmus	}
721f67148fSRene Gollent
730c169aceSStephan Aßmus	const BString& Depot() const
740c169aceSStephan Aßmus	{
750c169aceSStephan Aßmus		return fDepot.Name();
760c169aceSStephan Aßmus	}
770c169aceSStephan Aßmus
784abd2b71SStephan Aßmusprivate:
794abd2b71SStephan Aßmus	DepotInfo	fDepot;
804abd2b71SStephan Aßmus};
814abd2b71SStephan Aßmus
824abd2b71SStephan Aßmus
8342479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
8442479955SStephan Aßmuspublic:
8542479955SStephan Aßmus	CategoryFilter(const BString& category)
8642479955SStephan Aßmus		:
8742479955SStephan Aßmus		fCategory(category)
8842479955SStephan Aßmus	{
8942479955SStephan Aßmus	}
901f67148fSRene Gollent
91779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
9242479955SStephan Aßmus	{
93779d8213SStephan Aßmus		if (package.Get() == NULL)
94779d8213SStephan Aßmus			return false;
95634c2c68SAxel Dörfler
96779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
9742479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
9842479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
9942479955SStephan Aßmus			if (category.Get() == NULL)
10042479955SStephan Aßmus				continue;
10142479955SStephan Aßmus			if (category->Name() == fCategory)
10242479955SStephan Aßmus				return true;
10342479955SStephan Aßmus		}
10442479955SStephan Aßmus		return false;
10542479955SStephan Aßmus	}
10642479955SStephan Aßmus
1070c169aceSStephan Aßmus	const BString& Category() const
1080c169aceSStephan Aßmus	{
1090c169aceSStephan Aßmus		return fCategory;
1100c169aceSStephan Aßmus	}
1110c169aceSStephan Aßmus
11242479955SStephan Aßmusprivate:
11342479955SStephan Aßmus	BString		fCategory;
11442479955SStephan Aßmus};
11542479955SStephan Aßmus
11642479955SStephan Aßmus
1173a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1183a6ccc85SStephan Aßmuspublic:
1193a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1203a6ccc85SStephan Aßmus		:
1213a6ccc85SStephan Aßmus		fPackageList(packageList)
1223a6ccc85SStephan Aßmus	{
1233a6ccc85SStephan Aßmus	}
1241f67148fSRene Gollent
125779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1263a6ccc85SStephan Aßmus	{
1273a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1283a6ccc85SStephan Aßmus	}
1293a6ccc85SStephan Aßmus
1303a6ccc85SStephan Aßmusprivate:
1313a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1323a6ccc85SStephan Aßmus};
1333a6ccc85SStephan Aßmus
1343a6ccc85SStephan Aßmus
1353a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1363a6ccc85SStephan Aßmuspublic:
1373a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1383a6ccc85SStephan Aßmus		const PackageList& packageListB)
1393a6ccc85SStephan Aßmus		:
1403a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1413a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1423a6ccc85SStephan Aßmus	{
1433a6ccc85SStephan Aßmus	}
1441f67148fSRene Gollent
145779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1463a6ccc85SStephan Aßmus	{
1473a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1483a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1493a6ccc85SStephan Aßmus	}
1503a6ccc85SStephan Aßmus
1513a6ccc85SStephan Aßmusprivate:
1523a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1533a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1543a6ccc85SStephan Aßmus};
1553a6ccc85SStephan Aßmus
1563a6ccc85SStephan Aßmus
157634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
158634c2c68SAxel Dörflerpublic:
159634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
160634c2c68SAxel Dörfler	{
161634c2c68SAxel Dörfler		va_list args;
162634c2c68SAxel Dörfler		va_start(args, packageList);
163634c2c68SAxel Dörfler		while (true) {
164634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
165634c2c68SAxel Dörfler			if (packageList == NULL)
166634c2c68SAxel Dörfler				break;
167634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
168634c2c68SAxel Dörfler		}
169634c2c68SAxel Dörfler		va_end(args);
170634c2c68SAxel Dörfler	}
171634c2c68SAxel Dörfler
172634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
173634c2c68SAxel Dörfler	{
174634c2c68SAxel Dörfler		if (package.Get()==NULL)
175634c2c68SAxel Dörfler			return false;
176a10ad54cSJonathan Schleifer
177a10ad54cSJonathan Schleifer		printf("TEST %s\n", package->Title().String());
178a10ad54cSJonathan Schleifer
179634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
180634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
181998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
182634c2c68SAxel Dörfler				return false;
183634c2c68SAxel Dörfler			}
184634c2c68SAxel Dörfler		}
185634c2c68SAxel Dörfler		return true;
186634c2c68SAxel Dörfler	}
187634c2c68SAxel Dörfler
188634c2c68SAxel Dörflerprivate:
189634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
190634c2c68SAxel Dörfler};
191634c2c68SAxel Dörfler
192634c2c68SAxel Dörfler
193634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
194634c2c68SAxel Dörflerpublic:
195634c2c68SAxel Dörfler	StateFilter(PackageState state)
196634c2c68SAxel Dörfler		:
197634c2c68SAxel Dörfler		fState(state)
198634c2c68SAxel Dörfler	{
199634c2c68SAxel Dörfler	}
200634c2c68SAxel Dörfler
201634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
202634c2c68SAxel Dörfler	{
203634c2c68SAxel Dörfler		return package->State() == NONE;
204634c2c68SAxel Dörfler	}
205634c2c68SAxel Dörfler
206634c2c68SAxel Dörflerprivate:
207634c2c68SAxel Dörfler	PackageState	fState;
208634c2c68SAxel Dörfler};
209634c2c68SAxel Dörfler
210634c2c68SAxel Dörfler
21109d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
21209d44cecSStephan Aßmuspublic:
21309d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
21409d44cecSStephan Aßmus	{
21509d44cecSStephan Aßmus		// Separate the string into terms at spaces
21609d44cecSStephan Aßmus		int32 index = 0;
21709d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
21809d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
21909d44cecSStephan Aßmus			if (nextSpace < 0)
22009d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
22109d44cecSStephan Aßmus			if (nextSpace > index) {
22209d44cecSStephan Aßmus				BString term;
22309d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
22409d44cecSStephan Aßmus				term.ToLower();
22509d44cecSStephan Aßmus				fSearchTerms.Add(term);
22609d44cecSStephan Aßmus			}
22709d44cecSStephan Aßmus			index = nextSpace + 1;
22809d44cecSStephan Aßmus		}
22909d44cecSStephan Aßmus	}
2301f67148fSRene Gollent
231779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
23209d44cecSStephan Aßmus	{
233779d8213SStephan Aßmus		if (package.Get() == NULL)
234779d8213SStephan Aßmus			return false;
23509d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
23609d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
23709d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
238779d8213SStephan Aßmus			if (!_TextContains(package->Title(), term)
239779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
240779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
241779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
24209d44cecSStephan Aßmus				return false;
24309d44cecSStephan Aßmus			}
24409d44cecSStephan Aßmus		}
24509d44cecSStephan Aßmus		return true;
24609d44cecSStephan Aßmus	}
24709d44cecSStephan Aßmus
2480c169aceSStephan Aßmus	BString SearchTerms() const
2490c169aceSStephan Aßmus	{
2500c169aceSStephan Aßmus		BString searchTerms;
2510c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2520c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2530c169aceSStephan Aßmus			if (term.IsEmpty())
2540c169aceSStephan Aßmus				continue;
2550c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2560c169aceSStephan Aßmus				searchTerms.Append(" ");
2570c169aceSStephan Aßmus			searchTerms.Append(term);
2580c169aceSStephan Aßmus		}
2590c169aceSStephan Aßmus		return searchTerms;
2600c169aceSStephan Aßmus	}
2610c169aceSStephan Aßmus
26209d44cecSStephan Aßmusprivate:
26309d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
26409d44cecSStephan Aßmus	{
26509d44cecSStephan Aßmus		text.ToLower();
26609d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
26709d44cecSStephan Aßmus		return index >= 0;
26809d44cecSStephan Aßmus	}
26909d44cecSStephan Aßmus
27009d44cecSStephan Aßmusprivate:
2710c169aceSStephan Aßmus	StringList fSearchTerms;
27209d44cecSStephan Aßmus};
27309d44cecSStephan Aßmus
27409d44cecSStephan Aßmus
275870554acSStephan Aßmusstatic inline bool
276870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
277870554acSStephan Aßmus{
278870554acSStephan Aßmus	const BString& packageName = package->Title();
279870554acSStephan Aßmus	return packageName.EndsWith("_source");
280870554acSStephan Aßmus}
281870554acSStephan Aßmus
282870554acSStephan Aßmus
283870554acSStephan Aßmusstatic inline bool
284870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
285870554acSStephan Aßmus{
286870554acSStephan Aßmus	const BString& packageName = package->Title();
287870554acSStephan Aßmus	return packageName.EndsWith("_devel");
288870554acSStephan Aßmus}
289870554acSStephan Aßmus
290870554acSStephan Aßmus
2914abd2b71SStephan Aßmus// #pragma mark - Model
2924abd2b71SStephan Aßmus
2934abd2b71SStephan Aßmus
2946d367ba4SStephan AßmusModel::Model()
2956d367ba4SStephan Aßmus	:
296f75ec10dSStephan Aßmus	fDepots(),
297f75ec10dSStephan Aßmus
298f75ec10dSStephan Aßmus	fCategoryAudio(new PackageCategory(
299f75ec10dSStephan Aßmus		BitmapRef(),
300f75ec10dSStephan Aßmus		B_TRANSLATE("Audio"), "audio"), true),
3014a02fc4fSStephan Aßmus	fCategoryBusiness(new PackageCategory(
302f75ec10dSStephan Aßmus		BitmapRef(),
3034a02fc4fSStephan Aßmus		B_TRANSLATE("Business"), "business"), true),
3044a02fc4fSStephan Aßmus	fCategoryDevelopment(new PackageCategory(
3054a02fc4fSStephan Aßmus		BitmapRef(),
3064a02fc4fSStephan Aßmus		B_TRANSLATE("Development"), "development"), true),
3074a02fc4fSStephan Aßmus	fCategoryEducation(new PackageCategory(
3084a02fc4fSStephan Aßmus		BitmapRef(),
3094a02fc4fSStephan Aßmus		B_TRANSLATE("Education"), "education"), true),
3104a02fc4fSStephan Aßmus	fCategoryGames(new PackageCategory(
3114a02fc4fSStephan Aßmus		BitmapRef(),
3124a02fc4fSStephan Aßmus		B_TRANSLATE("Games"), "games"), true),
313f75ec10dSStephan Aßmus	fCategoryGraphics(new PackageCategory(
314f75ec10dSStephan Aßmus		BitmapRef(),
315f75ec10dSStephan Aßmus		B_TRANSLATE("Graphics"), "graphics"), true),
3164a02fc4fSStephan Aßmus	fCategoryInternetAndNetwork(new PackageCategory(
3174a02fc4fSStephan Aßmus		BitmapRef(),
3184a02fc4fSStephan Aßmus		B_TRANSLATE("Internet & Network"), "internetandnetwork"), true),
319f75ec10dSStephan Aßmus	fCategoryProductivity(new PackageCategory(
320f75ec10dSStephan Aßmus		BitmapRef(),
321f75ec10dSStephan Aßmus		B_TRANSLATE("Productivity"), "productivity"), true),
3224a02fc4fSStephan Aßmus	fCategoryScienceAndMathematics(new PackageCategory(
323f75ec10dSStephan Aßmus		BitmapRef(),
3244a02fc4fSStephan Aßmus		B_TRANSLATE("Science & Mathematics"), "scienceandmathematics"), true),
3254a02fc4fSStephan Aßmus	fCategorySystemAndUtilities(new PackageCategory(
326f75ec10dSStephan Aßmus		BitmapRef(),
3274a02fc4fSStephan Aßmus		B_TRANSLATE("System & Utilities"), "systemandutilities"), true),
3284a02fc4fSStephan Aßmus	fCategoryVideo(new PackageCategory(
3294abd2b71SStephan Aßmus		BitmapRef(),
3304a02fc4fSStephan Aßmus		B_TRANSLATE("Video"), "video"), true),
3314abd2b71SStephan Aßmus
3324abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
33309d44cecSStephan Aßmus	fDepotFilter(""),
334870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
335634c2c68SAxel Dörfler
3364fade61fSStephan Aßmus	fShowFeaturedPackages(true),
337ab172803SStephan Aßmus	fShowAvailablePackages(true),
3384fade61fSStephan Aßmus	fShowInstalledPackages(true),
339870554acSStephan Aßmus	fShowSourcePackages(false),
3405bd5f161SStephan Aßmus	fShowDevelopPackages(false),
3415bd5f161SStephan Aßmus
3425bd5f161SStephan Aßmus	fPopulateAllPackagesThread(-1),
3435bd5f161SStephan Aßmus	fStopPopulatingAllPackages(false)
3446d367ba4SStephan Aßmus{
345633ed4c4SStephan Aßmus	// Don't forget to add new categories to this list:
3464a02fc4fSStephan Aßmus	fCategories.Add(fCategoryGames);
3474a02fc4fSStephan Aßmus	fCategories.Add(fCategoryBusiness);
348633ed4c4SStephan Aßmus	fCategories.Add(fCategoryAudio);
349633ed4c4SStephan Aßmus	fCategories.Add(fCategoryVideo);
350633ed4c4SStephan Aßmus	fCategories.Add(fCategoryGraphics);
3514a02fc4fSStephan Aßmus	fCategories.Add(fCategoryEducation);
352633ed4c4SStephan Aßmus	fCategories.Add(fCategoryProductivity);
3534a02fc4fSStephan Aßmus	fCategories.Add(fCategorySystemAndUtilities);
3544a02fc4fSStephan Aßmus	fCategories.Add(fCategoryInternetAndNetwork);
355633ed4c4SStephan Aßmus	fCategories.Add(fCategoryDevelopment);
3564a02fc4fSStephan Aßmus	fCategories.Add(fCategoryScienceAndMathematics);
3572b411c02SStephan Aßmus	// TODO: The server will eventually support an API to
3582b411c02SStephan Aßmus	// get the defined categories and their translated names.
3592b411c02SStephan Aßmus	// This should then be used instead of hard-coded
3602b411c02SStephan Aßmus	// categories and translations in the app.
361d07ca069SStephan Aßmus
362d07ca069SStephan Aßmus	fPreferredLanguage = "en";
363d07ca069SStephan Aßmus	BLocaleRoster* localeRoster = BLocaleRoster::Default();
364d07ca069SStephan Aßmus	if (localeRoster != NULL) {
365d07ca069SStephan Aßmus		BMessage preferredLanguages;
366d07ca069SStephan Aßmus		if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
367d07ca069SStephan Aßmus			BString language;
368d07ca069SStephan Aßmus			if (preferredLanguages.FindString("language", 0, &language) == B_OK)
369d07ca069SStephan Aßmus				language.CopyInto(fPreferredLanguage, 0, 2);
370d07ca069SStephan Aßmus		}
371d07ca069SStephan Aßmus	}
3724ff3d17aSStephan Aßmus
3734ff3d17aSStephan Aßmus	// TODO: Fetch this from the web-app.
3744ff3d17aSStephan Aßmus	fSupportedLanguages.Add("en");
3754ff3d17aSStephan Aßmus	fSupportedLanguages.Add("de");
3764ff3d17aSStephan Aßmus	fSupportedLanguages.Add("fr");
3774ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ja");
3784ff3d17aSStephan Aßmus	fSupportedLanguages.Add("es");
3794ff3d17aSStephan Aßmus	fSupportedLanguages.Add("zh");
3804ff3d17aSStephan Aßmus	fSupportedLanguages.Add("pt");
3814ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ru");
3824ff3d17aSStephan Aßmus
3834ff3d17aSStephan Aßmus	if (!fSupportedLanguages.Contains(fPreferredLanguage)) {
38412f29d44SStephan Aßmus		// Force the preferred language to one of the currently supported
38512f29d44SStephan Aßmus		// ones, until the web application supports all ISO language codes.
3864ff3d17aSStephan Aßmus		printf("User preferred language '%s' not currently supported, "
3874ff3d17aSStephan Aßmus			"defaulting to 'en'.", fPreferredLanguage.String());
38812f29d44SStephan Aßmus		fPreferredLanguage = "en";
38912f29d44SStephan Aßmus	}
39028075ee4SStephan Aßmus	fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
3916d367ba4SStephan Aßmus}
3926d367ba4SStephan Aßmus
3936d367ba4SStephan Aßmus
3945bd5f161SStephan AßmusModel::~Model()
3955bd5f161SStephan Aßmus{
3965bd5f161SStephan Aßmus	StopPopulatingAllPackages();
3975bd5f161SStephan Aßmus}
3985bd5f161SStephan Aßmus
3995bd5f161SStephan Aßmus
4008f03a0f9SStephan Aßmusbool
4018f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
4028f03a0f9SStephan Aßmus{
4038f03a0f9SStephan Aßmus	return fListeners.Add(listener);
4048f03a0f9SStephan Aßmus}
4058f03a0f9SStephan Aßmus
4068f03a0f9SStephan Aßmus
407706edd80SStephan AßmusPackageList
4086d367ba4SStephan AßmusModel::CreatePackageList() const
4096d367ba4SStephan Aßmus{
410ab172803SStephan Aßmus	// Iterate all packages from all depots.
411ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
412706edd80SStephan Aßmus	PackageList resultList;
4136d367ba4SStephan Aßmus
4146d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
41509d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
4161f67148fSRene Gollent
41709d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
41809d44cecSStephan Aßmus			continue;
4191f67148fSRene Gollent
42009d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
4216d367ba4SStephan Aßmus
422706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
423779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
4244abd2b71SStephan Aßmus			if (fCategoryFilter->AcceptsPackage(package)
425870554acSStephan Aßmus				&& fSearchTermsFilter->AcceptsPackage(package)
4264fade61fSStephan Aßmus				&& (package->IsProminent() || !fShowFeaturedPackages)
427ab172803SStephan Aßmus				&& (fShowAvailablePackages || package->State() != NONE)
428ab172803SStephan Aßmus				&& (fShowInstalledPackages || package->State() != ACTIVATED)
429870554acSStephan Aßmus				&& (fShowSourcePackages || !is_source_package(package))
430870554acSStephan Aßmus				&& (fShowDevelopPackages || !is_develop_package(package))) {
4314abd2b71SStephan Aßmus				resultList.Add(package);
4324abd2b71SStephan Aßmus			}
4336d367ba4SStephan Aßmus		}
4346d367ba4SStephan Aßmus	}
4356d367ba4SStephan Aßmus
4366d367ba4SStephan Aßmus	return resultList;
4376d367ba4SStephan Aßmus}
4386d367ba4SStephan Aßmus
4396d367ba4SStephan Aßmus
4406d367ba4SStephan Aßmusbool
4416d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4426d367ba4SStephan Aßmus{
4436d367ba4SStephan Aßmus	return fDepots.Add(depot);
4446d367ba4SStephan Aßmus}
4456d367ba4SStephan Aßmus
44642479955SStephan Aßmus
4472c287f5bSRene Gollentvoid
4482c287f5bSRene GollentModel::Clear()
4492c287f5bSRene Gollent{
4502c287f5bSRene Gollent	fDepots.Clear();
4512c287f5bSRene Gollent}
4522c287f5bSRene Gollent
4532c287f5bSRene Gollent
454706edd80SStephan Aßmusvoid
455779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
456706edd80SStephan Aßmus{
457706edd80SStephan Aßmus	switch (state) {
458706edd80SStephan Aßmus		default:
459706edd80SStephan Aßmus		case NONE:
460706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
461706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
462706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
463706edd80SStephan Aßmus			break;
464706edd80SStephan Aßmus		case INSTALLED:
465706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
466706edd80SStephan Aßmus				fInstalledPackages.Add(package);
467706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
468706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
469706edd80SStephan Aßmus			break;
470706edd80SStephan Aßmus		case ACTIVATED:
471706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
472706edd80SStephan Aßmus				fInstalledPackages.Add(package);
473706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
474706edd80SStephan Aßmus				fActivatedPackages.Add(package);
475706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
476706edd80SStephan Aßmus			break;
477706edd80SStephan Aßmus		case UNINSTALLED:
478706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
479706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
480ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
481ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
482706edd80SStephan Aßmus			break;
483706edd80SStephan Aßmus	}
484b4c8d2ffSRene Gollent
485b4c8d2ffSRene Gollent	package->SetState(state);
486706edd80SStephan Aßmus}
487706edd80SStephan Aßmus
488706edd80SStephan Aßmus
48909d44cecSStephan Aßmus// #pragma mark - filters
49009d44cecSStephan Aßmus
49109d44cecSStephan Aßmus
49242479955SStephan Aßmusvoid
49342479955SStephan AßmusModel::SetCategory(const BString& category)
49442479955SStephan Aßmus{
4953a6ccc85SStephan Aßmus	PackageFilter* filter;
4961f67148fSRene Gollent
49742479955SStephan Aßmus	if (category.Length() == 0)
4983a6ccc85SStephan Aßmus		filter = new AnyFilter();
49942479955SStephan Aßmus	else
5003a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5013a6ccc85SStephan Aßmus
5023a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
50342479955SStephan Aßmus}
50442479955SStephan Aßmus
50509d44cecSStephan Aßmus
5060c169aceSStephan AßmusBString
5070c169aceSStephan AßmusModel::Category() const
5080c169aceSStephan Aßmus{
5090c169aceSStephan Aßmus	CategoryFilter* filter
5100c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5110c169aceSStephan Aßmus	if (filter == NULL)
5120c169aceSStephan Aßmus		return "";
5130c169aceSStephan Aßmus	return filter->Category();
5140c169aceSStephan Aßmus}
5150c169aceSStephan Aßmus
5160c169aceSStephan Aßmus
51709d44cecSStephan Aßmusvoid
51809d44cecSStephan AßmusModel::SetDepot(const BString& depot)
51909d44cecSStephan Aßmus{
52009d44cecSStephan Aßmus	fDepotFilter = depot;
52109d44cecSStephan Aßmus}
52209d44cecSStephan Aßmus
52309d44cecSStephan Aßmus
5240c169aceSStephan AßmusBString
5250c169aceSStephan AßmusModel::Depot() const
5260c169aceSStephan Aßmus{
5270c169aceSStephan Aßmus	return fDepotFilter;
5280c169aceSStephan Aßmus}
5290c169aceSStephan Aßmus
5300c169aceSStephan Aßmus
53109d44cecSStephan Aßmusvoid
53209d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
53309d44cecSStephan Aßmus{
53409d44cecSStephan Aßmus	PackageFilter* filter;
5351f67148fSRene Gollent
53609d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
53709d44cecSStephan Aßmus		filter = new AnyFilter();
53809d44cecSStephan Aßmus	else
53909d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
54009d44cecSStephan Aßmus
54109d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
54209d44cecSStephan Aßmus}
54309d44cecSStephan Aßmus
5447eec822dSStephan Aßmus
5450c169aceSStephan AßmusBString
5460c169aceSStephan AßmusModel::SearchTerms() const
5470c169aceSStephan Aßmus{
5480c169aceSStephan Aßmus	SearchTermsFilter* filter
5490c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
5500c169aceSStephan Aßmus	if (filter == NULL)
5510c169aceSStephan Aßmus		return "";
5520c169aceSStephan Aßmus	return filter->SearchTerms();
5530c169aceSStephan Aßmus}
5540c169aceSStephan Aßmus
5550c169aceSStephan Aßmus
5564fade61fSStephan Aßmusvoid
5574fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
5584fade61fSStephan Aßmus{
5594fade61fSStephan Aßmus	fShowFeaturedPackages = show;
5604fade61fSStephan Aßmus}
5614fade61fSStephan Aßmus
5624fade61fSStephan Aßmus
563ab172803SStephan Aßmusvoid
564ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
565ab172803SStephan Aßmus{
566ab172803SStephan Aßmus	fShowAvailablePackages = show;
567ab172803SStephan Aßmus}
568ab172803SStephan Aßmus
569ab172803SStephan Aßmus
570ab172803SStephan Aßmusvoid
571ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
572ab172803SStephan Aßmus{
573ab172803SStephan Aßmus	fShowInstalledPackages = show;
574ab172803SStephan Aßmus}
575ab172803SStephan Aßmus
576ab172803SStephan Aßmus
577870554acSStephan Aßmusvoid
578870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
579870554acSStephan Aßmus{
580870554acSStephan Aßmus	fShowSourcePackages = show;
581870554acSStephan Aßmus}
582870554acSStephan Aßmus
583870554acSStephan Aßmus
584870554acSStephan Aßmusvoid
585870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
586870554acSStephan Aßmus{
587870554acSStephan Aßmus	fShowDevelopPackages = show;
588870554acSStephan Aßmus}
589870554acSStephan Aßmus
590870554acSStephan Aßmus
5917eec822dSStephan Aßmus// #pragma mark - information retrival
5927eec822dSStephan Aßmus
5937eec822dSStephan Aßmus
5947eec822dSStephan Aßmusvoid
595a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
5967eec822dSStephan Aßmus{
5977eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
5987eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
5997eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6007eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6017eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6027eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
60348045f5aSStephan Aßmus	{
60448045f5aSStephan Aßmus		BAutolock locker(&fLock);
60548045f5aSStephan Aßmus		if (fPopulatedPackages.Contains(package))
60648045f5aSStephan Aßmus			return;
60748045f5aSStephan Aßmus		fPopulatedPackages.Add(package);
60848045f5aSStephan Aßmus	}
6097eec822dSStephan Aßmus
6107ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6117ff94fd0SStephan Aßmus		// Retrieve info from web-app
6127ff94fd0SStephan Aßmus		BMessage info;
61348045f5aSStephan Aßmus
61448045f5aSStephan Aßmus		BString packageName;
61548045f5aSStephan Aßmus		BString architecture;
61648045f5aSStephan Aßmus		{
61748045f5aSStephan Aßmus			BAutolock locker(&fLock);
61848045f5aSStephan Aßmus			packageName = package->Title();
61948045f5aSStephan Aßmus			architecture = package->Architecture();
62048045f5aSStephan Aßmus		}
6217ff94fd0SStephan Aßmus
62228075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
62348045f5aSStephan Aßmus			architecture, 0, 50, info);
6247ff94fd0SStephan Aßmus		if (status == B_OK) {
6257ff94fd0SStephan Aßmus			// Parse message
6267ff94fd0SStephan Aßmus			BMessage result;
6277ff94fd0SStephan Aßmus			BMessage items;
6287ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6297ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
63048045f5aSStephan Aßmus
63148045f5aSStephan Aßmus				BAutolock locker(&fLock);
6327eb09bfaSStephan Aßmus				package->ClearUserRatings();
63348045f5aSStephan Aßmus
6347ff94fd0SStephan Aßmus				int index = 0;
6357ff94fd0SStephan Aßmus				while (true) {
6367ff94fd0SStephan Aßmus					BString name;
6377ff94fd0SStephan Aßmus					name << index++;
6387ff94fd0SStephan Aßmus
6397ff94fd0SStephan Aßmus					BMessage item;
6407ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6417ff94fd0SStephan Aßmus						break;
6427ff94fd0SStephan Aßmus//					item.PrintToStream();
6437ff94fd0SStephan Aßmus
6447ff94fd0SStephan Aßmus					BString user;
6457ff94fd0SStephan Aßmus					BMessage userInfo;
6467ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
6477ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
6487ff94fd0SStephan Aßmus						// Ignore, we need the user name
6497ff94fd0SStephan Aßmus						continue;
6507ff94fd0SStephan Aßmus					}
6517ff94fd0SStephan Aßmus
65236a9b557SStephan Aßmus					// Extract basic info, all items are optional
6537ff94fd0SStephan Aßmus					BString languageCode;
6547ff94fd0SStephan Aßmus					BString comment;
6557ff94fd0SStephan Aßmus					double rating;
65636a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
65736a9b557SStephan Aßmus					item.FindString("comment", &comment);
65836a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
65936a9b557SStephan Aßmus						rating = -1;
66036a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
66136a9b557SStephan Aßmus						// No useful information given.
6627ff94fd0SStephan Aßmus						continue;
6637ff94fd0SStephan Aßmus					}
66436a9b557SStephan Aßmus
6657ff94fd0SStephan Aßmus					// For which version of the package was the rating?
6667ff94fd0SStephan Aßmus					BString major = "?";
6677ff94fd0SStephan Aßmus					BString minor = "?";
6687ff94fd0SStephan Aßmus					BString micro = "";
6697ff94fd0SStephan Aßmus					BMessage version;
6707ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
6717ff94fd0SStephan Aßmus						version.FindString("major", &major);
6727ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
6737ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
6747ff94fd0SStephan Aßmus					}
6757ff94fd0SStephan Aßmus					BString versionString = major;
6767ff94fd0SStephan Aßmus					versionString << ".";
6777ff94fd0SStephan Aßmus					versionString << minor;
6787ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
6797ff94fd0SStephan Aßmus						versionString << ".";
6807ff94fd0SStephan Aßmus						versionString << micro;
6817ff94fd0SStephan Aßmus					}
6827ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
6837ff94fd0SStephan Aßmus					package->AddUserRating(
6847ff94fd0SStephan Aßmus						UserRating(UserInfo(user), rating,
6857ff94fd0SStephan Aßmus							comment, languageCode, versionString, 0, 0)
6867ff94fd0SStephan Aßmus					);
6877ff94fd0SStephan Aßmus				}
6887ff94fd0SStephan Aßmus			} else if (info.FindMessage("error", &result) == B_OK) {
6897ff94fd0SStephan Aßmus				result.PrintToStream();
6907ff94fd0SStephan Aßmus			}
691a60a23f8SStephan Aßmus		}
6927eec822dSStephan Aßmus	}
69387038da7SStephan Aßmus
69487038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
69587038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
69687038da7SStephan Aßmus		{
69787038da7SStephan Aßmus			BAutolock locker(&fLock);
69887038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
6997eb09bfaSStephan Aßmus			package->ClearScreenshots();
70087038da7SStephan Aßmus		}
70187038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
70287038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
703e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
70487038da7SStephan Aßmus		}
70587038da7SStephan Aßmus	}
7067eec822dSStephan Aßmus}
7075bd5f161SStephan Aßmus
7085bd5f161SStephan Aßmus
7095bd5f161SStephan Aßmusvoid
7105bd5f161SStephan AßmusModel::PopulateAllPackages()
7115bd5f161SStephan Aßmus{
7125bd5f161SStephan Aßmus	StopPopulatingAllPackages();
7135bd5f161SStephan Aßmus
7145bd5f161SStephan Aßmus	fStopPopulatingAllPackages = false;
7155bd5f161SStephan Aßmus
7165bd5f161SStephan Aßmus	fPopulateAllPackagesThread = spawn_thread(&_PopulateAllPackagesEntry,
7175bd5f161SStephan Aßmus		"Package populator", B_NORMAL_PRIORITY, this);
7185bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread >= 0)
7195bd5f161SStephan Aßmus		resume_thread(fPopulateAllPackagesThread);
7205bd5f161SStephan Aßmus}
7215bd5f161SStephan Aßmus
7225bd5f161SStephan Aßmus
7235bd5f161SStephan Aßmusvoid
7245bd5f161SStephan AßmusModel::StopPopulatingAllPackages()
7255bd5f161SStephan Aßmus{
7265bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread < 0)
7275bd5f161SStephan Aßmus		return;
7285bd5f161SStephan Aßmus
7295bd5f161SStephan Aßmus	fStopPopulatingAllPackages = true;
7305bd5f161SStephan Aßmus	wait_for_thread(fPopulateAllPackagesThread, NULL);
7315bd5f161SStephan Aßmus	fPopulateAllPackagesThread = -1;
7325bd5f161SStephan Aßmus}
7335bd5f161SStephan Aßmus
7345bd5f161SStephan Aßmus
73528075ee4SStephan Aßmusvoid
7368f03a0f9SStephan AßmusModel::SetUsername(BString username)
73789ec84a4SStephan Aßmus{
7388f03a0f9SStephan Aßmus	BString password;
7398f03a0f9SStephan Aßmus	if (username.Length() > 0) {
7408f03a0f9SStephan Aßmus		BPasswordKey key;
7418f03a0f9SStephan Aßmus		BKeyStore keyStore;
7428f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
7438f03a0f9SStephan Aßmus				key) == B_OK) {
7448f03a0f9SStephan Aßmus			password = key.Password();
7458f03a0f9SStephan Aßmus		} else {
7468f03a0f9SStephan Aßmus			username = "";
7478f03a0f9SStephan Aßmus		}
74889ec84a4SStephan Aßmus	}
7498f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
7508f03a0f9SStephan Aßmus}
7518f03a0f9SStephan Aßmus
7528f03a0f9SStephan Aßmus
7538f03a0f9SStephan Aßmusconst BString&
7548f03a0f9SStephan AßmusModel::Username() const
7558f03a0f9SStephan Aßmus{
7568f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
75789ec84a4SStephan Aßmus}
75889ec84a4SStephan Aßmus
75989ec84a4SStephan Aßmus
76089ec84a4SStephan Aßmusvoid
76189ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
76289ec84a4SStephan Aßmus	bool storePassword)
76328075ee4SStephan Aßmus{
7648f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
76589ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
76689ec84a4SStephan Aßmus		BKeyStore keyStore;
76789ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
76889ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
76989ec84a4SStephan Aßmus	}
77089ec84a4SStephan Aßmus
77128075ee4SStephan Aßmus	BAutolock locker(&fLock);
77228075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
7738f03a0f9SStephan Aßmus
7748f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
77528075ee4SStephan Aßmus}
77628075ee4SStephan Aßmus
77728075ee4SStephan Aßmus
77828075ee4SStephan Aßmus// #pragma mark - private
77928075ee4SStephan Aßmus
78028075ee4SStephan Aßmus
7815bd5f161SStephan Aßmusint32
7825bd5f161SStephan AßmusModel::_PopulateAllPackagesEntry(void* cookie)
7835bd5f161SStephan Aßmus{
7845bd5f161SStephan Aßmus	Model* model = static_cast<Model*>(cookie);
785726db742SStephan Aßmus	model->_PopulateAllPackagesThread(true);
786726db742SStephan Aßmus	model->_PopulateAllPackagesThread(false);
7875bd5f161SStephan Aßmus	return 0;
7885bd5f161SStephan Aßmus}
7895bd5f161SStephan Aßmus
7905bd5f161SStephan Aßmus
7915bd5f161SStephan Aßmusvoid
792726db742SStephan AßmusModel::_PopulateAllPackagesThread(bool fromCacheOnly)
7935bd5f161SStephan Aßmus{
7945bd5f161SStephan Aßmus	int32 depotIndex = 0;
7955bd5f161SStephan Aßmus	int32 packageIndex = 0;
796e3fdc894SStephan Aßmus	PackageList bulkPackageList;
797e3fdc894SStephan Aßmus	PackageList packagesWithIconsList;
7985bd5f161SStephan Aßmus
7995bd5f161SStephan Aßmus	while (!fStopPopulatingAllPackages) {
8005bd5f161SStephan Aßmus		// Obtain PackageInfoRef while keeping the depot and package lists
8015bd5f161SStephan Aßmus		// locked.
8025bd5f161SStephan Aßmus		PackageInfoRef package;
8035bd5f161SStephan Aßmus		{
8045bd5f161SStephan Aßmus			BAutolock locker(&fLock);
8055bd5f161SStephan Aßmus
8065bd5f161SStephan Aßmus			if (depotIndex >= fDepots.CountItems())
8075bd5f161SStephan Aßmus				break;
8085bd5f161SStephan Aßmus			const DepotInfo& depot = fDepots.ItemAt(depotIndex);
8095bd5f161SStephan Aßmus
8105bd5f161SStephan Aßmus			const PackageList& packages = depot.Packages();
8115bd5f161SStephan Aßmus			if (packageIndex >= packages.CountItems()) {
8125bd5f161SStephan Aßmus				// Need the next depot
8135bd5f161SStephan Aßmus				packageIndex = 0;
8145bd5f161SStephan Aßmus				depotIndex++;
8155bd5f161SStephan Aßmus				continue;
8165bd5f161SStephan Aßmus			}
8175bd5f161SStephan Aßmus
8185bd5f161SStephan Aßmus			package = packages.ItemAt(packageIndex);
8195bd5f161SStephan Aßmus			packageIndex++;
8205bd5f161SStephan Aßmus		}
8215bd5f161SStephan Aßmus
8225bd5f161SStephan Aßmus		if (package.Get() == NULL)
8235bd5f161SStephan Aßmus			continue;
8245bd5f161SStephan Aßmus
825e3fdc894SStephan Aßmus		//_PopulatePackageInfo(package, fromCacheOnly);
826e3fdc894SStephan Aßmus		bulkPackageList.Add(package);
827e3fdc894SStephan Aßmus		if (bulkPackageList.CountItems() == 50) {
828e3fdc894SStephan Aßmus			_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
829e3fdc894SStephan Aßmus				packagesWithIconsList);
830e3fdc894SStephan Aßmus			bulkPackageList.Clear();
831e3fdc894SStephan Aßmus		}
832e3fdc894SStephan Aßmus		if (fromCacheOnly)
833e3fdc894SStephan Aßmus			_PopulatePackageIcon(package, fromCacheOnly);
8345bd5f161SStephan Aßmus		// TODO: Average user rating. It needs to be shown in the
8355bd5f161SStephan Aßmus		// list view, so without the user clicking the package.
8365bd5f161SStephan Aßmus	}
837e3fdc894SStephan Aßmus
8386e48c81bSStephan Aßmus	if (bulkPackageList.CountItems() > 0) {
839e3fdc894SStephan Aßmus		_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
840e3fdc894SStephan Aßmus			packagesWithIconsList);
841e3fdc894SStephan Aßmus	}
842e3fdc894SStephan Aßmus
843e3fdc894SStephan Aßmus	if (!fromCacheOnly) {
844e3fdc894SStephan Aßmus		for (int i = packagesWithIconsList.CountItems() - 1; i >= 0; i--) {
845e3fdc894SStephan Aßmus			if (fStopPopulatingAllPackages)
846e3fdc894SStephan Aßmus				break;
847e3fdc894SStephan Aßmus			const PackageInfoRef& package = packagesWithIconsList.ItemAtFast(i);
848e3fdc894SStephan Aßmus			printf("Getting/Updating native icon for %s\n",
849e3fdc894SStephan Aßmus				package->Title().String());
850e3fdc894SStephan Aßmus			_PopulatePackageIcon(package, fromCacheOnly);
851e3fdc894SStephan Aßmus		}
852e3fdc894SStephan Aßmus	}
8535bd5f161SStephan Aßmus}
8545bd5f161SStephan Aßmus
8555bd5f161SStephan Aßmus
856a3dfc57dSStephan Aßmusbool
857a3dfc57dSStephan AßmusModel::_GetCacheFile(BPath& path, BFile& file, directory_which directory,
858a3dfc57dSStephan Aßmus	const char* relativeLocation, const char* fileName, uint32 openMode) const
859a3dfc57dSStephan Aßmus{
860a3dfc57dSStephan Aßmus	if (find_directory(directory, &path) == B_OK
861a3dfc57dSStephan Aßmus		&& path.Append(relativeLocation) == B_OK
862a3dfc57dSStephan Aßmus		&& create_directory(path.Path(), 0777) == B_OK
863a3dfc57dSStephan Aßmus		&& path.Append(fileName) == B_OK) {
864a3dfc57dSStephan Aßmus		// Try opening the file which will fail if its
865a3dfc57dSStephan Aßmus		// not a file or does not exist.
866a3dfc57dSStephan Aßmus		return file.SetTo(path.Path(), openMode) == B_OK;
867a3dfc57dSStephan Aßmus	}
868a3dfc57dSStephan Aßmus	return false;
869a3dfc57dSStephan Aßmus}
870a3dfc57dSStephan Aßmus
871a3dfc57dSStephan Aßmus
872a3dfc57dSStephan Aßmusbool
873a3dfc57dSStephan AßmusModel::_GetCacheFile(BPath& path, BFile& file, directory_which directory,
874a3dfc57dSStephan Aßmus	const char* relativeLocation, const char* fileName,
875a3dfc57dSStephan Aßmus	bool ignoreAge, time_t maxAge) const
876a3dfc57dSStephan Aßmus{
877a3dfc57dSStephan Aßmus	if (!_GetCacheFile(path, file, directory, relativeLocation, fileName,
878a3dfc57dSStephan Aßmus			B_READ_ONLY)) {
879a3dfc57dSStephan Aßmus		return false;
880a3dfc57dSStephan Aßmus	}
881a3dfc57dSStephan Aßmus
882a3dfc57dSStephan Aßmus	if (ignoreAge)
883a3dfc57dSStephan Aßmus		return true;
884a3dfc57dSStephan Aßmus
885a3dfc57dSStephan Aßmus	time_t modifiedTime;
886a3dfc57dSStephan Aßmus	file.GetModificationTime(&modifiedTime);
887a3dfc57dSStephan Aßmus	time_t now;
888a3dfc57dSStephan Aßmus	time(&now);
889a3dfc57dSStephan Aßmus	return now - modifiedTime < maxAge;
890a3dfc57dSStephan Aßmus}
891a3dfc57dSStephan Aßmus
892a3dfc57dSStephan Aßmus
893e08beebeSStephan Aßmusvoid
894e3fdc894SStephan AßmusModel::_PopulatePackageInfos(PackageList& packages, bool fromCacheOnly,
895e3fdc894SStephan Aßmus	PackageList& packagesWithIcons)
896e08beebeSStephan Aßmus{
8976e48c81bSStephan Aßmus	if (fStopPopulatingAllPackages)
8986e48c81bSStephan Aßmus		return;
8996e48c81bSStephan Aßmus
900a3dfc57dSStephan Aßmus	// See if there are cached info files
901a3dfc57dSStephan Aßmus	for (int i = packages.CountItems() - 1; i >= 0; i--) {
902a3dfc57dSStephan Aßmus		if (fStopPopulatingAllPackages)
903a3dfc57dSStephan Aßmus			return;
904a3dfc57dSStephan Aßmus
905a3dfc57dSStephan Aßmus		const PackageInfoRef& package = packages.ItemAtFast(i);
906a3dfc57dSStephan Aßmus
907a3dfc57dSStephan Aßmus		BFile file;
908a3dfc57dSStephan Aßmus		BPath path;
909a3dfc57dSStephan Aßmus		BString name(package->Title());
910a3dfc57dSStephan Aßmus		name << ".info";
911a3dfc57dSStephan Aßmus		if (_GetCacheFile(path, file, B_USER_CACHE_DIRECTORY,
912a3dfc57dSStephan Aßmus			"HaikuDepot", name, fromCacheOnly, 60 * 60)) {
913a3dfc57dSStephan Aßmus			// Cache file is recent enough, just use it and return.
914a3dfc57dSStephan Aßmus			BMessage pkgInfo;
915a3dfc57dSStephan Aßmus			if (pkgInfo.Unflatten(&file) == B_OK) {
916a3dfc57dSStephan Aßmus				_PopulatePackageInfo(package, pkgInfo);
917a3dfc57dSStephan Aßmus				if (_HasNativeIcon(pkgInfo))
918a3dfc57dSStephan Aßmus					packagesWithIcons.Add(package);
919a3dfc57dSStephan Aßmus				packages.Remove(i);
920a3dfc57dSStephan Aßmus			}
921a3dfc57dSStephan Aßmus		}
922a3dfc57dSStephan Aßmus	}
923a3dfc57dSStephan Aßmus
924a3dfc57dSStephan Aßmus	if (fromCacheOnly || packages.IsEmpty())
925e08beebeSStephan Aßmus		return;
926e08beebeSStephan Aßmus
927e08beebeSStephan Aßmus	// Retrieve info from web-app
928e08beebeSStephan Aßmus	BMessage info;
929e08beebeSStephan Aßmus
930e3fdc894SStephan Aßmus	StringList packageNames;
9312b411c02SStephan Aßmus	StringList packageArchitectures;
932e3fdc894SStephan Aßmus	for (int i = 0; i < packages.CountItems(); i++) {
933e3fdc894SStephan Aßmus		const PackageInfoRef& package = packages.ItemAtFast(i);
934e3fdc894SStephan Aßmus		packageNames.Add(package->Title());
9352b411c02SStephan Aßmus		packageArchitectures.Add(package->Architecture());
936e3fdc894SStephan Aßmus	}
937e3fdc894SStephan Aßmus
93828075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveBulkPackageInfo(packageNames,
9392b411c02SStephan Aßmus		packageArchitectures, info);
940e08beebeSStephan Aßmus	if (status == B_OK) {
94195e4ca3dSStephan Aßmus		// Parse message
942e3fdc894SStephan Aßmus//		info.PrintToStream();
94395e4ca3dSStephan Aßmus		BMessage result;
944e3fdc894SStephan Aßmus		BMessage pkgs;
945e3fdc894SStephan Aßmus		if (info.FindMessage("result", &result) == B_OK
946e3fdc894SStephan Aßmus			&& result.FindMessage("pkgs", &pkgs) == B_OK) {
947e3fdc894SStephan Aßmus			int32 index = 0;
948e3fdc894SStephan Aßmus			while (true) {
9496e48c81bSStephan Aßmus				if (fStopPopulatingAllPackages)
9506e48c81bSStephan Aßmus					return;
951e3fdc894SStephan Aßmus				BString name;
952e3fdc894SStephan Aßmus				name << index++;
953e3fdc894SStephan Aßmus				BMessage pkgInfo;
954e3fdc894SStephan Aßmus				if (pkgs.FindMessage(name, &pkgInfo) != B_OK)
955e3fdc894SStephan Aßmus					break;
956e3fdc894SStephan Aßmus
957e3fdc894SStephan Aßmus				BString pkgName;
958e3fdc894SStephan Aßmus				if (pkgInfo.FindString("name", &pkgName) != B_OK)
959e3fdc894SStephan Aßmus					continue;
960e3fdc894SStephan Aßmus
961e3fdc894SStephan Aßmus				// Find the PackageInfoRef
962e3fdc894SStephan Aßmus				bool found = false;
963e3fdc894SStephan Aßmus				for (int i = 0; i < packages.CountItems(); i++) {
964e3fdc894SStephan Aßmus					const PackageInfoRef& package = packages.ItemAtFast(i);
965e3fdc894SStephan Aßmus					if (pkgName == package->Title()) {
966e3fdc894SStephan Aßmus						_PopulatePackageInfo(package, pkgInfo);
967e3fdc894SStephan Aßmus						if (_HasNativeIcon(pkgInfo))
968e3fdc894SStephan Aßmus							packagesWithIcons.Add(package);
969a3dfc57dSStephan Aßmus
970a3dfc57dSStephan Aßmus						// Store in cache
971a3dfc57dSStephan Aßmus						BFile file;
972a3dfc57dSStephan Aßmus						BPath path;
973a3dfc57dSStephan Aßmus						BString fileName(package->Title());
974a3dfc57dSStephan Aßmus						fileName << ".info";
975a3dfc57dSStephan Aßmus						if (_GetCacheFile(path, file, B_USER_CACHE_DIRECTORY,
976a3dfc57dSStephan Aßmus								"HaikuDepot", fileName,
977a3dfc57dSStephan Aßmus								B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)) {
978a3dfc57dSStephan Aßmus							pkgInfo.Flatten(&file);
979a3dfc57dSStephan Aßmus						}
980a3dfc57dSStephan Aßmus
981e3fdc894SStephan Aßmus						packages.Remove(i);
982e3fdc894SStephan Aßmus						found = true;
98395e4ca3dSStephan Aßmus						break;
984e3fdc894SStephan Aßmus					}
98595e4ca3dSStephan Aßmus				}
986e3fdc894SStephan Aßmus				if (!found)
987e3fdc894SStephan Aßmus					printf("No matching package for %s\n", pkgName.String());
98895e4ca3dSStephan Aßmus			}
989e3fdc894SStephan Aßmus		}
990e3fdc894SStephan Aßmus	} else {
991e3fdc894SStephan Aßmus		printf("Error sending request: %s\n", strerror(status));
992e3fdc894SStephan Aßmus		int count = packages.CountItems();
993e3fdc894SStephan Aßmus		if (count >= 4) {
994e3fdc894SStephan Aßmus			// Retry in smaller chunks
995e3fdc894SStephan Aßmus			PackageList firstHalf;
996e3fdc894SStephan Aßmus			PackageList secondHalf;
997e3fdc894SStephan Aßmus			for (int i = 0; i < count / 2; i++)
998e3fdc894SStephan Aßmus				firstHalf.Add(packages.ItemAtFast(i));
999e3fdc894SStephan Aßmus			for (int i = count / 2; i < count; i++)
1000e3fdc894SStephan Aßmus				secondHalf.Add(packages.ItemAtFast(i));
1001e3fdc894SStephan Aßmus			packages.Clear();
1002e3fdc894SStephan Aßmus			_PopulatePackageInfos(firstHalf, fromCacheOnly, packagesWithIcons);
1003e3fdc894SStephan Aßmus			_PopulatePackageInfos(secondHalf, fromCacheOnly, packagesWithIcons);
1004e3fdc894SStephan Aßmus		} else {
1005e3fdc894SStephan Aßmus			while (packages.CountItems() > 0) {
1006e3fdc894SStephan Aßmus				const PackageInfoRef& package = packages.ItemAtFast(0);
1007e3fdc894SStephan Aßmus				_PopulatePackageInfo(package, fromCacheOnly);
1008e3fdc894SStephan Aßmus				packages.Remove(0);
100995e4ca3dSStephan Aßmus			}
101095e4ca3dSStephan Aßmus		}
1011e08beebeSStephan Aßmus	}
1012e3fdc894SStephan Aßmus
1013e3fdc894SStephan Aßmus	if (packages.CountItems() > 0) {
1014e3fdc894SStephan Aßmus		for (int i = 0; i < packages.CountItems(); i++) {
1015e3fdc894SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(i);
1016e3fdc894SStephan Aßmus			printf("No package info for %s\n", package->Title().String());
1017e3fdc894SStephan Aßmus		}
1018e3fdc894SStephan Aßmus	}
1019e3fdc894SStephan Aßmus}
1020e3fdc894SStephan Aßmus
1021e3fdc894SStephan Aßmus
1022e3fdc894SStephan Aßmusvoid
1023e3fdc894SStephan AßmusModel::_PopulatePackageInfo(const PackageInfoRef& package, bool fromCacheOnly)
1024e3fdc894SStephan Aßmus{
1025e3fdc894SStephan Aßmus	if (fromCacheOnly)
1026e3fdc894SStephan Aßmus		return;
1027e3fdc894SStephan Aßmus
1028e3fdc894SStephan Aßmus	// Retrieve info from web-app
1029e3fdc894SStephan Aßmus	BMessage info;
1030e3fdc894SStephan Aßmus
103128075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrievePackageInfo(package->Title(),
10326e48c81bSStephan Aßmus		package->Architecture(), info);
1033e3fdc894SStephan Aßmus	if (status == B_OK) {
1034e3fdc894SStephan Aßmus		// Parse message
1035e3fdc894SStephan Aßmus//		info.PrintToStream();
1036e3fdc894SStephan Aßmus		BMessage result;
1037e3fdc894SStephan Aßmus		if (info.FindMessage("result", &result) == B_OK)
1038e3fdc894SStephan Aßmus			_PopulatePackageInfo(package, result);
1039e3fdc894SStephan Aßmus	}
1040e3fdc894SStephan Aßmus}
1041e3fdc894SStephan Aßmus
1042e3fdc894SStephan Aßmus
10436e48c81bSStephan Aßmusstatic void
10446e48c81bSStephan Aßmusappend_word_list(BString& words, const char* word)
10456e48c81bSStephan Aßmus{
10466e48c81bSStephan Aßmus	if (words.Length() > 0)
10476e48c81bSStephan Aßmus		words << ", ";
10486e48c81bSStephan Aßmus	words << word;
10496e48c81bSStephan Aßmus}
10506e48c81bSStephan Aßmus
10516e48c81bSStephan Aßmus
1052e3fdc894SStephan Aßmusvoid
10536e48c81bSStephan AßmusModel::_PopulatePackageInfo(const PackageInfoRef& package, const BMessage& data)
1054e3fdc894SStephan Aßmus{
10556e48c81bSStephan Aßmus	BAutolock locker(&fLock);
10566e48c81bSStephan Aßmus
10576e48c81bSStephan Aßmus	BString foundInfo;
10586e48c81bSStephan Aßmus
10596e48c81bSStephan Aßmus	BMessage versions;
1060ab172803SStephan Aßmus	if (data.FindMessage("versions", &versions) == B_OK) {
1061ab172803SStephan Aßmus		// Search a summary and description in the preferred language
1062ab172803SStephan Aßmus		int32 index = 0;
1063ab172803SStephan Aßmus		while (true) {
1064ab172803SStephan Aßmus			BString name;
1065ab172803SStephan Aßmus			name << index++;
1066ab172803SStephan Aßmus			BMessage version;
1067ab172803SStephan Aßmus			if (versions.FindMessage(name, &version) != B_OK)
1068ab172803SStephan Aßmus				break;
1069ab172803SStephan Aßmus			BString languageCode;
1070ab172803SStephan Aßmus			if (version.FindString("naturalLanguageCode",
1071ab172803SStephan Aßmus					&languageCode) != B_OK
1072ab172803SStephan Aßmus				|| languageCode != fPreferredLanguage) {
1073ab172803SStephan Aßmus				continue;
10746e48c81bSStephan Aßmus			}
1075ab172803SStephan Aßmus
1076ab172803SStephan Aßmus			BString summary;
1077ab172803SStephan Aßmus			if (version.FindString("summary", &summary) == B_OK) {
1078ab172803SStephan Aßmus				package->SetShortDescription(summary);
1079ab172803SStephan Aßmus				append_word_list(foundInfo, "summary");
1080ab172803SStephan Aßmus			}
1081ab172803SStephan Aßmus			BString description;
1082ab172803SStephan Aßmus			if (version.FindString("description", &description) == B_OK) {
1083ab172803SStephan Aßmus				package->SetFullDescription(description);
1084ab172803SStephan Aßmus				append_word_list(foundInfo, "description");
1085ab172803SStephan Aßmus			}
1086ab172803SStephan Aßmus			break;
10876e48c81bSStephan Aßmus		}
10886e48c81bSStephan Aßmus	}
1089e3fdc894SStephan Aßmus
1090e3fdc894SStephan Aßmus	BMessage categories;
1091e3fdc894SStephan Aßmus	if (data.FindMessage("pkgCategoryCodes", &categories) == B_OK) {
10926e48c81bSStephan Aßmus		bool foundCategory = false;
1093e3fdc894SStephan Aßmus		int32 index = 0;
1094e3fdc894SStephan Aßmus		while (true) {
1095e3fdc894SStephan Aßmus			BString name;
1096e3fdc894SStephan Aßmus			name << index++;
1097e3fdc894SStephan Aßmus			BString category;
1098e3fdc894SStephan Aßmus			if (categories.FindString(name, &category) != B_OK)
1099e3fdc894SStephan Aßmus				break;
1100e3fdc894SStephan Aßmus
11017eb09bfaSStephan Aßmus			package->ClearCategories();
11022b411c02SStephan Aßmus			for (int i = fCategories.CountItems() - 1; i >= 0; i--) {
11032b411c02SStephan Aßmus				const CategoryRef& categoryRef = fCategories.ItemAtFast(i);
11042b411c02SStephan Aßmus				if (categoryRef->Name() == category) {
11052b411c02SStephan Aßmus					package->AddCategory(categoryRef);
11062b411c02SStephan Aßmus					foundCategory = true;
11072b411c02SStephan Aßmus					break;
11082b411c02SStephan Aßmus				}
11092b411c02SStephan Aßmus			}
1110e3fdc894SStephan Aßmus		}
11116e48c81bSStephan Aßmus		if (foundCategory)
11126e48c81bSStephan Aßmus			append_word_list(foundInfo, "categories");
1113e3fdc894SStephan Aßmus	}
11141d9d4873SStephan Aßmus
1115e3fdc894SStephan Aßmus	double derivedRating;
111636a9b557SStephan Aßmus	if (data.FindDouble("derivedRating", &derivedRating) == B_OK) {
111736a9b557SStephan Aßmus		RatingSummary summary;
111836a9b557SStephan Aßmus		summary.averageRating = derivedRating;
111936a9b557SStephan Aßmus		package->SetRatingSummary(summary);
112036a9b557SStephan Aßmus
112136a9b557SStephan Aßmus		append_word_list(foundInfo, "rating");
1122e3fdc894SStephan Aßmus	}
11231d9d4873SStephan Aßmus
11249f40cd41SStephan Aßmus	double prominenceOrdering;
11259f40cd41SStephan Aßmus	if (data.FindDouble("prominenceOrdering", &prominenceOrdering) == B_OK) {
11269f40cd41SStephan Aßmus		package->SetProminence(prominenceOrdering);
11279f40cd41SStephan Aßmus
11289f40cd41SStephan Aßmus		append_word_list(foundInfo, "prominence");
11299f40cd41SStephan Aßmus	}
11309f40cd41SStephan Aßmus
11311d9d4873SStephan Aßmus	BMessage screenshots;
11321d9d4873SStephan Aßmus	if (data.FindMessage("pkgScreenshots", &screenshots) == B_OK) {
11337eb09bfaSStephan Aßmus		package->ClearScreenshotInfos();
11341d9d4873SStephan Aßmus		bool foundScreenshot = false;
11351d9d4873SStephan Aßmus		int32 index = 0;
11361d9d4873SStephan Aßmus		while (true) {
11371d9d4873SStephan Aßmus			BString name;
11381d9d4873SStephan Aßmus			name << index++;
11391d9d4873SStephan Aßmus
11401d9d4873SStephan Aßmus			BMessage screenshot;
11411d9d4873SStephan Aßmus			if (screenshots.FindMessage(name, &screenshot) != B_OK)
11421d9d4873SStephan Aßmus				break;
11431d9d4873SStephan Aßmus
11441d9d4873SStephan Aßmus			BString code;
11451d9d4873SStephan Aßmus			double width;
11461d9d4873SStephan Aßmus			double height;
11471d9d4873SStephan Aßmus			double dataSize;
11481d9d4873SStephan Aßmus			if (screenshot.FindString("code", &code) == B_OK
11491d9d4873SStephan Aßmus				&& screenshot.FindDouble("width", &width) == B_OK
11501d9d4873SStephan Aßmus				&& screenshot.FindDouble("height", &height) == B_OK
11511d9d4873SStephan Aßmus				&& screenshot.FindDouble("length", &dataSize) == B_OK) {
11521d9d4873SStephan Aßmus				package->AddScreenshotInfo(ScreenshotInfo(code, (int32)width,
11531d9d4873SStephan Aßmus					(int32)height, (int32)dataSize));
11541d9d4873SStephan Aßmus				foundScreenshot = true;
11551d9d4873SStephan Aßmus			}
11561d9d4873SStephan Aßmus		}
11571d9d4873SStephan Aßmus		if (foundScreenshot)
11581d9d4873SStephan Aßmus			append_word_list(foundInfo, "screenshots");
11591d9d4873SStephan Aßmus	}
11601d9d4873SStephan Aßmus
11616e48c81bSStephan Aßmus	if (foundInfo.Length() > 0) {
11626e48c81bSStephan Aßmus		printf("Populated package info for %s: %s\n",
11636e48c81bSStephan Aßmus			package->Title().String(), foundInfo.String());
11646e48c81bSStephan Aßmus	}
116587038da7SStephan Aßmus
116687038da7SStephan Aßmus	// If the user already clicked this package, remove it from the
116787038da7SStephan Aßmus	// list of populated packages, so that clicking it again will
116887038da7SStephan Aßmus	// populate any additional information.
116987038da7SStephan Aßmus	// TODO: Trigger re-populating if the package is currently showing.
117087038da7SStephan Aßmus	fPopulatedPackages.Remove(package);
1171e08beebeSStephan Aßmus}
1172e08beebeSStephan Aßmus
1173e08beebeSStephan Aßmus
11745bd5f161SStephan Aßmusvoid
1175726db742SStephan AßmusModel::_PopulatePackageIcon(const PackageInfoRef& package, bool fromCacheOnly)
11765bd5f161SStephan Aßmus{
1177726db742SStephan Aßmus	// See if there is a cached icon file
1178726db742SStephan Aßmus	BFile iconFile;
1179726db742SStephan Aßmus	BPath iconCachePath;
1180726db742SStephan Aßmus	BString iconName(package->Title());
1181726db742SStephan Aßmus	iconName << ".hvif";
1182a3dfc57dSStephan Aßmus	if (_GetCacheFile(iconCachePath, iconFile, B_USER_CACHE_DIRECTORY,
1183a3dfc57dSStephan Aßmus		"HaikuDepot", iconName, fromCacheOnly, 60 * 60)) {
1184a3dfc57dSStephan Aßmus		// Cache file is recent enough, just use it and return.
1185a3dfc57dSStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(iconFile), true);
1186a3dfc57dSStephan Aßmus		BAutolock locker(&fLock);
1187a3dfc57dSStephan Aßmus		package->SetIcon(bitmapRef);
1188a3dfc57dSStephan Aßmus		return;
1189726db742SStephan Aßmus	}
1190726db742SStephan Aßmus
1191726db742SStephan Aßmus	if (fromCacheOnly)
1192726db742SStephan Aßmus		return;
1193726db742SStephan Aßmus
1194726db742SStephan Aßmus	// Retrieve icon from web-app
11955bd5f161SStephan Aßmus	BMallocIO buffer;
11965bd5f161SStephan Aßmus
119728075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrievePackageIcon(package->Title(),
119828075ee4SStephan Aßmus		&buffer);
11995bd5f161SStephan Aßmus	if (status == B_OK) {
12005bd5f161SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
12016e48c81bSStephan Aßmus		BAutolock locker(&fLock);
12025bd5f161SStephan Aßmus		package->SetIcon(bitmapRef);
12036e48c81bSStephan Aßmus		locker.Unlock();
1204726db742SStephan Aßmus		if (iconFile.SetTo(iconCachePath.Path(),
1205726db742SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
1206726db742SStephan Aßmus			iconFile.Write(buffer.Buffer(), buffer.BufferLength());
1207726db742SStephan Aßmus		}
12085bd5f161SStephan Aßmus	}
12095bd5f161SStephan Aßmus}
1210e3fdc894SStephan Aßmus
1211e3fdc894SStephan Aßmus
121287038da7SStephan Aßmusvoid
121387038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
121487038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
121587038da7SStephan Aßmus{
121687038da7SStephan Aßmus	// See if there is a cached screenshot
121787038da7SStephan Aßmus	BFile screenshotFile;
121887038da7SStephan Aßmus	BPath screenshotCachePath;
121987038da7SStephan Aßmus	bool fileExists = false;
122087038da7SStephan Aßmus	BString screenshotName(info.Code());
122187038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
122287038da7SStephan Aßmus	screenshotName << ".png";
122387038da7SStephan Aßmus	time_t modifiedTime;
122487038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
122587038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
122687038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
122787038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
122887038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
122987038da7SStephan Aßmus		// not a file or does not exist.
123087038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
123187038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
123287038da7SStephan Aßmus		if (fileExists)
123387038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
123487038da7SStephan Aßmus	}
123587038da7SStephan Aßmus
123687038da7SStephan Aßmus	if (fileExists) {
123787038da7SStephan Aßmus		time_t now;
123887038da7SStephan Aßmus		time(&now);
123987038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
124087038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
124187038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
124287038da7SStephan Aßmus				true);
124387038da7SStephan Aßmus			BAutolock locker(&fLock);
124487038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
124587038da7SStephan Aßmus			return;
124687038da7SStephan Aßmus		}
124787038da7SStephan Aßmus	}
124887038da7SStephan Aßmus
124987038da7SStephan Aßmus	if (fromCacheOnly)
125087038da7SStephan Aßmus		return;
125187038da7SStephan Aßmus
125287038da7SStephan Aßmus	// Retrieve screenshot from web-app
125387038da7SStephan Aßmus	BMallocIO buffer;
125487038da7SStephan Aßmus
125587038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
125687038da7SStephan Aßmus
125728075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
125887038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
125987038da7SStephan Aßmus	if (status == B_OK) {
126087038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
126187038da7SStephan Aßmus		BAutolock locker(&fLock);
126287038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
126387038da7SStephan Aßmus		locker.Unlock();
126487038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
126587038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
126687038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
126787038da7SStephan Aßmus		}
1268e0881f21SStephan Aßmus	} else {
1269e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
1270e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
1271e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
127287038da7SStephan Aßmus	}
127387038da7SStephan Aßmus}
127487038da7SStephan Aßmus
127587038da7SStephan Aßmus
1276e3fdc894SStephan Aßmusbool
1277e3fdc894SStephan AßmusModel::_HasNativeIcon(const BMessage& message) const
1278e3fdc894SStephan Aßmus{
1279e3fdc894SStephan Aßmus	BMessage pkgIcons;
1280e3fdc894SStephan Aßmus	if (message.FindMessage("pkgIcons", &pkgIcons) != B_OK)
1281e3fdc894SStephan Aßmus		return false;
1282e3fdc894SStephan Aßmus
1283e3fdc894SStephan Aßmus	int32 index = 0;
1284e3fdc894SStephan Aßmus	while (true) {
1285e3fdc894SStephan Aßmus		BString name;
1286e3fdc894SStephan Aßmus		name << index++;
1287e3fdc894SStephan Aßmus
1288e3fdc894SStephan Aßmus		BMessage typeCodeInfo;
1289e3fdc894SStephan Aßmus		if (pkgIcons.FindMessage(name, &typeCodeInfo) != B_OK)
1290e3fdc894SStephan Aßmus			break;
1291e3fdc894SStephan Aßmus
1292e3fdc894SStephan Aßmus		BString mediaTypeCode;
1293e3fdc894SStephan Aßmus		if (typeCodeInfo.FindString("mediaTypeCode", &mediaTypeCode) == B_OK
1294e3fdc894SStephan Aßmus			&& mediaTypeCode == "application/x-vnd.haiku-icon") {
1295e3fdc894SStephan Aßmus			return true;
1296e3fdc894SStephan Aßmus		}
1297e3fdc894SStephan Aßmus	}
1298e3fdc894SStephan Aßmus	return false;
1299e3fdc894SStephan Aßmus}
13008f03a0f9SStephan Aßmus
13018f03a0f9SStephan Aßmus
13028f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
13038f03a0f9SStephan Aßmus
13048f03a0f9SStephan Aßmus
13058f03a0f9SStephan Aßmusvoid
13068f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
13078f03a0f9SStephan Aßmus{
13088f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
13098f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
13108f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
13118f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
13128f03a0f9SStephan Aßmus	}
13138f03a0f9SStephan Aßmus}
13148f03a0f9SStephan Aßmus
1315