Added languages module

This commit is contained in:
Eism 2020-07-15 16:57:54 +02:00
parent c350dcb9c4
commit 4433ce2e6f
25 changed files with 1519 additions and 10 deletions

View file

@ -58,6 +58,9 @@ public:
MidiFirst = 600,
MidiLast = 699,
LanguagesFirst = 700,
LanguagesLast = 799,
NotationFirst = 1000,
NotationLast = 1299
};

View file

@ -91,6 +91,7 @@ if (BUILD_UI_MU4)
mu4_midi
userscores
extensions
languages
importexport
notation
notation_scene

View file

@ -35,6 +35,7 @@
#include "mu4/context/contextmodule.h"
#include "mu4/userscores/userscoresmodule.h"
#include "mu4/extensions/extensionsmodule.h"
#include "mu4/languages/languagesmodule.h"
#include "mu4/domain/notation/notationdomainmodule.h"
#include "mu4/domain/importexport/importexportmodule.h"
#include "mu4/scenes/common/commonscenemodule.h"
@ -71,6 +72,7 @@ ModulesSetup::ModulesSetup()
<< new mu::midi::MidiModule()
<< new mu::userscores::UserScoresModule()
<< new mu::extensions::ExtensionsModule()
<< new mu::languages::LanguagesModule()
<< new mu::domain::notation::NotationDomainModule()
<< new mu::scene::common::CommonSceneModule()
<< new mu::scene::notation::NotationSceneModule()

View file

@ -11,6 +11,7 @@ add_subdirectory(domain/importexport)
# Home
add_subdirectory(userscores)
add_subdirectory(extensions)
add_subdirectory(languages)
# Scenes common
add_subdirectory(scenes/common)

View file

@ -4,6 +4,7 @@ import QtQuick.Layouts 1.3
import MuseScore.UiComponents 1.0
import MuseScore.Extensions 1.0
import MuseScore.Languages 1.0
Rectangle {
id: root
@ -105,17 +106,17 @@ Rectangle {
}
}
}
Rectangle {
LanguagesModule {
id: languagesComp
color: ui.theme.backgroundColor
StyledTextLabel {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: "Languages Module"
Connections {
target: search
function onCurrentTextEdited(newTextValue) {
languagesComp.search = newTextValue
}
}
}
}
}

View file

@ -180,7 +180,7 @@ Ret ExtensionsController::uninstall(const QString& extensionCode)
}
m_extensionChanged.send(extensionHash[extensionCode]);
return make_ret(Err::NoError);
}

View file

@ -0,0 +1,45 @@
#=============================================================================
# MuseScore
# Music Composition & Notation
#
# Copyright (C) 2020 MuseScore BVBA and others
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#=============================================================================
set(MODULE languages)
set(MODULE_QRC languages.qrc)
set(MODULE_QML_IMPORT ${CMAKE_CURRENT_LIST_DIR}/qml )
set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/languagesmodule.cpp
${CMAKE_CURRENT_LIST_DIR}/languagesmodule.h
${CMAKE_CURRENT_LIST_DIR}/languagestypes.h
${CMAKE_CURRENT_LIST_DIR}/languageserrors.h
${CMAKE_CURRENT_LIST_DIR}/ilanguagesconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/ilanguagescontroller.h
${CMAKE_CURRENT_LIST_DIR}/ilanguageunpacker.h
${CMAKE_CURRENT_LIST_DIR}/internal/languagesconfiguration.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/languagesconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/internal/languagescontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/languagescontroller.h
${CMAKE_CURRENT_LIST_DIR}/internal/languageunpacker.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/languageunpacker.h
${CMAKE_CURRENT_LIST_DIR}/view/languagelistmodel.cpp
${CMAKE_CURRENT_LIST_DIR}/view/languagelistmodel.h
)
include(${PROJECT_SOURCE_DIR}/build/module.cmake)

View file

@ -0,0 +1,51 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_ILANGUAGESCONFIGURATION_H
#define MU_LANGUAGES_ILANGUAGESCONFIGURATION_H
#include "retval.h"
#include "modularity/imoduleexport.h"
#include "languagestypes.h"
namespace mu {
namespace languages {
class ILanguagesConfiguration : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(ILanguagesConfiguration)
public:
virtual ~ILanguagesConfiguration() = default;
virtual QString currentLanguageCode() const = 0;
virtual Ret setCurrentLanguageCode(const QString& languageCode) const = 0;
virtual QUrl languagesUpdateUrl() const = 0;
virtual QUrl languagesFileServerUrl() const = 0;
virtual ValCh<LanguagesHash> languages() const = 0;
virtual Ret setLanguages(const LanguagesHash& languages) const = 0;
virtual QString languagesSharePath() const = 0;
virtual QString languagesDataPath() const = 0;
};
}
}
#endif // MU_LANGUAGES_ILANGUAGESCONFIGURATION_H

