1/*
2 * Copyright 2013-2014, Stephan A��mus <superstippi@gmx.de>.
3 * Copyright 2013, Rene Gollent <rene@gollent.com>.
4 * Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
5 * All rights reserved. Distributed under the terms of the MIT License.
6 */
7
8#include "PackageInfo.h"
9
10#include <stdio.h>
11
12#include <FindDirectory.h>
13#include <package/PackageDefs.h>
14#include <package/PackageFlags.h>
15#include <Path.h>
16
17
18// #pragma mark - Language
19
20
21Language::Language(const char* language, const BString& serverName,
22	bool isPopular)
23	:
24	BLanguage(language),
25	fServerName(serverName),
26	fIsPopular(isPopular)
27{
28}
29
30
31Language::Language(const Language& other)
32	:
33	BLanguage(other.Code()),
34	fServerName(other.fServerName),
35	fIsPopular(other.fIsPopular)
36{
37}
38
39
40status_t
41Language::GetName(BString& name,
42	const BLanguage* displayLanguage) const
43{
44	status_t result = BLanguage::GetName(name, displayLanguage);
45
46	if (result == B_OK && (name.IsEmpty() || name == Code()))
47		name.SetTo(fServerName);
48
49	return result;
50}
51
52
53// #pragma mark - UserInfo
54
55
56UserInfo::UserInfo()
57	:
58	fNickName()
59{
60}
61
62
63UserInfo::UserInfo(const BString& nickName)
64	:
65	fNickName(nickName)
66{
67}
68
69
70UserInfo::UserInfo(const UserInfo& other)
71	:
72	fNickName(other.fNickName)
73{
74}
75
76
77UserInfo&
78UserInfo::operator=(const UserInfo& other)
79{
80	fNickName = other.fNickName;
81	return *this;
82}
83
84
85bool
86UserInfo::operator==(const UserInfo& other) const
87{
88	return fNickName == other.fNickName;
89}
90
91
92bool
93UserInfo::operator!=(const UserInfo& other) const
94{
95	return !(*this == other);
96}
97
98
99// #pragma mark - UserRating
100
101
102UserRating::UserRating()
103	:
104	fUserInfo(),
105	fRating(0.0f),
106	fComment(),
107	fLanguage(),
108	fPackageVersion(),
109	fCreateTimestamp(0)
110{
111}
112
113
114UserRating::UserRating(const UserInfo& userInfo, float rating,
115		const BString& comment, const BString& language,
116		const BString& packageVersion, uint64 createTimestamp)
117	:
118	fUserInfo(userInfo),
119	fRating(rating),
120	fComment(comment),
121	fLanguage(language),
122	fPackageVersion(packageVersion),
123	fCreateTimestamp(createTimestamp)
124{
125}
126
127
128UserRating::UserRating(const UserRating& other)
129	:
130	fUserInfo(other.fUserInfo),
131	fRating(other.fRating),
132	fComment(other.fComment),
133	fLanguage(other.fLanguage),
134	fPackageVersion(other.fPackageVersion),
135	fCreateTimestamp(other.fCreateTimestamp)
136{
137}
138
139
140UserRating&
141UserRating::operator=(const UserRating& other)
142{
143	fUserInfo = other.fUserInfo;
144	fRating = other.fRating;
145	fComment = other.fComment;
146	fLanguage = other.fLanguage;
147	fPackageVersion = other.fPackageVersion;
148	fCreateTimestamp = other.fCreateTimestamp;
149	return *this;
150}
151
152
153bool
154UserRating::operator==(const UserRating& other) const
155{
156	return fUserInfo == other.fUserInfo
157		&& fRating == other.fRating
158		&& fComment == other.fComment
159		&& fLanguage == other.fLanguage
160		&& fPackageVersion == other.fPackageVersion
161		&& fCreateTimestamp == other.fCreateTimestamp;
162}
163
164
165bool
166UserRating::operator!=(const UserRating& other) const
167{
168	return !(*this == other);
169}
170
171
172// #pragma mark - RatingSummary
173
174
175RatingSummary::RatingSummary()
176	:
177	averageRating(0.0f),
178	ratingCount(0)
179{
180	for (int i = 0; i < 5; i++)
181		ratingCountByStar[i] = 0;
182}
183
184
185RatingSummary::RatingSummary(const RatingSummary& other)
186{
187	*this = other;
188}
189
190
191RatingSummary&
192RatingSummary::operator=(const RatingSummary& other)
193{
194	averageRating = other.averageRating;
195	ratingCount = other.ratingCount;
196
197	for (int i = 0; i < 5; i++)
198		ratingCountByStar[i] = other.ratingCountByStar[i];
199
200	return *this;
201}
202
203
204bool
205RatingSummary::operator==(const RatingSummary& other) const
206{
207	if (averageRating != other.averageRating
208		|| ratingCount != other.ratingCount) {
209		return false;
210	}
211
212	for (int i = 0; i < 5; i++) {
213		if (ratingCountByStar[i] != other.ratingCountByStar[i])
214			return false;
215	}
216
217	return true;
218}
219
220
221bool
222RatingSummary::operator!=(const RatingSummary& other) const
223{
224	return !(*this == other);
225}
226
227
228// #pragma mark - StabilityRating
229
230
231StabilityRating::StabilityRating()
232	:
233	fLabel(),
234	fName()
235{
236}
237
238
239StabilityRating::StabilityRating(const BString& label,
240		const BString& name)
241	:
242	fLabel(label),
243	fName(name)
244{
245}
246
247
248StabilityRating::StabilityRating(const StabilityRating& other)
249	:
250	fLabel(other.fLabel),
251	fName(other.fName)
252{
253}
254
255
256StabilityRating&
257StabilityRating::operator=(const StabilityRating& other)
258{
259	fLabel = other.fLabel;
260	fName = other.fName;
261	return *this;
262}
263
264
265bool
266StabilityRating::operator==(const StabilityRating& other) const
267{
268	return fLabel == other.fLabel
269		&& fName == other.fName;
270}
271
272
273bool
274StabilityRating::operator!=(const StabilityRating& other) const
275{
276	return !(*this == other);
277}
278
279
280// #pragma mark - PublisherInfo
281
282
283PublisherInfo::PublisherInfo()
284	:
285	fLogo(),
286	fName(),
287	fEmail(),
288	fWebsite()
289{
290}
291
292
293PublisherInfo::PublisherInfo(const BitmapRef& logo, const BString& name,
294		const BString& email, const BString& website)
295	:
296	fLogo(logo),
297	fName(name),
298	fEmail(email),
299	fWebsite(website)
300{
301}
302
303
304PublisherInfo::PublisherInfo(const PublisherInfo& other)
305	:
306	fLogo(other.fLogo),
307	fName(other.fName),
308	fEmail(other.fEmail),
309	fWebsite(other.fWebsite)
310{
311}
312
313
314PublisherInfo&
315PublisherInfo::operator=(const PublisherInfo& other)
316{
317	fLogo = other.fLogo;
318	fName = other.fName;
319	fEmail = other.fEmail;
320	fWebsite = other.fWebsite;
321	return *this;
322}
323
324
325bool
326PublisherInfo::operator==(const PublisherInfo& other) const
327{
328	return fLogo == other.fLogo
329		&& fName == other.fName
330		&& fEmail == other.fEmail
331		&& fWebsite == other.fWebsite;
332}
333
334
335bool
336PublisherInfo::operator!=(const PublisherInfo& other) const
337{
338	return !(*this == other);
339}
340
341
342// #pragma mark - PackageCategory
343
344
345PackageCategory::PackageCategory()
346	:
347	BReferenceable(),
348	fCode(),
349	fName()
350{
351}
352
353
354PackageCategory::PackageCategory(const BString& code, const BString& name)
355	:
356	BReferenceable(),
357	fCode(code),
358	fName(name)
359{
360}
361
362
363PackageCategory::PackageCategory(const PackageCategory& other)
364	:
365	BReferenceable(),
366	fCode(other.fCode),
367	fName(other.fName)
368{
369}
370
371
372PackageCategory&
373PackageCategory::operator=(const PackageCategory& other)
374{
375	fCode = other.fCode;
376	fName = other.fName;
377	return *this;
378}
379
380
381bool
382PackageCategory::operator==(const PackageCategory& other) const
383{
384	return fCode == other.fCode && fName == other.fName;
385}
386
387
388bool
389PackageCategory::operator!=(const PackageCategory& other) const
390{
391	return !(*this == other);
392}
393
394
395// #pragma mark - ScreenshotInfo
396
397
398ScreenshotInfo::ScreenshotInfo()
399	:
400	fCode(),
401	fWidth(),
402	fHeight(),
403	fDataSize()
404{
405}
406
407
408ScreenshotInfo::ScreenshotInfo(const BString& code,
409		int32 width, int32 height, int32 dataSize)
410	:
411	fCode(code),
412	fWidth(width),
413	fHeight(height),
414	fDataSize(dataSize)
415{
416}
417
418
419ScreenshotInfo::ScreenshotInfo(const ScreenshotInfo& other)
420	:
421	fCode(other.fCode),
422	fWidth(other.fWidth),
423	fHeight(other.fHeight),
424	fDataSize(other.fDataSize)
425{
426}
427
428
429ScreenshotInfo&
430ScreenshotInfo::operator=(const ScreenshotInfo& other)
431{
432	fCode = other.fCode;
433	fWidth = other.fWidth;
434	fHeight = other.fHeight;
435	fDataSize = other.fDataSize;
436	return *this;
437}
438
439
440bool
441ScreenshotInfo::operator==(const ScreenshotInfo& other) const
442{
443	return fCode == other.fCode
444		&& fWidth == other.fWidth
445		&& fHeight == other.fHeight
446		&& fDataSize == other.fDataSize;
447}
448
449
450bool
451ScreenshotInfo::operator!=(const ScreenshotInfo& other) const
452{
453	return !(*this == other);
454}
455
456
457// #pragma mark - PackageInfo
458
459
460BitmapRef
461PackageInfo::sDefaultIcon(new(std::nothrow) SharedBitmap(
462	"application/x-vnd.haiku-package"), true);
463
464
465PackageInfo::PackageInfo()
466	:
467	fIcon(sDefaultIcon),
468	fName(),
469	fTitle(),
470	fVersion(),
471	fPublisher(),
472	fShortDescription(),
473	fFullDescription(),
474	fChangelog(),
475	fUserRatings(),
476	fCachedRatingSummary(),
477	fProminence(0.0f),
478	fScreenshotInfos(),
479	fScreenshots(),
480	fState(NONE),
481	fDownloadProgress(0.0),
482	fFlags(0),
483	fSystemDependency(false),
484	fArchitecture(),
485	fLocalFilePath(),
486	fFileName(),
487	fSize(0),
488	fDepotName(""),
489	fIsCollatingChanges(false),
490	fCollatedChanges(0)
491{
492}
493
494
495PackageInfo::PackageInfo(const BPackageInfo& info)
496	:
497	fIcon(sDefaultIcon),
498	fName(info.Name()),
499	fTitle(),
500	fVersion(info.Version()),
501	fPublisher(),
502	fShortDescription(info.Summary()),
503	fFullDescription(info.Description()),
504	fChangelog(),
505	fUserRatings(),
506	fCachedRatingSummary(),
507	fProminence(0.0f),
508	fScreenshotInfos(),
509	fScreenshots(),
510	fState(NONE),
511	fDownloadProgress(0.0),
512	fFlags(info.Flags()),
513	fSystemDependency(false),
514	fArchitecture(info.ArchitectureName()),
515	fLocalFilePath(),
516	fFileName(info.FileName()),
517	fSize(0), // TODO: Retrieve local file size
518	fDepotName(""),
519	fIsCollatingChanges(false),
520	fCollatedChanges(0)
521
522{
523	BString publisherURL;
524	if (info.URLList().CountStrings() > 0)
525		publisherURL = info.URLList().StringAt(0);
526
527	BString publisherName = info.Vendor();
528	const BStringList& rightsList = info.CopyrightList();
529	if (rightsList.CountStrings() > 0)
530		publisherName = rightsList.Last();
531	if (!publisherName.IsEmpty())
532		publisherName.Prepend("�� ");
533
534	fPublisher = PublisherInfo(BitmapRef(), publisherName, "", publisherURL);
535}
536
537
538PackageInfo::PackageInfo(const BString& name,
539		const BPackageVersion& version, const PublisherInfo& publisher,
540		const BString& shortDescription, const BString& fullDescription,
541		int32 flags, const char* architecture)
542	:
543	fIcon(sDefaultIcon),
544	fName(name),
545	fTitle(),
546	fVersion(version),
547	fPublisher(publisher),
548	fShortDescription(shortDescription),
549	fFullDescription(fullDescription),
550	fChangelog(),
551	fCategories(),
552	fUserRatings(),
553	fCachedRatingSummary(),
554	fProminence(0.0f),
555	fScreenshotInfos(),
556	fScreenshots(),
557	fState(NONE),
558	fDownloadProgress(0.0),
559	fFlags(flags),
560	fSystemDependency(false),
561	fArchitecture(architecture),
562	fLocalFilePath(),
563	fFileName(),
564	fSize(0),
565	fDepotName(""),
566	fIsCollatingChanges(false),
567	fCollatedChanges(0)
568{
569}
570
571
572PackageInfo::PackageInfo(const PackageInfo& other)
573	:
574	fIcon(other.fIcon),
575	fName(other.fName),
576	fTitle(other.fTitle),
577	fVersion(other.fVersion),
578	fPublisher(other.fPublisher),
579	fShortDescription(other.fShortDescription),
580	fFullDescription(other.fFullDescription),
581	fChangelog(other.fChangelog),
582	fCategories(other.fCategories),
583	fUserRatings(other.fUserRatings),
584	fCachedRatingSummary(other.fCachedRatingSummary),
585	fProminence(other.fProminence),
586	fScreenshotInfos(other.fScreenshotInfos),
587	fScreenshots(other.fScreenshots),
588	fState(other.fState),
589	fInstallationLocations(other.fInstallationLocations),
590	fDownloadProgress(other.fDownloadProgress),
591	fFlags(other.fFlags),
592	fSystemDependency(other.fSystemDependency),
593	fArchitecture(other.fArchitecture),
594	fLocalFilePath(other.fLocalFilePath),
595	fFileName(other.fFileName),
596	fSize(other.fSize),
597	fDepotName(other.fDepotName),
598	fIsCollatingChanges(false),
599	fCollatedChanges(0)
600{
601}
602
603
604PackageInfo&
605PackageInfo::operator=(const PackageInfo& other)
606{
607	fIcon = other.fIcon;
608	fName = other.fName;
609	fTitle = other.fTitle;
610	fVersion = other.fVersion;
611	fPublisher = other.fPublisher;
612	fShortDescription = other.fShortDescription;
613	fFullDescription = other.fFullDescription;
614	fChangelog = other.fChangelog;
615	fCategories = other.fCategories;
616	fUserRatings = other.fUserRatings;
617	fCachedRatingSummary = other.fCachedRatingSummary;
618	fProminence = other.fProminence;
619	fScreenshotInfos = other.fScreenshotInfos;
620	fScreenshots = other.fScreenshots;
621	fState = other.fState;
622	fInstallationLocations = other.fInstallationLocations;
623	fDownloadProgress = other.fDownloadProgress;
624	fFlags = other.fFlags;
625	fSystemDependency = other.fSystemDependency;
626	fArchitecture = other.fArchitecture;
627	fLocalFilePath = other.fLocalFilePath;
628	fFileName = other.fFileName;
629	fSize = other.fSize;
630
631	return *this;
632}
633
634
635bool
636PackageInfo::operator==(const PackageInfo& other) const
637{
638	return fIcon == other.fIcon
639		&& fName == other.fName
640		&& fTitle == other.fTitle
641		&& fVersion == other.fVersion
642		&& fPublisher == other.fPublisher
643		&& fShortDescription == other.fShortDescription
644		&& fFullDescription == other.fFullDescription
645		&& fChangelog == other.fChangelog
646		&& fCategories == other.fCategories
647		&& fUserRatings == other.fUserRatings
648		&& fCachedRatingSummary == other.fCachedRatingSummary
649		&& fProminence == other.fProminence
650		&& fScreenshotInfos == other.fScreenshotInfos
651		&& fScreenshots == other.fScreenshots
652		&& fState == other.fState
653		&& fFlags == other.fFlags
654		&& fDownloadProgress == other.fDownloadProgress
655		&& fSystemDependency == other.fSystemDependency
656		&& fArchitecture == other.fArchitecture
657		&& fLocalFilePath == other.fLocalFilePath
658		&& fFileName == other.fFileName
659		&& fSize == other.fSize;
660}
661
662
663bool
664PackageInfo::operator!=(const PackageInfo& other) const
665{
666	return !(*this == other);
667}
668
669
670void
671PackageInfo::SetTitle(const BString& title)
672{
673	if (fTitle != title) {
674		fTitle = title;
675		_NotifyListeners(PKG_CHANGED_TITLE);
676	}
677}
678
679
680const BString&
681PackageInfo::Title() const
682{
683	return fTitle.Length() > 0 ? fTitle : fName;
684}
685
686
687void
688PackageInfo::SetShortDescription(const BString& description)
689{
690	if (fShortDescription != description) {
691		fShortDescription = description;
692		_NotifyListeners(PKG_CHANGED_SUMMARY);
693	}
694}
695
696
697void
698PackageInfo::SetFullDescription(const BString& description)
699{
700	if (fFullDescription != description) {
701		fFullDescription = description;
702		_NotifyListeners(PKG_CHANGED_DESCRIPTION);
703	}
704}
705
706
707void
708PackageInfo::SetIcon(const BitmapRef& icon)
709{
710	if (fIcon != icon) {
711		fIcon = icon;
712		_NotifyListeners(PKG_CHANGED_ICON);
713	}
714}
715
716
717void
718PackageInfo::SetChangelog(const BString& changelog)
719{
720	if (fChangelog != changelog) {
721		fChangelog = changelog;
722		_NotifyListeners(PKG_CHANGED_CHANGELOG);
723	}
724}
725
726
727bool
728PackageInfo::IsSystemPackage() const
729{
730	return (fFlags & BPackageKit::B_PACKAGE_FLAG_SYSTEM_PACKAGE) != 0;
731}
732
733
734void
735PackageInfo::ClearCategories()
736{
737	if (!fCategories.IsEmpty()) {
738		fCategories.Clear();
739		_NotifyListeners(PKG_CHANGED_CATEGORIES);
740	}
741}
742
743
744bool
745PackageInfo::AddCategory(const CategoryRef& category)
746{
747	if (fCategories.Add(category)) {
748		_NotifyListeners(PKG_CHANGED_CATEGORIES);
749		return true;
750	}
751	return false;
752}
753
754
755void
756PackageInfo::SetSystemDependency(bool isDependency)
757{
758	fSystemDependency = isDependency;
759}
760
761
762void
763PackageInfo::SetState(PackageState state)
764{
765	if (fState != state) {
766		fState = state;
767		if (fState != DOWNLOADING)
768			fDownloadProgress = 0.0;
769		_NotifyListeners(PKG_CHANGED_STATE);
770	}
771}
772
773
774void
775PackageInfo::AddInstallationLocation(int32 location)
776{
777	fInstallationLocations.insert(location);
778	SetState(ACTIVATED);
779		// TODO: determine how to differentiate between installed and active.
780}
781
782
783void
784PackageInfo::SetDownloadProgress(float progress)
785{
786	fState = DOWNLOADING;
787	fDownloadProgress = progress;
788	_NotifyListeners(PKG_CHANGED_STATE);
789}
790
791
792void
793PackageInfo::SetLocalFilePath(const char* path)
794{
795	fLocalFilePath = path;
796}
797
798
799bool
800PackageInfo::IsLocalFile() const
801{
802	return !fLocalFilePath.IsEmpty() && fInstallationLocations.empty();
803}
804
805
806void
807PackageInfo::ClearUserRatings()
808{
809	if (!fUserRatings.IsEmpty()) {
810		fUserRatings.Clear();
811		_NotifyListeners(PKG_CHANGED_RATINGS);
812	}
813}
814
815
816bool
817PackageInfo::AddUserRating(const UserRating& rating)
818{
819	if (!fUserRatings.Add(rating))
820		return false;
821
822	_NotifyListeners(PKG_CHANGED_RATINGS);
823
824	return true;
825}
826
827
828void
829PackageInfo::SetRatingSummary(const RatingSummary& summary)
830{
831	if (fCachedRatingSummary == summary)
832		return;
833
834	fCachedRatingSummary = summary;
835
836	_NotifyListeners(PKG_CHANGED_RATINGS);
837}
838
839
840RatingSummary
841PackageInfo::CalculateRatingSummary() const
842{
843	if (fUserRatings.CountItems() == 0)
844		return fCachedRatingSummary;
845
846	RatingSummary summary;
847	summary.ratingCount = fUserRatings.CountItems();
848	summary.averageRating = 0.0f;
849	int starRatingCount = sizeof(summary.ratingCountByStar) / sizeof(int);
850	for (int i = 0; i < starRatingCount; i++)
851		summary.ratingCountByStar[i] = 0;
852
853	if (summary.ratingCount <= 0)
854		return summary;
855
856	float ratingSum = 0.0f;
857
858	int ratingsSpecified = summary.ratingCount;
859	for (int i = 0; i < summary.ratingCount; i++) {
860		float rating = fUserRatings.ItemAtFast(i).Rating();
861
862		if (rating < 0.0f)
863			rating = -1.0f;
864		else if (rating > 5.0f)
865			rating = 5.0f;
866
867		if (rating >= 0.0f)
868			ratingSum += rating;
869
870		if (rating <= 0.0f)
871			ratingsSpecified--; // No rating specified by user
872		else if (rating <= 1.0f)
873			summary.ratingCountByStar[0]++;
874		else if (rating <= 2.0f)
875			summary.ratingCountByStar[1]++;
876		else if (rating <= 3.0f)
877			summary.ratingCountByStar[2]++;
878		else if (rating <= 4.0f)
879			summary.ratingCountByStar[3]++;
880		else if (rating <= 5.0f)
881			summary.ratingCountByStar[4]++;
882	}
883
884	if (ratingsSpecified > 1)
885		ratingSum /= ratingsSpecified;
886
887	summary.averageRating = ratingSum;
888	summary.ratingCount = ratingsSpecified;
889
890	return summary;
891}
892
893
894void
895PackageInfo::SetProminence(float prominence)
896{
897	if (fProminence != prominence) {
898		fProminence = prominence;
899		_NotifyListeners(PKG_CHANGED_PROMINENCE);
900	}
901}
902
903
904bool
905PackageInfo::IsProminent() const
906{
907	return HasProminence() && Prominence() <= 200;
908}
909
910
911void
912PackageInfo::ClearScreenshotInfos()
913{
914	fScreenshotInfos.Clear();
915}
916
917
918bool
919PackageInfo::AddScreenshotInfo(const ScreenshotInfo& info)
920{
921	return fScreenshotInfos.Add(info);
922}
923
924
925void
926PackageInfo::ClearScreenshots()
927{
928	if (!fScreenshots.IsEmpty()) {
929		fScreenshots.Clear();
930		_NotifyListeners(PKG_CHANGED_SCREENSHOTS);
931	}
932}
933
934
935bool
936PackageInfo::AddScreenshot(const BitmapRef& screenshot)
937{
938	if (!fScreenshots.Add(screenshot))
939		return false;
940
941	_NotifyListeners(PKG_CHANGED_SCREENSHOTS);
942
943	return true;
944}
945
946
947void
948PackageInfo::SetSize(int64 size)
949{
950	if (fSize != size) {
951		fSize = size;
952		_NotifyListeners(PKG_CHANGED_SIZE);
953	}
954}
955
956
957void
958PackageInfo::SetDepotName(const BString& depotName)
959{
960	if (fDepotName != depotName) {
961		fDepotName = depotName;
962		_NotifyListeners(PKG_CHANGED_DEPOT);
963	}
964}
965
966
967bool
968PackageInfo::AddListener(const PackageInfoListenerRef& listener)
969{
970	return fListeners.Add(listener);
971}
972
973
974void
975PackageInfo::RemoveListener(const PackageInfoListenerRef& listener)
976{
977	fListeners.Remove(listener);
978}
979
980
981void
982PackageInfo::CleanupDefaultIcon()
983{
984	sDefaultIcon.Unset();
985}
986
987
988void
989PackageInfo::StartCollatingChanges()
990{
991	fIsCollatingChanges = true;
992	fCollatedChanges = 0;
993}
994
995
996void
997PackageInfo::EndCollatingChanges()
998{
999	if (fIsCollatingChanges && fCollatedChanges != 0)
1000		_NotifyListenersImmediate(fCollatedChanges);
1001	fIsCollatingChanges = false;
1002	fCollatedChanges = 0;
1003}
1004
1005
1006void
1007PackageInfo::_NotifyListeners(uint32 changes)
1008{
1009	if (fIsCollatingChanges)
1010		fCollatedChanges |= changes;
1011	else
1012		_NotifyListenersImmediate(changes);
1013}
1014
1015
1016void
1017PackageInfo::_NotifyListenersImmediate(uint32 changes)
1018{
1019	int count = fListeners.CountItems();
1020	if (count == 0)
1021		return;
1022
1023	// Clone list to avoid listeners detaching themselves in notifications
1024	// to screw up the list while iterating it.
1025	PackageListenerList listeners(fListeners);
1026	// Check if it worked:
1027	if (listeners.CountItems() != count)
1028		return;
1029
1030	PackageInfoEvent event(PackageInfoRef(this), changes);
1031
1032	for (int i = 0; i < count; i++) {
1033		const PackageInfoListenerRef& listener = listeners.ItemAtFast(i);
1034		if (listener.Get() != NULL)
1035			listener->PackageChanged(event);
1036	}
1037}
1038
1039
1040// #pragma mark - Sorting Functions
1041
1042
1043/*! This function is used with the List class in order to facilitate fast
1044    ordered inserting of packages.
1045 */
1046
1047static int32
1048PackageCompare(const PackageInfoRef& p1, const PackageInfoRef& p2)
1049{
1050	return p1->Name().Compare(p2->Name());
1051}
1052
1053
1054/*! This function is used with the List class in order to facilitate fast
1055    searching of packages.
1056 */
1057
1058static int32
1059PackageFixedNameCompare(const void* context,
1060	const PackageInfoRef& package)
1061{
1062	const BString* packageName = static_cast<const BString*>(context);
1063	return packageName->Compare(package->Name());
1064}
1065
1066
1067// #pragma mark -
1068
1069
1070DepotInfo::DepotInfo()
1071	:
1072	fName(),
1073	fPackages(&PackageCompare, &PackageFixedNameCompare),
1074	fWebAppRepositoryCode()
1075{
1076}
1077
1078
1079DepotInfo::DepotInfo(const BString& name)
1080	:
1081	fName(name),
1082	fPackages(&PackageCompare, &PackageFixedNameCompare),
1083	fWebAppRepositoryCode(),
1084	fWebAppRepositorySourceCode()
1085{
1086}
1087
1088
1089DepotInfo::DepotInfo(const DepotInfo& other)
1090	:
1091	fName(other.fName),
1092	fPackages(other.fPackages),
1093	fWebAppRepositoryCode(other.fWebAppRepositoryCode),
1094	fWebAppRepositorySourceCode(other.fWebAppRepositorySourceCode),
1095	fURL(other.fURL)
1096{
1097}
1098
1099
1100DepotInfo&
1101DepotInfo::operator=(const DepotInfo& other)
1102{
1103	fName = other.fName;
1104	fPackages = other.fPackages;
1105	fURL = other.fURL;
1106	fWebAppRepositoryCode = other.fWebAppRepositoryCode;
1107	fWebAppRepositorySourceCode = other.fWebAppRepositorySourceCode;
1108	return *this;
1109}
1110
1111
1112bool
1113DepotInfo::operator==(const DepotInfo& other) const
1114{
1115	return fName == other.fName
1116		&& fPackages == other.fPackages;
1117}
1118
1119
1120bool
1121DepotInfo::operator!=(const DepotInfo& other) const
1122{
1123	return !(*this == other);
1124}
1125
1126
1127/*! This method will insert the package into the list of packages
1128    in order so that the list of packages remains in order.
1129 */
1130
1131bool
1132DepotInfo::AddPackage(const PackageInfoRef& package)
1133{
1134 	return fPackages.Add(package);
1135}
1136
1137
1138int32
1139DepotInfo::PackageIndexByName(const BString& packageName) const
1140{
1141	return fPackages.Search(&packageName);
1142}
1143
1144
1145void
1146DepotInfo::SyncPackages(const PackageList& otherPackages)
1147{
1148	PackageList packages(fPackages);
1149
1150	for (int32 i = otherPackages.CountItems() - 1; i >= 0; i--) {
1151		const PackageInfoRef& otherPackage = otherPackages.ItemAtFast(i);
1152		bool found = false;
1153		for (int32 j = packages.CountItems() - 1; j >= 0; j--) {
1154			const PackageInfoRef& package = packages.ItemAtFast(j);
1155			if (package->Name() == otherPackage->Name()) {
1156				package->SetState(otherPackage->State());
1157				package->SetLocalFilePath(otherPackage->LocalFilePath());
1158				package->SetSystemDependency(
1159					otherPackage->IsSystemDependency());
1160				found = true;
1161				packages.Remove(j);
1162				break;
1163			}
1164		}
1165		if (!found) {
1166			printf("%s: new package: '%s'\n", fName.String(),
1167				otherPackage->Name().String());
1168			fPackages.Add(otherPackage);
1169		}
1170	}
1171
1172	for (int32 i = packages.CountItems() - 1; i >= 0; i--) {
1173		const PackageInfoRef& package = packages.ItemAtFast(i);
1174		printf("%s: removing package: '%s'\n", fName.String(),
1175			package->Name().String());
1176		fPackages.Remove(package);
1177	}
1178}
1179
1180
1181void
1182DepotInfo::SetURL(const BString& URL)
1183{
1184	fURL = URL;
1185}
1186
1187
1188void
1189DepotInfo::SetWebAppRepositoryCode(const BString& code)
1190{
1191	fWebAppRepositoryCode = code;
1192}
1193
1194
1195void
1196DepotInfo::SetWebAppRepositorySourceCode(const BString& code)
1197{
1198	fWebAppRepositorySourceCode = code;
1199}
1200