[haiku-commits] haiku: hrev53131 - in src/apps/haikudepot: model server util ui .

  • From: Andrew Lindesay <apl@xxxxxxxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 15 May 2019 15:03:52 -0400 (EDT)

hrev53131 adds 1 changeset to branch 'master'
old head: 9fe74faa61be8fb64a79610fa1a1713538b4fa50
new head: f85e030047e96fc3ae57af2fff35e6059d71ea77
overview: 
https://git.haiku-os.org/haiku/log/?qt=range&q=f85e030047e9+%5E9fe74faa61be

----------------------------------------------------------------------------

f85e030047e9: 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 <pulkomandy@xxxxxxxxx>

                                    [ Andrew Lindesay <apl@xxxxxxxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev53131
Commit:      f85e030047e96fc3ae57af2fff35e6059d71ea77
URL:         https://git.haiku-os.org/haiku/commit/?id=f85e030047e9
Author:      Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:        Sun Apr 28 20:17:34 2019 UTC

----------------------------------------------------------------------------

25 files changed, 947 insertions(+), 171 deletions(-)
src/apps/haikudepot/HaikuDepotConstants.h        |  10 +-
src/apps/haikudepot/Jamfile                      |  62 ++++++-
src/apps/haikudepot/build/jam/HdsSchemaGenRules  |  23 +++
.../build/scripts/jsonschema2cppmodel.py         |  22 ++-
src/apps/haikudepot/model/LanguageModel.cpp      | 168 +++++++++++++++++++
src/apps/haikudepot/model/LanguageModel.h        |  47 ++++++
src/apps/haikudepot/model/Model.cpp              |  85 +++++-----
src/apps/haikudepot/model/Model.h                |  19 +--
src/apps/haikudepot/model/PackageInfo.cpp        |  37 +++-
src/apps/haikudepot/model/PackageInfo.h          |  27 ++-
.../server/ProcessCoordinatorFactory.cpp         |  12 +-
.../server/ServerReferenceDataUpdateProcess.cpp  | 152 +++++++++++++++++
.../server/ServerReferenceDataUpdateProcess.h    |  55 ++++++
.../server/ServerRepositoryDataUpdateProcess.cpp |   7 +-
src/apps/haikudepot/server/WebAppInterface.cpp   |  17 +-
src/apps/haikudepot/server/WebAppInterface.h     |   6 +-
.../server/schema/dumpexportreference.json       |  72 ++++++++
src/apps/haikudepot/ui/PackageInfoView.cpp       |  28 +---
src/apps/haikudepot/ui/RatePackageWindow.cpp     |  42 ++---
src/apps/haikudepot/ui/RatePackageWindow.h       |   4 +-
src/apps/haikudepot/ui/UserLoginWindow.cpp       |  38 ++---
src/apps/haikudepot/ui/UserLoginWindow.h         |   3 +-
src/apps/haikudepot/util/AppUtils.cpp            |   2 +-
src/apps/haikudepot/util/LanguageMenuUtils.cpp   | 137 +++++++++++++++
src/apps/haikudepot/util/LanguageMenuUtils.h     |  43 +++++

----------------------------------------------------------------------------

diff --git a/src/apps/haikudepot/HaikuDepotConstants.h 
b/src/apps/haikudepot/HaikuDepotConstants.h
index d251f69c20..9671451c7e 100644
--- a/src/apps/haikudepot/HaikuDepotConstants.h
+++ b/src/apps/haikudepot/HaikuDepotConstants.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2018-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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
\ No newline at end of file
diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile
index 2a04c7ba49..875f9fec7b 100644
--- a/src/apps/haikudepot/Jamfile
+++ b/src/apps/haikudepot/Jamfile
@@ -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) ]
 ;
\ No newline at end of file
diff --git a/src/apps/haikudepot/build/jam/HdsSchemaGenRules 
b/src/apps/haikudepot/build/jam/HdsSchemaGenRules
index 8c3bf8c1d5..96872b4bb2 100644
--- a/src/apps/haikudepot/build/jam/HdsSchemaGenRules
+++ b/src/apps/haikudepot/build/jam/HdsSchemaGenRules
@@ -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
diff --git a/src/apps/haikudepot/build/scripts/jsonschema2cppmodel.py 
b/src/apps/haikudepot/build/scripts/jsonschema2cppmodel.py
index ce4b453a43..8eb54ad943 100644
--- a/src/apps/haikudepot/build/scripts/jsonschema2cppmodel.py
+++ b/src/apps/haikudepot/build/scripts/jsonschema2cppmodel.py
@@ -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};
 }
 