View file

@ -0,0 +1,49 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_ILANGUAGESCONTROLLER_H
#define MU_LANGUAGES_ILANGUAGESCONTROLLER_H
#include "modularity/imoduleexport.h"
#include "retval.h"
#include "languagestypes.h"
namespace mu {
namespace languages {
class ILanguagesController : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(ILanguagesController)
public:
virtual ~ILanguagesController() = default;
virtual Ret refreshLanguages() = 0;
virtual ValCh<LanguagesHash> languages() = 0;
virtual Ret install(const QString& languageCode) = 0;
virtual Ret uninstall(const QString& languageCode) = 0;
virtual Ret setLanguage(const QString& languageCode) = 0;
virtual RetCh<Language> languageChanged() = 0;
};
}
}
#endif // MU_LANGUAGES_ILANGUAGESCONTROLLER_H

View file

@ -0,0 +1,41 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_ILANGUAGEUNPACKER_H
#define MU_LANGUAGES_ILANGUAGEUNPACKER_H
#include <QString>
#include "modularity/imoduleexport.h"
#include "ret.h"
namespace mu {
namespace languages {
class ILanguageUnpacker : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(ILanguageUnpacker)
public:
virtual ~ILanguageUnpacker() = default;
virtual Ret unpack(const QString& languageCode, const QString& source, const QString& destination) const = 0;
};
}
}
#endif // MU_LANGUAGES_ILANGUAGEUNPACKER_H

View file

@ -0,0 +1,131 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "languagesconfiguration.h"
#include <QDir>
#include <QVariant>
#include "log.h"
#include "settings.h"
#include "languagestypes.h"
#include "languageserrors.h"
using namespace mu;
using namespace mu::framework;
using namespace mu::languages;
static std::string module_name("languages");
static const Settings::Key LANGUAGES_JSON(module_name, "languages/languagesJson");
static const Settings::Key LANGUAGE("ui", "ui/application/language");
void LanguagesConfiguration::init()
{
settings()->addItem(LANGUAGE, Val("system"));
settings()->valueChanged(LANGUAGES_JSON).onReceive(nullptr, [this](const Val& val) {
LanguagesHash languagesHash = parseLanguagesConfig(io::pathToQString(val.toString()).toLocal8Bit());
m_languagesHashChanged.send(languagesHash);
});
}
QString LanguagesConfiguration::currentLanguageCode() const
{
return io::pathToQString(settings()->value(LANGUAGE).toString());
}
Ret LanguagesConfiguration::setCurrentLanguageCode(const QString& languageCode) const
{
Val value(languageCode.toStdString());
settings()->setValue(LANGUAGE, value);
return make_ret(Err::NoError);
}
QUrl LanguagesConfiguration::languagesUpdateUrl() const
{
return QUrl("http://extensions.musescore.org/4.0/languages/details.json");
}
QUrl LanguagesConfiguration::languagesFileServerUrl() const
{
return QUrl("http://extensions.musescore.org/4.0/languages/");
}
ValCh<LanguagesHash> LanguagesConfiguration::languages() const
{
ValCh<LanguagesHash> result;
result.val = parseLanguagesConfig(io::pathToQString(settings()->value(LANGUAGES_JSON).toString()).toLocal8Bit());
result.ch = m_languagesHashChanged;
return result;
}
Ret LanguagesConfiguration::setLanguages(const LanguagesHash& languages) const
{
QJsonArray jsonArray;
for (const Language& language: languages) {
QJsonObject obj;
obj[language.code] = language.toJson();
jsonArray << obj;
}
QJsonDocument jsonDoc(jsonArray);
Val value(jsonDoc.toJson(QJsonDocument::Compact).constData());
settings()->setValue(LANGUAGES_JSON, value);
return make_ret(Err::NoError);
}
LanguagesHash LanguagesConfiguration::parseLanguagesConfig(const QByteArray& json) const
{
LanguagesHash result;
QJsonParseError err;
QJsonDocument jsodDoc = QJsonDocument::fromJson(json, &err);
if (err.error != QJsonParseError::NoError || !jsodDoc.isArray()) {
return LanguagesHash();
}
QVariantList languages = jsodDoc.array().toVariantList();
for (const QVariant& languagesObj: languages) {
QMap<QString, QVariant> value = languagesObj.toMap();
QVariantMap lngMap = value.first().toMap();
Language language;
language.code = value.keys().first();
language.name = lngMap.value("name").toString();
language.fileName = lngMap.value("fileName").toString();
language.fileSize = lngMap.value("fileSize").toDouble();
language.status = static_cast<LanguageStatus::Status>(lngMap.value("status").toInt());
result.insert(language.code, language);
}
return result;
}
QString LanguagesConfiguration::languagesSharePath() const
{
return io::pathToQString(globalConfiguration()->sharePath() + "/locale");
}
QString LanguagesConfiguration::languagesDataPath() const
{
return io::pathToQString(globalConfiguration()->dataPath() + "/locale");
}

