diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a122bd..ce8297b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,196 +1,198 @@ ### # Copyright 2013-2015 Andreas Cord-Landwehr # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### ecm_setup_version(0.99.90 VARIABLE_PREFIX ARTIKULATE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/ArtikulateConfigVersion.cmake" ) ecm_optional_add_subdirectory(qml) # set include directories include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${artikulate_SOURCE_DIR} ) # set the source code files from which Artikulate is compiled set(artikulateCore_SRCS + core/icourse.h + core/drawertrainingactions.cpp core/course.cpp core/resourcemanager.cpp core/language.cpp core/phrase.cpp core/phoneme.cpp core/phonemegroup.cpp core/unit.cpp core/skeleton.cpp core/editorsession.cpp core/trainingaction.cpp core/trainingactionicon.cpp core/trainingsession.cpp core/resources/resourceinterface.cpp core/resources/languageresource.cpp core/resources/courseresource.cpp core/resources/skeletonresource.cpp core/player.cpp core/recorder.cpp qmlcontrols/iconitem.cpp qmlcontrols/imagetexturescache.cpp qmlcontrols/managedtexturenode.cpp artikulate_debug.cpp ) kconfig_add_kcfg_files (artikulateCore_SRCS settings.kcfgc) add_library(artikulatecore SHARED ${artikulateCore_SRCS}) generate_export_header(artikulatecore BASE_NAME artikulatecore) target_link_libraries(artikulatecore LINK_PUBLIC artikulatelearnerprofile artikulatesound Qt5::XmlPatterns Qt5::Quick KF5::Archive KF5::ConfigGui ) # internal library without any API or ABI guarantee set(GENERIC_LIB_VERSION "0") set(GENERIC_LIB_SOVERSION "0") set_target_properties( artikulatecore PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) install( TARGETS artikulatecore DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES artikulate.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) # set the source code files from which Artikulate is compiled set(artikulate_SRCS main.cpp mainwindow.cpp application.cpp artikulate_debug.cpp models/coursemodel.cpp models/coursefiltermodel.cpp models/languagemodel.cpp models/languageresourcemodel.cpp # models/learningprogressmodel.cpp //TODO must be adapted to new trainingsession models/unitmodel.cpp models/unitfiltermodel.cpp models/phrasemodel.cpp models/phraselistmodel.cpp models/phrasefiltermodel.cpp models/phonememodel.cpp models/phonemegroupmodel.cpp models/phonemeunitmodel.cpp models/profilemodel.cpp models/skeletonmodel.cpp ui/sounddevicedialogpage.cpp ui/appearencedialogpage.cpp ui/resourcesdialogpage.cpp ) ki18n_wrap_ui (artikulate_SRCS ui/resourcesdialogpage.ui ui/sounddevicedialogpage.ui ui/appearencedialogpage.ui ) qt5_add_resources(artikulate_SRCS resources.qrc) kconfig_add_kcfg_files (artikulate_SRCS settings.kcfgc) set(artikulate_editor_SRCS main_editor.cpp mainwindow_editor.cpp application.cpp artikulate_debug.cpp models/coursemodel.cpp models/coursefiltermodel.cpp models/languagemodel.cpp models/languageresourcemodel.cpp # models/learningprogressmodel.cpp //TODO must be adapted to new trainingsession models/unitmodel.cpp models/unitfiltermodel.cpp models/phrasemodel.cpp models/phraselistmodel.cpp models/phrasefiltermodel.cpp models/phonememodel.cpp models/phonemegroupmodel.cpp models/phonemeunitmodel.cpp models/profilemodel.cpp models/skeletonmodel.cpp ui/sounddevicedialogpage.cpp ui/appearencedialogpage.cpp ui/resourcesdialogpage.cpp ui/exportghnsdialog.cpp ) ki18n_wrap_ui(artikulate_editor_SRCS ui/appearencedialogpage.ui ui/exportghnsdialog.ui ui/resourcesdialogpage.ui ui/sounddevicedialogpage.ui ) qt5_add_resources(artikulate_editor_SRCS resources.qrc) kconfig_add_kcfg_files (artikulate_editor_SRCS settings.kcfgc) # executables add_executable(artikulate ${artikulate_SRCS}) target_link_libraries(artikulate LINK_PUBLIC artikulatelearnerprofile artikulatesound artikulatecore Qt5::Qml Qt5::Quick KF5::Crash KF5::NewStuff KF5::XmlGui ) qt5_add_resources(artikulate_editor_SRCS editor.qrc) add_executable(artikulate_editor ${artikulate_editor_SRCS}) target_link_libraries(artikulate_editor LINK_PUBLIC artikulatesound artikulatecore Qt5::Qml Qt5::Quick Qt5::QuickWidgets KF5::Crash KF5::NewStuff KF5::XmlGui ) install(FILES artikulate.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(TARGETS artikulate ${INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS artikulate_editor ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/application.cpp b/src/application.cpp index 75cc43b..b9254be 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,121 +1,122 @@ /* * Copyright 2013-2015 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 "application.h" #include "core/course.h" #include "core/drawertrainingactions.h" #include "core/trainingaction.h" #include "core/editorsession.h" #include "core/language.h" #include "core/phoneme.h" #include "core/phonemegroup.h" #include "core/phrase.h" #include "core/player.h" #include "core/recorder.h" #include "core/resourcemanager.h" #include "core/skeleton.h" #include "core/trainingsession.h" #include "core/unit.h" #include "models/coursefiltermodel.h" #include "models/coursemodel.h" #include "models/languagemodel.h" #include "models/languageresourcemodel.h" #include "models/learningprogressmodel.h" #include "models/phonemegroupmodel.h" #include "models/phonememodel.h" #include "models/phonemeunitmodel.h" #include "models/phrasefiltermodel.h" #include "models/phraselistmodel.h" #include "models/phrasemodel.h" #include "models/profilemodel.h" #include "models/skeletonmodel.h" #include "models/unitfiltermodel.h" #include "models/unitmodel.h" #include "qmlcontrols/iconitem.h" #include "liblearnerprofile/src/learner.h" #include "liblearnerprofile/src/profilemanager.h" #include "liblearnerprofile/src/learninggoal.h" #include "liblearnerprofile/src/models/learninggoalmodel.h" #include #include #include #include Application::Application(int& argc, char** argv) : QApplication(argc, argv) { registerQmlTypes(); } void Application::registerQmlTypes() { qmlRegisterUncreatableType( "artikulate", 1, 0, "TrainingSession", QStringLiteral("TrainingSession is unique object provided by the backend")); qmlRegisterUncreatableType( "artikulate", 1, 0, "EditorSession", QStringLiteral("EditorSession is unique object provided by the backend")); qmlRegisterUncreatableType( "artikulate", 1, 0, "ResourceManager", QStringLiteral("ResourceManager is unique object provided by the backend")); qmlRegisterUncreatableType( "artikulate", 1, 0, "ProfileManager", QStringLiteral("ProfileManager is unique object provided by the backend")); + qmlRegisterUncreatableType("artikulate", 1, 0, "ICourse", "Courses are managed by repository"); qmlRegisterType("artikulate", 1, 0, "Learner"); qmlRegisterType("artikulate", 1, 0, "LearningGoal"); qmlRegisterType("artikulate", 1, 0, "Unit"); qmlRegisterType("artikulate", 1, 0, "Skeleton"); qmlRegisterType("artikulate", 1, 0, "Course"); qmlRegisterType("artikulate", 1, 0, "Language"); qmlRegisterType("artikulate", 1, 0, "ResourceManager"); qmlRegisterType("artikulate", 1, 0, "Phrase"); qmlRegisterType("artikulate", 1, 0, "Phoneme"); qmlRegisterType("artikulate", 1, 0, "PhonemeGroup"); qmlRegisterType("artikulate", 1, 0, "Player"); qmlRegisterType("artikulate", 1, 0, "Recorder"); qmlRegisterType("artikulate", 1, 0, "Icon"); qmlRegisterType("artikulate", 1, 0, "DrawerTrainingActions"); qmlRegisterType("artikulate", 1, 0, "TrainingAction"); qmlRegisterType("artikulate", 1, 0, "CourseModel"); qmlRegisterType("artikulate", 1, 0, "CourseFilterModel"); qmlRegisterType("artikulate", 1, 0, "LanguageModel"); qmlRegisterType("artikulate", 1, 0, "LanguageResourceModel"); // qmlRegisterType("artikulate", 1, 0, "LearningProgressModel");//TODO must be ported to new trainingsession qmlRegisterType("artikulate", 1, 0, "UnitModel"); qmlRegisterType("artikulate", 1, 0, "UnitFilterModel"); qmlRegisterType("artikulate", 1, 0, "PhraseModel"); qmlRegisterType("artikulate", 1, 0, "PhraseListModel"); qmlRegisterType("artikulate", 1, 0, "PhraseFilterModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeGroupModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeUnitModel"); qmlRegisterType("artikulate", 1, 0, "ProfileModel"); qmlRegisterType("artikulate", 1, 0, "SkeletonModel"); qmlRegisterType("artikulate", 1, 0, "LearningGoalModel"); } diff --git a/src/core/course.cpp b/src/core/course.cpp index 9f58ed8..0026faf 100644 --- a/src/core/course.cpp +++ b/src/core/course.cpp @@ -1,340 +1,340 @@ /* * Copyright 2013-2015 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 "course.h" #include "unit.h" #include "language.h" #include "resources/resourceinterface.h" #include "resources/courseresource.h" #include "resourcemanager.h" #include "phonemegroup.h" #include "artikulate_debug.h" #include #include #include #include Course::Course(ResourceInterface *resource) - : QObject(resource) + : ICourse(resource) , m_resource(qobject_cast(resource)) , m_language(nullptr) , m_modified(false) { } Course::~Course() { foreach (Unit *unit, m_unitList) { unit->deleteLater(); } m_unitList.clear(); // clear phonom units QMultiMap< PhonemeGroup*, QList< QPair > >::iterator groupIter = m_phonemeUnitList.begin(); while (groupIter != m_phonemeUnitList.end()) { QList< QPair >::iterator itemIter = groupIter->begin(); while (itemIter != groupIter->end()) { itemIter->first->deleteLater(); // delete phoneme itemIter->second->deleteLater(); // delete unit ++itemIter; } groupIter->clear(); ++groupIter; } m_phonemeUnitList.clear(); m_phonemeGroupList.clear(); } QString Course::id() const { return m_id; } void Course::setId(const QString &id) { if (id != m_id) { m_id = id; emit idChanged(); setModified(); } } QString Course::foreignId() const { return m_foreignId; } void Course::setForeignId(const QString &id) { m_foreignId = id; } QString Course::title() const { return m_title; } QString Course::i18nTitle() const { return m_resource->i18nTitle(); } void Course::setTitle(const QString &title) { if (QString::compare(title, m_title) != 0) { m_title = title; emit titleChanged(); setModified(); } } QString Course::description() const { return m_description; } void Course::setDescription(const QString &description) { m_description = description; emit descriptionChanged(); } Language * Course::language() const { return m_language; } void Course::setLanguage(Language *language) { Q_ASSERT(language); // TODO this should happen in the ctor foreach (PhonemeGroup *group, language->phonemeGroups()) { addPhonemeGroup(group); } m_language = language; emit languageChanged(); } QUrl Course::file() const { return m_file; } void Course::setFile(const QUrl &file) { m_file = file; } QList< Unit* > Course::unitList() const { return m_unitList; } void Course::addUnit(Unit *unit) { QList::ConstIterator iter = m_unitList.constBegin(); while (iter != m_unitList.constEnd()) { if (unit->id() == (*iter)->id()) { qCWarning(ARTIKULATE_LOG) << "Unit already contained in this course, aborting"; return; } ++iter; } emit unitAboutToBeAdded(unit, m_unitList.length()); m_unitList.append(unit); connect(unit, &Unit::modified, this, [=]() { setModified(true); }); // these connections are only present for "normal units" and take care to register // there phrases also at phoneme units // TODO: removing of phrase and upading of phonemes for that case is not implemented connect(unit, &Unit::phraseAdded, this, &Course::registerPhrasePhonemes); emit unitAdded(); setModified(true); } Unit * Course::createUnit() { // find first unused id QStringList unitIds; foreach (Unit *unit, m_unitList) { 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 Unit *unit = new Unit(this); unit->setCourse(this); unit->setId(id); unit->setTitle(i18n("New Unit")); addUnit(unit); return unit; } Phrase * Course::createPhrase(Unit *unit) { // find globally unique phrase id inside course QStringList phraseIds; foreach (Unit *unit, m_unitList) { foreach (Phrase *phrase, unit->phraseList()) { 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 Phrase *phrase = new Phrase(this); phrase->setId(id); phrase->setText(QLatin1String("")); phrase->setType(Phrase::Word); unit->addPhrase(phrase); return phrase; } QList< Unit* > Course::phonemeUnitList(PhonemeGroup *phonemeGroup) const { QList list; for (const auto &group : m_phonemeUnitList.value(phonemeGroup)) { list.append(group.second); } return list; } Unit * Course::phonemeUnit(Phoneme *phoneme) const { for (auto group = m_phonemeUnitList.keyBegin(); group != m_phonemeUnitList.keyEnd(); ++group) { m_phonemeUnitList.value(*group); for (const auto &phonemeUnit : m_phonemeUnitList.value(*group)) { if (phonemeUnit.first == phoneme) { return phonemeUnit.second; } } } return nullptr; } PhonemeGroup * Course::phonemeGroup(Unit *unit) const { for (auto group = m_phonemeUnitList.keyBegin(); group != m_phonemeUnitList.keyEnd(); ++group) { m_phonemeUnitList.value(*group); for (const auto &phonemeUnit : m_phonemeUnitList.value(*group)) { if (phonemeUnit.second == unit) { return *group; } } } return nullptr; } void Course::addPhonemeGroup(PhonemeGroup *phonemeGroup) { if (m_phonemeUnitList.contains(phonemeGroup)) { qCWarning(ARTIKULATE_LOG) << "Phoneme group already contained in this course, aborting"; return; } emit phonemeGroupAboutToBeAdded(phonemeGroup, m_phonemeGroupList.count()); // add to phoneme list m_phonemeGroupList.append(phonemeGroup); m_phonemeUnitList.insert(phonemeGroup, QList< QPair >()); emit phonemeGroupAdded(); setModified(); } QList Course::phonemeGroupList() const { return m_phonemeGroupList; } bool Course::modified() const { return m_modified; } void Course::setModified(bool modified) { if (m_modified == modified) { return; } m_modified = modified; emit modifiedChanged(); } void Course::sync() { if (!m_file.isValid() || m_file.isEmpty() || m_resource == nullptr) { qCritical() << "Path" << m_file.toLocalFile() << "not valid, aborting sync operation."; return; } m_resource->sync(); setModified(false); } bool Course::isContributorResource() const { return m_resource->isContributorResource(); } void Course::registerPhrasePhonemes(Phrase *phrase) { // iterate over all phonemes of this phrase foreach (Phoneme *phoneme, phrase->phonemes()) { // try to find corresponding phonem groups (phonem groups are registered on course creation) foreach (PhonemeGroup *group, m_phonemeGroupList) { if (!group->contains(phoneme)) { continue; } // either add phrase to existing unit or register a new one bool phraseRegistered = false; for (const auto &phonemeUnit : m_phonemeUnitList.value(group)) { if (phonemeUnit.first->id() == phoneme->id()) { phonemeUnit.second->addPhrase(phrase); phraseRegistered = true; } } // otherwise, need to create a new unit if (phraseRegistered == false) { // create unit based on the phoneme group Unit *unit = new Unit(this); unit->setId(phoneme->id()); unit->setTitle(phoneme->title()); unit->setCourse(this); m_phonemeUnitList[group].append(qMakePair(phoneme, unit)); unit->addPhrase(phrase); } } } } diff --git a/src/core/course.h b/src/core/course.h index ffde7b3..c1fa6c3 100644 --- a/src/core/course.h +++ b/src/core/course.h @@ -1,141 +1,140 @@ /* * 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 COURSE_H #define COURSE_H #include "artikulatecore_export.h" +#include "icourse.h" #include #include #include class ResourceInterface; class CourseResource; class QString; class Language; class Unit; class Phrase; class PhonemeGroup; class Phoneme; -class ARTIKULATECORE_EXPORT Course : public QObject +class ARTIKULATECORE_EXPORT Course : public ICourse { Q_OBJECT + Q_INTERFACES(ICourse) + Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString i18nTitle READ i18nTitle NOTIFY titleChanged) Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged) Q_PROPERTY(Language * language READ language NOTIFY languageChanged) public: - explicit Course(ResourceInterface *resource=0); - ~Course(); - QString id() const; + explicit Course(ResourceInterface *resource = nullptr); + ~Course() override; + QString id() const override; void setId(const QString &id); QString foreignId() const; void setForeignId(const QString &id); - QString title() const; - QString i18nTitle() const; + QString title() const override; + QString i18nTitle() const override; void setTitle(const QString &title); - Language * language() const; + Language * language() const override; void setLanguage(Language *language); - QString description() const; + QString description() const override; void setDescription(const QString &description); QUrl file() const; void setFile(const QUrl &file); - QList unitList() const; + QList unitList() const override; QList phonemeUnitList(PhonemeGroup *phonemeGroup) const; /** * \return the corresponding unit for phoneme \p phoneme */ Unit * phonemeUnit(Phoneme *phoneme) const; /** * \return the phoneme group containing the phoneme corresponding to \p unit */ PhonemeGroup * phonemeGroup(Unit *unit) const; void addUnit(Unit *unit); QList phonemeGroupList() const; void addPhonemeGroup(PhonemeGroup *phonemeGroup); /** * Create and add a new unit to course. * * \return pointer to the created unit */ Q_INVOKABLE Unit * createUnit(); /** * Create and add a new phrase and add it to the specified unit. The type of the created phrase * is initially Phrase::Word. * * \param unit the unit to that the created hprase shall be added * \return pointer to the created phrase */ Q_INVOKABLE Phrase * createPhrase(Unit *unit); /** * \return true if the course was modified after the last sync, otherwise false */ virtual bool modified() const; /** * Writes course object back to file and set \ref modified state to false. * If no file is set, no operation is performed. */ virtual Q_INVOKABLE void sync(); bool isContributorResource() const; public Q_SLOTS: void setModified(bool modified = true); void registerPhrasePhonemes(Phrase *phrase); Q_SIGNALS: - void idChanged(); - void titleChanged(); - void descriptionChanged(); void modifiedChanged(); - void languageChanged(); void unitAdded(); void unitAboutToBeAdded(Unit*,int); void unitsRemoved(); void unitsAboutToBeRemoved(int,int); void phonemeGroupAdded(); void phonemeGroupAboutToBeAdded(PhonemeGroup*,int); void phonemeGroupRemoved(); void phonemeGroupAboutToBeRemoved(int,int); private: Q_DISABLE_COPY(Course) CourseResource * const m_resource; QString m_id; QString m_foreignId; QString m_title; QString m_description; Language *m_language; QUrl m_file; bool m_modified; QList m_unitList; QList m_phonemeGroupList; QMap>> m_phonemeUnitList; }; #endif // COURSE_H diff --git a/src/core/icourse.h b/src/core/icourse.h new file mode 100644 index 0000000..4238717 --- /dev/null +++ b/src/core/icourse.h @@ -0,0 +1,67 @@ +/* + * 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 + +class QString; +class Language; +class Unit; + +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 NOTIFY titleChanged) + Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) + Q_PROPERTY(Language * language NOTIFY languageChanged) + +public: + ICourse(QObject *parent = nullptr) + : QObject(parent) + { + } + virtual ~ICourse() = default; + virtual QString id() const = 0; + virtual QString title() const = 0; + virtual QString i18nTitle() const = 0; + virtual QString description() const = 0; + virtual Language * language() const = 0; + virtual QList unitList() const = 0; + +Q_SIGNALS: + void idChanged(); + void titleChanged(); + void descriptionChanged(); + void languageChanged(); + void unitAdded(); + void unitAboutToBeAdded(Unit*,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/language.h b/src/core/language.h index 0d0956b..ce6fb55 100644 --- a/src/core/language.h +++ b/src/core/language.h @@ -1,74 +1,74 @@ /* * 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 LANGUAGE_H #define LANGUAGE_H #include "artikulatecore_export.h" #include #include #include class QString; class Phoneme; class PhonemeGroup; class ARTIKULATECORE_EXPORT Language : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString i18nTitle READ i18nTitle WRITE seti18nTitle NOTIFY i18nTitleChanged) public: - explicit Language(QObject *parent = 0); + explicit Language(QObject *parent = nullptr); ~Language(); QString id() const; void setId(const QString &id); QString title() const; void seti18nTitle(const QString &title); QString i18nTitle() const; void setTitle(const QString &title); QUrl file() const; void setFile(const QUrl &file); QList phonemes() const; Phoneme * addPhoneme(const QString &identifier, const QString &title); QList phonemeGroups() const; PhonemeGroup * addPhonemeGroup(const QString &identifier, const QString &title); -signals: +Q_SIGNALS: void idChanged(); void associatedLanguageItemChanged(); void titleChanged(); void i18nTitleChanged(); void phonemesChanged(); void phonemeGroupsChanged(); private: Q_DISABLE_COPY(Language) QString m_id; QString m_title; QString m_i18nTitle; QUrl m_file; QList m_phonemeGroups; }; #endif // LANGUAGE_H diff --git a/src/core/trainingsession.cpp b/src/core/trainingsession.cpp index 00c12d6..9a6eca7 100644 --- a/src/core/trainingsession.cpp +++ b/src/core/trainingsession.cpp @@ -1,273 +1,273 @@ /* * 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 . */ #include "trainingsession.h" #include "core/language.h" -#include "core/course.h" +#include "core/icourse.h" #include "core/unit.h" #include "core/phrase.h" #include "profilemanager.h" #include "learner.h" #include "trainingaction.h" #include "artikulate_debug.h" TrainingSession::TrainingSession(QObject *parent) : QObject(parent) , m_profileManager(nullptr) , m_course(nullptr) , m_unit(nullptr) { } void TrainingSession::setProfileManager(LearnerProfile::ProfileManager *manager) { if (m_profileManager == manager) { return; } m_profileManager = manager; } -Course * TrainingSession::course() const +ICourse * TrainingSession::course() const { return m_course; } -void TrainingSession::setCourse(Course *course) +void TrainingSession::setCourse(ICourse *course) { if (!course) { return; } if (m_course == course) { return; } m_course = course; if (m_course && m_course->unitList().count() > 0) { setUnit(m_course->unitList().constFirst()); } // lazy loading of training data LearnerProfile::LearningGoal * goal = m_profileManager->goal( LearnerProfile::LearningGoal::Language, m_course->id()); if (!goal) { goal = m_profileManager->registerGoal( LearnerProfile::LearningGoal::Language, course->language()->id(), course->language()->i18nTitle() ); } auto data = m_profileManager->progressValues(m_profileManager->activeProfile(), goal, m_course->id() ); Q_FOREACH(Unit *unit, m_course->unitList()) { Q_FOREACH(Phrase *phrase, unit->phraseList()) { auto iter = data.find(phrase->id()); if (iter != data.end()) { phrase->setProgress(iter.value()); } } } emit courseChanged(); } Unit * TrainingSession::unit() const { return m_unit; } void TrainingSession::setUnit(Unit *unit) { if (m_unit == unit) { return; } m_unit = unit; if (m_unit && m_unit->phraseList().count() > 0) { setPhrase(m_unit->phraseList().constFirst()); } emit unitChanged(); } TrainingAction * TrainingSession::activeAction() const { if (m_indexUnit < 0 || m_indexPhrase < 0) { return nullptr; } return qobject_cast(m_actions.at(m_indexUnit)->actions().at(m_indexPhrase)); } Phrase * TrainingSession::activePhrase() const { if (const auto action = activeAction()) { return action->phrase(); } return nullptr; } void TrainingSession::setPhrase(Phrase *phrase) { for (int i = 0; i < m_actions.count(); ++i) { for (int j = 0; j < m_actions.at(i)->actions().count(); ++j) { const auto testPhrase = qobject_cast(m_actions.at(i)->actions().at(j))->phrase(); if (phrase == testPhrase) { if (auto action = activeAction()) { action->setChecked(false); } m_indexUnit = i; m_indexPhrase = j; if (auto action = activeAction()) { action->setChecked(true); } emit phraseChanged(); return; } } } } void TrainingSession::accept() { Q_ASSERT(m_indexUnit >= 0); Q_ASSERT(m_indexPhrase >= 0); if (m_indexUnit < 0 || m_indexPhrase < 0) { return; } auto phrase = activePhrase(); // possibly update goals of learner updateGoal(); phrase->updateProgress(Phrase::Progress::Done); // store training activity LearnerProfile::LearningGoal * goal = m_profileManager->goal( LearnerProfile::LearningGoal::Language, m_course->language()->id()); m_profileManager->recordProgress(m_profileManager->activeProfile(), goal, m_course->id(), phrase->id(), static_cast(LearnerProfile::ProfileManager::Skip), phrase->progress() ); selectNextPhrase(); } void TrainingSession::skip() { Q_ASSERT(m_indexUnit >= 0); Q_ASSERT(m_indexPhrase >= 0); if (m_indexUnit < 0 || m_indexPhrase < 0) { return; } // possibly update goals of learner updateGoal(); auto phrase = activePhrase(); phrase->updateProgress(Phrase::Progress::Skip); // store training activity LearnerProfile::LearningGoal * goal = m_profileManager->goal( LearnerProfile::LearningGoal::Language, m_course->language()->id()); m_profileManager->recordProgress(m_profileManager->activeProfile(), goal, m_course->id(), phrase->id(), static_cast(LearnerProfile::ProfileManager::Skip), phrase->progress() ); selectNextPhrase(); } void TrainingSession::selectNextPhrase() { if (auto action = activeAction()) { action->setChecked(false); } // try to find next phrase, otherwise return completed if (m_indexPhrase >= m_actions.at(m_indexUnit)->actions().count() - 1) { if (m_indexUnit >= m_actions.count() - 1) { emit completed(); } else { ++m_indexUnit; m_indexPhrase = 0; } } else { ++m_indexPhrase; } if (auto action = activeAction()) { action->setChecked(true); } emit phraseChanged(); } bool TrainingSession::hasNext() const { return m_indexUnit < m_actions.count() - 1 || m_indexPhrase < m_actions.last()->actions().count() - 1; } void TrainingSession::updateGoal() { if (!m_profileManager) { qCWarning(ARTIKULATE_LOG) << "No ProfileManager registered, aborting operation"; return; } LearnerProfile::Learner *learner = m_profileManager->activeProfile(); if (!learner) { qCWarning(ARTIKULATE_LOG) << "No active Learner registered, aborting operation"; return; } LearnerProfile::LearningGoal * goal = m_profileManager->goal( LearnerProfile::LearningGoal::Language, m_course->language()->id()); learner->addGoal(goal); learner->setActiveGoal(goal); } QVector TrainingSession::trainingActions() { // cleanup for (const auto &action : qAsConst(m_actions)) { action->deleteLater(); } m_actions.clear(); if (!m_course) { return QVector(); } const auto unitList = m_course->unitList(); for (const auto &unit : qAsConst(unitList)) { auto action = new TrainingAction(unit->title()); const auto phraseList = unit->phraseList(); for (const auto &phrase : qAsConst(phraseList)) { if (phrase->sound().isEmpty()) { continue; } action->appendChild(new TrainingAction(phrase, this, unit)); } if (action->hasChildren()) { m_actions.append(action); } else { action->deleteLater(); } } return m_actions; } diff --git a/src/core/trainingsession.h b/src/core/trainingsession.h index 81fc26b..141ce77 100644 --- a/src/core/trainingsession.h +++ b/src/core/trainingsession.h @@ -1,90 +1,89 @@ /* * Copyright 2013-2015 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 TRAININGSESSION_H #define TRAININGSESSION_H #include "artikulatecore_export.h" -#include "course.h" #include "phrase.h" #include class QString; class Language; -class Course; +class ICourse; class Unit; class TrainingAction; namespace LearnerProfile { class ProfileManager; } /** * \class TrainingSession */ class ARTIKULATECORE_EXPORT TrainingSession : public QObject { Q_OBJECT - Q_PROPERTY(Course *course READ course WRITE setCourse NOTIFY courseChanged) + Q_PROPERTY(ICourse *course READ course WRITE setCourse NOTIFY courseChanged) Q_PROPERTY(Unit *unit READ unit WRITE setUnit NOTIFY unitChanged) Q_PROPERTY(Phrase *phrase READ activePhrase WRITE setPhrase NOTIFY phraseChanged) Q_PROPERTY(bool hasNext READ hasNext NOTIFY phraseChanged) public: explicit TrainingSession(QObject *parent = nullptr); void setProfileManager(LearnerProfile::ProfileManager *manager); - Course * course() const; - void setCourse(Course *course); + ICourse * course() const; + void setCourse(ICourse *course); Unit * unit() const; void setUnit(Unit *unit); TrainingAction * activeAction() const; Phrase * activePhrase() const; void setPhrase(Phrase *phrase); bool hasPreviousPhrase() const; bool hasNext() const; Q_INVOKABLE void accept(); Q_INVOKABLE void skip(); QVector trainingActions(); Q_SIGNALS: void courseChanged(); void unitChanged(); void phraseChanged(); /** * @brief Emitted when last phrase of session is skipped or marked as completed. */ void completed(); private: Q_DISABLE_COPY(TrainingSession) void selectNextPhrase(); Phrase * nextPhrase() const; void updateGoal(); LearnerProfile::ProfileManager *m_profileManager; - Course *m_course; + ICourse *m_course; Unit *m_unit; QVector m_actions; int m_indexUnit{-1}; int m_indexPhrase{-1}; }; #endif diff --git a/src/core/unit.cpp b/src/core/unit.cpp index d582add..2c81b9f 100644 --- a/src/core/unit.cpp +++ b/src/core/unit.cpp @@ -1,164 +1,164 @@ /* * 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 "unit.h" #include "phrase.h" #include #include #include #include #include #include "artikulate_debug.h" #include #include Unit::Unit(QObject *parent) : QObject(parent) , m_course(nullptr) , m_phraseSignalMapper(new QSignalMapper(this)) { } Unit::~Unit() { m_phraseSignalMapper->deleteLater(); } QString Unit::id() const { return m_id; } void Unit::setId(const QString &id) { if (id != m_id) { m_id = id; emit idChanged(); emit modified(); } } QString Unit::foreignId() const { return m_foreignId; } void Unit::setForeignId(const QString &id) { m_foreignId = id; } -Course * Unit::course() const +ICourse *Unit::course() const { return m_course; } -void Unit::setCourse(Course *course) +void Unit::setCourse(ICourse *course) { if (course == m_course) { return; } m_course = course; emit courseChanged(); } QString Unit::title() const { return m_title; } void Unit::setTitle(const QString &title) { if (QString::compare(title, m_title) != 0) { m_title = title; emit titleChanged(); emit modified(); } } QList< Phrase* > Unit::phraseList() const { return m_phraseList; } void Unit::addPhrase(Phrase *phrase) { QList::ConstIterator iter = m_phraseList.constBegin(); while (iter != m_phraseList.constEnd()) { if (phrase->id() == (*iter)->id()) { qCWarning(ARTIKULATE_LOG) << "Phrase is already contained in this unit, aborting"; return; } ++iter; } phrase->setUnit(this); emit phraseAboutToBeAdded(phrase, m_phraseList.length()); m_phraseList.append(phrase); m_phraseSignalMapper->setMapping(phrase, phrase->id()); emit phraseAdded(phrase); connect(phrase, &Phrase::typeChanged, m_phraseSignalMapper, static_cast(&QSignalMapper::map)); connect(phrase, &Phrase::modified, this, &Unit::modified); emit modified(); } QList Unit::excludedSkeletonPhraseList() const { QList excludedPhraseList; QList::ConstIterator iter = m_phraseList.constBegin(); while (iter != m_phraseList.constEnd()) { if ((*iter)->isExcluded() == true) { excludedPhraseList.append(*iter); } ++iter; } return excludedPhraseList; } void Unit::excludeSkeletonPhrase(const QString &phraseId) { foreach (Phrase *phrase, m_phraseList) { if (phrase->id() == phraseId) { phrase->setExcluded(true); emit modified(); return; } } qCWarning(ARTIKULATE_LOG) << "Could not exclude phrase with ID " << phraseId << ", no phrase with this ID."; } void Unit::includeSkeletonPhrase(const QString &phraseId) { foreach (Phrase *phrase, m_phraseList) { if (phrase->id() == phraseId) { phrase->setExcluded(false); emit modified(); return; } } qCWarning(ARTIKULATE_LOG) << "Could not include phrase with ID " << phraseId << ", no phrase with this ID."; } diff --git a/src/core/unit.h b/src/core/unit.h index 130ee58..57d1504 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1,88 +1,88 @@ /* * 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 UNIT_H #define UNIT_H #include "artikulatecore_export.h" #include "phrase.h" #include #include #include class QSignalMapper; class QString; class Phrase; -class Course; +class ICourse; class ARTIKULATECORE_EXPORT Unit : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) - Q_PROPERTY(Course *course READ course WRITE setCourse NOTIFY courseChanged) + Q_PROPERTY(ICourse *course READ course WRITE setCourse NOTIFY courseChanged) public: explicit Unit(QObject *parent = nullptr); ~Unit(); QString id() const; void setId(const QString &id); QString foreignId() const; void setForeignId(const QString &id); - Course * course() const; - void setCourse(Course* course); + ICourse * course() const; + void setCourse(ICourse* course); QString title() const; void setTitle(const QString &title); QList phraseList() const; void addPhrase(Phrase *phrase); QList excludedSkeletonPhraseList() const; /** * Removes phrase with ID \p phraseId from unit and adds ID to set * of excluded IDs. * * \param phraseId is the UID of the to be excluded phrase */ Q_INVOKABLE void excludeSkeletonPhrase(const QString &phraseId); Q_INVOKABLE void includeSkeletonPhrase(const QString &phraseId); Q_SIGNALS: void idChanged(); void titleChanged(); void courseChanged(); void displayPhraseTypeChanged(); void modified(); void phraseAdded(Phrase*); void phraseAboutToBeAdded(Phrase*,int); void phraseRemoved(Phrase*); void phraseAboutToBeRemoved(int,int); private: Q_DISABLE_COPY(Unit) QString m_id; QString m_foreignId; - Course *m_course; + ICourse *m_course; QString m_title; QList m_phraseList; QSignalMapper *m_phraseSignalMapper; }; #endif // UNIT_H