diff --git a/src/apps/haikudepot/model/LanguageModel.cpp 
b/src/apps/haikudepot/model/LanguageModel.cpp
new file mode 100644
index 0000000000..30263cd2c6
--- /dev/null
+++ b/src/apps/haikudepot/model/LanguageModel.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * 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);
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/model/LanguageModel.h 
b/src/apps/haikudepot/model/LanguageModel.h
new file mode 100644
index 0000000000..5010332d32
--- /dev/null
+++ b/src/apps/haikudepot/model/LanguageModel.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * 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
\ No newline at end of file
diff --git a/src/apps/haikudepot/model/Model.cpp 
b/src/apps/haikudepot/model/Model.cpp
index f6ee6a675f..7f583f6348 100644
--- a/src/apps/haikudepot/model/Model.cpp
+++ b/src/apps/haikudepot/model/Model.cpp
@@ -1,7 +1,7 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
  * Copyright 2014, Axel Dörfler <axeld@xxxxxxxxxxxxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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,43 +377,18 @@ 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);
+Model::~Model()
+{
 }
 
 
-Model::~Model()
+LanguageModel&
+Model::Language()
 {
+       return fLanguageModel;
 }
 
 
@@ -903,32 +877,55 @@ Model::SetAuthorization(const BString& username, const 
BString& password,
 }
 
 
-/*! 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
-    this file.
-*/
-
 status_t
