diff --git a/autotests/unittests/editorsession/test_editorsession.cpp b/autotests/unittests/editorsession/test_editorsession.cpp index d2ef460..b278909 100644 --- a/autotests/unittests/editorsession/test_editorsession.cpp +++ b/autotests/unittests/editorsession/test_editorsession.cpp @@ -1,231 +1,231 @@ /* * 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 "test_editorsession.h" #include "editablerepositorystub.h" #include "src/core/editorsession.h" #include "src/core/icourse.h" #include "src/core/ieditablecourse.h" #include "src/core/ieditablerepository.h" #include "src/core/language.h" #include "src/core/resources/skeletonresource.h" #include "src/core/unit.h" #include "../mocks/editablecoursestub.h" #include "../mocks/languagestub.h" #include #include void TestEditorSession::init() { // no initialization of test case } void TestEditorSession::cleanup() { // no cleanup after test run } void TestEditorSession::createEditorSession() { auto languageGerman = std::make_shared("de"); auto languageEnglish = std::make_shared("en"); std::shared_ptr course = EditableCourseStub::create(languageGerman, QVector>()); course->setLanguage(languageGerman); auto skeleton = SkeletonResource::create(QUrl(), nullptr); EditableRepositoryStub repository{ {languageGerman, languageEnglish}, // languages {skeleton}, // skeletons {course} // courses }; EditorSession session; session.setRepository(&repository); QVERIFY(session.course() == nullptr); QVERIFY(session.language() == nullptr); QVERIFY(session.skeleton() == nullptr); } void TestEditorSession::nonSkeletonSwitchingBehavior() { auto languageGerman = std::make_shared("de"); auto languageEnglish = std::make_shared("en"); std::shared_ptr courseGerman = EditableCourseStub::create(languageGerman, QVector>()); courseGerman->setId("course-german"); std::shared_ptr courseEnglish = EditableCourseStub::create(languageEnglish, QVector>()); courseEnglish->setId("course-english"); EditableRepositoryStub repository{ {languageGerman, languageEnglish}, // languages {}, // skeletons {courseGerman, courseEnglish} // courses }; EditorSession session; session.setRepository(&repository); QVERIFY(session.course() == nullptr); session.setCourse(courseGerman.get()); QCOMPARE(session.course()->id(), courseGerman->id()); QVERIFY(session.language() != nullptr); QCOMPARE(session.language()->id(), languageGerman->id()); QVERIFY(session.language() != nullptr); QCOMPARE(session.language()->id(), languageGerman->id()); session.setCourse(courseEnglish.get()); QVERIFY(session.course() != nullptr); QCOMPARE(session.course()->id(), courseEnglish->id()); QVERIFY(session.language() != nullptr); QCOMPARE(session.language()->id(), languageEnglish->id()); } void TestEditorSession::skeletonSwitchingBehavior() { auto languageGerman = std::make_shared("de"); auto languageEnglish = std::make_shared("en"); std::shared_ptr courseGermanA = EditableCourseStub::create(languageGerman, QVector>()); courseGermanA->setId("course-german"); courseGermanA->setForeignId("testskeletonA"); std::shared_ptr courseGermanB = EditableCourseStub::create(languageGerman, QVector>()); courseGermanB->setId("course-german"); courseGermanB->setForeignId("testskeletonB"); std::shared_ptr courseEnglishA = EditableCourseStub::create(languageEnglish, QVector>()); courseEnglishA->setId("course-english"); courseEnglishA->setForeignId("testskeletonA"); auto skeletonA = SkeletonResource::create(QUrl(), nullptr); skeletonA->setId("testskeletonA"); auto skeletonB = SkeletonResource::create(QUrl(), nullptr); skeletonB->setId("testskeletonB"); EditableRepositoryStub repository{ {languageGerman, languageEnglish}, // languages {skeletonA, skeletonB}, // skeletons {courseGermanA, courseEnglishA, courseGermanB} // courses }; EditorSession session; session.setRepository(&repository); session.setSkeleton(skeletonA.get()); Q_ASSERT(session.skeleton() != nullptr); QCOMPARE(session.skeleton()->id(), skeletonA->id()); Q_ASSERT(session.course() != nullptr); QCOMPARE(session.course()->id(), courseGermanA->id()); session.setCourse(courseEnglishA.get()); Q_ASSERT(session.course() != nullptr); QCOMPARE(session.course()->id(), courseEnglishA->id()); session.setCourse(courseGermanB.get()); QVERIFY(session.skeleton() != nullptr); QCOMPARE(session.skeleton()->id(), skeletonB->id()); QVERIFY(session.course() != nullptr); QCOMPARE(session.course()->id(), courseGermanB->id()); QVERIFY(session.language() != nullptr); QCOMPARE(session.language()->id(), languageGerman->id()); } void TestEditorSession::iterateCourse() { // language auto language = std::make_shared("de"); // course auto unitA = Unit::create(); auto unitB = Unit::create(); std::shared_ptr phraseA1 = Phrase::create(); std::shared_ptr phraseA2 = Phrase::create(); std::shared_ptr phraseB1 = Phrase::create(); std::shared_ptr phraseB2 = Phrase::create(); // note: phrases without soundfiles are skipped in session generation phraseA1->setId("A1"); phraseA2->setId("A2"); phraseB1->setId("B1"); phraseB2->setId("B2"); phraseA1->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); phraseA2->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); phraseB1->setSound(QUrl::fromLocalFile("/tmp/b1.ogg")); phraseB2->setSound(QUrl::fromLocalFile("/tmp/b2.ogg")); unitA->addPhrase(phraseA1); unitA->addPhrase(phraseA2); unitB->addPhrase(phraseB1); unitB->addPhrase(phraseB2); auto course = EditableCourseStub::create(language, QVector>({unitA, unitB})); EditableRepositoryStub repository{ {language}, // languages {}, // skeletons {course} // courses }; EditorSession session; session.setRepository(&repository); session.setCourse(course.get()); // session assumed to initialize with first units's first phrase QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase()->self(), phraseA1); QVERIFY(course.get() == session.course()); // test direct unit setters - session.setUnit(unitA.get()); + session.setActiveUnit(unitA.get()); QCOMPARE(session.activeUnit()->self(), unitA); - session.setUnit(unitB.get()); + session.setActiveUnit(unitB.get()); QCOMPARE(session.activeUnit()->self(), unitB); // test direct phrase setters session.setActivePhrase(phraseA1.get()); QCOMPARE(session.activePhrase()->self(), phraseA1); QCOMPARE(session.activeUnit()->self(), unitA); session.setActivePhrase(phraseB1.get()); QCOMPARE(session.activePhrase()->self(), phraseB1); QCOMPARE(session.activeUnit()->self(), unitB); // test phrase forward iterators session.setActivePhrase(phraseA1.get()); QCOMPARE(session.activeUnit()->self(), unitA); QCOMPARE(session.activePhrase()->id(), phraseA1->id()); QVERIFY(session.hasNextPhrase()); session.switchToNextPhrase(); QCOMPARE(session.activeUnit()->self(), unitA); QCOMPARE(session.activePhrase()->id(), phraseA2->id()); session.switchToNextPhrase(); QCOMPARE(session.activePhrase()->self(), phraseB1); session.switchToNextPhrase(); QCOMPARE(session.activePhrase()->self(), phraseB2); QVERIFY(!session.hasNextPhrase()); // at the end, do not iterate further session.switchToNextPhrase(); QCOMPARE(session.activePhrase()->self(), phraseB2); // test phrase backward iterators QVERIFY(session.hasPreviousPhrase()); session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->self(), phraseB1); session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->id(), phraseA2->id()); session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->id(), phraseA1->id()); QVERIFY(!session.hasPreviousPhrase()); // at the end, do not iterate further session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->id(), phraseA1->id()); } QTEST_GUILESS_MAIN(TestEditorSession) diff --git a/src/core/editorsession.cpp b/src/core/editorsession.cpp index 9d5abb8..bb399b2 100644 --- a/src/core/editorsession.cpp +++ b/src/core/editorsession.cpp @@ -1,391 +1,357 @@ /* * 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 "editorsession.h" #include "core/language.h" #include "core/resources/editablecourseresource.h" #include "core/resources/skeletonresource.h" #include "core/unit.h" #include "core/iunit.h" #include "core/phrase.h" #include "core/trainingaction.h" #include "core/contributorrepository.h" #include "artikulate_debug.h" EditorSession::EditorSession(QObject *parent) : ISessionActions(parent) { connect(this, &EditorSession::skeletonChanged, this, &EditorSession::displayedCourseChanged); connect(this, &EditorSession::courseChanged, this, &EditorSession::displayedCourseChanged); connect(this, &EditorSession::editSkeletonChanged, this, &EditorSession::displayedCourseChanged); connect(this, &EditorSession::displayedCourseChanged, this, &EditorSession::updateDisplayedUnit); connect(this, &EditorSession::courseChanged, this, &EditorSession::skeletonModeChanged); } void EditorSession::setRepository(IEditableRepository *repository) { m_repository = repository; } bool EditorSession::skeletonMode() const { return m_skeleton != nullptr; } void EditorSession::setEditSkeleton(bool enabled) { if (m_editSkeleton == enabled) { return; } m_editSkeleton = enabled; emit editSkeletonChanged(); } bool EditorSession::isEditSkeleton() const { return m_editSkeleton; } IEditableCourse * EditorSession::skeleton() const { return m_skeleton; } void EditorSession::setSkeleton(IEditableCourse *skeleton) { if (m_skeleton == skeleton) { return; } m_skeleton = skeleton; IEditableCourse *newCourse{ nullptr }; if (m_skeleton && m_repository) { for (const auto &course : m_repository->editableCourses()) { if (course->foreignId() == m_skeleton->id()) { newCourse = course.get(); break; } } } setCourse(newCourse); emit skeletonChanged(); } ILanguage * EditorSession::language() const { return m_language; } IEditableCourse *EditorSession::course() const { return m_course; } void EditorSession::setCourse(IEditableCourse *course) { if (m_course == course) { return; } m_course = course; if (m_course != nullptr) { // update skeleton IEditableCourse * newSkeleton{ nullptr }; if (m_skeleton == nullptr || m_skeleton->id() != course->foreignId()) { for (const auto &skeleton : m_repository->skeletons()) { if (skeleton->id() == course->foreignId()) { newSkeleton = skeleton.get(); break; } } m_skeleton = newSkeleton; emit skeletonChanged(); } // update language m_language = m_course->language().get(); } else { m_language = nullptr; } updateTrainingActions(); emit languageChanged(); emit courseChanged(); } void EditorSession::setCourseByLanguage(ILanguage *language) { if (!skeletonMode()) { qDebug() << "Course selection by language is only available in skeleton mode"; return; } if (language == nullptr || m_repository == nullptr) { return; } IEditableCourse *newCourse{ nullptr }; QString languageId; if (language) { languageId = language->id(); } for (auto course : m_repository->editableCourses()) { if (course->foreignId() == m_skeleton->id() && course->language()->id() == language->id()) { newCourse = course.get(); break; } } setCourse(newCourse); } IEditableCourse * EditorSession::displayedCourse() const { IEditableCourse * course{ nullptr }; if (m_editSkeleton) { course = m_skeleton; } else { course = m_course; } return course; } void EditorSession::updateDisplayedUnit() { auto course = displayedCourse(); if (course != nullptr) { auto units = course->units(); if (!units.isEmpty()) { - setUnit(units.constFirst().get()); + setActiveUnit(units.constFirst().get()); return; } } } -IUnit * EditorSession::unit() const -{ - return m_unit.get(); -} - IUnit * EditorSession::activeUnit() const { - return m_unit.get(); + if (auto phrase = activePhrase()) { + return phrase->unit().get(); + } + return nullptr; } -void EditorSession::setUnit(IUnit *unit) +void EditorSession::setActiveUnit(IUnit *unit) { - if (unit != m_unit.get()) { - if (!unit) { - m_unit.reset(); - } else { - m_unit = unit->self(); - } - if (m_unit && !m_unit->phrases().isEmpty()) { - setActivePhrase(m_unit->phrases().first().get()); - } else { - setActivePhrase(nullptr); + // 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; + } } - emit unitChanged(); } } void EditorSession::setActivePhrase(IPhrase * phrase) { - if (phrase && m_phrase == phrase->self()) { - return; - } - if (phrase == nullptr) { - m_phrase = nullptr; - } - else { - setUnit(phrase->unit().get()); - m_phrase = phrase->self(); - } - emit phraseChanged(); -} - -IPhrase * EditorSession::activePhrase() const -{ - return m_phrase.get(); -} - -std::shared_ptr EditorSession::previousPhrase() const -{ - if (!m_phrase) { - return nullptr; - } - const int index = m_phrase->unit()->phrases().indexOf(m_phrase); - if (index > 0) { - return m_phrase->unit()->phrases().at(index - 1); - } else { - auto unit = m_phrase->unit(); - int uIndex{ -1 }; - for (int i = 0; i < unit->course()->units().size(); ++i) { - auto testUnit = unit->course()->units().at(i); - if (testUnit.get() == unit.get()) { - uIndex = i; - break; + 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; } } - if (uIndex > 0) { - return unit->course()->units().at(uIndex - 1)->phrases().last(); - } } - return m_phrase; } -std::shared_ptr EditorSession::nextPhrase() const +IPhrase * EditorSession::activePhrase() const { - if (!m_phrase) { - return nullptr; + if (const auto action = activeAction()) { + return action->phrase(); } - const int index = m_phrase->unit()->phrases().indexOf(m_phrase); - if (index < m_phrase->unit()->phrases().length() - 1) { - return m_phrase->unit()->phrases().at(index + 1); - } else { - auto unit = m_phrase->unit(); - int uIndex{ -1 }; - for (int i = 0; i < unit->course()->units().size(); ++i) { - auto testUnit = unit->course()->units().at(i); - if (testUnit.get() == unit.get()) { - uIndex = i; - break; - } - } - if (uIndex < unit->course()->units().length() - 1) { - auto nextUnit = unit->course()->units().at(uIndex + 1); - if (nextUnit->phrases().isEmpty()) { - return nullptr; - } - return nextUnit->phrases().constFirst(); - } - } - return m_phrase; + return nullptr; } void EditorSession::switchToPreviousPhrase() { if (hasPreviousPhrase()) { - setActivePhrase(previousPhrase().get()); + if (m_indexPhrase == 0) { + qCDebug(ARTIKULATE_CORE()) << "switching to previous unit"; + if (m_indexUnit > 0) { + --m_indexUnit; + m_indexPhrase = m_actions.at(m_indexUnit)->actions().count() - 1; + } + } else { + --m_indexPhrase; + } + if (auto action = activeAction()) { + action->setChecked(true); + } + emit phraseChanged(); + } else { + qCWarning(ARTIKULATE_CORE()) << "The is no previous phrase, aborting"; } } void EditorSession::switchToNextPhrase() { if (hasNextPhrase()) { - setActivePhrase(nextPhrase().get()); + if (m_indexPhrase >= m_actions.at(m_indexUnit)->actions().count() - 1) { + qCDebug(ARTIKULATE_CORE()) << "switching to next unit"; + if (m_indexUnit < m_actions.count() - 1) { + ++m_indexUnit; + m_indexPhrase = 0; + } + } else { + ++m_indexPhrase; + } + if (auto action = activeAction()) { + action->setChecked(true); + } + emit phraseChanged(); + } else { + qCWarning(ARTIKULATE_CORE()) << "The is no next phrase, aborting"; } } bool EditorSession::hasPreviousPhrase() const { - if (!m_unit || !m_phrase) { - return false; - } - Q_ASSERT(m_unit->course() != nullptr); - - const int phraseIndex = m_phrase->unit()->phrases().indexOf(m_phrase); - int unitIndex = -1; - for (int i = 0; i < m_unit->course()->units().size(); ++i) { - if (m_unit->course()->units().at(i) == m_unit) { - unitIndex = i; - break; - } - } - if (unitIndex > 0 || phraseIndex > 0) { - return true; - } - return false; + return m_indexUnit > 0 || m_indexPhrase > 0; } bool EditorSession::hasNextPhrase() const { - if (!m_unit || !m_phrase) { - return false; + if (m_indexUnit < m_actions.count() - 1) { + return true; } - Q_ASSERT(m_unit->course() != nullptr); - - const int phraseIndex = m_phrase->unit()->phrases().indexOf(m_phrase); - int unitIndex = -1; - for (int i = 0; i < m_unit->course()->units().size(); ++i) { - if (m_unit->course()->units().at(i) == m_unit) { - unitIndex = i; - break; + if (m_actions.constLast()) { + if (m_indexPhrase < m_actions.constLast()->actions().count() - 1) { + return true; } } - if ((unitIndex >= 0 && unitIndex < m_course->units().size() - 1) - || (phraseIndex >= 0 && phraseIndex < m_unit->phrases().size() - 1)) { - return true; - } return false; } void EditorSession::updateCourseFromSkeleton() { if (!m_course) { qCritical() << "Not updating course from skeleton, no one set."; return; } m_repository->updateCourseFromSkeleton(m_course->self()); } TrainingAction * EditorSession::activeAction() const { - if (!m_phrase) { + if (m_indexUnit < 0 || m_indexPhrase < 0) { return nullptr; } - return nullptr; -// return qobject_cast(m_actions.at(m_indexUnit)->actions().at(m_indexPhrase)); + return qobject_cast(m_actions.at(m_indexUnit)->actions().at(m_indexPhrase)); } void EditorSession::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; -// } -// } + 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; + } + } } QVector EditorSession::trainingActions() const { return m_actions; } diff --git a/src/core/editorsession.h b/src/core/editorsession.h index 3cd6304..441d05b 100644 --- a/src/core/editorsession.h +++ b/src/core/editorsession.h @@ -1,137 +1,130 @@ /* * 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 EDITORSESSION_H #define EDITORSESSION_H #include "artikulatecore_export.h" #include "phrase.h" #include "isessionactions.h" class ILanguage; class IEditableCourse; class Unit; class IPhrase; class SkeletonResource; class IEditableRepository; /** * \class EditorSession * * An object of this class is used to set the current state of the editor. By this, we put all logic * how language, skeleton and course fit to each other into this class. The main concept is that * we have to fundamentally different workflows that both are modeled in this class: * * 1. Skeleton based workflow * - a skeleton is selected * - every language is available, since eventually the course should be available in every language * - for every language, there is at most one course (there is none only in case it is not created yet) * - adding new units or phrases is only possible in the skeleton course * - every course can update/sync with the skeleton * * 2. Course based workflow * - there is no skeleton from which the course is derived * - the base is a language that is selected first * - for a language there can be none to arbitrarily many courses * * The main switch is \c EditorSession::setSkeletonMode(bool) */ class ARTIKULATECORE_EXPORT EditorSession : public ISessionActions { Q_OBJECT Q_INTERFACES(ISessionActions) Q_PROPERTY(bool skeletonMode READ skeletonMode NOTIFY skeletonModeChanged) Q_PROPERTY(bool editSkeleton READ isEditSkeleton WRITE setEditSkeleton NOTIFY editSkeletonChanged) Q_PROPERTY(IEditableCourse *skeleton READ skeleton WRITE setSkeleton NOTIFY skeletonChanged) Q_PROPERTY(IEditableCourse *course READ course WRITE setCourse NOTIFY courseChanged) // editor elements depending on currently selected mode, skeleton and course /** * @brief the displayed course (skeleton or course) depending on the user selection */ Q_PROPERTY(IEditableCourse *displayedCourse READ displayedCourse NOTIFY displayedCourseChanged) - Q_PROPERTY(ILanguage *language READ language NOTIFY languageChanged) - Q_PROPERTY(IUnit *unit READ unit WRITE setUnit NOTIFY unitChanged) Q_PROPERTY(IPhrase *phrase READ activePhrase WRITE setActivePhrase NOTIFY phraseChanged) Q_PROPERTY(bool hasNextPhrase READ hasNextPhrase NOTIFY phraseChanged) Q_PROPERTY(bool hasPreviousPhrase READ hasPreviousPhrase NOTIFY phraseChanged) public: explicit EditorSession(QObject *parent = nullptr); void setRepository(IEditableRepository *repository); bool skeletonMode() const; void setEditSkeleton(bool enabled=true); bool isEditSkeleton() const; IEditableCourse * skeleton() const; void setSkeleton(IEditableCourse *skeleton); ILanguage * language() const; IEditableCourse * course() const; void setCourse(IEditableCourse *course); /** * @brief Open course resource by specifying the language * @param language the target language */ Q_INVOKABLE void setCourseByLanguage(ILanguage *language); IEditableCourse * displayedCourse() const; - Q_DECL_DEPRECATED IUnit * unit() const; IUnit * activeUnit() const; - void setUnit(IUnit *unit); + void setActiveUnit(IUnit *unit); IPhrase * activePhrase() const; void setActivePhrase(IPhrase *phrase) override; IPhrase::Type phraseType() const; void setPhraseType(IPhrase::Type type); bool hasPreviousPhrase() const; bool hasNextPhrase() const; Q_INVOKABLE void switchToPreviousPhrase(); Q_INVOKABLE void switchToNextPhrase(); Q_INVOKABLE void updateCourseFromSkeleton(); TrainingAction * activeAction() const override; QVector trainingActions() const override; -private: - std::shared_ptr nextPhrase() const; - std::shared_ptr previousPhrase() const; - private Q_SLOTS: void updateDisplayedUnit(); Q_SIGNALS: void editSkeletonChanged(); void skeletonModeChanged(); void skeletonChanged(); void languageChanged(); void displayedCourseChanged(); void unitChanged(); private: Q_DISABLE_COPY(EditorSession) void updateTrainingActions(); IEditableRepository * m_repository{ nullptr }; bool m_editSkeleton{ false }; IEditableCourse *m_skeleton{ nullptr }; ILanguage *m_language{ nullptr }; IEditableCourse *m_course{ nullptr }; - std::shared_ptr m_unit{ nullptr }; - std::shared_ptr m_phrase; QVector m_actions; + int m_indexUnit{-1}; + int m_indexPhrase{-1}; }; #endif