View file

@ -0,0 +1,57 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGESCONFIGURATION_H
#define MU_LANGUAGES_LANGUAGESCONFIGURATION_H
#include "modularity/ioc.h"
#include "../ilanguagesconfiguration.h"
#include "iglobalconfiguration.h"
namespace mu {
namespace languages {
class LanguagesConfiguration : public ILanguagesConfiguration
{
INJECT(languages, framework::IGlobalConfiguration, globalConfiguration)
public:
LanguagesConfiguration() = default;
void init();
QString currentLanguageCode() const override;
Ret setCurrentLanguageCode(const QString& languageCode) const override;
QUrl languagesUpdateUrl() const override;
QUrl languagesFileServerUrl() const override;
ValCh<LanguagesHash> languages() const override;
Ret setLanguages(const LanguagesHash& languages) const override;
QString languagesSharePath() const override;
QString languagesDataPath() const override;
private:
LanguagesHash parseLanguagesConfig(const QByteArray& json) const;
async::Channel<LanguagesHash> m_languagesHashChanged;
};
}
}
#endif // MU_LANGUAGES_LANGUAGESCONFIGURATION_H

View file

@ -0,0 +1,343 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "languagescontroller.h"
#include <QDir>
#include <QTranslator>
#include <QCoreApplication>
#include "log.h"
#include "mscore/downloadUtils.h"
#include "languageserrors.h"
using namespace mu;
using namespace mu::languages;
static const QString DEFAULT_LANGUAGE("system");
void LanguagesController::init()
{
QString code = configuration()->currentLanguageCode();
LOGD() << "==========" << code;
loadLanguage(code);
}
Ret LanguagesController::refreshLanguages()
{
Ms::DownloadUtils* js = new Ms::DownloadUtils();
js->setTarget(configuration()->languagesUpdateUrl().toString());
js->download();
QByteArray json = js->returnData();
RetVal<LanguagesHash> actualLanguages = parseLanguagesConfig(json);
if (!actualLanguages.ret) {
return actualLanguages.ret;
}
LanguagesHash savedLanguages = configuration()->languages().val;
LanguagesHash resultLanguages = savedLanguages;
for (Language& language : actualLanguages.val) {
if (resultLanguages.contains(language.code)) {
Language& savedLanguage = resultLanguages[language.code];
if (!isLanguageExists(language.code)) {
savedLanguage.status = LanguageStatus::Status::NoInstalled;
continue;
}
savedLanguage.status = LanguageStatus::Status::Installed;
} else {
language.status = LanguageStatus::Status::NoInstalled;
resultLanguages.insert(language.code, language);
}
}
Ret ret = configuration()->setLanguages(resultLanguages);
return ret;
}
ValCh<LanguagesHash> LanguagesController::languages()
{
ValCh<LanguagesHash> languagesHash = configuration()->languages();
languagesHash.val = correctLanguagesStates(languagesHash.val).val;
return languagesHash;
}
Ret LanguagesController::install(const QString& languageCode)
{
RetVal<QString> download = downloadLanguage(languageCode);
if (!download.ret) {
return download.ret;
}
QString languageArchivePath = download.val;
QDir languagesShareDir(configuration()->languagesSharePath());
if (!languagesShareDir.exists()) {
languagesShareDir.mkpath(languagesShareDir.absolutePath());
}
Ret unpack = languageUnpacker()->unpack(languageCode, languageArchivePath, languagesShareDir.absolutePath());
if (!unpack) {
LOGE() << "Error unpack" << unpack.code();
return unpack;
}
QFile languageArchive(languageArchivePath);
languageArchive.remove();
LanguagesHash languageHash = this->languages().val;
languageHash[languageCode].status = LanguageStatus::Status::Installed;
Ret ret = configuration()->setLanguages(languageHash);
if (!ret) {
return ret;
}
m_languageChanged.send(languageHash[languageCode]);
return make_ret(Err::NoError);
}
Ret LanguagesController::uninstall(const QString& languageCode)
{
LanguagesHash languagesHash = languages().val;
if (!languagesHash.contains(languageCode)) {
return make_ret(Err::ErrorLanguageNotFound);
}
Ret remove = removeLanguage(languageCode);
if (!remove) {
return remove;
}
if (languagesHash[languageCode].isCurrent) {
resetLanguageByDefault();
}
languagesHash[languageCode].status = LanguageStatus::Status::NoInstalled;
Ret ret = configuration()->setLanguages(languagesHash);
if (!ret) {
return ret;
}
m_languageChanged.send(languagesHash[languageCode]);
return make_ret(Err::NoError);
}
Ret LanguagesController::setLanguage(const QString& languageCode)
{
LanguagesHash languageHash = this->languages().val;
if (!languageHash.contains(languageCode)) {
return make_ret(Err::ErrorLanguageNotFound);
}
for (QTranslator* t: m_translatorList) {
qApp->removeTranslator(t);
delete t;
}
m_translatorList.clear();
Ret load = loadLanguage(languageCode);
if (!load) {
return load;
}
QString previousLanguage = configuration()->currentLanguageCode();
Ret save = configuration()->setCurrentLanguageCode(languageCode);
if (!save) {
return save;
}
languageHash[previousLanguage].isCurrent = false;
m_languageChanged.send(languageHash[previousLanguage]);
languageHash[languageCode].isCurrent = true;
m_languageChanged.send(languageHash[languageCode]);
return save;
}
RetCh<Language> LanguagesController::languageChanged()
{
RetCh<Language> result;
result.ret = make_ret(Err::NoError);
result.ch = m_languageChanged;
return result;
}
RetVal<LanguagesHash> LanguagesController::parseLanguagesConfig(const QByteArray& json) const
{
RetVal<LanguagesHash> result;
QJsonParseError err;
QJsonDocument jsodDoc = QJsonDocument::fromJson(json, &err);
if (err.error != QJsonParseError::NoError || !jsodDoc.isObject()) {
result.ret = make_ret(Err::ErrorParseConfig);
return result;
}
result.ret = make_ret(Err::NoError);
QStringList languages = jsodDoc.object().keys();
for (const QString& key : languages) {
if (!jsodDoc.object().value(key).isObject()) {
continue;
}
QJsonObject value = jsodDoc.object().value(key).toObject();
Language language;
language.code = key;
language.name = value.value("name").toString();
language.fileName = value.value("file_name").toString();
language.fileSize = value.value("file_size").toDouble();
language.status = LanguageStatus::Status::Undefined;
result.val.insert(key, language);
}
return result;
}
bool LanguagesController::isLanguageExists(const QString& languageCode) const
{
QDir languagesDir(configuration()->languagesSharePath());
QStringList files = languagesDir.entryList({ QString("*%1.qm").arg(languageCode) }, QDir::Files);
return !files.empty();
}
RetVal<LanguagesHash> LanguagesController::correctLanguagesStates(LanguagesHash& languages) const
{
RetVal<LanguagesHash> result;
bool isNeedUpdate = false;
QString currentLanguage = configuration()->currentLanguageCode();
for (Language& language: languages) {
if (language.status == LanguageStatus::Status::Installed && !isLanguageExists(language.code)) {
language.status = LanguageStatus::Status::NoInstalled;
isNeedUpdate = true;
}
language.isCurrent = (language.code == currentLanguage);
}
if (isNeedUpdate) {
Ret update = configuration()->setLanguages(languages);
if (!update) {
result.ret = update;
return result;
}
}
result.ret = make_ret(Err::NoError);
result.val = languages;
return result;
}
RetVal<QString> LanguagesController::downloadLanguage(const QString& languageCode) const
{
RetVal<QString> result;
ValCh<LanguagesHash> languages = configuration()->languages();
QString fileName = languages.val.value(languageCode).fileName;
QDir languagesDir(configuration()->languagesDataPath());
if (!languagesDir.exists()) {
languagesDir.mkpath(languagesDir.absolutePath());
}
QString languageArchivePath = languagesDir.absolutePath() + "/" + fileName;
Ms::DownloadUtils* js = new Ms::DownloadUtils();
js->setTarget(configuration()->languagesFileServerUrl().toString() + fileName);
js->setLocalFile(languageArchivePath);
js->download(true);
if (!js->saveFile()) {
LOGE() << "Error save file";
result.ret = make_ret(Err::ErrorDownloadLanguage);
return result;
}
result.ret = make_ret(Err::NoError);
result.val = languageArchivePath;
return result;
}
Ret LanguagesController::removeLanguage(const QString& languageCode) const
{
QDir languageDir(configuration()->languagesSharePath());
QStringList files = languageDir.entryList({ QString("*%1.qm").arg(languageCode) }, QDir::Files);
for (const QString& fileName: files) {
QString filePath(languageDir.absolutePath() + "/" + fileName);
QFile file(filePath);
if (!file.remove()) {
LOGE() << "Error remove file" << filePath << file.errorString();
return make_ret(Err::ErrorRemoveLanguageDirectory);
}
}
return make_ret(Err::NoError);
}
Ret LanguagesController::loadLanguage(const QString &languageCode)
{
QDir languageDir(configuration()->languagesSharePath());
QStringList files = languageDir.entryList({ QString("*%1.qm").arg(languageCode) }, QDir::Files);
for (const QString& fileName: files) {
QFileInfo file(fileName);
QString filePath(languageDir.absolutePath() + "/" + file.baseName());
QTranslator* translator = new QTranslator;
bool ok = translator->load(filePath);
if (ok) {
qApp->installTranslator(translator);
m_translatorList.append(translator);
} else {
LOGE() << "Error load translate" << filePath;
delete translator;
}
}
QLocale locale(languageCode);
QLocale::setDefault(locale);
qApp->setLayoutDirection(locale.textDirection());
return make_ret(Err::NoError);
}
void LanguagesController::resetLanguageByDefault()
{
Ret load = loadLanguage(DEFAULT_LANGUAGE);
if (!load) {
return;
}
configuration()->setCurrentLanguageCode(DEFAULT_LANGUAGE);
}

