diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 040a4ff..092ae16 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,187 +1,188 @@ ### # Copyright 2013-2019 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 application.cpp artikulate_debug.cpp core/icourse.h core/ieditablecourse.h core/ieditablephrase.h core/ieditablerepository.h core/ieditableunit.h core/ilanguage.h core/iphrase.h + core/isessionactions.h core/iunit.h core/iresourcerepository.h core/drawertrainingactions.cpp core/resourcerepository.cpp core/contributorrepository.cpp core/language.cpp core/phrase.cpp core/phoneme.cpp core/phonemegroup.cpp core/unit.cpp core/editorsession.cpp core/trainingaction.cpp core/trainingactionicon.cpp core/trainingsession.cpp core/resources/courseparser.cpp core/resources/courseresource.cpp core/resources/editablecourseresource.cpp core/resources/skeletonresource.cpp core/player.cpp core/recorder.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 qmlcontrols/iconitem.cpp qmlcontrols/imagetexturescache.cpp qmlcontrols/managedtexturenode.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::Qml 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 LIBRARY NAMELINK_SKIP 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 artikulate_debug.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) qt5_add_resources(artikulate_SRCS ../data/languages.qrc) kconfig_add_kcfg_files (artikulate_SRCS settings.kcfgc) set(artikulate_editor_SRCS main_editor.cpp mainwindow_editor.cpp artikulate_debug.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) qt5_add_resources(artikulate_editor_SRCS ../data/languages.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 8bb8db7..46d8657 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,162 +1,164 @@ /* * 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/iresourcerepository.h" -#include "core/ieditablerepository.h" -#include "core/ilanguage.h" -#include "core/language.h" #include "core/contributorrepository.h" #include "core/drawertrainingactions.h" -#include "core/trainingaction.h" #include "core/editorsession.h" +#include "core/ieditablerepository.h" +#include "core/ilanguage.h" +#include "core/iresourcerepository.h" +#include "core/isessionactions.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/trainingsession.h" -#include "core/unit.h" #include "core/resources/editablecourseresource.h" #include "core/resources/skeletonresource.h" +#include "core/trainingaction.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(); } IResourceRepository * Application::resourceRepository() const { return m_resourceRepository; } IEditableRepository * Application::editableRepository() const { return qobject_cast(m_resourceRepository); } void Application::installResourceRepository(IResourceRepository *resourceRepository) { m_resourceRepository = resourceRepository; } 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, "ContributorRepository", QStringLiteral("ContributorRepository 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, "EditableCourseResource", QStringLiteral("EditableCourseResource objects are backend objects")); qmlRegisterUncreatableType( "artikulate", 1, 0, "SkeletonResource", QStringLiteral("SkeletonResource objects are backend objects")); qmlRegisterUncreatableType( "artikulate", 1, 0, "Phrase", QStringLiteral("Phrase objects are backend objects")); qmlRegisterUncreatableType( "artikulate", 1, 0, "Unit", QStringLiteral("Unit objects are backend objects")); // interfaces - qmlRegisterInterface("IResourceRepository"); - qmlRegisterInterface("IEditableRepository"); qmlRegisterInterface("ICourse"); qmlRegisterInterface("IEditableCourse"); + qmlRegisterInterface("IEditablePhrase"); qmlRegisterInterface("ILanguage"); qmlRegisterInterface("IPhrase"); - qmlRegisterInterface("IEditablePhrase"); + qmlRegisterInterface("IEditableRepository"); + qmlRegisterInterface("IResourceRepository"); + qmlRegisterInterface("ISessionActions"); qmlRegisterInterface("IUnit"); // concrete instantiable types + qmlRegisterType("artikulate", 1, 0, "DrawerTrainingActions"); + qmlRegisterType("artikulate", 1, 0, "Icon"); + qmlRegisterType("artikulate", 1, 0, "Language"); qmlRegisterType("artikulate", 1, 0, "Learner"); qmlRegisterType("artikulate", 1, 0, "LearningGoal"); - qmlRegisterType("artikulate", 1, 0, "Language"); 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"); // models - qmlRegisterType("artikulate", 1, 0, "CourseModel"); qmlRegisterType("artikulate", 1, 0, "CourseFilterModel"); + qmlRegisterType("artikulate", 1, 0, "CourseModel"); 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, "LearningGoalModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeGroupModel"); + qmlRegisterType("artikulate", 1, 0, "PhonemeModel"); qmlRegisterType("artikulate", 1, 0, "PhonemeUnitModel"); + qmlRegisterType("artikulate", 1, 0, "PhraseFilterModel"); + qmlRegisterType("artikulate", 1, 0, "PhraseListModel"); + qmlRegisterType("artikulate", 1, 0, "PhraseModel"); qmlRegisterType("artikulate", 1, 0, "ProfileModel"); qmlRegisterType("artikulate", 1, 0, "SkeletonModel"); - qmlRegisterType("artikulate", 1, 0, "LearningGoalModel"); + qmlRegisterType("artikulate", 1, 0, "UnitFilterModel"); + qmlRegisterType("artikulate", 1, 0, "UnitModel"); +// qmlRegisterType("artikulate", 1, 0, "LearningProgressModel");//TODO must be ported to new trainingsession } diff --git a/src/core/drawertrainingactions.cpp b/src/core/drawertrainingactions.cpp index f9a9b97..a906aba 100644 --- a/src/core/drawertrainingactions.cpp +++ b/src/core/drawertrainingactions.cpp @@ -1,70 +1,70 @@ /* * Copyright 2018-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 "drawertrainingactions.h" #include "trainingaction.h" #include #include #include DrawerTrainingActions::DrawerTrainingActions(QObject* parent) : QObject{parent} , m_defaultAction{new TrainingAction(i18n("Please select a course"), this)} { } -void DrawerTrainingActions::setSession(TrainingSession *session) +void DrawerTrainingActions::setSession(ISessionActions *session) { if (session == m_session) { return; } if (m_session) { disconnect(m_session, &TrainingSession::courseChanged, this, &DrawerTrainingActions::actionsChanged); disconnect(m_session, &TrainingSession::phraseChanged, this, &DrawerTrainingActions::triggerTrainingView); } m_session = session; connect(m_session, &TrainingSession::courseChanged, this, &DrawerTrainingActions::actionsChanged); connect(m_session, &TrainingSession::phraseChanged, this, &DrawerTrainingActions::triggerTrainingView); emit sessionChanged(); emit actionsChanged(); } -TrainingSession * DrawerTrainingActions::session() const +ISessionActions * DrawerTrainingActions::session() const { return m_session; } QList DrawerTrainingActions::actions() const { if (!m_session || m_session->trainingActions().isEmpty()) { QList list; list << qobject_cast(m_defaultAction); return list; } QList actions; const auto trainingActions = m_session->trainingActions(); for (const auto &action : qAsConst(trainingActions)) { actions.append(qobject_cast(action)); } return actions; } diff --git a/src/core/drawertrainingactions.h b/src/core/drawertrainingactions.h index 8323ebe..c67337b 100644 --- a/src/core/drawertrainingactions.h +++ b/src/core/drawertrainingactions.h @@ -1,55 +1,55 @@ /* * Copyright 2018-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 DRAWERTRAININGACTIONS_H #define DRAWERTRAININGACTIONS_H #include "artikulatecore_export.h" -#include "trainingaction.h" +#include "isessionactions.h" #include class Course; class ARTIKULATECORE_EXPORT DrawerTrainingActions : public QObject { Q_OBJECT - Q_PROPERTY(TrainingSession *session READ session WRITE setSession NOTIFY sessionChanged) + Q_PROPERTY(ISessionActions *session READ session WRITE setSession NOTIFY sessionChanged) Q_PROPERTY(QList actions READ actions NOTIFY actionsChanged) public: DrawerTrainingActions(QObject *parent = nullptr); - void setSession(TrainingSession *session); - TrainingSession * session() const; + void setSession(ISessionActions *session); + ISessionActions * session() const; QList actions() const; Q_SIGNALS: void actionsChanged(); void sessionChanged(); /** * Notify that training view shall be displayed. */ void triggerTrainingView(); private: - TrainingSession *m_session{nullptr}; + ISessionActions *m_session{nullptr}; TrainingAction *m_defaultAction{nullptr}; }; #endif diff --git a/src/core/isessionactions.h b/src/core/isessionactions.h new file mode 100644 index 0000000..7797961 --- /dev/null +++ b/src/core/isessionactions.h @@ -0,0 +1,66 @@ +/* + * 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 ISESSIONACTIONS_H +#define ISESSIONACTIONS_H + +#include "artikulatecore_export.h" +#include +#include + +class ICourse; +class IPhrase; +class TrainingAction; + +/** + * \class ISessionActions + * + * Interface for both training and editor sessions that exposes simple iterator functionalities for a selecte course. + * The interface provides all properties that are needed to create a navigatible menu. + */ +class ARTIKULATECORE_EXPORT ISessionActions : public QObject +{ + Q_OBJECT +public: + ISessionActions(QObject *parent) + : QObject(parent) + { + } + virtual ~ISessionActions() = default; + virtual ICourse * course() const = 0; + virtual TrainingAction * activeAction() const = 0; + virtual IPhrase * activePhrase() const = 0; + virtual void setPhrase(IPhrase *phrase) = 0; + /** + * \brief Return tree of training actions + * + * The return actions form a 2-level hierarchy: + * - the first level are all units + * - the unit actions may contain sub-actions, which are the phrases + */ + virtual QVector trainingActions() const = 0; + +Q_SIGNALS: + void courseChanged(); + void phraseChanged(); +}; +Q_DECLARE_INTERFACE(ISessionActions, "ISessionActions") + +#endif diff --git a/src/core/trainingsession.cpp b/src/core/trainingsession.cpp index e780ece..fe87273 100644 --- a/src/core/trainingsession.cpp +++ b/src/core/trainingsession.cpp @@ -1,310 +1,310 @@ /* * 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/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(LearnerProfile::ProfileManager *manager, QObject *parent) - : QObject(parent) + : ISessionActions(parent) , m_profileManager(manager) , m_course(nullptr) { Q_ASSERT(m_profileManager != nullptr); } ICourse * TrainingSession::course() const { return m_course; } void TrainingSession::setCourse(ICourse *course) { if (!course) { updateTrainingActions(); return; } if (m_course == course) { return; } m_course = course; if (m_course && m_course->units().count() > 0) { setUnit(m_course->units().constFirst().get()); } // 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() ); const auto unitList = m_course->units(); for (auto unit : qAsConst(unitList)) { const auto phrases = unit->phrases(); for (auto &phrase : phrases) { auto iter = data.find(phrase->id()); if (iter != data.end()) { // phrase->setProgress(iter.value()); //FIXME add a decorator? } } } updateTrainingActions(); emit courseChanged(); } IUnit * TrainingSession::activeUnit() const { if (auto phrase = activePhrase()) { return phrase->unit().get(); } return nullptr; } void TrainingSession::setUnit(IUnit *unit) { // checking phrases in increasing order ensures that always the first phrase is selected 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 (unit == testPhrase->unit().get()) { if (auto action = activeAction()) { action->setChecked(false); } m_indexUnit = i; m_indexPhrase = j; if (auto action = activeAction()) { action->setChecked(true); } emit phraseChanged(); return; } } } } 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)); } IPhrase * TrainingSession::activePhrase() const { if (const auto action = activeAction()) { return action->phrase(); } return nullptr; } void TrainingSession::setPhrase(IPhrase *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); //FIXME // store training activity LearnerProfile::LearningGoal * goal = m_profileManager->goal( LearnerProfile::LearningGoal::Language, m_course->language()->id()); // m_profileManager->recordProgress(m_profileManager->activeProfile(), //FIXME // 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); //FIXME // 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() // ); // FIXME 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) { qDebug() << "switching to next unit"; 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::hasPrevious() const { return m_indexUnit > 0 || m_indexPhrase > 0; } bool TrainingSession::hasNext() const { if (m_indexUnit < m_actions.count() - 1) { return true; } if (m_actions.constLast()) { if (m_indexPhrase < m_actions.constLast()->actions().count() - 1) { return true; } } return false; } 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() const { return m_actions; } void TrainingSession::updateTrainingActions() { for (const auto &action : qAsConst(m_actions)) { action->deleteLater(); } m_actions.clear(); if (!m_course) { m_indexUnit = -1; m_indexPhrase = -1; return; } const auto unitList = m_course->units(); for (const auto &unit : qAsConst(unitList)) { auto action = new TrainingAction(unit->title(), this); const auto phraseList = unit->phrases(); for (const auto &phrase : qAsConst(phraseList)) { if (phrase->sound().isEmpty()) { continue; } action->appendChild(new TrainingAction(phrase, this, unit.get())); } if (action->hasChildren()) { m_actions.append(action); } else { action->deleteLater(); } } // update indices m_indexUnit = -1; m_indexPhrase = -1; if (m_course->units().count() > 0) { m_indexUnit = 0; if (m_course->units().constFirst()->phrases().count() > 0) { m_indexPhrase = 0; } } } diff --git a/src/core/trainingsession.h b/src/core/trainingsession.h index 8e32fb3..a00b9e2 100644 --- a/src/core/trainingsession.h +++ b/src/core/trainingsession.h @@ -1,94 +1,93 @@ /* * 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 "isessionactions.h" #include "phrase.h" #include class Language; class ICourse; class Unit; class TrainingAction; namespace LearnerProfile { class ProfileManager; } /** * \class TrainingSession */ -class ARTIKULATECORE_EXPORT TrainingSession : public QObject +class ARTIKULATECORE_EXPORT TrainingSession : public ISessionActions { Q_OBJECT Q_PROPERTY(ICourse *course READ course WRITE setCourse NOTIFY courseChanged) Q_PROPERTY(IUnit *unit READ activeUnit WRITE setUnit NOTIFY phraseChanged) Q_PROPERTY(IPhrase *phrase READ activePhrase WRITE setPhrase NOTIFY phraseChanged) Q_PROPERTY(bool hasNext READ hasNext NOTIFY phraseChanged) public: explicit TrainingSession(LearnerProfile::ProfileManager *manager, QObject *parent = nullptr); - ICourse * course() const; + ICourse * course() const override; void setCourse(ICourse *course); IUnit * activeUnit() const; void setUnit(IUnit *unit); - TrainingAction * activeAction() const; - IPhrase * activePhrase() const; - void setPhrase(IPhrase *phrase); + TrainingAction * activeAction() const override; + IPhrase * activePhrase() const override; + void setPhrase(IPhrase *phrase) override; bool hasPrevious() const; bool hasNext() const; Q_INVOKABLE void accept(); Q_INVOKABLE void skip(); /** * @brief Return tree of training actions * * The return actions form a 2-level hierarchy: * - the first level are all units * - the unit actions may contain sub-actions, which are the phrases * * @note phrases without sound file paths are skipped when generating actions */ - QVector trainingActions() const; + QVector trainingActions() const override; Q_SIGNALS: - void courseChanged(); - void phraseChanged(); /** * @brief Emitted when last phrase of session is skipped or marked as completed. */ void completed(); private: Q_DISABLE_COPY(TrainingSession) void updateTrainingActions(); void selectNextPhrase(); void updateGoal(); LearnerProfile::ProfileManager *m_profileManager; ICourse *m_course; QVector m_actions; int m_indexUnit{-1}; int m_indexPhrase{-1}; }; #endif