HaikuDepot: Refactor for general performance

This commit is a large "big bang" commit of a composite of changes
that would have been challenging to put in place over a PR train.
The changes yield a significant performance improvement in general
but also lay the foundation for being able to make further smaller
gains over time. The changes should also improve stability of the
application. This commit is a high-risk change and would ideally
be coupled with testing.

The key things which have changed in this commit;

- Code reformatting has been applied at the same time.
- Data models are now largely immutable with builder-style
  constructors.
  - The code base was previously handling locking of data obtained
    from the central model manually in many places which was
    prone to mistakes. Now accessing data is, from the clients'
    perspective, largely lock-free.
  - The removal of locks now removes lock-contention in various
    areas of the application including startup which helps with
    improving performance.
  - Repositories of data such as categories and languages are
    now also immutable.
- To simplify data immutability, packages' data is stored in the
  central model separately from depots' data.
- Large package data mutations are now done in chunks of packages
  rather than package-by-package which means reduced messaging
  within the application; especially around the GUI.
- The resolution of data / mutations' notifications is larger
  so that changes are not being considered on each property of
  a package which simplifies the processing logic in a number of
  places.
- Package change-notification is driven out of the central model
  rather than from each and every package. There is scope for
  further simplification in this space.
- Where logic already knows what data is changed, it is able to
  control the change-notification process rather than cause a
  large number of object-comparisons to be required.
- The data-loaders from the local package manager and from HDS
  have been re-written to work with immutable models.
- The icon repositories have been re-written to make a one-time
  notification that icons have changed rather than notify for
  every package.
- Data updates reflection in the UI has been changed to work in
  chunks of packages rather than package by package which
  reduces the quantity of UI window-invalidation / redraws and
  also reduces the quantity of re-calculation of scrollbars and
  other UI elements.
- Some UI elements have their metrics calculated lazily rather
  than up-front which yields a significant performance gain.

Change-Id: Iefaf4a137411e379e4c461ebc8c783fd1646bcf4
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9093
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Andrew Lindesay <[email protected]>
This commit is contained in:
Andrew Lindesay
2025-03-15 06:56:32 +00:00
parent 20a6449385
commit 05ff00f04c
125 changed files with 7445 additions and 5037 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ resource app_flags B_SINGLE_LAUNCH;
resource app_version {
major = 0,
middle = 0,
minor = 8,
minor = 9,
variety = B_APPV_ALPHA,
internal = 1,
+11 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef HAIKU_DEPOT_CONSTANTS_H
@@ -118,9 +118,19 @@ enum UserUsageConditionsSelectionMode {
// any prominence ordering value greater than this is not prominent.
#define PROMINANCE_ORDERING_MAX 1000
// this is the highest prominence value possible.
#define PROMINANCE_ORDERING_NONE 0
#define SINGLE_PACKAGE_DEPOT_NAME "__hd_single_pkg_depot"
// used as a fake depot name for the case where the application is looking
// at a single package based on a file.
#define SINGLE_PACKAGE_DEPOT_IDENTIFIER "__hd_single_pkg_depot_id_c07fb16620f4"
// used as a fake depot name for the case where the application is looking
// at a single package based on a file.
#define LOCAL_DEPOT_IDENTIFIER "__hd_local_depot_id_008922773013"
// used as a fake depot identifier that is a placeholder used to convey
// packages that are only installed locally and do not exist in a remote
// repository.
#endif // HAIKU_DEPOT_CONSTANTS_H
+9 -7
View File
@@ -57,6 +57,7 @@ local dumpExportReferenceModelTargetFile =
# source directories
local sourceDirs =
edits_generic
icon
model
packagemanagement
packagemodel
@@ -124,21 +125,18 @@ local applicationSources =
FeaturedPackagesView.cpp
FilterView.cpp
GeneralContentScrollView.cpp
IconTarPtr.cpp
IdentityAndAccessUtils.cpp
IncrementViewCounterProcess.cpp
JobStateListener.cpp
JwtTokenHelper.cpp
LanguageRepository.cpp
LinkView.cpp
LinkedBitmapView.cpp
Logger.cpp
main.cpp
MainWindow.cpp
MarkupTextView.cpp
MessagePackageListener.cpp
Model.cpp
PackageContentsView.cpp
PackageIconTarRepository.cpp
PackageInfoListener.cpp
PackageInfoView.cpp
PackageListView.cpp
@@ -165,6 +163,11 @@ local applicationSources =
ValidationUtils.cpp
WorkStatusView.cpp
# icon
IconTarPtr.cpp
PackageIconDefaultRepository.cpp
PackageIconTarRepository.cpp
# packagemanagement
AbstractPackageProcess.cpp
InstallPackageProcess.cpp
@@ -180,17 +183,17 @@ local applicationSources =
PackageClassificationInfo.cpp
PackageCoreInfo.cpp
PackageFilter.cpp
PackageFilterModel.cpp
PackageFilterSpecification.cpp
PackageInfo.cpp
PackageLocalInfo.cpp
PackageLocalizedText.cpp
PackagePublisherInfo.cpp
PackageScreenshotInfo.cpp
PackageUserRatingInfo.cpp
PackageVersion.cpp
ScreenshotInfo.cpp
UserInfo.cpp
UserRating.cpp
UserRatingInfo.cpp
UserRatingSummary.cpp
# network + server / local processes
@@ -231,7 +234,6 @@ local applicationSources =
PackageUtils.cpp
StorageUtils.cpp
StringUtils.cpp
LoggingUrlProtocolListener.cpp
# package_daemon
ProblemWindow.cpp
@@ -1,7 +1,7 @@
#!/usr/bin/python
# =====================================
# Copyright 2017-2023, Andrew Lindesay
# Copyright 2017-2025, Andrew Lindesay
# Distributed under the terms of the MIT License.
# =====================================
@@ -38,21 +38,21 @@ public:
void Reset();
{{#propertyarray}}{{#property.iscppscalartype}}
{{property.cpptype}} {{property.cppname}}();
{{property.cpptype}} {{property.cppname}}() const;
void Set{{property.cppname}}({{property.cpptype}} value);
void Set{{property.cppname}}Null();
bool {{property.cppname}}IsNull();
bool {{property.cppname}}IsNull() const;
{{/property.iscppscalartype}}{{#property.isarray}}
void AddTo{{property.cppname}}({{property.items.cpptype}}* value);
void Set{{property.cppname}}({{property.cpptype}}* value);
int32 Count{{property.cppname}}();
{{property.items.cpptype}}* {{property.cppname}}ItemAt(int32 index);
bool {{property.cppname}}IsNull();
int32 Count{{property.cppname}}() const;
{{property.items.cpptype}}* {{property.cppname}}ItemAt(int32 index) const;
bool {{property.cppname}}IsNull() const;
{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}
{{property.cpptype}}* {{property.cppname}}();
{{property.cpptype}}* {{property.cppname}}() const;
void Set{{property.cppname}}({{property.cpptype}}* value);
void Set{{property.cppname}}Null();
bool {{property.cppname}}IsNull();
bool {{property.cppname}}IsNull() const;
{{/property.iscppnonscalarnoncollectiontype}}
{{/propertyarray}}
@@ -112,7 +112,7 @@ void
}
{{#propertyarray}}{{#property.iscppscalartype}}{{property.cpptype}}
{{cppobjectname}}::{{property.cppname}}()
{{cppobjectname}}::{{property.cppname}}() const
{
return {{property.cppmembername}};
}
@@ -135,7 +135,7 @@ void
bool
{{cppobjectname}}::{{property.cppname}}IsNull()
{{cppobjectname}}::{{property.cppname}}IsNull() const
{
return 0 == (fHasValueBitmask & k{{property.cppname}}Bitmask);
}
@@ -160,7 +160,7 @@ void
int32
{{cppobjectname}}::Count{{property.cppname}}()
{{cppobjectname}}::Count{{property.cppname}}() const
{
if ({{property.cppmembername}} == NULL)
return 0;
@@ -169,20 +169,20 @@ int32
{{property.items.cpptype}}*
{{cppobjectname}}::{{property.cppname}}ItemAt(int32 index)
{{cppobjectname}}::{{property.cppname}}ItemAt(int32 index) const
{
return {{property.cppmembername}}->ItemAt(index);
}
bool
{{cppobjectname}}::{{property.cppname}}IsNull()
{{cppobjectname}}::{{property.cppname}}IsNull() const
{
return {{property.cppmembername}} == NULL;
}
{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}{{property.cpptype}}*
{{cppobjectname}}::{{property.cppname}}()
{{cppobjectname}}::{{property.cppname}}() const
{
return {{property.cppmembername}};
}
@@ -206,7 +206,7 @@ void
bool
{{cppobjectname}}::{{property.cppname}}IsNull()
{{cppobjectname}}::{{property.cppname}}IsNull() const
{
return {{property.cppmembername}} == NULL;
}
@@ -0,0 +1,104 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageIconDefaultRepository.h"
#include <IconUtils.h>
#include "Logger.h"
static const int32 kCacheLimit = 50;
PackageIconDefaultRepository::PackageIconDefaultRepository()
:
fVectorData(NULL),
fVectorDataSize(0),
fCache(kCacheLimit)
{
_InitDefaultVectorIcon();
}
PackageIconDefaultRepository::~PackageIconDefaultRepository()
{
delete fVectorData;
}
status_t
PackageIconDefaultRepository::GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef)
{
if (fVectorData == NULL)
return B_NOT_INITIALIZED;
bitmapHolderRef.Unset();
status_t status = B_OK;
HashString key(BString() << size);
if (!fCache.ContainsKey(key)) {
BBitmap* bitmap = NULL;
if (status == B_OK) {
bitmap = new BBitmap(BRect(0, 0, size - 1, size - 1), 0, B_RGBA32);
status = bitmap->InitCheck();
}
if (status == B_OK)
status = BIconUtils::GetVectorIcon(fVectorData, fVectorDataSize, bitmap);
if (status == B_OK) {
HDINFO("did create default package icon size %" B_PRId32, size);
BitmapHolderRef bitmapHolder(new(std::nothrow) BitmapHolder(bitmap), true);
fCache.Put(key, bitmapHolder);
} else {
delete bitmap;
bitmap = NULL;
}
}
if (status == B_OK)
bitmapHolderRef.SetTo(fCache.Get(key).Get());
else
HDERROR("failed to create default package icon size %" B_PRId32, size);
return status;
}
void
PackageIconDefaultRepository::_InitDefaultVectorIcon()
{
if (fVectorData != NULL) {
delete fVectorData;
fVectorData = NULL;
}
fVectorDataSize = 0;
BMimeType mimeType("application/x-vnd.haiku-package");
status_t status = mimeType.InitCheck();
if (status != B_OK)
return;
uint8* data;
size_t dataSize;
if (mimeType.GetIcon(&data, &dataSize) != B_OK)
debugger("the default icon was unable to be acquired");
fVectorData = new(std::nothrow) uint8[dataSize];
if (fVectorData == NULL)
HDFATAL("unable to allocate memory for the default icon");
memcpy(fVectorData, data, dataSize);
fVectorDataSize = dataSize;
}
@@ -0,0 +1,36 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_ICON_DEFAULT_REPOSITORY_H
#define PACKAGE_ICON_DEFAULT_REPOSITORY_H
#include <HashMap.h>
#include <HashString.h>
#include "LRUCache.h"
#include "PackageIconRepository.h"
class PackageIconDefaultRepository : public PackageIconRepository {
public:
PackageIconDefaultRepository();
virtual ~PackageIconDefaultRepository();
virtual status_t GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef);
private:
void _InitDefaultVectorIcon();
private:
uint8* fVectorData;
size_t fVectorDataSize;
LRUCache<HashString, BitmapHolderRef>
fCache;
};
#endif // PACKAGE_ICON_DEFAULT_REPOSITORY_H
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_ICON_REPOSITORY_H
@@ -11,13 +11,14 @@
#include "HaikuDepotConstants.h"
class PackageIconRepository {
class PackageIconRepository : public BReferenceable {
public:
virtual bool HasAnyIcon(const BString& pkgName) = 0;
virtual status_t GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef) = 0;
virtual void Clear() = 0;
};
typedef BReference<PackageIconRepository> PackageIconRepositoryRef;
#endif // PACKAGE_ICON_REPOSITORY_H
@@ -1,11 +1,10 @@
/*
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageIconTarRepository.h"
#include <AutoDeleter.h>
#include <Autolock.h>
#include <File.h>
#include <IconUtils.h>
#include <StopWatch.h>
@@ -87,8 +86,7 @@ IconTarPtrEntryListener::Handle(const TarArchiveHeader& header,
BString packageName;
BString leafName;
fileName.CopyInto(packageName, 5, secondSlashIdx - 5);
fileName.CopyInto(leafName, secondSlashIdx + 1,
fileName.Length() - (secondSlashIdx + 1));
fileName.CopyInto(leafName, secondSlashIdx + 1, fileName.Length() - (secondSlashIdx + 1));
BitmapSize storedSize;
if (_LeafNameToBitmapSize(leafName, &storedSize) == B_OK)
@@ -121,31 +119,21 @@ IconTarPtrEntryListener::_LeafNameToBitmapSize(BString& leafName, BitmapSize* st
}
PackageIconTarRepository::PackageIconTarRepository()
PackageIconTarRepository::PackageIconTarRepository(BPath& tarPath)
:
fTarPath(tarPath),
fTarIo(NULL),
fIconCache(LIMIT_ICON_CACHE),
fDefaultIconVectorData(NULL),
fDefaultIconVectorDataSize(0),
fDefaultIconCache(LIMIT_ICON_CACHE),
fIconDataBuffer(new BMallocIO())
fIconDataBuffer(new BMallocIO()),
fFallbackRepository(PackageIconDefaultRepository())
{
_InitDefaultVectorIcon();
}
PackageIconTarRepository::~PackageIconTarRepository()
{
_Close();
delete fIconDataBuffer;
delete fDefaultIconVectorData;
}
void
PackageIconTarRepository::Clear() {
BAutolock locker(&fLock);
fIconCache.Clear();
fDefaultIconCache.Clear();
}
@@ -155,27 +143,25 @@ PackageIconTarRepository::Clear() {
*/
status_t
PackageIconTarRepository::Init(BPath& tarPath)
PackageIconTarRepository::Init()
{
BAutolock locker(&fLock);
_Close();
status_t result = B_OK;
if (tarPath.Path() == NULL) {
if (fTarPath.Path() == NULL) {
HDINFO("empty path to tar-ball");
result = B_BAD_VALUE;
}
BFile *tarIo = NULL;
BFile* tarIo = NULL;
if (result == B_OK) {
HDINFO("will init icon model from tar [%s]", tarPath.Path());
tarIo = new BFile(tarPath.Path(), O_RDONLY);
HDINFO("will init icon model from tar [%s]", fTarPath.Path());
tarIo = new BFile(fTarPath.Path(), O_RDONLY);
if (!tarIo->IsReadable()) {
HDERROR("unable to read the tar [%s]", tarPath.Path());
HDERROR("unable to read the tar [%s]", fTarPath.Path());
result = B_IO_ERROR;
}
}
@@ -184,15 +170,14 @@ PackageIconTarRepository::Init(BPath& tarPath)
if (result == B_OK) {
BStopWatch watch("PackageIconTarRepository::Init", true);
HDINFO("will read [%s] and collect the tar pointers", tarPath.Path());
HDINFO("will read [%s] and collect the tar pointers", fTarPath.Path());
IconTarPtrEntryListener* listener = new IconTarPtrEntryListener(this);
ObjectDeleter<IconTarPtrEntryListener> listenerDeleter(listener);
TarArchiveService::ForEachEntry(*tarIo, listener);
double secs = watch.ElapsedTime() / 1000000.0;
HDINFO("did collect %" B_PRIi32 " tar pointers (%6.3g secs)",
fIconTarPtrs.Size(), secs);
HDINFO("did collect %" B_PRIi32 " tar pointers (%6.3g secs)", fIconTarPtrs.Size(), secs);
}
if (result == B_OK)
@@ -214,14 +199,13 @@ PackageIconTarRepository::_Close()
delete fTarIo;
fTarIo = NULL;
fIconTarPtrs.Clear();
fDefaultIconCache.Clear();
}
/*! This method should be treated private and only called from a situation
in which the class's lock is acquired. It is used to populate data from
the parsing of the tar headers. It is called from the listener above.
/*! This method is used to populate data from the parsing of the tar headers.
It is called from the listener above.
*/
// TODO; should be friend to the listener
void
PackageIconTarRepository::AddIconTarPtr(const BString& packageName, BitmapSize storedSize,
off_t offset)
@@ -231,15 +215,6 @@ PackageIconTarRepository::AddIconTarPtr(const BString& packageName, BitmapSize s
}
bool
PackageIconTarRepository::HasAnyIcon(const BString& pkgName)
{
BAutolock locker(&fLock);
HashString key(pkgName);
return fIconTarPtrs.ContainsKey(key);
}
status_t
PackageIconTarRepository::GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef)
@@ -250,39 +225,44 @@ PackageIconTarRepository::GetIcon(const BString& pkgName, uint32 size,
return B_BAD_VALUE;
}
status_t result = B_OK;
bitmapHolderRef.Unset();
BAutolock locker(&fLock);
status_t result = B_OK;
off_t iconDataTarOffset = -1;
const IconTarPtrRef tarPtrRef = _GetIconTarPtr(pkgName);
BitmapSize storedSize;
if (fTarIo == NULL) {
// get the default icon in this case.
HDDEBUG("the tar io was not configured indicating a possible initialization error");
} else {
off_t iconDataTarOffset = -1;
const IconTarPtrRef tarPtrRef = _GetIconTarPtr(pkgName);
BitmapSize storedSize;
if (tarPtrRef.IsSet()) {
storedSize = _BestStoredSize(tarPtrRef, size);
iconDataTarOffset = tarPtrRef->Offset(storedSize);
}
if (tarPtrRef.IsSet()) {
storedSize = _BestStoredSize(tarPtrRef, size);
iconDataTarOffset = tarPtrRef->Offset(storedSize);
}
if (iconDataTarOffset >= 0) {
HashString key = _ToIconCacheKey(pkgName, storedSize, size);
if (iconDataTarOffset >= 0) {
HashString key = _ToIconCacheKey(pkgName, storedSize, size);
if (fIconCache.ContainsKey(key)) {
bitmapHolderRef.SetTo(fIconCache.Get(key).Get());
} else {
result = _CreateIconFromTarOffset(iconDataTarOffset, storedSize, size, bitmapHolderRef);
if (result == B_OK) {
HDTRACE("loaded package icon [%s] of size %" B_PRId32, pkgName.String(), size);
fIconCache.Put(key, bitmapHolderRef);
if (fIconCache.ContainsKey(key)) {
bitmapHolderRef.SetTo(fIconCache.Get(key).Get());
} else {
HDERROR("failure to read image for package [%s] at offset %"
B_PRIdOFF, pkgName.String(), iconDataTarOffset);
result = _CreateIconFromTarOffset(iconDataTarOffset, storedSize, size,
bitmapHolderRef);
if (result == B_OK) {
HDTRACE("loaded package icon [%s] of size %" B_PRId32, pkgName.String(), size);
fIconCache.Put(key, bitmapHolderRef);
bitmapHolderRef.SetTo(fIconCache.Get(key).Get());
} else {
HDERROR("failure to read image for package [%s] at offset %" B_PRIdOFF,
pkgName.String(), iconDataTarOffset);
}
}
}
}
if (!bitmapHolderRef.IsSet())
result = _GetDefaultIcon(size, bitmapHolderRef);
result = fFallbackRepository.GetIcon(pkgName, size, bitmapHolderRef);
return result;
}
@@ -324,8 +304,6 @@ PackageIconTarRepository::_ToIconCacheKey(const BString& pkgName, BitmapSize sto
}
/*! This method must only be invoked with the class locked.
*/
status_t
PackageIconTarRepository::_CreateIconFromTarOffset(off_t offset, BitmapSize storedSize, uint32 size,
BitmapHolderRef& bitmapHolderRef)
@@ -409,7 +387,6 @@ PackageIconTarRepository::_BestStoredSize(const IconTarPtrRef iconTarPtrRef, int
IconTarPtrRef
PackageIconTarRepository::_GetOrCreateIconTarPtr(const BString& pkgName)
{
BAutolock locker(&fLock);
HashString key(pkgName);
if (!fIconTarPtrs.ContainsKey(key)) {
IconTarPtrRef value(new IconTarPtr(pkgName));
@@ -418,80 +395,3 @@ PackageIconTarRepository::_GetOrCreateIconTarPtr(const BString& pkgName)
}
return fIconTarPtrs.Get(key);
}
status_t
PackageIconTarRepository::_GetDefaultIcon(uint32 size, BitmapHolderRef& bitmapHolderRef)
{
if (fDefaultIconVectorData == NULL)
return B_NOT_INITIALIZED;
bitmapHolderRef.Unset();
status_t status = B_OK;
HashString key(BString() << size);
if (!fDefaultIconCache.ContainsKey(key)) {
BBitmap* bitmap = NULL;
if (status == B_OK) {
bitmap = new BBitmap(BRect(0, 0, size - 1, size - 1), 0, B_RGBA32);
status = bitmap->InitCheck();
}
if (status == B_OK) {
status = BIconUtils::GetVectorIcon(fDefaultIconVectorData, fDefaultIconVectorDataSize,
bitmap);
}
if (status == B_OK) {
HDINFO("did create default package icon size %" B_PRId32, size);
BitmapHolderRef bitmapHolder(new(std::nothrow) BitmapHolder(bitmap), true);
fDefaultIconCache.Put(key, bitmapHolder);
} else {
delete bitmap;
bitmap = NULL;
}
}
if (status == B_OK)
bitmapHolderRef.SetTo(fDefaultIconCache.Get(key).Get());
else
HDERROR("failed to create default package icon size %" B_PRId32, size);
return status;
}
void
PackageIconTarRepository::_InitDefaultVectorIcon()
{
if (fDefaultIconVectorData != NULL) {
delete fDefaultIconVectorData;
fDefaultIconVectorData = NULL;
}
fDefaultIconVectorDataSize = 0;
BMimeType mimeType("application/x-vnd.haiku-package");
status_t status = mimeType.InitCheck();
uint8* data;
size_t dataSize;
if (status == B_OK)
status = mimeType.GetIcon(&data, &dataSize);
if (status == B_OK) {
fDefaultIconVectorData = new(std::nothrow) uint8[dataSize];
if (fDefaultIconVectorData == NULL)
HDFATAL("unable to allocate memory for the default icon");
memcpy(fDefaultIconVectorData, data, dataSize);
fDefaultIconVectorDataSize = dataSize;
} else {
fDefaultIconVectorData = NULL;
fDefaultIconVectorDataSize = 0;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_ICON_TAR_REPOSITORY_H
@@ -9,12 +9,12 @@
#include <DataIO.h>
#include <HashMap.h>
#include <HashString.h>
#include <Locker.h>
#include <Path.h>
#include <Referenceable.h>
#include "IconTarPtr.h"
#include "LRUCache.h"
#include "PackageIconDefaultRepository.h"
#include "PackageIconRepository.h"
@@ -23,17 +23,15 @@ typedef BReference<IconTarPtr> IconTarPtrRef;
class PackageIconTarRepository : public PackageIconRepository {
public:
PackageIconTarRepository();
PackageIconTarRepository(BPath& tarPath);
virtual ~PackageIconTarRepository();
status_t Init(BPath& tarPath);
status_t Init();
void AddIconTarPtr(const BString& pkgName,
BitmapSize size, off_t offset);
virtual status_t GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef);
virtual bool HasAnyIcon(const BString& pkgName);
virtual void Clear();
private:
void _Close();
@@ -48,27 +46,21 @@ private:
status_t _CreateIconFromTarOffset(off_t offset, BitmapSize bitmapSize,
uint32 size, BitmapHolderRef& bitmapHolderRef);
status_t _GetDefaultIcon(uint32 size, BitmapHolderRef& bitmapHolderRef);
static BitmapSize _BestStoredSize(const IconTarPtrRef iconTarPtrRef,
int32 desiredSize);
void _InitDefaultVectorIcon();
private:
BLocker fLock;
BPath fTarPath;
BPositionIO* fTarIo;
LRUCache<HashString, BitmapHolderRef>
fIconCache;
HashMap<HashString, IconTarPtrRef>
fIconTarPtrs;
uint8* fDefaultIconVectorData;
size_t fDefaultIconVectorDataSize;
LRUCache<HashString, BitmapHolderRef>
fDefaultIconCache;
BMallocIO* fIconDataBuffer;
PackageIconDefaultRepository
fFallbackRepository;
};
#endif // PACKAGE_ICON_TAR_REPOSITORY_H
@@ -1,88 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LanguageRepository.h"
#include <algorithm>
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "StringUtils.h"
LanguageRepository::LanguageRepository()
{
}
LanguageRepository::~LanguageRepository()
{
}
void
LanguageRepository::Clear()
{
fLanguages.clear();
HDINFO("did clear the languages");
}
bool
LanguageRepository::IsEmpty() const
{
return fLanguages.empty();
}
int32
LanguageRepository::CountLanguages() const
{
return fLanguages.size();
}
const LanguageRef
LanguageRepository::LanguageAtIndex(int32 index) const
{
return fLanguages[index];
}
void
LanguageRepository::AddLanguage(const LanguageRef& value)
{
int32 index = IndexOfLanguage(value->Code(), value->CountryCode(), value->ScriptCode());
if (-1 == index) {
std::vector<LanguageRef>::iterator itInsertionPt
= std::lower_bound(fLanguages.begin(), fLanguages.end(), value, &IsLanguageBefore);
fLanguages.insert(itInsertionPt, value);
HDTRACE("did add the language [%s]", value->ID());
} else {
fLanguages[index] = value;
HDTRACE("did replace the language [%s]", value->ID());
}
}
int32
LanguageRepository::IndexOfLanguage(const char* code, const char* countryCode,
const char* scriptCode) const
{
size_t size = fLanguages.size();
for (uint32 i = 0; i < size; i++) {
const char* lCode = fLanguages[i]->Code();
const char* lCountryCode = fLanguages[i]->CountryCode();
const char* lScriptCode = fLanguages[i]->ScriptCode();
if (0 == StringUtils::NullSafeCompare(code, lCode)
&& 0 == StringUtils::NullSafeCompare(countryCode, lCountryCode)
&& 0 == StringUtils::NullSafeCompare(scriptCode, lScriptCode)) {
return i;
}
}
return -1;
}
@@ -1,34 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LANGUAGE_REPOSITORY_H
#define LANGUAGE_REPOSITORY_H
#include <vector>
#include "Language.h"
class LanguageRepository
{
public:
LanguageRepository();
virtual ~LanguageRepository();
void Clear();
bool IsEmpty() const;
int32 CountLanguages() const;
const LanguageRef LanguageAtIndex(int32 index) const;
void AddLanguage(const LanguageRef& value);
int32 IndexOfLanguage(const char* code, const char* countryCode,
const char* scriptCode) const;
private:
std::vector<LanguageRef>
fLanguages;
};
#endif // LANGUAGE_REPOSITORY_H
+415 -299
View File
@@ -1,16 +1,16 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2014, Axel Dörfler <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "Model.h"
#include <algorithm>
#include <ctime>
#include <vector>
#include <stdarg.h>
#include <stdint.h>
#include <time.h>
#include <Autolock.h>
@@ -35,11 +35,8 @@
#define B_TRANSLATION_CONTEXT "Model"
#define KEY_STORE_IDENTIFIER_PREFIX "hds.password."
// this prefix is added before the nickname in the keystore
// so that HDS username/password pairs can be identified.
static const char* kHaikuDepotKeyring = "HaikuDepot";
static const uint32 kChangeMaskAll = UINT32_MAX;
// covers all of the possible values.
ModelListener::~ModelListener()
@@ -56,58 +53,107 @@ Model::Model()
fDepots(),
fCategories(),
fPackageListViewMode(PROMINENT),
fCanShareAnonymousUsageData(false)
fCanShareAnonymousUsageData(false),
fWebApp(WebAppInterfaceRef(new WebAppInterface(UserCredentials()), true)),
fLanguages(LocaleUtils::WellKnownLanguages())
{
fIconRepository = PackageIconRepositoryRef(new PackageIconDefaultRepository(), true);
// setup the language into a default state with a hard-coded language so
// that there is a language at all.
fLanguageRepository = new LanguageRepository();
fLanguageRepository->AddLanguage(fPreferredLanguage);
fFilterSpecification = PackageFilterSpecificationBuilder().BuildRef();
fFilter = PackageFilterFactory::CreateFilter(fFilterSpecification);
fPackageFilterModel = new PackageFilterModel();
fPackageScreenshotRepository = new PackageScreenshotRepository(
PackageScreenshotRepositoryListenerRef(this),
&fWebAppInterface);
fPackageScreenshotRepository
= new PackageScreenshotRepository(PackageScreenshotRepositoryListenerRef(this), this);
}
Model::~Model()
{
delete fPackageFilterModel;
delete fPackageScreenshotRepository;
delete fLanguageRepository;
}
LanguageRepository*
Model::Languages()
const std::vector<LanguageRef>
Model::Languages() const
{
return fLanguageRepository;
BAutolock locker(&fLock);
return fLanguages;
}
PackageFilterModel*
Model::PackageFilter()
void
Model::SetLanguagesAndPreferred(const std::vector<LanguageRef>& value, const LanguageRef& preferred)
{
return fPackageFilterModel;
if (value.empty())
HDFATAL("unable to set the languages to an empty list");
if (std::find(value.begin(), value.end(), preferred) == value.end())
HDFATAL("the preferred language is not in the list of languages");
BAutolock locker(&fLock);
fLanguages = value;
std::sort(fLanguages.begin(), fLanguages.end(), IsLanguageRefLess);
SetPreferredLanguage(preferred);
HDINFO("did configure %" B_PRIu32 " languages with default [%s]",
static_cast<uint32>(fLanguages.size()), preferred->ID());
}
PackageIconRepository&
Model::GetPackageIconRepository()
void
Model::SetFilterSpecification(const PackageFilterSpecificationRef& value)
{
return fPackageIconRepository;
BAutolock locker(&fLock);
if (value != fFilterSpecification) {
fFilterSpecification = value;
fFilter = PackageFilterFactory::CreateFilter(value);
_NotifyPackageFilterChanged();
}
}
status_t
Model::InitPackageIconRepository()
const PackageFilterSpecificationRef
Model::FilterSpecification() const
{
BPath tarPath;
status_t result = StorageUtils::IconTarPath(tarPath);
if (result == B_OK)
result = fPackageIconRepository.Init(tarPath);
return result;
BAutolock locker(&fLock);
return fFilterSpecification;
}
PackageFilterRef
Model::Filter() const
{
BAutolock locker(&fLock);
return fFilter;
}
PackageIconRepositoryRef
Model::IconRepository()
{
BAutolock locker(&fLock);
return fIconRepository;
}
void
Model::SetIconRepository(PackageIconRepositoryRef value)
{
BAutolock locker(&fLock);
fIconRepository = value;
_NotifyIconsChanged();
}
void
Model::_NotifyIconsChanged()
{
std::vector<ModelListenerRef>::const_iterator it;
for (it = fListeners.begin(); it != fListeners.end(); it++) {
const ModelListenerRef& listener = *it;
if (listener.IsSet())
listener->IconsChanged();
}
}
@@ -125,101 +171,286 @@ Model::AddListener(const ModelListenerRef& listener)
}
LanguageRef
void
Model::AddPackageListener(const PackageInfoListenerRef& packageListener)
{
fPackageListeners.push_back(packageListener);
}
/*! This method will take the event info data stored in the supplied message
and turn it back into an instance of `PackageInfoEvents`. It is done in
this way because the lookup of the packages within each `PackageInfoEvent`
has to come from the model.
*/
status_t
Model::DearchiveInfoEvents(const BMessage* message, PackageInfoEvents& packageInfoEvents) const
{
BAutolock locker(&fLock);
int32 i = 0;
BMessage eventMessage;
BString packageName;
uint32 changeMask;
while (message->FindMessage(kPackageInfoEventsKey, i, &eventMessage) == B_OK) {
status_t result = B_OK;
if (result == B_OK)
result = eventMessage.FindString(kPackageInfoPackageNameKey, &packageName);
if (result == B_OK)
result = eventMessage.FindUInt32(kPackageInfoChangesKey, &changeMask);
if (result == B_OK) {
PackageInfoRef package = PackageForName(packageName);
if (package.IsSet()) {
PackageInfoEvent event(package, changeMask);
packageInfoEvents.AddEvent(event);
}
} else {
HDFATAL("broken event info found processing events");
// should not occur as the data assembly is entirely inside the application
}
i++;
}
return B_OK;
}
const LanguageRef
Model::PreferredLanguage() const
{
BAutolock locker(&fLock);
return fPreferredLanguage;
}
void
Model::SetPreferredLanguage(LanguageRef value)
Model::SetPreferredLanguage(const LanguageRef& value)
{
if (value.IsSet()) {
if (fPreferredLanguage != value)
fPreferredLanguage = value;
BAutolock locker(&fLock);
fPreferredLanguage = value;
}
}
// TODO; part of a wider change; cope with the package being in more than one
// depot
PackageInfoRef
Model::PackageForName(const BString& name)
{
std::vector<DepotInfoRef>::iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef depotInfoRef = *it;
PackageInfoRef packageInfoRef = depotInfoRef->PackageByName(name);
if (packageInfoRef.Get() != NULL)
return packageInfoRef;
}
return PackageInfoRef();
}
void
Model::MergeOrAddDepot(const DepotInfoRef& depot)
Model::SetDepots(const DepotInfoRef& depot)
{
BString depotName = depot->Name();
for(uint32 i = 0; i < fDepots.size(); i++) {
if (fDepots[i]->Name() == depotName) {
DepotInfoRef ersatzDepot(new DepotInfo(*(fDepots[i].Get())), true);
ersatzDepot->SyncPackagesFromDepot(depot);
fDepots[i] = ersatzDepot;
return;
}
}
fDepots.push_back(depot);
BAutolock locker(&fLock);
std::vector<DepotInfoRef> depots;
depots.push_back(depot);
SetDepots(depots);
}
bool
Model::HasDepot(const BString& name) const
void
Model::SetDepots(const std::vector<DepotInfoRef>& depots)
{
return NULL != DepotForName(name).Get();
BAutolock locker(&fLock);
fDepots.clear();
std::vector<DepotInfoRef>::const_iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
DepotInfoRef depot = *it;
if (!depot.IsSet()) {
HDFATAL("attempt to add an unset depot");
} else {
if (depot->Name().IsEmpty())
HDFATAL("depot has no name");
if (depot->Identifier().IsEmpty())
HDFATAL("depot has no identifier");
fDepots[depot->Name()] = depot;
}
}
}
const std::vector<DepotInfoRef>
Model::Depots() const
{
BAutolock locker(&fLock);
std::vector<DepotInfoRef> result;
std::map<BString, DepotInfoRef>::const_iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++)
result.push_back(it->second);
return result;
}
const DepotInfoRef
Model::DepotForName(const BString& name) const
{
if (name.IsEmpty())
return DepotInfoRef();
std::vector<DepotInfoRef>::const_iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef aDepot = *it;
if (aDepot->Name() == name)
return aDepot;
}
BAutolock locker(&fLock);
std::map<BString, DepotInfoRef>::const_iterator it = fDepots.find(name);
if (it != fDepots.end())
return it->second;
return DepotInfoRef();
}
int32
Model::CountDepots() const
const DepotInfoRef
Model::DepotForIdentifier(const BString& identifier) const
{
return fDepots.size();
BAutolock locker(&fLock);
std::map<BString, DepotInfoRef>::const_iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
if (it->second->Identifier() == identifier)
return it->second;
}
return DepotInfoRef();
}
DepotInfoRef
Model::DepotAtIndex(int32 index) const
const std::vector<PackageInfoRef>
Model::FilteredPackages() const
{
return fDepots[index];
BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::const_iterator it;
std::vector<PackageInfoRef> result;
for (it = fPackages.begin(); it != fPackages.end(); it++) {
PackageInfoRef package = it->second;
if (fFilter->AcceptsPackage(package))
result.push_back(package);
}
return result;
}
const std::vector<PackageInfoRef>
Model::Packages() const
{
BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::const_iterator it;
std::vector<PackageInfoRef> result;
for (it = fPackages.begin(); it != fPackages.end(); it++)
result.push_back(it->second);
return result;
}
uint32
Model::_ChangeDiff(const PackageInfoRef& package)
{
uint32 changeMask = kChangeMaskAll;
const PackageInfoRef existingPackage = PackageForName(package->Name());
if (existingPackage.IsSet())
changeMask = package->ChangeMask(*(existingPackage.Get()));
return changeMask;
}
void
Model::AddPackage(const PackageInfoRef& package)
{
if (!package.IsSet())
HDFATAL("attempt to add an unset package");
BAutolock locker(&fLock);
uint32 changeMask = _ChangeDiff(package);
fPackages[package->Name()] = package;
_NotifyPackageChange(PackageInfoEvent(package, changeMask));
}
void
Model::AddPackages(const std::vector<PackageInfoRef>& packages)
{
if (packages.empty())
return;
BAutolock locker(&fLock);
PackageInfoEvents events;
std::vector<PackageInfoRef>::const_iterator it;
for (it = packages.begin(); it != packages.end(); it++) {
PackageInfoRef package = *it;
events.AddEvent(PackageInfoEvent(package, _ChangeDiff(package)));
fPackages[package->Name()] = package;
}
_NotifyPackageChanges(events);
}
void
Model::AddPackagesWithChange(const std::vector<PackageInfoRef>& packages, uint32 changeMask)
{
if (packages.empty())
return;
BAutolock locker(&fLock);
PackageInfoEvents events;
std::vector<PackageInfoRef>::const_iterator it;
for (it = packages.begin(); it != packages.end(); it++) {
PackageInfoRef package = *it;
events.AddEvent(PackageInfoEvent(package, changeMask));
fPackages[package->Name()] = package;
}
_NotifyPackageChanges(events);
}
const PackageInfoRef
Model::PackageForName(const BString& name) const
{
BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::const_iterator it = fPackages.find(name);
if (it != fPackages.end())
return it->second;
return PackageInfoRef();
}
bool
Model::HasPackage(const BString& packageName) const
{
BAutolock locker(&fLock);
if (fPackages.find(packageName) != fPackages.end())
return true;
return false;
}
bool
Model::HasAnyProminentPackages()
{
std::vector<DepotInfoRef>::iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef aDepot = *it;
if (aDepot->HasAnyProminentPackages())
return true;
BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::iterator it;
for (it = fPackages.begin(); it != fPackages.end(); it++) {
const PackageInfoRef package = it->second;
if (package.IsSet()) {
const PackageClassificationInfoRef classificationInfo
= package->PackageClassificationInfo();
if (classificationInfo.IsSet()) {
if (classificationInfo->IsProminent())
return true;
}
}
}
return false;
}
@@ -227,47 +458,41 @@ Model::HasAnyProminentPackages()
void
Model::Clear()
{
GetPackageIconRepository().Clear();
BAutolock locker(&fLock);
fIconRepository = PackageIconRepositoryRef(new PackageIconDefaultRepository(), true);
fDepots.clear();
fPopulatedPackageNames.MakeEmpty();
fPackages.clear();
}
void
Model::SetStateForPackagesByName(BStringList& packageNames, PackageState state)
package_list_view_mode
Model::PackageListViewMode() const
{
for (int32 i = 0; i < packageNames.CountStrings(); i++) {
BString packageName = packageNames.StringAt(i);
PackageInfoRef packageInfo = PackageForName(packageName);
if (packageInfo.IsSet()) {
// TODO; make this immutable
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(packageInfo);
localInfo->SetState(state);
packageInfo->SetLocalInfo(localInfo);
HDINFO("did update package [%s] with state [%s]", packageName.String(),
PackageUtils::StateToString(state));
}
else {
HDINFO("was unable to find package [%s] so was not possible to set the state to [%s]",
packageName.String(), PackageUtils::StateToString(state));
}
}
BAutolock locker(&fLock);
return fPackageListViewMode;
}
void
Model::SetPackageListViewMode(package_list_view_mode mode)
{
BAutolock locker(&fLock);
fPackageListViewMode = mode;
}
bool
Model::CanShareAnonymousUsageData() const
{
BAutolock locker(&fLock);
return fCanShareAnonymousUsageData;
}
void
Model::SetCanShareAnonymousUsageData(bool value)
{
BAutolock locker(&fLock);
fCanShareAnonymousUsageData = value;
}
@@ -293,104 +518,29 @@ Model::CanPopulatePackage(const PackageInfoRef& package)
}
static void
model_remove_key_for_user(const BString& nickname)
WebAppInterfaceRef
Model::WebApp()
{
if (nickname.IsEmpty())
return;
BKeyStore keyStore;
BPasswordKey key;
BString passwordIdentifier = BString(KEY_STORE_IDENTIFIER_PREFIX)
<< nickname;
status_t result = keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD,
passwordIdentifier, key);
switch (result) {
case B_OK:
result = keyStore.RemoveKey(kHaikuDepotKeyring, key);
if (result != B_OK) {
HDERROR("error occurred when removing password for nickname "
"[%s] : %s", nickname.String(), strerror(result));
}
break;
case B_ENTRY_NOT_FOUND:
return;
default:
HDERROR("error occurred when finding password for nickname "
"[%s] : %s", nickname.String(), strerror(result));
break;
}
}
void
Model::SetNickname(BString nickname)
{
BString password;
BString existingNickname = Nickname();
// this happens when the user is logging out. Best to remove the password
// stored for the existing user since it is no longer required.
if (!existingNickname.IsEmpty() && nickname.IsEmpty())
model_remove_key_for_user(existingNickname);
if (nickname.Length() > 0) {
BPasswordKey key;
BKeyStore keyStore;
BString passwordIdentifier = BString(KEY_STORE_IDENTIFIER_PREFIX)
<< nickname;
if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD,
passwordIdentifier, key) == B_OK) {
password = key.Password();
}
if (password.IsEmpty())
nickname = "";
}
SetCredentials(nickname, password, false);
BAutolock locker(&fLock);
return fWebApp;
}
const BString&
Model::Nickname()
{
return fWebAppInterface.Nickname();
BAutolock locker(&fLock);
return fWebApp->Nickname();
}
void
Model::SetCredentials(const BString& nickname, const BString& passwordClear,
bool storePassword)
Model::SetCredentials(const UserCredentials& credentials)
{
BString existingNickname = Nickname();
if (storePassword) {
// no point continuing to store the password for the previous user.
if (!existingNickname.IsEmpty())
model_remove_key_for_user(existingNickname);
// adding a key that is already there does not seem to override the
// existing key so the old key needs to be removed first.
if (!nickname.IsEmpty())
model_remove_key_for_user(nickname);
if (!nickname.IsEmpty() && !passwordClear.IsEmpty()) {
BString keyIdentifier = BString(KEY_STORE_IDENTIFIER_PREFIX)
<< nickname;
BPasswordKey key(passwordClear, B_KEY_PURPOSE_WEB, keyIdentifier);
BKeyStore keyStore;
keyStore.AddKeyring(kHaikuDepotKeyring);
keyStore.AddKey(kHaikuDepotKeyring, key);
}
}
BAutolock locker(&fLock);
fWebAppInterface.SetCredentials(UserCredentials(nickname, passwordClear));
if (nickname != existingNickname)
const BString existingNickname = Nickname();
fWebApp = WebAppInterfaceRef(new WebAppInterface(credentials));
if (credentials.Nickname() != existingNickname)
_NotifyAuthorizationChanged();
}
@@ -398,6 +548,22 @@ Model::SetCredentials(const BString& nickname, const BString& passwordClear,
// #pragma mark - listener notification methods
/*! Assumes that the class is locked.
*/
void
Model::_NotifyPackageFilterChanged()
{
std::vector<ModelListenerRef>::const_iterator it;
for (it = fListeners.begin(); it != fListeners.end(); it++) {
const ModelListenerRef& listener = *it;
if (listener.IsSet())
listener->PackageFilterChanged();
}
}
/*! Assumes that the class is locked.
*/
void
Model::_NotifyAuthorizationChanged()
{
@@ -423,8 +589,35 @@ Model::_NotifyCategoryListChanged()
void
Model::_MaybeLogJsonRpcError(const BMessage &responsePayload,
const char *sourceDescription) const
Model::_NotifyPackageChange(const PackageInfoEvent& event)
{
std::vector<PackageInfoListenerRef>::const_iterator it;
for (it = fPackageListeners.begin(); it != fPackageListeners.end(); it++) {
const PackageInfoListenerRef& listener = *it;
if (listener.IsSet())
listener->PackagesChanged(PackageInfoEvents(event));
}
}
// TODO: future work to optimize how this is conveyed to the listener in one go.
void
Model::_NotifyPackageChanges(const PackageInfoEvents& events)
{
if (events.IsEmpty())
return;
std::vector<PackageInfoListenerRef>::const_iterator it;
for (it = fPackageListeners.begin(); it != fPackageListeners.end(); it++) {
const PackageInfoListenerRef& listener = *it;
if (listener.IsSet())
listener->PackagesChanged(events);
}
}
void
Model::_MaybeLogJsonRpcError(const BMessage& responsePayload, const char* sourceDescription) const
{
BMessage error;
BString errorMessage;
@@ -433,143 +626,66 @@ Model::_MaybeLogJsonRpcError(const BMessage &responsePayload,
if (responsePayload.FindMessage("error", &error) == B_OK
&& error.FindString("message", &errorMessage) == B_OK
&& error.FindDouble("code", &errorCode) == B_OK) {
HDERROR("[%s] --> error : [%s] (%f)", sourceDescription,
errorMessage.String(), errorCode);
} else
HDERROR("[%s] --> error : [%s] (%f)", sourceDescription, errorMessage.String(), errorCode);
} else {
HDERROR("[%s] --> an undefined error has occurred", sourceDescription);
}
}
// #pragma mark - Rating Stabilities
int32
Model::CountRatingStabilities() const
const std::vector<RatingStabilityRef>
Model::RatingStabilities() const
{
return fRatingStabilities.size();
}
RatingStabilityRef
Model::RatingStabilityByCode(BString& code) const
{
std::vector<RatingStabilityRef>::const_iterator it;
for (it = fRatingStabilities.begin(); it != fRatingStabilities.end();
it++) {
RatingStabilityRef aRatingStability = *it;
if (aRatingStability->Code() == code)
return aRatingStability;
}
return RatingStabilityRef();
}
RatingStabilityRef
Model::RatingStabilityAtIndex(int32 index) const
{
return fRatingStabilities[index];
BAutolock locker(&fLock);
return fRatingStabilities;
}
void
Model::AddRatingStabilities(std::vector<RatingStabilityRef>& values)
Model::SetRatingStabilities(const std::vector<RatingStabilityRef> value)
{
std::vector<RatingStabilityRef>::const_iterator it;
for (it = values.begin(); it != values.end(); it++)
_AddRatingStability(*it);
}
void
Model::_AddRatingStability(const RatingStabilityRef& value)
{
std::vector<RatingStabilityRef>::const_iterator itInsertionPtConst
= std::lower_bound(
fRatingStabilities.begin(),
fRatingStabilities.end(),
value,
&IsRatingStabilityBefore);
std::vector<RatingStabilityRef>::iterator itInsertionPt =
fRatingStabilities.begin()
+ (itInsertionPtConst - fRatingStabilities.begin());
if (itInsertionPt != fRatingStabilities.end()
&& (*itInsertionPt)->Code() == value->Code()) {
itInsertionPt = fRatingStabilities.erase(itInsertionPt);
// replace the one with the same code.
}
fRatingStabilities.insert(itInsertionPt, value);
BAutolock locker(&fLock);
fRatingStabilities = value;
std::sort(fRatingStabilities.begin(), fRatingStabilities.end(), IsRatingStabilityRefLess);
}
// #pragma mark - Categories
int32
Model::CountCategories() const
bool
Model::HasCategories()
{
return fCategories.size();
BAutolock locker(&fLock);
return !fCategories.empty();
}
CategoryRef
Model::CategoryByCode(BString& code) const
const std::vector<CategoryRef>
Model::Categories() const
{
std::vector<CategoryRef>::const_iterator it;
for (it = fCategories.begin(); it != fCategories.end(); it++) {
CategoryRef aCategory = *it;
if (aCategory->Code() == code)
return aCategory;
}
return CategoryRef();
}
CategoryRef
Model::CategoryAtIndex(int32 index) const
{
return fCategories[index];
BAutolock locker(&fLock);
return fCategories;
}
void
Model::AddCategories(std::vector<CategoryRef>& values)
Model::SetCategories(const std::vector<CategoryRef> value)
{
std::vector<CategoryRef>::iterator it;
for (it = values.begin(); it != values.end(); it++)
_AddCategory(*it);
BAutolock locker(&fLock);
fCategories = value;
std::sort(fCategories.begin(), fCategories.end(), IsPackageCategoryRefLess);
_NotifyCategoryListChanged();
}
/*! This will insert the category in order.
*/
void
Model::_AddCategory(const CategoryRef& category)
{
std::vector<CategoryRef>::const_iterator itInsertionPtConst
= std::lower_bound(
fCategories.begin(),
fCategories.end(),
category,
&IsPackageCategoryBefore);
std::vector<CategoryRef>::iterator itInsertionPt =
fCategories.begin() + (itInsertionPtConst - fCategories.begin());
if (itInsertionPt != fCategories.end()
&& (*itInsertionPt)->Code() == category->Code()) {
itInsertionPt = fCategories.erase(itInsertionPt);
// replace the one with the same code.
}
fCategories.insert(itInsertionPt, category);
}
void
Model::ScreenshotCached(const ScreenshotCoordinate& coord)
{
BAutolock locker(&fLock);
std::vector<ModelListenerRef>::const_iterator it;
for (it = fListeners.begin(); it != fListeners.end(); it++) {
const ModelListenerRef& listener = *it;
+82 -62
View File
@@ -1,21 +1,23 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MODEL_H
#define MODEL_H
#include <vector>
#include <map>
#include <Locker.h>
#include "AbstractProcess.h"
#include "DepotInfo.h"
#include "LanguageRepository.h"
#include "PackageFilterModel.h"
#include "PackageIconTarRepository.h"
#include "PackageFilter.h"
#include "PackageFilterSpecification.h"
#include "PackageIconDefaultRepository.h"
#include "PackageInfo.h"
#include "PackageInfoListener.h"
#include "PackageScreenshotRepository.h"
#include "RatingStability.h"
#include "ScreenshotCoordinate.h"
@@ -40,6 +42,8 @@ public:
virtual void AuthorizationChanged() = 0;
virtual void CategoryListChanged() = 0;
virtual void ScreenshotCached(const ScreenshotCoordinate& coordinate) = 0;
virtual void IconsChanged() = 0;
virtual void PackageFilterChanged() = 0;
};
@@ -59,77 +63,82 @@ public:
Model();
virtual ~Model();
PackageFilterModel* PackageFilter();
PackageIconRepository&
GetPackageIconRepository();
status_t InitPackageIconRepository();
void Clear();
void AddListener(const ModelListenerRef& listener);
void AddPackageListener(const PackageInfoListenerRef& packageListener);
status_t DearchiveInfoEvents(const BMessage* message,
PackageInfoEvents& packageInfoEvents) const;
PackageScreenshotRepository*
GetPackageScreenshotRepository();
BLocker* Lock()
{ return &fLock; }
void SetFilterSpecification(const PackageFilterSpecificationRef& value);
const PackageFilterSpecificationRef
FilterSpecification() const;
PackageFilterRef Filter() const;
void AddListener(const ModelListenerRef& listener);
PackageIconRepositoryRef
IconRepository();
void SetIconRepository(PackageIconRepositoryRef value);
LanguageRef PreferredLanguage() const;
void SetPreferredLanguage(LanguageRef value);
LanguageRepository* Languages();
PackageInfoRef PackageForName(const BString& name);
void MergeOrAddDepot(const DepotInfoRef& depot);
bool HasDepot(const BString& name) const;
int32 CountDepots() const;
DepotInfoRef DepotAtIndex(int32 index) const;
const DepotInfoRef DepotForName(const BString& name) const;
const std::vector<PackageInfoRef>
Packages() const;
const std::vector<PackageInfoRef>
FilteredPackages() const;
void AddPackage(const PackageInfoRef& package);
void AddPackages(const std::vector<PackageInfoRef>& packages);
void AddPackagesWithChange(const std::vector<PackageInfoRef>& packages,
uint32 changesMask);
const PackageInfoRef
PackageForName(const BString& name) const;
bool HasPackage(const BString& packageName) const;
bool HasAnyProminentPackages();
void Clear();
const LanguageRef PreferredLanguage() const;
void SetPreferredLanguage(const LanguageRef& value);
const std::vector<LanguageRef>
Languages() const;
void SetLanguagesAndPreferred(const std::vector<LanguageRef>& value,
const LanguageRef& preferred);
int32 CountCategories() const;
CategoryRef CategoryByCode(BString& code) const;
CategoryRef CategoryAtIndex(int32 index) const;
void AddCategories(
std::vector<CategoryRef>& values);
void SetDepots(const DepotInfoRef& depot);
void SetDepots(const std::vector<DepotInfoRef>& depots);
const std::vector<DepotInfoRef>
Depots() const;
const DepotInfoRef DepotForName(const BString& name) const;
const DepotInfoRef DepotForIdentifier(const BString& identifier) const;
int32 CountRatingStabilities() const;
RatingStabilityRef RatingStabilityByCode(BString& code) const;
RatingStabilityRef RatingStabilityAtIndex(int32 index) const;
void AddRatingStabilities(
std::vector<RatingStabilityRef>& values);
void SetStateForPackagesByName(
BStringList& packageNames,
PackageState state);
const std::vector<CategoryRef>
Categories() const;
void SetCategories(const std::vector<CategoryRef> value);
bool HasCategories();
const std::vector<RatingStabilityRef>
RatingStabilities() const;
void SetRatingStabilities(const std::vector<RatingStabilityRef> value);
void SetPackageListViewMode(
package_list_view_mode mode);
package_list_view_mode
PackageListViewMode() const
{ return fPackageListViewMode; }
PackageListViewMode() const;
void SetCanShareAnonymousUsageData(bool value);
bool CanShareAnonymousUsageData() const
{ return fCanShareAnonymousUsageData; }
bool CanShareAnonymousUsageData() const;
bool CanPopulatePackage(
const PackageInfoRef& package);
bool CanPopulatePackage(const PackageInfoRef& package);
void SetNickname(BString nickname);
const BString& Nickname();
void SetCredentials(const BString& nickname,
const BString& passwordClear,
bool storePassword);
WebAppInterface* GetWebAppInterface()
{ return &fWebAppInterface; }
void SetCredentials(const UserCredentials& credentials);
const BString& Nickname();
WebAppInterfaceRef WebApp();
// PackageScreenshotRepositoryListener
virtual void ScreenshotCached(const ScreenshotCoordinate& coord);
private:
void _AddCategory(const CategoryRef& category);
uint32 _ChangeDiff(const PackageInfoRef& package);
void _AddRatingStability(
const RatingStabilityRef& value);
@@ -138,41 +147,52 @@ private:
const BMessage &responsePayload,
const char *sourceDescription) const;
void _NotifyPackageFilterChanged();
void _NotifyIconsChanged();
void _NotifyAuthorizationChanged();
void _NotifyCategoryListChanged();
void _NotifyPackageChange(const PackageInfoEvent& event);
void _NotifyPackageChanges(const PackageInfoEvents& events);
private:
BLocker fLock;
mutable BLocker fLock;
LanguageRef fPreferredLanguage;
std::vector<DepotInfoRef>
std::map<BString, DepotInfoRef>
fDepots;
std::map<BString, PackageInfoRef>
fPackages;
std::vector<CategoryRef>
fCategories;
std::vector<RatingStabilityRef>
fRatingStabilities;
BStringList fPopulatedPackageNames;
package_list_view_mode
fPackageListViewMode;
bool fCanShareAnonymousUsageData;
WebAppInterface fWebAppInterface;
WebAppInterfaceRef fWebApp;
PackageFilterModel* fPackageFilterModel;
PackageFilterSpecificationRef
fFilterSpecification;
PackageFilterRef fFilter;
LanguageRepository* fLanguageRepository;
PackageIconTarRepository
fPackageIconRepository;
std::vector<LanguageRef>
fLanguages;
PackageIconRepositoryRef
fIconRepository;
PackageScreenshotRepository*
fPackageScreenshotRepository;
std::vector<ModelListenerRef>
fListeners;
std::vector<PackageInfoListenerRef>
fPackageListeners;
};
#endif // PACKAGE_INFO_H
#endif // MODEL_H
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2013-2025, Stephan Aßmus <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageInfoListener.h"
@@ -9,6 +9,11 @@
#include "PackageInfo.h"
const char* kPackageInfoChangesKey = "change";
const char* kPackageInfoPackageNameKey = "packageName";
const char* kPackageInfoEventsKey = "events";
// #pragma mark - PackageInfoEvent
@@ -20,8 +25,7 @@ PackageInfoEvent::PackageInfoEvent()
}
PackageInfoEvent::PackageInfoEvent(const PackageInfoRef& package,
uint32 changes)
PackageInfoEvent::PackageInfoEvent(const PackageInfoRef& package, uint32 changes)
:
fPackage(package),
fChanges(changes)
@@ -47,9 +51,7 @@ PackageInfoEvent::operator==(const PackageInfoEvent& other)
{
if (this == &other)
return true;
return fPackage == other.fPackage
&& fChanges == other.fChanges;
return fPackage == other.fPackage && fChanges == other.fChanges;
}
@@ -72,6 +74,85 @@ PackageInfoEvent::operator=(const PackageInfoEvent& other)
}
status_t
PackageInfoEvent::Archive(BMessage* into, bool deep) const
{
status_t result = B_OK;
if (result == B_OK)
result = into->AddUInt32(kPackageInfoChangesKey, fChanges);
if (result == B_OK)
result = into->AddString(kPackageInfoPackageNameKey, fPackage->Name());
return result;
}
// #pragma mark - PackageInfoEvents
PackageInfoEvents::PackageInfoEvents()
{
}
PackageInfoEvents::PackageInfoEvents(const PackageInfoEvent& event)
{
fEvents.push_back(event);
}
PackageInfoEvents::PackageInfoEvents(const PackageInfoEvents& other)
{
for (int32 i = other.CountEvents() - 1; i >= 0; i--)
fEvents.push_back(other.EventAtIndex(i));
}
void
PackageInfoEvents::AddEvent(const PackageInfoEvent event)
{
fEvents.push_back(event);
}
bool
PackageInfoEvents::IsEmpty() const
{
return fEvents.empty();
}
int32
PackageInfoEvents::CountEvents() const
{
return fEvents.size();
}
const PackageInfoEvent&
PackageInfoEvents::EventAtIndex(int32 index) const
{
return fEvents[index];
}
status_t
PackageInfoEvents::Archive(BMessage* into, bool deep) const
{
status_t result = B_OK;
BString indexString;
std::vector<PackageInfoEvent>::const_iterator it;
for (it = fEvents.begin(); it != fEvents.end(); it++) {
BMessage eventMessage;
result = (*it).Archive(&eventMessage);
if (result == B_OK)
result = into->AddMessage(kPackageInfoEventsKey, &eventMessage);
}
return result;
}
// #pragma mark - PackageInfoListener
+39 -12
View File
@@ -1,15 +1,24 @@
/*
* Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2022, Andrew Lindesay <[email protected]>.
* Copyright 2022-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_INFO_LISTENER_H
#define PACKAGE_INFO_LISTENER_H
#include <vector>
#include <Archivable.h>
#include <String.h>
#include <Referenceable.h>
extern const char* kPackageInfoChangesKey;
extern const char* kPackageInfoPackageNameKey;
extern const char* kPackageInfoEventsKey;
enum {
PKG_CHANGED_LOCALIZED_TEXT = 1 << 0,
// ^ Covers title, summary, description and changelog.
@@ -17,13 +26,10 @@ enum {
PKG_CHANGED_SCREENSHOTS = 1 << 2,
PKG_CHANGED_LOCAL_INFO = 1 << 3,
// ^ Covers state, download and size.
PKG_CHANGED_ICON = 1 << 4,
// ^ Handled slightly differently.
PKG_CHANGED_CLASSIFICATION = 1 << 5,
PKG_CHANGED_CLASSIFICATION = 1 << 4,
// ^ This covers categories, prominence and is native desktop
PKG_CHANGED_CORE_INFO = 1 << 6
PKG_CHANGED_CORE_INFO = 1 << 5
// ^ covers the version change timestamp
// ...
};
@@ -31,11 +37,10 @@ class PackageInfo;
typedef BReference<PackageInfo> PackageInfoRef;
class PackageInfoEvent {
class PackageInfoEvent : public BArchivable {
public:
PackageInfoEvent();
PackageInfoEvent(const PackageInfoRef& package,
uint32 changes);
PackageInfoEvent(const PackageInfoRef& package, uint32 changes);
PackageInfoEvent(const PackageInfoEvent& other);
virtual ~PackageInfoEvent();
@@ -43,25 +48,47 @@ public:
bool operator!=(const PackageInfoEvent& other);
PackageInfoEvent& operator=(const PackageInfoEvent& other);
inline const PackageInfoRef& Package() const
inline const PackageInfoRef&
Package() const
{ return fPackage; }
inline uint32 Changes() const
{ return fChanges; }
status_t Archive(BMessage* into, bool deep = true) const;
private:
PackageInfoRef fPackage;
uint32 fChanges;
};
class PackageInfoEvents : public BArchivable {
public:
PackageInfoEvents();
PackageInfoEvents(const PackageInfoEvent& event);
PackageInfoEvents(const PackageInfoEvents& other);
bool IsEmpty() const;
void AddEvent(const PackageInfoEvent event);
int32 CountEvents() const;
const PackageInfoEvent&
EventAtIndex(int32 index) const;
status_t Archive(BMessage* into, bool deep = true) const;
private:
std::vector<PackageInfoEvent>
fEvents;
};
class PackageInfoListener : public BReferenceable {
public:
PackageInfoListener();
virtual ~PackageInfoListener();
virtual void PackageChanged(
const PackageInfoEvent& event) = 0;
virtual void PackagesChanged(const PackageInfoEvents& events) = 0;
};
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageScreenshotRepository.h"
@@ -11,6 +11,7 @@
#include "FileIO.h"
#include "Logger.h"
#include "Model.h"
#include "StorageUtils.h"
#include "WebAppInterface.h"
@@ -19,11 +20,10 @@ static const uint32 kMaxRetainedCachedScreenshots = 25;
PackageScreenshotRepository::PackageScreenshotRepository(
PackageScreenshotRepositoryListenerRef listener,
WebAppInterface* webAppInterface)
PackageScreenshotRepositoryListenerRef listener, Model* model)
:
fListener(listener),
fWebAppInterface(webAppInterface)
fModel(model)
{
_Init();
}
@@ -162,8 +162,7 @@ PackageScreenshotRepository::_Init()
{
fBaseDirectory.Unset();
status_t result = StorageUtils::LocalWorkingDirectoryPath(
"screenshot_cache", fBaseDirectory);
status_t result = StorageUtils::LocalWorkingDirectoryPath("screenshot_cache", fBaseDirectory);
if (B_OK != result)
HDERROR("unable to setup the cache directory");
@@ -202,8 +201,8 @@ PackageScreenshotRepository::_DownloadToLocalFile(const ScreenshotCoordinate& co
}
BFileIO outputDataStream(file, true); // takes ownership
status_t result = fWebAppInterface->RetrieveScreenshot(
coord.Code(), coord.Width(), coord.Height(), &outputDataStream);
status_t result = _WebApp()->RetrieveScreenshot(coord.Code(), coord.Width(), coord.Height(),
&outputDataStream);
if (result == B_OK)
result = outputDataStream.Flush();
@@ -225,7 +224,8 @@ PackageScreenshotRepository::_DeriveCachePath(const ScreenshotCoordinate& coord)
status_t
PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord, BPositionIO** data)
PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord,
BPositionIO** data)
{
status_t result = B_OK;
BPath path = _DeriveCachePath(coord);
@@ -243,3 +243,10 @@ PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord
return result;
}
WebAppInterfaceRef
PackageScreenshotRepository::_WebApp()
{
return fModel->WebApp();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SCREENSHOT_REPOSITORY_H
@@ -12,9 +12,10 @@
#include "BitmapHolder.h"
#include "ScreenshotCoordinate.h"
#include "WebAppInterface.h"
class WebAppInterface;
class Model;
class PackageScreenshotRepositoryListener : public BReferenceable {
@@ -36,7 +37,7 @@ public:
PackageScreenshotRepository(
PackageScreenshotRepositoryListenerRef listener,
WebAppInterface* webAppInterface);
Model* model);
~PackageScreenshotRepository();
status_t LoadScreenshot(const ScreenshotCoordinate& coord,
@@ -48,6 +49,8 @@ public:
status_t CacheScreenshot(const ScreenshotCoordinate& coord);
private:
WebAppInterfaceRef _WebApp();
status_t _Init();
status_t _CleanCache();
@@ -61,7 +64,7 @@ private:
private:
PackageScreenshotRepositoryListenerRef
fListener;
WebAppInterface* fWebAppInterface;
Model* fModel;
BPath fBaseDirectory;
};
+10 -15
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2020, Andrew Lindesay <[email protected]>.
* Copyright 2020-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -12,12 +12,12 @@
#include "Logger.h"
bool IsRatingStabilityBefore(const RatingStabilityRef& rs1,
const RatingStabilityRef& rs2)
bool
IsRatingStabilityRefLess(const RatingStabilityRef& rs1, const RatingStabilityRef& rs2)
{
if (!rs1.IsSet() || !rs2.IsSet())
HDFATAL("unexpected NULL reference in a referencable");
return rs1->Compare(*rs2) < 0;
debugger("illegal state in rating stability less");
return *(rs1.Get()) < *(rs2.Get());
}
@@ -30,8 +30,7 @@ RatingStability::RatingStability()
}
RatingStability::RatingStability(const BString& code,
const BString& name, int64 ordering)
RatingStability::RatingStability(const BString& code, const BString& name, int64 ordering)
:
fCode(code),
fName(name),
@@ -49,21 +48,17 @@ RatingStability::RatingStability(const RatingStability& other)
}
RatingStability&
RatingStability::operator=(const RatingStability& other)
bool
RatingStability::operator<(const RatingStability& other) const
{
fCode = other.fCode;
fName = other.fName;
fOrdering = other.fOrdering;
return *this;
return Compare(other) < 0;
}
bool
RatingStability::operator==(const RatingStability& other) const
{
return fCode == other.fCode && fName == other.fName
&& fOrdering == other.fOrdering;
return fCode == other.fCode && fName == other.fName && fOrdering == other.fOrdering;
}
+5 -8
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2020, Andrew Lindesay <[email protected]>.
* Copyright 2020-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef RATING_STABILITY_H
@@ -20,12 +20,9 @@ public:
RatingStability(
const RatingStability& other);
RatingStability&
operator=(const RatingStability& other);
bool operator==(const RatingStability& other)
const;
bool operator!=(const RatingStability& other)
const;
bool operator<(const RatingStability& other) const;
bool operator==(const RatingStability& other) const;
bool operator!=(const RatingStability& other) const;
const BString& Code() const
{ return fCode; }
@@ -46,7 +43,7 @@ private:
typedef BReference<RatingStability> RatingStabilityRef;
extern bool IsRatingStabilityBefore(const RatingStabilityRef& rs1,
extern bool IsRatingStabilityRefLess(const RatingStabilityRef& rs1,
const RatingStabilityRef& rs2);
@@ -22,8 +22,7 @@ UserCredentials::UserCredentials(BMessage* from)
}
UserCredentials::UserCredentials(const BString& nickname,
const BString& passwordClear)
UserCredentials::UserCredentials(const BString& nickname, const BString& passwordClear)
:
fNickname(nickname),
fPasswordClear(passwordClear),
@@ -140,4 +139,4 @@ UserCredentials::Archive(BMessage* into, bool deep) const
if (result == B_OK)
result = into->AddBool(KEY_IS_SUCCESSFUL, fIsSuccessful);
return result;
}
}
+1 -1
View File
@@ -18,11 +18,11 @@
class UserCredentials : public BArchivable {
public:
UserCredentials();
UserCredentials(BMessage* from);
UserCredentials(const BString& nickname,
const BString& passwordClear);
UserCredentials(const UserCredentials& other);
UserCredentials();
virtual ~UserCredentials();
UserCredentials& operator=(const UserCredentials& other);
@@ -1,7 +1,7 @@
/*
* Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2020-2024, Andrew Lindesay <[email protected]>
* Copyright 2020-2025, Andrew Lindesay <[email protected]>
*
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -9,6 +9,8 @@
#include "AbstractPackageProcess.h"
#include <Autolock.h>
#include "Logger.h"
#include "Model.h"
#include "PackageKitUtils.h"
@@ -18,78 +20,93 @@
using namespace BPackageKit;
// #pragma mark - PackageAction
AbstractPackageProcess::AbstractPackageProcess(
PackageInfoRef package, Model* model)
AbstractPackageProcess::AbstractPackageProcess(const BString& packageName, Model* model)
:
fPackage(package),
fPackageName(packageName),
fModel(model)
{
if (package.IsSet())
fInstallLocation = PackageKitUtils::DeriveInstallLocation(package.Get());
else
fInstallLocation = B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
// TODO: ideally if the package is installed at multiple locations,
// the user should be able to pick which one to remove.
// TODO: allow configuring the installation location
fPackageManager = new(std::nothrow) PackageManager(
(BPackageInstallationLocation)fInstallLocation);
}
AbstractPackageProcess::~AbstractPackageProcess()
{
delete fPackageManager;
}
int32
AbstractPackageProcess::InstallLocation() const
{
PackageInfoRef package = FindPackageByName(fPackageName);
if (package.IsSet())
return PackageKitUtils::DeriveInstallLocation(package.Get());
return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
}
PackageInfoRef
AbstractPackageProcess::FindPackageByName(const BString& name)
AbstractPackageProcess::FindPackageByName(const BString& packageName) const
{
return fModel->PackageForName(name);
return fModel->PackageForName(packageName);
}
// TODO; will refactor once the models go immutable.
void
AbstractPackageProcess::SetPackageState(PackageInfoRef& package, PackageState state)
AbstractPackageProcess::SetPackageState(const BString& packageName, PackageState state)
{
PackageInfoRef package = fModel->PackageForName(packageName);
if (package.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package);
localInfo->SetState(state);
package->SetLocalInfo(localInfo);
PackageLocalInfoRef localInfo
= PackageLocalInfoBuilder(package->LocalInfo()).WithState(state).BuildRef();
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef();
fModel->AddPackage(updatedPackage);
} else {
HDERROR("setting state, but the package is not set");
HDERROR("setting state, but the package [%s] is not present", packageName.String());
}
}
// TODO; will refactor once the models go immutable.
void
AbstractPackageProcess::SetPackageDownloadProgress(PackageInfoRef& package, float value)
AbstractPackageProcess::SetPackageDownloadProgress(const BString& packageName, float value)
{
PackageInfoRef package = fModel->PackageForName(packageName);
if (package.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package);
localInfo->SetDownloadProgress(value);
package->SetLocalInfo(localInfo);
PackageLocalInfoRef localInfo
= PackageLocalInfoBuilder(package->LocalInfo()).WithDownloadProgress(value).BuildRef();
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef();
fModel->AddPackage(updatedPackage);
} else {
HDERROR("setting progress, but the package is not set");
HDERROR("setting progress, but the package [%s] is not present", packageName.String());
}
}
// TODO; will refactor once the models go immutable.
void
AbstractPackageProcess::ClearPackageInstallationLocations(PackageInfoRef& package)
AbstractPackageProcess::ClearPackageInstallationLocations(const BString& packageName)
{
PackageInfoRef package = fModel->PackageForName(packageName);
if (package.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package);
localInfo->ClearInstallationLocations();
package->SetLocalInfo(localInfo);
PackageLocalInfoRef localInfo
= PackageLocalInfoBuilder(package->LocalInfo()).ClearInstallationLocations().BuildRef();
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef();
fModel->AddPackage(updatedPackage);
} else {
HDERROR("clearing installation locations, but the package is not set");
HDERROR("clearing installation locations, but the package [%s] is not present",
packageName.String());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2022, Andrew Lindesay <[email protected]>
* Copyright 2022-2025, Andrew Lindesay <[email protected]>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_PACKAGE_PROCESS_H
@@ -18,27 +18,23 @@ class Model;
class AbstractPackageProcess : public BReferenceable, public AbstractProcess {
public:
AbstractPackageProcess(PackageInfoRef package,
Model* model);
AbstractPackageProcess(const BString& packageName, Model* model);
virtual ~AbstractPackageProcess();
int32 InstallLocation() const
{ return fInstallLocation; }
protected:
PackageInfoRef FindPackageByName(const BString& name);
int32 InstallLocation() const;
PackageInfoRef FindPackageByName(const BString& packageName) const;
void SetPackageState(PackageInfoRef& package, PackageState state);
void SetPackageDownloadProgress(PackageInfoRef& package, float value);
void ClearPackageInstallationLocations(PackageInfoRef& package);
void SetPackageState(const BString& packageName, PackageState state);
void SetPackageDownloadProgress(const BString& packageName, float value);
void ClearPackageInstallationLocations(const BString& packageName);
protected:
PackageManager* fPackageManager;
PackageInfoRef fPackage;
BString fPackageName;
private:
Model* fModel;
int32 fInstallLocation;
};
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2024, Haiku, Inc. All Rights Reserved.
* Copyright 2013-2025, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -18,16 +18,17 @@
#include <algorithm>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Catalog.h>
#include <Locker.h>
#include <package/manager/Exceptions.h>
#include <package/hpkg/NoErrorOutput.h>
#include <package/hpkg/PackageContentHandler.h>
#include <package/hpkg/PackageEntry.h>
#include <package/hpkg/PackageEntryAttribute.h>
#include <package/hpkg/PackageReader.h>
#include <package/manager/Exceptions.h>
#include <package/solver/SolverPackage.h>
#include "AppUtils.h"
@@ -57,6 +58,13 @@ using BPackageKit::BHPKG::BPackageReader;
class DownloadProgress {
public:
DownloadProgress()
:
fPackageName(),
fProgress(0)
{
}
DownloadProgress(BString packageName, float progress)
:
fPackageName(packageName),
@@ -84,14 +92,13 @@ private:
};
InstallPackageProcess::InstallPackageProcess(
PackageInfoRef package, Model* model)
InstallPackageProcess::InstallPackageProcess(const BString& packageName, Model* model)
:
AbstractPackageProcess(package, model),
AbstractPackageProcess(packageName, model),
fLastDownloadUpdate(0)
{
fDescription = B_TRANSLATE("Installing \"%PackageName%\"");
fDescription.ReplaceAll("%PackageName%", package->Name());
fDescription.ReplaceAll("%PackageName%", packageName);
}
@@ -120,14 +127,20 @@ InstallPackageProcess::Progress()
if (ProcessState() == PROCESS_RUNNING && !fDownloadProgresses.empty()) {
AutoLocker<BLocker> locker(&fLock);
float sum = 0.0;
std::vector<DownloadProgress>::const_iterator it;
for (it = fDownloadProgresses.begin();
it != fDownloadProgresses.end(); it++) {
DownloadProgress downloadProgress = *it;
sum += downloadProgress.Progress();
int32 count = 0;
std::map<BString, DownloadProgress>::const_iterator it;
for (it = fDownloadProgresses.begin(); it != fDownloadProgresses.end(); it++) {
DownloadProgress downloadProgress = it->second;
if (!downloadProgress.PackageName().IsEmpty()) {
sum += downloadProgress.Progress();
count++;
}
}
if (sum > 0.0)
return sum / (float) fDownloadProgresses.size();
return sum / static_cast<float>(count);
}
return kProgressIndeterminate;
}
@@ -136,57 +149,61 @@ InstallPackageProcess::Progress()
status_t
InstallPackageProcess::RunInternal()
{
PackageInfoRef ref(fPackage);
SetPackageState(ref, PENDING);
// TODO: allow configuring the installation location
fPackageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
| BPackageManager::B_ADD_REMOTE_REPOSITORIES
| BPackageManager::B_REFRESH_REPOSITORIES);
PackageManager* packageManager = new(std::nothrow)
PackageManager(static_cast<BPackageInstallationLocation>(InstallLocation()));
ObjectDeleter<PackageManager> solverDeleter(packageManager);
PackageState state = PackageUtils::State(ref);
SetPackageState(fPackageName, PENDING);
fPackageManager->SetCurrentActionPackage(ref, true);
fPackageManager->AddProgressListener(this);
packageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
| BPackageManager::B_ADD_REMOTE_REPOSITORIES | BPackageManager::B_REFRESH_REPOSITORIES);
BString packageName = ref->Name();
PackageLocalInfoRef localInfo = ref->LocalInfo();
PackageInfoRef package = FindPackageByName(fPackageName);
PackageState state = PackageUtils::State(package);
packageManager->SetCurrentActionPackage(package, true);
packageManager->AddProgressListener(this);
BString packageName = fPackageName;
PackageLocalInfoRef localInfo = package->LocalInfo();
if (localInfo.IsSet() && localInfo->IsLocalFile())
packageName = localInfo->LocalFilePath();
const char* packageNameString = packageName.String();
try {
fPackageManager->Install(&packageNameString, 1);
packageManager->Install(&packageNameString, 1);
} catch (BFatalErrorException& ex) {
BString errorString;
errorString.SetToFormat(
"Fatal error occurred while installing package %s: "
"%s (%s)\n", packageNameString, ex.Message().String(),
ex.Details().String());
AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString,
B_STOP_ALERT);
errorString.SetToFormat("Fatal error occurred while installing package %s: %s (%s)\n",
packageNameString, ex.Message().String(), ex.Details().String());
AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString, B_STOP_ALERT);
_SetDownloadedPackagesState(NONE);
SetPackageState(ref, state);
SetPackageState(fPackageName, state);
return ex.Error();
} catch (BAbortedByUserException& ex) {
HDINFO("Installation of package %s is aborted by user: %s",
packageNameString, ex.Message().String());
HDINFO("Installation of package %s is aborted by user: %s", packageNameString,
ex.Message().String());
_SetDownloadedPackagesState(NONE);
SetPackageState(ref, state);
SetPackageState(fPackageName, state);
return B_OK;
} catch (BNothingToDoException& ex) {
HDINFO("Nothing to do while installing package %s: %s",
packageNameString, ex.Message().String());
HDINFO("Nothing to do while installing package %s: %s", packageNameString,
ex.Message().String());
return B_OK;
} catch (BException& ex) {
HDERROR("Exception occurred while installing package %s: %s",
packageNameString, ex.Message().String());
HDERROR("Exception occurred while installing package %s: %s", packageNameString,
ex.Message().String());
_SetDownloadedPackagesState(NONE);
SetPackageState(ref, state);
SetPackageState(fPackageName, state);
return B_ERROR;
}
fPackageManager->RemoveProgressListener(this);
packageManager->RemoveProgressListener(this);
_SetDownloadedPackagesState(ACTIVATED);
@@ -198,26 +215,20 @@ InstallPackageProcess::RunInternal()
void
InstallPackageProcess::DownloadProgressChanged(
const char* packageName, float progress)
InstallPackageProcess::DownloadProgressChanged(const char* packageName, float progress)
{
bigtime_t now = system_time();
if (now - fLastDownloadUpdate < 250000 && progress != 1.0)
if (!_ShouldProcessProgress() && progress != 1.0)
return;
fLastDownloadUpdate = now;
BString simplePackageName;
if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) {
HDERROR("malformed canonical package name [%s]", packageName);
return;
}
PackageInfoRef ref(FindPackageByName(simplePackageName));
if (ref.IsSet()) {
SetPackageDownloadProgress(ref, progress);
_SetDownloadProgress(simplePackageName, progress);
} else {
HDERROR("unable to find the package info for simple package name [%s]",
simplePackageName.String());
}
SetPackageDownloadProgress(simplePackageName, progress);
_SetDownloadProgress(simplePackageName, progress);
}
@@ -230,29 +241,21 @@ InstallPackageProcess::DownloadProgressComplete(const char* packageName)
return;
}
_SetDownloadProgress(simplePackageName, 1.0);
PackageInfoRef ref(FindPackageByName(simplePackageName));
if (!ref.IsSet()) {
HDERROR("unable to find the package info for simple package name [%s]",
simplePackageName.String());
return;
}
SetPackageDownloadProgress(ref, 1.0);
fDownloadedPackages.insert(ref);
SetPackageDownloadProgress(simplePackageName, 1.0);
fDownloadedPackageNames.insert(simplePackageName);
}
void
InstallPackageProcess::ConfirmedChanges(
BPackageManager::InstalledRepository& repository)
InstallPackageProcess::ConfirmedChanges(BPackageManager::InstalledRepository& repository)
{
BPackageManager::InstalledRepository::PackageList& activationList =
repository.PackagesToActivate();
BPackageManager::InstalledRepository::PackageList& activationList
= repository.PackagesToActivate();
BSolverPackage* package = NULL;
for (int32 i = 0; (package = activationList.ItemAt(i)); i++) {
PackageInfoRef ref(FindPackageByName(package->Info().Name()));
if (ref.IsSet())
SetPackageState(ref, PENDING);
BString packageName = package->Info().Name();
SetPackageState(packageName, PENDING);
}
}
@@ -260,22 +263,14 @@ InstallPackageProcess::ConfirmedChanges(
void
InstallPackageProcess::_SetDownloadedPackagesState(PackageState state)
{
for (PackageInfoSet::iterator it = fDownloadedPackages.begin();
it != fDownloadedPackages.end(); ++it) {
PackageInfoRef ref = (*it);
SetPackageState(ref, state);
std::set<BString>::const_iterator it;
for (it = fDownloadedPackageNames.begin(); it != fDownloadedPackageNames.end(); ++it) {
BString packageName = *it;
SetPackageState(packageName, state);
}
}
static bool
_IsDownloadProgressBefore(const DownloadProgress& dp1,
const DownloadProgress& dp2)
{
return dp1.PackageName().Compare(dp2.PackageName()) < 0;
}
/*! This method will extract the plain package name from the canonical
*/
@@ -293,22 +288,9 @@ InstallPackageProcess::_DeriveSimplePackageName(const BString& canonicalForm,
void
InstallPackageProcess::_SetDownloadProgress(const BString& simplePackageName,
float progress)
InstallPackageProcess::_SetDownloadProgress(const BString& simplePackageName, float progress)
{
AutoLocker<BLocker> locker(&fLock);
DownloadProgress downloadProgress(simplePackageName, progress);
std::vector<DownloadProgress>::iterator itInsertionPt
= std::lower_bound(
fDownloadProgresses.begin(), fDownloadProgresses.end(),
downloadProgress, &_IsDownloadProgressBefore);
if (itInsertionPt != fDownloadProgresses.end()) {
if ((*itInsertionPt).PackageName() == simplePackageName) {
itInsertionPt = fDownloadProgresses.erase(itInsertionPt);
}
}
fDownloadProgresses.insert(itInsertionPt, downloadProgress);
fDownloadProgresses[simplePackageName] = downloadProgress;
_NotifyChanged();
}
@@ -3,7 +3,7 @@
* Copyright 2011, Ingo Weinhold, <[email protected]>
* Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2017, Julian Harnath <[email protected]>.
* Copyright 2021, Andrew Lindesay <[email protected]>.
* Copyright 2021-2025, Andrew Lindesay <[email protected]>.
*
* All rights reserved. Distributed under the terms of the MIT License.
*
@@ -30,7 +30,7 @@ class InstallPackageProcess
private PackageProgressListener {
public:
InstallPackageProcess(
PackageInfoRef package, Model* model);
const BString& packageName, Model* model);
virtual ~InstallPackageProcess();
virtual const char* Name() const;
@@ -63,11 +63,11 @@ private:
float progress);
private:
std::vector<DownloadProgress>
std::map<BString, DownloadProgress>
fDownloadProgresses;
BString fDescription;
bigtime_t fLastDownloadUpdate;
PackageInfoSet fDownloadedPackages;
std::set<BString> fDownloadedPackageNames;
};
#endif // INSTALL_PACKAGE_PROCESS_H
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022, Haiku, Inc. All Rights Reserved.
* Copyright 2013-2025, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -64,8 +64,7 @@ public:
return B_OK;
}
virtual status_t HandleEntryAttribute(BPackageEntry* entry,
BPackageEntryAttribute* attribute)
virtual status_t HandleEntryAttribute(BPackageEntry* entry, BPackageEntryAttribute* attribute)
{
return B_OK;
}
@@ -75,8 +74,7 @@ public:
return B_OK;
}
virtual status_t HandlePackageAttribute(
const BPackageInfoAttributeValue& value)
virtual status_t HandlePackageAttribute(const BPackageInfoAttributeValue& value)
{
return B_OK;
}
@@ -105,10 +103,10 @@ private:
// #pragma mark - OpenPackageProcess
OpenPackageProcess::OpenPackageProcess(PackageInfoRef package, Model* model,
const DeskbarLink& link)
OpenPackageProcess::OpenPackageProcess(const BString& packageName, Model* model,
const DeskbarLink& link)
:
AbstractPackageProcess(package, model),
AbstractPackageProcess(packageName, model),
fDeskbarLink(link)
{
fDescription = _DeriveDescription();
@@ -192,7 +190,7 @@ OpenPackageProcess::_DeriveDescription()
/*static*/ bool
OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
std::vector<DeskbarLink>& foundLinks)
std::vector<DeskbarLink>& foundLinks)
{
if (!package.IsSet())
return false;
@@ -208,8 +206,7 @@ OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
status_t status = reader.Init(packagePath.Path());
if (status != B_OK) {
HDINFO("OpenPackageAction::FindAppToLaunch(): "
"failed to init BPackageReader(%s): %s",
HDINFO("OpenPackageAction::FindAppToLaunch(): failed to init BPackageReader(%s): %s",
packagePath.Path(), strerror(status));
return false;
}
@@ -218,8 +215,7 @@ OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
DeskbarLinkFinder contentHandler(foundLinks);
status = reader.ParseContent(&contentHandler);
if (status != B_OK) {
HDINFO("OpenPackageAction::FindAppToLaunch(): "
"failed parse package contents (%s): %s",
HDINFO("OpenPackageAction::FindAppToLaunch(): failed parse package contents (%s): %s",
packagePath.Path(), strerror(status));
return false;
}
@@ -3,7 +3,7 @@
* Copyright 2011, Ingo Weinhold, <[email protected]>
* Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2017, Julian Harnath <[email protected]>
* Copyright 2021, Andrew Lindesay <[email protected]>
* Copyright 2021-2025, Andrew Lindesay <[email protected]>
*
* All rights reserved. Distributed under the terms of the MIT License.
*
@@ -24,7 +24,7 @@ class OpenPackageProcess
private PackageProgressListener {
public:
OpenPackageProcess(
PackageInfoRef package, Model* model,
const BString& packageName, Model* model,
const DeskbarLink& link);
virtual ~OpenPackageProcess();
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2021, Haiku, Inc. All Rights Reserved.
* Copyright 2013-2025, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -19,6 +19,7 @@
#include <package/manager/Exceptions.h>
#include <package/solver/SolverPackage.h>
#include <AutoDeleter.h>
#include <Catalog.h>
#include "Alert.h"
@@ -38,13 +39,12 @@ using namespace BPackageKit::BManager::BPrivate;
using BPackageKit::BSolverPackage;
UninstallPackageProcess::UninstallPackageProcess(PackageInfoRef package,
Model* model)
UninstallPackageProcess::UninstallPackageProcess(const BString& packageName, Model* model)
:
AbstractPackageProcess(package, model)
AbstractPackageProcess(packageName, model)
{
fDescription = B_TRANSLATE("Uninstalling \"%PackageName%\"");
fDescription.ReplaceAll("%PackageName%", fPackage->Name());
fDescription.ReplaceAll("%PackageName%", packageName);
}
@@ -70,67 +70,68 @@ UninstallPackageProcess::Description() const
status_t
UninstallPackageProcess::RunInternal()
{
fPackageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES);
// TODO: ideally if the package is installed at multiple locations,
// the user should be able to pick which one to remove.
PackageInfoRef ref(fPackage);
PackageState state = PackageUtils::State(fPackage);
const char* packageName = ref->Name().String();
PackageManager* packageManager = new(std::nothrow)
PackageManager(static_cast<BPackageInstallationLocation>(InstallLocation()));
ObjectDeleter<PackageManager> solverDeleter(packageManager);
fPackageManager->SetCurrentActionPackage(ref, false);
fPackageManager->AddProgressListener(this);
packageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES);
PackageInfoRef package = FindPackageByName(fPackageName);
PackageState state = PackageUtils::State(package);
packageManager->SetCurrentActionPackage(package, false);
packageManager->AddProgressListener(this);
const char* packageNameString = package->Name().String();
try {
fPackageManager->Uninstall(&packageName, 1);
packageManager->Uninstall(&packageNameString, 1);
} catch (BFatalErrorException& ex) {
BString errorString;
errorString.SetToFormat(
"Fatal error occurred while uninstalling package %s: "
"%s (%s)\n", packageName, ex.Message().String(),
ex.Details().String());
AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString,
B_STOP_ALERT);
SetPackageState(ref, state);
errorString.SetToFormat("Fatal error occurred while uninstalling package %s: %s (%s)\n",
fPackageName.String(), ex.Message().String(), ex.Details().String());
AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString, B_STOP_ALERT);
SetPackageState(fPackageName, state);
return ex.Error();
} catch (BAbortedByUserException& ex) {
return B_OK;
} catch (BNothingToDoException& ex) {
return B_OK;
} catch (BException& ex) {
HDERROR("Exception occurred while uninstalling package %s: %s",
packageName, ex.Message().String());
SetPackageState(ref, state);
HDERROR("Exception occurred while uninstalling package %s: %s", fPackageName.String(),
ex.Message().String());
SetPackageState(fPackageName, state);
return B_ERROR;
}
fPackageManager->RemoveProgressListener(this);
packageManager->RemoveProgressListener(this);
return B_OK;
}
void
UninstallPackageProcess::StartApplyingChanges(
BPackageManager::InstalledRepository& repository)
UninstallPackageProcess::StartApplyingChanges(BPackageManager::InstalledRepository& repository)
{
BPackageManager::InstalledRepository::PackageList& packages
= repository.PackagesToDeactivate();
BPackageManager::InstalledRepository::PackageList& packages = repository.PackagesToDeactivate();
for (int32 i = 0; i < packages.CountItems(); i++) {
PackageInfoRef ref(FindPackageByName(packages.ItemAt(i)
->Name()));
if (ref.IsSet())
fRemovedPackages.push_back(ref);
BString packageName = packages.ItemAt(i)->Name();
fRemovedPackageNames.insert(packageName);
}
}
void
UninstallPackageProcess::ApplyingChangesDone(
BPackageManager::InstalledRepository& repository)
UninstallPackageProcess::ApplyingChangesDone(BPackageManager::InstalledRepository& repository)
{
std::vector<PackageInfoRef>::iterator it;
for (it = fRemovedPackages.begin(); it != fRemovedPackages.end(); it++) {
PackageInfoRef packageInfoRef = *it;
SetPackageState(packageInfoRef, NONE);
ClearPackageInstallationLocations(packageInfoRef);
std::set<BString>::const_iterator it;
for (it = fRemovedPackageNames.begin(); it != fRemovedPackageNames.end(); it++) {
BString packageName = *it;
SetPackageState(packageName, NONE);
ClearPackageInstallationLocations(packageName);
}
}
@@ -3,7 +3,7 @@
* Copyright 2011, Ingo Weinhold, <[email protected]>
* Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2017, Julian Harnath <[email protected]>
* Copyright 2021, Andrew Lindesay <[email protected]>
* Copyright 2021-2025, Andrew Lindesay <[email protected]>
*
* All rights reserved. Distributed under the terms of the MIT License.
*
@@ -23,7 +23,7 @@ class UninstallPackageProcess
private PackageProgressListener {
public:
UninstallPackageProcess(
PackageInfoRef package, Model* model);
const BString& packageName, Model* model);
virtual ~UninstallPackageProcess();
virtual const char* Name() const;
@@ -41,8 +41,7 @@ protected:
private:
BString fDescription;
std::vector<PackageInfoRef>
fRemovedPackages;
std::set<BString> fRemovedPackageNames;
};
#endif // INSTALL_PACKAGE_PROCESS_H
+121 -146
View File
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -14,27 +14,6 @@
#include "PackageUtils.h"
// #pragma mark - Sorting Functions
static bool
_IsPackageBeforeByName(const PackageInfoRef& p1, const BString& packageName)
{
return p1->Name().Compare(packageName) < 0;
}
/*! This function is used in order to provide an ordering on the packages
that are stored on a Depot.
*/
static bool
_IsPackageBefore(const PackageInfoRef& p1, const PackageInfoRef& p2)
{
return _IsPackageBeforeByName(p1, p2->Name());
}
// #pragma mark - Class implementation
@@ -61,31 +40,16 @@ DepotInfo::DepotInfo(const DepotInfo& other)
:
fName(other.fName),
fIdentifier(other.fIdentifier),
fPackages(other.fPackages),
fWebAppRepositoryCode(other.fWebAppRepositoryCode),
fWebAppRepositorySourceCode(other.fWebAppRepositorySourceCode)
{
}
DepotInfo&
DepotInfo::operator=(const DepotInfo& other)
{
fName = other.fName;
fIdentifier = other.fIdentifier;
fPackages = other.fPackages;
fWebAppRepositoryCode = other.fWebAppRepositoryCode;
fWebAppRepositorySourceCode = other.fWebAppRepositorySourceCode;
return *this;
}
bool
DepotInfo::operator==(const DepotInfo& other) const
{
return fName == other.fName
&& fIdentifier == fIdentifier
&& fPackages == other.fPackages;
return fName == other.fName && fIdentifier == fIdentifier;
}
@@ -96,127 +60,31 @@ DepotInfo::operator!=(const DepotInfo& other) const
}
int32
DepotInfo::CountPackages() const
const BString&
DepotInfo::Name() const
{
return fPackages.size();
return fName;
}
PackageInfoRef
DepotInfo::PackageAtIndex(int32 index)
const BString&
DepotInfo::Identifier() const
{
return fPackages[index];
return fIdentifier;
}
/*! This method will insert the package into the list of packages
in order so that the list of packages remains in order.
*/
void
DepotInfo::AddPackage(PackageInfoRef& package)
const BString&
DepotInfo::WebAppRepositoryCode() const
{
std::vector<PackageInfoRef>::iterator itInsertionPt
= std::lower_bound(
fPackages.begin(),
fPackages.end(),
package,
&_IsPackageBefore);
if (fPackages.end() != itInsertionPt && (*itInsertionPt)->Name() == package->Name())
*itInsertionPt = package;
else
fPackages.insert(itInsertionPt, package);
return fWebAppRepositoryCode;
}
bool
DepotInfo::HasPackage(const BString& packageName)
const BString&
DepotInfo::WebAppRepositorySourceCode() const
{
std::vector<PackageInfoRef>::const_iterator it
= std::lower_bound(
fPackages.begin(),
fPackages.end(),
packageName,
&_IsPackageBeforeByName);
if (it != fPackages.end()) {
PackageInfoRef candidate = *it;
return (candidate.Get() != NULL
&& candidate.Get()->Name() == packageName);
}
return false;
}
PackageInfoRef
DepotInfo::PackageByName(const BString& packageName)
{
std::vector<PackageInfoRef>::const_iterator it
= std::lower_bound(
fPackages.begin(),
fPackages.end(),
packageName,
&_IsPackageBeforeByName);
if (it != fPackages.end()) {
PackageInfoRef candidate = *it;
if (candidate.Get() != NULL && candidate.Get()->Name() == packageName)
return candidate;
}
return PackageInfoRef();
}
void
DepotInfo::SyncPackagesFromDepot(const DepotInfoRef& other)
{
for (int32 i = other->CountPackages() - 1; i >= 0; i--) {
PackageInfoRef otherPackage = other->PackageAtIndex(i);
PackageInfoRef myPackage = PackageByName(otherPackage->Name());
if (myPackage.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(myPackage);
PackageLocalInfoRef otherLocalInfo = otherPackage->LocalInfo();
if (otherLocalInfo.IsSet()) {
localInfo->SetState(otherLocalInfo->State());
localInfo->SetLocalFilePath(otherLocalInfo->LocalFilePath());
localInfo->SetSystemDependency(otherLocalInfo->IsSystemDependency());
}
myPackage->SetLocalInfo(localInfo);
} else {
HDINFO("%s: new package: '%s'", fName.String(),
otherPackage->Name().String());
AddPackage(otherPackage);
}
}
for (int32 i = CountPackages() - 1; i >= 0; i--) {
PackageInfoRef myPackage = PackageAtIndex(i);
if (!other->HasPackage(myPackage->Name())) {
HDINFO("%s: removing package: '%s'", fName.String(),
myPackage->Name().String());
fPackages.erase(fPackages.begin() + i);
}
}
}
bool
DepotInfo::HasAnyProminentPackages() const
{
std::vector<PackageInfoRef>::const_iterator it;
for (it = fPackages.begin(); it != fPackages.end(); it++) {
const PackageInfoRef& package = *it;
const PackageClassificationInfoRef& classification = package->PackageClassificationInfo();
if (classification.IsSet()) {
if (classification->IsProminent())
return true;
}
}
return false;
return fWebAppRepositorySourceCode;
}
@@ -239,3 +107,110 @@ DepotInfo::SetWebAppRepositorySourceCode(const BString& code)
{
fWebAppRepositorySourceCode = code;
}
// #pragma mark - DepotInfoBuilder
DepotInfoBuilder::DepotInfoBuilder()
:
fName(),
fIdentifier(),
fWebAppRepositoryCode(),
fWebAppRepositorySourceCode()
{
}
DepotInfoBuilder::DepotInfoBuilder(const DepotInfoRef& value)
:
fName(),
fIdentifier(),
fWebAppRepositoryCode(),
fWebAppRepositorySourceCode()
{
fSource = value;
}
DepotInfoBuilder::~DepotInfoBuilder()
{
}
void
DepotInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
DepotInfoBuilder::_Init(const DepotInfo* value)
{
fName = value->Name();
fIdentifier = value->Identifier();
fWebAppRepositoryCode = value->WebAppRepositoryCode();
fWebAppRepositorySourceCode = value->WebAppRepositorySourceCode();
}
DepotInfoRef
DepotInfoBuilder::BuildRef() const
{
if (fSource.IsSet())
return fSource;
DepotInfo* depotInfo = new DepotInfo(fName);
depotInfo->SetIdentifier(fIdentifier);
depotInfo->SetWebAppRepositoryCode(fWebAppRepositoryCode);
depotInfo->SetWebAppRepositorySourceCode(fWebAppRepositorySourceCode);
return DepotInfoRef(depotInfo, true);
}
DepotInfoBuilder&
DepotInfoBuilder::WithName(const BString& value)
{
if (!fSource.IsSet() || fSource->Name() != value) {
_InitFromSource();
fName = value;
}
return *this;
}
DepotInfoBuilder&
DepotInfoBuilder::WithIdentifier(const BString& value)
{
if (!fSource.IsSet() || fSource->Identifier() != value) {
_InitFromSource();
fIdentifier = value;
}
return *this;
}
DepotInfoBuilder&
DepotInfoBuilder::WithWebAppRepositoryCode(const BString& value)
{
if (!fSource.IsSet() || fSource->WebAppRepositoryCode() != value) {
_InitFromSource();
fWebAppRepositoryCode = value;
}
return *this;
}
DepotInfoBuilder&
DepotInfoBuilder::WithWebAppRepositorySourceCode(const BString& value)
{
if (!fSource.IsSet() || fSource->WebAppRepositorySourceCode() != value) {
_InitFromSource();
fWebAppRepositorySourceCode = value;
}
return *this;
}
+42 -27
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef DEPOT_INFO_H
@@ -14,48 +14,36 @@
#include "PackageInfo.h"
class DepotInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
DepotInfoBuilder class as a builder-constructor.
*/
class DepotInfo : public BReferenceable {
friend class DepotInfoBuilder;
public:
DepotInfo();
DepotInfo(const BString& name);
DepotInfo(const DepotInfo& other);
DepotInfo& operator=(const DepotInfo& other);
bool operator==(const DepotInfo& other) const;
bool operator!=(const DepotInfo& other) const;
const BString& Name() const
{ return fName; }
const BString& Name() const;
const BString& Identifier() const;
const BString& WebAppRepositoryCode() const;
const BString& WebAppRepositorySourceCode() const;
private:
void SetIdentifier(const BString& value);
const BString& Identifier() const
{ return fIdentifier; }
int32 CountPackages() const;
PackageInfoRef PackageAtIndex(int32 index);
void AddPackage(PackageInfoRef& package);
PackageInfoRef PackageByName(const BString& packageName);
bool HasPackage(const BString& packageName);
void SyncPackagesFromDepot(
const BReference<DepotInfo>& other);
bool HasAnyProminentPackages() const;
void SetWebAppRepositoryCode(const BString& code);
const BString& WebAppRepositoryCode() const
{ return fWebAppRepositoryCode; }
void SetWebAppRepositorySourceCode(
const BString& code);
const BString& WebAppRepositorySourceCode() const
{ return fWebAppRepositorySourceCode; }
void SetWebAppRepositorySourceCode(const BString& code);
private:
BString fName;
BString fIdentifier;
std::vector<PackageInfoRef>
fPackages;
BString fWebAppRepositoryCode;
BString fWebAppRepositorySourceCode;
};
@@ -64,4 +52,31 @@ private:
typedef BReference<DepotInfo> DepotInfoRef;
class DepotInfoBuilder
{
public:
DepotInfoBuilder();
DepotInfoBuilder(const DepotInfoRef& value);
virtual ~DepotInfoBuilder();
DepotInfoRef BuildRef() const;
DepotInfoBuilder& WithName(const BString& value);
DepotInfoBuilder& WithIdentifier(const BString& value);
DepotInfoBuilder& WithWebAppRepositoryCode(const BString& value);
DepotInfoBuilder& WithWebAppRepositorySourceCode(const BString& value);
private:
void _InitFromSource();
void _Init(const DepotInfo* value);
private:
DepotInfoRef fSource;
BString fName;
BString fIdentifier;
BString fWebAppRepositoryCode;
BString fWebAppRepositorySourceCode;
};
#endif // DEPOT_INFO_H
+27 -7
View File
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -13,11 +13,10 @@
#include "StringUtils.h"
/*! serverName is the name of the language on the server.
/*! @param serverName is the name of the language on the server.
*/
Language::Language(const char* language, const BString& serverName,
bool isPopular)
Language::Language(const char* language, const BString& serverName, bool isPopular)
:
BLanguage(language),
fServerName(serverName),
@@ -35,6 +34,27 @@ Language::Language(const Language& other)
}
bool
Language::operator==(const Language& other) const
{
return Compare(other) == 0;
}
bool
Language::operator!=(const Language& other) const
{
return !(*this == other);
}
bool
Language::operator<(const Language& other) const
{
return Compare(other) < 0;
}
status_t
Language::GetName(BString& name, const BLanguage* displayLanguage) const
{
@@ -60,9 +80,9 @@ Language::Compare(const Language& other) const
bool
IsLanguageBefore(const LanguageRef& l1, const LanguageRef& l2)
IsLanguageRefLess(const LanguageRef& l1, const LanguageRef& l2)
{
if (!l1.IsSet() || !l2.IsSet())
HDFATAL("unexpected NULL reference in a referencable");
return l1.Get()->Compare(*(l2.Get())) < 0;
debugger("illegal state in language less");
return *(l1.Get()) < *(l2.Get());
}
+10 -2
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LANGUAGE_H
@@ -14,6 +14,10 @@
/*! This class represents a language that is supported by the Haiku
Depot Server system. This may differ from the set of languages
that are supported in the platform itself.
No builder is provided for this class because it sub-classes from
a Haiku type. Instead always create new instances with the constructor
and do not use any mutating functions offered by the superclass.
*/
class Language : public BReferenceable, public BLanguage {
@@ -23,6 +27,10 @@ public:
bool isPopular);
Language(const Language& other);
bool operator<(const Language& other) const;
bool operator==(const Language& other) const;
bool operator!=(const Language& other) const;
status_t GetName(BString& name,
const BLanguage* displayLanguage = NULL
) const;
@@ -40,7 +48,7 @@ private:
typedef BReference<Language> LanguageRef;
extern bool IsLanguageBefore(const LanguageRef& l1, const LanguageRef& l2);
extern bool IsLanguageRefLess(const LanguageRef& l1, const LanguageRef& l2);
#endif // LANGUAGE_H
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -41,12 +41,10 @@ PackageCategory::PackageCategory(const PackageCategory& other)
}
PackageCategory&
PackageCategory::operator=(const PackageCategory& other)
bool
PackageCategory::operator<(const PackageCategory& other) const
{
fCode = other.fCode;
fName = other.fName;
return *this;
return Compare(other) < 0;
}
@@ -68,18 +66,17 @@ int
PackageCategory::Compare(const PackageCategory& other) const
{
BCollator* collator = LocaleUtils::GetSharedCollator();
int32 result = collator->Compare(Name().String(),
other.Name().String());
int32 result = collator->Compare(Name().String(), other.Name().String());
if (result == 0)
result = Code().Compare(other.Code());
return result;
}
bool IsPackageCategoryBefore(const CategoryRef& c1,
const CategoryRef& c2)
bool
IsPackageCategoryRefLess(const CategoryRef& c1, const CategoryRef& c2)
{
if (!c1.IsSet() || !c2.IsSet())
HDFATAL("unexpected NULL reference in a referencable");
return c1->Compare(*c2) < 0;
HDFATAL("illegal state in package category less");
return *(c1.Get()) < *(c2.Get());
}
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_CATEGORY_H
@@ -11,6 +11,13 @@
#include <String.h>
/*! This class represents a category into which a package may be assigned. The
`code` corresponds to a code on the server.
No builder is provided for this class; always create new instances using
the constructor.
*/
class PackageCategory : public BReferenceable {
public:
PackageCategory();
@@ -18,7 +25,7 @@ public:
const BString& name);
PackageCategory(const PackageCategory& other);
PackageCategory& operator=(const PackageCategory& other);
bool operator<(const PackageCategory& other) const;
bool operator==(const PackageCategory& other) const;
bool operator!=(const PackageCategory& other) const;
@@ -38,7 +45,7 @@ private:
typedef BReference<PackageCategory> CategoryRef;
extern bool IsPackageCategoryBefore(const CategoryRef& c1, const CategoryRef& c2);
extern bool IsPackageCategoryRefLess(const CategoryRef& c1, const CategoryRef& c2);
#endif // PACKAGE_CATEGORY_H
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -14,7 +14,7 @@
PackageClassificationInfo::PackageClassificationInfo()
:
fCategories(),
fProminence(-1L),
fProminence(PROMINANCE_ORDERING_NONE),
fIsNativeDesktop(false)
{
}
@@ -29,6 +29,20 @@ PackageClassificationInfo::PackageClassificationInfo(const PackageClassification
}
uint32
PackageClassificationInfo::Prominence() const
{
return fProminence;
}
bool
PackageClassificationInfo::HasProminence() const
{
return fProminence != 0;
}
int32
PackageClassificationInfo::CountCategories() const
{
@@ -36,26 +50,18 @@ PackageClassificationInfo::CountCategories() const
}
CategoryRef
const CategoryRef
PackageClassificationInfo::CategoryAtIndex(int32 index) const
{
return fCategories[index];
}
void
PackageClassificationInfo::ClearCategories()
{
if (!fCategories.empty())
fCategories.clear();
}
bool
PackageClassificationInfo::AddCategory(const CategoryRef& category)
{
std::vector<CategoryRef>::const_iterator itInsertionPt = std::lower_bound(fCategories.begin(),
fCategories.end(), category, &IsPackageCategoryBefore);
fCategories.end(), category, &IsPackageCategoryRefLess);
if (itInsertionPt == fCategories.end()) {
fCategories.push_back(category);
@@ -131,3 +137,104 @@ PackageClassificationInfo::operator!=(const PackageClassificationInfo& other) co
{
return !(*this == other);
}
// #pragma mark - PackageClassificationInfoBuilder
PackageClassificationInfoBuilder::PackageClassificationInfoBuilder()
:
fCategories(),
fProminence(-1L),
fIsNativeDesktop(false)
{
}
PackageClassificationInfoBuilder::PackageClassificationInfoBuilder(
const PackageClassificationInfoRef& value)
:
fCategories(),
fProminence(PROMINANCE_ORDERING_NONE),
fIsNativeDesktop(false)
{
fSource = value;
}
PackageClassificationInfoBuilder::~PackageClassificationInfoBuilder()
{
}
void
PackageClassificationInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageClassificationInfoBuilder::_Init(const PackageClassificationInfo* value)
{
for (int32 i = value->CountCategories() - 1; i >= 0; i--)
fCategories.push_back(value->CategoryAtIndex(i));
fProminence = value->Prominence();
fIsNativeDesktop = value->IsNativeDesktop();
}
PackageClassificationInfoRef
PackageClassificationInfoBuilder::BuildRef() const
{
if (fSource.IsSet())
return fSource;
PackageClassificationInfo* classificationInfo = new PackageClassificationInfo();
classificationInfo->SetProminence(fProminence);
classificationInfo->SetIsNativeDesktop(fIsNativeDesktop);
std::vector<CategoryRef>::const_iterator it;
for (it = fCategories.begin(); it != fCategories.end(); it++)
classificationInfo->AddCategory(*it);
return PackageClassificationInfoRef(classificationInfo, true);
}
PackageClassificationInfoBuilder&
PackageClassificationInfoBuilder::WithProminence(uint32 prominence)
{
if (!fSource.IsSet() || fSource->Prominence() != prominence) {
_InitFromSource();
fProminence = prominence;
}
return *this;
}
PackageClassificationInfoBuilder&
PackageClassificationInfoBuilder::WithIsNativeDesktop(bool value)
{
if (!fSource.IsSet() || fSource->IsNativeDesktop() != value) {
_InitFromSource();
fIsNativeDesktop = value;
}
return *this;
}
PackageClassificationInfoBuilder&
PackageClassificationInfoBuilder::AddCategory(const CategoryRef& category)
{
if (!fSource.IsSet() || fSource->HasCategoryByCode(category->Code())) {
_InitFromSource();
fCategories.push_back(category);
}
return *this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_CLASSIFICATION_INFO_H
@@ -13,8 +13,16 @@
#include "PackageCategory.h"
class PackageClassificationInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageClassificationInfoBuilder class as a builder-constructor.
*/
class PackageClassificationInfo : public BReferenceable
{
friend class PackageClassificationInfoBuilder;
public:
PackageClassificationInfo();
PackageClassificationInfo(const PackageClassificationInfo& other);
@@ -22,20 +30,20 @@ public:
bool operator==(const PackageClassificationInfo& other) const;
bool operator!=(const PackageClassificationInfo& other) const;
void SetProminence(uint32 prominence);
uint32 Prominence() const
{ return fProminence; }
bool HasProminence() const
{ return fProminence != 0; }
uint32 Prominence() const;
bool HasProminence() const;
bool IsProminent() const;
void ClearCategories();
bool AddCategory(const CategoryRef& category);
int32 CountCategories() const;
CategoryRef CategoryAtIndex(int32 index) const;
const CategoryRef CategoryAtIndex(int32 index) const;
bool HasCategoryByCode(const BString& code) const;
bool IsNativeDesktop() const;
private:
void SetProminence(uint32 prominence);
void ClearCategories();
bool AddCategory(const CategoryRef& category);
void SetIsNativeDesktop(bool value);
private:
@@ -49,4 +57,37 @@ private:
typedef BReference<PackageClassificationInfo> PackageClassificationInfoRef;
class PackageClassificationInfoBuilder
{
public:
PackageClassificationInfoBuilder();
PackageClassificationInfoBuilder(
const PackageClassificationInfoRef& value);
virtual ~PackageClassificationInfoBuilder();
PackageClassificationInfoRef
BuildRef() const;
PackageClassificationInfoBuilder&
WithProminence(uint32 prominence);
PackageClassificationInfoBuilder&
WithIsNativeDesktop(bool value);
PackageClassificationInfoBuilder&
AddCategory(const CategoryRef& category);
private:
void _InitFromSource();
void _Init(const PackageClassificationInfo* value);
private:
PackageClassificationInfoRef
fSource;
std::vector<CategoryRef>
fCategories;
uint32 fProminence;
bool fIsNativeDesktop;
};
#endif // PACKAGE_CLASSIFICATION_INFO_H
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -34,18 +34,6 @@ PackageCoreInfo::~PackageCoreInfo()
}
PackageCoreInfo&
PackageCoreInfo::operator=(const PackageCoreInfo& other)
{
fVersion = other.fVersion;
fPublisher = other.fPublisher;
fArchitecture = other.fArchitecture;
fDepotName = other.fDepotName;
return *this;
}
bool
PackageCoreInfo::operator==(const PackageCoreInfo& other) const
{
@@ -63,6 +51,34 @@ PackageCoreInfo::operator!=(const PackageCoreInfo& other) const
}
const PackageVersionRef&
PackageCoreInfo::Version() const
{
return fVersion;
}
const PackagePublisherInfoRef&
PackageCoreInfo::Publisher() const
{
return fPublisher;
}
const BString
PackageCoreInfo::Architecture() const
{
return fArchitecture;
}
const BString&
PackageCoreInfo::DepotName() const
{
return fDepotName;
}
void
PackageCoreInfo::SetVersion(PackageVersionRef value)
{
@@ -89,3 +105,112 @@ PackageCoreInfo::SetDepotName(const BString& value)
{
fDepotName = value;
}
// #pragma mark - PackageCoreInfoBuilder
PackageCoreInfoBuilder::PackageCoreInfoBuilder()
:
fVersion(),
fPublisher(),
fArchitecture(),
fDepotName()
{
}
PackageCoreInfoBuilder::PackageCoreInfoBuilder(const PackageCoreInfoRef& other)
:
fVersion(),
fPublisher(),
fArchitecture(),
fDepotName()
{
fSource = other;
}
PackageCoreInfoBuilder::~PackageCoreInfoBuilder()
{
}
void
PackageCoreInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageCoreInfoBuilder::_Init(const PackageCoreInfo* other)
{
fVersion = other->Version();
fPublisher = other->Publisher();
fArchitecture = other->Architecture();
fDepotName = other->DepotName();
}
PackageCoreInfoRef
PackageCoreInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageCoreInfo* coreInfo = new PackageCoreInfo();
coreInfo->SetVersion(fVersion);
coreInfo->SetPublisher(fPublisher);
coreInfo->SetArchitecture(fArchitecture);
coreInfo->SetDepotName(fDepotName);
return PackageCoreInfoRef(coreInfo, true);
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithVersion(PackageVersionRef value)
{
if (!fSource.IsSet() || fSource->Version() != value) {
_InitFromSource();
fVersion = value;
}
return *this;
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithPublisher(PackagePublisherInfoRef value)
{
if (!fSource.IsSet() || fSource->Publisher() != value) {
_InitFromSource();
fPublisher = value;
}
return *this;
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithArchitecture(const BString& value)
{
if (!fSource.IsSet() || fSource->Architecture() != value) {
_InitFromSource();
fArchitecture = value;
}
return *this;
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithDepotName(const BString& value)
{
if (!fSource.IsSet() || fSource->DepotName() != value) {
_InitFromSource();
fDepotName = value;
}
return *this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_CORE_INFO_H
@@ -15,38 +15,79 @@
using BPackageKit::BPackageVersion;
class PackageCoreInfoBuilder;
/*! This class models the core data for the package.
Instances of this class should not be created directly; instead use the
PackageCoreInfoBuilder class as a builder-constructor.
*/
class PackageCoreInfo : public BReferenceable
{
friend class PackageCoreInfoBuilder;
public:
PackageCoreInfo();
PackageCoreInfo(const PackageCoreInfo& other);
virtual ~PackageCoreInfo();
PackageCoreInfo& operator=(const PackageCoreInfo& other);
bool operator==(const PackageCoreInfo& other) const;
bool operator!=(const PackageCoreInfo& other) const;
const PackageVersionRef&
Version() const
{ return fVersion; }
void SetVersion(PackageVersionRef value);
Version() const;
const PackagePublisherInfoRef&
Publisher() const
{ return fPublisher; }
void SetPublisher(PackagePublisherInfoRef value);
const BString Architecture() const
{ return fArchitecture; }
void SetArchitecture(const BString& value);
void SetDepotName(const BString& value);
const BString& DepotName() const
{ return fDepotName; }
Publisher() const;
const BString Architecture() const;
const BString& DepotName() const;
private:
void SetVersion(PackageVersionRef value);
void SetPublisher(PackagePublisherInfoRef value);
void SetArchitecture(const BString& value);
void SetDepotName(const BString& value);
private:
PackageVersionRef
fVersion;
PackagePublisherInfoRef
fPublisher;
BString fArchitecture;
BString fDepotName;
// The name and not the identifier are used here because the
// identifier is not readily available in all API situations.
};
typedef BReference<PackageCoreInfo> PackageCoreInfoRef;
class PackageCoreInfoBuilder
{
public:
PackageCoreInfoBuilder();
PackageCoreInfoBuilder(const PackageCoreInfoRef& other);
virtual ~PackageCoreInfoBuilder();
PackageCoreInfoRef BuildRef();
PackageCoreInfoBuilder&
WithVersion(PackageVersionRef value);
PackageCoreInfoBuilder&
WithPublisher(PackagePublisherInfoRef value);
PackageCoreInfoBuilder&
WithArchitecture(const BString& value);
PackageCoreInfoBuilder&
WithDepotName(const BString& value);
private:
void _InitFromSource();
void _Init(const PackageCoreInfo* value);
private:
PackageCoreInfoRef
fSource;
PackageVersionRef
fVersion;
PackagePublisherInfoRef
@@ -56,7 +97,4 @@ private:
};
typedef BReference<PackageCoreInfo> PackageCoreInfoRef;
#endif // PACKAGE_CORE_INFO_H
@@ -1,15 +1,16 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2014, Axel Dörfler <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Note that this file included code earlier from `Model.cpp` and
* copyrights have been latterly been carried across in 2024.
*/
#include "PackageFilter.h"
#include "Logger.h"
#include "PackageFilterSpecification.h"
#include "PackageUtils.h"
@@ -52,6 +53,20 @@ AndFilter::AcceptsPackage(const PackageInfoRef& package) const
}
class FalseFilter : public PackageFilter
{
public:
FalseFilter()
{
}
virtual bool AcceptsPackage(const PackageInfoRef& package) const
{
return false;
}
};
class StateFilter : public PackageFilter
{
public:
@@ -138,16 +153,14 @@ public:
};
class DevelopmentFilter : public PackageFilter
{
class DevelopmentFilter : public PackageFilter {
public:
virtual bool AcceptsPackage(const PackageInfoRef& package) const
{
if (!package.IsSet())
return false;
const BString& packageName = package->Name();
return packageName.EndsWith("_devel")
|| packageName.EndsWith("_debuginfo");
return packageName.EndsWith("_devel") || packageName.EndsWith("_debuginfo");
}
};
@@ -179,8 +192,7 @@ public:
// Every search term must be found in one of the package texts
for (int32 i = fSearchTerms.CountStrings() - 1; i >= 0; i--) {
const BString& term = fSearchTerms.StringAt(i);
if (package->Name().FindFirst(term) < 0
&& !_AcceptsPackageFromPublisher(package, term)
if (package->Name().FindFirst(term) < 0 && !_AcceptsPackageFromPublisher(package, term)
&& !_AcceptsPackageFromLocalizedText(package, term)) {
return false;
}
@@ -199,15 +211,15 @@ public:
searchTerms.Append(" ");
searchTerms.Append(term);
}
return searchTerms;
return searchTerms;
}
private:
bool _TextContains(BString text, const BString& string) const
{
bool _TextContains(BString text, const BString& string) const
{
int32 index = text.IFindFirst(string);
return index >= 0;
}
return index >= 0;
}
bool _AcceptsPackageFromPublisher(const PackageInfoRef& package,
const BString& searchTerm) const
@@ -228,12 +240,12 @@ private:
return false;
return _TextContains(localizedText->Title(), searchTerm)
|| _TextContains(localizedText->Summary(), searchTerm)
|| _TextContains(localizedText->Description(), searchTerm);
|| _TextContains(localizedText->Summary(), searchTerm)
|| _TextContains(localizedText->Description(), searchTerm);
}
private:
BStringList fSearchTerms;
BStringList fSearchTerms;
};
@@ -280,3 +292,43 @@ PackageFilterFactory::CreateDevelopmentFilter()
{
return PackageFilterRef(new DevelopmentFilter(), true);
}
/*static*/ PackageFilterRef
PackageFilterFactory::CreateFalseFilter()
{
return PackageFilterRef(new FalseFilter(), true);
}
/*static*/ PackageFilterRef
PackageFilterFactory::CreateFilter(const PackageFilterSpecificationRef specification)
{
if (!specification.IsSet())
return CreateFalseFilter();
AndFilter* andFilter = new AndFilter();
if (!specification->SearchTerms().IsEmpty())
andFilter->AddFilter(CreateSearchTermsFilter(specification->SearchTerms()));
if (!specification->DepotName().IsEmpty())
andFilter->AddFilter(CreateDepotFilter(specification->DepotName()));
if (!specification->Category().IsEmpty())
andFilter->AddFilter(CreateCategoryFilter(specification->Category()));
if (!specification->ShowAvailablePackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateStateFilter(NONE)), true));
if (!specification->ShowInstalledPackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateStateFilter(ACTIVATED)), true));
if (!specification->ShowSourcePackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateSourceFilter()), true));
if (!specification->ShowDevelopPackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateDevelopmentFilter()), true));
return PackageFilterRef(andFilter, true);
}
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Note that this file included code earlier from `Model.cpp` and
@@ -14,9 +14,13 @@
#include <Referenceable.h>
#include <String.h>
#include "PackageFilterSpecification.h"
#include "PackageInfo.h"
/*! See the class PackageFilterFactory for static factory methods that will
create concrete instances of this abstract superclass.
*/
class PackageFilter : public BReferenceable
{
public:
@@ -61,6 +65,8 @@ public:
static PackageFilterRef CreateStateFilter(PackageState state);
static PackageFilterRef CreateSourceFilter();
static PackageFilterRef CreateDevelopmentFilter();
static PackageFilterRef CreateFalseFilter();
static PackageFilterRef CreateFilter(const PackageFilterSpecificationRef specification);
};
@@ -1,180 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageFilterModel.h"
PackageFilterModel::PackageFilterModel()
:
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
fFilter = _CreateFilter();
}
PackageFilterModel::~PackageFilterModel()
{
}
BString
PackageFilterModel::SearchTerms() const
{
return fSearchTerms;
}
BString
PackageFilterModel::DepotName() const
{
return fDepotName;
}
BString
PackageFilterModel::Category() const
{
return fCategory;
}
bool
PackageFilterModel::ShowAvailablePackages() const
{
return fShowAvailablePackages;
}
bool
PackageFilterModel::ShowInstalledPackages() const
{
return fShowInstalledPackages;
}
bool
PackageFilterModel::ShowSourcePackages() const
{
return fShowSourcePackages;
}
bool
PackageFilterModel::ShowDevelopPackages() const
{
return fShowDevelopPackages;
}
void
PackageFilterModel::SetSearchTerms(BString value)
{
if (fSearchTerms != value) {
fSearchTerms = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetDepotName(BString value)
{
if (fDepotName != value) {
fDepotName = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetCategory(BString value)
{
if (fCategory != value) {
fCategory = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowAvailablePackages(bool value)
{
if (fShowAvailablePackages != value) {
fShowAvailablePackages = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowInstalledPackages(bool value)
{
if (fShowInstalledPackages != value) {
fShowInstalledPackages = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowSourcePackages(bool value)
{
if (fShowSourcePackages != value) {
fShowSourcePackages = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowDevelopPackages(bool value)
{
if (fShowDevelopPackages != value) {
fShowDevelopPackages = value;
fFilter = _CreateFilter();
}
}
PackageFilterRef
PackageFilterModel::Filter()
{
return fFilter;
}
PackageFilterRef
PackageFilterModel::_CreateFilter() const
{
AndFilter* andFilter = new AndFilter();
if (!fSearchTerms.IsEmpty())
andFilter->AddFilter(PackageFilterFactory::CreateSearchTermsFilter(fSearchTerms));
if (!fDepotName.IsEmpty())
andFilter->AddFilter(PackageFilterFactory::CreateDepotFilter(fDepotName));
if (!fCategory.IsEmpty())
andFilter->AddFilter(PackageFilterFactory::CreateCategoryFilter(fCategory));
if (!fShowAvailablePackages) {
andFilter->AddFilter(
PackageFilterRef(new NotFilter(PackageFilterFactory::CreateStateFilter(NONE)), true));
}
if (!fShowInstalledPackages) {
andFilter->AddFilter(PackageFilterRef(
new NotFilter(PackageFilterFactory::CreateStateFilter(ACTIVATED)), true));
}
if (!fShowSourcePackages) {
andFilter->AddFilter(
PackageFilterRef(new NotFilter(PackageFilterFactory::CreateSourceFilter()), true));
}
if (!fShowDevelopPackages) {
andFilter->AddFilter(
PackageFilterRef(new NotFilter(PackageFilterFactory::CreateDevelopmentFilter()), true));
}
return PackageFilterRef(andFilter, true);
}
@@ -1,53 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_FILTER_MODEL_H
#define PACKAGE_FILTER_MODEL_H
#include <String.h>
#include "PackageFilter.h"
class PackageFilterModel
{
public:
PackageFilterModel();
virtual ~PackageFilterModel();
BString SearchTerms() const;
BString DepotName() const;
BString Category() const;
bool ShowAvailablePackages() const;
bool ShowInstalledPackages() const;
bool ShowSourcePackages() const;
bool ShowDevelopPackages() const;
void SetSearchTerms(BString value);
void SetDepotName(BString value);
void SetCategory(BString value);
void SetShowAvailablePackages(bool value);
void SetShowInstalledPackages(bool value);
void SetShowSourcePackages(bool value);
void SetShowDevelopPackages(bool value);
PackageFilterRef Filter();
private:
PackageFilterRef _CreateFilter() const;
private:
BString fSearchTerms;
BString fDepotName;
BString fCategory;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;
PackageFilterRef fFilter;
};
#endif // PACKAGE_FILTER_MODEL_H
@@ -0,0 +1,292 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageFilterSpecification.h"
PackageFilterSpecification::PackageFilterSpecification()
:
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
}
PackageFilterSpecification::~PackageFilterSpecification()
{
}
bool
PackageFilterSpecification::operator==(const PackageFilterSpecification& other) const
{
return fSearchTerms == other.SearchTerms() && fDepotName == other.DepotName()
&& fCategory == other.Category() && fShowAvailablePackages == other.ShowAvailablePackages()
&& fShowInstalledPackages == other.ShowInstalledPackages()
&& fShowDevelopPackages == other.ShowDevelopPackages();
}
bool
PackageFilterSpecification::operator!=(const PackageFilterSpecification& other) const
{
return !(*this == other);
}
BString
PackageFilterSpecification::SearchTerms() const
{
return fSearchTerms;
}
BString
PackageFilterSpecification::DepotName() const
{
return fDepotName;
}
BString
PackageFilterSpecification::Category() const
{
return fCategory;
}
bool
PackageFilterSpecification::ShowAvailablePackages() const
{
return fShowAvailablePackages;
}
bool
PackageFilterSpecification::ShowInstalledPackages() const
{
return fShowInstalledPackages;
}
bool
PackageFilterSpecification::ShowSourcePackages() const
{
return fShowSourcePackages;
}
bool
PackageFilterSpecification::ShowDevelopPackages() const
{
return fShowDevelopPackages;
}
void
PackageFilterSpecification::SetSearchTerms(BString value)
{
fSearchTerms = value;
}
void
PackageFilterSpecification::SetDepotName(BString value)
{
fDepotName = value;
}
void
PackageFilterSpecification::SetCategory(BString value)
{
fCategory = value;
}
void
PackageFilterSpecification::SetShowAvailablePackages(bool value)
{
fShowAvailablePackages = value;
}
void
PackageFilterSpecification::SetShowInstalledPackages(bool value)
{
fShowInstalledPackages = value;
}
void
PackageFilterSpecification::SetShowSourcePackages(bool value)
{
fShowSourcePackages = value;
}
void
PackageFilterSpecification::SetShowDevelopPackages(bool value)
{
fShowDevelopPackages = value;
}
// #pragma mark - PackageClassificationInfoBuilder
PackageFilterSpecificationBuilder::PackageFilterSpecificationBuilder()
:
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
}
PackageFilterSpecificationBuilder::PackageFilterSpecificationBuilder(
const PackageFilterSpecificationRef& value)
:
fSource(value),
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
}
PackageFilterSpecificationBuilder::~PackageFilterSpecificationBuilder()
{
}
void
PackageFilterSpecificationBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource);
fSource.Unset();
}
}
void
PackageFilterSpecificationBuilder::_Init(const PackageFilterSpecification* value)
{
fSearchTerms = value->SearchTerms();
fDepotName = value->DepotName();
fCategory = value->Category();
fShowAvailablePackages = value->ShowAvailablePackages();
fShowInstalledPackages = value->ShowInstalledPackages();
fShowSourcePackages = value->ShowSourcePackages();
fShowDevelopPackages = value->ShowDevelopPackages();
}
PackageFilterSpecificationRef
PackageFilterSpecificationBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageFilterSpecification* info = new PackageFilterSpecification();
info->SetSearchTerms(fSearchTerms);
info->SetDepotName(fDepotName);
info->SetCategory(fCategory);
info->SetShowAvailablePackages(fShowAvailablePackages);
info->SetShowInstalledPackages(fShowInstalledPackages);
info->SetShowSourcePackages(fShowSourcePackages);
info->SetShowDevelopPackages(fShowDevelopPackages);
return PackageFilterSpecificationRef(info, true);
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithSearchTerms(BString value)
{
if (!fSource.IsSet() || fSource->SearchTerms() != value) {
_InitFromSource();
fSearchTerms = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithDepotName(BString value)
{
if (!fSource.IsSet() || fSource->DepotName() != value) {
_InitFromSource();
fDepotName = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithCategory(BString value)
{
if (!fSource.IsSet() || fSource->Category() != value) {
_InitFromSource();
fCategory = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowAvailablePackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowAvailablePackages() != value) {
_InitFromSource();
fShowAvailablePackages = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowInstalledPackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowInstalledPackages() != value) {
_InitFromSource();
fShowInstalledPackages = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowSourcePackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowSourcePackages() != value) {
_InitFromSource();
fShowSourcePackages = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowDevelopPackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowDevelopPackages() != value) {
_InitFromSource();
fShowDevelopPackages = value;
}
return *this;
}
@@ -0,0 +1,100 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_FILTER_SPECIFICATION_H
#define PACKAGE_FILTER_SPECIFICATION_H
#include <Referenceable.h>
#include <String.h>
/*! Instances of this class should not be created directly; instead use the
PackageFilterSpecificationBuilder class as a builder-constructor.
*/
class PackageFilterSpecification : public BReferenceable
{
friend class PackageFilterSpecificationBuilder;
public:
PackageFilterSpecification();
virtual ~PackageFilterSpecification();
bool operator==(const PackageFilterSpecification& other) const;
bool operator!=(const PackageFilterSpecification& other) const;
BString SearchTerms() const;
BString DepotName() const;
BString Category() const;
bool ShowAvailablePackages() const;
bool ShowInstalledPackages() const;
bool ShowSourcePackages() const;
bool ShowDevelopPackages() const;
private:
void SetSearchTerms(BString value);
void SetDepotName(BString value);
void SetCategory(BString value);
void SetShowAvailablePackages(bool value);
void SetShowInstalledPackages(bool value);
void SetShowSourcePackages(bool value);
void SetShowDevelopPackages(bool value);
private:
BString fSearchTerms;
BString fDepotName;
BString fCategory;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;
};
typedef BReference<PackageFilterSpecification> PackageFilterSpecificationRef;
class PackageFilterSpecificationBuilder
{
public:
PackageFilterSpecificationBuilder();
PackageFilterSpecificationBuilder(
const PackageFilterSpecificationRef& other);
virtual ~PackageFilterSpecificationBuilder();
PackageFilterSpecificationRef
BuildRef();
PackageFilterSpecificationBuilder
WithSearchTerms(BString value);
PackageFilterSpecificationBuilder
WithDepotName(BString value);
PackageFilterSpecificationBuilder
WithCategory(BString value);
PackageFilterSpecificationBuilder
WithShowAvailablePackages(bool value);
PackageFilterSpecificationBuilder
WithShowInstalledPackages(bool value);
PackageFilterSpecificationBuilder
WithShowSourcePackages(bool value);
PackageFilterSpecificationBuilder
WithShowDevelopPackages(bool value);
private:
void _InitFromSource();
void _Init(const PackageFilterSpecification* value);
private:
PackageFilterSpecificationRef
fSource;
BString fSearchTerms;
BString fDepotName;
BString fCategory;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;
};
#endif // PACKAGE_FILTER_SPECIFICATION_H
+139 -150
View File
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -15,6 +15,7 @@
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "PackageInfoListener.h"
// #pragma mark - PackageInfo
@@ -29,66 +30,11 @@ PackageInfo::PackageInfo()
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fLocalInfo(),
fListeners(),
fIsCollatingChanges(false),
fCollatedChanges(0)
fLocalInfo()
{
}
PackageInfo::PackageInfo(const BPackageInfo& info)
:
fName(info.Name()),
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fListeners(),
fIsCollatingChanges(false),
fCollatedChanges(0)
{
// TODO; factor this material out.
BString publisherURL;
if (info.URLList().CountStrings() > 0)
publisherURL = info.URLList().StringAt(0);
BString publisherName = info.Vendor();
const BStringList& copyrightList = info.CopyrightList();
if (!copyrightList.IsEmpty()) {
publisherName = "";
for (int32 i = 0; i < copyrightList.CountStrings(); i++) {
if (!publisherName.IsEmpty())
publisherName << ", ";
publisherName << copyrightList.StringAt(i);
}
}
if (!publisherName.IsEmpty())
publisherName.Prepend("© ");
fCoreInfo = PackageCoreInfoRef(new PackageCoreInfo(), true);
fCoreInfo->SetArchitecture(info.ArchitectureName());
fCoreInfo->SetVersion(new PackageVersion(info.Version()));
fCoreInfo->SetPublisher(
PackagePublisherInfoRef(new PackagePublisherInfo(publisherName, publisherURL), true));
fLocalizedText = PackageLocalizedTextRef(new PackageLocalizedText(), true);
fLocalizedText->SetTitle(info.Name());
fLocalizedText->SetSummary(info.Summary());
fLocalizedText->SetDescription(info.Description());
// TODO: Retrieve local file size
fLocalInfo = PackageLocalInfoRef(new PackageLocalInfo(), true);
fLocalInfo->SetFlags(info.Flags());
fLocalInfo->SetFileName(info.FileName());
}
PackageInfo::PackageInfo(const PackageInfo& other)
:
fName(other.fName),
@@ -98,30 +44,11 @@ PackageInfo::PackageInfo(const PackageInfo& other)
fClassificationInfo(other.fClassificationInfo),
fScreenshotInfo(other.fScreenshotInfo),
fUserRatingInfo(other.fUserRatingInfo),
fLocalInfo(other.fLocalInfo),
fListeners(),
fIsCollatingChanges(false),
fCollatedChanges(0)
fLocalInfo(other.fLocalInfo)
{
}
PackageInfo&
PackageInfo::operator=(const PackageInfo& other)
{
fName = other.fName;
fCoreInfo = other.fCoreInfo;
fLocalizedText = other.fLocalizedText;
fClassificationInfo = other.fClassificationInfo;
fScreenshotInfo = other.fScreenshotInfo;
fUserRatingInfo = other.fUserRatingInfo;
fLocalInfo = other.fLocalInfo;
return *this;
}
bool
PackageInfo::operator==(const PackageInfo& other) const
{
@@ -142,8 +69,43 @@ PackageInfo::operator!=(const PackageInfo& other) const
}
const BString&
PackageInfo::Name() const
{
return fName;
}
const PackageCoreInfoRef
PackageInfo::CoreInfo() const
{
return fCoreInfo;
}
const PackageClassificationInfoRef
PackageInfo::PackageClassificationInfo() const
{
return fClassificationInfo;
}
const PackageScreenshotInfoRef
PackageInfo::ScreenshotInfo() const
{
return fScreenshotInfo;
}
void
PackageInfo::SetName(const BString& value)
{
fName = value;
}
uint32
PackageInfo::DiffMask(const PackageInfo& other) const
PackageInfo::ChangeMask(const PackageInfo& other) const
{
uint32 result = 0;
@@ -167,14 +129,11 @@ PackageInfo::DiffMask(const PackageInfo& other) const
void
PackageInfo::SetCoreInfo(PackageCoreInfoRef value)
{
if (value != fCoreInfo) {
fCoreInfo = value;
_NotifyListeners(PKG_CHANGED_CORE_INFO);
}
fCoreInfo = value;
}
PackageLocalizedTextRef
const PackageLocalizedTextRef
PackageInfo::LocalizedText() const
{
return fLocalizedText;
@@ -184,16 +143,11 @@ PackageInfo::LocalizedText() const
void
PackageInfo::SetLocalizedText(PackageLocalizedTextRef value)
{
if (fLocalizedText != value) {
fLocalizedText = value;
_NotifyListeners(PKG_CHANGED_LOCALIZED_TEXT);
// TODO; separate out these later - they are bundled for now to keep the existing
// logic working.
}
fLocalizedText = value;
}
UserRatingInfoRef
const PackageUserRatingInfoRef
PackageInfo::UserRatingInfo() const
{
return fUserRatingInfo;
@@ -201,16 +155,13 @@ PackageInfo::UserRatingInfo() const
void
PackageInfo::SetUserRatingInfo(UserRatingInfoRef value)
PackageInfo::SetUserRatingInfo(PackageUserRatingInfoRef& value)
{
if (fUserRatingInfo != value) {
fUserRatingInfo = value;
_NotifyListeners(PKG_CHANGED_RATINGS);
}
fUserRatingInfo = value;
}
PackageLocalInfoRef
const PackageLocalInfoRef
PackageInfo::LocalInfo() const
{
return fLocalInfo;
@@ -220,99 +171,137 @@ PackageInfo::LocalInfo() const
void
PackageInfo::SetLocalInfo(PackageLocalInfoRef& localInfo)
{
if (fLocalInfo != localInfo) {
fLocalInfo = localInfo;
_NotifyListeners(PKG_CHANGED_LOCAL_INFO);
}
fLocalInfo = localInfo;
}
void
PackageInfo::SetPackageClassificationInfo(PackageClassificationInfoRef value)
{
if (value != fClassificationInfo) {
fClassificationInfo = value;
_NotifyListeners(PKG_CHANGED_CLASSIFICATION);
}
fClassificationInfo = value;
}
void
PackageInfo::SetScreenshotInfo(PackageScreenshotInfoRef value)
{
if (value != fScreenshotInfo) {
fScreenshotInfo = value;
_NotifyListeners(PKG_CHANGED_SCREENSHOTS);
}
fScreenshotInfo = value;
}
bool
PackageInfo::AddListener(const PackageInfoListenerRef& listener)
// #pragma mark - PackageLocalInfoBuilder
PackageInfoBuilder::PackageInfoBuilder(const BString& name)
:
fName(name),
fCoreInfo(),
fLocalizedText(),
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fLocalInfo()
{
}
PackageInfoBuilder::PackageInfoBuilder(const PackageInfoRef& value)
:
fName(),
fCoreInfo(),
fLocalizedText(),
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fLocalInfo()
{
if (value.IsSet())
_Init(value.Get());
}
PackageInfoBuilder::PackageInfoBuilder(const PackageInfo& value)
{
_Init(&value);
}
PackageInfoBuilder::~PackageInfoBuilder()
{
fListeners.push_back(listener);
return true;
}
void
PackageInfo::RemoveListener(const PackageInfoListenerRef& listener)
PackageInfoBuilder::_Init(const PackageInfo* value)
{
fListeners.erase(std::remove(fListeners.begin(), fListeners.end(),
listener), fListeners.end());
fName = value->Name();
fCoreInfo = value->CoreInfo();
fLocalizedText = value->LocalizedText();
fClassificationInfo = value->PackageClassificationInfo();
fScreenshotInfo = value->ScreenshotInfo();
fUserRatingInfo = value->UserRatingInfo();
fLocalInfo = value->LocalInfo();
}
void
PackageInfo::NotifyChangedIcon()
PackageInfoRef
PackageInfoBuilder::BuildRef()
{
_NotifyListeners(PKG_CHANGED_ICON);
PackageInfo* info = new PackageInfo();
info->SetName(fName);
info->SetCoreInfo(fCoreInfo);
info->SetLocalizedText(fLocalizedText);
info->SetPackageClassificationInfo(fClassificationInfo);
info->SetScreenshotInfo(fScreenshotInfo);
info->SetUserRatingInfo(fUserRatingInfo);
info->SetLocalInfo(fLocalInfo);
return PackageInfoRef(info, true);
}
void
PackageInfo::StartCollatingChanges()
PackageInfoBuilder&
PackageInfoBuilder::WithCoreInfo(PackageCoreInfoRef value)
{
fIsCollatingChanges = true;
fCollatedChanges = 0;
fCoreInfo = value;
return *this;
}
void
PackageInfo::EndCollatingChanges()
PackageInfoBuilder&
PackageInfoBuilder::WithLocalizedText(PackageLocalizedTextRef value)
{
if (fIsCollatingChanges && fCollatedChanges != 0)
_NotifyListenersImmediate(fCollatedChanges);
fIsCollatingChanges = false;
fCollatedChanges = 0;
fLocalizedText = value;
return *this;
}
void
PackageInfo::_NotifyListeners(uint32 changes)
PackageInfoBuilder&
PackageInfoBuilder::WithPackageClassificationInfo(PackageClassificationInfoRef value)
{
if (fIsCollatingChanges)
fCollatedChanges |= changes;
else
_NotifyListenersImmediate(changes);
fClassificationInfo = value;
return *this;
}
void
PackageInfo::_NotifyListenersImmediate(uint32 changes)
PackageInfoBuilder&
PackageInfoBuilder::WithUserRatingInfo(PackageUserRatingInfoRef value)
{
if (fListeners.empty())
return;
// Clone list to avoid listeners detaching themselves in notifications
// to screw up the list while iterating it.
std::vector<PackageInfoListenerRef> listeners(fListeners);
PackageInfoEvent event(PackageInfoRef(this), changes);
std::vector<PackageInfoListenerRef>::iterator it;
for (it = listeners.begin(); it != listeners.end(); it++) {
const PackageInfoListenerRef listener = *it;
if (listener.IsSet())
listener->PackageChanged(event);
}
fUserRatingInfo = value;
return *this;
}
PackageInfoBuilder&
PackageInfoBuilder::WithLocalInfo(PackageLocalInfoRef value)
{
fLocalInfo = value;
return *this;
}
PackageInfoBuilder&
PackageInfoBuilder::WithScreenshotInfo(PackageScreenshotInfoRef value)
{
fScreenshotInfo = value;
return *this;
}
+75 -52
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_INFO_H
@@ -8,76 +8,63 @@
#include <Referenceable.h>
#include <package/PackageInfo.h>
#include "Language.h"
#include "List.h"
#include "PackageClassificationInfo.h"
#include "PackageCoreInfo.h"
#include "PackageInfoListener.h"
#include "PackageLocalInfo.h"
#include "PackageLocalizedText.h"
#include "PackagePublisherInfo.h"
#include "PackageScreenshotInfo.h"
#include "PackageUserRatingInfo.h"
#include "ScreenshotInfo.h"
#include "UserRatingInfo.h"
using BPackageKit::BPackageInfo;
class PackageInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageInfoBuilder class as a builder-constructor.
*/
class PackageInfo : public BReferenceable {
friend class PackageInfoBuilder;
public:
PackageInfo();
PackageInfo(const BPackageInfo& info);
PackageInfo(const PackageInfo& other);
PackageInfo& operator=(const PackageInfo& other);
bool operator==(const PackageInfo& other) const;
bool operator!=(const PackageInfo& other) const;
uint32 DiffMask(const PackageInfo& other) const;
uint32 ChangeMask(const PackageInfo& other) const;
const BString& Name() const
{ return fName; }
PackageCoreInfoRef CoreInfo() const
{ return fCoreInfo; }
void SetCoreInfo(PackageCoreInfoRef value);
PackageLocalizedTextRef
const BString& Name() const;
const PackageCoreInfoRef
CoreInfo() const;
const PackageLocalizedTextRef
LocalizedText() const;
void SetLocalizedText(PackageLocalizedTextRef value);
void SetPackageClassificationInfo(
PackageClassificationInfoRef value);
PackageClassificationInfoRef
PackageClassificationInfo() const
{ return fClassificationInfo; }
UserRatingInfoRef UserRatingInfo() const;
void SetUserRatingInfo(UserRatingInfoRef userRatingInfo);
PackageLocalInfoRef LocalInfo() const;
void SetLocalInfo(PackageLocalInfoRef& localInfo);
PackageScreenshotInfoRef
ScreenshotInfo() const
{ return fScreenshotInfo; }
void SetScreenshotInfo(PackageScreenshotInfoRef value);
bool AddListener(
const PackageInfoListenerRef& listener);
void RemoveListener(
const PackageInfoListenerRef& listener);
void StartCollatingChanges();
void EndCollatingChanges();
void NotifyChangedIcon();
const PackageClassificationInfoRef
PackageClassificationInfo() const;
const PackageUserRatingInfoRef
UserRatingInfo() const;
const PackageLocalInfoRef
LocalInfo() const;
const PackageScreenshotInfoRef
ScreenshotInfo() const;
private:
void _NotifyListeners(uint32 changes);
void _NotifyListenersImmediate(uint32 changes);
void SetName(const BString& value);
void SetCoreInfo(PackageCoreInfoRef value);
void SetLocalizedText(PackageLocalizedTextRef value);
void SetPackageClassificationInfo(PackageClassificationInfoRef value);
void SetUserRatingInfo(PackageUserRatingInfoRef& userRatingInfo);
void SetLocalInfo(PackageLocalInfoRef& localInfo);
void SetScreenshotInfo(PackageScreenshotInfoRef value);
private:
BString fName;
@@ -89,19 +76,55 @@ private:
fClassificationInfo;
PackageScreenshotInfoRef
fScreenshotInfo;
UserRatingInfoRef fUserRatingInfo;
PackageUserRatingInfoRef
fUserRatingInfo;
PackageLocalInfoRef fLocalInfo;
std::vector<PackageInfoListenerRef>
fListeners;
bool fIsCollatingChanges;
uint32 fCollatedChanges;
};
typedef BReference<PackageInfo> PackageInfoRef;
class PackageInfoBuilder
{
public:
PackageInfoBuilder(const BString& name);
PackageInfoBuilder(const PackageInfoRef& value);
PackageInfoBuilder(const PackageInfo& value);
virtual ~PackageInfoBuilder();
PackageInfoRef BuildRef();
PackageInfoBuilder&
WithCoreInfo(PackageCoreInfoRef value);
PackageInfoBuilder&
WithLocalizedText(PackageLocalizedTextRef value);
PackageInfoBuilder&
WithPackageClassificationInfo(PackageClassificationInfoRef value);
PackageInfoBuilder&
WithUserRatingInfo(PackageUserRatingInfoRef userRatingInfo);
PackageInfoBuilder&
WithLocalInfo(PackageLocalInfoRef localInfo);
PackageInfoBuilder&
WithScreenshotInfo(PackageScreenshotInfoRef value);
private:
void _Init(const PackageInfo* value);
private:
BString fName;
PackageCoreInfoRef fCoreInfo;
PackageLocalizedTextRef
fLocalizedText;
PackageClassificationInfoRef
fClassificationInfo;
PackageScreenshotInfoRef
fScreenshotInfo;
PackageUserRatingInfoRef
fUserRatingInfo;
PackageLocalInfoRef fLocalInfo;
};
#endif // PACKAGE_INFO_H
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -49,23 +49,6 @@ PackageLocalInfo::~PackageLocalInfo()
}
PackageLocalInfo&
PackageLocalInfo::operator=(const PackageLocalInfo& other)
{
fState = other.fState;
fInstallationLocations = other.fInstallationLocations;
fDownloadProgress = other.fDownloadProgress;
fFlags = other.fFlags;
fSystemDependency = other.fSystemDependency;
fLocalFilePath = other.fLocalFilePath;
fFileName = other.fFileName;
fSize = other.fSize;
fViewed = other.fViewed;
return *this;
}
bool
PackageLocalInfo::operator==(const PackageLocalInfo& other) const
{
@@ -88,6 +71,69 @@ PackageLocalInfo::operator!=(const PackageLocalInfo& other) const
}
bool
PackageLocalInfo::Viewed() const
{
return fViewed;
}
const BString&
PackageLocalInfo::LocalFilePath() const
{
return fLocalFilePath;
}
const BString&
PackageLocalInfo::FileName() const
{
return fFileName;
}
off_t
PackageLocalInfo::Size() const
{
return fSize;
}
int32
PackageLocalInfo::Flags() const
{
return fFlags;
}
bool
PackageLocalInfo::IsSystemDependency() const
{
return fSystemDependency;
}
PackageState
PackageLocalInfo::State() const
{
return fState;
}
const PackageInstallationLocationSet&
PackageLocalInfo::InstallationLocations() const
{
return fInstallationLocations;
}
float
PackageLocalInfo::DownloadProgress() const
{
return fDownloadProgress;
}
bool
PackageLocalInfo::IsSystemPackage() const
{
@@ -122,10 +168,17 @@ PackageLocalInfo::AddInstallationLocation(int32 location)
}
void
PackageLocalInfo::ClearInstallationLocations()
bool
PackageLocalInfo::HasInstallationLocation(int32 location) const
{
fInstallationLocations.clear();
return fInstallationLocations.find(location) != fInstallationLocations.end();
}
int32
PackageLocalInfo::CountInstallationLocations() const
{
return static_cast<int32>(fInstallationLocations.size());
}
@@ -177,3 +230,211 @@ PackageLocalInfo::SetFileName(const BString& value)
{
fFileName = value;
}
// #pragma mark - PackageLocalInfoBuilder
PackageLocalInfoBuilder::PackageLocalInfoBuilder()
:
fViewed(false),
fLocalFilePath(),
fFileName(),
fSize(0),
fFlags(0),
fSystemDependency(false),
fState(NONE),
fInstallationLocations(),
fDownloadProgress(0.0)
{
}
PackageLocalInfoBuilder::PackageLocalInfoBuilder(const PackageLocalInfoRef& value)
:
fViewed(false),
fLocalFilePath(),
fFileName(),
fSize(0),
fFlags(0),
fSystemDependency(false),
fState(NONE),
fInstallationLocations(),
fDownloadProgress(0.0)
{
fSource = value;
}
PackageLocalInfoBuilder::~PackageLocalInfoBuilder()
{
}
void
PackageLocalInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource);
fSource.Unset();
}
}
void
PackageLocalInfoBuilder::_Init(const PackageLocalInfo* value)
{
fViewed = value->Viewed();
fLocalFilePath = value->LocalFilePath();
fFileName = value->FileName();
fSize = value->Size();
fFlags = value->Flags();
fSystemDependency = value->IsSystemDependency();
fState = value->State();
fDownloadProgress = value->DownloadProgress();
PackageInstallationLocationSet valueLocations = value->InstallationLocations();
PackageInstallationLocationSet::const_iterator it;
for (it = valueLocations.begin(); it != valueLocations.end(); it++)
fInstallationLocations.insert(*it);
}
PackageLocalInfoRef
PackageLocalInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageLocalInfo* localInfo = new PackageLocalInfo();
if (fViewed)
localInfo->SetViewed();
localInfo->SetLocalFilePath(fLocalFilePath);
localInfo->SetFileName(fFileName);
localInfo->SetSize(fSize);
localInfo->SetFlags(fFlags);
localInfo->SetSystemDependency(fSystemDependency);
localInfo->SetState(fState);
if (fState == DOWNLOADING)
localInfo->SetDownloadProgress(fDownloadProgress);
PackageInstallationLocationSet::const_iterator it;
for (it = fInstallationLocations.begin(); it != fInstallationLocations.end(); it++)
localInfo->AddInstallationLocation(*it);
return PackageLocalInfoRef(localInfo, true);
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithViewed()
{
if (!fSource.IsSet() || fSource->Viewed()) {
_InitFromSource();
fViewed = true;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithLocalFilePath(const char* path)
{
if (!fSource.IsSet() || fSource->LocalFilePath() != path) {
_InitFromSource();
fLocalFilePath.SetTo(path);
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithFileName(const BString& value)
{
if (!fSource.IsSet() || fSource->FileName() != value) {
_InitFromSource();
fFileName.SetTo(value);
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithSize(off_t size)
{
if (!fSource.IsSet() || fSource->Size() != size) {
_InitFromSource();
fSize = size;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithFlags(int32 value)
{
if (!fSource.IsSet() || fSource->Flags() != value) {
_InitFromSource();
fFlags = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithSystemDependency(bool value)
{
if (!fSource.IsSet() || fSource->IsSystemDependency() != value) {
_InitFromSource();
fSystemDependency = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithState(PackageState value)
{
if (!fSource.IsSet() || fSource->State() != value) {
_InitFromSource();
fState = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithDownloadProgress(float value)
{
if (!fSource.IsSet() || fSource->DownloadProgress() != value) {
_InitFromSource();
fDownloadProgress = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::AddInstallationLocation(int32 value)
{
if (!fSource.IsSet() || !fSource->HasInstallationLocation(value)) {
_InitFromSource();
fInstallationLocations.insert(value);
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::ClearInstallationLocations()
{
if (!fSource.IsSet() || fSource->CountInstallationLocations() != 0) {
_InitFromSource();
fInstallationLocations.clear();
}
return *this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_LOCAL_INFO_H
@@ -27,58 +27,49 @@ enum PackageState {
};
class PackageLocalInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageLocalInfoBuilder class as a builder-constructor.
*/
class PackageLocalInfo : public BReferenceable
{
friend class PackageLocalInfoBuilder;
public:
PackageLocalInfo();
PackageLocalInfo(const PackageLocalInfo& other);
virtual ~PackageLocalInfo();
PackageLocalInfo& operator=(const PackageLocalInfo& other);
bool operator==(const PackageLocalInfo& other) const;
bool operator!=(const PackageLocalInfo& other) const;
bool IsLocalFile() const;
void SetViewed();
bool Viewed() const
{ return fViewed; }
void SetLocalFilePath(const char* path);
const BString& LocalFilePath() const
{ return fLocalFilePath; }
void SetFileName(const BString& value);
const BString& FileName() const
{ return fFileName; }
void SetSize(off_t size);
off_t Size() const
{ return fSize; }
void SetFlags(int32 value);
int32 Flags() const
{ return fFlags; }
bool Viewed() const;
const BString& LocalFilePath() const;
const BString& FileName() const;
off_t Size() const;
int32 Flags() const;
bool IsSystemPackage() const;
void SetSystemDependency(bool isDependency);
bool IsSystemDependency() const
{ return fSystemDependency; }
void SetState(PackageState state);
PackageState State() const
{ return fState; }
void AddInstallationLocation(int32 location);
bool IsSystemDependency() const;
PackageState State() const;
bool HasInstallationLocation(int32 location) const;
int32 CountInstallationLocations() const;
const PackageInstallationLocationSet&
InstallationLocations() const
{ return fInstallationLocations; }
void ClearInstallationLocations();
InstallationLocations() const;
float DownloadProgress() const;
private:
void SetViewed();
void SetLocalFilePath(const char* path);
void SetFileName(const BString& value);
void SetSize(off_t size);
void SetFlags(int32 value);
void SetSystemDependency(bool isDependency);
void SetState(PackageState state);
void AddInstallationLocation(int32 location);
void SetDownloadProgress(float progress);
float DownloadProgress() const
{ return fDownloadProgress; }
private:
bool fViewed;
@@ -97,4 +88,56 @@ private:
typedef BReference<PackageLocalInfo> PackageLocalInfoRef;
class PackageLocalInfoBuilder
{
public:
PackageLocalInfoBuilder();
PackageLocalInfoBuilder(const PackageLocalInfoRef& value);
virtual ~PackageLocalInfoBuilder();
PackageLocalInfoRef BuildRef();
PackageLocalInfoBuilder&
WithViewed();
PackageLocalInfoBuilder&
WithLocalFilePath(const char* path);
PackageLocalInfoBuilder&
WithFileName(const BString& value);
PackageLocalInfoBuilder&
WithSize(off_t size);
PackageLocalInfoBuilder&
WithFlags(int32 value);
PackageLocalInfoBuilder&
WithSystemDependency(bool isDependency);
PackageLocalInfoBuilder&
WithState(PackageState state);
PackageLocalInfoBuilder&
WithDownloadProgress(float progress);
PackageLocalInfoBuilder&
AddInstallationLocation(int32 location);
PackageLocalInfoBuilder&
ClearInstallationLocations();
private:
void _InitFromSource();
void _Init(const PackageLocalInfo* value);
private:
PackageLocalInfoRef fSource;
bool fViewed;
BString fLocalFilePath;
BString fFileName;
off_t fSize;
int32 fFlags;
bool fSystemDependency;
// When true, this value indicates that the package is a dependency of the
// system and so cannot be uninstalled.
PackageState fState;
PackageInstallationLocationSet
fInstallationLocations;
float fDownloadProgress;
};
#endif // PACKAGE_LOCAL_INFO_H
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -117,3 +117,127 @@ PackageLocalizedText::operator!=(const PackageLocalizedText& other) const
{
return !(*this == other);
}
// #pragma mark - PackageLocalInfoBuilder
PackageLocalizedTextBuilder::PackageLocalizedTextBuilder()
:
fTitle(),
fSummary(),
fDescription(),
fHasChangelog(false),
fChangelog()
{
}
PackageLocalizedTextBuilder::PackageLocalizedTextBuilder(const PackageLocalizedTextRef& value)
:
fTitle(),
fSummary(),
fDescription(),
fHasChangelog(false),
fChangelog()
{
fSource = value;
}
PackageLocalizedTextBuilder::~PackageLocalizedTextBuilder()
{
}
void
PackageLocalizedTextBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource);
fSource.Unset();
}
}
void
PackageLocalizedTextBuilder::_Init(const PackageLocalizedText* value)
{
fTitle = value->Title();
fSummary = value->Summary();
fDescription = value->Description();
fHasChangelog = value->HasChangelog();
fChangelog = value->Changelog();
}
PackageLocalizedTextRef
PackageLocalizedTextBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageLocalizedText* localizedText = new PackageLocalizedText();
localizedText->SetTitle(fTitle);
localizedText->SetSummary(fSummary);
localizedText->SetDescription(fDescription);
localizedText->SetHasChangelog(fHasChangelog);
localizedText->SetChangelog(fChangelog);
return PackageLocalizedTextRef(localizedText, true);
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithTitle(const BString& value)
{
if (!fSource.IsSet() || fSource->Title() != value) {
_InitFromSource();
fTitle = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithSummary(const BString& value)
{
if (!fSource.IsSet() || fSource->Summary() != value) {
_InitFromSource();
fSummary = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithDescription(const BString& value)
{
if (!fSource.IsSet() || fSource->Description() != value) {
_InitFromSource();
fDescription = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithHasChangelog(bool value)
{
if (!fSource.IsSet() || fSource->HasChangelog() != value) {
_InitFromSource();
fHasChangelog = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithChangelog(const BString& value)
{
if (!fSource.IsSet() || fSource->Changelog() != value) {
_InitFromSource();
fChangelog = value;
}
return *this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_LOCALIZED_TEXT_H
@@ -10,30 +10,37 @@
#include <String.h>
class PackageLocalizedTextBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageLocalizedTextBuilder class as a builder-constructor.
*/
class PackageLocalizedText : public BReferenceable
{
friend class PackageLocalizedTextBuilder;
public:
PackageLocalizedText();
PackageLocalizedText(const PackageLocalizedText& other);
const BString& Title() const;
void SetTitle(const BString& value);
const BString& Summary() const;
void SetSummary(const BString& value);
const BString& Description() const;
void SetDescription(const BString& value);
const bool HasChangelog() const;
void SetHasChangelog(bool value);
void SetChangelog(const BString& value);
const BString& Changelog() const;
bool operator==(const PackageLocalizedText& other) const;
bool operator!=(const PackageLocalizedText& other) const;
const BString& Title() const;
const BString& Summary() const;
const BString& Description() const;
const bool HasChangelog() const;
const BString& Changelog() const;
private:
void SetTitle(const BString& value);
void SetSummary(const BString& value);
void SetDescription(const BString& value);
void SetHasChangelog(bool value);
void SetChangelog(const BString& value);
private:
BString fTitle;
BString fSummary;
@@ -47,4 +54,40 @@ private:
typedef BReference<PackageLocalizedText> PackageLocalizedTextRef;
class PackageLocalizedTextBuilder
{
public:
PackageLocalizedTextBuilder();
PackageLocalizedTextBuilder(const PackageLocalizedTextRef& value);
virtual ~PackageLocalizedTextBuilder();
PackageLocalizedTextRef
BuildRef();
PackageLocalizedTextBuilder&
WithTitle(const BString& value);
PackageLocalizedTextBuilder&
WithSummary(const BString& value);
PackageLocalizedTextBuilder&
WithDescription(const BString& value);
PackageLocalizedTextBuilder&
WithHasChangelog(bool value);
PackageLocalizedTextBuilder&
WithChangelog(const BString& value);
private:
void _InitFromSource();
void _Init(const PackageLocalizedText* value);
private:
PackageLocalizedTextRef
fSource;
BString fTitle;
BString fSummary;
BString fDescription;
bool fHasChangelog;
BString fChangelog;
};
#endif // PACKAGE_LOCALIZED_TEXT_H
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_PUBLISHER_INFO_H
@@ -33,6 +33,8 @@ private:
BString fWebsite;
};
typedef BReference<PackagePublisherInfo> PackagePublisherInfoRef;
#endif // PACKAGE_PUBLISHER_INFO_H
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -19,14 +19,6 @@ PackageScreenshotInfo::PackageScreenshotInfo(const PackageScreenshotInfo& other)
}
PackageScreenshotInfo&
PackageScreenshotInfo::operator=(const PackageScreenshotInfo& other)
{
fScreenshotInfos = other.fScreenshotInfos;
return *this;
}
bool
PackageScreenshotInfo::operator==(const PackageScreenshotInfo& other) const
{
@@ -41,13 +33,6 @@ PackageScreenshotInfo::operator!=(const PackageScreenshotInfo& other) const
}
void
PackageScreenshotInfo::Clear()
{
fScreenshotInfos.clear();
}
int32
PackageScreenshotInfo::Count() const
{
@@ -55,7 +40,7 @@ PackageScreenshotInfo::Count() const
}
ScreenshotInfoRef
const ScreenshotInfoRef
PackageScreenshotInfo::ScreenshotAtIndex(int32 index) const
{
return fScreenshotInfos[index];
@@ -66,4 +51,73 @@ void
PackageScreenshotInfo::AddScreenshot(const ScreenshotInfoRef& info)
{
fScreenshotInfos.push_back(info);
}
}
// #pragma mark - PackageScreenshotInfoBuilder
PackageScreenshotInfoBuilder::PackageScreenshotInfoBuilder()
:
fSource(),
fScreenshotInfos()
{
}
PackageScreenshotInfoBuilder::PackageScreenshotInfoBuilder(const PackageScreenshotInfoRef& other)
:
fScreenshotInfos()
{
fSource = other;
}
PackageScreenshotInfoBuilder::~PackageScreenshotInfoBuilder()
{
}
void
PackageScreenshotInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageScreenshotInfoBuilder::_Init(const PackageScreenshotInfo* value)
{
int32 count = value->Count();
for (int32 i = 0; i < count; i++)
fScreenshotInfos.push_back(value->ScreenshotAtIndex(i));
}
PackageScreenshotInfoRef
PackageScreenshotInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageScreenshotInfo* screenshotInfo = new PackageScreenshotInfo();
std::vector<ScreenshotInfoRef>::const_iterator it;
for (it = fScreenshotInfos.begin(); it != fScreenshotInfos.end(); it++)
screenshotInfo->AddScreenshot(*it);
return PackageScreenshotInfoRef(screenshotInfo, true);
}
PackageScreenshotInfoBuilder&
PackageScreenshotInfoBuilder::AddScreenshot(const ScreenshotInfoRef& value)
{
if (fSource.IsSet())
_InitFromSource();
fScreenshotInfos.push_back(value);
return *this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_SCREENSHOT_INFO_H
@@ -14,21 +14,28 @@
#include "ScreenshotInfo.h"
class PackageScreenshotInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageScreenshotInfoBuilder class as a builder-constructor.
*/
class PackageScreenshotInfo : public BReferenceable
{
friend class PackageScreenshotInfoBuilder;
public:
PackageScreenshotInfo();
PackageScreenshotInfo(const PackageScreenshotInfo& other);
PackageScreenshotInfo&
operator=(const PackageScreenshotInfo& other);
bool operator==(const PackageScreenshotInfo& other) const;
bool operator!=(const PackageScreenshotInfo& other) const;
void Clear();
void AddScreenshot(const ScreenshotInfoRef& info);
int32 Count() const;
ScreenshotInfoRef ScreenshotAtIndex(int32 index) const;
const ScreenshotInfoRef ScreenshotAtIndex(int32 index) const;
private:
void AddScreenshot(const ScreenshotInfoRef& info);
private:
std::vector<ScreenshotInfoRef>
@@ -39,4 +46,29 @@ private:
typedef BReference<PackageScreenshotInfo> PackageScreenshotInfoRef;
class PackageScreenshotInfoBuilder
{
public:
PackageScreenshotInfoBuilder();
PackageScreenshotInfoBuilder(const PackageScreenshotInfoRef& other);
virtual ~PackageScreenshotInfoBuilder();
PackageScreenshotInfoRef
BuildRef();
PackageScreenshotInfoBuilder&
AddScreenshot(const ScreenshotInfoRef& value);
private:
void _InitFromSource();
void _Init(const PackageScreenshotInfo* value);
private:
PackageScreenshotInfoRef
fSource;
std::vector<ScreenshotInfoRef>
fScreenshotInfos;
};
#endif // PACKAGE_SCREENSHOT_INFO_H
@@ -0,0 +1,206 @@
/*
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageUserRatingInfo.h"
PackageUserRatingInfo::PackageUserRatingInfo()
:
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
PackageUserRatingInfo::PackageUserRatingInfo(const PackageUserRatingInfo& other)
:
fSummary(other.Summary()),
fUserRatingsPopulated(other.UserRatingsPopulated())
{
for (int32 i = CountUserRatings() - 1; i >= 0; i--)
AddUserRating(other.UserRatingAtIndex(i));
}
const UserRatingSummaryRef
PackageUserRatingInfo::Summary() const
{
return fSummary;
}
void
PackageUserRatingInfo::SetSummary(UserRatingSummaryRef value)
{
fSummary = value;
}
void
PackageUserRatingInfo::SetUserRatingsPopulated()
{
fUserRatingsPopulated = true;
}
bool
PackageUserRatingInfo::UserRatingsPopulated() const
{
return fUserRatingsPopulated;
}
int32
PackageUserRatingInfo::CountUserRatings() const
{
return fUserRatings.size();
}
const UserRatingRef
PackageUserRatingInfo::UserRatingAtIndex(int32 index) const
{
return fUserRatings[index];
}
void
PackageUserRatingInfo::AddUserRating(const UserRatingRef& rating)
{
fUserRatings.push_back(rating);
}
bool
PackageUserRatingInfo::operator==(const PackageUserRatingInfo& other) const
{
if (Summary() != other.Summary())
return false;
if (UserRatingsPopulated() != other.UserRatingsPopulated())
return false;
if (CountUserRatings() != other.CountUserRatings())
return false;
for (int32 i = CountUserRatings() - 1; i >= 0; i--) {
if (other.UserRatingAtIndex(i) != UserRatingAtIndex(i))
return false;
}
return true;
}
bool
PackageUserRatingInfo::operator!=(const PackageUserRatingInfo& other) const
{
return !(*this == other);
}
// #pragma mark - PackageUserRatingInfoBuilder
PackageUserRatingInfoBuilder::PackageUserRatingInfoBuilder()
:
fSource(),
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
PackageUserRatingInfoBuilder::PackageUserRatingInfoBuilder(const PackageUserRatingInfoRef& other)
:
fSource(other),
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
PackageUserRatingInfoBuilder::~PackageUserRatingInfoBuilder()
{
}
PackageUserRatingInfoRef
PackageUserRatingInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageUserRatingInfo* userRatingInfo = new PackageUserRatingInfo();
userRatingInfo->SetSummary(fSummary);
if (fUserRatingsPopulated)
userRatingInfo->SetUserRatingsPopulated();
std::vector<UserRatingRef>::const_iterator it;
for (it = fUserRatings.begin(); it != fUserRatings.end(); it++)
userRatingInfo->AddUserRating(*it);
return PackageUserRatingInfoRef(userRatingInfo, true);
}
PackageUserRatingInfoBuilder&
PackageUserRatingInfoBuilder::AddUserRating(const UserRatingRef& rating)
{
if (fSource.IsSet())
_InitFromSource();
fUserRatings.push_back(rating);
return *this;
}
PackageUserRatingInfoBuilder&
PackageUserRatingInfoBuilder::WithSummary(UserRatingSummaryRef value)
{
if (!fSource.IsSet() || fSource->Summary() != value) {
_InitFromSource();
fSummary = value;
}
return *this;
}
PackageUserRatingInfoBuilder&
PackageUserRatingInfoBuilder::WithUserRatingsPopulated()
{
if (!fSource.IsSet() || !fSource->UserRatingsPopulated()) {
_InitFromSource();
fUserRatingsPopulated = true;
}
return *this;
}
void
PackageUserRatingInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageUserRatingInfoBuilder::_Init(const PackageUserRatingInfo* value)
{
fSummary = value->Summary();
fUserRatingsPopulated = value->UserRatingsPopulated();
int32 count = value->CountUserRatings();
for (int32 i = 0; i < count; i++)
fUserRatings.push_back(value->UserRatingAtIndex(i));
}
@@ -0,0 +1,92 @@
/*
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_USER_RATING_INFO_H
#define PACKAGE_USER_RATING_INFO_H
#include <vector>
#include <Referenceable.h>
#include "UserRating.h"
#include "UserRatingSummary.h"
class PackageUserRatingInfoBuilder;
/*! This class carries the collection of user ratings related to a package but
also provides an instance of `UserRatingSummaryRef` which is a summary of
the user ratings.
Instances of this class should not be created directly; instead use the
PackageUserRatingInfoBuilder class as a builder-constructor.
*/
class PackageUserRatingInfo : public BReferenceable
{
friend class PackageUserRatingInfoBuilder;
public:
PackageUserRatingInfo();
PackageUserRatingInfo(const PackageUserRatingInfo& other);
bool operator==(const PackageUserRatingInfo& other) const;
bool operator!=(const PackageUserRatingInfo& other) const;
const UserRatingSummaryRef
Summary() const;
int32 CountUserRatings() const;
const UserRatingRef UserRatingAtIndex(int32 index) const;
bool UserRatingsPopulated() const;
private:
void SetSummary(UserRatingSummaryRef value);
void AddUserRating(const UserRatingRef& rating);
void SetUserRatingsPopulated();
private:
std::vector<UserRatingRef>
fUserRatings;
UserRatingSummaryRef
fSummary;
bool fUserRatingsPopulated;
};
typedef BReference<PackageUserRatingInfo> PackageUserRatingInfoRef;
class PackageUserRatingInfoBuilder
{
public:
PackageUserRatingInfoBuilder();
PackageUserRatingInfoBuilder(const PackageUserRatingInfoRef& other);
virtual ~PackageUserRatingInfoBuilder();
PackageUserRatingInfoRef
BuildRef();
PackageUserRatingInfoBuilder&
AddUserRating(const UserRatingRef& rating);
PackageUserRatingInfoBuilder&
WithSummary(UserRatingSummaryRef value);
PackageUserRatingInfoBuilder&
WithUserRatingsPopulated();
private:
void _InitFromSource();
void _Init(const PackageUserRatingInfo* value);
private:
PackageUserRatingInfoRef
fSource;
std::vector<UserRatingRef>
fUserRatings;
UserRatingSummaryRef
fSummary;
bool fUserRatingsPopulated;
};
#endif // PACKAGE_USER_RATING_INFO_H
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -15,6 +15,14 @@ PackageVersion::PackageVersion()
}
PackageVersion::PackageVersion(uint64 createTimestamp)
:
BPackageVersion(),
fCreateTimestamp(createTimestamp)
{
}
PackageVersion::PackageVersion(const PackageVersion& other)
:
BPackageVersion(other),
@@ -23,6 +31,14 @@ PackageVersion::PackageVersion(const PackageVersion& other)
}
PackageVersion::PackageVersion(const PackageVersion& other, uint64 createTimestamp)
:
BPackageVersion(other),
fCreateTimestamp(createTimestamp)
{
}
PackageVersion::PackageVersion(const BPackageVersion& other)
:
BPackageVersion(other),
@@ -36,22 +52,10 @@ PackageVersion::~PackageVersion()
}
void
PackageVersion::SetCreateTimestamp(uint64 value)
uint64
PackageVersion::CreateTimestamp() const
{
fCreateTimestamp = value;
}
PackageVersion&
PackageVersion::operator=(const PackageVersion& other)
{
if (this != &other) {
// can't use BPackageVersion operator= possibly because it is inlined?
SetTo(other.Major(), other.Minor(), other.Micro(), other.PreRelease(), other.Revision());
fCreateTimestamp = other.fCreateTimestamp;
}
return *this;
return fCreateTimestamp;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_VERSION_H
@@ -18,17 +18,16 @@ class PackageVersion : public BPackageVersion, public BReferenceable
{
public:
PackageVersion();
PackageVersion(uint64 createTimestamp);
PackageVersion(const PackageVersion& other);
PackageVersion(const PackageVersion& other, uint64 createTimestamp);
PackageVersion(const BPackageVersion& other);
virtual ~PackageVersion();
PackageVersion& operator=(const PackageVersion& other);
bool operator==(const PackageVersion& other) const;
bool operator!=(const PackageVersion& other) const;
uint64 CreateTimestamp() const
{ return fCreateTimestamp; }
void SetCreateTimestamp(uint64 value);
uint64 CreateTimestamp() const;
private:
uint64 fCreateTimestamp;
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -30,14 +30,6 @@ UserInfo::UserInfo(const UserInfo& other)
}
UserInfo&
UserInfo::operator=(const UserInfo& other)
{
fNickName = other.fNickName;
return *this;
}
bool
UserInfo::operator==(const UserInfo& other) const
{
+1 -2
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_INFO_H
@@ -16,7 +16,6 @@ public:
UserInfo(const BString& nickName);
UserInfo(const UserInfo& other);
UserInfo& operator=(const UserInfo& other);
bool operator==(const UserInfo& other) const;
bool operator!=(const UserInfo& other) const;
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -47,19 +47,6 @@ UserRating::UserRating(const UserRating& other)
}
UserRating&
UserRating::operator=(const UserRating& other)
{
fUserInfo = other.fUserInfo;
fRating = other.fRating;
fComment = other.fComment;
fLanguageId = other.fLanguageId;
fPackageVersion = other.fPackageVersion;
fCreateTimestamp = other.fCreateTimestamp;
return *this;
}
bool
UserRating::operator==(const UserRating& other) const
{
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_RATING_H
@@ -23,7 +23,6 @@ public:
uint64 createTimestamp);
UserRating(const UserRating& other);
UserRating& operator=(const UserRating& other);
bool operator==(const UserRating& other) const;
bool operator!=(const UserRating& other) const;
@@ -1,128 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "UserRatingInfo.h"
UserRatingInfo::UserRatingInfo()
:
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
UserRatingInfo::UserRatingInfo(const UserRatingInfo& other)
:
fSummary(other.Summary()),
fUserRatingsPopulated(other.UserRatingsPopulated())
{
for (int32 i = CountUserRatings() - 1; i >= 0; i--)
AddUserRating(other.UserRatingAtIndex(i));
}
void
UserRatingInfo::Clear()
{
fSummary.Unset();
ClearUserRatings();
}
void
UserRatingInfo::SetSummary(UserRatingSummaryRef value)
{
fSummary = value;
}
void
UserRatingInfo::SetUserRatingsPopulated()
{
fUserRatingsPopulated = true;
}
bool
UserRatingInfo::UserRatingsPopulated() const
{
return fUserRatingsPopulated;
}
void
UserRatingInfo::ClearUserRatings()
{
fUserRatings.clear();
fUserRatingsPopulated = false;
}
int32
UserRatingInfo::CountUserRatings() const
{
return fUserRatings.size();
}
UserRatingRef
UserRatingInfo::UserRatingAtIndex(int32 index) const
{
return fUserRatings[index];
}
void
UserRatingInfo::AddUserRating(const UserRatingRef& rating)
{
fUserRatings.push_back(rating);
}
UserRatingInfo&
UserRatingInfo::operator=(const UserRatingInfo& other)
{
fSummary = other.Summary();
fUserRatings.clear();
fUserRatingsPopulated = other.UserRatingsPopulated();
for (int32 i = CountUserRatings() - 1; i >= 0; i--)
AddUserRating(other.UserRatingAtIndex(i));
return *this;
}
bool
UserRatingInfo::operator==(const UserRatingInfo& other) const
{
if (Summary() != other.Summary())
return false;
if (UserRatingsPopulated() != other.UserRatingsPopulated())
return false;
if (CountUserRatings() != other.CountUserRatings())
return false;
for (int32 i = CountUserRatings() - 1; i >= 0; i--) {
if (other.UserRatingAtIndex(i) != UserRatingAtIndex(i))
return false;
}
return true;
}
bool
UserRatingInfo::operator!=(const UserRatingInfo& other) const
{
return !(*this == other);
}
@@ -1,56 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_RATING_INFO_H
#define USER_RATING_INFO_H
#include <vector>
#include <Referenceable.h>
#include "UserRating.h"
#include "UserRatingSummary.h"
/*! This class carries the collection of user ratings related to a package but also provides
an instance of `UserRatingSummaryRef` which is a summary of the user ratings.
*/
class UserRatingInfo : public BReferenceable
{
public:
UserRatingInfo();
UserRatingInfo(const UserRatingInfo& other);
void Clear();
const UserRatingSummaryRef
Summary() const
{ return fSummary; }
void SetSummary(UserRatingSummaryRef value);
void ClearUserRatings();
void AddUserRating(const UserRatingRef& rating);
int32 CountUserRatings() const;
UserRatingRef UserRatingAtIndex(int32 index) const;
void SetUserRatingsPopulated();
bool UserRatingsPopulated() const;
UserRatingInfo& operator=(const UserRatingInfo& other);
bool operator==(const UserRatingInfo& other) const;
bool operator!=(const UserRatingInfo& other) const;
private:
std::vector<UserRatingRef>
fUserRatings;
UserRatingSummaryRef
fSummary;
bool fUserRatingsPopulated;
};
typedef BReference<UserRatingInfo> UserRatingInfoRef;
#endif // USER_RATING_SUMMARY_H
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -18,7 +18,7 @@ UserRatingSummary::UserRatingSummary()
fRatingCount(0),
fRatingCountNoStar(0)
{
for (int i = 0; i < 6; i++)
for (int i = 0; i <= 5; i++)
fRatingCountByStar[i] = 0;
}
@@ -34,15 +34,17 @@ UserRatingSummary::UserRatingSummary(const UserRatingSummary& other)
}
void
UserRatingSummary::Clear()
float
UserRatingSummary::AverageRating() const
{
fAverageRating = RATING_MISSING;
fRatingCount = 0;
fRatingCountNoStar = 0;
return fAverageRating;
}
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = 0;
int
UserRatingSummary::RatingCount() const
{
return fRatingCount;
}
@@ -93,20 +95,6 @@ UserRatingSummary::SetRatingByStar(int star, int ratingCount)
}
UserRatingSummary&
UserRatingSummary::operator=(const UserRatingSummary& other)
{
fAverageRating = other.AverageRating();
fRatingCount = other.RatingCount();
fRatingCountNoStar = other.RatingCountByStar(-1);
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = other.RatingCountByStar(i);
return *this;
}
bool
UserRatingSummary::operator==(const UserRatingSummary& other) const
{
@@ -129,3 +117,117 @@ UserRatingSummary::operator!=(const UserRatingSummary& other) const
{
return !(*this == other);
}
// #pragma mark - UserRatingSummaryBuilder
UserRatingSummaryBuilder::UserRatingSummaryBuilder()
:
fAverageRating(RATING_MISSING),
fRatingCount(0),
fRatingCountNoStar(0)
{
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = 0;
}
UserRatingSummaryBuilder::UserRatingSummaryBuilder(const UserRatingSummaryRef& other)
:
fSource(),
fAverageRating(RATING_MISSING),
fRatingCount(0),
fRatingCountNoStar(0)
{
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = other->RatingCountByStar(i);
}
UserRatingSummaryBuilder::~UserRatingSummaryBuilder()
{
}
UserRatingSummaryRef
UserRatingSummaryBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
UserRatingSummary* userRatingSummary = new UserRatingSummary();
userRatingSummary->SetAverageRating(fAverageRating);
userRatingSummary->SetRatingCount(fRatingCount);
userRatingSummary->SetRatingByStar(RATING_MISSING_STAR, fRatingCountNoStar);
for (int star = 0; star <= 5; star++)
userRatingSummary->SetRatingByStar(star, fRatingCountByStar[star]);
return UserRatingSummaryRef(userRatingSummary, true);
}
UserRatingSummaryBuilder&
UserRatingSummaryBuilder::WithAverageRating(float value)
{
if (!fSource.IsSet() || fSource->AverageRating() != value) {
_InitFromSource();
fAverageRating = value;
}
return *this;
}
UserRatingSummaryBuilder&
UserRatingSummaryBuilder::WithRatingCount(int value)
{
if (!fSource.IsSet() || fSource->RatingCount() != value) {
_InitFromSource();
fRatingCount = value;
}
return *this;
}
UserRatingSummaryBuilder&
UserRatingSummaryBuilder::AddRatingByStar(int star, int ratingCount)
{
if (star < RATING_MISSING_STAR || star > 5) {
HDERROR("bad star %" B_PRId32 " in builder", star);
return *this;
}
if (!fSource.IsSet() || fSource->RatingCountByStar(star) != ratingCount) {
_InitFromSource();
if (star == RATING_MISSING_STAR)
fRatingCountNoStar = ratingCount;
else
fRatingCountByStar[star] = ratingCount;
}
return *this;
}
void
UserRatingSummaryBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
UserRatingSummaryBuilder::_Init(const UserRatingSummary* value)
{
fAverageRating = value->AverageRating();
fRatingCount = value->RatingCount();
fRatingCountNoStar = value->RatingCountByStar(RATING_MISSING_STAR);
for (int i = 0; i <= 5; i++)
fRatingCountByStar[i] = value->RatingCountByStar(i);
}
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_RATING_SUMMARY_H
@@ -15,39 +15,70 @@
include users' ratings for every single version of a package but only the last
one. Other rules also apply. For this reason, the calculation is performed on
the server-side and presented here.
*/
Instances of this class should not be created directly; instead use the
UserRatingSummaryBuilder class as a builder-constructor.
*/
class UserRatingSummary : public BReferenceable
{
friend class UserRatingSummaryBuilder;
public:
UserRatingSummary();
UserRatingSummary(const UserRatingSummary& other);
void Clear();
bool operator==(const UserRatingSummary& other) const;
bool operator!=(const UserRatingSummary& other) const;
float AverageRating() const
{ return fAverageRating; }
int RatingCount() const
{ return fRatingCount; }
float AverageRating() const;
int RatingCount() const;
int RatingCountByStar(int star) const;
private:
void SetAverageRating(float value);
void SetRatingCount(int value);
void SetRatingByStar(int star, int ratingCount);
UserRatingSummary& operator=(const UserRatingSummary& other);
bool operator==(const UserRatingSummary& other) const;
bool operator!=(const UserRatingSummary& other) const;
private:
float fAverageRating;
int fRatingCount;
int fRatingCountNoStar;
int fRatingCountByStar[6];
};
typedef BReference<UserRatingSummary> UserRatingSummaryRef;
class UserRatingSummaryBuilder
{
public:
UserRatingSummaryBuilder();
UserRatingSummaryBuilder(const UserRatingSummaryRef& other);
virtual ~UserRatingSummaryBuilder();
UserRatingSummaryRef
BuildRef();
UserRatingSummaryBuilder&
WithAverageRating(float value);
UserRatingSummaryBuilder&
WithRatingCount(int value);
UserRatingSummaryBuilder&
AddRatingByStar(int star, int ratingCount);
private:
void _InitFromSource();
void _Init(const UserRatingSummary* value);
private:
UserRatingSummaryRef
fSource;
float fAverageRating;
int fRatingCount;
int fRatingCountNoStar;
int fRatingCountByStar[6];
};
#endif // USER_RATING_SUMMARY_H
+30 -10
View File
@@ -1,12 +1,12 @@
/*
* Copyright 2018-2022, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractProcess.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
@@ -18,6 +18,9 @@
#include "ProcessListener.h"
static const bigtime_t kProcessUpdateMinimumDelay = 250000;
AbstractProcess::AbstractProcess()
:
fLock(),
@@ -25,7 +28,8 @@ AbstractProcess::AbstractProcess()
fWasStopped(false),
fProcessState(PROCESS_INITIAL),
fErrorStatus(B_OK),
fDurationSeconds(0.0)
fDurationSeconds(0.0),
fLastProgressUpdate(0)
{
}
@@ -72,7 +76,7 @@ AbstractProcess::Run()
BStopWatch stopWatch("process", true);
status_t runResult = RunInternal();
fDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0);
fDurationSeconds = static_cast<double>(stopWatch.ElapsedTime()) / 1000000.0;
if (runResult != B_OK)
HDERROR("[%s] an error has arisen; %s", Name(), strerror(runResult));
@@ -109,11 +113,11 @@ AbstractProcess::ErrorStatus()
}
/*! This method will stop the process. The actual process may carry on to
perform some tidy-ups on its thread so this does not stop the thread or
change the state of the process; just indicates to the running thread that
it should stop. If it has not yet been started then it will be put into
finished state.
/*! This method will stop the process. The actual process may carry on to
perform some tidy-ups on its thread so this does not stop the thread or
change the state of the process; just indicates to the running thread that
it should stop. If it has not yet been started then it will be put into
finished state.
*/
status_t
@@ -196,6 +200,22 @@ AbstractProcess::LogReport()
}
/*! This method should be called before processing any progress. It will prevent
progress updates from coming through too frequently.
*/
bool
AbstractProcess::_ShouldProcessProgress()
{
bigtime_t now = system_time();
if (now - fLastProgressUpdate < kProcessUpdateMinimumDelay)
return false;
fLastProgressUpdate = now;
return true;
}
/*static*/ char
AbstractProcess::_ProcessStateIdentifier(process_state value)
{
@@ -209,4 +229,4 @@ AbstractProcess::_ProcessStateIdentifier(process_state value)
default:
return '?';
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_PROCESS_H
@@ -50,6 +50,8 @@ protected:
static char _ProcessStateIdentifier(process_state value);
bool _ShouldProcessProgress();
protected:
BLocker fLock;
@@ -59,6 +61,7 @@ private:
process_state fProcessState;
status_t fErrorStatus;
double fDurationSeconds;
bigtime_t fLastProgressUpdate;
};
#endif // ABSTRACT_PROCESS_H
@@ -1,11 +1,11 @@
/*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ProcessCoordinatorFactory.h"
#include <Autolock.h>
#include <AutoLocker.h>
#include <Autolock.h>
#include <package/Context.h>
#include <package/PackageRoster.h>
@@ -42,13 +42,11 @@ using namespace BPackageKit;
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateIncrementViewCounter(
Model* model, const PackageInfoRef package)
ProcessCoordinatorFactory::CreateIncrementViewCounter(Model* model, const PackageInfoRef package)
{
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
"IncrementViewCounter");
AbstractProcessNode* node = new ThreadedProcessNode(
new IncrementViewCounterProcess(model, package));
ProcessCoordinator* processCoordinator = new ProcessCoordinator("IncrementViewCounter");
AbstractProcessNode* node
= new ThreadedProcessNode(new IncrementViewCounterProcess(model, package));
processCoordinator->AddNode(node);
return processCoordinator;
}
@@ -56,56 +54,46 @@ ProcessCoordinatorFactory::CreateIncrementViewCounter(
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
UserDetailVerifierListener* userDetailVerifierListener,
Model* model)
UserDetailVerifierListener* userDetailVerifierListener, Model* model)
{
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
"UserDetailVerifier");
AbstractProcessNode* userDetailVerifier = new ThreadedProcessNode(
new UserDetailVerifierProcess(model, userDetailVerifierListener));
ProcessCoordinator* processCoordinator = new ProcessCoordinator("UserDetailVerifier");
AbstractProcessNode* userDetailVerifier
= new ThreadedProcessNode(new UserDetailVerifierProcess(model, userDetailVerifierListener));
processCoordinator->AddNode(userDetailVerifier);
return processCoordinator;
}
/* static */ ProcessCoordinator*
ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
PackageInfoListenerRef packageInfoListener,
Model* model, bool forceLocalUpdate)
ProcessCoordinatorFactory::CreateBulkLoadCoordinator(Model* model, bool forceLocalUpdate)
{
bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();
uint32 serverProcessOptions = _CalculateServerProcessOptions();
BAutolock locker(model->Lock());
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
"BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
ProcessCoordinator* processCoordinator
= new ProcessCoordinator("BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
AbstractProcessNode *localRepositoryUpdate =
new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model,
forceLocalUpdate));
AbstractProcessNode* localRepositoryUpdate
= new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model, forceLocalUpdate));
processCoordinator->AddNode(localRepositoryUpdate);
AbstractProcessNode *localPkgDataLoad =
new ThreadedProcessNode(new LocalPkgDataLoadProcess(
packageInfoListener, model, forceLocalUpdate));
AbstractProcessNode* localPkgDataLoad
= new ThreadedProcessNode(new LocalPkgDataLoadProcess(model, forceLocalUpdate));
localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
processCoordinator->AddNode(localPkgDataLoad);
if (areWorkingFilesAvailable) {
AbstractProcessNode *serverIconExportUpdate =
new ThreadedProcessNode(new ServerIconExportUpdateProcess(model,
serverProcessOptions));
AbstractProcessNode* serverIconExportUpdate = new ThreadedProcessNode(
new ServerIconExportUpdateProcess(model, serverProcessOptions));
serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverIconExportUpdate);
AbstractProcessNode *serverRepositoryDataUpdate =
new ThreadedProcessNode(new ServerRepositoryDataUpdateProcess(model,
serverProcessOptions));
AbstractProcessNode* serverRepositoryDataUpdate = new ThreadedProcessNode(
new ServerRepositoryDataUpdateProcess(model, serverProcessOptions));
serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverRepositoryDataUpdate);
AbstractProcessNode *serverReferenceDataUpdate =
new ThreadedProcessNode(new ServerReferenceDataUpdateProcess(model,
serverProcessOptions));
AbstractProcessNode* serverReferenceDataUpdate = new ThreadedProcessNode(
new ServerReferenceDataUpdateProcess(model, serverProcessOptions));
processCoordinator->AddNode(serverReferenceDataUpdate);
// This one has to run after the server data is taken up because it
@@ -113,8 +101,8 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
// sourced from the server. It has all of the
// `ServerPkgDataUpdateProcess` nodes configured as its predecessors.
AbstractProcessNode* populatePkgSizes =
new ThreadedProcessNode(new PopulatePkgSizesProcess(model));
AbstractProcessNode* populatePkgSizes
= new ThreadedProcessNode(new PopulatePkgSizesProcess(model));
// create a process for each of the repositories that are configured on
// the local system. Later, only those that have a web-app repository
@@ -127,20 +115,19 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
status_t repoNamesResult = roster.GetRepositoryNames(repoNames);
if (repoNamesResult == B_OK) {
AutoLocker<BLocker> locker(model->Lock());
for (int32 i = 0; i < repoNames.CountStrings(); i++) {
AbstractProcessNode* processNode = new ThreadedProcessNode(
new ServerPkgDataUpdateProcess(
repoNames.StringAt(i), model, serverProcessOptions));
AbstractProcessNode* processNode
= new ThreadedProcessNode(new ServerPkgDataUpdateProcess(repoNames.StringAt(i),
model, serverProcessOptions));
processNode->AddPredecessor(serverRepositoryDataUpdate);
processNode->AddPredecessor(serverReferenceDataUpdate);
processCoordinator->AddNode(processNode);
populatePkgSizes->AddPredecessor(processNode);
}
} else
} else {
HDERROR("a problem has arisen getting the repository names.");
}
processCoordinator->AddNode(populatePkgSizes);
}
@@ -150,8 +137,7 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreatePackageActionCoordinator(
Model* model, BMessage* message)
ProcessCoordinatorFactory::CreatePackageActionCoordinator(Model* model, BMessage* message)
{
switch (message->what) {
case MSG_PKG_INSTALL:
@@ -176,89 +162,80 @@ ProcessCoordinatorFactory::CacheScreenshotCoordinator(Model* model,
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(Model* model, PackageInfoRef package)
ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(Model* model, const BString& packageName)
{
return _CreateSingleProcessCoordinator("PopulatePkgChangelog",
new PopulatePkgChangelogFromServerProcess(package, model));
new PopulatePkgChangelogFromServerProcess(packageName, model));
}
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(Model* model, PackageInfoRef package)
ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(Model* model,
const BString& packageName)
{
return _CreateSingleProcessCoordinator("PopulatePkgUserRatings",
new PopulatePkgUserRatingsFromServerProcess(package, model));
new PopulatePkgUserRatingsFromServerProcess(packageName, model));
}
/*static*/ PackageInfoRef
ProcessCoordinatorFactory::_ExtractPackageFromMessage(
Model* model, BMessage* message)
/*static*/ BString
ProcessCoordinatorFactory::_ExtractPackageNameFromMessage(BMessage* message)
{
BString pkgName;
if (message->FindString(KEY_PACKAGE_NAME, &pkgName) != B_OK)
HDFATAL("malformed message missing key [%s]", KEY_PACKAGE_NAME);
return model->PackageForName(pkgName);
return pkgName;
}
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator(
Model* model, BMessage* message)
ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator(Model* model, BMessage* message)
{
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
"InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
PackageInfoRef package = _ExtractPackageFromMessage(model, message);
if (package.IsSet()) {
AbstractProcessNode *processNode =
new ThreadedProcessNode(
new InstallPackageProcess(package, model), 10);
processCoordinator->AddNode(processNode);
} else {
HDERROR("unable to find the package");
}
ProcessCoordinator* processCoordinator
= new ProcessCoordinator("InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
AbstractProcessNode* processNode = new ThreadedProcessNode(
new InstallPackageProcess(_ExtractPackageNameFromMessage(message), model), 10);
processCoordinator->AddNode(processNode);
return processCoordinator;
}
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator(
Model* model, BMessage* message)
ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator(Model* model, BMessage* message)
{
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
"UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
PackageInfoRef package = _ExtractPackageFromMessage(model, message);
if (package.IsSet()) {
AbstractProcessNode *processNode =
new ThreadedProcessNode(
new UninstallPackageProcess(package, model), 10);
processCoordinator->AddNode(processNode);
} else {
HDERROR("unable to find the package");
}
ProcessCoordinator* processCoordinator
= new ProcessCoordinator("UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
AbstractProcessNode* processNode = new ThreadedProcessNode(
new UninstallPackageProcess(_ExtractPackageNameFromMessage(message), model), 10);
processCoordinator->AddNode(processNode);
return processCoordinator;
}
/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator(
Model* model, BMessage* message)
ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator(Model* model, BMessage* message)
{
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
"OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
PackageInfoRef package = _ExtractPackageFromMessage(model, message);
if (package.IsSet()) {
BMessage deskbarLinkMessage;
if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK)
HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK);
DeskbarLink deskbarLink(&deskbarLinkMessage);
AbstractProcessNode *processNode =
new ThreadedProcessNode(new OpenPackageProcess(
package, model, deskbarLink));
processCoordinator->AddNode(processNode);
} else {
HDERROR("unable to find the package");
}
ProcessCoordinator* processCoordinator
= new ProcessCoordinator("OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
BMessage deskbarLinkMessage;
if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK)
HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK);
DeskbarLink deskbarLink(&deskbarLinkMessage);
AbstractProcessNode* processNode = new ThreadedProcessNode(
new OpenPackageProcess(_ExtractPackageNameFromMessage(message), model, deskbarLink));
processCoordinator->AddNode(processNode);
return processCoordinator;
}
@@ -269,8 +246,7 @@ ProcessCoordinatorFactory::_CalculateServerProcessOptions()
uint32 processOptions = 0;
if (ServerSettings::IsClientTooOld()) {
HDINFO("bulk load proceeding without network communications "
"because the client is too old");
HDINFO("bulk load proceeding without network communications because the client is too old");
processOptions |= SERVER_PROCESS_NO_NETWORKING;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_COORDINATOR_FACTORY_H
@@ -26,13 +26,10 @@ public:
static ProcessCoordinator* CreateIncrementViewCounter(Model* model,
const PackageInfoRef package);
static ProcessCoordinator* CreateBulkLoadCoordinator(
PackageInfoListenerRef packageInfoListener,
Model* model, bool forceLocalUpdate);
static ProcessCoordinator* CreateBulkLoadCoordinator(Model* model, bool forceLocalUpdate);
static ProcessCoordinator* CreateUserDetailVerifierCoordinator(
UserDetailVerifierListener*
userDetailVerifierListener,
UserDetailVerifierListener* userDetailVerifierListener,
Model* model);
static ProcessCoordinator* CreatePackageActionCoordinator(
@@ -42,16 +39,15 @@ public:
Model* model, ScreenshotCoordinate& screenshotCoordinate);
static ProcessCoordinator* PopulatePkgChangelogCoordinator(Model* model,
PackageInfoRef package);
const BString& packageName);
static ProcessCoordinator* PopulatePkgUserRatingsCoordinator(Model* model,
PackageInfoRef package);
const BString& packageName);
private:
static uint32 _CalculateServerProcessOptions();
static PackageInfoRef _ExtractPackageFromMessage(
Model* model, BMessage* message);
static BString _ExtractPackageNameFromMessage(BMessage* message);
static ProcessCoordinator* _CreateInstallPackageActionCoordinator(
Model* model, BMessage* message);
@@ -1,17 +1,18 @@
/*
* Copyright 2017-2023, Andrew Lindesay <[email protected]>.
* Copyright 2017-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractServerProcess.h"
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include <BufferedDataIO.h>
#include <AutoDeleter.h>
#include <BufferedDataIO.h>
#include <File.h>
#include <FileIO.h>
#include <HttpTime.h>
@@ -26,7 +27,6 @@
#include "ServerSettings.h"
#include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h"
#include "LoggingUrlProtocolListener.h"
using namespace BPrivate::Network;
@@ -40,14 +40,173 @@ using namespace BPrivate::Network;
#define TIMEOUT_MICROSECONDS 3e+7
const size_t kFileBufferSize = 10 * 1024;
static const size_t kFileBufferSize = 10 * 1024;
// #pragma mark - Interface ReadProgressDataIO
class ReadProgressDataIO : public BDataIO
{
public:
ReadProgressDataIO(AbstractServerProcess* serverProcess,
BDataIO* delegate, size_t bytesTotal);
virtual ~ReadProgressDataIO();
virtual ssize_t Read(void* buffer, size_t size);
virtual ssize_t Write(const void* buffer, size_t size);
virtual status_t Flush();
private:
AbstractServerProcess*
fServerProcess;
BDataIO* fDelegate;
size_t fBytesRead;
size_t fBytesTotal;
};
// #pragma mark - Interface LoggingUrlProtocolListener
class LoggingUrlProtocolListener : public BUrlProtocolListener
{
public:
LoggingUrlProtocolListener(AbstractServerProcess* serverProcess,
BString traceLoggingIdentifier, bool traceLogging);
virtual ~LoggingUrlProtocolListener();
size_t ContentLength();
virtual void DownloadProgress(BUrlRequest* caller,
off_t bytesReceived, off_t bytesTotal);
virtual void BytesWritten(BUrlRequest* caller,
size_t bytesWritten);
virtual void DebugMessage(BUrlRequest* caller,
BUrlProtocolDebugMessage type,
const char* text);
private:
AbstractServerProcess*
fServerProcess;
bool fTraceLogging;
BString fTraceLoggingIdentifier;
size_t fContentLength;
};
// #pragma mark - Implementation for ReadProgressDataIO
ReadProgressDataIO::ReadProgressDataIO(AbstractServerProcess* serverProcess, BDataIO* delegate,
size_t bytesTotal)
:
fServerProcess(serverProcess),
fDelegate(delegate),
fBytesRead(0),
fBytesTotal(bytesTotal)
{
}
ReadProgressDataIO::~ReadProgressDataIO()
{
}
ssize_t
ReadProgressDataIO::Read(void* buffer, size_t size)
{
ssize_t actualRead = fDelegate->Read(buffer, size);
if (actualRead > 0) {
fBytesRead += actualRead;
if (fBytesTotal > 0.0f) {
float progress = static_cast<float>(fBytesRead) / static_cast<float>(fBytesTotal);
fServerProcess->SetDataProcessingProgress(progress);
}
}
return actualRead;
}
ssize_t
ReadProgressDataIO::Write(const void* buffer, size_t size)
{
return B_NOT_SUPPORTED;
}
status_t
ReadProgressDataIO::Flush()
{
return B_OK;
}
// #pragma mark - Implementation for LoggingUrlProtocolListener
LoggingUrlProtocolListener::LoggingUrlProtocolListener(AbstractServerProcess* serverProcess,
BString traceLoggingIdentifier, bool traceLogging)
:
fServerProcess(serverProcess),
fTraceLogging(traceLogging),
fTraceLoggingIdentifier(traceLoggingIdentifier),
fContentLength(0)
{
}
LoggingUrlProtocolListener::~LoggingUrlProtocolListener()
{
}
void
LoggingUrlProtocolListener::BytesWritten(BUrlRequest* caller, size_t bytesWritten)
{
fContentLength += bytesWritten;
}
void
LoggingUrlProtocolListener::DownloadProgress(BUrlRequest* caller, off_t bytesReceived,
off_t bytesTotal)
{
float progress = static_cast<float>(bytesReceived) / static_cast<float>(bytesTotal);
fServerProcess->SetDownloadProgress(progress);
}
void
LoggingUrlProtocolListener::DebugMessage(BUrlRequest* caller, BUrlProtocolDebugMessage type,
const char* text)
{
HDTRACE("url->file <%s>; %s", fTraceLoggingIdentifier.String(), text);
}
size_t
LoggingUrlProtocolListener::ContentLength()
{
return fContentLength;
}
// #pragma mark - AbstractServerProcess
AbstractServerProcess::AbstractServerProcess(uint32 options)
:
AbstractProcess(),
fOptions(options),
fRequest(NULL)
fRequest(NULL),
fDownloadProgress(kProgressIndeterminate),
fDataProcessingProgress(kProgressIndeterminate)
{
}
@@ -57,6 +216,44 @@ AbstractServerProcess::~AbstractServerProcess()
}
float
AbstractServerProcess::Progress()
{
if (fDataProcessingProgress >= 0.0f)
return 0.5f + (0.5f * fminf(fDataProcessingProgress, 1.0));
if (fDownloadProgress >= 0.0f)
return 0.5f * fminf(fDownloadProgress, 1.0);
return kProgressIndeterminate;
}
void
AbstractServerProcess::SetDownloadProgress(float value)
{
if (fDownloadProgress == 1.0 || (!_ShouldProcessProgress() && value != 1.0))
return;
if (fDataProcessingProgress < 0.0f)
fDownloadProgress = value;
else
fDownloadProgress = 1.0;
_NotifyChanged();
}
void
AbstractServerProcess::SetDataProcessingProgress(float value)
{
if (fDataProcessingProgress == 1.0 || (!_ShouldProcessProgress() && value != 1.0))
return;
fDownloadProgress = 1.0;
fDataProcessingProgress = value;
_NotifyChanged();
}
bool
AbstractServerProcess::HasOption(uint32 flag)
{
@@ -67,8 +264,7 @@ AbstractServerProcess::HasOption(uint32 flag)
bool
AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready)
{
return
!HasOption(SERVER_PROCESS_NO_NETWORKING)
return !HasOption(SERVER_PROCESS_NO_NETWORKING)
&& !(HasOption(SERVER_PROCESS_PREFER_CACHE) && hasDataAlready);
}
@@ -76,16 +272,15 @@ AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready)
status_t
AbstractServerProcess::StopInternal()
{
if (fRequest != NULL) {
if (fRequest != NULL)
return fRequest->Stop();
}
return AbstractProcess::StopInternal();
}
status_t
AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue) const
AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue)
{
BPath metaDataPath;
BString jsonPath;
@@ -102,15 +297,15 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue) const
status_t
AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue,
const BPath& metaDataPath, const BString& jsonPath) const
AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue, const BPath& metaDataPath,
const BString& jsonPath)
{
headerValue.SetTo("");
struct stat s;
if (-1 == stat(metaDataPath.Path(), &s)) {
if (ENOENT != errno)
return B_ERROR;
return B_ERROR;
return B_ENTRY_NOT_FOUND;
}
@@ -121,11 +316,11 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue,
StandardMetaData metaData;
status_t result = PopulateMetaData(metaData, metaDataPath, jsonPath);
if (result == B_OK)
if (result == B_OK) {
SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData);
else {
HDERROR("unable to parse the meta-data date and time from [%s]"
" - cannot set the 'If-Modified-Since' header",
} else {
HDERROR("unable to parse the meta-data date and time from [%s] - cannot set the "
"'If-Modified-Since' header",
metaDataPath.Path());
}
@@ -134,23 +329,19 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue,
/*static*/ void
AbstractServerProcess::SetIfModifiedSinceHeaderValueFromMetaData(
BString& headerValue,
AbstractServerProcess::SetIfModifiedSinceHeaderValueFromMetaData(BString& headerValue,
const StandardMetaData& metaData)
{
// An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000'
BDateTime modifiedDateTime = metaData
.GetDataModifiedTimestampAsDateTime();
BDateTime modifiedDateTime = metaData.GetDataModifiedTimestampAsDateTime();
BHttpTime modifiedHttpTime(modifiedDateTime);
headerValue.SetTo(modifiedHttpTime
.ToString(B_HTTP_TIME_FORMAT_COOKIE));
headerValue.SetTo(modifiedHttpTime.ToString(B_HTTP_TIME_FORMAT_COOKIE));
}
status_t
AbstractServerProcess::PopulateMetaData(
StandardMetaData& metaData, const BPath& path,
const BString& jsonPath) const
AbstractServerProcess::PopulateMetaData(StandardMetaData& metaData, const BPath& path,
const BString& jsonPath)
{
StandardMetaDataJsonEventListener listener(jsonPath, metaData);
status_t result = ParseJsonFromFileWithListener(&listener, path);
@@ -164,8 +355,7 @@ AbstractServerProcess::PopulateMetaData(
return result;
if (!metaData.IsPopulated()) {
HDERROR("the meta data was read from [%s], but no values "
"were extracted", path.Path());
HDERROR("the meta data was read from [%s], but no values were extracted", path.Path());
return B_BAD_DATA;
}
@@ -174,7 +364,7 @@ AbstractServerProcess::PopulateMetaData(
/* static */ bool
AbstractServerProcess::_LooksLikeGzip(const char *pathStr)
AbstractServerProcess::_LooksLikeGzip(const char* pathStr)
{
int l = strlen(pathStr);
return l > 4 && 0 == strncmp(&pathStr[l - 3], ".gz", 3);
@@ -187,36 +377,53 @@ AbstractServerProcess::_LooksLikeGzip(const char *pathStr)
*/
status_t
AbstractServerProcess::ParseJsonFromFileWithListener(
BJsonEventListener *listener,
const BPath& path) const
AbstractServerProcess::ParseJsonFromFileWithListener(BJsonEventListener* listener,
const BPath& path)
{
const char* pathStr = path.Path();
bool fileExists = false;
off_t fileSize = 0;
status_t existsResult = StorageUtils::ExistsObject(path, &fileExists, NULL, &fileSize);
if (existsResult != B_OK) {
HDERROR("[%s] unable to get the length of the data file [%s]", Name(), pathStr);
return existsResult;
}
if (!fileExists) {
HDERROR("[%s] missing data file [%s]", Name(), pathStr);
return B_ENTRY_NOT_FOUND;
}
if (fileSize == 0) {
HDERROR("[%s] empty data file [%s]", Name(), pathStr);
return B_BAD_DATA;
}
FILE* file = fopen(pathStr, "rb");
if (file == NULL) {
HDERROR("[%s] unable to find the meta data file at [%s]", Name(),
path.Path());
HDERROR("[%s] unable to find the data file at [%s]", Name(), pathStr);
return B_ENTRY_NOT_FOUND;
}
BFileIO rawInput(file, true); // takes ownership
BDataIO* bufferedRawInput = new BBufferedDataIO(rawInput, kFileBufferSize, false, true);
ReadProgressDataIO readProgress(this, &rawInput, fileSize);
BDataIO* bufferedRawInput = new BBufferedDataIO(readProgress, kFileBufferSize, false, true);
ObjectDeleter<BDataIO> bufferedRawInputDeleter(bufferedRawInput);
// if the file extension ends with '.gz' then the data will be
// compressed and the algorithm needs to decompress the data as
// it is parsed.
// if the file extension ends with '.gz' then the data will be
// compressed and the algorithm needs to decompress the data as
// it is parsed.
if (_LooksLikeGzip(pathStr)) {
BDataIO* gzDecompressedInput = NULL;
BZlibDecompressionParameters* zlibDecompressionParameters
= new BZlibDecompressionParameters();
status_t result = BZlibCompressionAlgorithm()
.CreateDecompressingInputStream(bufferedRawInput,
zlibDecompressionParameters, gzDecompressedInput);
status_t result = BZlibCompressionAlgorithm().CreateDecompressingInputStream(
bufferedRawInput, zlibDecompressionParameters, gzDecompressedInput);
if (B_OK != result)
return result;
@@ -231,49 +438,47 @@ AbstractServerProcess::ParseJsonFromFileWithListener(
}
/*! In order to reduce the chance of failure half way through downloading a
large file, this method will download the file to a temporary file and
then it can rename the file to the final target file.
/*! In order to reduce the chance of failure half way through downloading a
large file, this method will download the file to a temporary file and
then it can rename the file to the final target file.
*/
status_t
AbstractServerProcess::DownloadToLocalFileAtomically(
const BPath& targetFilePath,
const BUrl& url)
AbstractServerProcess::DownloadToLocalFileAtomically(const BPath& targetFilePath, const BUrl& url)
{
BPath temporaryFilePath(tmpnam(NULL), NULL, true);
status_t result = DownloadToLocalFile(
temporaryFilePath, url, 0, 0);
status_t result = DownloadToLocalFile(temporaryFilePath, url, 0, 0);
// if the data is coming in as .gz, but is not stored as .gz then
// the data should be decompressed in the temporary file location
// before being shifted into place.
if (result == B_OK
&& _LooksLikeGzip(url.Path())
&& !_LooksLikeGzip(targetFilePath.Path()))
if (result == B_OK && _LooksLikeGzip(url.Path()) && !_LooksLikeGzip(targetFilePath.Path()))
result = _DeGzipInSitu(temporaryFilePath);
// not copying if the data has not changed because the data will be
// zero length. This is if the result is APP_ERR_NOT_MODIFIED.
// not copying if the data has not changed because the data will be
// zero length. This is if the result is APP_ERR_NOT_MODIFIED.
if (result == B_OK) {
// if the file is zero length then assume that something has
// gone wrong.
// if the file is zero length then assume that something has
// gone wrong.
off_t size;
bool hasFile;
result = StorageUtils::ExistsObject(temporaryFilePath, &hasFile, NULL,
&size);
result = StorageUtils::ExistsObject(temporaryFilePath, &hasFile, NULL, &size);
if (result == B_OK && hasFile && size > 0) {
if (rename(temporaryFilePath.Path(), targetFilePath.Path()) != 0) {
HDINFO("[%s] did rename [%s] --> [%s]",
Name(), temporaryFilePath.Path(), targetFilePath.Path());
HDINFO("[%s] did rename [%s] --> [%s]", Name(), temporaryFilePath.Path(),
targetFilePath.Path());
result = B_IO_ERROR;
}
}
}
if (IsSuccess(result))
SetDownloadProgress(1.0);
return result;
}
@@ -292,13 +497,11 @@ AbstractServerProcess::_DeGzipInSitu(const BPath& path)
BZlibDecompressionParameters* zlibDecompressionParameters
= new BZlibDecompressionParameters();
result = BZlibCompressionAlgorithm()
.CreateDecompressingInputStream(&file,
zlibDecompressionParameters, gzDecompressedInput);
result = BZlibCompressionAlgorithm().CreateDecompressingInputStream(&file,
zlibDecompressionParameters, gzDecompressedInput);
if (result == B_OK) {
ObjectDeleter<BDataIO> gzDecompressedInputDeleter(
gzDecompressedInput);
ObjectDeleter<BDataIO> gzDecompressedInputDeleter(gzDecompressedInput);
result = DataIOUtils::CopyAll(&tmpFile, gzDecompressedInput);
}
}
@@ -308,8 +511,7 @@ AbstractServerProcess::_DeGzipInSitu(const BPath& path)
HDERROR("unable to move the uncompressed data into place");
result = B_ERROR;
}
}
else {
} else {
HDERROR("it was not possible to decompress the data");
}
@@ -318,15 +520,14 @@ AbstractServerProcess::_DeGzipInSitu(const BPath& path)
status_t
AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
const BUrl& url, uint32 redirects, uint32 failures)
AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, const BUrl& url,
uint32 redirects, uint32 failures)
{
if (WasStopped())
return B_CANCELED;
if (redirects > MAX_REDIRECTS) {
HDINFO("[%s] exceeded %d redirects --> failure", Name(),
MAX_REDIRECTS);
HDINFO("[%s] exceeded %d redirects --> failure", Name(), MAX_REDIRECTS);
return B_IO_ERROR;
}
@@ -338,7 +539,7 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
HDINFO("[%s] will stream '%s' to [%s]", Name(), url.UrlString().String(),
targetFilePath.Path());
LoggingUrlProtocolListener listener(Name(), Logger::IsTraceEnabled());
LoggingUrlProtocolListener listener(this, Name(), Logger::IsTraceEnabled());
BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT);
status_t err = targetFile.InitCheck();
if (err != B_OK)
@@ -348,26 +549,23 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
ServerSettings::AugmentHeaders(headers);
BString ifModifiedSinceHeader;
status_t ifModifiedSinceHeaderStatus = IfModifiedSinceHeaderValue(
ifModifiedSinceHeader);
status_t ifModifiedSinceHeaderStatus = IfModifiedSinceHeaderValue(ifModifiedSinceHeader);
if (ifModifiedSinceHeaderStatus == B_OK &&
ifModifiedSinceHeader.Length() > 0) {
if (ifModifiedSinceHeaderStatus == B_OK && ifModifiedSinceHeader.Length() > 0)
headers.AddHeader("If-Modified-Since", ifModifiedSinceHeader);
}
thread_id thread;
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, &targetFile,
&listener);
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, &targetFile, &listener);
if (request == NULL)
return B_NO_MEMORY;
fRequest = dynamic_cast<BHttpRequest *>(request);
fRequest = dynamic_cast<BHttpRequest*>(request);
if (fRequest == NULL) {
delete request;
return B_ERROR;
}
fRequest->SetHeaders(headers);
fRequest->SetMaxRedirections(0);
fRequest->SetTimeout(TIMEOUT_MICROSECONDS);
@@ -376,11 +574,10 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>(
fRequest->Result());
const BHttpResult& result = dynamic_cast<const BHttpResult&>(fRequest->Result());
int32 statusCode = result.StatusCode();
const BHttpHeaders responseHeaders = result.Headers();
const char *locationC = responseHeaders["Location"];
const char* locationC = responseHeaders["Location"];
BString location;
if (locationC != NULL)
@@ -415,8 +612,7 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
return DownloadToLocalFile(targetFilePath, url, redirects, failures + 1);
}
HDERROR("[%s] unexpected response from server [%" B_PRId32 "]",
Name(), statusCode);
HDERROR("[%s] unexpected response from server [%" B_PRId32 "]", Name(), statusCode);
return B_IO_ERROR;
}
}
@@ -433,9 +629,9 @@ AbstractServerProcess::DeleteLocalFile(const BPath& currentFilePath)
/*! When a file is damaged or corrupted in some way, the file should be 'moved
aside' so that it is not involved in the next update. This method will
create such an alternative 'damaged' file location and move this file to
that location.
aside' so that it is not involved in the next update. This method will
create such an alternative 'damaged' file location and move this file to
that location.
*/
status_t
@@ -449,19 +645,20 @@ AbstractServerProcess::MoveDamagedFileAside(const BPath& currentFilePath)
damagedFilePath.Append(damagedLeaf.String());
if (0 != rename(currentFilePath.Path(), damagedFilePath.Path())) {
HDERROR("[%s] unable to move damaged file [%s] aside to [%s]",
Name(), currentFilePath.Path(), damagedFilePath.Path());
HDERROR("[%s] unable to move damaged file [%s] aside to [%s]", Name(),
currentFilePath.Path(), damagedFilePath.Path());
return B_IO_ERROR;
}
HDINFO("[%s] did move damaged file [%s] aside to [%s]",
Name(), currentFilePath.Path(), damagedFilePath.Path());
HDINFO("[%s] did move damaged file [%s] aside to [%s]", Name(), currentFilePath.Path(),
damagedFilePath.Path());
return B_OK;
}
bool
AbstractServerProcess::IsSuccess(status_t e) {
AbstractServerProcess::IsSuccess(status_t e)
{
return e == B_OK || e == HD_ERR_NOT_MODIFIED;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020, Andrew Lindesay <[email protected]>.
* Copyright 2017-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_SERVER_PROCESS_H
@@ -34,6 +34,11 @@ public:
AbstractServerProcess(uint32 options);
virtual ~AbstractServerProcess();
virtual float Progress();
void SetDownloadProgress(float value);
void SetDataProcessingProgress(float value);
protected:
virtual status_t GetStandardMetaDataPath(
BPath& path) const = 0;
@@ -41,11 +46,11 @@ protected:
BString& jsonPath) const = 0;
virtual status_t IfModifiedSinceHeaderValue(
BString& headerValue) const;
BString& headerValue);
status_t IfModifiedSinceHeaderValue(
BString& headerValue,
const BPath& metaDataPath,
const BString& jsonPath) const;
const BString& jsonPath);
static void SetIfModifiedSinceHeaderValueFromMetaData(
BString& headerValue,
const StandardMetaData& metaData);
@@ -53,11 +58,11 @@ protected:
status_t PopulateMetaData(
StandardMetaData& metaData,
const BPath& path,
const BString& jsonPath) const;
const BString& jsonPath);
status_t ParseJsonFromFileWithListener(
BJsonEventListener *listener,
const BPath& path) const;
const BPath& path);
virtual status_t DownloadToLocalFileAtomically(
const BPath& targetFilePath,
@@ -81,13 +86,16 @@ private:
const BUrl& url,
uint32 redirects, uint32 failures);
static bool _LooksLikeGzip(const char *pathStr);
static status_t _DeGzipInSitu(const BPath& path);
static bool _LooksLikeGzip(const char *pathStr);
static status_t _DeGzipInSitu(const BPath& path);
private:
uint32 fOptions;
BHttpRequest* fRequest;
float fDownloadProgress;
float fDataProcessingProgress;
};
#endif // ABSTRACT_SERVER_PROCESS_H
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022, Andrew Lindesay <[email protected]>.
* Copyright 2017-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -16,12 +16,11 @@
#include "StorageUtils.h"
AbstractSingleFileServerProcess::AbstractSingleFileServerProcess(
uint32 options)
AbstractSingleFileServerProcess::AbstractSingleFileServerProcess(uint32 options)
:
AbstractServerProcess(options),
fDownloadDurationSeconds(0.0),
fProcessLocalDataDurationSeconds(0.0)
fProcessLocalDataDurationSeconds(0.0)
{
}
@@ -56,25 +55,25 @@ AbstractSingleFileServerProcess::RunInternal()
if (IsSuccess(result) && ShouldAttemptNetworkDownload(hasData)) {
BStopWatch stopWatch("download", true);
result = DownloadToLocalFileAtomically(
localPath,
result = DownloadToLocalFileAtomically(localPath,
ServerSettings::CreateFullUrl(urlPathComponent));
fDownloadDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0);
fDownloadDurationSeconds = ((double)stopWatch.ElapsedTime() / 1000000.0);
if (!IsSuccess(result)) {
if (hasData) {
HDINFO("[%s] failed to update data, but have old data "
"anyway so carry on with that", Name());
HDINFO("[%s] failed to update data, but have old data anyway so carry on with that",
Name());
result = B_OK;
} else
} else {
HDERROR("[%s] failed to obtain data", Name());
} else
}
} else {
HDINFO("[%s] did fetch data", Name());
}
}
if (IsSuccess(result)) {
status_t hasDataResult = StorageUtils::ExistsObject(
localPath, &hasData, NULL, &size);
status_t hasDataResult = StorageUtils::ExistsObject(localPath, &hasData, NULL, &size);
hasData = hasData && size > 0;
@@ -89,7 +88,7 @@ AbstractSingleFileServerProcess::RunInternal()
BStopWatch stopWatch("process local data", true);
result = ProcessLocalData();
fProcessLocalDataDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0);
fProcessLocalDataDurationSeconds = ((double)stopWatch.ElapsedTime() / 1000000.0);
switch (result) {
case B_OK:
@@ -124,8 +123,7 @@ AbstractSingleFileServerProcess::LogReport()
if (ProcessState() == PROCESS_COMPLETE) {
BString downloadLogLine;
BString localDataLogLine;
downloadLogLine.SetToFormat("\n - download %6.3f",
fDownloadDurationSeconds);
downloadLogLine.SetToFormat("\n - download %6.3f", fDownloadDurationSeconds);
localDataLogLine.SetToFormat("\n - process local data %6.3f",
fProcessLocalDataDurationSeconds);
result.Append(downloadLogLine);
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023, Andrew Lindesay <[email protected]>.
* Copyright 2021-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "IncrementViewCounterProcess.h"
@@ -20,14 +20,14 @@
#define B_TRANSLATION_CONTEXT "IncrementViewCounterProcess"
IncrementViewCounterProcess::IncrementViewCounterProcess(
Model* model, const PackageInfoRef& package)
IncrementViewCounterProcess::IncrementViewCounterProcess(Model* model,
const PackageInfoRef& package)
:
fPackage(package),
fModel(model)
{
fDescription = BString(B_TRANSLATE("Recording view of \"%PackageName%\""))
.ReplaceAll("%PackageName%", fPackage->Name());
.ReplaceAll("%PackageName%", fPackage->Name());
}
@@ -77,8 +77,8 @@ IncrementViewCounterProcess::RunInternal()
while (attempts > 0 && !WasStopped()) {
BMessage resultEnvelope;
WebAppInterface* webAppInterface = fModel->GetWebAppInterface();
result = webAppInterface->IncrementViewCounter(fPackage, depot, resultEnvelope);
WebAppInterfaceRef webApp = fModel->WebApp();
result = webApp->IncrementViewCounter(fPackage, depot, resultEnvelope);
if (result == B_OK) {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(resultEnvelope);
@@ -97,8 +97,9 @@ IncrementViewCounterProcess::RunInternal()
result = B_ERROR;
break;
}
} else
} else {
HDERROR("an error has arisen incrementing the view counter");
}
attempts--;
_SpinBetweenAttempts();
@@ -115,4 +116,3 @@ IncrementViewCounterProcess::_SpinBetweenAttempts()
for (int32 i = 0; i < 10 && !WasStopped(); i++)
usleep(miniSpinDelays);
}
File diff suppressed because it is too large Load Diff
@@ -1,11 +1,13 @@
/*
* Copyright 2018-2022, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LOCAL_PKG_DATA_LOAD_PROCESS__H
#define LOCAL_PKG_DATA_LOAD_PROCESS__H
#include <map>
#include "AbstractProcess.h"
#include "Model.h"
@@ -29,9 +31,7 @@ class PkgDataLoadState;
class LocalPkgDataLoadProcess : public AbstractProcess {
public:
LocalPkgDataLoadProcess(
PackageInfoListenerRef packageInfoListener,
Model *model, bool force = false);
LocalPkgDataLoadProcess(Model *model, bool force = false);
virtual ~LocalPkgDataLoadProcess();
const char* Name() const;
@@ -41,14 +41,11 @@ protected:
virtual status_t RunInternal();
private:
void _NotifyError(const BString& messageText) const;
off_t _DeriveSize(const PackageInfoRef modelInfo) const;
static void _NotifyError(const BString& messageText);
private:
Model* fModel;
bool fForce;
PackageInfoListenerRef
fPackageInfoListener;
};
#endif // LOCAL_PKG_DATA_LOAD_PROCESS__H
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024, Andrew Lindesay <[email protected]>.
* Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Note that this file included code earlier from `Model.cpp` and
@@ -21,10 +21,10 @@
PopulatePkgChangelogFromServerProcess::PopulatePkgChangelogFromServerProcess(
PackageInfoRef packageInfo, Model* model)
const BString& packageName, Model* model)
:
fModel(model),
fPackageInfo(packageInfo)
fPackageName(packageName)
{
}
@@ -54,10 +54,9 @@ PopulatePkgChangelogFromServerProcess::RunInternal()
// TODO; use API spec to code generation techniques instead of this manually written client.
BMessage responsePayload;
BString packageName = fPackageInfo->Name();
status_t result;
result = fModel->GetWebAppInterface()->GetChangelog(packageName, responsePayload);
result = fModel->WebApp()->GetChangelog(fPackageName, responsePayload);
if (result == B_OK) {
BMessage resultMessage;
@@ -67,31 +66,11 @@ PopulatePkgChangelogFromServerProcess::RunInternal()
if (result == B_OK) {
if (resultMessage.FindString("content", &content) == B_OK) {
content.Trim();
PackageLocalizedTextRef localizedText = fPackageInfo->LocalizedText();
if (localizedText.IsSet()) {
localizedText = PackageLocalizedTextRef(
new PackageLocalizedText(*(localizedText.Get())), true);
} else {
localizedText = PackageLocalizedTextRef(new PackageLocalizedText(), true);
}
localizedText->SetChangelog(content);
// TODO; later work to make the `PackageInfo` immutable to avoid this locking
BAutolock locker(fModel->Lock());
fPackageInfo->SetLocalizedText(localizedText);
HDDEBUG("changelog populated for [%s]", packageName.String());
result = _UpdateChangelog(content.Trim());
HDDEBUG("changelog populated for [%s]", fPackageName.String());
} else {
HDDEBUG("no changelog present for [%s]", packageName.String());
HDDEBUG("no changelog present for [%s]", fPackageName.String());
}
} else {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(responsePayload);
if (errorCode != ERROR_CODE_NONE)
ServerHelper::NotifyServerJsonRpcError(responsePayload);
}
} else {
ServerHelper::NotifyTransportError(result);
@@ -99,3 +78,25 @@ PopulatePkgChangelogFromServerProcess::RunInternal()
return result;
}
status_t
PopulatePkgChangelogFromServerProcess::_UpdateChangelog(const BString& value)
{
PackageInfoRef package = fModel->PackageForName(fPackageName);
if (!package.IsSet()) {
HDERROR("the package [%s] was not found", fPackageName.String());
return B_ERROR;
}
PackageLocalizedTextRef localizedText
= PackageLocalizedTextBuilder(package->LocalizedText()).WithChangelog(value).BuildRef();
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalizedText(localizedText).BuildRef();
fModel->AddPackage(updatedPackage);
return B_OK;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -31,7 +31,7 @@ class PkgDataLoadState;
class PopulatePkgChangelogFromServerProcess : public AbstractProcess
{
public:
PopulatePkgChangelogFromServerProcess(PackageInfoRef packageInfo,
PopulatePkgChangelogFromServerProcess(const BString& packageName,
Model *model);
virtual ~PopulatePkgChangelogFromServerProcess();
@@ -41,9 +41,12 @@ public:
protected:
virtual status_t RunInternal();
private:
status_t _UpdateChangelog(const BString& value);
private:
Model* fModel;
PackageInfoRef fPackageInfo;
BString fPackageName;
};
#endif // POPULATE_PKG_CHANGELOG_FROM_SERVER_PROCESS__H
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>.
* Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*
@@ -23,7 +23,8 @@
PopulatePkgSizesProcess::PopulatePkgSizesProcess(Model* model)
:
fModel(model)
fModel(model),
fProgress(0)
{
}
@@ -47,55 +48,97 @@ PopulatePkgSizesProcess::Description() const
}
float
PopulatePkgSizesProcess::Progress()
{
return fProgress;
}
status_t
PopulatePkgSizesProcess::RunInternal()
{
int32 countPkgs = 0;
int32 countPkgs;
int32 countPkgSized = 0;
int32 countPkgUnsized = 0;
HDINFO("[%s] will populate size for pkgs without a size", Name());
for (int32 d = 0; d < fModel->CountDepots() && !WasStopped(); d++) {
DepotInfoRef depotInfo = fModel->DepotAtIndex(d);
countPkgs += depotInfo->CountPackages();
const std::vector<PackageInfoRef> packages = _SnapshotOfPackages();
std::vector<PackageInfoRef> updatedPackages;
for (int32 p = 0; p < depotInfo->CountPackages(); p++) {
PackageInfoRef packageInfo = depotInfo->PackageAtIndex(p);
countPkgs = static_cast<int32>(packages.size());
if (!packageInfo.IsSet())
HDFATAL("package is not set");
for (int32 i = 0; i < countPkgs; i++) {
PackageInfoRef package = packages[i];
const char* packageName = package->Name().String();
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(packageInfo);
PackageState state = localInfo->State();
if (!package.IsSet())
HDFATAL("package is not set");
if (localInfo->Size() <= 0 && (state == ACTIVATED || state == INSTALLED)) {
off_t derivedSize = _DeriveSize(packageInfo);
PackageLocalInfoRef localInfo = package->LocalInfo();
if (derivedSize > 0) {
localInfo->SetSize(derivedSize);
countPkgSized++;
HDDEBUG("[%s] did derive a size for package [%s]", Name(),
packageInfo->Name().String());
} else {
countPkgUnsized++;
HDDEBUG("[%s] unable to derive a size for package [%s]", Name(),
packageInfo->Name().String());
}
if (_ShouldDeriveSize(localInfo)) {
off_t derivedSize = _DeriveSize(package);
if (derivedSize > 0) {
PackageLocalInfoBuilder localInfoBuilder;
if (localInfo.IsSet())
localInfoBuilder = PackageLocalInfoBuilder(localInfo);
localInfoBuilder.WithSize(derivedSize);
PackageLocalInfoRef localInfo = localInfoBuilder.BuildRef();
updatedPackages.push_back(
PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef());
countPkgSized++;
HDDEBUG("[%s] did derive a size for package [%s]", Name(), packageName);
} else {
countPkgUnsized++;
HDDEBUG("[%s] unable to derive a size for package [%s]", Name(), packageName);
}
packageInfo->SetLocalInfo(localInfo);
}
_SetProgress(static_cast<float>(i) / static_cast<float>(countPkgs));
}
fModel->AddPackagesWithChange(updatedPackages, PKG_CHANGED_LOCAL_INFO);
_SetProgress(1.0);
HDINFO("[%s] did populate size for %" B_PRId32 " packages with %" B_PRId32
" already having a size and %" B_PRId32 " unable to derive a size",
Name(), countPkgSized, countPkgs - countPkgSized, countPkgUnsized);
" already having a size and %" B_PRId32 " unable to derive a size",
Name(), countPkgSized, countPkgs - (countPkgSized + countPkgUnsized), countPkgUnsized);
return B_OK;
}
const std::vector<PackageInfoRef>
PopulatePkgSizesProcess::_SnapshotOfPackages() const
{
return fModel->Packages();
}
bool
PopulatePkgSizesProcess::_ShouldDeriveSize(PackageLocalInfoRef localInfo) const
{
if (!localInfo.IsSet())
return true;
if (localInfo->Size() > 0)
return false;
PackageState state = localInfo->State();
return state == ACTIVATED || state == INSTALLED;
}
off_t
PopulatePkgSizesProcess::_DeriveSize(const PackageInfoRef package) const
{
@@ -111,4 +154,14 @@ PopulatePkgSizesProcess::_DeriveSize(const PackageInfoRef package) const
HDDEBUG("unable to get the local file of package [%s]", package->Name().String());
}
return 0;
}
}
void
PopulatePkgSizesProcess::_SetProgress(float value)
{
if (!_ShouldProcessProgress() && value != 1.0 && (value - fProgress) < 0.1)
return;
fProgress = value;
_NotifyChanged();
}
@@ -1,10 +1,12 @@
/*
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2023-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef POPULATE_PKG_SIZES_PROCESS_H
#define POPULATE_PKG_SIZES_PROCESS_H
#include <vector>
#include <String.h>
#include "AbstractProcess.h"
@@ -23,15 +25,22 @@ public:
virtual const char* Name() const;
virtual const char* Description() const;
virtual float Progress();
protected:
virtual status_t RunInternal();
private:
const std::vector<PackageInfoRef>
_SnapshotOfPackages() const;
bool _ShouldDeriveSize(PackageLocalInfoRef localInfo) const;
off_t _DeriveSize(const PackageInfoRef modelInfo) const;
void _SetProgress(float value);
private:
Model* fModel;
float fProgress;
};
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*
@@ -23,10 +23,10 @@
PopulatePkgUserRatingsFromServerProcess::PopulatePkgUserRatingsFromServerProcess(
PackageInfoRef packageInfo, Model* model)
const BString& packageName, Model* model)
:
fModel(model),
fPackageInfo(packageInfo)
fPackageName(packageName)
{
}
@@ -59,29 +59,21 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
// Retrieve info from web-app
BMessage info;
BString webAppRepositoryCode = _WebAppRepositoryCode();
BString packageName;
BString webAppRepositoryCode;
{
BAutolock locker(&fLock);
packageName = fPackageInfo->Name();
BString depotName = PackageUtils::DepotName(fPackageInfo);
const DepotInfo* depot = fModel->DepotForName(depotName);
if (depot != NULL)
webAppRepositoryCode = depot->WebAppRepositoryCode();
if (webAppRepositoryCode.IsEmpty()) {
HDERROR("unable to get the web app repository code for pkg [%s]", fPackageName.String());
status = B_ERROR;
}
if (status == B_OK) {
status = fModel->GetWebAppInterface()->RetrieveUserRatingsForPackageForDisplay(packageName,
status = fModel->WebApp()->RetrieveUserRatingsForPackageForDisplay(fPackageName,
webAppRepositoryCode, BString(), 0, PACKAGE_INFO_MAX_USER_RATINGS, info);
// ^ note intentionally not using the repository source code as this would then show
// too few results as it would be architecture specific.
}
UserRatingInfoRef userRatingInfo(new UserRatingInfo(), true);
PackageUserRatingInfoBuilder userRatingInfoBuilder;
if (status == B_OK) {
// Parse message
@@ -112,7 +104,7 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
BString user;
BMessage userInfo;
if (item.FindMessage("user", &userInfo) != B_OK
|| userInfo.FindString("nickname", &user) != B_OK) {
|| userInfo.FindString("nickname", &user) != B_OK) {
HDERROR("ignored user rating [%s] without a user nickname", code.String());
continue;
}
@@ -170,15 +162,15 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
UserRatingRef userRating(
new UserRating(UserInfo(user), rating, comment, languageCode,
// note that language identifiers are "code" in HDS and "id" in Haiku
versionString, (uint64) createTimestamp),
versionString, static_cast<uint64>(createTimestamp)),
true);
userRatingInfo->AddUserRating(userRating);
userRatingInfoBuilder.AddUserRating(userRating);
HDDEBUG("rating [%s] retrieved from server", code.String());
}
userRatingInfo->SetUserRatingsPopulated();
userRatingInfoBuilder.WithUserRatingsPopulated();
HDDEBUG("did retrieve %" B_PRIi32 " user ratings for [%s]", index - 1,
packageName.String());
fPackageName.String());
}
} else {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(info);
@@ -197,14 +189,13 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
BMessage summaryResponse;
if (status == B_OK) {
status = fModel->GetWebAppInterface()->RetrieveUserRatingSummaryForPackage(packageName,
status = fModel->WebApp()->RetrieveUserRatingSummaryForPackage(fPackageName,
webAppRepositoryCode, summaryResponse);
}
if (status == B_OK) {
// Parse message
UserRatingSummaryRef userRatingSummary(new UserRatingSummary(), true);
UserRatingSummaryBuilder userRatingSummaryBuilder;
BMessage result;
@@ -214,15 +205,18 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
status = summaryResponse.FindMessage("result", &result);
double sampleSizeF;
int sampleSize = 0;
bool hasData;
if (status == B_OK)
status = result.FindDouble("sampleSize", &sampleSizeF);
if (status == B_OK)
userRatingSummary->SetRatingCount(static_cast<int>(sampleSizeF));
if (status == B_OK) {
sampleSize = static_cast<int>(sampleSizeF);
userRatingSummaryBuilder.WithRatingCount(sampleSize);
}
hasData = status == B_OK && userRatingSummary->RatingCount() > 0;
hasData = status == B_OK && sampleSize > 0;
if (hasData) {
double ratingF;
@@ -231,7 +225,7 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
status = result.FindDouble("rating", &ratingF);
if (status == B_OK)
userRatingSummary->SetAverageRating(ratingF);
userRatingSummaryBuilder.WithAverageRating(ratingF);
}
if (hasData) {
@@ -261,11 +255,12 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
if (status == B_OK)
status = ratingDistributionItem.FindDouble("total", &ratingDistributionTotalF);
userRatingSummary->SetRatingByStar(static_cast<int>(ratingDistributionRatingF),
userRatingSummaryBuilder.AddRatingByStar(
static_cast<int>(ratingDistributionRatingF),
static_cast<int>(ratingDistributionTotalF));
}
userRatingInfo->SetSummary(userRatingSummary);
userRatingInfoBuilder.WithSummary(userRatingSummaryBuilder.BuildRef());
} else {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(summaryResponse);
@@ -277,10 +272,28 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
}
if (status == B_OK) {
// TODO; later make the PackageInfo immutable to avoid the need for locking here.
BAutolock locker(&fLock);
fPackageInfo->SetUserRatingInfo(userRatingInfo);
PackageInfoRef package = fModel->PackageForName(fPackageName);
PackageInfoRef updatedPackage = PackageInfoBuilder(package)
.WithUserRatingInfo(userRatingInfoBuilder.BuildRef())
.BuildRef();
fModel->AddPackage(updatedPackage);
}
return status;
}
const BString
PopulatePkgUserRatingsFromServerProcess::_WebAppRepositoryCode() const
{
const PackageInfoRef package = fModel->PackageForName(fPackageName);
const BString depotName = PackageUtils::DepotName(package);
const DepotInfoRef depot = fModel->DepotForName(depotName);
if (depot.IsSet())
return depot->WebAppRepositoryCode();
return BString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2024-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef POPULATE_PKG_USER_RATINGS_FROM_SERVER_PROCESS__H
@@ -29,7 +29,7 @@ class PkgDataLoadState;
class PopulatePkgUserRatingsFromServerProcess : public AbstractProcess
{
public:
PopulatePkgUserRatingsFromServerProcess(PackageInfoRef packageInfo,
PopulatePkgUserRatingsFromServerProcess(const BString& packageName,
Model *model);
virtual ~PopulatePkgUserRatingsFromServerProcess();
@@ -39,9 +39,12 @@ public:
protected:
virtual status_t RunInternal();
private:
const BString _WebAppRepositoryCode() const;
private:
Model* fModel;
PackageInfoRef fPackageInfo;
BString fPackageName;
};
#endif // POPULATE_PKG_USER_RATINGS_FROM_SERVER_PROCESS__H
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerIconExportUpdateProcess.h"
@@ -8,13 +8,14 @@
#include <time.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <BufferIO.h>
#include <Catalog.h>
#include <FileIO.h>
#include "DataIOUtils.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "PackageIconTarRepository.h"
#include "ServerHelper.h"
#include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h"
@@ -55,6 +56,7 @@ InfoJsonExtractEntryListener::InfoJsonExtractEntryListener()
{
}
InfoJsonExtractEntryListener::~InfoJsonExtractEntryListener()
{
}
@@ -68,14 +70,12 @@ InfoJsonExtractEntryListener::GetInfoJsonData()
status_t
InfoJsonExtractEntryListener::Handle( const TarArchiveHeader& header,
size_t offset, BDataIO *data)
InfoJsonExtractEntryListener::Handle(const TarArchiveHeader& header, size_t offset, BDataIO* data)
{
if (header.Length() > 0 && header.FileName() == ENTRY_PATH_METADATA) {
status_t copyResult = DataIOUtils::CopyAll(&fInfoJsonData, data);
if (copyResult == B_OK) {
HDINFO("[InfoJsonExtractEntryListener] did extract [%s]",
ENTRY_PATH_METADATA);
HDINFO("[InfoJsonExtractEntryListener] did extract [%s]", ENTRY_PATH_METADATA);
fInfoJsonData.Seek(0, SEEK_SET);
return B_CANCELED;
// this will prevent further scanning of the tar file
@@ -88,10 +88,8 @@ InfoJsonExtractEntryListener::Handle( const TarArchiveHeader& header,
/*! This constructor will locate the cached data in a standardized location
*/
ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(
Model* model,
*/
ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(Model* model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
@@ -122,8 +120,20 @@ ServerIconExportUpdateProcess::Description() const
status_t
ServerIconExportUpdateProcess::ProcessLocalData()
{
status_t result = fModel->InitPackageIconRepository();
_NotifyPackagesWithIconsInDepots();
BPath tarPath;
status_t result = GetLocalPath(tarPath);
if (result == B_OK) {
PackageIconTarRepository* tarRepository = new PackageIconTarRepository(tarPath);
result = tarRepository->Init();
if (result == B_OK)
fModel->SetIconRepository(PackageIconRepositoryRef(tarRepository, true));
else
delete tarRepository;
}
return result;
}
@@ -144,7 +154,7 @@ ServerIconExportUpdateProcess::GetLocalPath(BPath& path) const
*/
status_t
ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue) const
ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
{
headerValue.SetTo("");
@@ -164,13 +174,13 @@ ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
}
if (result == B_OK) {
BFile *tarIo = new BFile(tarPath.Path(), O_RDONLY);
ObjectDeleter<BFile> tarIoDeleter(tarIo);
InfoJsonExtractEntryListener* extractDataListener
= new InfoJsonExtractEntryListener();
ObjectDeleter<InfoJsonExtractEntryListener> extractDataListenerDeleter(
extractDataListener);
result = TarArchiveService::ForEachEntry(*tarIo, extractDataListener);
BFile* tarIo = new BFile(tarPath.Path(), O_RDONLY);
BBufferIO* bufferTarIo = new BBufferIO(tarIo, 10 * 1024, true);
ObjectDeleter<BBufferIO> tarIoDeleter(bufferTarIo);
InfoJsonExtractEntryListener* extractDataListener = new InfoJsonExtractEntryListener();
ObjectDeleter<InfoJsonExtractEntryListener> extractDataListenerDeleter(extractDataListener);
result = TarArchiveService::ForEachEntry(*bufferTarIo, extractDataListener);
if (result == B_CANCELED) {
// the cancellation is expected because it will cancel when it finds
@@ -181,26 +191,20 @@ ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
StandardMetaData metaData;
BString metaDataJsonPath;
GetStandardMetaDataJsonPath(metaDataJsonPath);
StandardMetaDataJsonEventListener parseInfoJsonListener(
metaDataJsonPath, metaData);
BPrivate::BJson::Parse(
&(extractDataListener->GetInfoJsonData()),
StandardMetaDataJsonEventListener parseInfoJsonListener(metaDataJsonPath, metaData);
BPrivate::BJson::Parse(&(extractDataListener->GetInfoJsonData()),
&parseInfoJsonListener);
result = parseInfoJsonListener.ErrorStatus();
// An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000'
// An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000'
if (result == B_OK) {
SetIfModifiedSinceHeaderValueFromMetaData(
headerValue, metaData);
} else {
HDERROR("[%s] unable to parse the meta data from the tar file",
Name());
}
if (result == B_OK)
SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData);
else
HDERROR("[%s] unable to parse the meta data from the tar file", Name());
} else {
HDERROR("[%s] did not find the metadata [%s] in the tar",
Name(), ENTRY_PATH_METADATA);
HDERROR("[%s] did not find the metadata [%s] in the tar", Name(), ENTRY_PATH_METADATA);
result = B_BAD_DATA;
}
}
@@ -218,8 +222,7 @@ ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void
ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
{
jsonPath.SetTo("$");
// the "$" here indicates that the data is at the top level.
@@ -231,27 +234,3 @@ ServerIconExportUpdateProcess::UrlPathComponent()
{
return "/__pkgicon/all.tar.gz";
}
void
ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepots() const
{
for (int32 d = 0; d < fModel->CountDepots(); d++) {
_NotifyPackagesWithIconsInDepot(fModel->DepotAtIndex(d));
}
}
void
ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepot(
const DepotInfoRef& depot) const
{
PackageIconRepository& packageIconRepository
= fModel->GetPackageIconRepository();
for (int32 p = 0; p < depot->CountPackages(); p++) {
AutoLocker<BLocker> locker(fModel->Lock());
const PackageInfoRef& packageInfoRef = depot->PackageAtIndex(p);
if (packageIconRepository.HasAnyIcon(packageInfoRef->Name()))
packageInfoRef->NotifyChangedIcon();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H
@@ -32,7 +32,7 @@ public:
virtual status_t GetLocalPath(BPath& path) const;
virtual status_t IfModifiedSinceHeaderValue(
BString& headerValue) const;
BString& headerValue);
virtual status_t GetStandardMetaDataPath(BPath& path) const;
@@ -42,11 +42,6 @@ public:
protected:
virtual BString UrlPathComponent();
private:
void _NotifyPackagesWithIconsInDepots() const;
void _NotifyPackagesWithIconsInDepot(
const DepotInfoRef& depotInfo) const;
private:
Model* fModel;
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -33,18 +33,22 @@
#define B_TRANSLATION_CONTEXT "ServerPkgDataUpdateProcess"
/*! This package listener (not at the JSON level) is feeding in the
packages as they are parsed and processing them.
static std::size_t kPackageBatchSize = 100;
static uint32 kPackageChangeMask = PKG_CHANGED_RATINGS | PKG_CHANGED_SCREENSHOTS
| PKG_CHANGED_CLASSIFICATION | PKG_CHANGED_LOCALIZED_TEXT | PKG_CHANGED_LOCAL_INFO
| PKG_CHANGED_CORE_INFO;
/*! This package listener (not at the JSON level) is feeding in the
packages as they are parsed and processing them.
*/
class PackageFillingPkgListener : public DumpExportPkgListener {
public:
PackageFillingPkgListener(Model *model,
BString& depotName, Stoppable* stoppable);
PackageFillingPkgListener(Model *model, Stoppable* stoppable);
virtual ~PackageFillingPkgListener();
virtual bool ConsumePackage(const PackageInfoRef& package,
DumpExportPkg* pkg);
virtual bool Handle(DumpExportPkg* item);
virtual void Complete();
@@ -53,26 +57,36 @@ public:
private:
static ScreenshotInfoRef _CreateScreenshot(DumpExportPkgScreenshot* screenshot);
void _FlushUpdatedPackages();
const PackageInfoRef
_PackageForName(const BString& name) const;
const PackageInfoRef
_CreateUpdatePackage(const PackageInfoRef& package,
const DumpExportPkg* pkg);
void _InitCategories();
private:
BString fDepotName;
Model* fModel;
std::vector<CategoryRef>
std::map<BString, CategoryRef>
fCategories;
std::vector<PackageInfoRef>
fUpdatedPackages;
Stoppable* fStoppable;
uint32 fCount;
bool fDebugEnabled;
};
PackageFillingPkgListener::PackageFillingPkgListener(Model* model,
BString& depotName, Stoppable* stoppable)
PackageFillingPkgListener::PackageFillingPkgListener(Model* model, Stoppable* stoppable)
:
fDepotName(depotName),
fModel(model),
fStoppable(stoppable),
fCount(0),
fDebugEnabled(Logger::IsDebugEnabled())
{
_InitCategories();
}
@@ -81,110 +95,133 @@ PackageFillingPkgListener::~PackageFillingPkgListener()
}
bool
PackageFillingPkgListener::ConsumePackage(const PackageInfoRef& package,
DumpExportPkg* pkg)
void
PackageFillingPkgListener::_InitCategories()
{
std::vector<CategoryRef> categories = fModel->Categories();
std::vector<CategoryRef>::const_iterator it;
for (it = categories.begin(); it != categories.end(); it++) {
const CategoryRef& category = *it;
fCategories[category->Code()] = category;
}
if (fCategories.empty())
HDERROR("there are no categories present");
}
const PackageInfoRef
PackageFillingPkgListener::_PackageForName(const BString& name) const
{
return fModel->PackageForName(name);
}
void
PackageFillingPkgListener::_FlushUpdatedPackages()
{
fModel->AddPackagesWithChange(fUpdatedPackages, kPackageChangeMask);
fUpdatedPackages.clear();
}
/*! This method will produce a new package with the data provided by the
server.
*/
const PackageInfoRef
PackageFillingPkgListener::_CreateUpdatePackage(const PackageInfoRef& package,
const DumpExportPkg* pkg)
{
int32 i;
// Collects all of the changes here into one set of notifications to
// the package's listeners. This way the quantity of BMessages
// communicated back to listeners is considerably reduced. See stop
// invocation later in this method.
PackageClassificationInfoBuilder classificationInfoBuilder(
package->PackageClassificationInfo());
PackageLocalizedTextBuilder localizedTextBuilder(package->LocalizedText());
PackageLocalInfoBuilder localInfoBuilder(package->LocalInfo());
PackageCoreInfoBuilder coreInfoBuilder(package->CoreInfo());
PackageScreenshotInfoBuilder screenshotInfoBuilder;
// don't want to start with the existing data; just take what comes from the server.
PackageUserRatingInfoBuilder userRatingBuilder;
package->StartCollatingChanges();
PackageClassificationInfoRef packageClassificationInfo(new PackageClassificationInfo(), true);
PackageLocalizedTextRef localizedText = PackageUtils::NewLocalizedText(package);
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package);
PackageCoreInfoRef coreInfo = PackageUtils::NewCoreInfo(package);
localizedText->SetHasChangelog(pkg->HasChangelog());
localizedTextBuilder.WithHasChangelog(pkg->HasChangelog());
if (0 != pkg->CountPkgVersions()) {
// this makes the assumption that the only version will be the
// latest one.
// this makes the assumption that the only version will be the
// latest one.
DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0);
if (!pkgVersion->TitleIsNull())
localizedText->SetTitle(*(pkgVersion->Title()));
localizedTextBuilder.WithTitle(*(pkgVersion->Title()));
if (!pkgVersion->SummaryIsNull())
localizedText->SetSummary(*(pkgVersion->Summary()));
localizedTextBuilder.WithSummary(*(pkgVersion->Summary()));
if (!pkgVersion->DescriptionIsNull())
localizedText->SetDescription(*(pkgVersion->Description()));
localizedTextBuilder.WithDescription(*(pkgVersion->Description()));
if (!pkgVersion->PayloadLengthIsNull())
localInfo->SetSize(static_cast<off_t>(pkgVersion->PayloadLength()));
localInfoBuilder.WithSize(static_cast<off_t>(pkgVersion->PayloadLength()));
if (!pkgVersion->CreateTimestampIsNull()) {
PackageVersionRef version = coreInfo->Version();
PackageVersionRef versionRef = PackageUtils::Version(package);
if (!version.IsSet()) {
version = PackageVersionRef(new PackageVersion(), true);
coreInfo->SetVersion(version);
if (versionRef.IsSet()) {
PackageVersion* version = versionRef.Get();
versionRef = PackageVersionRef(
new PackageVersion(*version, pkgVersion->CreateTimestamp()), true);
} else {
versionRef
= PackageVersionRef(new PackageVersion(pkgVersion->CreateTimestamp()), true);
}
version->SetCreateTimestamp(pkgVersion->CreateTimestamp());
coreInfoBuilder.WithVersion(versionRef);
}
}
if (!pkg->DerivedRatingIsNull()) {
// TODO; unify the naming here!
userRatingBuilder.WithSummary(UserRatingSummaryBuilder()
.WithAverageRating(pkg->DerivedRating())
.WithRatingCount(pkg->DerivedRatingSampleSize())
.BuildRef());
}
int32 countPkgCategories = pkg->CountPkgCategories();
for (i = 0; i < countPkgCategories; i++) {
BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code();
CategoryRef category = fModel->CategoryByCode(*categoryCode);
CategoryRef category = fCategories[*categoryCode];
if (!category.IsSet()) {
HDERROR("unable to find the category for [%s]",
categoryCode->String());
} else
packageClassificationInfo->AddCategory(category);
}
if (!pkg->DerivedRatingIsNull()) {
UserRatingInfoRef userRatingInfo(new UserRatingInfo(), true);
UserRatingSummaryRef userRatingSummary(new UserRatingSummary(), true);
// TODO; unify the naming here!
userRatingSummary->SetAverageRating(pkg->DerivedRating());
userRatingSummary->SetRatingCount(pkg->DerivedRatingSampleSize());
userRatingInfo->SetSummary(userRatingSummary);
package->SetUserRatingInfo(userRatingInfo);
if (!category.IsSet())
HDERROR("unable to find the category for [%s]", categoryCode->String());
else
classificationInfoBuilder.AddCategory(category);
}
if (!pkg->ProminenceOrderingIsNull())
packageClassificationInfo->SetProminence(static_cast<uint32>(pkg->ProminenceOrdering()));
classificationInfoBuilder.WithProminence(static_cast<uint32>(pkg->ProminenceOrdering()));
if (!pkg->IsNativeDesktopIsNull())
packageClassificationInfo->SetIsNativeDesktop(pkg->IsNativeDesktop());
classificationInfoBuilder.WithIsNativeDesktop(pkg->IsNativeDesktop());
int32 countPkgScreenshots = pkg->CountPkgScreenshots();
PackageScreenshotInfoRef screenshotInfo(new PackageScreenshotInfo(), true);
for (i = 0; i < countPkgScreenshots; i++) {
DumpExportPkgScreenshot* screenshot = pkg->PkgScreenshotsItemAt(i);
screenshotInfo->AddScreenshot(_CreateScreenshot(screenshot));
screenshotInfoBuilder.AddScreenshot(_CreateScreenshot(screenshot));
}
package->SetScreenshotInfo(screenshotInfo);
package->SetLocalizedText(localizedText);
package->SetLocalInfo(localInfo);
package->SetCoreInfo(coreInfo);
HDTRACE("did populate data for [%s] (%s)", pkg->Name()->String(),
fDepotName.String());
fCount++;
package->SetPackageClassificationInfo(packageClassificationInfo);
package->EndCollatingChanges();
return !fStoppable->WasStopped();
return PackageInfoBuilder(package)
.WithScreenshotInfo(screenshotInfoBuilder.BuildRef())
.WithUserRatingInfo(userRatingBuilder.BuildRef())
.WithLocalizedText(localizedTextBuilder.BuildRef())
.WithLocalInfo(localInfoBuilder.BuildRef())
.WithCoreInfo(coreInfoBuilder.BuildRef())
.WithPackageClassificationInfo(classificationInfoBuilder.BuildRef())
.BuildRef();
}
@@ -208,23 +245,20 @@ PackageFillingPkgListener::Count()
bool
PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
{
AutoLocker<BLocker> locker(fModel->Lock());
DepotInfoRef depot = fModel->DepotForName(fDepotName);
const BString packageName = *(pkg->Name());
PackageInfoRef package = _PackageForName(packageName);
if (depot.Get() != NULL) {
const BString packageName = *(pkg->Name());
PackageInfoRef package = depot->PackageByName(packageName);
if (package.Get() != NULL)
ConsumePackage(package, pkg);
else {
HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]",
packageName.String());
}
if (package.IsSet()) {
fUpdatedPackages.push_back(_CreateUpdatePackage(package, pkg));
HDTRACE("did populate data for [%s]", pkg->Name()->String());
fCount++;
} else {
HDINFO("[PackageFillingPkgListener] unable to find the depot [%s]",
fDepotName.String());
HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]", packageName.String());
}
if (fUpdatedPackages.size() > kPackageBatchSize)
_FlushUpdatedPackages();
return !fStoppable->WasStopped();
}
@@ -232,12 +266,11 @@ PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
void
PackageFillingPkgListener::Complete()
{
_FlushUpdatedPackages();
}
ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(
BString depotName,
Model *model,
ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(BString depotName, Model* model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
@@ -245,9 +278,7 @@ ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(
fDepotName(depotName)
{
fName.SetToFormat("ServerPkgDataUpdateProcess<%s>", depotName.String());
fDescription.SetTo(
B_TRANSLATE("Synchronizing package data for repository "
"'%REPO_NAME%'"));
fDescription.SetTo(B_TRANSLATE("Synchronizing package data for repository '%REPO_NAME%'"));
fDescription.ReplaceAll("%REPO_NAME%", depotName.String());
}
@@ -287,7 +318,6 @@ ServerPkgDataUpdateProcess::GetLocalPath(BPath& path) const
BString webAppRepositorySourceCode = _DeriveWebAppRepositorySourceCode();
if (!webAppRepositorySourceCode.IsEmpty()) {
AutoLocker<BLocker> locker(fModel->Lock());
return StorageUtils::DumpExportPkgDataPath(path, webAppRepositorySourceCode,
fModel->PreferredLanguage());
}
@@ -301,15 +331,12 @@ ServerPkgDataUpdateProcess::ProcessLocalData()
{
BStopWatch watch("ServerPkgDataUpdateProcess::ProcessLocalData", true);
PackageFillingPkgListener* itemListener =
new PackageFillingPkgListener(fModel, fDepotName, this);
ObjectDeleter<PackageFillingPkgListener>
itemListenerDeleter(itemListener);
PackageFillingPkgListener* itemListener = new PackageFillingPkgListener(fModel, this);
ObjectDeleter<PackageFillingPkgListener> itemListenerDeleter(itemListener);
BulkContainerDumpExportPkgJsonListener* listener =
new BulkContainerDumpExportPkgJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportPkgJsonListener>
listenerDeleter(listener);
BulkContainerDumpExportPkgJsonListener* listener
= new BulkContainerDumpExportPkgJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportPkgJsonListener> listenerDeleter(listener);
BPath localPath;
status_t result = GetLocalPath(localPath);
@@ -324,8 +351,8 @@ ServerPkgDataUpdateProcess::ProcessLocalData()
if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0;
HDINFO("[%s] did process %" B_PRIi32 " packages' data "
"in (%6.3g secs)", Name(), itemListener->Count(), secs);
HDINFO("[%s] did process %" B_PRIi32 " packages' data in (%6.3g secs)", Name(),
itemListener->Count(), secs);
}
return listener->ErrorStatus();
@@ -340,8 +367,7 @@ ServerPkgDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void
ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
{
jsonPath.SetTo("$.info");
}
@@ -352,9 +378,8 @@ ServerPkgDataUpdateProcess::_DeriveWebAppRepositorySourceCode() const
{
const DepotInfo* depot = fModel->DepotForName(fDepotName);
if (depot == NULL) {
if (depot == NULL)
return BString();
}
return depot->WebAppRepositorySourceCode();
}
@@ -364,8 +389,8 @@ status_t
ServerPkgDataUpdateProcess::RunInternal()
{
if (_DeriveWebAppRepositorySourceCode().IsEmpty()) {
HDINFO("[%s] am not updating data for depot [%s] as there is no"
" web app repository source code available",
HDINFO("[%s] am not updating data for depot [%s] as there is no web app repository source "
"code available",
Name(), fDepotName.String());
return B_OK;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2019-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -32,8 +32,7 @@
#define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess"
ServerReferenceDataUpdateProcess::ServerReferenceDataUpdateProcess(
Model* model,
ServerReferenceDataUpdateProcess::ServerReferenceDataUpdateProcess(Model* model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
@@ -65,7 +64,6 @@ BString
ServerReferenceDataUpdateProcess::UrlPathComponent()
{
BString result;
AutoLocker<BLocker> locker(fModel->Lock());
result.SetToFormat("/__reference/all-%s.json.gz", fModel->PreferredLanguage()->ID());
return result;
}
@@ -74,7 +72,6 @@ ServerReferenceDataUpdateProcess::UrlPathComponent()
status_t
ServerReferenceDataUpdateProcess::GetLocalPath(BPath& path) const
{
AutoLocker<BLocker> locker(fModel->Lock());
return StorageUtils::DumpExportReferenceDataPath(path, fModel->PreferredLanguage());
}
@@ -82,8 +79,7 @@ ServerReferenceDataUpdateProcess::GetLocalPath(BPath& path) const
status_t
ServerReferenceDataUpdateProcess::ProcessLocalData()
{
SingleDumpExportReferenceJsonListener* listener =
new SingleDumpExportReferenceJsonListener();
SingleDumpExportReferenceJsonListener* listener = new SingleDumpExportReferenceJsonListener();
BPath localPath;
status_t result = GetLocalPath(localPath);
@@ -120,91 +116,68 @@ ServerReferenceDataUpdateProcess::_ProcessData(DumpExportReference* data)
status_t
ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(
DumpExportReference* data)
ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(DumpExportReference* data)
{
HDINFO("[%s] will populate from %" B_PRId32 " possible natural languages",
Name(), data->CountNaturalLanguages());
AutoLocker<BLocker> locker(fModel->Lock());
HDINFO("[%s] will populate from %" B_PRId32 " possible natural languages", Name(),
data->CountNaturalLanguages());
LanguageRepository* languageRepository = fModel->Languages();
languageRepository->Clear();
std::vector<LanguageRef> languages;
int32 count = 0;
for (int32 i = 0; i < data->CountNaturalLanguages(); i++) {
DumpExportReferenceNaturalLanguage* naturalLanguage =
data->NaturalLanguagesItemAt(i);
languageRepository->AddLanguage(
LanguageRef(new Language(*(naturalLanguage->Code()), *(naturalLanguage->Name()),
naturalLanguage->IsPopular()),
true));
for (int32 i = data->CountNaturalLanguages() - 1; i > 0; i--) {
DumpExportReferenceNaturalLanguage* naturalLanguage = data->NaturalLanguagesItemAt(i);
Language* language = new Language(*(naturalLanguage->Code()), *(naturalLanguage->Name()),
naturalLanguage->IsPopular());
languages.push_back(LanguageRef(language, true));
count++;
}
// it could be that the preferred language does not exist in the
// list. In this case it is necessary to choose one from the list.
fModel->SetPreferredLanguage(LocaleUtils::DeriveDefaultLanguage(fModel->Languages()));
HDINFO("[%s] did add %" B_PRId32 " supported languages", Name(), count);
if (!languages.empty()) {
fModel->SetLanguagesAndPreferred(languages, LocaleUtils::DeriveDefaultLanguage(languages));
HDINFO("[%s] did add %" B_PRId32 " supported languages", Name(), count);
}
return B_OK;
}
status_t
ServerReferenceDataUpdateProcess::_ProcessPkgCategories(
DumpExportReference* data)
ServerReferenceDataUpdateProcess::_ProcessPkgCategories(DumpExportReference* data)
{
HDINFO("[%s] will populate %" B_PRId32 " pkg categories",
Name(), data->CountPkgCategories());
HDINFO("[%s] will populate %" B_PRId32 " pkg categories", Name(), data->CountPkgCategories());
std::vector<CategoryRef> assembledCategories;
for (int32 i = 0; i < data->CountPkgCategories(); i++) {
DumpExportReferencePkgCategory* pkgCategory =
data->PkgCategoriesItemAt(i);
assembledCategories.push_back(CategoryRef(
new PackageCategory(
*(pkgCategory->Code()),
*(pkgCategory->Name())
),
true));
DumpExportReferencePkgCategory* pkgCategory = data->PkgCategoriesItemAt(i);
assembledCategories.push_back(
CategoryRef(new PackageCategory(*(pkgCategory->Code()), *(pkgCategory->Name())), true));
}
{
AutoLocker<BLocker> locker(fModel->Lock());
fModel->AddCategories(assembledCategories);
}
fModel->SetCategories(assembledCategories);
return B_OK;
}
status_t
ServerReferenceDataUpdateProcess::_ProcessRatingStabilities(
DumpExportReference* data)
ServerReferenceDataUpdateProcess::_ProcessRatingStabilities(DumpExportReference* data)
{
HDINFO("[%s] will populate %" B_PRId32 " rating stabilities",
Name(), data->CountUserRatingStabilities());
HDINFO("[%s] will populate %" B_PRId32 " rating stabilities", Name(),
data->CountUserRatingStabilities());
std::vector<RatingStabilityRef> assembledRatingStabilities;
for (int32 i = 0; i < data->CountUserRatingStabilities(); i++) {
DumpExportReferenceUserRatingStability* ratingStability =
data->UserRatingStabilitiesItemAt(i);
assembledRatingStabilities.push_back(RatingStabilityRef(
new RatingStability(
*(ratingStability->Code()),
*(ratingStability->Name()),
ratingStability->Ordering()
),
true));
DumpExportReferenceUserRatingStability* ratingStability
= data->UserRatingStabilitiesItemAt(i);
assembledRatingStabilities.push_back(
RatingStabilityRef(new RatingStability(*(ratingStability->Code()),
*(ratingStability->Name()), ratingStability->Ordering()),
true));
}
{
AutoLocker<BLocker> locker(fModel->Lock());
fModel->AddRatingStabilities(assembledRatingStabilities);
}
fModel->SetRatingStabilities(assembledRatingStabilities);
return B_OK;
}
@@ -218,8 +191,7 @@ ServerReferenceDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void
ServerReferenceDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
ServerReferenceDataUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
{
jsonPath.SetTo("$.info");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerRepositoryDataUpdateProcess.h"
@@ -17,10 +17,10 @@
#include "DumpExportRepository.h"
#include "DumpExportRepositoryJsonListener.h"
#include "DumpExportRepositorySource.h"
#include "Logger.h"
#include "PackageInfo.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
#include "Logger.h"
#undef B_TRANSLATION_CONTEXT
@@ -28,13 +28,12 @@
/*! This repository listener (not at the JSON level) is feeding in the
repositories as they are parsed and processing them. Processing
includes finding the matching depot record and coupling the data
from the server with the data about the depot.
repositories as they are parsed and processing them. Processing
includes finding the matching depot record and coupling the data
from the server with the data about the depot.
*/
class DepotMatchingRepositoryListener :
public DumpExportRepositoryListener {
class DepotMatchingRepositoryListener : public DumpExportRepositoryListener {
public:
DepotMatchingRepositoryListener(Model* model,
Stoppable* stoppable);
@@ -60,14 +59,16 @@ private:
private:
Model* fModel;
Stoppable* fStoppable;
std::vector<DepotInfoRef>
fDepots;
};
DepotMatchingRepositoryListener::DepotMatchingRepositoryListener(
Model* model, Stoppable* stoppable)
DepotMatchingRepositoryListener::DepotMatchingRepositoryListener(Model* model, Stoppable* stoppable)
:
fModel(model),
fStoppable(stoppable)
fStoppable(stoppable),
fDepots()
{
}
@@ -79,43 +80,41 @@ DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener()
void
DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
DumpExportRepository* repository,
DumpExportRepositorySource* repositorySource)
DumpExportRepository* repository, DumpExportRepositorySource* repositorySource)
{
BString* repositoryCode = repository->Code();
BString* repositorySourceCode = repositorySource->Code();
depot->SetWebAppRepositoryCode(*repositoryCode);
depot->SetWebAppRepositorySourceCode(*repositorySourceCode);
DepotInfoBuilder depotBuilder(depot);
depotBuilder.WithWebAppRepositoryCode(*repositoryCode);
depotBuilder.WithWebAppRepositorySourceCode(*repositorySourceCode);
DepotInfoRef depotInfo = depotBuilder.BuildRef();
fDepots.push_back(depotInfo);
if (Logger::IsDebugEnabled()) {
HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
"with server repository source [%s] (%s)",
depot->Name().String(),
depot->Identifier().String(),
repositorySourceCode->String(),
"with server repository source [%s] (%s)",
depot->Name().String(), depot->Identifier().String(), repositorySourceCode->String(),
repositorySource->Identifier()->String());
} else {
HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with "
"server repository source [%s]",
depot->Name().String(),
repositorySourceCode->String());
"server repository source [%s]",
depot->Name().String(), repositorySourceCode->String());
}
}
void
DepotMatchingRepositoryListener::Handle(const BString& identifier,
DumpExportRepository* repository,
DepotMatchingRepositoryListener::Handle(const BString& identifier, DumpExportRepository* repository,
DumpExportRepositorySource* repositorySource)
{
if (!identifier.IsEmpty()) {
AutoLocker<BLocker> locker(fModel->Lock());
for (int32 i = 0; i < fModel->CountDepots(); i++) {
DepotInfoRef depot = fModel->DepotAtIndex(i);
if (identifier == depot->Identifier())
_SetupRepositoryData(depot, repository, repositorySource);
}
DepotInfoRef depotForIdentifier = fModel->DepotForIdentifier(identifier);
if (depotForIdentifier.IsSet())
_SetupRepositoryData(depotForIdentifier, repository, repositorySource);
}
}
@@ -130,13 +129,8 @@ DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository,
// there may be additional identifiers for the remote repository and
// these should also be taken into consideration.
for(int32 i = 0;
i < repositorySource->CountExtraIdentifiers();
i++)
{
Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository,
repositorySource);
}
for (int32 i = 0; i < repositorySource->CountExtraIdentifiers(); i++)
Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository, repositorySource);
}
@@ -153,11 +147,11 @@ DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
void
DepotMatchingRepositoryListener::Complete()
{
fModel->SetDepots(fDepots);
}
ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess(
Model* model,
ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess(Model* model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
@@ -189,7 +183,6 @@ BString
ServerRepositoryDataUpdateProcess::UrlPathComponent()
{
BString result;
AutoLocker<BLocker> locker(fModel->Lock());
result.SetToFormat("/__repository/all-%s.json.gz", fModel->PreferredLanguage()->ID());
return result;
}
@@ -198,7 +191,6 @@ ServerRepositoryDataUpdateProcess::UrlPathComponent()
status_t
ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
{
AutoLocker<BLocker> locker(fModel->Lock());
return StorageUtils::DumpExportRepositoryDataPath(path, fModel->PreferredLanguage());
}
@@ -206,15 +198,13 @@ ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
status_t
ServerRepositoryDataUpdateProcess::ProcessLocalData()
{
DepotMatchingRepositoryListener* itemListener =
new DepotMatchingRepositoryListener(fModel, this);
ObjectDeleter<DepotMatchingRepositoryListener>
itemListenerDeleter(itemListener);
DepotMatchingRepositoryListener* itemListener
= new DepotMatchingRepositoryListener(fModel, this);
ObjectDeleter<DepotMatchingRepositoryListener> itemListenerDeleter(itemListener);
BulkContainerDumpExportRepositoryJsonListener* listener =
new BulkContainerDumpExportRepositoryJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener>
listenerDeleter(listener);
BulkContainerDumpExportRepositoryJsonListener* listener
= new BulkContainerDumpExportRepositoryJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener> listenerDeleter(listener);
BPath localPath;
status_t result = GetLocalPath(localPath);
@@ -239,8 +229,7 @@ ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void
ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
{
jsonPath.SetTo("$.info");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -63,7 +63,7 @@ UserDetailVerifierProcess::RunInternal()
case B_OK:
if (!userDetail.Agreement().IsLatest()) {
HDINFO("the user has not agreed to the latest user usage"
" conditions.");
" conditions.");
fListener->UserUsageConditionsNotLatest(userDetail);
}
break;
@@ -84,12 +84,9 @@ UserDetailVerifierProcess::_ShouldVerify()
return false;
}
{
AutoLocker<BLocker> locker(fModel->Lock());
if (fModel->Nickname().IsEmpty()) {
HDINFO("no nickname --> will not verify user");
return false;
}
if (fModel->Nickname().IsEmpty()) {
HDINFO("no nickname --> will not verify user");
return false;
}
return true;
@@ -99,16 +96,14 @@ UserDetailVerifierProcess::_ShouldVerify()
status_t
UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
{
WebAppInterface* interface = fModel->GetWebAppInterface();
WebAppInterfaceRef interface = fModel->WebApp();
BMessage userDetailResponse;
status_t result;
result = interface->RetrieveCurrentUserDetail(userDetailResponse);
if (result != B_OK) {
HDERROR("a problem has arisen retrieving the current user detail: %s",
strerror(result));
}
if (result != B_OK)
HDERROR("a problem has arisen retrieving the current user detail: %s", strerror(result));
if (result == B_OK) {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(userDetailResponse);
@@ -120,7 +115,7 @@ UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
break;
default:
HDERROR("a problem has arisen retrieving the current user "
"detail for user [%s]: jrpc error code %" B_PRId32 "",
"detail for user [%s]: jrpc error code %" B_PRId32 "",
fModel->Nickname().String(), errorCode);
result = B_ERROR;
break;
@@ -139,4 +134,4 @@ UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
}
return result;
}
}
+136 -196
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "WebAppInterface.h"
@@ -14,8 +14,8 @@
#include <HttpHeaders.h>
#include <HttpRequest.h>
#include <Json.h>
#include <JsonTextWriter.h>
#include <JsonMessageWriter.h>
#include <JsonTextWriter.h>
#include <UrlContext.h>
#include <UrlProtocolListener.h>
#include <UrlProtocolRoster.h>
@@ -89,12 +89,10 @@ public:
static BHttpRequest*
make_http_request(const BUrl& url, BDataIO* output,
BUrlProtocolListener* listener = NULL,
make_http_request(const BUrl& url, BDataIO* output, BUrlProtocolListener* listener = NULL,
BUrlContext* context = NULL)
{
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, output,
listener, context);
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, output, listener, context);
BHttpRequest* httpRequest = dynamic_cast<BHttpRequest*>(request);
if (httpRequest == NULL) {
delete request;
@@ -109,8 +107,9 @@ enum {
};
WebAppInterface::WebAppInterface()
WebAppInterface::WebAppInterface(const UserCredentials& value)
{
_SetCredentials(value);
}
@@ -120,9 +119,8 @@ WebAppInterface::~WebAppInterface()
void
WebAppInterface::SetCredentials(const UserCredentials& value)
WebAppInterface::_SetCredentials(const UserCredentials& value)
{
AutoLocker<BLocker> lock(&fLock);
if (fCredentials != value) {
fCredentials = value;
fAccessToken.Clear();
@@ -133,7 +131,6 @@ WebAppInterface::SetCredentials(const UserCredentials& value)
const BString&
WebAppInterface::Nickname()
{
AutoLocker<BLocker> lock(&fLock);
return fCredentials.Nickname();
}
@@ -212,21 +209,18 @@ WebAppInterface::RetrieveUserRatingsForPackageForDisplay(
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user-rating/search-user-ratings",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
0, message);
return _SendJsonRequest("user-rating/search-user-ratings", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
}
status_t
WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
const BString& packageName, const BPackageVersion& version,
const BString& architecture,
const BString& webAppRepositoryCode,
const BString& webAppRepositorySourceCode,
WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(const BString& packageName,
const BPackageVersion& version, const BString& architecture,
const BString& webAppRepositoryCode, const BString& webAppRepositorySourceCode,
const BString& userNickname, BMessage& message)
{
// BHttpRequest later takes ownership of this.
// BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -270,10 +264,8 @@ WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest(
"user-rating/get-user-rating-by-user-and-pkg-version",
requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
return _SendJsonRequest("user-rating/get-user-rating-by-user-and-pkg-version",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
message);
}
@@ -285,12 +277,12 @@ WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
*/
status_t
WebAppInterface::RetrieveUserDetailForCredentials(
const UserCredentials& credentials, BMessage& message)
WebAppInterface::RetrieveUserDetailForCredentials(const UserCredentials& credentials,
BMessage& message)
{
if (!credentials.IsValid()) {
debugger("the credentials supplied are invalid so it is not possible "
"to obtain the user detail");
"to obtain the user detail");
}
status_t result = B_OK;
@@ -301,9 +293,7 @@ WebAppInterface::RetrieveUserDetailForCredentials(
BMessage authenticateResponseEnvelopeMessage;
if (result == B_OK) {
result = AuthenticateUser(
credentials.Nickname(),
credentials.PasswordClear(),
result = AuthenticateUser(credentials.Nickname(), credentials.PasswordClear(),
authenticateResponseEnvelopeMessage);
}
@@ -313,7 +303,7 @@ WebAppInterface::RetrieveUserDetailForCredentials(
result = UnpackAccessToken(authenticateResponseEnvelopeMessage, accessToken);
if (result == B_OK) {
// BHttpRequest later takes ownership of this.
// BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -322,9 +312,8 @@ WebAppInterface::RetrieveUserDetailForCredentials(
requestEnvelopeWriter.WriteString(credentials.Nickname().String());
requestEnvelopeWriter.WriteObjectEnd();
result = _SendJsonRequest("user/get-user", accessToken,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
NEEDS_AUTHORIZATION, message);
result = _SendJsonRequest("user/get-user", accessToken, requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, message);
// note that the credentials used here are passed in as args.
}
@@ -350,12 +339,10 @@ WebAppInterface::RetrieveCurrentUserDetail(BMessage& message)
*/
/*static*/ status_t
WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
UserDetail& userDetail)
WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage, UserDetail& userDetail)
{
BMessage resultMessage;
status_t result = responseEnvelopeMessage.FindMessage(
"result", &resultMessage);
status_t result = responseEnvelopeMessage.FindMessage("result", &resultMessage);
if (result != B_OK) {
HDERROR("bad response envelope missing 'result' entry");
@@ -367,29 +354,24 @@ WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
userDetail.SetNickname(nickname);
BMessage agreementMessage;
if (resultMessage.FindMessage("userUsageConditionsAgreement",
&agreementMessage) == B_OK) {
if (resultMessage.FindMessage("userUsageConditionsAgreement", &agreementMessage) == B_OK) {
BString code;
BDateTime agreedToTimestamp;
BString userUsageConditionsCode;
UserUsageConditionsAgreement agreement = userDetail.Agreement();
bool isLatest;
if (agreementMessage.FindString("userUsageConditionsCode",
&userUsageConditionsCode) == B_OK) {
if (agreementMessage.FindString("userUsageConditionsCode", &userUsageConditionsCode)
== B_OK) {
agreement.SetCode(userUsageConditionsCode);
}
double timestampAgreedMillis;
if (agreementMessage.FindDouble("timestampAgreed",
&timestampAgreedMillis) == B_OK) {
agreement.SetTimestampAgreed((uint64) timestampAgreedMillis);
}
if (agreementMessage.FindDouble("timestampAgreed", &timestampAgreedMillis) == B_OK)
agreement.SetTimestampAgreed((uint64)timestampAgreedMillis);
if (agreementMessage.FindBool("isLatest", &isLatest)
== B_OK) {
if (agreementMessage.FindBool("isLatest", &isLatest) == B_OK)
agreement.SetIsLatest(isLatest);
}
userDetail.SetAgreement(agreement);
}
@@ -399,20 +381,18 @@ WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
/*! When an authentication API call is made, the response (if successful) will
return an access token in the response. This method will take the response
from the server and will parse out the access token data into the supplied
object.
return an access token in the response. This method will take the response
from the server and will parse out the access token data into the supplied
object.
*/
/*static*/ status_t
WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage,
AccessToken& accessToken)
WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage, AccessToken& accessToken)
{
status_t result;
BMessage resultMessage;
result = responseEnvelopeMessage.FindMessage(
"result", &resultMessage);
result = responseEnvelopeMessage.FindMessage("result", &resultMessage);
if (result != B_OK) {
HDERROR("bad response envelope missing 'result' entry");
@@ -469,12 +449,10 @@ WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage,
*/
status_t
WebAppInterface::RetrieveUserUsageConditions(const BString& code,
UserUsageConditions& conditions)
WebAppInterface::RetrieveUserUsageConditions(const BString& code, UserUsageConditions& conditions)
{
BMessage responseEnvelopeMessage;
status_t result = _RetrieveUserUsageConditionsMeta(code,
responseEnvelopeMessage);
status_t result = _RetrieveUserUsageConditionsMeta(code, responseEnvelopeMessage);
if (result != B_OK)
return result;
@@ -489,11 +467,10 @@ WebAppInterface::RetrieveUserUsageConditions(const BString& code,
double metaDataMinimumAge;
BString copyMarkdown;
if ( (resultMessage.FindString("code", &metaDataCode) != B_OK)
|| (resultMessage.FindDouble(
"minimumAge", &metaDataMinimumAge) != B_OK) ) {
if ((resultMessage.FindString("code", &metaDataCode) != B_OK)
|| (resultMessage.FindDouble("minimumAge", &metaDataMinimumAge) != B_OK)) {
HDERROR("unexpected response from server with missing user usage "
"conditions data");
"conditions data");
return B_BAD_DATA;
}
@@ -505,17 +482,15 @@ WebAppInterface::RetrieveUserUsageConditions(const BString& code,
conditions.SetCode(metaDataCode);
conditions.SetMinimumAge(metaDataMinimumAge);
conditions.SetCopyMarkdown(
BString(static_cast<const char*>(copyMarkdownData->Buffer()),
copyMarkdownData->BufferLength()));
conditions.SetCopyMarkdown(BString(static_cast<const char*>(copyMarkdownData->Buffer()),
copyMarkdownData->BufferLength()));
return B_OK;
}
status_t
WebAppInterface::AgreeUserUsageConditions(const BString& code,
BMessage& responsePayload)
WebAppInterface::AgreeUserUsageConditions(const BString& code, BMessage& responsePayload)
{
BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -529,15 +504,13 @@ WebAppInterface::AgreeUserUsageConditions(const BString& code,
// now fetch this information into an object.
return _SendJsonRequest("user/agree-user-usage-conditions",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
NEEDS_AUTHORIZATION, responsePayload);
return _SendJsonRequest("user/agree-user-usage-conditions", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, responsePayload);
}
status_t
WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code,
BMessage& message)
WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code, BMessage& message)
{
BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -553,33 +526,26 @@ WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code,
// now fetch this information into an object.
return _SendJsonRequest("user/get-user-usage-conditions",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
0, message);
return _SendJsonRequest("user/get-user-usage-conditions", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
}
status_t
WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code,
BDataIO* stream)
WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code, BDataIO* stream)
{
return _SendRawGetRequest(
BString("/__user/usageconditions/") << code << "/document.md",
return _SendRawGetRequest(BString("/__user/usageconditions/") << code << "/document.md",
stream);
}
status_t
WebAppInterface::CreateUserRating(const BString& packageName,
const BPackageVersion& version,
const BString& architecture,
const BString& webAppRepositoryCode,
const BString& webAppRepositorySourceCode,
const BString& naturalLanguageCode,
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& comment,
const BString& stability, int rating, BMessage& message)
WebAppInterface::CreateUserRating(const BString& packageName, const BPackageVersion& version,
const BString& architecture, const BString& webAppRepositoryCode,
const BString& webAppRepositorySourceCode, const BString& naturalLanguageCode,
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& comment, const BString& stability, int rating, BMessage& message)
{
BMallocIO* requestEnvelopeData = new BMallocIO();
// BHttpRequest later takes ownership of this.
@@ -628,7 +594,7 @@ WebAppInterface::CreateUserRating(const BString& packageName,
if (rating > 0.0f) {
requestEnvelopeWriter.WriteObjectName("rating");
requestEnvelopeWriter.WriteInteger(rating);
requestEnvelopeWriter.WriteInteger(rating);
}
if (stability.Length() > 0) {
@@ -643,19 +609,16 @@ WebAppInterface::CreateUserRating(const BString& packageName,
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user-rating/create-user-rating",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
NEEDS_AUTHORIZATION, message);
return _SendJsonRequest("user-rating/create-user-rating", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, message);
}
status_t
WebAppInterface::UpdateUserRating(const BString& ratingID,
const BString& naturalLanguageCode,
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& comment,
const BString& stability, int rating, bool active, BMessage& message)
WebAppInterface::UpdateUserRating(const BString& ratingID, const BString& naturalLanguageCode,
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& comment, const BString& stability, int rating, bool active, BMessage& message)
{
BMallocIO* requestEnvelopeData = new BMallocIO();
// BHttpRequest later takes ownership of this.
@@ -696,23 +659,21 @@ WebAppInterface::UpdateUserRating(const BString& ratingID,
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user-rating/update-user-rating",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
NEEDS_AUTHORIZATION, message);
return _SendJsonRequest("user-rating/update-user-rating", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, message);
}
/*! This method will call to the server to get a screenshot that will fit into
the specified width and height.
the specified width and height.
*/
status_t
WebAppInterface::RetrieveScreenshot(const BString& code,
int32 width, int32 height, BDataIO* stream)
WebAppInterface::RetrieveScreenshot(const BString& code, int32 width, int32 height, BDataIO* stream)
{
return _SendRawGetRequest(
BString("/__pkgscreenshot/") << code << ".png" << "?tw="
<< width << "&th=" << height, stream);
return _SendRawGetRequest(BString("/__pkgscreenshot/")
<< code << ".png" << "?tw=" << width << "&th=" << height,
stream);
}
@@ -726,25 +687,20 @@ WebAppInterface::RequestCaptcha(BMessage& message)
requestEnvelopeWriter.WriteObjectStart();
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("captcha/generate-captcha",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
0, message);
return _SendJsonRequest("captcha/generate-captcha", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
}
status_t
WebAppInterface::CreateUser(const BString& nickName,
const BString& passwordClear,
const BString& email,
const BString& captchaToken,
const BString& captchaResponse,
WebAppInterface::CreateUser(const BString& nickName, const BString& passwordClear,
const BString& email, const BString& captchaToken, const BString& captchaResponse,
const BString& naturalLanguageCode,
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& userUsageConditionsCode,
BMessage& message)
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& userUsageConditionsCode, BMessage& message)
{
// BHttpRequest later takes ownership of this.
// BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -776,7 +732,7 @@ WebAppInterface::CreateUser(const BString& nickName,
/*! This method will authenticate the user set in the credentials and will
retain the resultant access token for authenticating any latter API calls.
retain the resultant access token for authenticating any latter API calls.
*/
status_t
@@ -796,7 +752,8 @@ WebAppInterface::AuthenticateUserRetainingAccessToken()
status_t
WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName,
const BString& passwordClear) {
const BString& passwordClear)
{
AutoLocker<BLocker> lock(&fLock);
fAccessToken.Clear();
@@ -817,8 +774,8 @@ WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName,
status_t
WebAppInterface::AuthenticateUser(const BString& nickName,
const BString& passwordClear, BMessage& message)
WebAppInterface::AuthenticateUser(const BString& nickName, const BString& passwordClear,
BMessage& message)
{
BMallocIO* requestEnvelopeData = new BMallocIO();
// BHttpRequest later takes ownership of this.
@@ -833,9 +790,8 @@ WebAppInterface::AuthenticateUser(const BString& nickName,
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user/authenticate-user",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
0, message);
return _SendJsonRequest("user/authenticate-user", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
}
@@ -893,19 +849,16 @@ WebAppInterface::IncrementViewCounter(const PackageInfoRef package, const DepotI
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("pkg/increment-view-counter",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
0, message);
return _SendJsonRequest("pkg/increment-view-counter", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
}
status_t
WebAppInterface::RetrievePasswordRequirements(
PasswordRequirements& passwordRequirements)
WebAppInterface::RetrievePasswordRequirements(PasswordRequirements& passwordRequirements)
{
BMessage responseEnvelopeMessage;
status_t result = _RetrievePasswordRequirementsMeta(
responseEnvelopeMessage);
status_t result = _RetrievePasswordRequirementsMeta(responseEnvelopeMessage);
if (result != B_OK)
return result;
@@ -922,13 +875,13 @@ WebAppInterface::RetrievePasswordRequirements(
double value;
if (resultMessage.FindDouble("minPasswordLength", &value) == B_OK)
passwordRequirements.SetMinPasswordLength((uint32) value);
passwordRequirements.SetMinPasswordLength((uint32)value);
if (resultMessage.FindDouble("minPasswordUppercaseChar", &value) == B_OK)
passwordRequirements.SetMinPasswordUppercaseChar((uint32) value);
passwordRequirements.SetMinPasswordUppercaseChar((uint32)value);
if (resultMessage.FindDouble("minPasswordDigitsChar", &value) == B_OK)
passwordRequirements.SetMinPasswordDigitsChar((uint32) value);
passwordRequirements.SetMinPasswordDigitsChar((uint32)value);
return result;
}
@@ -944,9 +897,8 @@ WebAppInterface::_RetrievePasswordRequirementsMeta(BMessage& message)
requestEnvelopeWriter.WriteObjectStart();
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user/get-password-requirements",
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
0, message);
return _SendJsonRequest("user/get-password-requirements", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
}
@@ -966,7 +918,7 @@ WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
if (responseEnvelopeMessage.FindMessage("error", &error) == B_OK
&& error.FindDouble("code", &code) == B_OK) {
return (int32) code;
return (int32)code;
}
return 0;
@@ -977,9 +929,8 @@ WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
status_t
WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
BPositionIO* requestData, size_t requestDataSize, uint32 flags,
BMessage& reply)
WebAppInterface::_SendJsonRequest(const char* urlPathComponents, BPositionIO* requestData,
size_t requestDataSize, uint32 flags, BMessage& reply)
{
bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
AccessToken accessToken;
@@ -987,15 +938,14 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
if (needsAuthorization)
accessToken = _ObtainValidAccessToken();
return _SendJsonRequest(urlPathComponents, accessToken, requestData,
requestDataSize, flags, reply);
return _SendJsonRequest(urlPathComponents, accessToken, requestData, requestDataSize, flags,
reply);
}
/*static*/ status_t
WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
const AccessToken& accessToken, BPositionIO* requestData,
size_t requestDataSize, uint32 flags, BMessage& reply)
WebAppInterface::_SendJsonRequest(const char* urlPathComponents, const AccessToken& accessToken,
BPositionIO* requestData, size_t requestDataSize, uint32 flags, BMessage& reply)
{
if (requestDataSize == 0) {
HDINFO("%s; empty request payload", PROTOCOL_NAME);
@@ -1004,14 +954,15 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
if (!ServerHelper::IsNetworkAvailable()) {
HDDEBUG("%s; dropping request to ...[%s] as network is not"
" available", PROTOCOL_NAME, urlPathComponents);
" available",
PROTOCOL_NAME, urlPathComponents);
delete requestData;
return HD_NETWORK_INACCESSIBLE;
}
if (ServerSettings::IsClientTooOld()) {
HDDEBUG("%s; dropping request to ...[%s] as client is too old",
PROTOCOL_NAME, urlPathComponents);
HDDEBUG("%s; dropping request to ...[%s] as client is too old", PROTOCOL_NAME,
urlPathComponents);
delete requestData;
return HD_CLIENT_TOO_OLD;
}
@@ -1019,16 +970,14 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
if (needsAuthorization && !accessToken.IsValid()) {
HDDEBUG("%s; dropping request to ...[%s] as access token is not valid",
PROTOCOL_NAME, urlPathComponents);
HDDEBUG("%s; dropping request to ...[%s] as access token is not valid", PROTOCOL_NAME,
urlPathComponents);
delete requestData;
return B_NOT_ALLOWED;
}
BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v2/")
<< urlPathComponents);
HDDEBUG("%s; will make request to [%s]", PROTOCOL_NAME,
url.UrlString().String());
BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v2/") << urlPathComponents);
HDDEBUG("%s; will make request to [%s]", PROTOCOL_NAME, url.UrlString().String());
// If the request payload is logged then it must be copied to local memory
// from the stream. This then requires that the request data is then
@@ -1071,13 +1020,12 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
thread_id thread = request->Run();
wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>(
request->Result());
const BHttpResult& result = dynamic_cast<const BHttpResult&>(request->Result());
int32 statusCode = result.StatusCode();
HDDEBUG("%s; did receive http-status [%" B_PRId32 "] from [%s]",
PROTOCOL_NAME, statusCode, url.UrlString().String());
HDDEBUG("%s; did receive http-status [%" B_PRId32 "] from [%s]", PROTOCOL_NAME, statusCode,
url.UrlString().String());
switch (statusCode) {
case B_HTTP_STATUS_OK:
@@ -1089,8 +1037,8 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
default:
HDERROR("%s; request to endpoint [.../%s] failed with http "
"status [%" B_PRId32 "]\n", PROTOCOL_NAME, urlPathComponents,
statusCode);
"status [%" B_PRId32 "]\n",
PROTOCOL_NAME, urlPathComponents, statusCode);
return B_ERROR;
}
@@ -1108,7 +1056,7 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
status_t status = jsonMessageWriter.ErrorStatus();
if (Logger::IsTraceEnabled() && status == B_BAD_DATA) {
BString resultString(static_cast<const char *>(replyData.Buffer()),
BString resultString(static_cast<const char*>(replyData.Buffer()),
replyData.BufferLength());
HDERROR("Parser choked on JSON:\n%s", resultString.String());
}
@@ -1117,33 +1065,29 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
status_t
WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
const BString& jsonString, uint32 flags, BMessage& reply)
WebAppInterface::_SendJsonRequest(const char* urlPathComponents, const BString& jsonString,
uint32 flags, BMessage& reply)
{
// gets 'adopted' by the subsequent http request.
BMemoryIO* data = new BMemoryIO(jsonString.String(),
jsonString.Length() - 1);
BMemoryIO* data = new BMemoryIO(jsonString.String(), jsonString.Length() - 1);
return _SendJsonRequest(urlPathComponents, data, jsonString.Length() - 1,
flags, reply);
return _SendJsonRequest(urlPathComponents, data, jsonString.Length() - 1, flags, reply);
}
status_t
WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
BDataIO* stream)
WebAppInterface::_SendRawGetRequest(const BString urlPathComponents, BDataIO* stream)
{
BUrl url = ServerSettings::CreateFullUrl(urlPathComponents);
HDDEBUG("http-get; will make request to [%s]",
url.UrlString().String());
HDDEBUG("http-get; will make request to [%s]", url.UrlString().String());
ProtocolListener listener;
BHttpHeaders headers;
ServerSettings::AugmentHeaders(headers);
BHttpRequest *request = make_http_request(url, stream, &listener);
BHttpRequest* request = make_http_request(url, stream, &listener);
ObjectDeleter<BHttpRequest> _(request);
if (request == NULL)
return B_ERROR;
@@ -1153,19 +1097,17 @@ WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
thread_id thread = request->Run();
wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>(
request->Result());
const BHttpResult& result = dynamic_cast<const BHttpResult&>(request->Result());
int32 statusCode = result.StatusCode();
HDDEBUG("http-get; did receive http-status [%" B_PRId32 "] from [%s]",
statusCode, url.UrlString().String());
HDDEBUG("http-get; did receive http-status [%" B_PRId32 "] from [%s]", statusCode,
url.UrlString().String());
if (statusCode == 200)
return B_OK;
HDERROR("failed to get data from '%s': %" B_PRIi32 "",
url.UrlString().String(), statusCode);
HDERROR("failed to get data from '%s': %" B_PRIi32 "", url.UrlString().String(), statusCode);
return B_ERROR;
}
@@ -1183,17 +1125,16 @@ WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size)
printf("%s; error logging payload", PROTOCOL_NAME);
} else {
for (uint32 i = 0; i < size; i++) {
bool esc = buffer[i] > 126 ||
(buffer[i] < 0x20 && buffer[i] != 0x0a);
bool esc = buffer[i] > 126 || (buffer[i] < 0x20 && buffer[i] != 0x0a);
if (esc)
printf("\\u%02x", buffer[i]);
else
putchar(buffer[i]);
}
if (esc)
printf("\\u%02x", buffer[i]);
else
putchar(buffer[i]);
}
if (size == LOG_PAYLOAD_LIMIT)
printf("...(continues)");
if (size == LOG_PAYLOAD_LIMIT)
printf("...(continues)");
}
requestData->Seek(requestDataOffset, SEEK_SET);
@@ -1209,15 +1150,14 @@ off_t
WebAppInterface::_LengthAndSeekToZero(BPositionIO* data)
{
off_t dataSize = data->Position();
data->Seek(0, SEEK_SET);
return dataSize;
data->Seek(0, SEEK_SET);
return dataSize;
}
UserCredentials
WebAppInterface::_Credentials()
{
AutoLocker<BLocker> lock(&fLock);
return fCredentials;
}
+9 -4
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef WEB_APP_INTERFACE_H
@@ -10,6 +10,7 @@
#include <Application.h>
#include <JsonWriter.h>
#include <Locker.h>
#include <Referenceable.h>
#include <String.h>
#include <package/PackageVersion.h>
@@ -43,12 +44,12 @@ using BPackageKit::BPackageVersion;
#define RATING_NONE -1
class WebAppInterface {
class WebAppInterface : public BReferenceable
{
public:
WebAppInterface();
WebAppInterface(const UserCredentials& value);
virtual ~WebAppInterface();
void SetCredentials(const UserCredentials& value);
const BString& Nickname();
status_t GetChangelog(
@@ -157,6 +158,7 @@ public:
BMessage& responseEnvelopeMessage,
AccessToken& accessToken);
private:
void _SetCredentials(const UserCredentials& value);
UserCredentials _Credentials();
AccessToken _ObtainValidAccessToken();
@@ -199,4 +201,7 @@ private:
};
typedef BReference<WebAppInterface> WebAppInterfaceRef;
#endif // WEB_APP_INTERFACE_H
+25 -34
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "TarArchiveService.h"
@@ -30,7 +30,7 @@
data for the parsed entry.
*/
/*static*/ status_t
/*static*/ status_t
TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header)
{
uint8 buffer[LENGTH_BLOCK];
@@ -46,7 +46,7 @@ TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header)
useful for obtaining a catalog of items in the tar file for example.
*/
/*status*/ status_t
/*status*/ status_t
TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
{
uint8 buffer[LENGTH_BLOCK];
@@ -58,21 +58,17 @@ TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
memset(zero_buffer, 0, sizeof zero_buffer);
tarIo.Seek(offset, SEEK_SET);
while (result == B_OK
&& B_OK == (result = tarIo.ReadExactly(
buffer, LENGTH_BLOCK))) {
while (result == B_OK && B_OK == (result = tarIo.ReadExactly(buffer, LENGTH_BLOCK))) {
if (memcmp(zero_buffer, buffer, sizeof zero_buffer) == 0) {
HDDEBUG("detected end of tar-ball after %" B_PRIu32 " items",
countItemsRead);
HDTRACE("detected end of tar-ball after %" B_PRIu32 " items", countItemsRead);
return B_OK; // end of tar-ball.
}
TarArchiveHeader header;
result = _ReadHeader(buffer, header);
HDTRACE("did read tar entry header for [%s]",
header.FileName().String());
HDTRACE("did read tar entry header for [%s]", header.FileName().String());
if (result == B_OK) {
countItemsRead++;
@@ -81,8 +77,7 @@ TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
// and/or just process the header information.
if (listener != NULL) {
BDataIO *entryData = new ConstraintedDataIO(&tarIo,
header.Length());
BDataIO* entryData = new ConstraintedDataIO(&tarIo, header.Length());
ObjectDeleter<BDataIO> entryDataDeleter(entryData);
result = listener->Handle(header, offset, entryData);
}
@@ -105,7 +100,8 @@ TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
tar_file_type
TarArchiveService::_ReadHeaderFileType(unsigned char data) {
TarArchiveService::_ReadHeaderFileType(unsigned char data)
{
switch (data) {
case 0:
case '0':
@@ -117,22 +113,20 @@ TarArchiveService::_ReadHeaderFileType(unsigned char data) {
/*static*/ int32
TarArchiveService::_ReadHeaderStringLength(const uint8* data,
size_t maxStringLength)
TarArchiveService::_ReadHeaderStringLength(const uint8* data, size_t maxStringLength)
{
int32 actualLength = 0;
while (actualLength < (int32) maxStringLength && data[actualLength] != 0)
size_t actualLength = 0;
while (actualLength < maxStringLength && data[actualLength] != 0)
actualLength++;
return actualLength;
return static_cast<int32>(actualLength);
}
void
TarArchiveService::_ReadHeaderString(const uint8 *data, size_t maxStringLength,
BString& result)
TarArchiveService::_ReadHeaderString(const uint8* data, size_t maxStringLength, BString& result)
{
result.SetTo((const char *) data,
_ReadHeaderStringLength(data, maxStringLength));
result.SetTo((const char*)data, _ReadHeaderStringLength(data, maxStringLength));
}
@@ -140,7 +134,8 @@ TarArchiveService::_ReadHeaderString(const uint8 *data, size_t maxStringLength,
character in an number expressed in octal.
*/
static bool tar_is_octal_digit(unsigned char c)
static bool
tar_is_octal_digit(unsigned char c)
{
switch (c) {
case '0':
@@ -159,7 +154,7 @@ static bool tar_is_octal_digit(unsigned char c)
/*static*/ uint32
TarArchiveService::_ReadHeaderNumeric(const uint8 *data, size_t dataLength)
TarArchiveService::_ReadHeaderNumeric(const uint8* data, size_t dataLength)
{
uint32 actualLength = 0;
@@ -187,7 +182,7 @@ TarArchiveService::_CalculateBlockChecksum(const uint8* block)
if (i >= OFFSET_CHECKSUM && i < OFFSET_CHECKSUM + LENGTH_CHECKSUM)
result += 32;
else
result += (uint32) block[i];
result += static_cast<uint32>(block[i]);
}
return result;
@@ -198,12 +193,10 @@ TarArchiveService::_CalculateBlockChecksum(const uint8* block)
TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header)
{
uint32 actualChecksum = _CalculateBlockChecksum(block);
uint32 expectedChecksum = _ReadHeaderNumeric(&block[OFFSET_CHECKSUM],
LENGTH_CHECKSUM);
uint32 expectedChecksum = _ReadHeaderNumeric(&block[OFFSET_CHECKSUM], LENGTH_CHECKSUM);
if(actualChecksum != expectedChecksum) {
HDERROR("tar archive header has bad checksum;"
"expected %" B_PRIu32 " actual %" B_PRIu32,
if (actualChecksum != expectedChecksum) {
HDERROR("tar archive header has bad checksum; expected %" B_PRIu32 " actual %" B_PRIu32,
expectedChecksum, actualChecksum);
return B_BAD_DATA;
}
@@ -212,10 +205,8 @@ TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header)
_ReadHeaderString(&block[OFFSET_FILENAME], LENGTH_FILENAME, fileName);
header.SetFileName(fileName);
header.SetLength(
_ReadHeaderNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH));
header.SetFileType(
_ReadHeaderFileType(block[OFFSET_FILETYPE]));
header.SetLength(_ReadHeaderNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH));
header.SetFileType(_ReadHeaderFileType(block[OFFSET_FILETYPE]));
return B_OK;
}
+80 -113
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2017-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -13,19 +13,20 @@
#include <Catalog.h>
#include <Entry.h>
#include <Message.h>
#include <package/PackageDefs.h>
#include <package/PackageInfo.h>
#include <package/PackageRoster.h>
#include <Path.h>
#include <Roster.h>
#include <Screen.h>
#include <String.h>
#include <package/PackageDefs.h>
#include <package/PackageInfo.h>
#include <package/PackageRoster.h>
#include "support.h"
#include "AppUtils.h"
#include "Logger.h"
#include "MainWindow.h"
#include "PackageKitUtils.h"
#include "PackageUtils.h"
#include "ServerHelper.h"
#include "ServerSettings.h"
@@ -44,7 +45,7 @@ App::App()
fWindowCount(0),
fSettingsRead(false)
{
srand((unsigned int) time(NULL));
srand(static_cast<unsigned int>(time(NULL)));
_CheckPackageDaemonRuns();
fIsFirstRun = _CheckIsFirstRun();
}
@@ -62,13 +63,10 @@ App::~App()
bool
App::QuitRequested()
{
if (fMainWindow != NULL
&& fMainWindow->LockLooperWithTimeout(1500000) == B_OK) {
if (fMainWindow != NULL && fMainWindow->LockLooperWithTimeout(1500000) == B_OK) {
BMessage windowSettings;
fMainWindow->StoreSettings(windowSettings);
fMainWindow->UnlockLooper();
_StoreSettings(windowSettings);
}
@@ -111,10 +109,8 @@ App::MessageReceived(BMessage* message)
case MSG_MAIN_WINDOW_CLOSED:
{
BMessage windowSettings;
if (message->FindMessage(KEY_WINDOW_SETTINGS,
&windowSettings) == B_OK) {
if (message->FindMessage(KEY_WINDOW_SETTINGS, &windowSettings) == B_OK)
_StoreSettings(windowSettings);
}
fWindowCount--;
if (fWindowCount == 0)
@@ -199,7 +195,7 @@ app_print_help()
static arg_switch
app_resolve_switch(char *arg)
app_resolve_switch(char* arg)
{
int arglen = strlen(arg);
@@ -223,18 +219,14 @@ app_resolve_switch(char *arg)
if (0 == strcmp(&arg[2], "dropcache"))
return DROP_CACHE_SWITCH;
} else {
if (arglen == 2) { // short form
switch (arg[1]) {
case 'u':
return WEB_APP_BASE_URL_SWITCH;
case 'h':
return HELP_SWITCH;
case 'v':
return VERBOSITY_SWITCH;
}
} else if (arglen == 2) { // short form
switch (arg[1]) {
case 'h':
return HELP_SWITCH;
case 'u':
return WEB_APP_BASE_URL_SWITCH;
case 'v':
return VERBOSITY_SWITCH;
}
}
@@ -250,15 +242,14 @@ App::ArgvReceived(int32 argc, char* argv[])
{
for (int i = 1; i < argc;) {
// check to make sure that if there is a value for the switch,
// that the value is in fact supplied.
// check to make sure that if there is a value for the switch,
// that the value is in fact supplied.
switch (app_resolve_switch(argv[i])) {
case VERBOSITY_SWITCH:
case WEB_APP_BASE_URL_SWITCH:
if (i == argc-1) {
fprintf(stdout, "unexpected end of arguments; missing "
"value for switch [%s]\n", argv[i]);
if (i == argc - 1) {
HDERROR("unexpected end of arguments; missing value for switch [%s]", argv[i]);
Quit();
return;
}
@@ -268,12 +259,12 @@ App::ArgvReceived(int32 argc, char* argv[])
break;
}
// now process each switch.
// now process each switch.
switch (app_resolve_switch(argv[i])) {
case VERBOSITY_SWITCH:
if (!Logger::SetLevelByName(argv[i+1])) {
if (!Logger::SetLevelByName(argv[i + 1])) {
fprintf(stdout, "unknown log level [%s]\n", argv[i + 1]);
Quit();
}
@@ -287,13 +278,10 @@ App::ArgvReceived(int32 argc, char* argv[])
case WEB_APP_BASE_URL_SWITCH:
if (ServerSettings::SetBaseUrl(BUrl(argv[i + 1])) != B_OK) {
fprintf(stdout, "malformed web app base url; %s\n",
argv[i + 1]);
HDERROR("malformed web app base url; %s", argv[i + 1]);
Quit();
}
else {
fprintf(stdout, "did configure the web base url; %s\n",
argv[i + 1]);
} else {
HDERROR("did configure the web base url; %s", argv[i + 1]);
}
i++; // also move past the url value
@@ -330,8 +318,8 @@ App::ArgvReceived(int32 argc, char* argv[])
}
/*! This method will display an alert based on a message. This message arrives
from a number of possible background threads / processes in the application.
/*! This method will display an alert based on a message. This message arrives
from a number of possible background threads / processes in the application.
*/
void
@@ -350,8 +338,8 @@ App::_AlertSimpleError(BMessage* message)
if (message->FindInt32(KEY_ALERT_TYPE, &typeInt) != B_OK)
typeInt = B_INFO_ALERT;
BAlert* alert = new BAlert(alertTitle, alertText, B_TRANSLATE("OK"),
NULL, NULL, B_WIDTH_AS_USUAL, static_cast<alert_type>(typeInt));
BAlert* alert = new BAlert(alertTitle, alertText, B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_AS_USUAL, static_cast<alert_type>(typeInt));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
@@ -366,7 +354,7 @@ App::_Open(const BEntry& entry)
{
BPath path;
if (!entry.Exists() || entry.GetPath(&path) != B_OK) {
fprintf(stderr, "Package file not found: %s\n", path.Path());
HDERROR("package file not found: %s", path.Path());
return;
}
@@ -374,20 +362,16 @@ App::_Open(const BEntry& entry)
BPackageKit::BPackageInfo info;
status_t status = info.ReadFromPackageFile(path.Path());
if (status != B_OK) {
fprintf(stderr, "Failed to parse package file: %s\n",
strerror(status));
HDERROR("failed to parse package file: %s", strerror(status));
return;
}
// Transfer information into PackageInfo
PackageInfoRef package(new(std::nothrow) PackageInfo(info), true);
if (!package.IsSet()) {
fprintf(stderr, "Could not allocate PackageInfo\n");
return;
}
PackageInfoRef filePackageInfo = PackageKitUtils::CreatePackageInfo(info);
PackageInfoBuilder packageBuilder(filePackageInfo);
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package);
localInfo->SetLocalFilePath(path.Path());
PackageLocalInfoBuilder localInfoBuilder(filePackageInfo->LocalInfo());
localInfoBuilder.WithLocalFilePath(path.Path());
// Set if the package is active
//
@@ -396,33 +380,30 @@ App::_Open(const BEntry& entry)
// package kit have to know about these locations?
bool active = false;
BPackageKit::BPackageRoster roster;
status = roster.IsPackageActive(
BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, info, &active);
status = roster.IsPackageActive(BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, info,
&active);
if (status != B_OK) {
fprintf(stderr, "Could not check if package was active in system: %s\n",
strerror(status));
HDERROR("could not check if package was active in system: %s", strerror(status));
return;
}
if (!active) {
status = roster.IsPackageActive(
BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME, info, &active);
status = roster.IsPackageActive(BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME, info,
&active);
if (status != B_OK) {
fprintf(stderr,
"Could not check if package was active in home: %s\n",
strerror(status));
HDERROR("could not check if package was active in home: %s", strerror(status));
return;
}
}
if (active)
localInfo->SetState(ACTIVATED);
localInfoBuilder.WithState(ACTIVATED);
package->SetLocalInfo(localInfo);
packageBuilder.WithLocalInfo(localInfoBuilder.BuildRef());
BMessage settings;
_LoadSettings(settings);
MainWindow* window = new MainWindow(settings, package);
MainWindow* window = new MainWindow(settings, packageBuilder.BuildRef());
_ShowWindow(window);
}
@@ -478,21 +459,19 @@ App::_StoreSettings(const BMessage& settings)
// #pragma mark -
static const char* kPackageDaemonSignature
= "application/x-vnd.haiku-package_daemon";
static const char* kPackageDaemonSignature = "application/x-vnd.haiku-package_daemon";
void
App::_CheckPackageDaemonRuns()
{
while (!be_roster->IsRunning(kPackageDaemonSignature)) {
BAlert* alert = new BAlert(
B_TRANSLATE("Start package daemon"),
BAlert* alert = new BAlert(B_TRANSLATE("Start package daemon"),
B_TRANSLATE("HaikuDepot needs the package daemon to function, "
"and it appears to be not running.\n"
"Would you like to start it now?"),
B_TRANSLATE("No, quit HaikuDepot"),
B_TRANSLATE("Start package daemon"), NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
"and it appears to be not running.\n"
"Would you like to start it now?"),
B_TRANSLATE("No, quit HaikuDepot"), B_TRANSLATE("Start package daemon"), NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
@@ -509,14 +488,11 @@ App::_LaunchPackageDaemon()
{
status_t ret = be_roster->Launch(kPackageDaemonSignature);
if (ret != B_OK) {
BString errorMessage
= B_TRANSLATE("Starting the package daemon failed:\n\n%Error%");
BString errorMessage = B_TRANSLATE("Starting the package daemon failed:\n\n%Error%");
errorMessage.ReplaceAll("%Error%", strerror(ret));
BAlert* alert = new BAlert(
B_TRANSLATE("Package daemon problem"), errorMessage,
B_TRANSLATE("Quit HaikuDepot"),
B_TRANSLATE("Try again"), NULL, B_WIDTH_AS_USUAL,
BAlert* alert = new BAlert(B_TRANSLATE("Package daemon problem"), errorMessage,
B_TRANSLATE("Quit HaikuDepot"), B_TRANSLATE("Try again"), NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
@@ -535,20 +511,18 @@ App::_CheckIsFirstRun()
{
BPath testFilePath;
bool exists = false;
status_t status = StorageUtils::LocalWorkingFilesPath("testfile.txt",
testFilePath, false);
if (status != B_OK) {
status_t status = StorageUtils::LocalWorkingFilesPath("testfile.txt", testFilePath, false);
if (status != B_OK)
HDERROR("unable to establish the location of the test file");
}
else
status = StorageUtils::ExistsObject(testFilePath, &exists, NULL, NULL);
return !exists;
}
/*! \brief Checks to ensure that a working file is able to be written.
\return false if the startup should be stopped and the application should
quit.
/*! \brief Checks to ensure that a working file is able to be written.
\return false if the startup should be stopped and the application should
quit.
*/
bool
@@ -556,8 +530,7 @@ App::_CheckTestFile()
{
BPath testFilePath;
BString pathDescription = "???";
status_t result = StorageUtils::LocalWorkingFilesPath("testfile.txt",
testFilePath, false);
status_t result = StorageUtils::LocalWorkingFilesPath("testfile.txt", testFilePath, false);
if (result == B_OK) {
pathDescription = testFilePath.Path();
@@ -567,19 +540,18 @@ App::_CheckTestFile()
if (result != B_OK) {
StorageUtils::SetWorkingFilesUnavailable();
BString msg = B_TRANSLATE("This application writes and reads some"
" working files on your computer in order to function. It appears"
" that there are problems writing a test file at [%TestFilePath%]."
" Check that there are no issues with your local disk or"
" permissions that might prevent this application from writing"
" files into that directory location. You may choose to acknowledge"
" this problem and continue, but some functionality may be"
" disabled.");
BString msg
= B_TRANSLATE("This application writes and reads some"
" working files on your computer in order to function. It appears"
" that there are problems writing a test file at [%TestFilePath%]."
" Check that there are no issues with your local disk or"
" permissions that might prevent this application from writing"
" files into that directory location. You may choose to acknowledge"
" this problem and continue, but some functionality may be"
" disabled.");
msg.ReplaceAll("%TestFilePath%", pathDescription);
BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Problem with working files"),
msg,
BAlert* alert = new(std::nothrow) BAlert(B_TRANSLATE("Problem with working files"), msg,
B_TRANSLATE("Quit"), B_TRANSLATE("Continue"));
if (alert->Go() == 0)
@@ -611,8 +583,7 @@ App::_ClearCacheOnVersionChange()
}
BPath lastVersionPath;
if (StorageUtils::LocalWorkingFilesPath(
"version.txt", lastVersionPath) != B_OK) {
if (StorageUtils::LocalWorkingFilesPath("version.txt", lastVersionPath) != B_OK) {
HDERROR("clear cache; unable to get version file path");
return;
}
@@ -620,30 +591,26 @@ App::_ClearCacheOnVersionChange()
bool exists;
off_t size;
if (StorageUtils::ExistsObject(
lastVersionPath, &exists, NULL, &size) != B_OK) {
if (StorageUtils::ExistsObject(lastVersionPath, &exists, NULL, &size) != B_OK) {
HDERROR("clear cache; unable to check version file exists");
return;
}
BString lastVersion;
if (exists && StorageUtils::AppendToString(lastVersionPath, lastVersion)
!= B_OK) {
HDERROR("clear cache; unable to read the version from [%s]",
lastVersionPath.Path());
if (exists && StorageUtils::AppendToString(lastVersionPath, lastVersion) != B_OK) {
HDERROR("clear cache; unable to read the version from [%s]", lastVersionPath.Path());
return;
}
if (lastVersion != version) {
HDINFO("last version [%s] and current version [%s] do not match"
" -> will flush cache", lastVersion.String(), version.String());
HDINFO("last version [%s] and current version [%s] do not match -> will flush cache",
lastVersion.String(), version.String());
StorageUtils::RemoveWorkingDirectoryContents();
HDINFO("will write version [%s] to [%s]",
version.String(), lastVersionPath.Path());
HDINFO("will write version [%s] to [%s]", version.String(), lastVersionPath.Path());
StorageUtils::AppendToFile(version, lastVersionPath);
} else {
HDINFO("last version [%s] and current version [%s] match"
" -> cache retained", lastVersion.String(), version.String());
HDINFO("last version [%s] and current version [%s] match -> cache retained",
lastVersion.String(), version.String());
}
}
+179 -152
View File
@@ -1,7 +1,7 @@
/*
* Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -26,7 +26,6 @@
#include "Logger.h"
#include "MainWindow.h"
#include "MarkupTextView.h"
#include "MessagePackageListener.h"
#include "PackageUtils.h"
#include "RatingUtils.h"
#include "RatingView.h"
@@ -55,8 +54,7 @@
// #pragma mark - PackageView
class StackedFeaturesPackageBandMetrics
{
class StackedFeaturesPackageBandMetrics {
public:
StackedFeaturesPackageBandMetrics(float width, BFont* titleFont, BFont* metadataFont)
{
@@ -69,8 +67,7 @@ public:
metadataFont->GetHeight(&metadataFontHeight);
float totalTitleAndMetadataHeight = titleFontHeight.ascent + titleFontHeight.descent
+ titleFontHeight.leading
+ metadataFontHeight.leading
+ titleFontHeight.leading + metadataFontHeight.leading
+ 2.0 * (metadataFontHeight.ascent + metadataFontHeight.descent);
fHeight = fmaxf(totalTitleAndMetadataHeight, iconSize.Height()) + 2.0 * padding;
@@ -85,41 +82,43 @@ public:
float titleWidthM = titleFont->StringWidth("M");
float leftTitlePublisherAndChronologicalInfo = fIconRect.right + padding;
float rightTitlePublisherAndChronologicalInfo = fminf(width, fIconRect.Size().Width()
+ (2.0 * padding) + (titleWidthM * M_COUNT_TITLE));
float rightTitlePublisherAndChronologicalInfo = fminf(width,
fIconRect.Size().Width() + (2.0 * padding) + (titleWidthM * M_COUNT_TITLE));
// left, top, right bottom
// left, top, right bottom
fTitleRect = BRect(leftTitlePublisherAndChronologicalInfo,
(fHeight - totalTitleAndMetadataHeight) / 2.0,
rightTitlePublisherAndChronologicalInfo,
((fHeight - totalTitleAndMetadataHeight) / 2.0)
+ titleFontHeight.ascent + titleFontHeight.descent);
((fHeight - totalTitleAndMetadataHeight) / 2.0) + titleFontHeight.ascent
+ titleFontHeight.descent);
fPublisherRect = BRect(leftTitlePublisherAndChronologicalInfo,
fTitleRect.bottom + titleFontHeight.leading,
rightTitlePublisherAndChronologicalInfo,
fTitleRect.bottom + titleFontHeight.leading
+ metadataFontHeight.ascent + metadataFontHeight.descent);
fTitleRect.bottom + titleFontHeight.leading + metadataFontHeight.ascent
+ metadataFontHeight.descent);
fChronologicalInfoRect = BRect(leftTitlePublisherAndChronologicalInfo,
fPublisherRect.bottom + metadataFontHeight.leading,
rightTitlePublisherAndChronologicalInfo,
fPublisherRect.bottom + metadataFontHeight.leading
+ metadataFontHeight.ascent + metadataFontHeight.descent);
}
fPublisherRect.bottom + metadataFontHeight.leading + metadataFontHeight.ascent
+ metadataFontHeight.descent);
}
// sort out the ratings display
// sort out the ratings display
{
BSize ratingStarSize = SharedIcons::IconStarBlue16Scaled()->Bitmap()->Bounds().Size();
RatingStarsMetrics ratingStarsMetrics(ratingStarSize);
fRatingStarsRect = BRect(BPoint(fTitleRect.right + padding,
(fHeight - ratingStarsMetrics.Size().Height()) / 2), ratingStarsMetrics.Size());
(fHeight - ratingStarsMetrics.Size().Height()) / 2),
ratingStarsMetrics.Size());
if (fRatingStarsRect.right > width)
if (fRatingStarsRect.right > width) {
fRatingStarsRect = BRect();
else {
} else {
// Now sort out the position for the summary. This is reckoned as a container
// rect because it would be nice to layout the text with newlines and not just a
// single line.
@@ -190,10 +189,7 @@ public:
:
BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS),
fModel(model),
fSelectedIndex(-1),
fPackageListener(
new(std::nothrow) OnePackageMessagePackageListener(this)),
fLowestIndexAddedOrRemoved(-1)
fSelectedIndex(-1)
{
SetEventMask(B_POINTER_EVENTS);
@@ -208,29 +204,17 @@ public:
virtual ~StackedFeaturedPackagesView()
{
fPackageListener->SetPackage(PackageInfoRef(NULL));
fPackageListener->ReleaseReference();
delete fBandMetrics;
delete fTitleFont;
delete fMetadataFont;
delete fSummaryFont;
}
// #pragma mark - message handling and events
// #pragma mark - message handling and events
virtual void MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_UPDATE_PACKAGE:
{
BString name;
if (message->FindString("name", &name) != B_OK)
HDINFO("expected 'name' key on package update message");
else
_HandleUpdatePackage(name);
break;
}
case B_COLORS_UPDATED:
{
Invalidate();
@@ -268,16 +252,14 @@ public:
case B_DOWN_ARROW:
{
int32 lastIndex = static_cast<int32>(fPackages.size()) - 1;
if (!IsEmpty() && fSelectedIndex != -1
&& fSelectedIndex < lastIndex) {
if (!IsEmpty() && fSelectedIndex != -1 && fSelectedIndex < lastIndex)
_MessageSelectIndex(fSelectedIndex + 1);
}
break;
}
case B_LEFT_ARROW:
case B_UP_ARROW:
if (fSelectedIndex > 0)
_MessageSelectIndex( fSelectedIndex - 1);
_MessageSelectIndex(fSelectedIndex - 1);
break;
case B_PAGE_UP:
{
@@ -328,13 +310,26 @@ public:
}
// #pragma mark - update / add / remove / clear data
// #pragma mark - update / add / remove / clear data
void UpdatePackage(uint32 changeMask, const PackageInfoRef& package)
void HandlePackagesChanged(const PackageInfoEvents& events)
{
// TODO; could optimize the invalidation?
for (int32 i = events.CountEvents() - 1; i >= 0; i--)
_HandlePackageChanged(events.EventAtIndex(i));
}
void _HandlePackageChanged(const PackageInfoEvent& event)
{
uint32 changes = event.Changes();
PackageInfoRef package = event.Package();
if (!package.IsSet() || 0 == changes)
return;
int32 index = _IndexOfPackage(package);
if (index >= 0) {
fPackages[index] = package;
Invalidate(_RectOfIndex(index));
@@ -344,10 +339,6 @@ public:
void Clear()
{
for (std::vector<PackageInfoRef>::iterator it = fPackages.begin();
it != fPackages.end(); it++) {
(*it)->RemoveListener(fPackageListener);
}
fPackages.clear();
fSelectedIndex = -1;
@@ -367,8 +358,8 @@ public:
}
static BFont* CreateMetadataFont()
{
static BFont* CreateMetadataFont()
{
BFont* font = new BFont(be_plain_font);
font_family family;
font_style style;
@@ -396,14 +387,6 @@ public:
}
void _HandleUpdatePackage(const BString& name)
{
int32 index = _IndexOfName(name);
if (index != -1)
Invalidate(_RectOfIndex(index));
}
static int _CmpProminences(int64 a, int64 b)
{
if (a <= 0)
@@ -456,68 +439,110 @@ public:
}
void BeginAddRemove()
void _InvalidateFromIndex(int32 index)
{
fLowestIndexAddedOrRemoved = INT32_MAX;
}
void EndAddRemove()
{
if (fLowestIndexAddedOrRemoved < INT32_MAX) {
if (fPackages.empty())
if (index != -1) {
if (fPackages.empty()) {
Invalidate();
else {
} else {
BRect invalidRect = Bounds();
invalidRect.top = _YOfIndex(fLowestIndexAddedOrRemoved);
invalidRect.top = _YOfIndex(index);
Invalidate(invalidRect);
}
}
}
void AddPackage(const PackageInfoRef& package)
/*! Finds the index of the first difference between the current list of
packages and the provided list of packages. Returns -1 if there is
no difference. This is used with the `RetainPackages` method.
*/
int32 _IndexOfFirstDifference(const std::vector<PackageInfoRef>& packages)
{
size_t result = 0;
size_t packagesLen = packages.size();
size_t currentPackagesLen = fPackages.size();
while (true) {
if (result == packagesLen && result == currentPackagesLen)
return -1;
if (result >= packagesLen || result >= currentPackagesLen)
return static_cast<int32>(result);
if (packages[result] != fPackages[result])
return static_cast<int32>(result);
result++;
}
}
void RetainPackages(const std::vector<PackageInfoRef>& packages)
{
int32 lowestIndexAddedOrRemoved = _IndexOfFirstDifference(packages);
fPackages = packages;
_InvalidateFromIndex(lowestIndexAddedOrRemoved);
}
void _AddPackage(const PackageInfoRef& package, int32* lowestIndexAddedOrRemoved)
{
// fPackages is sorted and for this reason it is possible to find the
// insertion point by identifying the first item in fPackages that does
// not return true from the method '_IsPackageBefore'.
std::vector<PackageInfoRef>::iterator itInsertionPt
= std::lower_bound(fPackages.begin(), fPackages.end(), package,
&_IsPackageBefore);
= std::lower_bound(fPackages.begin(), fPackages.end(), package, &_IsPackageBefore);
if (itInsertionPt == fPackages.end() || package->Name() != (*itInsertionPt)->Name()) {
int32 insertionIndex = std::distance<std::vector<PackageInfoRef>::const_iterator>(
fPackages.begin(), itInsertionPt);
if (itInsertionPt == fPackages.end()
|| package->Name() != (*itInsertionPt)->Name()) {
int32 insertionIndex =
std::distance<std::vector<PackageInfoRef>::const_iterator>(
fPackages.begin(), itInsertionPt);
if (fSelectedIndex >= insertionIndex)
fSelectedIndex++;
fPackages.insert(itInsertionPt, package);
package->AddListener(fPackageListener);
if (insertionIndex < fLowestIndexAddedOrRemoved)
fLowestIndexAddedOrRemoved = insertionIndex;
if (insertionIndex > *lowestIndexAddedOrRemoved)
*lowestIndexAddedOrRemoved = insertionIndex;
}
}
void RemovePackage(const PackageInfoRef& package)
void _RemovePackage(const PackageInfoRef& package, int32* lowestIndexAddedOrRemoved)
{
int32 index = _IndexOfPackage(package);
if (index >= 0) {
if (fSelectedIndex == index)
int32 removalIndex = _IndexOfPackage(package);
if (removalIndex >= 0) {
if (fSelectedIndex == removalIndex)
fSelectedIndex = -1;
if (fSelectedIndex > index)
if (fSelectedIndex > removalIndex)
fSelectedIndex--;
fPackages[index]->RemoveListener(fPackageListener);
fPackages.erase(fPackages.begin() + index);
if (index < fLowestIndexAddedOrRemoved)
fLowestIndexAddedOrRemoved = index;
fPackages.erase(fPackages.begin() + removalIndex);
if (removalIndex > *lowestIndexAddedOrRemoved)
*lowestIndexAddedOrRemoved = removalIndex;
}
}
// #pragma mark - selection and index handling
void AddRemovePackages(const std::vector<PackageInfoRef>& addedPackages,
const std::vector<PackageInfoRef>& removedPackages)
{
int32 lowestIndexAddedOrRemoved = -1;
std::vector<PackageInfoRef>::const_iterator it;
for (it = addedPackages.begin(); it != addedPackages.end(); it++)
_AddPackage(*it, &lowestIndexAddedOrRemoved);
for (it = removedPackages.begin(); it != removedPackages.end(); it++)
_RemovePackage(*it, &lowestIndexAddedOrRemoved);
_InvalidateFromIndex(lowestIndexAddedOrRemoved);
}
// #pragma mark - selection and index handling
void SelectPackage(const PackageInfoRef& package)
@@ -543,11 +568,12 @@ public:
int32 _IndexOfPackage(PackageInfoRef package) const
{
std::vector<PackageInfoRef>::const_iterator it
= std::lower_bound(fPackages.begin(), fPackages.end(), package,
&_IsPackageBefore);
= std::lower_bound(fPackages.begin(), fPackages.end(), package, &_IsPackageBefore);
return (it == fPackages.end() || (*it)->Name() != package->Name())
? -1 : it - fPackages.begin();
if (it == fPackages.end() || (*it)->Name() != package->Name())
return -1;
return it - fPackages.begin();
}
@@ -564,7 +590,7 @@ public:
}
// #pragma mark - drawing and rendering
// #pragma mark - drawing and rendering
virtual void Draw(BRect updateRect)
@@ -626,9 +652,14 @@ public:
if (!iconRect.IsValid())
return;
PackageIconRepositoryRef iconRepository = fModel.IconRepository();
if (!iconRepository.IsSet())
return;
BitmapHolderRef icon;
status_t iconResult = fModel.GetPackageIconRepository().GetIcon(pkg->Name(),
iconRect.Width(), icon);
status_t iconResult = iconRepository->GetIcon(pkg->Name(), iconRect.Width(), icon);
if (iconResult == B_OK) {
if (icon.IsSet()) {
@@ -659,9 +690,9 @@ public:
float trailingIconsWidth = 0.0f;
for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin();
it != trailingIconBitmaps.end(); it++) {
trailingIconsWidth += (*it)->Bitmap()->Bounds().Width()
* (1.0 + TRAILING_ICON_PADDING_LEFT_FACTOR);
it != trailingIconBitmaps.end(); it++) {
trailingIconsWidth
+= (*it)->Bitmap()->Bounds().Width() * (1.0 + TRAILING_ICON_PADDING_LEFT_FACTOR);
}
SetDrawingMode(B_OP_COPY);
@@ -671,20 +702,20 @@ public:
float titleRightTrailingIconsPadding = 0.0f;
if (!trailingIconBitmaps.empty()) {
titleRightTrailingIconsPadding = StringWidth("M")
* TITLE_RIGHT_TRAILING_ICON_PADDING_M_FACTOR;
titleRightTrailingIconsPadding
= StringWidth("M") * TITLE_RIGHT_TRAILING_ICON_PADDING_M_FACTOR;
}
font_height fontHeight;
fTitleFont->GetHeight(&fontHeight);
BPoint pt = textRect.LeftTop() + BPoint(0.0, + fontHeight.ascent);
BPoint pt = textRect.LeftTop() + BPoint(0.0, +fontHeight.ascent);
BString title;
PackageUtils::TitleOrName(pkg, title);
BString renderedText = title;
TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width()
- (titleRightTrailingIconsPadding + trailingIconsWidth));
TruncateString(&renderedText, B_TRUNCATE_END,
textRect.Width() - (titleRightTrailingIconsPadding + trailingIconsWidth));
DrawString(renderedText, pt);
@@ -697,14 +728,15 @@ public:
SetDrawingMode(B_OP_ALPHA);
for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin();
it != trailingIconBitmaps.end(); it++) {
it != trailingIconBitmaps.end(); it++) {
const BBitmap* bitmap = (*it)->Bitmap();
BRect bitmapBounds = bitmap->Bounds();
float trailingIconTopLeftPtX = ceilf(
trailingIconX + (bitmapBounds.Width() * TRAILING_ICON_PADDING_LEFT_FACTOR)) + 0.5;
float trailingIconTopLeftPtY = ceilf(
trailingIconMidY - (bitmapBounds.Height() / 2.0)) + 0.5;
float trailingIconTopLeftPtX
= ceilf(trailingIconX + (bitmapBounds.Width() * TRAILING_ICON_PADDING_LEFT_FACTOR))
+ 0.5;
float trailingIconTopLeftPtY
= ceilf(trailingIconMidY - (bitmapBounds.Height() / 2.0)) + 0.5;
BRect trailingIconRect(BPoint(trailingIconTopLeftPtX, trailingIconTopLeftPtY),
bitmap->Bounds().Size());
@@ -727,7 +759,7 @@ public:
font_height fontHeight;
fMetadataFont->GetHeight(&fontHeight);
BPoint pt = textRect.LeftTop() + BPoint(0.0, + fontHeight.ascent);
BPoint pt = textRect.LeftTop() + BPoint(0.0, +fontHeight.ascent);
BString renderedText(text);
TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width());
@@ -762,7 +794,7 @@ public:
if (!ratingRect.IsValid())
return;
UserRatingInfoRef userRatingInfo = pkg->UserRatingInfo();
PackageUserRatingInfoRef userRatingInfo = pkg->UserRatingInfo();
if (userRatingInfo.IsSet()) {
UserRatingSummaryRef userRatingSummary = userRatingInfo->Summary();
@@ -789,9 +821,10 @@ public:
// The text rect is a container into which later text can be made to flow multi-line. For
// now just draw one line of the summary.
BPoint pt = textRect.LeftTop() + BPoint(0.0,
(textRect.Size().Height() / 2.0) - ((fontHeight.ascent + fontHeight.descent) / 2.0)
+ fontHeight.ascent);
BPoint pt = textRect.LeftTop()
+ BPoint(0.0,
(textRect.Size().Height() / 2.0) - ((fontHeight.ascent + fontHeight.descent) / 2.0)
+ fontHeight.ascent);
BString summary;
PackageUtils::Summary(pkg, summary);
@@ -801,7 +834,7 @@ public:
}
// #pragma mark - geometry and scrolling
// #pragma mark - geometry and scrolling
/*! This method will make sure that the package at the given index is
@@ -816,9 +849,9 @@ public:
if (!_IsIndexEntirelyVisible(index)) {
BRect bounds = Bounds();
int32 indexOfCentreVisible = _IndexOfY(bounds.top + bounds.Height() / 2);
if (index < indexOfCentreVisible)
if (index < indexOfCentreVisible) {
ScrollTo(0, _YOfIndex(index));
else {
} else {
float scrollPointY = (_YOfIndex(index) + fBandMetrics->Height()) - bounds.Height();
ScrollTo(0, scrollPointY);
}
@@ -902,7 +935,7 @@ public:
int32 i = static_cast<int32>(y / fBandMetrics->Height());
if (i < 0)
return 0;
return std::min(i, (int32) (fPackages.size() - 1));
return std::min(i, (int32)(fPackages.size() - 1));
}
@@ -918,9 +951,6 @@ private:
std::vector<PackageInfoRef>
fPackages;
int32 fSelectedIndex;
OnePackageMessagePackageListener*
fPackageListener;
int32 fLowestIndexAddedOrRemoved;
StackedFeaturesPackageBandMetrics*
fBandMetrics;
@@ -940,11 +970,10 @@ FeaturedPackagesView::FeaturedPackagesView(Model& model)
{
fPackagesView = new StackedFeaturedPackagesView(fModel);
fScrollView = new BScrollView("featured packages scroll view",
fPackagesView, 0, false, true, B_FANCY_BORDER);
fScrollView = new BScrollView("featured packages scroll view", fPackagesView, 0, false, true,
B_FANCY_BORDER);
BLayoutBuilder::Group<>(this)
.Add(fScrollView, 1.0f);
BLayoutBuilder::Group<>(this).Add(fScrollView, 1.0f);
}
@@ -954,35 +983,21 @@ FeaturedPackagesView::~FeaturedPackagesView()
void
FeaturedPackagesView::BeginAddRemove()
FeaturedPackagesView::RetainPackages(const std::vector<PackageInfoRef>& packages)
{
fPackagesView->BeginAddRemove();
}
void
FeaturedPackagesView::EndAddRemove()
{
fPackagesView->EndAddRemove();
fPackagesView->RetainPackages(packages);
_AdjustViews();
}
/*! This method will add the package into the list to be displayed. The
insertion will occur in alphabetical order.
*/
void
FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
FeaturedPackagesView::AddRemovePackages(const std::vector<PackageInfoRef>& addedPackages,
const std::vector<PackageInfoRef>& removedPackages)
{
fPackagesView->AddPackage(package);
}
void
FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
{
fPackagesView->RemovePackage(package);
if (!addedPackages.empty() || !removedPackages.empty()) {
fPackagesView->AddRemovePackages(addedPackages, removedPackages);
_AdjustViews();
}
}
@@ -996,8 +1011,7 @@ FeaturedPackagesView::Clear()
void
FeaturedPackagesView::SelectPackage(const PackageInfoRef& package,
bool scrollToEntry)
FeaturedPackagesView::SelectPackage(const PackageInfoRef& package, bool scrollToEntry)
{
fPackagesView->SelectPackage(package);
@@ -1017,9 +1031,22 @@ FeaturedPackagesView::DoLayout()
}
void
FeaturedPackagesView::HandleIconsChanged()
{
Invalidate();
}
void
FeaturedPackagesView::HandlePackagesChanged(const PackageInfoEvents& events)
{
fPackagesView->HandlePackagesChanged(events);
}
void
FeaturedPackagesView::_AdjustViews()
{
fScrollView->FrameResized(fScrollView->Frame().Width(),
fScrollView->Frame().Height());
fScrollView->FrameResized(fScrollView->Frame().Width(), fScrollView->Frame().Height());
}
+11 -5
View File
@@ -1,12 +1,14 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef FEATURED_PACKAGES_VIEW_H
#define FEATURED_PACKAGES_VIEW_H
#include <vector>
#include <View.h>
#include "Model.h"
@@ -24,17 +26,21 @@ public:
virtual void DoLayout();
void BeginAddRemove();
void EndAddRemove();
void AddPackage(const PackageInfoRef& package);
void RemovePackage(const PackageInfoRef& package);
void RetainPackages(const std::vector<PackageInfoRef>& packages);
void AddRemovePackages(const std::vector<PackageInfoRef>& addedPackages,
const std::vector<PackageInfoRef>& removedPackages);
void Clear();
void SelectPackage(const PackageInfoRef& package,
bool scrollToEntry = false);
void HandleIconsChanged();
void HandlePackagesChanged(const PackageInfoEvents& events);
private:
void _AdjustViews();
void _HandlePackageChanged(const PackageInfoEvent& event);
private:
Model& fModel;
+18 -22
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2019-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2019-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -36,15 +36,12 @@ FilterView::FilterView()
fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu);
// Construct search terms field
fSearchTermsText = new BTextControl("search terms",
B_TRANSLATE("Search terms:"), "", NULL);
fSearchTermsText->SetModificationMessage(
new BMessage(MSG_SEARCH_TERMS_MODIFIED));
fSearchTermsText = new BTextControl("search terms", B_TRANSLATE("Search terms:"), "", NULL);
fSearchTermsText->SetModificationMessage(new BMessage(MSG_SEARCH_TERMS_MODIFIED));
BSize minSearchSize = fSearchTermsText->MinSize();
float minSearchWidth
= be_plain_font->StringWidth(fSearchTermsText->Label())
+ be_plain_font->StringWidth("XXX") * 6;
float minSearchWidth = be_plain_font->StringWidth(fSearchTermsText->Label())
+ be_plain_font->StringWidth("XXX") * 6;
minSearchWidth = std::max(minSearchSize.width, minSearchWidth);
minSearchSize.width = minSearchWidth;
fSearchTermsText->SetExplicitMinSize(minSearchSize);
@@ -107,27 +104,25 @@ FilterView::AdoptModel(Model& model)
BMenu* showMenu = fShowField->Menu();
showMenu->RemoveItems(0, showMenu->CountItems(), true);
showMenu->AddItem(new BMenuItem(B_TRANSLATE("All categories"),
new BMessage(MSG_CATEGORY_SELECTED)));
showMenu->AddItem(
new BMenuItem(B_TRANSLATE("All categories"), new BMessage(MSG_CATEGORY_SELECTED)));
AutoLocker<BLocker> locker(model.Lock());
int32 categoryCount = model.CountCategories();
if (categoryCount > 0) {
if (model.HasCategories()) {
showMenu->AddItem(new BSeparatorItem());
_AddCategoriesToMenu(model, showMenu);
showMenu->SetEnabled(true);
} else {
showMenu->SetEnabled(false);
}
showMenu->SetEnabled(categoryCount > 0);
if (!_SelectCategoryCode(showMenu, model.PackageFilter()->Category()))
if (!_SelectCategoryCode(showMenu, model.FilterSpecification()->Category()))
showMenu->ItemAt(0)->SetMarked(true);
}
/*! Tries to mark the menu item that corresponds to the supplied
category code. If the supplied code was found and the item marked
then the method will return true.
category code. If the supplied code was found and the item marked
then the method will return true.
*/
/*static*/ bool
@@ -159,9 +154,10 @@ FilterView::_MatchesCategoryCode(BMenuItem* item, const BString& code)
/*static*/ void
FilterView::_AddCategoriesToMenu(Model& model, BMenu* menu)
{
int count = model.CountCategories();
for (int i = 0; i < count; i++) {
const CategoryRef& category = model.CategoryAtIndex(i);
std::vector<CategoryRef> categories = model.Categories();
std::vector<CategoryRef>::const_iterator it;
for (it = categories.begin(); it != categories.end(); it++) {
const CategoryRef& category = *it;
BMessage* message = new BMessage(MSG_CATEGORY_SELECTED);
message->AddString("code", category->Code());
BMenuItem* item = new BMenuItem(category->Name(), message);
File diff suppressed because it is too large Load Diff
+13 -6
View File
@@ -2,7 +2,7 @@
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent <rene@gollent.com>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2017-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MAIN_WINDOW_H
@@ -41,7 +41,7 @@ class MainWindow :
public:
MainWindow(const BMessage& settings);
MainWindow(const BMessage& settings,
PackageInfoRef& package);
const PackageInfoRef package);
virtual ~MainWindow();
// BWindow interface
@@ -63,12 +63,13 @@ public:
const UserDetail& userDetail);
// services PackageInfoListener via MainWindowPackageInfoListener
void PackageChanged(
const PackageInfoEvent& event);
void PackagesChanged(const PackageInfoEvents& events);
private:
static const BString _WindowTitleForPackage(const PackageInfoRef& pkg);
const std::vector<PackageInfoRef>
_CreateSnapshotOfFilteredPackages();
std::vector<DepotInfoRef>
_CreateSnapshotOfDepots();
@@ -95,8 +96,6 @@ private:
void _AdoptModelControls();
void _AdoptModel();
void _AddRemovePackageFromLists(
const PackageInfoRef& package);
void _AdoptPackage(const PackageInfoRef& package);
void _ClearPackage();
@@ -128,6 +127,9 @@ private:
void _HandleProcessCoordinatorChanged(
ProcessCoordinatorState& coordinatorState);
void _HandlePackagesChanged(const BMessage* message);
void _HandlePackagesChanged(const PackageInfoEvents& events);
static status_t _RefreshModelThreadWorker(void* arg);
static status_t _PopulatePackageWorker(void* arg);
static status_t _PackagesToShowWorker(void* arg);
@@ -148,6 +150,11 @@ private:
const UserDetail& userDetail);
void _HandleScreenshotCached(const BMessage* message);
void _HandleIconsChanged();
void _SetStateForPackagesByName(
BStringList& packageNames,
PackageState state);
private:
FilterView* fFilterView;
@@ -1,93 +0,0 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "MessagePackageListener.h"
#include <Messenger.h>
#include <View.h>
#include "PackageInfo.h"
// #pragma mark - MessagePackageListener
MessagePackageListener::MessagePackageListener(BHandler* target)
:
fTarget(target),
fChangesMask(0xffffffff)
{
}
MessagePackageListener::~MessagePackageListener()
{
}
void
MessagePackageListener::PackageChanged(const PackageInfoEvent& event)
{
if ((event.Changes() & fChangesMask) == 0)
return;
BMessenger messenger(fTarget);
if (!messenger.IsValid())
return;
BMessage message(MSG_UPDATE_PACKAGE);
message.AddString("name", event.Package()->Name());
message.AddUInt32("changes", event.Changes());
messenger.SendMessage(&message);
}
void
MessagePackageListener::SetChangesMask(uint32 mask)
{
fChangesMask = mask;
}
// #pragma mark - OnePackageMessagePackageListener
OnePackageMessagePackageListener::OnePackageMessagePackageListener(BHandler* target)
:
MessagePackageListener(target)
{
}
OnePackageMessagePackageListener::~OnePackageMessagePackageListener()
{
}
void
OnePackageMessagePackageListener::SetPackage(const PackageInfoRef& package)
{
if (fPackage == package)
return;
PackageInfoListenerRef listener(this);
if (fPackage.IsSet())
fPackage->RemoveListener(listener);
fPackage = package;
if (fPackage.IsSet())
fPackage->AddListener(listener);
}
const PackageInfoRef&
OnePackageMessagePackageListener::Package() const
{
return fPackage;
}

Some files were not shown because too many files have changed in this diff Show More