View file

@ -0,0 +1,72 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGESCONTROLLER_H
#define MU_LANGUAGES_LANGUAGESCONTROLLER_H
#include "modularity/ioc.h"
#include "../ilanguagescontroller.h"
#include "../ilanguagesconfiguration.h"
#include "../ilanguageunpacker.h"
#include "iglobalconfiguration.h"
class QTranslator;
namespace mu {
namespace languages {
class LanguagesController : public ILanguagesController
{
INJECT(languages, ILanguagesConfiguration, configuration)
INJECT(languages, ILanguageUnpacker, languageUnpacker)
INJECT(languages, framework::IGlobalConfiguration, globalConfiguration)
public:
LanguagesController() = default;
void init();
Ret refreshLanguages() override;
ValCh<LanguagesHash> languages() override;
Ret install(const QString& languageCode) override;
Ret uninstall(const QString& languageCode) override;
Ret setLanguage(const QString &languageCode) override;
RetCh<Language> languageChanged() override;
private:
RetVal<LanguagesHash> parseLanguagesConfig(const QByteArray& json) const;
bool isLanguageExists(const QString& languageCode) const;
RetVal<LanguagesHash> correctLanguagesStates(LanguagesHash& languages) const;
RetVal<QString> downloadLanguage(const QString& languageCode) const;
Ret removeLanguage(const QString& languageCode) const;
Ret loadLanguage(const QString& languageCode);
void resetLanguageByDefault();
private:
async::Channel<Language> m_languageChanged;
QList<QTranslator*> m_translatorList;
};
}
}
#endif // MU_LANGUAGES_LANGUAGESCONTROLLER_H