-Model::DumpExportRepositoryDataPath(BPath& path) const
+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("repository-all_en.json.gz") == 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 repositories'"
-               " data");
+       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
+    this file.
+*/
+
+status_t
+Model::DumpExportRepositoryDataPath(BPath& path) const
+{
+       return _LocalDataPath("repository-all_%languageCode%.json.gz", path);
+}
+
+
+/*! 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);
+}
+
+
 status_t
 Model::IconStoragePath(BPath& path) const
 {
@@ -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
diff --git a/src/apps/haikudepot/model/Model.h 
b/src/apps/haikudepot/model/Model.h
index 3799c408f3..553f48614a 100644
--- a/src/apps/haikudepot/model/Model.h
+++ b/src/apps/haikudepot/model/Model.h
@@ -1,6 +1,6 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
diff --git a/src/apps/haikudepot/model/PackageInfo.cpp 
b/src/apps/haikudepot/model/PackageInfo.cpp
index e0ba889dde..e1cade50db 100644
--- a/src/apps/haikudepot/model/PackageInfo.cpp
+++ b/src/apps/haikudepot/model/PackageInfo.cpp
@@ -1,7 +1,7 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
  * Copyright 2013, Rene Gollent <rene@xxxxxxxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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
 
 
diff --git a/src/apps/haikudepot/model/PackageInfo.h 
b/src/apps/haikudepot/model/PackageInfo.h
index 263d052e4a..c2c8ddb35b 100644
--- a/src/apps/haikudepot/model/PackageInfo.h
+++ b/src/apps/haikudepot/model/PackageInfo.h
@@ -1,6 +1,6 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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();
diff --git a/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp 
b/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp
index f5a544c524..eeeec2f614 100644
--- a/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp
+++ b/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2018-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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 {
diff --git a/src/apps/haikudepot/server/ServerReferenceDataUpdateProcess.cpp 
b/src/apps/haikudepot/server/ServerReferenceDataUpdateProcess.cpp
new file mode 100644
index 0000000000..ceaa4849ea
--- /dev/null
+++ b/src/apps/haikudepot/server/ServerReferenceDataUpdateProcess.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * 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");
+}
diff --git a/src/apps/haikudepot/server/ServerReferenceDataUpdateProcess.h 
b/src/apps/haikudepot/server/ServerReferenceDataUpdateProcess.h
new file mode 100644
index 0000000000..fe051b6efa
--- /dev/null
+++ b/src/apps/haikudepot/server/ServerReferenceDataUpdateProcess.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * 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
diff --git a/src/apps/haikudepot/server/ServerRepositoryDataUpdateProcess.cpp 
b/src/apps/haikudepot/server/ServerRepositoryDataUpdateProcess.cpp
index cc4b78e109..ebc6ffa9f3 100644
--- a/src/apps/haikudepot/server/ServerRepositoryDataUpdateProcess.cpp
+++ b/src/apps/haikudepot/server/ServerRepositoryDataUpdateProcess.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2017-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
 }
 
 
diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp 
b/src/apps/haikudepot/server/WebAppInterface.cpp
index c03c8d8293..30231264a3 100644
--- a/src/apps/haikudepot/server/WebAppInterface.cpp
+++ b/src/apps/haikudepot/server/WebAppInterface.cpp
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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)
 {
diff --git a/src/apps/haikudepot/server/WebAppInterface.h 
b/src/apps/haikudepot/server/WebAppInterface.h
index 543aac1557..4b99fa231f 100644
--- a/src/apps/haikudepot/server/WebAppInterface.h
+++ b/src/apps/haikudepot/server/WebAppInterface.h
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
 };
 
diff --git a/src/apps/haikudepot/server/schema/dumpexportreference.json 
b/src/apps/haikudepot/server/schema/dumpexportreference.json
new file mode 100644
index 0000000000..08eb684863
--- /dev/null
+++ b/src/apps/haikudepot/server/schema/dumpexportreference.json
@@ -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"
+                 }
+               }
+         }
+       }
+  }
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/ui/PackageInfoView.cpp 
b/src/apps/haikudepot/ui/PackageInfoView.cpp
index b93bc82648..d7733bd371 100644
--- a/src/apps/haikudepot/ui/PackageInfoView.cpp
+++ b/src/apps/haikudepot/ui/PackageInfoView.cpp
@@ -1,6 +1,6 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2018-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
 };
 
 
diff --git a/src/apps/haikudepot/ui/RatePackageWindow.cpp 
b/src/apps/haikudepot/ui/RatePackageWindow.cpp
index 975a82cf3d..aff723798c 100644
--- a/src/apps/haikudepot/ui/RatePackageWindow.cpp
+++ b/src/apps/haikudepot/ui/RatePackageWindow.cpp
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
 
diff --git a/src/apps/haikudepot/ui/RatePackageWindow.h 
b/src/apps/haikudepot/ui/RatePackageWindow.h
index 04ca5f0183..bd6e2c5393 100644
--- a/src/apps/haikudepot/ui/RatePackageWindow.h
+++ b/src/apps/haikudepot/ui/RatePackageWindow.h
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2018-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
diff --git a/src/apps/haikudepot/ui/UserLoginWindow.cpp 
b/src/apps/haikudepot/ui/UserLoginWindow.cpp
index 29263a1d05..a225540bc5 100644
--- a/src/apps/haikudepot/ui/UserLoginWindow.cpp
+++ b/src/apps/haikudepot/ui/UserLoginWindow.cpp
@@ -1,5 +1,6 @@
 /*
  * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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();
 
diff --git a/src/apps/haikudepot/ui/UserLoginWindow.h 
b/src/apps/haikudepot/ui/UserLoginWindow.h
index 6d529e717e..ee52d452ca 100644
--- a/src/apps/haikudepot/ui/UserLoginWindow.h
+++ b/src/apps/haikudepot/ui/UserLoginWindow.h
@@ -1,5 +1,6 @@
 /*
  * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * 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;
 
diff --git a/src/apps/haikudepot/util/AppUtils.cpp 
b/src/apps/haikudepot/util/AppUtils.cpp
index 54933eff1c..79bdfff77a 100644
--- a/src/apps/haikudepot/util/AppUtils.cpp
+++ b/src/apps/haikudepot/util/AppUtils.cpp
@@ -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.
  */
diff --git a/src/apps/haikudepot/util/LanguageMenuUtils.cpp 
b/src/apps/haikudepot/util/LanguageMenuUtils.cpp
new file mode 100644
index 0000000000..f22e468aeb
--- /dev/null
+++ b/src/apps/haikudepot/util/LanguageMenuUtils.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * 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;
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/util/LanguageMenuUtils.h 
b/src/apps/haikudepot/util/LanguageMenuUtils.h
new file mode 100644
index 0000000000..420d711af8
--- /dev/null
+++ b/src/apps/haikudepot/util/LanguageMenuUtils.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * 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


Other related posts:

  • » [haiku-commits] haiku: hrev53131 - in src/apps/haikudepot: model server util ui . - Andrew Lindesay