diff --git a/src/presentation/artifacteditormodel.cpp b/src/presentation/artifacteditormodel.cpp index ceea05a4..96a5796a 100644 --- a/src/presentation/artifacteditormodel.cpp +++ b/src/presentation/artifacteditormodel.cpp @@ -1,402 +1,424 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "artifacteditormodel.h" #include +#include +#include #include +#include #include #include #include "domain/task.h" #include "errorhandler.h" namespace Presentation { class AttachmentModel : public QAbstractListModel { Q_OBJECT public: explicit AttachmentModel(QObject *parent = nullptr) : QAbstractListModel(parent) { } void setTask(const Domain::Task::Ptr &task) { if (m_task == task) return; beginResetModel(); if (m_task) { disconnect(m_task.data(), &Domain::Task::attachmentsChanged, this, &AttachmentModel::triggerReset); } m_task = task; if (m_task) { connect(m_task.data(), &Domain::Task::attachmentsChanged, this, &AttachmentModel::triggerReset); } endResetModel(); } int rowCount(const QModelIndex &parent) const override { if (parent.isValid()) return 0; return m_task->attachments().size(); } QVariant data(const QModelIndex &index, int role) const override { if (!index.isValid()) return QVariant(); auto attachment = m_task->attachments().at(index.row()); switch (role) { case Qt::DisplayRole: return attachment.label(); case Qt::DecorationRole: return QVariant::fromValue(QIcon::fromTheme(attachment.iconName())); default: return QVariant(); } } private slots: void triggerReset() { beginResetModel(); endResetModel(); } private: Domain::Task::Ptr m_task; }; } using namespace Presentation; ArtifactEditorModel::ArtifactEditorModel(QObject *parent) : QObject(parent), m_done(false), m_attachmentModel(new AttachmentModel(this)), m_saveTimer(new QTimer(this)), m_saveNeeded(false), m_editingInProgress(false) { m_saveTimer->setSingleShot(true); m_saveTimer->setInterval(autoSaveDelay()); connect(m_saveTimer, &QTimer::timeout, this, &ArtifactEditorModel::save); } ArtifactEditorModel::~ArtifactEditorModel() { save(); } Domain::Artifact::Ptr ArtifactEditorModel::artifact() const { return m_artifact; } void ArtifactEditorModel::setArtifact(const Domain::Artifact::Ptr &artifact) { if (m_artifact == artifact) return; save(); m_text = QString(); m_title = QString(); m_done = false; m_start = QDateTime(); m_due = QDateTime(); m_attachmentModel->setTask(Domain::Task::Ptr()); m_delegateText = QString(); if (m_artifact) disconnect(m_artifact.data(), Q_NULLPTR, this, Q_NULLPTR); m_artifact = artifact; if (m_artifact) { m_text = m_artifact->text(); m_title = m_artifact->title(); connect(m_artifact.data(), &Domain::Artifact::textChanged, this, &ArtifactEditorModel::onTextChanged); connect(m_artifact.data(), &Domain::Artifact::titleChanged, this, &ArtifactEditorModel::onTitleChanged); } if (auto task = artifact.objectCast()) { m_done = task->isDone(); m_start = task->startDate(); m_due = task->dueDate(); m_attachmentModel->setTask(task); m_delegateText = task->delegate().display(); connect(task.data(), &Domain::Task::doneChanged, this, &ArtifactEditorModel::onDoneChanged); connect(task.data(), &Domain::Task::startDateChanged, this, &ArtifactEditorModel::onStartDateChanged); connect(task.data(), &Domain::Task::dueDateChanged, this, &ArtifactEditorModel::onDueDateChanged); connect(task.data(), &Domain::Task::delegateChanged, this, &ArtifactEditorModel::onDelegateChanged); } emit textChanged(m_text); emit titleChanged(m_title); emit doneChanged(m_done); emit startDateChanged(m_start); emit dueDateChanged(m_due); emit delegateTextChanged(m_delegateText); emit hasTaskPropertiesChanged(hasTaskProperties()); emit artifactChanged(m_artifact); } bool ArtifactEditorModel::hasSaveFunction() const { return bool(m_saveFunction); } void ArtifactEditorModel::setSaveFunction(const SaveFunction &function) { m_saveFunction = function; } bool ArtifactEditorModel::hasDelegateFunction() const { return bool(m_delegateFunction); } void ArtifactEditorModel::setDelegateFunction(const DelegateFunction &function) { m_delegateFunction = function; } bool ArtifactEditorModel::hasTaskProperties() const { return m_artifact.objectCast(); } QString ArtifactEditorModel::text() const { return m_text; } QString ArtifactEditorModel::title() const { return m_title; } bool ArtifactEditorModel::isDone() const { return m_done; } QDateTime ArtifactEditorModel::startDate() const { return m_start; } QDateTime ArtifactEditorModel::dueDate() const { return m_due; } QAbstractItemModel *ArtifactEditorModel::attachmentModel() const { return m_attachmentModel; } QString ArtifactEditorModel::delegateText() const { return m_delegateText; } int ArtifactEditorModel::autoSaveDelay() { return 500; } bool ArtifactEditorModel::editingInProgress() const { return m_editingInProgress; } void ArtifactEditorModel::setText(const QString &text) { if (m_text == text) return; applyNewText(text); setSaveNeeded(true); } void ArtifactEditorModel::setTitle(const QString &title) { if (m_title == title) return; applyNewTitle(title); setSaveNeeded(true); } void ArtifactEditorModel::setDone(bool done) { if (m_done == done) return; applyNewDone(done); setSaveNeeded(true); } void ArtifactEditorModel::setStartDate(const QDateTime &start) { if (m_start == start) return; applyNewStartDate(start); setSaveNeeded(true); } void ArtifactEditorModel::setDueDate(const QDateTime &due) { if (m_due == due) return; applyNewDueDate(due); setSaveNeeded(true); } void ArtifactEditorModel::delegate(const QString &name, const QString &email) { auto task = m_artifact.objectCast(); Q_ASSERT(task); auto delegate = Domain::Task::Delegate(name, email); m_delegateFunction(task, delegate); } +void ArtifactEditorModel::openAttachment(const QModelIndex &index) +{ + auto task = m_artifact.objectCast(); + Q_ASSERT(task); + auto attachment = task->attachments().at(index.row()); + + auto uri = attachment.uri(); + if (!attachment.isUri()) { + auto tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/zanshin_attachment_XXXXXX"), this); + tempFile->open(); + tempFile->setPermissions(QFile::ReadUser); + tempFile->write(attachment.data()); + tempFile->close(); + uri = QUrl::fromLocalFile(tempFile->fileName()); + } + + QDesktopServices::openUrl(uri); +} + void ArtifactEditorModel::setEditingInProgress(bool editing) { m_editingInProgress = editing; } void ArtifactEditorModel::onTextChanged(const QString &text) { if (!m_editingInProgress) applyNewText(text); } void ArtifactEditorModel::onTitleChanged(const QString &title) { if (!m_editingInProgress) applyNewTitle(title); } void ArtifactEditorModel::onDoneChanged(bool done) { if (!m_editingInProgress) applyNewDone(done); } void ArtifactEditorModel::onStartDateChanged(const QDateTime &start) { if (!m_editingInProgress) applyNewStartDate(start); } void ArtifactEditorModel::onDueDateChanged(const QDateTime &due) { if (!m_editingInProgress) applyNewDueDate(due); } void ArtifactEditorModel::onDelegateChanged(const Domain::Task::Delegate &delegate) { m_delegateText = delegate.display(); emit delegateTextChanged(m_delegateText); } void ArtifactEditorModel::save() { if (!isSaveNeeded()) return; Q_ASSERT(m_artifact); const auto currentTitle = m_artifact->title(); m_artifact->setTitle(m_title); m_artifact->setText(m_text); if (auto task = m_artifact.objectCast()) { task->setDone(m_done); task->setStartDate(m_start); task->setDueDate(m_due); } const auto job = m_saveFunction(m_artifact); installHandler(job, i18n("Cannot modify task %1", currentTitle)); setSaveNeeded(false); } void ArtifactEditorModel::setSaveNeeded(bool needed) { if (needed) m_saveTimer->start(); else m_saveTimer->stop(); m_saveNeeded = needed; } bool ArtifactEditorModel::isSaveNeeded() const { return m_saveNeeded; } void ArtifactEditorModel::applyNewText(const QString &text) { m_text = text; emit textChanged(m_text); } void ArtifactEditorModel::applyNewTitle(const QString &title) { m_title = title; emit titleChanged(m_title); } void ArtifactEditorModel::applyNewDone(bool done) { m_done = done; emit doneChanged(m_done); } void ArtifactEditorModel::applyNewStartDate(const QDateTime &start) { m_start = start; emit startDateChanged(m_start); } void ArtifactEditorModel::applyNewDueDate(const QDateTime &due) { m_due = due; emit dueDateChanged(m_due); } #include "artifacteditormodel.moc" diff --git a/src/presentation/artifacteditormodel.h b/src/presentation/artifacteditormodel.h index 7fd21b38..f6bcd950 100644 --- a/src/presentation/artifacteditormodel.h +++ b/src/presentation/artifacteditormodel.h @@ -1,146 +1,147 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PRESENTATION_ARTIFACTEDITORMODEL_H #define PRESENTATION_ARTIFACTEDITORMODEL_H #include #include #include #include "domain/task.h" #include "presentation/errorhandlingmodelbase.h" class QAbstractItemModel; class QTimer; namespace Presentation { class AttachmentModel; class ArtifactEditorModel : public QObject, public ErrorHandlingModelBase { Q_OBJECT Q_PROPERTY(Domain::Artifact::Ptr artifact READ artifact WRITE setArtifact NOTIFY artifactChanged) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(bool done READ isDone WRITE setDone NOTIFY doneChanged) Q_PROPERTY(QDateTime startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) Q_PROPERTY(QDateTime dueDate READ dueDate WRITE setDueDate NOTIFY dueDateChanged) Q_PROPERTY(QAbstractItemModel* attachmentModel READ attachmentModel CONSTANT) Q_PROPERTY(QString delegateText READ delegateText NOTIFY delegateTextChanged) Q_PROPERTY(bool hasTaskProperties READ hasTaskProperties NOTIFY hasTaskPropertiesChanged) Q_PROPERTY(bool editingInProgress READ editingInProgress WRITE setEditingInProgress) public: typedef std::function SaveFunction; typedef std::function DelegateFunction; explicit ArtifactEditorModel(QObject *parent = Q_NULLPTR); ~ArtifactEditorModel(); Domain::Artifact::Ptr artifact() const; void setArtifact(const Domain::Artifact::Ptr &artifact); bool hasSaveFunction() const; void setSaveFunction(const SaveFunction &function); bool hasDelegateFunction() const; void setDelegateFunction(const DelegateFunction &function); bool hasTaskProperties() const; QString text() const; QString title() const; bool isDone() const; QDateTime startDate() const; QDateTime dueDate() const; QAbstractItemModel *attachmentModel() const; QString delegateText() const; static int autoSaveDelay(); bool editingInProgress() const; public slots: void setText(const QString &text); void setTitle(const QString &title); void setDone(bool done); void setStartDate(const QDateTime &start); void setDueDate(const QDateTime &due); void delegate(const QString &name, const QString &email); + void openAttachment(const QModelIndex &index); void setEditingInProgress(bool editingInProgress); signals: void artifactChanged(const Domain::Artifact::Ptr &artifact); void hasTaskPropertiesChanged(bool hasTaskProperties); void textChanged(const QString &text); void titleChanged(const QString &title); void doneChanged(bool done); void startDateChanged(const QDateTime &date); void dueDateChanged(const QDateTime &due); void delegateTextChanged(const QString &delegateText); private slots: void onTextChanged(const QString &text); void onTitleChanged(const QString &title); void onDoneChanged(bool done); void onStartDateChanged(const QDateTime &start); void onDueDateChanged(const QDateTime &due); void onDelegateChanged(const Domain::Task::Delegate &delegate); void save(); private: void setSaveNeeded(bool needed); bool isSaveNeeded() const; void applyNewText(const QString &text); void applyNewTitle(const QString &title); void applyNewDone(bool done); void applyNewStartDate(const QDateTime &start); void applyNewDueDate(const QDateTime &due); Domain::Artifact::Ptr m_artifact; SaveFunction m_saveFunction; DelegateFunction m_delegateFunction; QString m_text; QString m_title; bool m_done; QDateTime m_start; QDateTime m_due; AttachmentModel *m_attachmentModel; QString m_delegateText; QTimer *m_saveTimer; bool m_saveNeeded; bool m_editingInProgress; }; } #endif // PRESENTATION_ARTIFACTEDITORMODEL_H diff --git a/src/widgets/editorview.cpp b/src/widgets/editorview.cpp index 6c308bab..111472d0 100644 --- a/src/widgets/editorview.cpp +++ b/src/widgets/editorview.cpp @@ -1,280 +1,289 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "editorview.h" #include #include #include #include #include #include #include "kdateedit.h" #include "addressline/addresseelineedit.h" #include "domain/artifact.h" #include "ui_editorview.h" using namespace Widgets; EditorView::EditorView(QWidget *parent) : QWidget(parent), m_model(Q_NULLPTR), ui(new Ui::EditorView), m_delegateEdit(Q_NULLPTR) { ui->setupUi(this); // To avoid having unit tests talking to akonadi // while we don't need the completion for them if (qEnvironmentVariableIsEmpty("ZANSHIN_UNIT_TEST_RUN")) m_delegateEdit = new KPIM::AddresseeLineEdit(ui->delegateEditPlaceHolder); else m_delegateEdit = new KLineEdit(ui->delegateEditPlaceHolder); // placing our special DelegateEdit into the placeholder we prepared m_delegateEdit->setObjectName("delegateEdit"); ui->delegateToLabel->setBuddy(m_delegateEdit); ui->delegateEditPlaceHolder->layout()->addWidget(m_delegateEdit); ui->startDateEdit->setMinimumContentsLength(10); ui->dueDateEdit->setMinimumContentsLength(10); // Make sure our minimum width is always the one with // the task group visible ui->layout->activate(); setMinimumWidth(minimumSizeHint().width()); ui->delegateLabel->setVisible(false); ui->taskGroup->setVisible(false); ui->textEdit->installEventFilter(this); ui->startDateEdit->installEventFilter(this); ui->dueDateEdit->installEventFilter(this); ui->doneButton->installEventFilter(this); m_delegateEdit->installEventFilter(this); connect(ui->textEdit, &QPlainTextEdit::textChanged, this, &EditorView::onTextEditChanged); connect(ui->startDateEdit, &KPIM::KDateEdit::dateEntered, this, &EditorView::onStartEditEntered); connect(ui->dueDateEdit, &KPIM::KDateEdit::dateEntered, this, &EditorView::onDueEditEntered); connect(ui->doneButton, &QAbstractButton::toggled, this, &EditorView::onDoneButtonChanged); connect(ui->startTodayButton, &QAbstractButton::clicked, this, &EditorView::onStartTodayClicked); + connect(ui->attachmentList, &QAbstractItemView::doubleClicked, this, &EditorView::onAttachmentDoubleClicked); connect(m_delegateEdit, &KLineEdit::returnPressed, this, &EditorView::onDelegateEntered); setEnabled(false); } EditorView::~EditorView() { delete ui; } QObject *EditorView::model() const { return m_model; } void EditorView::setModel(QObject *model) { if (model == m_model) return; if (m_model) { ui->attachmentList->setModel(Q_NULLPTR); disconnect(m_model, Q_NULLPTR, this, Q_NULLPTR); disconnect(this, Q_NULLPTR, m_model, Q_NULLPTR); } m_model = model; setEnabled(m_model); if (!m_model) { ui->taskGroup->setVisible(false); ui->textEdit->clear(); return; } auto attachments = m_model->property("attachmentModel").value(); ui->attachmentList->setModel(attachments); onArtifactChanged(); onTextOrTitleChanged(); onHasTaskPropertiesChanged(); onStartDateChanged(); onDueDateChanged(); onDoneChanged(); onDelegateTextChanged(); connect(m_model, SIGNAL(artifactChanged(Domain::Artifact::Ptr)), this, SLOT(onArtifactChanged())); connect(m_model, SIGNAL(hasTaskPropertiesChanged(bool)), this, SLOT(onHasTaskPropertiesChanged())); connect(m_model, SIGNAL(titleChanged(QString)), this, SLOT(onTextOrTitleChanged())); connect(m_model, SIGNAL(textChanged(QString)), this, SLOT(onTextOrTitleChanged())); connect(m_model, SIGNAL(startDateChanged(QDateTime)), this, SLOT(onStartDateChanged())); connect(m_model, SIGNAL(dueDateChanged(QDateTime)), this, SLOT(onDueDateChanged())); connect(m_model, SIGNAL(doneChanged(bool)), this, SLOT(onDoneChanged())); connect(m_model, SIGNAL(delegateTextChanged(QString)), this, SLOT(onDelegateTextChanged())); connect(this, SIGNAL(titleChanged(QString)), m_model, SLOT(setTitle(QString))); connect(this, SIGNAL(textChanged(QString)), m_model, SLOT(setText(QString))); connect(this, SIGNAL(startDateChanged(QDateTime)), m_model, SLOT(setStartDate(QDateTime))); connect(this, SIGNAL(dueDateChanged(QDateTime)), m_model, SLOT(setDueDate(QDateTime))); connect(this, SIGNAL(doneChanged(bool)), m_model, SLOT(setDone(bool))); } bool EditorView::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); switch (event->type()) { case QEvent::FocusIn: // We don't want to replace text being edited by the user with older text // coming from akonadi notifications (async, after some older save job) m_model->setProperty("editingInProgress", true); break; case QEvent::FocusOut: // We do react to notifications, however, when not having the focus, // for instance when changing the title using the central list. m_model->setProperty("editingInProgress", false); break; default: break; } return false; } void EditorView::onArtifactChanged() { auto artifact = m_model->property("artifact").value(); setEnabled(artifact); m_delegateEdit->clear(); } void EditorView::onHasTaskPropertiesChanged() { ui->taskGroup->setVisible(m_model->property("hasTaskProperties").toBool()); } void EditorView::onTextOrTitleChanged() { const auto title = m_model->property("title").toString(); const auto text = m_model->property("text").toString(); const auto fullText = QString(title + '\n' + text); if (ui->textEdit->toPlainText() != fullText) // QPlainTextEdit doesn't do this check ui->textEdit->setPlainText(fullText); } void EditorView::onStartDateChanged() { ui->startDateEdit->setDate(m_model->property("startDate").toDateTime().date()); } void EditorView::onDueDateChanged() { ui->dueDateEdit->setDate(m_model->property("dueDate").toDateTime().date()); } void EditorView::onDoneChanged() { ui->doneButton->setChecked(m_model->property("done").toBool()); } void EditorView::onDelegateTextChanged() { const auto delegateText = m_model->property("delegateText").toString(); const auto labelText = delegateText.isEmpty() ? QString() : i18n("Delegated to: %1", delegateText); ui->delegateLabel->setVisible(!labelText.isEmpty()); ui->delegateLabel->setText(labelText); } void EditorView::onTextEditChanged() { const QString plainText = ui->textEdit->toPlainText(); const int index = plainText.indexOf('\n'); if (index < 0) { emit titleChanged(plainText); emit textChanged(QString()); } else { const QString title = plainText.left(index); const QString text = plainText.mid(index + 1); emit titleChanged(title); emit textChanged(text); } } void EditorView::onStartEditEntered(const QDate &start) { emit startDateChanged(QDateTime(start, QTime(), Qt::UTC)); } void EditorView::onDueEditEntered(const QDate &due) { emit dueDateChanged(QDateTime(due, QTime(), Qt::UTC)); } void EditorView::onDoneButtonChanged(bool checked) { emit doneChanged(checked); } void EditorView::onStartTodayClicked() { QDate today(QDate::currentDate()); ui->startDateEdit->setDate(today); emit startDateChanged(QDateTime(today, QTime(), Qt::UTC)); } void EditorView::onDelegateEntered() { const auto input = m_delegateEdit->text(); auto name = QString(); auto email = QString(); auto gotMatch = false; QRegExp fullRx("\\s*(.*) <([\\w\\.]+@[\\w\\.]+)>\\s*"); QRegExp emailOnlyRx("\\s*?\\s*"); if (input.contains(fullRx)) { name = fullRx.cap(1); email = fullRx.cap(2); gotMatch = true; } else if (input.contains(emailOnlyRx)) { email = emailOnlyRx.cap(1); gotMatch = true; } if (gotMatch) { QMetaObject::invokeMethod(m_model, "delegate", Q_ARG(QString, name), Q_ARG(QString, email)); m_delegateEdit->clear(); } } + +void EditorView::onAttachmentDoubleClicked(const QModelIndex &index) +{ + if (!m_model) + return; + + QMetaObject::invokeMethod(m_model, "openAttachment", Q_ARG(QModelIndex, index)); +} diff --git a/src/widgets/editorview.h b/src/widgets/editorview.h index db72d9b9..a017ed2e 100644 --- a/src/widgets/editorview.h +++ b/src/widgets/editorview.h @@ -1,95 +1,97 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WIDGETS_EDITORVIEW_H #define WIDGETS_EDITORVIEW_H #include #include class QAbstractButton; class QLabel; class QPlainTextEdit; class KLineEdit; namespace KPIM { class KDateEdit; } namespace Ui { class EditorView; } namespace Widgets { class EditorView : public QWidget { Q_OBJECT public: explicit EditorView(QWidget *parent = Q_NULLPTR); ~EditorView(); QObject *model() const; public slots: void setModel(QObject *model); signals: void textChanged(const QString &text); void titleChanged(const QString &title); void startDateChanged(const QDateTime &start); void dueDateChanged(const QDateTime &due); void doneChanged(bool done); protected: bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; private slots: void onArtifactChanged(); void onHasTaskPropertiesChanged(); void onTextOrTitleChanged(); void onStartDateChanged(); void onDueDateChanged(); void onDoneChanged(); void onDelegateTextChanged(); void onTextEditChanged(); void onStartEditEntered(const QDate &start); void onDueEditEntered(const QDate &due); void onDoneButtonChanged(bool checked); void onStartTodayClicked(); void onDelegateEntered(); + void onAttachmentDoubleClicked(const QModelIndex &index); + private: QObject *m_model; Ui::EditorView *ui; KLineEdit *m_delegateEdit; }; } #endif // WIDGETS_EDITORVIEW_H