View file

@ -0,0 +1,109 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "languageunpacker.h"
#include <QFile>
#include <QFileInfo>
#include <QStorageInfo>
#include "thirdparty/qzip/qzipreader_p.h"
#include "log.h"
#include "../ilanguageunpacker.h"
#include "languageserrors.h"
using namespace mu;
using namespace mu::languages;
Ret LanguageUnpacker::unpack(const QString& languageCode, const QString& source, const QString& destination) const
{
Ret destinationWritable = checkDirectoryIsWritable(destination);
if (!destinationWritable) {
return destinationWritable;
}
MQZipReader zipFile(source);
qint64 totalZipSize = 0;
for (const MQZipReader::FileInfo& fileInfo : zipFile.fileInfoList()) {
totalZipSize += fileInfo.size;
}
Ret freeSpace = checkFreeSpace(destination, totalZipSize);
if (!freeSpace) {
return freeSpace;
}
Ret remove = removePreviousVersion(destination, languageCode);
if (!remove) {
return remove;
}
Ret unzipLanguage = unzip(&zipFile, destination);
return unzipLanguage;
}
Ret LanguageUnpacker::checkDirectoryIsWritable(const QString& directoryPath) const
{
QFileInfo destinationDirInfo(directoryPath);
if (!destinationDirInfo.isWritable()) {
return make_ret(Err::UnpackDestinationReadOnly);
}
return make_ret(Err::NoError);
}
Ret LanguageUnpacker::checkFreeSpace(const QString& directoryPath, quint64 neededSpace) const
{
QStorageInfo destinationStorageInfo(directoryPath);
if (neededSpace > destinationStorageInfo.bytesAvailable()) {
return make_ret(Err::UnpackNoFreeSpace);
}
return make_ret(Err::NoError);
}
Ret LanguageUnpacker::removePreviousVersion(const QString& path, const QString& languageCode) const
{
QDir languageDir(path);
QStringList files = languageDir.entryList({ QString("*%1.qm").arg(languageCode) }, QDir::Files);
for (const QString& fileName: files) {
QString filePath(languageDir.absolutePath() + "/" + fileName);
QFile file(filePath);
if (!file.remove()) {
LOGE() << "Error remove file" << filePath << file.errorString();
return make_ret(Err::UnpackErrorRemovePreviousVersion);
}
}
return make_ret(Err::NoError);
}
Ret LanguageUnpacker::unzip(const MQZipReader* zip, const QString& destination) const
{
QDir destinationDir(destination);
if (!destinationDir.exists()) {
destinationDir.mkpath(destinationDir.absolutePath());
}
if (!zip->extractAll(destination)) {
return make_ret(Err::UnpackError);
}
return make_ret(Err::NoError);
}

