Model.cpp revision 0c169ace
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
336ab172803SStephan Aßmus	fShowAvailablePackages(true),
337ab172803SStephan Aßmus	fShowInstalledPackages(false),
338870554acSStephan Aßmus	fShowSourcePackages(false),
3395bd5f161SStephan Aßmus	fShowDevelopPackages(false),
3405bd5f161SStephan Aßmus
3415bd5f161SStephan Aßmus	fPopulateAllPackagesThread(-1),
3425bd5f161SStephan Aßmus	fStopPopulatingAllPackages(false)
3436d367ba4SStephan Aßmus{
344633ed4c4SStephan Aßmus	// Don't forget to add new categories to this list:
3454a02fc4fSStephan Aßmus	fCategories.Add(fCategoryGames);
3464a02fc4fSStephan Aßmus	fCategories.Add(fCategoryBusiness);
347633ed4c4SStephan Aßmus	fCategories.Add(fCategoryAudio);
348633ed4c4SStephan Aßmus	fCategories.Add(fCategoryVideo);
349633ed4c4SStephan Aßmus	fCategories.Add(fCategoryGraphics);
3504a02fc4fSStephan Aßmus	fCategories.Add(fCategoryEducation);
351633ed4c4SStephan Aßmus	fCategories.Add(fCategoryProductivity);
3524a02fc4fSStephan Aßmus	fCategories.Add(fCategorySystemAndUtilities);
3534a02fc4fSStephan Aßmus	fCategories.Add(fCategoryInternetAndNetwork);
354633ed4c4SStephan Aßmus	fCategories.Add(fCategoryDevelopment);
3554a02fc4fSStephan Aßmus	fCategories.Add(fCategoryScienceAndMathematics);
3562b411c02SStephan Aßmus	// TODO: The server will eventually support an API to
3572b411c02SStephan Aßmus	// get the defined categories and their translated names.
3582b411c02SStephan Aßmus	// This should then be used instead of hard-coded
3592b411c02SStephan Aßmus	// categories and translations in the app.
360d07ca069SStephan Aßmus
361d07ca069SStephan Aßmus	fPreferredLanguage = "en";
362d07ca069SStephan Aßmus	BLocaleRoster* localeRoster = BLocaleRoster::Default();
363d07ca069SStephan Aßmus	if (localeRoster != NULL) {
364d07ca069SStephan Aßmus		BMessage preferredLanguages;
365d07ca069SStephan Aßmus		if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
366d07ca069SStephan Aßmus			BString language;
367d07ca069SStephan Aßmus			if (preferredLanguages.FindString("language", 0, &language) == B_OK)
368d07ca069SStephan Aßmus				language.CopyInto(fPreferredLanguage, 0, 2);
369d07ca069SStephan Aßmus		}
370d07ca069SStephan Aßmus	}
3714ff3d17aSStephan Aßmus
3724ff3d17aSStephan Aßmus	// TODO: Fetch this from the web-app.
3734ff3d17aSStephan Aßmus	fSupportedLanguages.Add("en");
3744ff3d17aSStephan Aßmus	fSupportedLanguages.Add("de");
3754ff3d17aSStephan Aßmus	fSupportedLanguages.Add("fr");
3764ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ja");
3774ff3d17aSStephan Aßmus	fSupportedLanguages.Add("es");
3784ff3d17aSStephan Aßmus	fSupportedLanguages.Add("zh");
3794ff3d17aSStephan Aßmus	fSupportedLanguages.Add("pt");
3804ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ru");
3814ff3d17aSStephan Aßmus
3824ff3d17aSStephan Aßmus	if (!fSupportedLanguages.Contains(fPreferredLanguage)) {
38312f29d44SStephan Aßmus		// Force the preferred language to one of the currently supported
38412f29d44SStephan Aßmus		// ones, until the web application supports all ISO language codes.
3854ff3d17aSStephan Aßmus		printf("User preferred language '%s' not currently supported, "
3864ff3d17aSStephan Aßmus			"defaulting to 'en'.", fPreferredLanguage.String());
38712f29d44SStephan Aßmus		fPreferredLanguage = "en";
38812f29d44SStephan Aßmus	}
38928075ee4SStephan Aßmus	fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
3906d367ba4SStephan Aßmus}
3916d367ba4SStephan Aßmus
3926d367ba4SStephan Aßmus
3935bd5f161SStephan AßmusModel::~Model()
3945bd5f161SStephan Aßmus{
3955bd5f161SStephan Aßmus	StopPopulatingAllPackages();
3965bd5f161SStephan Aßmus}
3975bd5f161SStephan Aßmus
3985bd5f161SStephan Aßmus
3998f03a0f9SStephan Aßmusbool
4008f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
4018f03a0f9SStephan Aßmus{
4028f03a0f9SStephan Aßmus	return fListeners.Add(listener);
4038f03a0f9SStephan Aßmus}
4048f03a0f9SStephan Aßmus
4058f03a0f9SStephan Aßmus
406706edd80SStephan AßmusPackageList
4076d367ba4SStephan AßmusModel::CreatePackageList() const
4086d367ba4SStephan Aßmus{
409ab172803SStephan Aßmus	// Iterate all packages from all depots.
410ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
411706edd80SStephan Aßmus	PackageList resultList;
4126d367ba4SStephan Aßmus
4136d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
41409d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
4151f67148fSRene Gollent
41609d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
41709d44cecSStephan Aßmus			continue;
4181f67148fSRene Gollent
41909d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
4206d367ba4SStephan Aßmus
421706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
422779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
4234abd2b71SStephan Aßmus			if (fCategoryFilter->AcceptsPackage(package)
424870554acSStephan Aßmus				&& fSearchTermsFilter->AcceptsPackage(package)
425ab172803SStephan Aßmus				&& (fShowAvailablePackages || package->State() != NONE)
426ab172803SStephan Aßmus				&& (fShowInstalledPackages || package->State() != ACTIVATED)
427870554acSStephan Aßmus				&& (fShowSourcePackages || !is_source_package(package))
428870554acSStephan Aßmus				&& (fShowDevelopPackages || !is_develop_package(package))) {
4294abd2b71SStephan Aßmus				resultList.Add(package);
4304abd2b71SStephan Aßmus			}
4316d367ba4SStephan Aßmus		}
4326d367ba4SStephan Aßmus	}
4336d367ba4SStephan Aßmus
4346d367ba4SStephan Aßmus	return resultList;
4356d367ba4SStephan Aßmus}
4366d367ba4SStephan Aßmus
4376d367ba4SStephan Aßmus
4386d367ba4SStephan Aßmusbool
4396d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4406d367ba4SStephan Aßmus{
4416d367ba4SStephan Aßmus	return fDepots.Add(depot);
4426d367ba4SStephan Aßmus}
4436d367ba4SStephan Aßmus
44442479955SStephan Aßmus
4452c287f5bSRene Gollentvoid
4462c287f5bSRene GollentModel::Clear()
4472c287f5bSRene Gollent{
4482c287f5bSRene Gollent	fDepots.Clear();
4492c287f5bSRene Gollent}
4502c287f5bSRene Gollent
4512c287f5bSRene Gollent
452706edd80SStephan Aßmusvoid
453779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
454706edd80SStephan Aßmus{
455706edd80SStephan Aßmus	switch (state) {
456706edd80SStephan Aßmus		default:
457706edd80SStephan Aßmus		case NONE:
458706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
459706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
460706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
461706edd80SStephan Aßmus			break;
462706edd80SStephan Aßmus		case INSTALLED:
463706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
464706edd80SStephan Aßmus				fInstalledPackages.Add(package);
465706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
466706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
467706edd80SStephan Aßmus			break;
468706edd80SStephan Aßmus		case ACTIVATED:
469706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
470706edd80SStephan Aßmus				fInstalledPackages.Add(package);
471706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
472706edd80SStephan Aßmus				fActivatedPackages.Add(package);
473706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
474706edd80SStephan Aßmus			break;
475706edd80SStephan Aßmus		case UNINSTALLED:
476706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
477706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
478ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
479ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
480706edd80SStephan Aßmus			break;
481706edd80SStephan Aßmus	}
482b4c8d2ffSRene Gollent
483b4c8d2ffSRene Gollent	package->SetState(state);
484706edd80SStephan Aßmus}
485706edd80SStephan Aßmus
486706edd80SStephan Aßmus
48709d44cecSStephan Aßmus// #pragma mark - filters
48809d44cecSStephan Aßmus
48909d44cecSStephan Aßmus
49042479955SStephan Aßmusvoid
49142479955SStephan AßmusModel::SetCategory(const BString& category)
49242479955SStephan Aßmus{
4933a6ccc85SStephan Aßmus	PackageFilter* filter;
4941f67148fSRene Gollent
49542479955SStephan Aßmus	if (category.Length() == 0)
4963a6ccc85SStephan Aßmus		filter = new AnyFilter();
49742479955SStephan Aßmus	else
4983a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
4993a6ccc85SStephan Aßmus
5003a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
50142479955SStephan Aßmus}
50242479955SStephan Aßmus
50309d44cecSStephan Aßmus
5040c169aceSStephan AßmusBString
5050c169aceSStephan AßmusModel::Category() const
5060c169aceSStephan Aßmus{
5070c169aceSStephan Aßmus	CategoryFilter* filter
5080c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5090c169aceSStephan Aßmus	if (filter == NULL)
5100c169aceSStephan Aßmus		return "";
5110c169aceSStephan Aßmus	return filter->Category();
5120c169aceSStephan Aßmus}
5130c169aceSStephan Aßmus
5140c169aceSStephan Aßmus
51509d44cecSStephan Aßmusvoid
51609d44cecSStephan AßmusModel::SetDepot(const BString& depot)
51709d44cecSStephan Aßmus{
51809d44cecSStephan Aßmus	fDepotFilter = depot;
51909d44cecSStephan Aßmus}
52009d44cecSStephan Aßmus
52109d44cecSStephan Aßmus
5220c169aceSStephan AßmusBString
5230c169aceSStephan AßmusModel::Depot() const
5240c169aceSStephan Aßmus{
5250c169aceSStephan Aßmus	return fDepotFilter;
5260c169aceSStephan Aßmus}
5270c169aceSStephan Aßmus
5280c169aceSStephan Aßmus
52909d44cecSStephan Aßmusvoid
53009d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
53109d44cecSStephan Aßmus{
53209d44cecSStephan Aßmus	PackageFilter* filter;
5331f67148fSRene Gollent
53409d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
53509d44cecSStephan Aßmus		filter = new AnyFilter();
53609d44cecSStephan Aßmus	else
53709d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
53809d44cecSStephan Aßmus
53909d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
54009d44cecSStephan Aßmus}
54109d44cecSStephan Aßmus
5427eec822dSStephan Aßmus
5430c169aceSStephan AßmusBString
5440c169aceSStephan AßmusModel::SearchTerms() const
5450c169aceSStephan Aßmus{
5460c169aceSStephan Aßmus	SearchTermsFilter* filter
5470c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
5480c169aceSStephan Aßmus	if (filter == NULL)
5490c169aceSStephan Aßmus		return "";
5500c169aceSStephan Aßmus	return filter->SearchTerms();
5510c169aceSStephan Aßmus}
5520c169aceSStephan Aßmus
5530c169aceSStephan Aßmus
554ab172803SStephan Aßmusvoid
555ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
556ab172803SStephan Aßmus{
557ab172803SStephan Aßmus	fShowAvailablePackages = show;
558ab172803SStephan Aßmus}
559ab172803SStephan Aßmus
560ab172803SStephan Aßmus
561ab172803SStephan Aßmusvoid
562ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
563ab172803SStephan Aßmus{
564ab172803SStephan Aßmus	fShowInstalledPackages = show;
565ab172803SStephan Aßmus}
566ab172803SStephan Aßmus
567ab172803SStephan Aßmus
568870554acSStephan Aßmusvoid
569870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
570870554acSStephan Aßmus{
571870554acSStephan Aßmus	fShowSourcePackages = show;
572870554acSStephan Aßmus}
573870554acSStephan Aßmus
574870554acSStephan Aßmus
575870554acSStephan Aßmusvoid
576870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
577870554acSStephan Aßmus{
578870554acSStephan Aßmus	fShowDevelopPackages = show;
579870554acSStephan Aßmus}
580870554acSStephan Aßmus
581870554acSStephan Aßmus
5827eec822dSStephan Aßmus// #pragma mark - information retrival
5837eec822dSStephan Aßmus
5847eec822dSStephan Aßmus
5857eec822dSStephan Aßmusvoid
586a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
5877eec822dSStephan Aßmus{
5887eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
5897eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
5907eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
5917eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
5927eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
5937eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
59448045f5aSStephan Aßmus	{
59548045f5aSStephan Aßmus		BAutolock locker(&fLock);
59648045f5aSStephan Aßmus		if (fPopulatedPackages.Contains(package))
59748045f5aSStephan Aßmus			return;
59848045f5aSStephan Aßmus		fPopulatedPackages.Add(package);
59948045f5aSStephan Aßmus	}
6007eec822dSStephan Aßmus
6017ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6027ff94fd0SStephan Aßmus		// Retrieve info from web-app
6037ff94fd0SStephan Aßmus		BMessage info;
60448045f5aSStephan Aßmus
60548045f5aSStephan Aßmus		BString packageName;
60648045f5aSStephan Aßmus		BString architecture;
60748045f5aSStephan Aßmus		{
60848045f5aSStephan Aßmus			BAutolock locker(&fLock);
60948045f5aSStephan Aßmus			packageName = package->Title();
61048045f5aSStephan Aßmus			architecture = package->Architecture();
61148045f5aSStephan Aßmus		}
6127ff94fd0SStephan Aßmus
61328075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
61448045f5aSStephan Aßmus			architecture, 0, 50, info);
6157ff94fd0SStephan Aßmus		if (status == B_OK) {
6167ff94fd0SStephan Aßmus			// Parse message
6177ff94fd0SStephan Aßmus			BMessage result;
6187ff94fd0SStephan Aßmus			BMessage items;
6197ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6207ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
62148045f5aSStephan Aßmus
62248045f5aSStephan Aßmus				BAutolock locker(&fLock);
6237eb09bfaSStephan Aßmus				package->ClearUserRatings();
62448045f5aSStephan Aßmus
6257ff94fd0SStephan Aßmus				int index = 0;
6267ff94fd0SStephan Aßmus				while (true) {
6277ff94fd0SStephan Aßmus					BString name;
6287ff94fd0SStephan Aßmus					name << index++;
6297ff94fd0SStephan Aßmus
6307ff94fd0SStephan Aßmus					BMessage item;
6317ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6327ff94fd0SStephan Aßmus						break;
6337ff94fd0SStephan Aßmus//					item.PrintToStream();
6347ff94fd0SStephan Aßmus
6357ff94fd0SStephan Aßmus					BString user;
6367ff94fd0SStephan Aßmus					BMessage userInfo;
6377ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
6387ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
6397ff94fd0SStephan Aßmus						// Ignore, we need the user name
6407ff94fd0SStephan Aßmus						continue;
6417ff94fd0SStephan Aßmus					}
6427ff94fd0SStephan Aßmus
64336a9b557SStephan Aßmus					// Extract basic info, all items are optional
6447ff94fd0SStephan Aßmus					BString languageCode;
6457ff94fd0SStephan Aßmus					BString comment;
6467ff94fd0SStephan Aßmus					double rating;
64736a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
64836a9b557SStephan Aßmus					item.FindString("comment", &comment);
64936a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
65036a9b557SStephan Aßmus						rating = -1;
65136a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
65236a9b557SStephan Aßmus						// No useful information given.
6537ff94fd0SStephan Aßmus						continue;
6547ff94fd0SStephan Aßmus					}
65536a9b557SStephan Aßmus
6567ff94fd0SStephan Aßmus					// For which version of the package was the rating?
6577ff94fd0SStephan Aßmus					BString major = "?";
6587ff94fd0SStephan Aßmus					BString minor = "?";
6597ff94fd0SStephan Aßmus					BString micro = "";
6607ff94fd0SStephan Aßmus					BMessage version;
6617ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
6627ff94fd0SStephan Aßmus						version.FindString("major", &major);
6637ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
6647ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
6657ff94fd0SStephan Aßmus					}
6667ff94fd0SStephan Aßmus					BString versionString = major;
6677ff94fd0SStephan Aßmus					versionString << ".";
6687ff94fd0SStephan Aßmus					versionString << minor;
6697ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
6707ff94fd0SStephan Aßmus						versionString << ".";
6717ff94fd0SStephan Aßmus						versionString << micro;
6727ff94fd0SStephan Aßmus					}
6737ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
6747ff94fd0SStephan Aßmus					package->AddUserRating(
6757ff94fd0SStephan Aßmus						UserRating(UserInfo(user), rating,
6767ff94fd0SStephan Aßmus							comment, languageCode, versionString, 0, 0)
6777ff94fd0SStephan Aßmus					);
6787ff94fd0SStephan Aßmus				}
6797ff94fd0SStephan Aßmus			} else if (info.FindMessage("error", &result) == B_OK) {
6807ff94fd0SStephan Aßmus				result.PrintToStream();
6817ff94fd0SStephan Aßmus			}
682a60a23f8SStephan Aßmus		}
6837eec822dSStephan Aßmus	}
68487038da7SStephan Aßmus
68587038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
68687038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
68787038da7SStephan Aßmus		{
68887038da7SStephan Aßmus			BAutolock locker(&fLock);
68987038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
6907eb09bfaSStephan Aßmus			package->ClearScreenshots();
69187038da7SStephan Aßmus		}
69287038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
69387038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
694e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
69587038da7SStephan Aßmus		}
69687038da7SStephan Aßmus	}
6977eec822dSStephan Aßmus}
6985bd5f161SStephan Aßmus
6995bd5f161SStephan Aßmus
7005bd5f161SStephan Aßmusvoid
7015bd5f161SStephan AßmusModel::PopulateAllPackages()
7025bd5f161SStephan Aßmus{
7035bd5f161SStephan Aßmus	StopPopulatingAllPackages();
7045bd5f161SStephan Aßmus
7055bd5f161SStephan Aßmus	fStopPopulatingAllPackages = false;
7065bd5f161SStephan Aßmus
7075bd5f161SStephan Aßmus	fPopulateAllPackagesThread = spawn_thread(&_PopulateAllPackagesEntry,
7085bd5f161SStephan Aßmus		"Package populator", B_NORMAL_PRIORITY, this);
7095bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread >= 0)
7105bd5f161SStephan Aßmus		resume_thread(fPopulateAllPackagesThread);
7115bd5f161SStephan Aßmus}
7125bd5f161SStephan Aßmus
7135bd5f161SStephan Aßmus
7145bd5f161SStephan Aßmusvoid
7155bd5f161SStephan AßmusModel::StopPopulatingAllPackages()
7165bd5f161SStephan Aßmus{
7175bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread < 0)
7185bd5f161SStephan Aßmus		return;
7195bd5f161SStephan Aßmus
7205bd5f161SStephan Aßmus	fStopPopulatingAllPackages = true;
7215bd5f161SStephan Aßmus	wait_for_thread(fPopulateAllPackagesThread, NULL);
7225bd5f161SStephan Aßmus	fPopulateAllPackagesThread = -1;
7235bd5f161SStephan Aßmus}
7245bd5f161SStephan Aßmus
7255bd5f161SStephan Aßmus
72628075ee4SStephan Aßmusvoid
7278f03a0f9SStephan AßmusModel::SetUsername(BString username)
72889ec84a4SStephan Aßmus{
7298f03a0f9SStephan Aßmus	BString password;
7308f03a0f9SStephan Aßmus	if (username.Length() > 0) {
7318f03a0f9SStephan Aßmus		BPasswordKey key;
7328f03a0f9SStephan Aßmus		BKeyStore keyStore;
7338f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
7348f03a0f9SStephan Aßmus				key) == B_OK) {
7358f03a0f9SStephan Aßmus			password = key.Password();
7368f03a0f9SStephan Aßmus		} else {
7378f03a0f9SStephan Aßmus			username = "";
7388f03a0f9SStephan Aßmus		}
73989ec84a4SStephan Aßmus	}
7408f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
7418f03a0f9SStephan Aßmus}
7428f03a0f9SStephan Aßmus
7438f03a0f9SStephan Aßmus
7448f03a0f9SStephan Aßmusconst BString&
7458f03a0f9SStephan AßmusModel::Username() const
7468f03a0f9SStephan Aßmus{
7478f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
74889ec84a4SStephan Aßmus}
74989ec84a4SStephan Aßmus
75089ec84a4SStephan Aßmus
75189ec84a4SStephan Aßmusvoid
75289ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
75389ec84a4SStephan Aßmus	bool storePassword)
75428075ee4SStephan Aßmus{
7558f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
75689ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
75789ec84a4SStephan Aßmus		BKeyStore keyStore;
75889ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
75989ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
76089ec84a4SStephan Aßmus	}
76189ec84a4SStephan Aßmus
76228075ee4SStephan Aßmus	BAutolock locker(&fLock);
76328075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
7648f03a0f9SStephan Aßmus
7658f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
76628075ee4SStephan Aßmus}
76728075ee4SStephan Aßmus
76828075ee4SStephan Aßmus
76928075ee4SStephan Aßmus// #pragma mark - private
77028075ee4SStephan Aßmus
77128075ee4SStephan Aßmus
7725bd5f161SStephan Aßmusint32
7735bd5f161SStephan AßmusModel::_PopulateAllPackagesEntry(void* cookie)
7745bd5f161SStephan Aßmus{
7755bd5f161SStephan Aßmus	Model* model = static_cast<Model*>(cookie);
776726db742SStephan Aßmus	model->_PopulateAllPackagesThread(true);
777726db742SStephan Aßmus	model->_PopulateAllPackagesThread(false);
7785bd5f161SStephan Aßmus	return 0;
7795bd5f161SStephan Aßmus}
7805bd5f161SStephan Aßmus
7815bd5f161SStephan Aßmus
7825bd5f161SStephan Aßmusvoid
783726db742SStephan AßmusModel::_PopulateAllPackagesThread(bool fromCacheOnly)
7845bd5f161SStephan Aßmus{
7855bd5f161SStephan Aßmus	int32 depotIndex = 0;
7865bd5f161SStephan Aßmus	int32 packageIndex = 0;
787e3fdc894SStephan Aßmus	PackageList bulkPackageList;
788e3fdc894SStephan Aßmus	PackageList packagesWithIconsList;
7895bd5f161SStephan Aßmus
7905bd5f161SStephan Aßmus	while (!fStopPopulatingAllPackages) {
7915bd5f161SStephan Aßmus		// Obtain PackageInfoRef while keeping the depot and package lists
7925bd5f161SStephan Aßmus		// locked.
7935bd5f161SStephan Aßmus		PackageInfoRef package;
7945bd5f161SStephan Aßmus		{
7955bd5f161SStephan Aßmus			BAutolock locker(&fLock);
7965bd5f161SStephan Aßmus
7975bd5f161SStephan Aßmus			if (depotIndex >= fDepots.CountItems())
7985bd5f161SStephan Aßmus				break;
7995bd5f161SStephan Aßmus			const DepotInfo& depot = fDepots.ItemAt(depotIndex);
8005bd5f161SStephan Aßmus
8015bd5f161SStephan Aßmus			const PackageList& packages = depot.Packages();
8025bd5f161SStephan Aßmus			if (packageIndex >= packages.CountItems()) {
8035bd5f161SStephan Aßmus				// Need the next depot
8045bd5f161SStephan Aßmus				packageIndex = 0;
8055bd5f161SStephan Aßmus				depotIndex++;
8065bd5f161SStephan Aßmus				continue;
8075bd5f161SStephan Aßmus			}
8085bd5f161SStephan Aßmus
8095bd5f161SStephan Aßmus			package = packages.ItemAt(packageIndex);
8105bd5f161SStephan Aßmus			packageIndex++;
8115bd5f161SStephan Aßmus		}
8125bd5f161SStephan Aßmus
8135bd5f161SStephan Aßmus		if (package.Get() == NULL)
8145bd5f161SStephan Aßmus			continue;
8155bd5f161SStephan Aßmus
816e3fdc894SStephan Aßmus		//_PopulatePackageInfo(package, fromCacheOnly);
817e3fdc894SStephan Aßmus		bulkPackageList.Add(package);
818e3fdc894SStephan Aßmus		if (bulkPackageList.CountItems() == 50) {
819e3fdc894SStephan Aßmus			_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
820e3fdc894SStephan Aßmus				packagesWithIconsList);
821e3fdc894SStephan Aßmus			bulkPackageList.Clear();
822e3fdc894SStephan Aßmus		}
823e3fdc894SStephan Aßmus		if (fromCacheOnly)
824e3fdc894SStephan Aßmus			_PopulatePackageIcon(package, fromCacheOnly);
8255bd5f161SStephan Aßmus		// TODO: Average user rating. It needs to be shown in the
8265bd5f161SStephan Aßmus		// list view, so without the user clicking the package.
8275bd5f161SStephan Aßmus	}
828e3fdc894SStephan Aßmus
8296e48c81bSStephan Aßmus	if (bulkPackageList.CountItems() > 0) {
830e3fdc894SStephan Aßmus		_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
831e3fdc894SStephan Aßmus			packagesWithIconsList);
832e3fdc894SStephan Aßmus	}
833e3fdc894SStephan Aßmus
834e3fdc894SStephan Aßmus	if (!fromCacheOnly) {
835e3fdc894SStephan Aßmus		for (int i = packagesWithIconsList.CountItems() - 1; i >= 0; i--) {
836e3fdc894SStephan Aßmus			if (fStopPopulatingAllPackages)
837e3fdc894SStephan Aßmus				break;
838e3fdc894SStephan Aßmus			const PackageInfoRef& package = packagesWithIconsList.ItemAtFast(i);
839e3fdc894SStephan Aßmus			printf("Getting/Updating native icon for %s\n",
840e3fdc894SStephan Aßmus				package->Title().String());
841e3fdc894SStephan Aßmus			_PopulatePackageIcon(package, fromCacheOnly);
842e3fdc894SStephan Aßmus		}
843e3fdc894SStephan Aßmus	}
8445bd5f161SStephan Aßmus}
8455bd5f161SStephan Aßmus
8465bd5f161SStephan Aßmus
847a3dfc57dSStephan Aßmusbool
848a3dfc57dSStephan AßmusModel::_GetCacheFile(BPath& path, BFile& file, directory_which directory,
849a3dfc57dSStephan Aßmus	const char* relativeLocation, const char* fileName, uint32 openMode) const
850a3dfc57dSStephan Aßmus{
851a3dfc57dSStephan Aßmus	if (find_directory(directory, &path) == B_OK
852a3dfc57dSStephan Aßmus		&& path.Append(relativeLocation) == B_OK
853a3dfc57dSStephan Aßmus		&& create_directory(path.Path(), 0777) == B_OK
854a3dfc57dSStephan Aßmus		&& path.Append(fileName) == B_OK) {
855a3dfc57dSStephan Aßmus		// Try opening the file which will fail if its
856a3dfc57dSStephan Aßmus		// not a file or does not exist.
857a3dfc57dSStephan Aßmus		return file.SetTo(path.Path(), openMode) == B_OK;
858a3dfc57dSStephan Aßmus	}
859a3dfc57dSStephan Aßmus	return false;
860a3dfc57dSStephan Aßmus}
861a3dfc57dSStephan Aßmus
862a3dfc57dSStephan Aßmus
863a3dfc57dSStephan Aßmusbool
864a3dfc57dSStephan AßmusModel::_GetCacheFile(BPath& path, BFile& file, directory_which directory,
865a3dfc57dSStephan Aßmus	const char* relativeLocation, const char* fileName,
866a3dfc57dSStephan Aßmus	bool ignoreAge, time_t maxAge) const
867a3dfc57dSStephan Aßmus{
868a3dfc57dSStephan Aßmus	if (!_GetCacheFile(path, file, directory, relativeLocation, fileName,
869a3dfc57dSStephan Aßmus			B_READ_ONLY)) {
870a3dfc57dSStephan Aßmus		return false;
871a3dfc57dSStephan Aßmus	}
872a3dfc57dSStephan Aßmus
873a3dfc57dSStephan Aßmus	if (ignoreAge)
874a3dfc57dSStephan Aßmus		return true;
875a3dfc57dSStephan Aßmus
876a3dfc57dSStephan Aßmus	time_t modifiedTime;
877a3dfc57dSStephan Aßmus	file.GetModificationTime(&modifiedTime);
878a3dfc57dSStephan Aßmus	time_t now;
879a3dfc57dSStephan Aßmus	time(&now);
880a3dfc57dSStephan Aßmus	return now - modifiedTime < maxAge;
881a3dfc57dSStephan Aßmus}
882a3dfc57dSStephan Aßmus
883a3dfc57dSStephan Aßmus
884e08beebeSStephan Aßmusvoid
885e3fdc894SStephan AßmusModel::_PopulatePackageInfos(PackageList& packages, bool fromCacheOnly,
886e3fdc894SStephan Aßmus	PackageList& packagesWithIcons)
887e08beebeSStephan Aßmus{
8886e48c81bSStephan Aßmus	if (fStopPopulatingAllPackages)
8896e48c81bSStephan Aßmus		return;
8906e48c81bSStephan Aßmus
891a3dfc57dSStephan Aßmus	// See if there are cached info files
892a3dfc57dSStephan Aßmus	for (int i = packages.CountItems() - 1; i >= 0; i--) {
893a3dfc57dSStephan Aßmus		if (fStopPopulatingAllPackages)
894a3dfc57dSStephan Aßmus			return;
895a3dfc57dSStephan Aßmus
896a3dfc57dSStephan Aßmus		const PackageInfoRef& package = packages.ItemAtFast(i);
897a3dfc57dSStephan Aßmus
898a3dfc57dSStephan Aßmus		BFile file;
899a3dfc57dSStephan Aßmus		BPath path;
900a3dfc57dSStephan Aßmus		BString name(package->Title());
901a3dfc57dSStephan Aßmus		name << ".info";
902a3dfc57dSStephan Aßmus		if (_GetCacheFile(path, file, B_USER_CACHE_DIRECTORY,
903a3dfc57dSStephan Aßmus			"HaikuDepot", name, fromCacheOnly, 60 * 60)) {
904a3dfc57dSStephan Aßmus			// Cache file is recent enough, just use it and return.
905a3dfc57dSStephan Aßmus			BMessage pkgInfo;
906a3dfc57dSStephan Aßmus			if (pkgInfo.Unflatten(&file) == B_OK) {
907a3dfc57dSStephan Aßmus				_PopulatePackageInfo(package, pkgInfo);
908a3dfc57dSStephan Aßmus				if (_HasNativeIcon(pkgInfo))
909a3dfc57dSStephan Aßmus					packagesWithIcons.Add(package);
910a3dfc57dSStephan Aßmus				packages.Remove(i);
911a3dfc57dSStephan Aßmus			}
912a3dfc57dSStephan Aßmus		}
913a3dfc57dSStephan Aßmus	}
914a3dfc57dSStephan Aßmus
915a3dfc57dSStephan Aßmus	if (fromCacheOnly || packages.IsEmpty())
916e08beebeSStephan Aßmus		return;
917e08beebeSStephan Aßmus
918e08beebeSStephan Aßmus	// Retrieve info from web-app
919e08beebeSStephan Aßmus	BMessage info;
920e08beebeSStephan Aßmus
921e3fdc894SStephan Aßmus	StringList packageNames;
9222b411c02SStephan Aßmus	StringList packageArchitectures;
923e3fdc894SStephan Aßmus	for (int i = 0; i < packages.CountItems(); i++) {
924e3fdc894SStephan Aßmus		const PackageInfoRef& package = packages.ItemAtFast(i);
925e3fdc894SStephan Aßmus		packageNames.Add(package->Title());
9262b411c02SStephan Aßmus		packageArchitectures.Add(package->Architecture());
927e3fdc894SStephan Aßmus	}
928e3fdc894SStephan Aßmus
92928075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveBulkPackageInfo(packageNames,
9302b411c02SStephan Aßmus		packageArchitectures, info);
931e08beebeSStephan Aßmus	if (status == B_OK) {
93295e4ca3dSStephan Aßmus		// Parse message
933e3fdc894SStephan Aßmus//		info.PrintToStream();
93495e4ca3dSStephan Aßmus		BMessage result;
935e3fdc894SStephan Aßmus		BMessage pkgs;
936e3fdc894SStephan Aßmus		if (info.FindMessage("result", &result) == B_OK
937e3fdc894SStephan Aßmus			&& result.FindMessage("pkgs", &pkgs) == B_OK) {
938e3fdc894SStephan Aßmus			int32 index = 0;
939e3fdc894SStephan Aßmus			while (true) {
9406e48c81bSStephan Aßmus				if (fStopPopulatingAllPackages)
9416e48c81bSStephan Aßmus					return;
942e3fdc894SStephan Aßmus				BString name;
943e3fdc894SStephan Aßmus				name << index++;
944e3fdc894SStephan Aßmus				BMessage pkgInfo;
945e3fdc894SStephan Aßmus				if (pkgs.FindMessage(name, &pkgInfo) != B_OK)
946e3fdc894SStephan Aßmus					break;
947e3fdc894SStephan Aßmus
948e3fdc894SStephan Aßmus				BString pkgName;
949e3fdc894SStephan Aßmus				if (pkgInfo.FindString("name", &pkgName) != B_OK)
950e3fdc894SStephan Aßmus					continue;
951e3fdc894SStephan Aßmus
952e3fdc894SStephan Aßmus				// Find the PackageInfoRef
953e3fdc894SStephan Aßmus				bool found = false;
954e3fdc894SStephan Aßmus				for (int i = 0; i < packages.CountItems(); i++) {
955e3fdc894SStephan Aßmus					const PackageInfoRef& package = packages.ItemAtFast(i);
956e3fdc894SStephan Aßmus					if (pkgName == package->Title()) {
957e3fdc894SStephan Aßmus						_PopulatePackageInfo(package, pkgInfo);
958e3fdc894SStephan Aßmus						if (_HasNativeIcon(pkgInfo))
959e3fdc894SStephan Aßmus							packagesWithIcons.Add(package);
960a3dfc57dSStephan Aßmus
961a3dfc57dSStephan Aßmus						// Store in cache
962a3dfc57dSStephan Aßmus						BFile file;
963a3dfc57dSStephan Aßmus						BPath path;
964a3dfc57dSStephan Aßmus						BString fileName(package->Title());
965a3dfc57dSStephan Aßmus						fileName << ".info";
966a3dfc57dSStephan Aßmus						if (_GetCacheFile(path, file, B_USER_CACHE_DIRECTORY,
967a3dfc57dSStephan Aßmus								"HaikuDepot", fileName,
968a3dfc57dSStephan Aßmus								B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)) {
969a3dfc57dSStephan Aßmus							pkgInfo.Flatten(&file);
970a3dfc57dSStephan Aßmus						}
971a3dfc57dSStephan Aßmus
972e3fdc894SStephan Aßmus						packages.Remove(i);
973e3fdc894SStephan Aßmus						found = true;
97495e4ca3dSStephan Aßmus						break;
975e3fdc894SStephan Aßmus					}
97695e4ca3dSStephan Aßmus				}
977e3fdc894SStephan Aßmus				if (!found)
978e3fdc894SStephan Aßmus					printf("No matching package for %s\n", pkgName.String());
97995e4ca3dSStephan Aßmus			}
980e3fdc894SStephan Aßmus		}
981e3fdc894SStephan Aßmus	} else {
982e3fdc894SStephan Aßmus		printf("Error sending request: %s\n", strerror(status));
983e3fdc894SStephan Aßmus		int count = packages.CountItems();
984e3fdc894SStephan Aßmus		if (count >= 4) {
985e3fdc894SStephan Aßmus			// Retry in smaller chunks
986e3fdc894SStephan Aßmus			PackageList firstHalf;
987e3fdc894SStephan Aßmus			PackageList secondHalf;
988e3fdc894SStephan Aßmus			for (int i = 0; i < count / 2; i++)
989e3fdc894SStephan Aßmus				firstHalf.Add(packages.ItemAtFast(i));
990e3fdc894SStephan Aßmus			for (int i = count / 2; i < count; i++)
991e3fdc894SStephan Aßmus				secondHalf.Add(packages.ItemAtFast(i));
992e3fdc894SStephan Aßmus			packages.Clear();
993e3fdc894SStephan Aßmus			_PopulatePackageInfos(firstHalf, fromCacheOnly, packagesWithIcons);
994e3fdc894SStephan Aßmus			_PopulatePackageInfos(secondHalf, fromCacheOnly, packagesWithIcons);
995e3fdc894SStephan Aßmus		} else {
996e3fdc894SStephan Aßmus			while (packages.CountItems() > 0) {
997e3fdc894SStephan Aßmus				const PackageInfoRef& package = packages.ItemAtFast(0);
998e3fdc894SStephan Aßmus				_PopulatePackageInfo(package, fromCacheOnly);
999e3fdc894SStephan Aßmus				packages.Remove(0);
100095e4ca3dSStephan Aßmus			}
100195e4ca3dSStephan Aßmus		}
1002e08beebeSStephan Aßmus	}
1003e3fdc894SStephan Aßmus
1004e3fdc894SStephan Aßmus	if (packages.CountItems() > 0) {
1005e3fdc894SStephan Aßmus		for (int i = 0; i < packages.CountItems(); i++) {
1006e3fdc894SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(i);
1007e3fdc894SStephan Aßmus			printf("No package info for %s\n", package->Title().String());
1008e3fdc894SStephan Aßmus		}
1009e3fdc894SStephan Aßmus	}
1010e3fdc894SStephan Aßmus}
1011e3fdc894SStephan Aßmus
1012e3fdc894SStephan Aßmus
1013e3fdc894SStephan Aßmusvoid
1014e3fdc894SStephan AßmusModel::_PopulatePackageInfo(const PackageInfoRef& package, bool fromCacheOnly)
1015e3fdc894SStephan Aßmus{
1016e3fdc894SStephan Aßmus	if (fromCacheOnly)
1017e3fdc894SStephan Aßmus		return;
1018e3fdc894SStephan Aßmus
1019e3fdc894SStephan Aßmus	// Retrieve info from web-app
1020e3fdc894SStephan Aßmus	BMessage info;
1021e3fdc894SStephan Aßmus
102228075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrievePackageInfo(package->Title(),
10236e48c81bSStephan Aßmus		package->Architecture(), info);
1024e3fdc894SStephan Aßmus	if (status == B_OK) {
1025e3fdc894SStephan Aßmus		// Parse message
1026e3fdc894SStephan Aßmus//		info.PrintToStream();
1027e3fdc894SStephan Aßmus		BMessage result;
1028e3fdc894SStephan Aßmus		if (info.FindMessage("result", &result) == B_OK)
1029e3fdc894SStephan Aßmus			_PopulatePackageInfo(package, result);
1030e3fdc894SStephan Aßmus	}
1031e3fdc894SStephan Aßmus}
1032e3fdc894SStephan Aßmus
1033e3fdc894SStephan Aßmus
10346e48c81bSStephan Aßmusstatic void
10356e48c81bSStephan Aßmusappend_word_list(BString& words, const char* word)
10366e48c81bSStephan Aßmus{
10376e48c81bSStephan Aßmus	if (words.Length() > 0)
10386e48c81bSStephan Aßmus		words << ", ";
10396e48c81bSStephan Aßmus	words << word;
10406e48c81bSStephan Aßmus}
10416e48c81bSStephan Aßmus
10426e48c81bSStephan Aßmus
1043e3fdc894SStephan Aßmusvoid
10446e48c81bSStephan AßmusModel::_PopulatePackageInfo(const PackageInfoRef& package, const BMessage& data)
1045e3fdc894SStephan Aßmus{
10466e48c81bSStephan Aßmus	BAutolock locker(&fLock);
10476e48c81bSStephan Aßmus
10486e48c81bSStephan Aßmus	BString foundInfo;
10496e48c81bSStephan Aßmus
10506e48c81bSStephan Aßmus	BMessage versions;
1051ab172803SStephan Aßmus	if (data.FindMessage("versions", &versions) == B_OK) {
1052ab172803SStephan Aßmus		// Search a summary and description in the preferred language
1053ab172803SStephan Aßmus		int32 index = 0;
1054ab172803SStephan Aßmus		while (true) {
1055ab172803SStephan Aßmus			BString name;
1056ab172803SStephan Aßmus			name << index++;
1057ab172803SStephan Aßmus			BMessage version;
1058ab172803SStephan Aßmus			if (versions.FindMessage(name, &version) != B_OK)
1059ab172803SStephan Aßmus				break;
1060ab172803SStephan Aßmus			BString languageCode;
1061ab172803SStephan Aßmus			if (version.FindString("naturalLanguageCode",
1062ab172803SStephan Aßmus					&languageCode) != B_OK
1063ab172803SStephan Aßmus				|| languageCode != fPreferredLanguage) {
1064ab172803SStephan Aßmus				continue;
10656e48c81bSStephan Aßmus			}
1066ab172803SStephan Aßmus
1067ab172803SStephan Aßmus			BString summary;
1068ab172803SStephan Aßmus			if (version.FindString("summary", &summary) == B_OK) {
1069ab172803SStephan Aßmus				package->SetShortDescription(summary);
1070ab172803SStephan Aßmus				append_word_list(foundInfo, "summary");
1071ab172803SStephan Aßmus			}
1072ab172803SStephan Aßmus			BString description;
1073ab172803SStephan Aßmus			if (version.FindString("description", &description) == B_OK) {
1074ab172803SStephan Aßmus				package->SetFullDescription(description);
1075ab172803SStephan Aßmus				append_word_list(foundInfo, "description");
1076ab172803SStephan Aßmus			}
1077ab172803SStephan Aßmus			break;
10786e48c81bSStephan Aßmus		}
10796e48c81bSStephan Aßmus	}
1080e3fdc894SStephan Aßmus
1081e3fdc894SStephan Aßmus	BMessage categories;
1082e3fdc894SStephan Aßmus	if (data.FindMessage("pkgCategoryCodes", &categories) == B_OK) {
10836e48c81bSStephan Aßmus		bool foundCategory = false;
1084e3fdc894SStephan Aßmus		int32 index = 0;
1085e3fdc894SStephan Aßmus		while (true) {
1086e3fdc894SStephan Aßmus			BString name;
1087e3fdc894SStephan Aßmus			name << index++;
1088e3fdc894SStephan Aßmus			BString category;
1089e3fdc894SStephan Aßmus			if (categories.FindString(name, &category) != B_OK)
1090e3fdc894SStephan Aßmus				break;
1091e3fdc894SStephan Aßmus
10927eb09bfaSStephan Aßmus			package->ClearCategories();
10932b411c02SStephan Aßmus			for (int i = fCategories.CountItems() - 1; i >= 0; i--) {
10942b411c02SStephan Aßmus				const CategoryRef& categoryRef = fCategories.ItemAtFast(i);
10952b411c02SStephan Aßmus				if (categoryRef->Name() == category) {
10962b411c02SStephan Aßmus					package->AddCategory(categoryRef);
10972b411c02SStephan Aßmus					foundCategory = true;
10982b411c02SStephan Aßmus					break;
10992b411c02SStephan Aßmus				}
11002b411c02SStephan Aßmus			}
1101e3fdc894SStephan Aßmus		}
11026e48c81bSStephan Aßmus		if (foundCategory)
11036e48c81bSStephan Aßmus			append_word_list(foundInfo, "categories");
1104e3fdc894SStephan Aßmus	}
11051d9d4873SStephan Aßmus
1106e3fdc894SStephan Aßmus	double derivedRating;
110736a9b557SStephan Aßmus	if (data.FindDouble("derivedRating", &derivedRating) == B_OK) {
110836a9b557SStephan Aßmus		RatingSummary summary;
110936a9b557SStephan Aßmus		summary.averageRating = derivedRating;
111036a9b557SStephan Aßmus		package->SetRatingSummary(summary);
111136a9b557SStephan Aßmus
111236a9b557SStephan Aßmus		append_word_list(foundInfo, "rating");
1113e3fdc894SStephan Aßmus	}
11141d9d4873SStephan Aßmus
11159f40cd41SStephan Aßmus	double prominenceOrdering;
11169f40cd41SStephan Aßmus	if (data.FindDouble("prominenceOrdering", &prominenceOrdering) == B_OK) {
11179f40cd41SStephan Aßmus		package->SetProminence(prominenceOrdering);
11189f40cd41SStephan Aßmus
11199f40cd41SStephan Aßmus		append_word_list(foundInfo, "prominence");
11209f40cd41SStephan Aßmus	}
11219f40cd41SStephan Aßmus
11221d9d4873SStephan Aßmus	BMessage screenshots;
11231d9d4873SStephan Aßmus	if (data.FindMessage("pkgScreenshots", &screenshots) == B_OK) {
11247eb09bfaSStephan Aßmus		package->ClearScreenshotInfos();
11251d9d4873SStephan Aßmus		bool foundScreenshot = false;
11261d9d4873SStephan Aßmus		int32 index = 0;
11271d9d4873SStephan Aßmus		while (true) {
11281d9d4873SStephan Aßmus			BString name;
11291d9d4873SStephan Aßmus			name << index++;
11301d9d4873SStephan Aßmus
11311d9d4873SStephan Aßmus			BMessage screenshot;
11321d9d4873SStephan Aßmus			if (screenshots.FindMessage(name, &screenshot) != B_OK)
11331d9d4873SStephan Aßmus				break;
11341d9d4873SStephan Aßmus
11351d9d4873SStephan Aßmus			BString code;
11361d9d4873SStephan Aßmus			double width;
11371d9d4873SStephan Aßmus			double height;
11381d9d4873SStephan Aßmus			double dataSize;
11391d9d4873SStephan Aßmus			if (screenshot.FindString("code", &code) == B_OK
11401d9d4873SStephan Aßmus				&& screenshot.FindDouble("width", &width) == B_OK
11411d9d4873SStephan Aßmus				&& screenshot.FindDouble("height", &height) == B_OK
11421d9d4873SStephan Aßmus				&& screenshot.FindDouble("length", &dataSize) == B_OK) {
11431d9d4873SStephan Aßmus				package->AddScreenshotInfo(ScreenshotInfo(code, (int32)width,
11441d9d4873SStephan Aßmus					(int32)height, (int32)dataSize));
11451d9d4873SStephan Aßmus				foundScreenshot = true;
11461d9d4873SStephan Aßmus			}
11471d9d4873SStephan Aßmus		}
11481d9d4873SStephan Aßmus		if (foundScreenshot)
11491d9d4873SStephan Aßmus			append_word_list(foundInfo, "screenshots");
11501d9d4873SStephan Aßmus	}
11511d9d4873SStephan Aßmus
11526e48c81bSStephan Aßmus	if (foundInfo.Length() > 0) {
11536e48c81bSStephan Aßmus		printf("Populated package info for %s: %s\n",
11546e48c81bSStephan Aßmus			package->Title().String(), foundInfo.String());
11556e48c81bSStephan Aßmus	}
115687038da7SStephan Aßmus
115787038da7SStephan Aßmus	// If the user already clicked this package, remove it from the
115887038da7SStephan Aßmus	// list of populated packages, so that clicking it again will
115987038da7SStephan Aßmus	// populate any additional information.
116087038da7SStephan Aßmus	// TODO: Trigger re-populating if the package is currently showing.
116187038da7SStephan Aßmus	fPopulatedPackages.Remove(package);
1162e08beebeSStephan Aßmus}
1163e08beebeSStephan Aßmus
1164e08beebeSStephan Aßmus
11655bd5f161SStephan Aßmusvoid
1166726db742SStephan AßmusModel::_PopulatePackageIcon(const PackageInfoRef& package, bool fromCacheOnly)
11675bd5f161SStephan Aßmus{
1168726db742SStephan Aßmus	// See if there is a cached icon file
1169726db742SStephan Aßmus	BFile iconFile;
1170726db742SStephan Aßmus	BPath iconCachePath;
1171726db742SStephan Aßmus	BString iconName(package->Title());
1172726db742SStephan Aßmus	iconName << ".hvif";
1173a3dfc57dSStephan Aßmus	if (_GetCacheFile(iconCachePath, iconFile, B_USER_CACHE_DIRECTORY,
1174a3dfc57dSStephan Aßmus		"HaikuDepot", iconName, fromCacheOnly, 60 * 60)) {
1175a3dfc57dSStephan Aßmus		// Cache file is recent enough, just use it and return.
1176a3dfc57dSStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(iconFile), true);
1177a3dfc57dSStephan Aßmus		BAutolock locker(&fLock);
1178a3dfc57dSStephan Aßmus		package->SetIcon(bitmapRef);
1179a3dfc57dSStephan Aßmus		return;
1180726db742SStephan Aßmus	}
1181726db742SStephan Aßmus
1182726db742SStephan Aßmus	if (fromCacheOnly)
1183726db742SStephan Aßmus		return;
1184726db742SStephan Aßmus
1185726db742SStephan Aßmus	// Retrieve icon from web-app
11865bd5f161SStephan Aßmus	BMallocIO buffer;
11875bd5f161SStephan Aßmus
118828075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrievePackageIcon(package->Title(),
118928075ee4SStephan Aßmus		&buffer);
11905bd5f161SStephan Aßmus	if (status == B_OK) {
11915bd5f161SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
11926e48c81bSStephan Aßmus		BAutolock locker(&fLock);
11935bd5f161SStephan Aßmus		package->SetIcon(bitmapRef);
11946e48c81bSStephan Aßmus		locker.Unlock();
1195726db742SStephan Aßmus		if (iconFile.SetTo(iconCachePath.Path(),
1196726db742SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
1197726db742SStephan Aßmus			iconFile.Write(buffer.Buffer(), buffer.BufferLength());
1198726db742SStephan Aßmus		}
11995bd5f161SStephan Aßmus	}
12005bd5f161SStephan Aßmus}
1201e3fdc894SStephan Aßmus
1202e3fdc894SStephan Aßmus
120387038da7SStephan Aßmusvoid
120487038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
120587038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
120687038da7SStephan Aßmus{
120787038da7SStephan Aßmus	// See if there is a cached screenshot
120887038da7SStephan Aßmus	BFile screenshotFile;
120987038da7SStephan Aßmus	BPath screenshotCachePath;
121087038da7SStephan Aßmus	bool fileExists = false;
121187038da7SStephan Aßmus	BString screenshotName(info.Code());
121287038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
121387038da7SStephan Aßmus	screenshotName << ".png";
121487038da7SStephan Aßmus	time_t modifiedTime;
121587038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
121687038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
121787038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
121887038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
121987038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
122087038da7SStephan Aßmus		// not a file or does not exist.
122187038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
122287038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
122387038da7SStephan Aßmus		if (fileExists)
122487038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
122587038da7SStephan Aßmus	}
122687038da7SStephan Aßmus
122787038da7SStephan Aßmus	if (fileExists) {
122887038da7SStephan Aßmus		time_t now;
122987038da7SStephan Aßmus		time(&now);
123087038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
123187038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
123287038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
123387038da7SStephan Aßmus				true);
123487038da7SStephan Aßmus			BAutolock locker(&fLock);
123587038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
123687038da7SStephan Aßmus			return;
123787038da7SStephan Aßmus		}
123887038da7SStephan Aßmus	}
123987038da7SStephan Aßmus
124087038da7SStephan Aßmus	if (fromCacheOnly)
124187038da7SStephan Aßmus		return;
124287038da7SStephan Aßmus
124387038da7SStephan Aßmus	// Retrieve screenshot from web-app
124487038da7SStephan Aßmus	BMallocIO buffer;
124587038da7SStephan Aßmus
124687038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
124787038da7SStephan Aßmus
124828075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
124987038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
125087038da7SStephan Aßmus	if (status == B_OK) {
125187038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
125287038da7SStephan Aßmus		BAutolock locker(&fLock);
125387038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
125487038da7SStephan Aßmus		locker.Unlock();
125587038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
125687038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
125787038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
125887038da7SStephan Aßmus		}
1259e0881f21SStephan Aßmus	} else {
1260e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
1261e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
1262e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
126387038da7SStephan Aßmus	}
126487038da7SStephan Aßmus}
126587038da7SStephan Aßmus
126687038da7SStephan Aßmus
1267e3fdc894SStephan Aßmusbool
1268e3fdc894SStephan AßmusModel::_HasNativeIcon(const BMessage& message) const
1269e3fdc894SStephan Aßmus{
1270e3fdc894SStephan Aßmus	BMessage pkgIcons;
1271e3fdc894SStephan Aßmus	if (message.FindMessage("pkgIcons", &pkgIcons) != B_OK)
1272e3fdc894SStephan Aßmus		return false;
1273e3fdc894SStephan Aßmus
1274e3fdc894SStephan Aßmus	int32 index = 0;
1275e3fdc894SStephan Aßmus	while (true) {
1276e3fdc894SStephan Aßmus		BString name;
1277e3fdc894SStephan Aßmus		name << index++;
1278e3fdc894SStephan Aßmus
1279e3fdc894SStephan Aßmus		BMessage typeCodeInfo;
1280e3fdc894SStephan Aßmus		if (pkgIcons.FindMessage(name, &typeCodeInfo) != B_OK)
1281e3fdc894SStephan Aßmus			break;
1282e3fdc894SStephan Aßmus
1283e3fdc894SStephan Aßmus		BString mediaTypeCode;
1284e3fdc894SStephan Aßmus		if (typeCodeInfo.FindString("mediaTypeCode", &mediaTypeCode) == B_OK
1285e3fdc894SStephan Aßmus			&& mediaTypeCode == "application/x-vnd.haiku-icon") {
1286e3fdc894SStephan Aßmus			return true;
1287e3fdc894SStephan Aßmus		}
1288e3fdc894SStephan Aßmus	}
1289e3fdc894SStephan Aßmus	return false;
1290e3fdc894SStephan Aßmus}
12918f03a0f9SStephan Aßmus
12928f03a0f9SStephan Aßmus
12938f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
12948f03a0f9SStephan Aßmus
12958f03a0f9SStephan Aßmus
12968f03a0f9SStephan Aßmusvoid
12978f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
12988f03a0f9SStephan Aßmus{
12998f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
13008f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
13018f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
13028f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
13038f03a0f9SStephan Aßmus	}
13048f03a0f9SStephan Aßmus}
13058f03a0f9SStephan Aßmus
1306