HaikuDepot: Load Languages from HDS

The HaikuDepot application has, thus far had its
own hard-coded list of languages that the user is
able to choose when (a) creating a new account or
(b) creating a user-rating.  This change will mean
that those languages are loaded from the HDS
server dynamically and in this way the user can
choose from the full list.  There have also been
improvements to the way in which the languages are
displayed in the menu as well.

Change-Id: If7cb7b87f348ca59d503d276a22444e72d0e6168
Reviewed-on: https://review.haiku-os.org/c/1425
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
Andrew Lindesay
2019-05-15 19:03:48 +00:00
parent 9fe74faa61
commit f85e030047
25 changed files with 950 additions and 174 deletions
+8 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* Copyright 2018-2019, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef HAIKU_DEPOT_CONSTANTS_H
@@ -18,7 +18,8 @@ enum {
MSG_SERVER_DATA_CHANGED = 'svdc',
MSG_ALERT_SIMPLE_ERROR = 'nser',
MSG_DID_ADD_USER_RATING = 'adur',
MSG_DID_UPDATE_USER_RATING = 'upur'
MSG_DID_UPDATE_USER_RATING = 'upur',
MSG_LANGUAGE_SELECTED = 'lngs'
};
@@ -54,4 +55,9 @@ enum {
RSRC_ARROW_RIGHT = 550,
};
#define LANGUAGE_DEFAULT_CODE "en"
#define LANGUAGE_DEFAULT Language(LANGUAGE_DEFAULT_CODE, "English", true)
#endif // HAIKU_DEPOT_CONSTANTS_H
+58 -4
View File
@@ -16,12 +16,19 @@ local dumpExportRepositoryBulkListerTargetDirectory =
local dumpExportPkgBulkListenerTargetDirectory =
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
dumpexportpkgbulklistener ] ;
local dumpExportReferenceListenerTargetDirectory =
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
dumpexportreferencelistener ] ;
local dumpExportPkgModelTargetDirectory =
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
dumpexportpkgmodel ] ;
local dumpExportRepositoryModelTargetDirectory =
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
dumpexportrepositorymodel ] ;
local dumpExportReferenceModelTargetDirectory =
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
dumpexportreferencemodel ] ;
# During the build process, some sources are generated into a directory. In
# order to maintain a timestamp on that generation process, a dummy file is
@@ -34,10 +41,15 @@ local dumpExportRepositoryBulkListerTargetFile =
[ FDirName $(dumpExportRepositoryBulkListerTargetDirectory) $(dummyFile) ] ;
local dumpExportPkgBulkListenerTargetFile =
[ FDirName $(dumpExportPkgBulkListenerTargetDirectory) $(dummyFile) ] ;
local dumpExportReferenceListenerTargetFile =
[ FDirName $(dumpExportReferenceListenerTargetDirectory) $(dummyFile) ] ;
local dumpExportPkgModelTargetFile =
[ FDirName $(dumpExportPkgModelTargetDirectory) $(dummyFile) ] ;
local dumpExportRepositoryModelTargetFile =
[ FDirName $(dumpExportRepositoryModelTargetDirectory) $(dummyFile) ] ;
local dumpExportReferenceModelTargetFile =
[ FDirName $(dumpExportReferenceModelTargetDirectory) $(dummyFile) ] ;
# source directories
local sourceDirs =
@@ -57,10 +69,14 @@ for sourceDir in $(sourceDirs) {
}
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers package ] ;
SEARCH_SOURCE += $(dumpExportRepositoryBulkListerTargetDirectory) ;
SEARCH_SOURCE += $(dumpExportPkgBulkListenerTargetDirectory) ;
SEARCH_SOURCE += $(dumpExportReferenceListenerTargetDirectory) ;
SEARCH_SOURCE += $(dumpExportPkgModelTargetDirectory) ;
SEARCH_SOURCE += $(dumpExportRepositoryModelTargetDirectory) ;
SEARCH_SOURCE += $(dumpExportReferenceModelTargetDirectory) ;
local textDocumentSources =
# edits_generic
@@ -101,6 +117,7 @@ local applicationSources =
FilterView.cpp
LocalIconStore.cpp
JobStateListener.cpp
LanguageModel.cpp
LinkView.cpp
LinkedBitmapView.cpp
Logger.cpp
@@ -138,6 +155,7 @@ local applicationSources =
ServerHelper.cpp
ServerSettings.cpp
ServerPkgDataUpdateProcess.cpp
ServerReferenceDataUpdateProcess.cpp
ServerRepositoryDataUpdateProcess.cpp
ServerIconExportUpdateProcess.cpp
StandardMetaDataJsonEventListener.cpp
@@ -151,6 +169,7 @@ local applicationSources =
# util
AppUtils.cpp
DataIOUtils.cpp
LanguageMenuUtils.cpp
RepositoryUrlUtils.cpp
StorageUtils.cpp
ToFileUrlProtocolListener.cpp
@@ -173,6 +192,14 @@ local generatedRepositoryModelSourceFiles =
DumpExportRepositorySourceMirror.cpp
;
local generatedReferenceModelSourceFiles =
DumpExportReference.cpp
DumpExportReferenceNaturalLanguage.cpp
DumpExportReferencePkgCategory.cpp
DumpExportReferenceUserRatingStability.cpp
DumpExportReferenceCountry.cpp
;
local generatedPkgParserSourceFiles =
DumpExportPkgJsonListener.cpp
;
@@ -181,10 +208,19 @@ local generatedRepositoryParserSourceFiles =
DumpExportRepositoryJsonListener.cpp
;
local generatedReferenceParserSourceFiles =
DumpExportReferenceJsonListener.cpp
;
Application HaikuDepot
: $(applicationSources) $(textDocumentSources)
$(generatedPkgModelSourceFiles) $(generatedRepositoryModelSourceFiles)
$(generatedPkgParserSourceFiles) $(generatedRepositoryParserSourceFiles)
: $(applicationSources)
$(textDocumentSources)
$(generatedPkgModelSourceFiles)
$(generatedRepositoryModelSourceFiles)
$(generatedReferenceModelSourceFiles)
$(generatedPkgParserSourceFiles)
$(generatedRepositoryParserSourceFiles)
$(generatedReferenceParserSourceFiles)
: be package bnetapi translation libcolumnlistview.a shared
[ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub
: HaikuDepot.rdef
@@ -225,12 +261,18 @@ HdsSchemaGenModel $(dumpExportPkgModelTargetFile)
HdsSchemaGenModel $(dumpExportRepositoryModelTargetFile)
: dumpexportrepository.json : jsonschema2cppmodel.py ;
HdsSchemaGenModel $(dumpExportReferenceModelTargetFile)
: dumpexportreference.json : jsonschema2cppmodel.py ;
HdsSchemaGenBulkParser $(dumpExportRepositoryBulkListerTargetFile)
: dumpexportrepository.json : jsonschema2cppparser.py ;
HdsSchemaGenBulkParser $(dumpExportPkgBulkListenerTargetFile)
: dumpexportpkg.json : jsonschema2cppparser.py ;
HdsSchemaGenParser $(dumpExportReferenceListenerTargetFile)
: dumpexportreference.json : jsonschema2cppparser.py ;
HdsSchemaGenAppSrcDependsOnGeneration
[ FGristFiles $(generatedPkgParserSourceFiles) ]
[ FGristFiles $(generatedPkgParserSourceFiles:S=.h) ]
@@ -241,6 +283,11 @@ HdsSchemaGenAppSrcDependsOnGeneration
[ FGristFiles $(generatedRepositoryParserSourceFiles:S=.h) ]
: $(dumpExportRepositoryBulkListerTargetFile) ;
HdsSchemaGenAppSrcDependsOnGeneration
[ FGristFiles $(generatedReferenceParserSourceFiles) ]
[ FGristFiles $(generatedReferenceParserSourceFiles:S=.h) ]
: $(dumpExportReferenceListenerTargetFile) ;
HdsSchemaGenAppSrcDependsOnGeneration
[ FGristFiles $(generatedRepositoryModelSourceFiles) ]
[ FGristFiles $(generatedRepositoryModelSourceFiles:S=.h) ]
@@ -251,13 +298,20 @@ HdsSchemaGenAppSrcDependsOnGeneration
[ FGristFiles $(generatedPkgModelSourceFiles:S=.h) ]
: $(dumpExportPkgModelTargetFile) ;
HdsSchemaGenAppSrcDependsOnGeneration
[ FGristFiles $(generatedReferenceModelSourceFiles) ]
[ FGristFiles $(generatedReferenceModelSourceFiles:S=.h) ]
: $(dumpExportReferenceModelTargetFile) ;
# This will ensure that if any of the generated files' header files change, then
# the application should be re-built.
Depends [ FGristFiles $(applicationSources:S=.o) ]
:
[ FGristFiles $(generatedPkgParserSourceFiles:S=.h) ]
[ FGristFiles $(generatedPkgModelSourceFiles:S=.h) ]
[ FGristFiles $(generatedRepositoryParserSourceFiles:S=.h) ]
[ FGristFiles $(generatedRepositoryModelSourceFiles:S=.h) ]
[ FGristFiles $(generatedPkgModelSourceFiles:S=.h) ]
[ FGristFiles $(generatedReferenceParserSourceFiles:S=.h) ]
[ FGristFiles $(generatedReferenceModelSourceFiles:S=.h) ]
;
@@ -72,6 +72,29 @@ actions HdsSchemaGenBulkParser1
touch $(1)
}
# pragma mark - Non-Bulk Parsing Class Generation
# 1 : the dummy file in the class generation directory (target)
# 2 : the JSON schema file
# 3 : the Python script to use
rule HdsSchemaGenParser
{
SEARCH on $(2) = [ FDirName $(SUBDIR) server schema ] ;
SEARCH on $(3) = [ FDirName $(SUBDIR) build scripts ] ;
Clean $(1:D) ;
Depends $(1) : $(2) $(3) ;
HdsSchemaGenParser1 $(1) : $(2) $(3) $(1:D) ;
}
actions HdsSchemaGenParser1
{
mkdir -p $(2[3])
$(HOST_PYTHON) $(2[2]) -i $(2[1]) --outputdirectory $(2[3])
touch $(1)
}
# pragma mark - Registering Generated Classes
# Because a number of .cpp and .h files will be generated from a single python
@@ -152,7 +152,27 @@ def writescalaraccessors(outputfile, cppclassname, cppname, cppmembername, cppty
string.Template("""
${cpptype}
${cppclassname}::${cppname}()
{
{""").substitute(dict))
if cpptype == jscom.CPP_TYPE_BOOLEAN:
outputfile.write(string.Template("""
if (${cppname}IsNull())
return false;
""").substitute(dict))
if cpptype == jscom.CPP_TYPE_INTEGER:
outputfile.write(string.Template("""
if (${cppname}IsNull())
return 0;
""").substitute(dict))
if cpptype == jscom.CPP_TYPE_NUMBER:
outputfile.write(string.Template("""
if (${cppname}IsNull())
return 0.0;
""").substitute(dict))
outputfile.write(string.Template("""
return *${cppmembername};
}
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright 2019, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LanguageModel.h"
#include <Collator.h>
#include <Locale.h>
#include <LocaleRoster.h>
#include "HaikuDepotConstants.h"
#include "Logger.h"
static int32
LanguagesCompareFn(const LanguageRef& l1, const LanguageRef& l2)
{
const BLocale* locale = BLocaleRoster::Default()->GetDefaultLocale();
BCollator collator;
if (B_OK != locale->GetCollator(&collator)) {
debugger("unable to get the locale's collator");
}
BString name1;
BString name2;
if (l1->GetName(name1) == B_OK && l2->GetName(name2) == B_OK)
return collator.Compare(name1.String(), name2.String());
return collator.Compare(l1->Code(), l2->Code());
}
LanguageModel::LanguageModel()
:
fSupportedLanguages(LanguagesCompareFn, NULL),
fPreferredLanguage(LANGUAGE_DEFAULT)
{
const Language defaultLanguage = _DeriveDefaultLanguage();
fSupportedLanguages.Add(LanguageRef(
new Language(defaultLanguage)));
_SetPreferredLanguage(defaultLanguage);
}
LanguageModel::~LanguageModel()
{
}
void
LanguageModel::AddSupportedLanguages(const LanguageList& languages)
{
for (int32 i = 0; i < languages.CountItems(); i++) {
const LanguageRef language = languages.ItemAt(i);
int32 index = IndexOfSupportedLanguage(language->Code());
if (index == -1)
fSupportedLanguages.Add(language);
else
fSupportedLanguages.Replace(index, language);
}
// 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.
_SetPreferredLanguage(_DeriveDefaultLanguage());
}
void
LanguageModel::_SetPreferredLanguage(const Language& language)
{
fPreferredLanguage = language;
if(Logger::IsDebugEnabled())
printf("set preferred language [%s]\n", language.Code());
}
int32
LanguageModel::IndexOfSupportedLanguage(const BString& languageCode) const
{
for (int32 i = 0; i < fSupportedLanguages.CountItems(); i++) {
if (fSupportedLanguages.ItemAt(i)->Code() == languageCode)
return i;
}
return -1;
}
/*! This will derive the default language. If there are no other
possible languages configured then the default language will be
assumed to exist. Otherwise if there is a set of possible languages
then this method will ensure that the default language is in that
set.
*/
Language
LanguageModel::_DeriveDefaultLanguage() const
{
Language defaultLanguage = _DeriveSystemDefaultLanguage();
if(Logger::IsDebugEnabled()) {
printf("derived system default language [%s]\n",
defaultLanguage.Code());
}
// if there are no supported languages; as is the case to start with as the
// application starts, the default language from the system is used anyway.
// The data queried in HDS will handle the case where the language is not
// 'known' at the HDS end so it doesn't matter if it is invalid.
if (fSupportedLanguages.IsEmpty())
return defaultLanguage;
// if there are supported languages defined then the preferred language
// needs to be one of the supported ones.
Language* foundSupportedLanguage = _FindSupportedLanguage(
defaultLanguage.Code());
if (foundSupportedLanguage == NULL) {
printf("unable to find the language [%s] - looking for app default",
defaultLanguage.Code());
foundSupportedLanguage = _FindSupportedLanguage(
LANGUAGE_DEFAULT.Code());
}
if (foundSupportedLanguage == NULL) {
printf("unable to find the app default language - using the first "
"supported language");
foundSupportedLanguage = fSupportedLanguages.ItemAt(0);
}
return Language(*foundSupportedLanguage);
}
/*static*/ Language
LanguageModel::_DeriveSystemDefaultLanguage()
{
BLocaleRoster* localeRoster = BLocaleRoster::Default();
if (localeRoster != NULL) {
BMessage preferredLanguages;
if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
BString language;
BString languageIso;
if (preferredLanguages.FindString(
"language", 0, &language) == B_OK) {
language.CopyInto(languageIso, 0, 2);
return Language(languageIso, languageIso, true);
}
}
}
return LANGUAGE_DEFAULT;
}
Language*
LanguageModel::_FindSupportedLanguage(const BString& code) const
{
int32 index = IndexOfSupportedLanguage(code);
if (-1 == index)
return NULL;
return fSupportedLanguages.ItemAt(index);
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright 2019, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LANGUAGE_MODEL_H
#define LANGUAGE_MODEL_H
#include <Referenceable.h>
#include "List.h"
#include "PackageInfo.h"
typedef BReference<Language> LanguageRef;
typedef List<LanguageRef, false> LanguageList;
class LanguageModel {
public:
LanguageModel();
virtual ~LanguageModel();
const LanguageList& SupportedLanguages() const
{ return fSupportedLanguages; }
void AddSupportedLanguages(
const LanguageList& languages);
int32 IndexOfSupportedLanguage(
const BString& languageCode) const;
const Language& PreferredLanguage() const
{ return fPreferredLanguage; }
private:
static Language _DeriveSystemDefaultLanguage();
Language _DeriveDefaultLanguage() const;
Language* _FindSupportedLanguage(
const BString& code) const;
void _SetPreferredLanguage(const Language& language);
private:
LanguageList fSupportedLanguages;
Language fPreferredLanguage;
};
#endif // LANGUAGE_MODEL_H
+44 -47
View File
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2014, Axel Dörfler <[email protected]>.
* Copyright 2016-2018, Andrew Lindesay <[email protected]>.
* Copyright 2016-2019, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -353,7 +353,6 @@ Model::Model()
fDepotFilter(""),
fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
fIsFeaturedFilter(),
fShowFeaturedPackages(true),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
@@ -378,38 +377,6 @@ Model::Model()
// get the defined categories and their translated names.
// This should then be used instead of hard-coded
// categories and translations in the app.
fPreferredLanguage = "en";
BLocaleRoster* localeRoster = BLocaleRoster::Default();
if (localeRoster != NULL) {
BMessage preferredLanguages;
if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
BString language;
if (preferredLanguages.FindString("language", 0, &language) == B_OK)
language.CopyInto(fPreferredLanguage, 0, 2);
}
}
// TODO: Fetch this from the web-app.
fSupportedLanguages.Add("en");
fSupportedLanguages.Add("es");
fSupportedLanguages.Add("de");
fSupportedLanguages.Add("fr");
fSupportedLanguages.Add("it");
fSupportedLanguages.Add("ja");
fSupportedLanguages.Add("pt");
fSupportedLanguages.Add("ru");
fSupportedLanguages.Add("sk");
fSupportedLanguages.Add("zh");
if (!fSupportedLanguages.Contains(fPreferredLanguage)) {
// Force the preferred language to one of the currently supported
// ones, until the web application supports all ISO language codes.
printf("User preferred language '%s' not currently supported, "
"defaulting to 'en'.\n", fPreferredLanguage.String());
fPreferredLanguage = "en";
}
fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
}
@@ -418,6 +385,13 @@ Model::~Model()
}
LanguageModel&
Model::Language()
{
return fLanguageModel;
}
bool
Model::AddListener(const ModelListenerRef& listener)
{
@@ -903,6 +877,30 @@ Model::SetAuthorization(const BString& username, const BString& password,
}
status_t
Model::_LocalDataPath(const BString leaf, BPath& path) const
{
BString leafAssembled(leaf);
leafAssembled.ReplaceAll("%languageCode%",
LanguageModel().PreferredLanguage().Code());
BPath repoDataPath;
if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
&& repoDataPath.Append("HaikuDepot") == B_OK
&& create_directory(repoDataPath.Path(), 0777) == B_OK
&& repoDataPath.Append(leafAssembled) == B_OK) {
path.SetTo(repoDataPath.Path());
return B_OK;
}
path.Unset();
fprintf(stdout, "unable to find the user cache file for [%s] data",
leaf.String());
return B_ERROR;
}
/*! When bulk repository data comes down from the server, it will
arrive as a json.gz payload. This is stored locally as a cache
and this method will provide the on-disk storage location for
@@ -912,20 +910,19 @@ Model::SetAuthorization(const BString& username, const BString& password,
status_t
Model::DumpExportRepositoryDataPath(BPath& path) const
{
BPath repoDataPath;
return _LocalDataPath("repository-all_%languageCode%.json.gz", path);
}
if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
&& repoDataPath.Append("HaikuDepot") == B_OK
&& create_directory(repoDataPath.Path(), 0777) == B_OK
&& repoDataPath.Append("repository-all_en.json.gz") == B_OK) {
path.SetTo(repoDataPath.Path());
return B_OK;
}
path.Unset();
fprintf(stdout, "unable to find the user cache file for repositories'"
" data");
return B_ERROR;
/*! When the system downloads reference data (eg; categories) from the server
then the downloaded data is stored and cached at the path defined by this
method.
*/
status_t
Model::DumpExportReferenceDataPath(BPath& path) const
{
return _LocalDataPath("reference-all_%languageCode%.json.gz", path);
}
@@ -956,7 +953,7 @@ Model::DumpExportPkgDataPath(BPath& path,
BString leafName;
leafName.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
fPreferredLanguage.String());
LanguageModel().PreferredLanguage().Code());
if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
&& repoDataPath.Append("HaikuDepot") == B_OK
+9 -10
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2018, Andrew Lindesay <[email protected]>.
* Copyright 2016-2019, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MODEL_H
@@ -10,6 +10,7 @@
#include <Locker.h>
#include "AbstractProcess.h"
#include "LanguageModel.h"
#include "LocalIconStore.h"
#include "PackageInfo.h"
#include "WebAppInterface.h"
@@ -63,6 +64,8 @@ public:
Model();
virtual ~Model();
LanguageModel& Language();
BLocker* Lock()
{ return &fLock; }
@@ -150,12 +153,6 @@ public:
void PopulatePackage(const PackageInfoRef& package,
uint32 flags);
const StringList& SupportedLanguages() const
{ return fSupportedLanguages; }
const BString& PreferredLanguage() const
{ return fPreferredLanguage; }
void SetUsername(BString username);
const BString& Username() const;
void SetAuthorization(const BString& username,
@@ -171,6 +168,7 @@ public:
void* context);
status_t IconStoragePath(BPath& path) const;
status_t DumpExportReferenceDataPath(BPath& path) const;
status_t DumpExportRepositoryDataPath(BPath& path) const;
status_t DumpExportPkgDataPath(BPath& path,
const BString& repositorySourceCode) const;
@@ -178,6 +176,9 @@ public:
void LogDepotsWithNoWebAppRepositoryCode() const;
private:
status_t _LocalDataPath(const BString leaf,
BPath& path) const;
void _MaybeLogJsonRpcError(
const BMessage &responsePayload,
const char *sourceDescription) const;
@@ -245,9 +246,7 @@ private:
bool fShowSourcePackages;
bool fShowDevelopPackages;
StringList fSupportedLanguages;
BString fPreferredLanguage;
LanguageModel fLanguageModel;
WebAppInterface fWebAppInterface;
ModelListenerList fListeners;
+36 -1
View File
@@ -1,7 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2018, Andrew Lindesay <[email protected]>.
* Copyright 2016-2019, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -15,6 +15,41 @@
#include <Path.h>
// #pragma mark - Language
Language::Language(const char* language, const BString& serverName,
bool isPopular)
:
BLanguage(language),
fServerName(serverName),
fIsPopular(isPopular)
{
}
Language::Language(const Language& other)
:
BLanguage(other.Code()),
fServerName(other.fServerName),
fIsPopular(other.fIsPopular)
{
}
status_t
Language::GetName(BString& name,
const BLanguage* displayLanguage) const
{
status_t result = BLanguage::GetName(name, displayLanguage);
if (result == B_OK && (name.IsEmpty() || name == Code()))
name.SetTo(fServerName);
return result;
}
// #pragma mark - UserInfo
+26 -1
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_INFO_H
@@ -9,6 +9,7 @@
#include <set>
#include <Language.h>
#include <Referenceable.h>
#include <package/PackageInfo.h>
@@ -21,6 +22,30 @@
class BPath;
/*! 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.
*/
class Language : public BReferenceable, public BLanguage {
public:
Language(const char* language,
const BString& serverName,
bool isPopular);
Language(const Language& other);
status_t GetName(BString& name,
const BLanguage* displayLanguage = NULL
) const;
bool IsPopular() const
{ return fIsPopular; }
private:
BString fServerName;
bool fIsPopular;
};
class UserInfo {
public:
UserInfo();
@@ -1,5 +1,5 @@
/*
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -21,6 +21,7 @@
#include "ServerHelper.h"
#include "ServerIconExportUpdateProcess.h"
#include "ServerPkgDataUpdateProcess.h"
#include "ServerReferenceDataUpdateProcess.h"
#include "ServerRepositoryDataUpdateProcess.h"
#include "ServerSettings.h"
@@ -62,6 +63,11 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverRepositoryDataUpdate);
ProcessNode *serverReferenceDataUpdate =
new ProcessNode(new ServerReferenceDataUpdateProcess(model,
serverProcessOptions));
processCoordinator->AddNode(serverReferenceDataUpdate);
// create a process for each of the repositories that are configured on the
// local system. Later, only those that have a web-app repository server
// code will be actually processed, but this means that the creation of the
@@ -74,9 +80,11 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
if (repoNamesResult == B_OK) {
for (int32 i = 0; i < repoNames.CountStrings(); i++) {
ProcessNode* processNode = new ProcessNode(
new ServerPkgDataUpdateProcess(model->PreferredLanguage(),
new ServerPkgDataUpdateProcess(
model->Language().PreferredLanguage().Code(),
repoNames.StringAt(i), model, serverProcessOptions));
processNode->AddPredecessor(serverRepositoryDataUpdate);
processNode->AddPredecessor(serverReferenceDataUpdate);
processCoordinator->AddNode(processNode);
}
} else {
@@ -0,0 +1,152 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerReferenceDataUpdateProcess.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <AutoDeleter.h>
#include <Catalog.h>
#include <FileIO.h>
#include <Url.h>
#include "ServerSettings.h"
#include "StorageUtils.h"
#include "Logger.h"
#include "DumpExportReference.h"
#include "DumpExportReferenceNaturalLanguage.h"
#include "DumpExportReferencePkgCategory.h"
#include "DumpExportReferenceUserRatingStability.h"
#include "DumpExportReferenceCountry.h"
#include "DumpExportReferenceJsonListener.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess"
ServerReferenceDataUpdateProcess::ServerReferenceDataUpdateProcess(
Model* model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
fModel(model)
{
}
ServerReferenceDataUpdateProcess::~ServerReferenceDataUpdateProcess()
{
}
const char*
ServerReferenceDataUpdateProcess::Name() const
{
return "ServerReferenceDataUpdateProcess";
}
const char*
ServerReferenceDataUpdateProcess::Description() const
{
return B_TRANSLATE("Synchronizing reference data from server");
}
BString
ServerReferenceDataUpdateProcess::UrlPathComponent()
{
BString result;
result.SetToFormat("/__reference/all-%s.json.gz",
fModel->Language().PreferredLanguage().Code());
return result;
}
status_t
ServerReferenceDataUpdateProcess::GetLocalPath(BPath& path) const
{
return fModel->DumpExportReferenceDataPath(path);
}
status_t
ServerReferenceDataUpdateProcess::ProcessLocalData()
{
SingleDumpExportReferenceJsonListener* listener =
new SingleDumpExportReferenceJsonListener();
BPath localPath;
status_t result = GetLocalPath(localPath);
if (result != B_OK)
return result;
result = ParseJsonFromFileWithListener(listener, localPath);
if (result != B_OK)
return result;
result = listener->ErrorStatus();
if (result != B_OK)
return result;
return _ProcessData(listener->Target());
}
status_t
ServerReferenceDataUpdateProcess::_ProcessData(DumpExportReference* data)
{
// more to come here later...
return _ProcessNaturalLanguages(data);
}
status_t
ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(
DumpExportReference* data)
{
printf("[%s] will populate %" B_PRId32 " natural languages\n",
Name(), data->CountNaturalLanguages());
LanguageList result;
for (int32 i = 0; i < data->CountNaturalLanguages(); i++) {
DumpExportReferenceNaturalLanguage* naturalLanguage =
data->NaturalLanguagesItemAt(i);
result.Add(LanguageRef(new Language(
*(naturalLanguage->Code()),
*(naturalLanguage->Name()),
naturalLanguage->IsPopular())));
}
fModel->Language().AddSupportedLanguages(result);
printf("[%s] did add %" B_PRId32 " supported languages\n",
Name(), result.CountItems());
return B_OK;
}
status_t
ServerReferenceDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
{
return GetLocalPath(path);
}
void
ServerReferenceDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
{
jsonPath.SetTo("$.info");
}
@@ -0,0 +1,55 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SERVER_REFERENCE_DATA_UPDATE_PROCESS_H
#define SERVER_REFERENCE_DATA_UPDATE_PROCESS_H
#include "AbstractSingleFileServerProcess.h"
#include <File.h>
#include <Path.h>
#include <String.h>
#include <Url.h>
#include "Model.h"
#include "PackageInfo.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess"
class DumpExportReference;
class ServerReferenceDataUpdateProcess : public AbstractSingleFileServerProcess
{
public:
ServerReferenceDataUpdateProcess(
Model* model, uint32 serverProcessOptions);
virtual ~ServerReferenceDataUpdateProcess();
const char* Name() const;
const char* Description() const;
protected:
status_t GetStandardMetaDataPath(BPath& path) const;
void GetStandardMetaDataJsonPath(
BString& jsonPath) const;
BString UrlPathComponent();
status_t ProcessLocalData();
status_t GetLocalPath(BPath& path) const;
private:
status_t _ProcessData(DumpExportReference* data);
status_t _ProcessNaturalLanguages(
DumpExportReference* data);
private:
Model* fModel;
};
#endif // SERVER_REFERENCE_DATA_UPDATE_PROCESS_H
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -177,7 +177,10 @@ ServerRepositoryDataUpdateProcess::Description() const
BString
ServerRepositoryDataUpdateProcess::UrlPathComponent()
{
return BString("/__repository/all-en.json.gz");
BString result;
result.SetToFormat("/__repository/all-%s.json.gz",
fModel->Language().PreferredLanguage().Code());
return result;
}
+2 -15
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -271,8 +271,6 @@ enum {
WebAppInterface::WebAppInterface()
:
fLanguage("en")
{
}
@@ -280,8 +278,7 @@ WebAppInterface::WebAppInterface()
WebAppInterface::WebAppInterface(const WebAppInterface& other)
:
fUsername(other.fUsername),
fPassword(other.fPassword),
fLanguage(other.fLanguage)
fPassword(other.fPassword)
{
}
@@ -296,11 +293,8 @@ WebAppInterface::operator=(const WebAppInterface& other)
{
if (this == &other)
return *this;
fUsername = other.fUsername;
fPassword = other.fPassword;
fLanguage = other.fLanguage;
return *this;
}
@@ -314,13 +308,6 @@ WebAppInterface::SetAuthorization(const BString& username,
}
void
WebAppInterface::SetPreferredLanguage(const BString& language)
{
fLanguage = language;
}
status_t
WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
{
+1 -5
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef WEB_APP_INTERFACE_H
@@ -51,9 +51,6 @@ public:
const BString& Username() const
{ return fUsername; }
void SetPreferredLanguage(const BString& language);
void SetArchitecture(const BString& architecture);
status_t GetChangelog(
const BString& packageName,
BMessage& message);
@@ -130,7 +127,6 @@ private:
private:
BString fUsername;
BString fPassword;
BString fLanguage;
static int fRequestIndex;
};
@@ -0,0 +1,72 @@
{
"$schema": "http://json-schema.org/schema#",
"id": "http://depot.haiku-os.org/schema/reference/dumpreference.json",
"title": "Reference",
"javaType": "org.haiku.haikudepotserver.reference.model.dumpexport.DumpExportReference",
"type": "object",
"properties": {
"naturalLanguages": {
"type": "array",
"items": {
"type": "object",
"javaType": "org.haiku.haikudepotserver.reference.model.dumpexport.DumpExportReferenceNaturalLanguage",
"properties": {
"code": {
"type": "string"
},
"name": {
"type": "string"
},
"isPopular": {
"type": "boolean"
}
}
}
},
"pkgCategories": {
"type": "array",
"items": {
"type": "object",
"javaType": "org.haiku.haikudepotserver.reference.model.dumpexport.DumpExportReferencePkgCategory",
"properties": {
"code": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"userRatingStabilities": {
"type": "array",
"items": {
"type": "object",
"javaType": "org.haiku.haikudepotserver.reference.model.dumpexport.DumpExportReferenceUserRatingStability",
"properties": {
"code": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"countries": {
"type": "array",
"items": {
"type": "object",
"javaType": "org.haiku.haikudepotserver.reference.model.dumpexport.DumpExportReferenceCountry",
"properties": {
"code": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
}
}
}
+1 -27
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -1050,8 +1050,6 @@ public:
.Add(scrollView, 1.0f)
.SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f)
;
_InitPreferredLanguages();
}
virtual ~UserRatingsView()
@@ -1113,33 +1111,9 @@ public:
}
}
private:
void _InitPreferredLanguages()
{
fPreferredLanguages.Clear();
BLocaleRoster* localeRoster = BLocaleRoster::Default();
if (localeRoster == NULL)
return;
BMessage preferredLanguages;
if (localeRoster->GetPreferredLanguages(&preferredLanguages) != B_OK)
return;
BString language;
int32 index = 0;
while (preferredLanguages.FindString("language", index++,
&language) == B_OK) {
BString languageCode;
language.CopyInto(languageCode, 0, 2);
fPreferredLanguages.Add(languageCode);
}
}
private:
BGroupLayout* fRatingContainerLayout;
RatingSummaryView* fRatingSummaryView;
StringList fPreferredLanguages;
};
+13 -29
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -22,6 +22,7 @@
#include <StringView.h>
#include "HaikuDepotConstants.h"
#include "LanguageMenuUtils.h"
#include "MarkupParser.h"
#include "RatingView.h"
#include "ServerHelper.h"
@@ -37,7 +38,6 @@ enum {
MSG_SEND = 'send',
MSG_PACKAGE_RATED = 'rpkg',
MSG_STABILITY_SELECTED = 'stbl',
MSG_LANGUAGE_SELECTED = 'lngs',
MSG_RATING_ACTIVE_CHANGED = 'rtac',
MSG_RATING_DETERMINATE_CHANGED = 'rdch'
};
@@ -180,19 +180,6 @@ add_stabilities_to_menu(const StabilityRatingList& stabilities, BMenu* menu)
}
static void
add_languages_to_menu(const StringList& languages, BMenu* menu)
{
for (int i = 0; i < languages.CountItems(); i++) {
const BString& language = languages.ItemAtFast(i);
BMessage* message = new BMessage(MSG_LANGUAGE_SELECTED);
message->AddString("code", language);
BMenuItem* item = new BMenuItem(language, message);
menu->AddItem(item);
}
}
RatePackageWindow::RatePackageWindow(BWindow* parent, BRect frame,
Model& model)
:
@@ -204,7 +191,7 @@ RatePackageWindow::RatePackageWindow(BWindow* parent, BRect frame,
fTextEditor(new TextEditor(), true),
fRating(RATING_NONE),
fRatingDeterminate(false),
fCommentLanguage(fModel.PreferredLanguage()),
fCommentLanguageCode(fModel.Language().PreferredLanguage().Code()),
fWorkerThread(-1)
{
AddToSubset(parent);
@@ -260,13 +247,12 @@ RatePackageWindow::RatePackageWindow(BWindow* parent, BRect frame,
fCommentLanguageField = new BMenuField("language",
B_TRANSLATE("Comment language:"), languagesMenu);
add_languages_to_menu(fModel.SupportedLanguages(), languagesMenu);
LanguageMenuUtils::AddLanguagesToMenu(
fModel.Language().SupportedLanguages(),
languagesMenu);
languagesMenu->SetTargetForItems(this);
BMenuItem* defaultItem = languagesMenu->ItemAt(
fModel.SupportedLanguages().IndexOf(fCommentLanguage));
if (defaultItem != NULL)
defaultItem->SetMarked(true);
LanguageMenuUtils::MarkLanguageInMenu(fCommentLanguageCode,
languagesMenu);
fRatingActiveCheckBox = new BCheckBox("rating active",
B_TRANSLATE("Other users can see this rating"),
@@ -348,7 +334,7 @@ RatePackageWindow::MessageReceived(BMessage* message)
break;
case MSG_LANGUAGE_SELECTED:
message->FindString("code", &fCommentLanguage);
message->FindString("code", &fCommentLanguageCode);
break;
case MSG_RATING_DETERMINATE_CHANGED:
@@ -514,11 +500,9 @@ RatePackageWindow::_RelayServerDataToUI(BMessage& response)
item->SetMarked(true);
}
if (response.FindString("naturalLanguageCode",
&fCommentLanguage) == B_OK) {
BMenuItem* item = fCommentLanguageField->Menu()->ItemAt(
fModel.SupportedLanguages().IndexOf(fCommentLanguage));
if (item != NULL)
item->SetMarked(true);
&fCommentLanguageCode) == B_OK) {
LanguageMenuUtils::MarkLanguageInMenu(
fCommentLanguageCode, fCommentLanguageField->Menu());
}
double rating;
if (response.FindDouble("rating", &rating) == B_OK) {
@@ -649,7 +633,7 @@ RatePackageWindow::_SendRatingThread()
int rating = (int)fRating;
BString stability = fStability;
BString comment = fRatingText->Text();
BString languageCode = fCommentLanguage;
BString languageCode = fCommentLanguageCode;
BString ratingID = fRatingID;
bool active = fRatingActive;
+2 -2
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef RATE_PACKAGE_WINDOW_H
@@ -56,7 +56,7 @@ private:
bool fRatingDeterminate;
BString fStability;
StabilityRatingList fStabilityCodes;
BString fCommentLanguage;
BString fCommentLanguageCode;
BString fRatingID;
bool fRatingActive;
PackageInfoRef fPackage;
+14 -24
View File
@@ -1,5 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -21,6 +22,8 @@
#include <UnicodeChar.h>
#include "BitmapView.h"
#include "HaikuDepotConstants.h"
#include "LanguageMenuUtils.h"
#include "Model.h"
#include "TabView.h"
#include "WebAppInterface.h"
@@ -34,31 +37,17 @@ enum {
MSG_SEND = 'send',
MSG_TAB_SELECTED = 'tbsl',
MSG_CAPTCHA_OBTAINED = 'cpob',
MSG_VALIDATE_FIELDS = 'vldt',
MSG_LANGUAGE_SELECTED = 'lngs',
MSG_VALIDATE_FIELDS = 'vldt'
};
static void
add_languages_to_menu(const StringList& languages, BMenu* menu)
{
for (int i = 0; i < languages.CountItems(); i++) {
const BString& language = languages.ItemAtFast(i);
BMessage* message = new BMessage(MSG_LANGUAGE_SELECTED);
message->AddString("code", language);
BMenuItem* item = new BMenuItem(language, message);
menu->AddItem(item);
}
}
UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
:
BWindow(frame, B_TRANSLATE("Log in"),
B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
fPreferredLanguage(model.PreferredLanguage()),
fPreferredLanguageCode(model.Language().PreferredLanguage().Code()),
fModel(model),
fMode(NONE),
fWorkerThread(-1)
@@ -83,14 +72,15 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
fLanguageCodeField = new BMenuField("language",
B_TRANSLATE("Preferred language:"), languagesMenu);
add_languages_to_menu(fModel.SupportedLanguages(), languagesMenu);
LanguageMenuUtils::AddLanguagesToMenu(
fModel.Language().SupportedLanguages(),
languagesMenu);
languagesMenu->SetTargetForItems(this);
BMenuItem* defaultItem = languagesMenu->ItemAt(
fModel.SupportedLanguages().IndexOf(fPreferredLanguage));
if (defaultItem != NULL)
defaultItem->SetMarked(true);
printf("using preferred language code [%s]\n",
fPreferredLanguageCode.String());
LanguageMenuUtils::MarkLanguageInMenu(fPreferredLanguageCode,
languagesMenu);
fEmailField = new BTextControl(B_TRANSLATE("Email address:"), "", NULL);
fCaptchaView = new BitmapView("captcha view");
@@ -218,7 +208,7 @@ UserLoginWindow::MessageReceived(BMessage* message)
break;
case MSG_LANGUAGE_SELECTED:
message->FindString("code", &fPreferredLanguage);
message->FindString("code", &fPreferredLanguageCode);
break;
default:
@@ -623,7 +613,7 @@ UserLoginWindow::_CreateAccountThread()
BString email(fEmailField->Text());
BString captchaToken(fCaptchaToken);
BString captchaResponse(fCaptchaResultField->Text());
BString languageCode(fPreferredLanguage);
BString languageCode(fPreferredLanguageCode);
Unlock();
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_LOGIN_WINDOW_H
@@ -84,7 +85,7 @@ private:
BString fCaptchaToken;
BitmapRef fCaptchaImage;
BString fPreferredLanguage;
BString fPreferredLanguageCode;
Model& fModel;
+1 -1
View File
@@ -9,10 +9,10 @@
#include <string.h>
#include <Application.h>
#include <Messenger.h>
#include "HaikuDepotConstants.h"
/*! This method can be called to pop up an error in the user interface;
typically in a background thread.
*/
@@ -0,0 +1,137 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LanguageMenuUtils.h"
#include <string.h>
#include <Application.h>
#include <MenuItem.h>
#include <Messenger.h>
#include "HaikuDepotConstants.h"
#include "Logger.h"
/*! This method will add the supplied languages to the menu. It will
add first the popular languages, followed by a separator and then
other less popular languages.
*/
/* static */ void
LanguageMenuUtils::AddLanguagesToMenu(
const LanguageList& languages, BMenu* menu)
{
if (languages.IsEmpty())
printf("there are no languages defined\n");
int32 addedPopular = LanguageMenuUtils::_AddLanguagesToMenu(
languages, menu, true);
if (addedPopular > 0)
menu->AddSeparatorItem();
int32 addedNonPopular = LanguageMenuUtils::_AddLanguagesToMenu(
languages, menu, false);
if (Logger::IsDebugEnabled()) {
printf("did add %" B_PRId32 " popular languages and %" B_PRId32
" non-popular languages to a menu\n", addedPopular,
addedNonPopular);
}
}
/* static */ void
LanguageMenuUtils::MarkLanguageInMenu(
const BString& languageCode, BMenu* menu) {
if (menu->CountItems() == 0) {
debugger("menu contains no items; not able to set the "
"language");
return;
}
int32 index = LanguageMenuUtils::_IndexOfLanguageInMenu(
languageCode, menu);
if (index == -1) {
printf("unable to find the language [%s] in the menu\n",
languageCode.String());
menu->ItemAt(0)->SetMarked(true);
}
else
menu->ItemAt(index)->SetMarked(true);
}
/* static */ void
LanguageMenuUtils::_AddLanguageToMenu(
const BString& code, const BString& name, BMenu* menu)
{
BMessage* message = new BMessage(MSG_LANGUAGE_SELECTED);
message->AddString("code", code);
BMenuItem* item = new BMenuItem(name, message);
menu->AddItem(item);
}
/* static */ void
LanguageMenuUtils::_AddLanguageToMenu(
const LanguageRef& language, BMenu* menu)
{
BString name;
if (language->GetName(name) != B_OK || name.IsEmpty())
name.SetTo("???");
LanguageMenuUtils::_AddLanguageToMenu(language->Code(), name, menu);
}
/* static */ int32
LanguageMenuUtils::_AddLanguagesToMenu(const LanguageList& languages,
BMenu* menu, bool isPopular)
{
int32 count = 0;
for (int32 i = 0; i < languages.CountItems(); i++) {
const LanguageRef language = languages.ItemAtFast(i);
if (language->IsPopular() == isPopular) {
LanguageMenuUtils::_AddLanguageToMenu(language, menu);
count++;
}
}
return count;
}
/* static */ status_t
LanguageMenuUtils::_GetLanguageAtIndexInMenu(BMenu* menu, int32 index,
BString* result)
{
BMessage *itemMessage = menu->ItemAt(index)->Message();
if (itemMessage == NULL)
return B_ERROR;
return itemMessage->FindString("code", result);
}
/* static */ int32
LanguageMenuUtils::_IndexOfLanguageInMenu(
const BString& languageCode, BMenu* menu)
{
BString itemLanguageCode;
for (int32 i = 0; i < menu->CountItems(); i++) {
if (_GetLanguageAtIndexInMenu(
menu, i, &itemLanguageCode) == B_OK) {
if (itemLanguageCode == languageCode) {
return i;
}
}
}
return -1;
}
@@ -0,0 +1,43 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LANGUAGE_MENU_UTILS_H
#define LANGUAGE_MENU_UTILS_H
#include <Menu.h>
#include "Model.h"
#include "PackageInfo.h"
class LanguageMenuUtils {
public:
static void AddLanguagesToMenu(
const LanguageList& languages,
BMenu* menu);
static void MarkLanguageInMenu(
const BString& languageCode,
BMenu* menu);
private:
static int32 _IndexOfLanguageInMenu(
const BString& languageCode,
BMenu* menu);
static status_t _GetLanguageAtIndexInMenu(BMenu* menu,
int32 index, BString* result);
static int32 _AddLanguagesToMenu(
const LanguageList& languages,
BMenu* menu, bool isPopular);
static void _AddLanguageToMenu(
const LanguageRef& language,
BMenu* menu);
static void _AddLanguageToMenu(
const BString& code,
const BString& name, BMenu* menu);
};
#endif // LANGUAGE_MENU_UTILS_H