View file

@ -0,0 +1,47 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGEUNPACKER_H
#define MU_LANGUAGES_LANGUAGEUNPACKER_H
#include "retval.h"
#include "../ilanguageunpacker.h"
class MQZipReader;
namespace mu {
namespace languages {
class LanguageUnpacker : public ILanguageUnpacker
{
public:
LanguageUnpacker() = default;
Ret unpack(const QString& languageCode, const QString& source, const QString& destination) const override;
private:
Ret checkDirectoryIsWritable(const QString& directoryPath) const;
Ret checkFreeSpace(const QString& directoryPath, quint64 neededSpace) const;
Ret removePreviousVersion(const QString& path, const QString& languageCode) const;
Ret unzip(const MQZipReader* zip, const QString& destination) const;
};
}
}
#endif // MU_LANGUAGES_LANGUAGEUNPACKER_H

View file

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>qml/MuseScore/Languages/qmldir</file>
<file>qml/MuseScore/Languages/LanguagesModule.qml</file>
</qresource>
</RCC>

View file

@ -0,0 +1,72 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGESERRORS_H
#define MU_LANGUAGES_LANGUAGESERRORS_H
#include "ret.h"
#include "translation.h"
namespace mu {
namespace languages {
enum class Err {
Undefined = int(Ret::Code::Undefined),
NoError = int(Ret::Code::Ok),
UnknownError = int(Ret::Code::LanguagesFirst),
ErrorParseConfig,
ErrorDownloadLanguage,
ErrorLanguageNotFound,
ErrorRemoveLanguageDirectory,
UnpackDestinationReadOnly,
UnpackNoFreeSpace,
UnpackErrorRemovePreviousVersion,
UnpackError
};
inline Ret make_ret(Err e)
{
switch (e) {
case Err::Undefined: return Ret(static_cast<int>(Ret::Code::Undefined));
case Err::NoError: return Ret(static_cast<int>(Ret::Code::Ok));
case Err::UnknownError: return Ret(static_cast<int>(Ret::Code::UnknownError));
case Err::ErrorParseConfig: return Ret(static_cast<int>(Err::ErrorParseConfig),
trc("languages", "Error parsing response from server"));
case Err::ErrorDownloadLanguage: return Ret(static_cast<int>(Err::ErrorDownloadLanguage),
trc("languages", "Error download language"));
case Err::ErrorLanguageNotFound: return Ret(static_cast<int>(Err::ErrorLanguageNotFound),
trc("languages", "Language not found"));
case Err::ErrorRemoveLanguageDirectory: return Ret(static_cast<int>(Err::ErrorRemoveLanguageDirectory),
trc("languages", "Error remove language directory"));
case Err::UnpackDestinationReadOnly: return Ret(static_cast<int>(Err::UnpackDestinationReadOnly),
trc("languages", "Cannot import extension on read-only storage"));
case Err::UnpackNoFreeSpace: return Ret(static_cast<int>(Err::UnpackNoFreeSpace),
trc("languages", "Cannot import extension on full storage"));
case Err::UnpackErrorRemovePreviousVersion: return Ret(static_cast<int>(Err::UnpackErrorRemovePreviousVersion),
trc("languages", "Error removing previous version"));
case Err::UnpackError: return Ret(static_cast<int>(Err::UnpackError),
trc("languages", "Error unpacking extension"));
}
return Ret(static_cast<int>(e));
}
}
}
#endif // MU_LANGUAGES_LANGUAGESERRORS_H

