diff --git a/autotests/mocks/coursestub.h b/autotests/mocks/coursestub.h index 6375c02..d1822e9 100644 --- a/autotests/mocks/coursestub.h +++ b/autotests/mocks/coursestub.h @@ -1,98 +1,105 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef COURSESTUB_H #define COURSESTUB_H #include "src/core/icourse.h" #include "src/core/language.h" #include "src/core/unit.h" #include class CourseStub : public ICourse { public: CourseStub(std::shared_ptr language, QVector> units) : m_language(language) , m_units(units) { } ~CourseStub() override; static std::shared_ptr create(std::shared_ptr language, QVector> units) { auto course = std::make_shared(language, units); course->setSelf(course); return std::static_pointer_cast(course); } void setSelf(std::shared_ptr self) override { m_self = self; } QString id() const override { return "courseid"; } QString foreignId() const override { return "foreigncourseid"; } QString title() const override { return m_title; } void setTitle(QString title) { m_title = title; emit titleChanged(); } QString i18nTitle() const override { return "i18n title"; } QString description() const override { return "description of the course"; } std::shared_ptr language() const override { return m_language; } + QString languageTitle() const override + { + if (m_language) { + m_language->title(); + } + return QString(); + } QVector> units() override { return m_units; } QUrl file() const override { return QUrl(); } private: QString m_title{ "title" }; std::weak_ptr m_self; std::shared_ptr m_language; QVector> m_units; }; #endif diff --git a/autotests/mocks/editablecoursestub.h b/autotests/mocks/editablecoursestub.h index 834a200..3fa6c66 100644 --- a/autotests/mocks/editablecoursestub.h +++ b/autotests/mocks/editablecoursestub.h @@ -1,155 +1,162 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef EDITABLECOURSESTUB_H #define EDITABLECOURSESTUB_H #include "src/core/ieditablecourse.h" #include "src/core/ilanguage.h" #include "src/core/unit.h" #include class EditableCourseStub : public IEditableCourse { public: EditableCourseStub(std::shared_ptr language, QVector> units) : IEditableCourse() , m_language(language) , m_units(units) { } ~EditableCourseStub() override; static std::shared_ptr create(std::shared_ptr language, QVector> units) { auto course = std::make_shared(language, units); course->setSelf(course); for (auto &unit : units) { unit->setCourse(course); } return std::static_pointer_cast(course); } void setSelf(std::shared_ptr self) override { m_self = self; } std::shared_ptr self() const override { return std::static_pointer_cast(m_self.lock()); } QString id() const override { return m_id; } void setId(QString id) override { m_id = id; emit idChanged(); } QString foreignId() const override { return m_foreignId; } void setForeignId(QString id) override { m_foreignId = id; } QString title() const override { return m_title; } void setTitle(QString title) override { m_title = title; emit titleChanged(); } QString i18nTitle() const override { return m_i18nTitle; } void setI18nTitle(QString title) override { m_i18nTitle = title; } QString description() const override { return m_description; } void setDescription(QString description) override { m_description = description; emit descriptionChanged(); } std::shared_ptr language() const override { return m_language; } + QString languageTitle() const override + { + if (m_language) { + m_language->title(); + } + return QString(); + } void setLanguage(std::shared_ptr language) override { m_language = language; emit languageChanged(); } QVector> units() override { return m_units; } std::shared_ptr addUnit(std::shared_ptr unit) override { m_units.append(std::move(unit)); auto unitPtr = m_units.last(); unitPtr->setCourse(self()); return unitPtr; } QUrl file() const override { return QUrl(); } bool sync() override { return false; } void updateFrom(std::shared_ptr) override { // not implemented } bool isModified() const override { return false; } bool exportToFile(const QUrl &) const override { // do nothing return false; } private: std::weak_ptr m_self; QString m_id{ "courseid" }; QString m_foreignId{ "foreigncourseid" }; QString m_title{ "title" }; QString m_i18nTitle{ "i18n title" }; QString m_description{ "description of the course" }; std::shared_ptr m_language; QVector> m_units; }; #endif diff --git a/src/core/icourse.h b/src/core/icourse.h index 3957b16..a518636 100644 --- a/src/core/icourse.h +++ b/src/core/icourse.h @@ -1,78 +1,80 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef ICOURSE_H #define ICOURSE_H #include "artikulatecore_export.h" #include #include #include #include class QString; class ILanguage; class Unit; class Phoneme; class ARTIKULATECORE_EXPORT ICourse : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id NOTIFY idChanged) Q_PROPERTY(QString title READ title NOTIFY titleChanged) Q_PROPERTY(QString i18nTitle READ i18nTitle NOTIFY titleChanged) Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) + Q_PROPERTY(QString languageTitle READ languageTitle CONSTANT) public: virtual ~ICourse() = default; virtual QString id() const = 0; virtual QString foreignId() const = 0; virtual QString title() const = 0; virtual QString i18nTitle() const = 0; virtual QString description() const = 0; + virtual QString languageTitle() const = 0; virtual std::shared_ptr language() const = 0; /** * @brief Lazy loading unit list * @return list of units in course */ virtual QVector> units() = 0; virtual QUrl file() const = 0; protected: ICourse() : QObject() { } virtual void setSelf(std::shared_ptr course) = 0; Q_SIGNALS: void idChanged(); void titleChanged(); void descriptionChanged(); void languageChanged(); void unitAdded(); void unitAboutToBeAdded(std::shared_ptr,int); void unitsRemoved(); void unitsAboutToBeRemoved(int,int); }; Q_DECLARE_INTERFACE(ICourse, "com.kde.artikulate.ICourse/1.0") #endif // COURSE_H diff --git a/src/core/resources/courseresource.cpp b/src/core/resources/courseresource.cpp index 55f428f..21ff223 100644 --- a/src/core/resources/courseresource.cpp +++ b/src/core/resources/courseresource.cpp @@ -1,287 +1,295 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * Copyright 2013 Oindrila Gupta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "courseresource.h" #include "courseparser.h" #include "core/language.h" #include "core/unit.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include "core/iresourcerepository.h" #include #include #include #include #include #include #include #include #include "artikulate_debug.h" class CourseResourcePrivate { public: CourseResourcePrivate() = default; ~CourseResourcePrivate(); void loadCourse(CourseResource *parent); std::weak_ptr m_self; IResourceRepository *m_repository{ nullptr }; QUrl m_file; QString m_identifier; QString m_foreignId; QString m_title; QString m_languageId; std::shared_ptr m_language; QString m_i18nTitle; QString m_description; QVector> m_units; bool m_courseLoaded{ false }; ///> phonemes = m_language->phonemes(); auto units = CourseParser::parseUnits(m_file, phonemes); for (auto &unit : units) { parent->addUnit(std::move(unit)); } } std::shared_ptr CourseResource::create(const QUrl &path, IResourceRepository *repository) { std::shared_ptr course(new CourseResource(path, repository)); course->setSelf(course); return course; } void CourseResource::setSelf(std::shared_ptr self) { Q_ASSERT(d->m_self.expired()); d->m_self = self; } std::shared_ptr CourseResource::self() const { Q_ASSERT(!d->m_self.expired()); return d->m_self.lock(); } CourseResource::CourseResource(const QUrl &path, IResourceRepository *repository) : ICourse() , d(new CourseResourcePrivate()) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); d->m_file = path; d->m_repository = repository; // load basic information from language file, but does not parse everything QXmlStreamReader xml; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { xml.setDevice(&file); xml.readNextStartElement(); while (xml.readNext() && !xml.atEnd()) { if (xml.name() == "id") { d->m_identifier = xml.readElementText(); continue; } if (xml.name() == "foreignId") { d->m_foreignId = xml.readElementText(); continue; } //TODO i18nTitle must be implemented, currently missing and hence not parsed if (xml.name() == "title") { d->m_title = xml.readElementText(); d->m_i18nTitle = d->m_title; continue; } if (xml.name() == "description") { d->m_description = xml.readElementText(); continue; } if (xml.name() == "language") { d->m_languageId = xml.readElementText(); continue; } // quit reading when basic elements are read if (!d->m_identifier.isEmpty() && !d->m_title.isEmpty() && !d->m_i18nTitle.isEmpty() && !d->m_description.isEmpty() && !d->m_languageId.isEmpty() && !d->m_foreignId.isEmpty() ) { break; } } if (xml.hasError()) { qCritical() << "Error occurred when reading Course XML file:" << path.toLocalFile(); } } else { qCCritical(ARTIKULATE_CORE()) << "Could not open course file" << path.toLocalFile(); } xml.clear(); file.close(); // find correct language if (repository != nullptr) { for (const auto &language : repository->languages()) { if (language == nullptr) { continue; } if (language->id() == d->m_languageId) { d->m_language = language; } } } if (d->m_language == nullptr) { qCCritical(ARTIKULATE_CORE()) << "A course with an unknown language was loaded"; } } CourseResource::~CourseResource() = default; QString CourseResource::id() const { return d->m_identifier; } void CourseResource::setId(const QString &id) { if (d->m_identifier == id) { return; } d->m_identifier = id; emit idChanged(); } QString CourseResource::foreignId() const { return d->m_foreignId; } void CourseResource::setForeignId(const QString &foreignId) { if (d->m_foreignId == foreignId) { return; } d->m_foreignId = foreignId; emit foreignIdChanged(); } QString CourseResource::title() const { return d->m_title; } void CourseResource::setTitle(const QString &title) { if (d->m_title == title) { return; } d->m_title = title; emit titleChanged(); } QString CourseResource::i18nTitle() const { return d->m_i18nTitle; } void CourseResource::setI18nTitle(const QString &i18nTitle) { if (d->m_i18nTitle == i18nTitle) { return; } d->m_i18nTitle = i18nTitle; emit i18nTitleChanged(); } QString CourseResource::description() const { return d->m_description; } void CourseResource::setDescription(const QString &description) { if (d->m_description == description) { return; } d->m_description = description; emit descriptionChanged(); } std::shared_ptr CourseResource::language() const { return d->m_language; } +QString CourseResource::languageTitle() const +{ + if (d->m_language) { + return d->m_language->title(); + } + return QString(); +} + void CourseResource::setLanguage(std::shared_ptr language) { if (d->m_language == language) { return; } d->m_language = language; emit languageChanged(); } std::shared_ptr CourseResource::addUnit(std::shared_ptr unit) { std::shared_ptr storedUnit(std::move(unit)); storedUnit->setCourse(self()); emit unitAboutToBeAdded(storedUnit, d->m_units.count() - 1); d->m_units.append(storedUnit); emit unitAdded(); return storedUnit; } QVector> CourseResource::units() { if (d->m_courseLoaded == false) { d->loadCourse(this); } return d->m_units; } QUrl CourseResource::file() const { return d->m_file; } diff --git a/src/core/resources/courseresource.h b/src/core/resources/courseresource.h index 18f42ca..4c62b78 100644 --- a/src/core/resources/courseresource.h +++ b/src/core/resources/courseresource.h @@ -1,118 +1,120 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef COURSERESOURCE_H #define COURSERESOURCE_H #include "artikulatecore_export.h" #include "core/icourse.h" #include #include #include class QString; class CourseResourcePrivate; class Unit; class Phrase; class ILanguage; class IResourceRepository; class EditableCourseResource; class ARTIKULATECORE_EXPORT CourseResource : public ICourse { Q_OBJECT Q_INTERFACES(ICourse) public: static std::shared_ptr create(const QUrl &path, IResourceRepository *repository); ~CourseResource() override; /** * \return unique identifier */ QString id() const override; void setId(const QString &id); /** * \return global ID for this course */ QString foreignId() const override; void setForeignId(const QString &foreignId); /** * \return human readable localized title */ QString title() const override; void setTitle(const QString &title); /** * \return human readable title in English */ QString i18nTitle() const override; void setI18nTitle(const QString &i18nTitle); /** * \return description text for course */ QString description() const override; void setDescription(const QString &description); /** * \return language identifier of this course */ std::shared_ptr language() const override; + QString languageTitle() const override; + void setLanguage(std::shared_ptr language); std::shared_ptr addUnit(std::shared_ptr unit); void sync(); QUrl file() const override; QVector> units() override; Q_SIGNALS: void idChanged(); void foreignIdChanged(); void titleChanged(); void i18nTitleChanged(); void descriptionChanged(); void languageChanged(); private: /** * Create course resource from file. */ explicit CourseResource(const QUrl &path, IResourceRepository *repository); void setSelf(std::shared_ptr self) override; std::shared_ptr self() const; const std::unique_ptr d; friend EditableCourseResource; }; #endif diff --git a/src/core/resources/editablecourseresource.cpp b/src/core/resources/editablecourseresource.cpp index 05a1fb3..1f9b102 100644 --- a/src/core/resources/editablecourseresource.cpp +++ b/src/core/resources/editablecourseresource.cpp @@ -1,293 +1,298 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "editablecourseresource.h" #include "artikulate_debug.h" #include "core/phoneme.h" #include "core/phrase.h" #include "core/unit.h" #include "courseparser.h" #include #include #include #include #include #include #include #include #include #include EditableCourseResource::EditableCourseResource(const QUrl &path, IResourceRepository *repository) : IEditableCourse() , m_course(new CourseResource(path, repository)) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); connect(m_course.get(), &ICourse::unitAboutToBeAdded, this, &ICourse::unitAboutToBeAdded); connect(m_course.get(), &ICourse::unitAdded, this, &ICourse::unitAdded); connect(m_course.get(), &CourseResource::idChanged, this, &EditableCourseResource::idChanged); connect(m_course.get(), &CourseResource::foreignIdChanged, this, &EditableCourseResource::foreignIdChanged); connect( m_course.get(), &CourseResource::titleChanged, this, &EditableCourseResource::titleChanged); connect(m_course.get(), &CourseResource::descriptionChanged, this, &EditableCourseResource::descriptionChanged); connect(m_course.get(), &CourseResource::languageChanged, this, &EditableCourseResource::languageChanged); } std::shared_ptr EditableCourseResource::create( const QUrl &path, IResourceRepository *repository) { std::shared_ptr course(new EditableCourseResource(path, repository)); course->setSelf(course); return course; } void EditableCourseResource::setSelf(std::shared_ptr self) { m_course->setSelf(self); } QString EditableCourseResource::id() const { return m_course->id(); } void EditableCourseResource::setId(QString id) { if (m_course->id() != id) { m_course->setId(id); m_modified = true; } } QString EditableCourseResource::foreignId() const { return m_course->foreignId(); } void EditableCourseResource::setForeignId(QString foreignId) { m_course->setForeignId(std::move(foreignId)); } QString EditableCourseResource::title() const { return m_course->title(); } void EditableCourseResource::setTitle(QString title) { if (m_course->title() != title) { m_course->setTitle(title); m_modified = true; } } QString EditableCourseResource::i18nTitle() const { return m_course->i18nTitle(); } void EditableCourseResource::setI18nTitle(QString i18nTitle) { if (m_course->i18nTitle() != i18nTitle) { m_course->setI18nTitle(i18nTitle); m_modified = true; } } QString EditableCourseResource::description() const { return m_course->description(); } void EditableCourseResource::setDescription(QString description) { if (m_course->description() != description) { m_course->setDescription(description); m_modified = true; } } std::shared_ptr EditableCourseResource::language() const { return m_course->language(); } +QString EditableCourseResource::languageTitle() const +{ + return m_course->languageTitle(); +} + void EditableCourseResource::setLanguage(std::shared_ptr language) { if (m_course->language() != language) { m_course->setLanguage(language); m_modified = true; } } QUrl EditableCourseResource::file() const { return m_course->file(); } std::shared_ptr EditableCourseResource::self() const { return std::static_pointer_cast(m_course->self()); } bool EditableCourseResource::sync() { Q_ASSERT(file().isValid()); Q_ASSERT(file().isLocalFile()); Q_ASSERT(!file().isEmpty()); // not writing back if not modified if (!m_modified) { qCDebug(ARTIKULATE_LOG()) << "Aborting sync, course was not modified."; return false; } bool ok = exportToFile(file()); if (ok) { m_modified = false; } return ok; } bool EditableCourseResource::exportToFile(const QUrl &filePath) const { // write back to file // create directories if necessary QFileInfo info(filePath.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); if (!info.exists()) { qCDebug(ARTIKULATE_LOG()) << "create xml output file directory, not existing"; QDir dir; dir.mkpath(filePath.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); } // TODO port to KSaveFile QFile file(filePath.toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { qCWarning(ARTIKULATE_LOG()) << "Unable to open file " << file.fileName() << " in write mode, aborting."; return false; } file.write(CourseParser::serializedDocument(self(), false).toByteArray()); return true; } std::shared_ptr EditableCourseResource::addUnit(std::shared_ptr unit) { m_modified = true; auto sharedUnit = m_course->addUnit(std::move(unit)); sharedUnit->setCourse(self()); return sharedUnit; } QVector> EditableCourseResource::units() { if (!m_unitsLoaded) { for (auto &unit : m_course->units()) { unit->setCourse(self()); } m_unitsLoaded = true; } return m_course->units(); } void EditableCourseResource::updateFrom(std::shared_ptr skeleton) { for (auto skeletonUnit : skeleton->units()) { // find matching unit or create one std::shared_ptr matchingUnit; auto it = std::find_if(m_course->units().cbegin(), m_course->units().cend(), [skeletonUnit](std::shared_ptr compareUnit) { return compareUnit->foreignId() == skeletonUnit->id(); }); if (it == m_course->units().cend()) { // import complete unit auto importUnit = Unit::create(); importUnit->setId(skeletonUnit->id()); importUnit->setForeignId(skeletonUnit->id()); importUnit->setTitle(skeletonUnit->title()); matchingUnit = m_course->addUnit(std::move(importUnit)); } else { matchingUnit = *it; } // import phrases for (auto skeletonPhrase : skeletonUnit->phrases()) { auto it = std::find_if(matchingUnit->phrases().cbegin(), matchingUnit->phrases().cend(), [skeletonPhrase](std::shared_ptr comparePhrase) { return comparePhrase->foreignId() == skeletonPhrase->id(); }); if (it == matchingUnit->phrases().cend()) { // import complete Phrase std::shared_ptr importPhrase = Phrase::create(); importPhrase->setId(skeletonPhrase->id()); importPhrase->setForeignId(skeletonPhrase->id()); importPhrase->setText(skeletonPhrase->text()); importPhrase->seti18nText(skeletonPhrase->i18nText()); importPhrase->setType(skeletonPhrase->type()); importPhrase->setUnit(matchingUnit); matchingUnit->addPhrase(importPhrase); } } } qCInfo(ARTIKULATE_LOG()) << "Update performed!"; } bool EditableCourseResource::isModified() const { return m_modified; } Unit *EditableCourseResource::createUnit() { // find first unused id QStringList unitIds; for (auto unit : m_course->units()) { unitIds.append(unit->id()); } QString id = QUuid::createUuid().toString(); while (unitIds.contains(id)) { id = QUuid::createUuid().toString(); qCWarning(ARTIKULATE_LOG) << "Unit id generator has found a collision, recreating id."; } // create unit std::shared_ptr unit = Unit::create(); unit->setCourse(self()); unit->setId(id); unit->setTitle(i18n("New Unit")); auto sharedUnit = addUnit(std::move(unit)); return sharedUnit.get(); } std::shared_ptr EditableCourseResource::createPhrase(Unit *unit) { // find globally unique phrase id inside course QStringList phraseIds; for (auto unit : m_course->units()) { for (auto &phrase : unit->phrases()) { phraseIds.append(phrase->id()); } } QString id = QUuid::createUuid().toString(); while (phraseIds.contains(id)) { id = QUuid::createUuid().toString(); qCWarning(ARTIKULATE_LOG) << "Phrase id generator has found a collision, recreating id."; } // create unit std::shared_ptr phrase = Phrase::create(); phrase->setId(id); phrase->setText(QLatin1String("")); phrase->setType(IPhrase::Type::Word); unit->addPhrase(phrase); return phrase; } diff --git a/src/core/resources/editablecourseresource.h b/src/core/resources/editablecourseresource.h index 74b2dec..edb9af8 100644 --- a/src/core/resources/editablecourseresource.h +++ b/src/core/resources/editablecourseresource.h @@ -1,127 +1,128 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef EDITABLECOURSERESOURCE_H #define EDITABLECOURSERESOURCE_H #include "artikulatecore_export.h" #include "courseresource.h" #include "core/icourse.h" #include "core/ieditablecourse.h" #include #include #include class IResourceRepository; class Course; class Unit; class Phrase; class QString; class QDomDocument; /** * @brief Decorator for CourseResource * * This decorator adds functionality to modify and write back changes of a course. */ class ARTIKULATECORE_EXPORT EditableCourseResource : public IEditableCourse { Q_OBJECT Q_INTERFACES(ICourse) Q_INTERFACES(IEditableCourse) public: static std::shared_ptr create(const QUrl &path, IResourceRepository *repository); ~EditableCourseResource() override = default; /** * \return unique identifier */ QString id() const override; void setId(QString id) override; /** * \return unique identifier */ QString foreignId() const override; void setForeignId(QString foreignId) override; /** * \return human readable localized title */ QString title() const override; void setTitle(QString title) override; /** * \return human readable title in English */ QString i18nTitle() const override; void setI18nTitle(QString i18nTitle) override; /** * \return description text for course */ QString description() const override; void setDescription(QString description) override; /** * \return language identifier of this course */ std::shared_ptr language() const override; + QString languageTitle() const override; void setLanguage(std::shared_ptr language) override; bool sync() override; bool exportToFile(const QUrl &filePath) const override; std::shared_ptr addUnit(std::shared_ptr unit) override; QVector> units() override; void updateFrom(std::shared_ptr course) override; bool isModified() const override; QUrl file() const override; std::shared_ptr self() const override; Q_INVOKABLE Unit * createUnit(); Q_INVOKABLE std::shared_ptr createPhrase(Unit *unit); Q_SIGNALS: void idChanged(); void foreignIdChanged(); void titleChanged(); void i18nTitleChanged(); void descriptionChanged(); void languageChanged(); private: Q_DISABLE_COPY(EditableCourseResource) /** * Create course resource from file. */ explicit EditableCourseResource(const QUrl &path, IResourceRepository *repository); void setSelf(std::shared_ptr self) override; mutable bool m_unitsLoaded{ false }; ///< parsing of all units is postponed until needed, this variable indicates if they are read bool m_modified{ false }; const std::unique_ptr m_course; }; #endif diff --git a/src/core/resources/skeletonresource.cpp b/src/core/resources/skeletonresource.cpp index 57cb6f5..63cf7e0 100644 --- a/src/core/resources/skeletonresource.cpp +++ b/src/core/resources/skeletonresource.cpp @@ -1,370 +1,375 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "skeletonresource.h" #include "courseparser.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" #include "editablecourseresource.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include #include #include #include #include #include #include #include "artikulate_debug.h" class SkeletonResourcePrivate { public: SkeletonResourcePrivate(const QUrl &path) : m_path(path) { // load basic information from language file, but does not parse everything QXmlStreamReader xml; QFile file(path.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { xml.setDevice(&file); xml.readNextStartElement(); while (xml.readNext() && !xml.atEnd()) { if (xml.name() == "id") { m_identifier = xml.readElementText(); continue; } if (xml.name() == "title") { m_title = xml.readElementText(); continue; } if (xml.name() == "description") { m_description = xml.readElementText(); continue; } // quit reading when basic elements are read if (!m_identifier.isEmpty() && !m_title.isEmpty() && !m_description.isEmpty() ) { break; } } if (xml.hasError()) { qCritical() << "Error occurred when reading Course XML file:" << path.toLocalFile(); } } else { qCCritical(ARTIKULATE_CORE()) << "Could not open course file" << path.toLocalFile(); } xml.clear(); file.close(); m_modified = false; } QVector> units(); std::shared_ptr appendUnit(std::shared_ptr unit); /** * @return the skeleton resource as serialized byte array */ QDomDocument serializedSkeleton(); std::weak_ptr m_self; QUrl m_path; QString m_identifier; QString m_title; QString m_description; bool m_unitsParsed{ false }; bool m_modified{ false }; protected: QVector> m_units; ///!< the units variable is loaded lazily and shall never be access directly }; QVector> SkeletonResourcePrivate::units() { if (m_unitsParsed) { return m_units; } auto units = CourseParser::parseUnits(m_path); for (auto &unit : units) { Q_ASSERT(m_self.lock() != nullptr); unit->setCourse(m_self.lock()); m_units.append(std::move(unit)); } m_unitsParsed = true; return m_units; } std::shared_ptr SkeletonResourcePrivate::appendUnit(std::shared_ptr unit) { units(); // ensure that units are parsed m_units.append(unit); m_modified = true; Q_ASSERT(m_self.lock() != nullptr); unit->setCourse(m_self.lock()); return m_units.last(); } QDomDocument SkeletonResourcePrivate::serializedSkeleton() { QDomDocument document; // prepare xml header QDomProcessingInstruction header = document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\"")); document.appendChild(header); // create main element QDomElement root = document.createElement(QStringLiteral("skeleton")); document.appendChild(root); QDomElement idElement = document.createElement(QStringLiteral("id")); QDomElement titleElement = document.createElement(QStringLiteral("title")); QDomElement descriptionElement = document.createElement(QStringLiteral("description")); idElement.appendChild(document.createTextNode(m_identifier)); titleElement.appendChild(document.createTextNode(m_title)); descriptionElement.appendChild(document.createTextNode(m_description)); QDomElement unitListElement = document.createElement(QStringLiteral("units")); // create units for (auto unit : units()) { QDomElement unitElement = document.createElement(QStringLiteral("unit")); QDomElement unitIdElement = document.createElement(QStringLiteral("id")); QDomElement unitTitleElement = document.createElement(QStringLiteral("title")); QDomElement unitPhraseListElement = document.createElement(QStringLiteral("phrases")); unitIdElement.appendChild(document.createTextNode(unit->id())); unitTitleElement.appendChild(document.createTextNode(unit->title())); // construct phrases for (auto &phrase : unit->phrases()) { QDomElement phraseElement = document.createElement(QStringLiteral("phrase")); QDomElement phraseIdElement = document.createElement(QStringLiteral("id")); QDomElement phraseTextElement = document.createElement(QStringLiteral("text")); QDomElement phraseTypeElement = document.createElement(QStringLiteral("type")); phraseIdElement.appendChild(document.createTextNode(phrase->id())); phraseTextElement.appendChild(document.createTextNode(phrase->text())); phraseTypeElement.appendChild(document.createTextNode(phrase->typeString())); phraseElement.appendChild(phraseIdElement); phraseElement.appendChild(phraseTextElement); phraseElement.appendChild(phraseTypeElement); unitPhraseListElement.appendChild(phraseElement); } // construct the unit element unitElement.appendChild(unitIdElement); unitElement.appendChild(unitTitleElement); unitElement.appendChild(unitPhraseListElement); unitListElement.appendChild(unitElement); } root.appendChild(idElement); root.appendChild(titleElement); root.appendChild(descriptionElement); root.appendChild(unitListElement); return document; } std::shared_ptr SkeletonResource::create(const QUrl &path, IResourceRepository *repository) { std::shared_ptr course(new SkeletonResource(path, repository)); course->setSelf(course); return course; } SkeletonResource::SkeletonResource(const QUrl &path, IResourceRepository *repository) : IEditableCourse() , d(new SkeletonResourcePrivate(path)) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); connect(this, &SkeletonResource::idChanged, this, [=]() { d->m_modified = true; }); connect(this, &SkeletonResource::titleChanged, this, [=]() { d->m_modified = true; }); connect(this, &SkeletonResource::descriptionChanged, this, [=]() { d->m_modified = true; }); Q_UNUSED(repository); } SkeletonResource::~SkeletonResource() = default; void SkeletonResource::setSelf(std::shared_ptr self) { d->m_self = self; } std::shared_ptr SkeletonResource::self() const { return std::static_pointer_cast(d->m_self.lock()); } QString SkeletonResource::id() const { return d->m_identifier; } void SkeletonResource::setId(QString id) { if (d->m_identifier == id) { return; } d->m_identifier = id; emit idChanged(); } QString SkeletonResource::foreignId() const { return id(); } void SkeletonResource::setForeignId(QString id) { Q_UNUSED(id); Q_UNREACHABLE(); } QString SkeletonResource::title() const { return d->m_title; } void SkeletonResource::setTitle(QString title) { if (d->m_title == title) { return; } d->m_title = title; emit titleChanged(); } QString SkeletonResource::i18nTitle() const { // there are no localized titles available return title(); } void SkeletonResource::setI18nTitle(QString title) { Q_UNUSED(title); Q_UNREACHABLE(); } QString SkeletonResource::description() const { return d->m_description; } void SkeletonResource::setDescription(QString description) { if (d->m_description == description) { return; } d->m_description = description; emit descriptionChanged(); } bool SkeletonResource::exportToFile(const QUrl &filePath) const { // write back to file // create directories if necessary QFileInfo info(filePath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); if (!info.exists()) { qCDebug(ARTIKULATE_LOG()) << "create xml output file directory, not existing"; QDir dir; dir.mkpath(filePath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } //TODO port to atomic file swap QFile file(filePath.toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { qCWarning(ARTIKULATE_LOG()) << "Unable to open file " << filePath << " in write mode, aborting."; return false; } file.write(d->serializedSkeleton().toByteArray()); return true; } std::shared_ptr SkeletonResource::addUnit(std::shared_ptr unit) { std::shared_ptr storedUnit(std::move(unit)); emit unitAboutToBeAdded(storedUnit, d->units().count() - 1); d->appendUnit(storedUnit); emit unitAdded(); return storedUnit; } bool SkeletonResource::sync() { if (!d->m_modified) { qCDebug(ARTIKULATE_LOG()) << "Aborting sync, skeleton was not modified."; return false; } bool ok = exportToFile(file()); if (ok) { d->m_modified = false; } return ok; } void SkeletonResource::updateFrom(std::shared_ptr) { // not supported } bool SkeletonResource::isModified() const { return d->m_modified; } std::shared_ptr SkeletonResource::language() const { // skeleton must not have a dedicated language return std::shared_ptr(); } +QString SkeletonResource::languageTitle() const +{ + return QString(); +} + void SkeletonResource::setLanguage(std::shared_ptr language) { Q_UNUSED(language); Q_UNREACHABLE(); } QVector> SkeletonResource::units() { return d->units(); } QUrl SkeletonResource::file() const { return d->m_path; } diff --git a/src/core/resources/skeletonresource.h b/src/core/resources/skeletonresource.h index 0d15828..a84cfc9 100644 --- a/src/core/resources/skeletonresource.h +++ b/src/core/resources/skeletonresource.h @@ -1,74 +1,75 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef SKELETONRESOURCE_H #define SKELETONRESOURCE_H #include "artikulatecore_export.h" #include "core/ieditablecourse.h" #include class SkeletonResourcePrivate; class IResourceRepository; /** * @brief The SkeletonResource class is a decorator for EditableCourseResource */ class ARTIKULATECORE_EXPORT SkeletonResource : public IEditableCourse { Q_OBJECT Q_INTERFACES(ICourse) public: static std::shared_ptr create(const QUrl &path, IResourceRepository *repository); ~SkeletonResource() override; QString id() const override; void setId(QString id) override; QString foreignId() const override; void setForeignId(QString id) override; QString title() const override; void setTitle(QString title) override; QString i18nTitle() const override; void setI18nTitle(QString title) override; QString description() const override; void setDescription(QString description) override; std::shared_ptr language() const override; + QString languageTitle() const override; void setLanguage(std::shared_ptr language) override; QVector> units() override; QUrl file() const override; bool exportToFile(const QUrl &filePath) const override; std::shared_ptr addUnit(std::shared_ptr unit) override; bool sync() override; void updateFrom(std::shared_ptr) override; bool isModified() const override; std::shared_ptr self() const override; private: /** * Create course resource from file. */ explicit SkeletonResource(const QUrl &path, IResourceRepository *repository); void setSelf(std::shared_ptr self) override; const QScopedPointer d; }; #endif diff --git a/src/qml/CourseConfigurationPage.qml b/src/qml/CourseConfigurationPage.qml new file mode 100644 index 0000000..15f7e47 --- /dev/null +++ b/src/qml/CourseConfigurationPage.qml @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Andreas Cord-Landwehr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.4 +import QtQuick.Dialogs 1.2 +import org.kde.kirigami 2.7 as Kirigami + +Kirigami.Page { + id: root + //TODO adapt to editor session if course or skeleton: requires refactoring of editorsession to only operate on one course + title: i18n("Course Configuration") + + Kirigami.FormLayout { + anchors.fill: parent + Kirigami.Separator { + Kirigami.FormData.label: i18n("Course Description") + Kirigami.FormData.isSection: true + } + TextField { + Kirigami.FormData.label: i18n("Title:") + text: g_editorSession.course.title + Layout.preferredWidth: .7 * root.width + onAccepted: g_editorSession.course.title = text + } + TextField { + Kirigami.FormData.label: i18n("Localized Title:") + text: g_editorSession.course.i18nTitle + Layout.preferredWidth: .7 * root.width + onAccepted: g_editorSession.course.i18nTitle = text + } + TextField { + Kirigami.FormData.label: i18n("Description:") + text: g_editorSession.course.description + Layout.preferredWidth: .7 * root.width + onAccepted: g_editorSession.course.description = text + } + TextField { + Kirigami.FormData.label: i18n("Language:") + text: g_editorSession.course.languageTitle + readOnly: true + Layout.preferredWidth: .7 * root.width + } + + Kirigami.Separator { + Kirigami.FormData.label: i18n("Prototype") + Kirigami.FormData.isSection: true + } + Button { + Kirigami.FormData.label: i18n("Update from Prototype:") + enabled: g_editorSession.isSkeletonMode + Layout.minimumWidth: 200 + text: i18n("Update") + icon.name: "view-refresh" + ToolTip.visible: hovered + ToolTip.delay: 1000 + ToolTip.timeout: 5000 + ToolTip.text: i18n("Update the course with elements from prototype.") + onClicked: g_editorSession.updateCourseFromSkeleton() + } + +// TODO add export functionalities +// Kirigami.Separator { +// Kirigami.FormData.label: i18n("Export") +// Kirigami.FormData.isSection: true +// } + + } +} diff --git a/src/qml/EditCoursePage.qml b/src/qml/EditCoursePage.qml index 5f2832b..1646824 100644 --- a/src/qml/EditCoursePage.qml +++ b/src/qml/EditCoursePage.qml @@ -1,138 +1,122 @@ /* * Copyright 2013-2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ import QtQuick 2.5 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQml.Models 2.2 import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.ScrollablePage { id: root title: i18n("Edit Course") actions { left: Kirigami.Action { text: i18n("Previous") tooltip: i18n("Switch to previous phrase.") iconName: "go-previous" enabled: g_editorSession.hasPreviousPhrase onTriggered: g_editorSession.switchToPreviousPhrase() } right: Kirigami.Action { text: i18n("Next") tooltip: i18n("Switch to next phrase.") iconName: "go-next" enabled: g_editorSession.hasNextPhrase onTriggered: g_editorSession.switchToNextPhrase() } } ColumnLayout { id: main spacing: 10 LanguageModel { id: languageModel view: LanguageModel.AllLanguages resourceModel: LanguageResourceModel { repository: g_repository } } CourseModel { id: courseModel } UnitModel { id: selectedUnitModel course: g_editorSession.displayedCourse } - RowLayout { - Item { Layout.fillWidth: true } - Button { - id: buttonSyncFromSkeleton - enabled: g_editorSession.isSkeletonMode - Layout.minimumWidth: 200 - text: i18n("Sync Prototype") - icon.name: "view-refresh" - ToolTip.visible: hovered - ToolTip.delay: 1000 - ToolTip.timeout: 5000 - ToolTip.text: i18n("Update the course with elements from prototype.") - onClicked: g_editorSession.updateCourseFromSkeleton() - } - } - RowLayout { id: createNewCourseRow visible: g_editorSession.isSkeletonMode Label { text: i18n("There is no course in the selected language.") } ComboBox { // course selection only necessary when we do not edit skeleton derived course id: comboCourse visible: !g_editorSession.skeletonMode Layout.fillWidth: true model: CourseFilterModel { id: courseFilterModel courseModel: courseModel language: g_editorSession.language } textRole: "title" onCurrentIndexChanged: { if (courseFilterModel.course(currentIndex)) { g_editorSession.course = courseFilterModel.course(languageSelectionComboBox.currentIndex) } } onVisibleChanged: { if (visible && courseFilterModel.course(currentIndex)) { g_editorSession.course = courseFilterModel.course(languageSelectionComboBox.currentIndex) } } } Button { text: i18n("Create Course") icon.name: "journal-new" onClicked: { g_editorSession.course = g_repository.createCourse(languageModel.language(languageSelectionComboBox.currentIndex), g_editorSession.skeleton) } } Button { // add units only if skeleton id: newUnitButton visible: !g_editorSession.skeletonMode || g_editorSession.editSkeleton icon.name: "list-add" text: i18n("New Unit") onClicked: phraseModel.course.createUnit() } } ColumnLayout { PhraseEditor { visible: g_editorSession.phrase !== null phrase: g_editorSession.phrase isSkeletonPhrase: g_editorSession.editSkeleton Layout.minimumWidth: Math.floor(main.width * 0.6) Layout.fillHeight: true } } } } diff --git a/src/qml/Editor.qml b/src/qml/Editor.qml index 0519672..4f4d94a 100644 --- a/src/qml/Editor.qml +++ b/src/qml/Editor.qml @@ -1,65 +1,69 @@ /* * Copyright 2013-2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ import QtQuick 2.5 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.ApplicationWindow { id: root function changePage(pageItem) { root.pageStack.clear(); root.pageStack.push(pageItem); } globalDrawer: EditorDrawer { pageStack: root.pageStack } pageStack.initialPage: [ editorCourseSelectionPage, editorSkeletonSelectionPage ] pageStack.globalToolBar.style: pageStack.depth > 1 ? Kirigami.ApplicationHeaderStyle.TabBar :Kirigami.ApplicationHeaderStyle.Titles // pages Component { id: editorCourseSelectionPage EditorCourseSelectionPage { } } Component { id: editorSkeletonSelectionPage EditorSkeletonSelectionPage { } } Component { id: editCoursePageComponent EditCoursePage { } } + Component { + id: courseConfigurationPageComponent + CourseConfigurationPage { } + } Component { id: repositoryPageComponent RepositoryConfigurationPage { } } Component { id: aboutPageComponent Kirigami.AboutPage { aboutData: g_artikulateAboutData } } } diff --git a/src/qml/EditorDrawer.qml b/src/qml/EditorDrawer.qml index edb42ca..d1a4eee 100644 --- a/src/qml/EditorDrawer.qml +++ b/src/qml/EditorDrawer.qml @@ -1,148 +1,159 @@ /* * Copyright 2018 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ import QtQuick 2.5 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.GlobalDrawer { id: root title: "Editor" titleIcon: "artikulate" resetMenuOnTriggered: false bottomPadding: 0 property QtObject pageStack // enforce drawer always to be open modal: false handleVisible: false topContent: [ ColumnLayout { spacing: 0 Layout.fillWidth: true Layout.leftMargin: -root.leftPadding Layout.rightMargin: -root.rightPadding ActionListItem { action: Kirigami.Action { text: i18n("Courses") iconName: "artikulate" onTriggered: { root.pageStack.clear(); root.pageStack.push(editorCourseSelectionPage); root.pageStack.push(editorSkeletonSelectionPage); } } } ActionListItem { action: Kirigami.Action { text: i18n("Repository") iconName: "folder-sync" onTriggered: { root.pageStack.clear(); root.pageStack.push(repositoryPageComponent); } } } Kirigami.Separator { Layout.fillWidth: true } + ActionListItem { + action: Kirigami.Action { + text: i18n("Course Configuration") + iconName: "document-properties" + enabled: g_editorSession.course !== null + onTriggered: { + root.pageStack.clear(); + root.pageStack.push(courseConfigurationPageComponent); + } + } + } } ] // ordinary Kirigami actions are filled from training units/phrases actions: trainingActions.actions DrawerTrainingActions { id: trainingActions session: g_editorSession onTriggerTrainingView: { root.pageStack.clear(); root.pageStack.push(editCoursePageComponent); } } //TODO integrate again // [ // Kirigami.Action { // text: i18n("Help") // iconName: "help-about" // Kirigami.Action { // text: i18n("Artikulate Handbook") // iconName: "help-contents" // onTriggered: { // triggerAction("help_contents"); // globalDrawer.resetMenu(); // } // } // Kirigami.Action { // text: i18n("Report Bug") // iconName: "tools-report-bug" // onTriggered: { // triggerAction("help_report_bug"); // globalDrawer.resetMenu(); // } // } // Kirigami.Action { // text: i18n("About KDE") // iconName: "help-about" // onTriggered: { // triggerAction("help_about_kde") // globalDrawer.resetMenu(); // } // } // } // ] ColumnLayout { spacing: 0 Layout.fillWidth: true Layout.leftMargin: -root.leftPadding Layout.rightMargin: -root.rightPadding Kirigami.Separator { Layout.fillWidth: true } //TODO planned but not implemented // ActionListItem { // action: Kirigami.Action { // text: i18n("Upload Training") // iconName: "get-hot-new-stuff" // onTriggered: { // root.pageStack.pop(); // root.pageStack.push(downloadPageComponent); // } // } // } ActionListItem { action: Kirigami.Action { text: i18n("About") iconName: "help-about" onTriggered: { root.pageStack.pop(); root.pageStack.push(aboutPageComponent); } } } } } diff --git a/src/qml/EditorSkeletonSelectionPage.qml b/src/qml/EditorSkeletonSelectionPage.qml index 470adef..bc0779e 100644 --- a/src/qml/EditorSkeletonSelectionPage.qml +++ b/src/qml/EditorSkeletonSelectionPage.qml @@ -1,84 +1,84 @@ /* * Copyright 2015-2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ import QtQuick 2.1 import QtQuick.Controls 2.1 as QQC2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.ScrollablePage { id: root - title: i18n("Select Skeleton") + title: i18n("Select Prototype") Kirigami.CardsListView { id: listView width: root.width - 40 model: SkeletonModel { id: courseModel } delegate: Kirigami.AbstractCard { contentItem: Item { implicitHeight: delegateLayout.implicitHeight GridLayout { id: delegateLayout anchors { left: parent.left top: parent.top right: parent.right } rowSpacing: Kirigami.Units.largeSpacing columnSpacing: Kirigami.Units.largeSpacing columns: width > Kirigami.Units.gridUnit * 20 ? 4 : 2 Kirigami.Icon { source: "language-artikulate" Layout.fillHeight: true Layout.maximumHeight: Kirigami.Units.iconSizes.huge Layout.preferredWidth: height } ColumnLayout { Kirigami.Heading { level: 2 - text: i18nc("@title:window skeleton name", "%1", model.title) + text: i18nc("@title:window prototype name", "%1", model.title) } Kirigami.Separator { Layout.fillWidth: true } QQC2.Label { Layout.fillWidth: true wrapMode: Text.WordWrap text: model.description } } QQC2.Button { Layout.alignment: Qt.AlignRight|Qt.AlignVCenter Layout.columnSpan: 2 - text: i18nc("@action:button", "Edit Skeleton") + text: i18nc("@action:button", "Edit Prototype") onClicked: { - showPassiveNotification("Selected skeleton for editor: " + model.title + "."); + showPassiveNotification("Selected prototype for editor: " + model.title + "."); g_editorSession.skeleton = model.dataRole } } } } } } } diff --git a/src/resources.qrc b/src/resources.qrc index 13de296..20be6a7 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,30 +1,31 @@ qml/ActionListItem.qml qml/ArtikulateDrawer.qml + qml/CourseConfigurationPage.qml qml/DownloadPage.qml qml/EditCoursePage.qml qml/Editor.qml qml/EditorDrawer.qml qml/EditorSkeletonSelectionPage.qml qml/EditorCourseSelectionPage.qml qml/Main.qml qml/PhonemeUnitSelector.qml qml/PhraseEditor.qml qml/PhraseEditorEditStateComponent.qml qml/PhraseEditorSoundComponent.qml qml/PhraseEditorTypeComponent.qml qml/ProfileSelector.qml qml/ProfileSettingsPage.qml qml/ProfileUserImageItem.qml qml/RepositoryConfigurationPage.qml qml/SoundPlayer.qml qml/SoundRecorder.qml qml/TrainerCourseStatistics.qml qml/TrainingPage.qml qml/WelcomePage.qml artikulate.knsrc