diff --git a/src/core/editorsession.cpp b/src/core/editorsession.cpp index 2e27a9c..810174f 100644 --- a/src/core/editorsession.cpp +++ b/src/core/editorsession.cpp @@ -1,299 +1,285 @@ /* * 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 "artikulate_debug.h" #include "core/contributorrepository.h" #include "core/iunit.h" #include "core/language.h" #include "core/phrase.h" #include "core/resources/editablecourseresource.h" #include "core/resources/skeletonresource.h" #include "core/trainingaction.h" #include "core/unit.h" EditorSession::EditorSession(QObject *parent) : ISessionActions(parent) { connect(this, &EditorSession::courseChanged, this, &EditorSession::skeletonModeChanged); } void EditorSession::setRepository(IEditableRepository *repository) { m_repository = repository; } bool EditorSession::skeletonMode() const { for (const auto &skeleton : m_repository->skeletons()) { if (skeleton->id() == m_course->id()) { return true; } } return false; } ILanguage *EditorSession::language() const { if (m_course && m_course->language()) { return m_course->language().get(); } return nullptr; } IEditableCourse *EditorSession::course() const { return m_course; } void EditorSession::setCourse(IEditableCourse *course) { if (m_course == course) { return; } m_course = course; connect(course, &IEditableCourse::unitChanged, this, [=](std::shared_ptr unit) { this->updateActions(unit); emit actionsChanged(); //TODO much too global effect }); updateTrainingActions(); if (m_course && m_course->units().count() > 0) { setActiveUnit(m_course->units().first().get()); } emit languageChanged(); emit courseChanged(); } IUnit *EditorSession::activeUnit() const { if (auto phrase = activePhrase()) { return phrase->unit().get(); } return nullptr; } void EditorSession::setActiveUnit(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; } } } } void EditorSession::setActivePhrase(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; } } } } IPhrase *EditorSession::activePhrase() const { if (const auto action = activeAction()) { return action->phrase(); } return nullptr; } void EditorSession::switchToPreviousPhrase() { if (hasPreviousPhrase()) { 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()) { 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 { return m_indexUnit > 0 || m_indexPhrase > 0; } bool EditorSession::hasNextPhrase() 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 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_indexUnit < 0 || m_indexPhrase < 0) { return nullptr; } return qobject_cast(m_actions.at(m_indexUnit)->actions().at(m_indexPhrase)); } void EditorSession::updateTrainingActions() { for (const auto &action : qAsConst(m_actions)) { action->clearActions(); 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)) { action->appendAction(new TrainingAction(phrase, this, unit.get())); } if (action->actions().count() > 0) { 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; } } emit actionsChanged(); } void EditorSession::updateActions(std::shared_ptr changedUnit) { int unitIndex = -1; for (int i = 0; i < m_course->units().size(); ++i) { if (changedUnit == m_course->units().at(i)) { unitIndex = i; break; } } Q_ASSERT(unitIndex >= 0); auto unitAction = m_actions.at(unitIndex); // TODO this is a heavy operation if only one phrase was changed - qDeleteAll(unitAction->actions()); - unitAction->actions().clear(); + unitAction->clearActions(); for (int i = 0; i < changedUnit->phrases().size(); ++i) { unitAction->appendAction(new TrainingAction(changedUnit->phrases().at(i), this, changedUnit.get())); } - qDebug() << "unit action changed" << unitAction << unitAction->text(); emit unitAction->actionsChanged(); - emit unitAction->changed(); - - // update indices - //TODO update indexes according to changed phrase action -// 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/resources/editablecourseresource.cpp b/src/core/resources/editablecourseresource.cpp index 5bc9b98..17dae98 100644 --- a/src/core/resources/editablecourseresource.cpp +++ b/src/core/resources/editablecourseresource.cpp @@ -1,344 +1,344 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "editablecourseresource.h" #include "artikulate_debug.h" #include "core/phoneme.h" #include "core/phrase.h" #include "core/unit.h" #include "courseparser.h" #include #include #include #include #include #include #include #include #include #include EditableCourseResource::EditableCourseResource(const QUrl &path, IResourceRepository *repository) : IEditableCourse() , m_course(new CourseResource(path, repository)) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); connect(m_course.get(), &ICourse::unitAboutToBeAdded, this, &ICourse::unitAboutToBeAdded); connect(m_course.get(), &ICourse::unitAdded, this, &ICourse::unitAdded); connect(m_course.get(), &CourseResource::idChanged, this, &EditableCourseResource::idChanged); connect(m_course.get(), &CourseResource::foreignIdChanged, this, &EditableCourseResource::foreignIdChanged); connect(m_course.get(), &CourseResource::titleChanged, this, &EditableCourseResource::titleChanged); connect(m_course.get(), &CourseResource::descriptionChanged, this, &EditableCourseResource::descriptionChanged); connect(m_course.get(), &CourseResource::languageChanged, this, &EditableCourseResource::languageChanged); for (auto &unit : m_course->units()) { connect(unit.get(), &Unit::phrasesChanged, this, &IEditableCourse::unitChanged); } } std::shared_ptr EditableCourseResource::create(const QUrl &path, IResourceRepository *repository) { std::shared_ptr course(new EditableCourseResource(path, repository)); course->setSelf(course); return course; } void EditableCourseResource::setSelf(std::shared_ptr self) { m_course->setSelf(self); } QString EditableCourseResource::id() const { return m_course->id(); } void EditableCourseResource::setId(QString id) { if (m_course->id() != id) { m_course->setId(id); m_modified = true; } } QString EditableCourseResource::foreignId() const { return m_course->foreignId(); } void EditableCourseResource::setForeignId(QString foreignId) { m_course->setForeignId(std::move(foreignId)); } QString EditableCourseResource::title() const { return m_course->title(); } void EditableCourseResource::setTitle(QString title) { if (m_course->title() != title) { m_course->setTitle(title); m_modified = true; } } QString EditableCourseResource::i18nTitle() const { return m_course->i18nTitle(); } void EditableCourseResource::setI18nTitle(QString i18nTitle) { if (m_course->i18nTitle() != i18nTitle) { m_course->setI18nTitle(i18nTitle); m_modified = true; } } QString EditableCourseResource::description() const { return m_course->description(); } void EditableCourseResource::setDescription(QString description) { if (m_course->description() != description) { m_course->setDescription(description); m_modified = true; } } std::shared_ptr EditableCourseResource::language() const { return m_course->language(); } QString EditableCourseResource::languageTitle() const { return m_course->languageTitle(); } void EditableCourseResource::setLanguage(std::shared_ptr language) { if (m_course->language() != language) { m_course->setLanguage(language); m_modified = true; } } QUrl EditableCourseResource::file() const { return m_course->file(); } std::shared_ptr EditableCourseResource::self() const { return std::static_pointer_cast(m_course->self()); } bool EditableCourseResource::sync() { Q_ASSERT(file().isValid()); Q_ASSERT(file().isLocalFile()); Q_ASSERT(!file().isEmpty()); // not writing back if not modified if (!m_modified) { qCDebug(ARTIKULATE_LOG()) << "Aborting sync, course was not modified."; return false; } bool ok = exportToFile(file()); if (ok) { m_modified = false; } return ok; } bool EditableCourseResource::exportToFile(const QUrl &filePath) const { // write back to file // create directories if necessary QFileInfo info(filePath.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); if (!info.exists()) { qCDebug(ARTIKULATE_LOG()) << "create xml output file directory, not existing"; QDir dir; dir.mkpath(filePath.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); } // TODO port to KSaveFile QFile file(filePath.toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { qCWarning(ARTIKULATE_LOG()) << "Unable to open file " << file.fileName() << " in write mode, aborting."; return false; } file.write(CourseParser::serializedDocument(self(), false).toByteArray()); return true; } std::shared_ptr EditableCourseResource::addUnit(std::shared_ptr unit) { m_modified = true; m_course->addUnit(unit); unit->setCourse(self()); connect(unit.get(), &Unit::phrasesChanged, this, &IEditableCourse::unitChanged); return unit; } QVector> EditableCourseResource::units() { if (!m_unitsLoaded) { for (auto &unit : m_course->units()) { unit->setCourse(self()); } m_unitsLoaded = true; } return m_course->units(); } void EditableCourseResource::updateFrom(std::shared_ptr skeleton) { for (auto skeletonUnit : skeleton->units()) { // find matching unit or create one std::shared_ptr matchingUnit; auto it = std::find_if(m_course->units().cbegin(), m_course->units().cend(), [skeletonUnit](std::shared_ptr compareUnit) { return compareUnit->foreignId() == skeletonUnit->id(); }); if (it == m_course->units().cend()) { // import complete unit auto importUnit = Unit::create(); importUnit->setId(skeletonUnit->id()); importUnit->setForeignId(skeletonUnit->id()); importUnit->setTitle(skeletonUnit->title()); matchingUnit = m_course->addUnit(std::move(importUnit)); } else { matchingUnit = *it; } // import phrases for (auto skeletonPhrase : skeletonUnit->phrases()) { auto it = std::find_if(matchingUnit->phrases().cbegin(), matchingUnit->phrases().cend(), [skeletonPhrase](std::shared_ptr comparePhrase) { return comparePhrase->foreignId() == skeletonPhrase->id(); }); if (it == matchingUnit->phrases().cend()) { // import complete Phrase std::shared_ptr importPhrase = Phrase::create(); importPhrase->setId(skeletonPhrase->id()); importPhrase->setForeignId(skeletonPhrase->id()); importPhrase->setText(skeletonPhrase->text()); importPhrase->seti18nText(skeletonPhrase->i18nText()); importPhrase->setType(skeletonPhrase->type()); importPhrase->setUnit(matchingUnit); matchingUnit->addPhrase(importPhrase, matchingUnit->phrases().size()); } } } qCInfo(ARTIKULATE_LOG()) << "Update performed!"; } bool EditableCourseResource::isModified() const { return m_modified; } Unit *EditableCourseResource::createUnit() { // find first unused id QStringList unitIds; for (auto unit : m_course->units()) { unitIds.append(unit->id()); } QString id = QUuid::createUuid().toString(); while (unitIds.contains(id)) { id = QUuid::createUuid().toString(); qCWarning(ARTIKULATE_LOG) << "Unit id generator has found a collision, recreating id."; } // create unit std::shared_ptr unit = Unit::create(); unit->setCourse(self()); unit->setId(id); unit->setTitle(i18n("New Unit")); auto sharedUnit = addUnit(std::move(unit)); return sharedUnit.get(); } bool EditableCourseResource::createPhraseAfter(IPhrase *previousPhrase) { std::shared_ptr parentUnit = units().last(); if (previousPhrase) { for (const auto &unit : units()) { if (previousPhrase->unit()->id() == unit->id()) { parentUnit = unit; break; } } } // find index int index = parentUnit->phrases().size(); for (int i = 0; i < parentUnit->phrases().size(); ++i) { if (parentUnit->phrases().at(i)->id() == previousPhrase->id()) { index = i; break; } } // find globally unique phrase id inside course QStringList phraseIds; for (auto unit : m_course->units()) { for (auto &phrase : unit->phrases()) { phraseIds.append(phrase->id()); } } QString id = QUuid::createUuid().toString(); while (phraseIds.contains(id)) { id = QUuid::createUuid().toString(); qCWarning(ARTIKULATE_LOG) << "Phrase id generator has found a collision, recreating id."; } // create unit std::shared_ptr phrase = Phrase::create(); phrase->setId(id); phrase->setText(QLatin1String("")); phrase->setType(IPhrase::Type::Word); - parentUnit->addPhrase(phrase, index); + parentUnit->addPhrase(phrase, index + 1); - qCDebug(ARTIKULATE_CORE()) << "Created phrase at index" << index; + qCDebug(ARTIKULATE_CORE()) << "Created phrase at index" << index + 1; return true; } bool EditableCourseResource::deletePhrase(IPhrase *phrase) { Q_ASSERT(phrase); if (!phrase) { return false; } auto unitId = phrase->unit()->id(); for (auto &unit : units()) { if (unit->id() == unitId) { unit->removePhrase(phrase->self()); return true; } } return false; } diff --git a/src/core/trainingaction.cpp b/src/core/trainingaction.cpp index 6902a46..006b816 100644 --- a/src/core/trainingaction.cpp +++ b/src/core/trainingaction.cpp @@ -1,188 +1,189 @@ /* * 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 "trainingaction.h" #include "drawertrainingactions.h" #include "trainingactionicon.h" #include "trainingsession.h" #include TrainingAction::TrainingAction(QObject *parent) : QAbstractListModel(parent) , m_text(QString()) , m_icon(nullptr, QString()) // TODO "rating-unrated" vs. "rating" { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } TrainingAction::TrainingAction(const QString &text, QObject *parent) : QAbstractListModel(parent) , m_text(text) , m_icon(nullptr, QString()) // TODO "rating-unrated" vs. "rating" { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } TrainingAction::TrainingAction(std::shared_ptr phrase, ISessionActions *session, QObject *parent) : QAbstractListModel(parent) , m_icon(nullptr, QString()) , m_phrase(phrase) , m_session(session) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); if (m_phrase) { m_text = phrase->text(); } } QHash TrainingAction::roleNames() const { QHash roles; roles[ModelDataRole] = "modelData"; return roles; } int TrainingAction::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return 1; } int TrainingAction::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return actionsCount(); } QVariant TrainingAction::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= actionsCount()) { return QVariant(); } switch (role) { case ModelDataRole: return QVariant::fromValue(m_actions.at(index.row())); case Qt::DisplayRole: return m_text; default: return QVariant(); } } void TrainingAction::trigger() { if (m_phrase && m_session) { m_session->setActivePhrase(m_phrase.get()); } } bool TrainingAction::enabled() const { return m_enabled; } void TrainingAction::setEnabled(bool enabled) { if (enabled == m_enabled) { return; } m_enabled = enabled; emit enabledChanged(m_enabled); } QString TrainingAction::text() const { return m_text; } void TrainingAction::setText(QString text) { if (text == m_text) { return; } m_text = std::move(text); emit textChanged(m_text); } bool TrainingAction::checked() const { return m_checked; } void TrainingAction::setChecked(bool checked) { if (checked == m_checked) { return; } m_checked = checked; emit checkedChanged(m_checked); } QObject * TrainingAction::icon() { return qobject_cast(&m_icon); } IPhrase * TrainingAction::phrase() const { return m_phrase.get(); } QVector TrainingAction::actions() const { return m_actions; } QAbstractListModel * TrainingAction::actionModel() { return this; } int TrainingAction::actionsCount() const { return m_actions.count(); } TrainingAction * TrainingAction::action(int index) const { if (index < 0 || index >= m_actions.count()) { qWarning() << "index not in range, aborting"; return nullptr; } return m_actions.at(index); } void TrainingAction::appendAction(TrainingAction *action) { beginInsertRows(QModelIndex(), m_actions.count(), m_actions.count()); m_actions.append(action); endInsertRows(); emit actionsChanged(); } void TrainingAction::clearActions() { beginResetModel(); m_actions.clear(); + qDeleteAll(m_actions); endResetModel(); emit actionsChanged(); } diff --git a/src/core/trainingaction.h b/src/core/trainingaction.h index 8be93fe..aeb619b 100644 --- a/src/core/trainingaction.h +++ b/src/core/trainingaction.h @@ -1,97 +1,96 @@ /* * 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 TRAININGACTION_H #define TRAININGACTION_H #include "artikulatecore_export.h" #include "iphrase.h" #include "trainingactionicon.h" #include "trainingsession.h" #include #include #include class DrawerTrainingActions; class ARTIKULATECORE_EXPORT TrainingAction : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QString title READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QObject *icon READ icon CONSTANT) Q_PROPERTY(bool visible MEMBER m_visible CONSTANT) Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool checked READ checked NOTIFY checkedChanged) Q_PROPERTY(QString tooltip MEMBER m_tooltip CONSTANT) Q_PROPERTY(QAbstractItemModel * children READ actionModel NOTIFY actionsChanged) Q_PROPERTY(bool checkable MEMBER m_checkable CONSTANT) Q_PROPERTY(int length READ actionsCount NOTIFY actionsChanged) public: enum ModelRoles { ModelDataRole = Qt::UserRole + 1 }; TrainingAction(QObject *parent = nullptr); TrainingAction(const QString &text, QObject *parent = nullptr); TrainingAction(std::shared_ptr phrase, ISessionActions *session, QObject *parent = nullptr); Q_INVOKABLE void trigger(); bool enabled() const; void setEnabled(bool enabled); QString text() const; void setText(QString text); void setChecked(bool checked); bool checked() const; QObject *icon(); IPhrase *phrase() const; QAbstractListModel * actionModel(); QVector actions() const; int actionsCount() const; bool hasActions() { return m_actions.count() > 0; } TrainingAction * action(int index) const; void appendAction(TrainingAction *action); void clearActions(); QHash roleNames() const override; int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Q_SIGNALS: - void changed(); void actionsChanged(); void enabledChanged(bool enabled); void checkedChanged(bool checked); void textChanged(QString text); private: QVector m_actions; QString m_text; TrainingActionIcon m_icon; bool m_visible {true}; bool m_enabled {true}; bool m_checked {false}; bool m_checkable {false}; QString m_tooltip {QString()}; std::shared_ptr m_phrase; ISessionActions *m_session {nullptr}; }; #endif diff --git a/src/core/unit.cpp b/src/core/unit.cpp index 437abce..52b60eb 100644 --- a/src/core/unit.cpp +++ b/src/core/unit.cpp @@ -1,168 +1,166 @@ /* * 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 "artikulate_debug.h" #include #include Unit::Unit(QObject *parent) : IEditableUnit(parent) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } Unit::~Unit() = default; std::shared_ptr Unit::create() { std::shared_ptr unit(new Unit); unit->setSelf(unit); std::weak_ptr unitParameter = unit; connect(unit.get(), &IUnit::phraseAdded, unit.get(), [unitParameter](){ if (auto unit = unitParameter.lock()) { - qDebug() << "emit due to added" << unit.get(); unit->emitPhrasesChanged(unit); } }); connect(unit.get(), &IUnit::phraseRemoved, unit.get(), [unitParameter](){ if (auto unit = unitParameter.lock()) { - qDebug() << "emit due to removed" << unit.get(); unit->emitPhrasesChanged(unit); } }); return unit; } void Unit::setSelf(std::shared_ptr self) { m_self = self; } std::shared_ptr Unit::self() const { return m_self.lock(); } 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; } std::shared_ptr Unit::course() const { return m_course.lock(); } void Unit::setCourse(std::shared_ptr course) { if (course == m_course.lock()) { 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(); } } QVector> Unit::phrases() const { return m_phrases; } void Unit::addPhrase(std::shared_ptr phrase, int index) { auto iter = m_phrases.constBegin(); while (iter != m_phrases.constEnd()) { if (phrase->id() == (*iter)->id()) { qCWarning(ARTIKULATE_LOG()) << "Phrase is already contained in this unit, aborting"; return; } ++iter; } phrase->setUnit(m_self.lock()); emit phraseAboutToBeAdded(phrase, index); m_phrases.insert(index, phrase); qDebug() << "append phrase to unit" << index << id() << phrase->id(); qDebug() << "new count" << m_phrases.count(); emit phraseAdded(phrase); connect(phrase.get(), &Phrase::modified, this, &Unit::modified); emit modified(); } void Unit::removePhrase(std::shared_ptr phrase) { int index = -1; for (int i = 0; i < m_phrases.count(); ++i) { if (m_phrases.at(i)->id() == phrase->id()) { index = i; break; } } Q_ASSERT(index >= 0); emit phraseAboutToBeRemoved(index); m_phrases.removeAt(index); emit phraseRemoved(); } void Unit::emitPhrasesChanged(std::shared_ptr unit) { emit phrasesChanged(unit); } diff --git a/src/qml/EditCoursePage.qml b/src/qml/EditCoursePage.qml index 217fe5b..a081143 100644 --- a/src/qml/EditCoursePage.qml +++ b/src/qml/EditCoursePage.qml @@ -1,75 +1,74 @@ /* * Copyright 2013-2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.5 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQml.Models 2.2 import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.ScrollablePage { id: root title: i18n("Edit Course") actions { left: Kirigami.Action { text: i18n("Previous") tooltip: i18n("Switch to previous phrase.") iconName: "go-previous" enabled: g_editorSession.hasPreviousPhrase onTriggered: g_editorSession.switchToPreviousPhrase() } main: Kirigami.Action { text: i18n("Next") tooltip: i18n("Switch to next phrase.") iconName: "go-next" enabled: g_editorSession.hasNextPhrase onTriggered: g_editorSession.switchToNextPhrase() } -//TODO backend behavior is broken -// contextualActions: [ -// Kirigami.Action { -// text: i18n("Delete") -// tooltip: i18n("Delete this phrase.") -// iconName: "edit-delete-remove" -// onTriggered: g_editorSession.course.deletePhrase(g_editorSession.phrase) -// }, -// Kirigami.Action { -// separator: true -// }, -// Kirigami.Action { -// text: i18n("Create Phrase") -// tooltip: i18n("Create phrase after current phrase.") -// iconName: "list-add" -// onTriggered: g_editorSession.course.createPhraseAfter(g_editorSession.phrase) -// } -// ] + contextualActions: [ + Kirigami.Action { + text: i18n("Delete") + tooltip: i18n("Delete this phrase.") + iconName: "edit-delete-remove" + onTriggered: g_editorSession.course.deletePhrase(g_editorSession.phrase) + }, + Kirigami.Action { + separator: true + }, + Kirigami.Action { + text: i18n("Create Phrase") + tooltip: i18n("Create phrase after current phrase.") + iconName: "list-add" + onTriggered: g_editorSession.course.createPhraseAfter(g_editorSession.phrase) + } + ] } ColumnLayout { PhraseEditor { visible: g_editorSession.phrase !== null phrase: g_editorSession.phrase isSkeletonPhrase: g_editorSession.skeletonMode Layout.fillHeight: true } } }