View file

@ -0,0 +1,65 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "languagesmodule.h"
#include <QQmlEngine>
#include "internal/languagesconfiguration.h"
#include "internal/languagescontroller.h"
#include "internal/languageunpacker.h"
#include "view/languagelistmodel.h"
using namespace mu::languages;
static LanguagesConfiguration* m_languagesConfiguration = new LanguagesConfiguration();
static LanguagesController* m_languagesController = new LanguagesController();
static void languages_init_qrc()
{
Q_INIT_RESOURCE(languages);
}
std::string LanguagesModule::moduleName() const
{
return "languages";
}
void LanguagesModule::registerExports()
{
framework::ioc()->registerExport<ILanguagesConfiguration>(moduleName(), m_languagesConfiguration);
framework::ioc()->registerExport<ILanguagesController>(moduleName(), m_languagesController);
framework::ioc()->registerExport<ILanguageUnpacker>(moduleName(), new LanguageUnpacker());
}
void LanguagesModule::registerResources()
{
languages_init_qrc();
}
void LanguagesModule::registerUiTypes()
{
qmlRegisterType<LanguageListModel>("MuseScore.Languages", 1, 0, "LanguageListModel");
qmlRegisterUncreatableType<LanguageStatus>("MuseScore.Languages", 1, 0, "LanguageStatus", "Cannot create an LanguageStatus");
}
void LanguagesModule::onInit()
{
m_languagesController->init();
m_languagesConfiguration->init();
}

View file

@ -0,0 +1,39 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGESMODULE_H
#define MU_LANGUAGES_LANGUAGESMODULE_H
#include "modularity/imodulesetup.h"
namespace mu {
namespace languages {
class LanguagesModule : public framework::IModuleSetup
{
public:
std::string moduleName() const override;
void registerExports() override;
void registerResources() override;
void registerUiTypes() override;
void onInit() override;
};
}
}
#endif // MU_LANGUAGES_LANGUAGESMODULE_H

View file

@ -0,0 +1,66 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGESTYPES_H
#define MU_LANGUAGES_LANGUAGESTYPES_H
#include <QString>
#include <QVersionNumber>
#include <QJsonObject>
#include <QObject>
#include <QMetaObject>
namespace mu {
namespace languages {
class LanguageStatus
{
Q_GADGET
public:
enum class Status {
Undefined = 0,
Installed,
NoInstalled
};
Q_ENUM(Status)
};
struct Language
{
QString code;
QString name;
QString fileName;
double fileSize = 0.0;
bool isCurrent = false;
LanguageStatus::Status status = LanguageStatus::Status::Undefined;
Language() = default;
QJsonObject toJson() const
{
return { { "name", name },
{ "fileName", fileName },
{ "fileSize", fileSize },
{ "status", QString::number(static_cast<int>(status)) } };
}
};
using LanguagesHash = QHash<QString /*code*/, Language>;
}
}
#endif // MU_LANGUAGES_LANGUAGESTYPES_H

View file

@ -0,0 +1,104 @@
import QtQuick 2.7
import MuseScore.UiComponents 1.0
import MuseScore.Languages 1.0
Rectangle {
color: ui.theme.backgroundColor
Component.onCompleted: {
languageListModel.load()
}
LanguageListModel {
id: languageListModel
}
// TODO
FlatButton {
text: qsTrc("languages", "Update")
onClicked: {
languageListModel.updateList()
}
}
GridView {
id: view
anchors.fill: parent
anchors.topMargin: 50
model: languageListModel
clip: true
cellHeight: 150
cellWidth: 200
boundsBehavior: Flickable.StopAtBounds
delegate: Item {
height: view.cellHeight
width: view.cellWidth
Rectangle {
anchors.centerIn: parent
height: 130
width: 180
color: ui.theme.popupBackgroundColor
Column {
anchors.fill: parent
spacing: 10
StyledTextLabel {
anchors.left: parent.left
anchors.right: parent.right
text: name
}
Row {
anchors.left: parent.left
anchors.right: parent.right
spacing: 4
FlatButton {
text: qsTrc("languages", "Install")
width: 60
visible: status === LanguageStatus.NoInstalled
onClicked: {
languageListModel.install(index)
}
}
FlatButton {
text: qsTrc("languages", "Set as language")
width: 60
visible: status === LanguageStatus.Installed && !isCurrent
onClicked: {
languageListModel.setLanguage(index)
}
}
FlatButton {
text: qsTrc("languages", "Uninstall")
width: 60
visible: status === LanguageStatus.Installed || status === LanguageStatus.NeedUpdate
onClicked: {
languageListModel.uninstall(index)
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,2 @@
module MuseScore.Languages
LanguagesModule 1.0 LanguagesModule.qml

View file

@ -0,0 +1,138 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "languagelistmodel.h"
#include "log.h"
using namespace mu::languages;
LanguageListModel::LanguageListModel(QObject* parent)
: QAbstractListModel(parent)
{
m_roles.insert(rName, "name");
m_roles.insert(rFileSize, "fileSize");
m_roles.insert(rStatus, "status");
m_roles.insert(rIsCurrent, "isCurrent");
}
QVariant LanguageListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
Language item = m_list[index.row()];
switch (role) {
case rName:
return QVariant::fromValue(item.name);
case rFileSize:
return QVariant::fromValue(item.fileSize);
case rStatus:
return QVariant::fromValue(static_cast<int>(item.status));
case rIsCurrent:
return QVariant::fromValue(item.isCurrent);
}
return QVariant();
}
int LanguageListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent)
return m_list.count();
}
QHash<int, QByteArray> LanguageListModel::roleNames() const
{
return m_roles;
}
void LanguageListModel::load()
{
m_list.clear();
ValCh<LanguagesHash> languages = languagesController()->languages();
beginResetModel();
QList<Language> languageList = languages.val.values();
std::sort(languageList.begin(), languageList.end(), [](const Language& l, const Language& r){
return l.code < r.code;
});
m_list = languageList;
endResetModel();
RetCh<Language> languageChanged = languagesController()->languageChanged();
languageChanged.ch.onReceive(this, [this](const Language& newLanguage) {
for (int i = 0; i < m_list.count(); i++) {
if (m_list[i].code == newLanguage.code) {
m_list[i] = newLanguage;
QModelIndex index = createIndex(i, 0);
emit dataChanged(index, index);
return;
}
}
});
}
void LanguageListModel::updateList()
{
languagesController()->refreshLanguages();
load();
}
void LanguageListModel::install(int index)
{
if (index < 0 || index > m_list.count()) {
return;
}
Ret ret = languagesController()->install(m_list.at(index).code);
if (!ret) {
LOGE() << "Error" << ret.code() << ret.text();
return;
}
}
void LanguageListModel::uninstall(int index)
{
if (index < 0 || index > m_list.count()) {
return;
}
Ret ret = languagesController()->uninstall(m_list.at(index).code);
if (!ret) {
LOGE() << "Error" << ret.code() << ret.text();
return;
}
}
void LanguageListModel::setLanguage(int index)
{
if (index < 0 || index > m_list.count()) {
return;
}
Ret ret = languagesController()->setLanguage(m_list.at(index).code);
if (!ret) {
LOGE() << "Error" << ret.code() << ret.text();
return;
}
}

View file

@ -0,0 +1,64 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_LANGUAGES_LANGUAGELISTMODEL_H
#define MU_LANGUAGES_LANGUAGELISTMODEL_H
#include <QAbstractListModel>
#include "modularity/ioc.h"
#include "../ilanguagescontroller.h"
#include "async/asyncable.h"
namespace mu {
namespace languages {
class LanguageListModel : public QAbstractListModel, async::Asyncable
{
Q_OBJECT
INJECT(languages, ILanguagesController, languagesController)
public:
LanguageListModel(QObject* parent = nullptr);
enum Roles {
rName = Qt::UserRole + 1,
rFileSize,
rStatus,
rIsCurrent
};
QVariant data(const QModelIndex& index, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QHash<int,QByteArray> roleNames() const override;
Q_INVOKABLE void load();
Q_INVOKABLE void updateList();
Q_INVOKABLE void install(int index);
Q_INVOKABLE void uninstall(int index);
Q_INVOKABLE void setLanguage(int index);
private:
QHash<int, QByteArray> m_roles;
QList<Language> m_list;
};
}
}
#endif // MU_LANGUAGES_LANGUAGELISTMODEL_H