diff --git a/src/widgets/editorview.h b/src/widgets/editorview.h --- a/src/widgets/editorview.h +++ b/src/widgets/editorview.h @@ -29,6 +29,8 @@ #include +#include + #include "domain/task.h" class QAbstractButton; @@ -51,13 +53,17 @@ { Q_OBJECT public: + typedef std::function RequestFileNameFunction; + explicit EditorView(QWidget *parent = Q_NULLPTR); ~EditorView(); QObject *model() const; + RequestFileNameFunction requestFileNameFunction() const; public slots: void setModel(QObject *model); + void setRequestFileNameFunction(const RequestFileNameFunction &function); signals: void textChanged(const QString &text); @@ -88,10 +94,14 @@ void onRecurrenceComboChanged(int index); void onDelegateEntered(); + void onAttachmentSelectionChanged(); + void onAddAttachmentClicked(); + void onRemoveAttachmentClicked(); void onAttachmentDoubleClicked(const QModelIndex &index); private: QObject *m_model; + RequestFileNameFunction m_requestFileNameFunction; Ui::EditorView *ui; KLineEdit *m_delegateEdit; diff --git a/src/widgets/editorview.cpp b/src/widgets/editorview.cpp --- a/src/widgets/editorview.cpp +++ b/src/widgets/editorview.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,10 @@ ui(new Ui::EditorView), m_delegateEdit(Q_NULLPTR) { + m_requestFileNameFunction = [](QWidget *parent) { + return QFileDialog::getOpenFileName(parent, i18n("Add Attachment")); + }; + ui->setupUi(this); // To avoid having unit tests talking to akonadi @@ -92,6 +97,8 @@ connect(ui->recurrenceCombo, static_cast(&QComboBox::currentIndexChanged), this, &EditorView::onRecurrenceComboChanged); connect(ui->attachmentList, &QAbstractItemView::doubleClicked, this, &EditorView::onAttachmentDoubleClicked); + connect(ui->addAttachmentButton, &QToolButton::clicked, this, &EditorView::onAddAttachmentClicked); + connect(ui->removeAttachmentButton, &QToolButton::clicked, this, &EditorView::onRemoveAttachmentClicked); connect(m_delegateEdit, &KLineEdit::returnPressed, this, &EditorView::onDelegateEntered); setEnabled(false); @@ -107,12 +114,19 @@ return m_model; } +EditorView::RequestFileNameFunction EditorView::requestFileNameFunction() const +{ + return m_requestFileNameFunction; +} + void EditorView::setModel(QObject *model) { if (model == m_model) return; if (m_model) { + disconnect(ui->attachmentList->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &EditorView::onAttachmentSelectionChanged); ui->attachmentList->setModel(Q_NULLPTR); disconnect(m_model, Q_NULLPTR, this, Q_NULLPTR); disconnect(this, Q_NULLPTR, m_model, Q_NULLPTR); @@ -130,6 +144,8 @@ auto attachments = m_model->property("attachmentModel").value(); ui->attachmentList->setModel(attachments); + connect(ui->attachmentList->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &EditorView::onAttachmentSelectionChanged); onArtifactChanged(); onTextOrTitleChanged(); @@ -139,6 +155,7 @@ onDoneChanged(); onRecurrenceChanged(); onDelegateTextChanged(); + onAttachmentSelectionChanged(); connect(m_model, SIGNAL(artifactChanged(Domain::Artifact::Ptr)), this, SLOT(onArtifactChanged())); @@ -160,6 +177,11 @@ connect(this, SIGNAL(recurrenceChanged(Domain::Task::Recurrence)), m_model, SLOT(setRecurrence(Domain::Task::Recurrence))); } +void EditorView::setRequestFileNameFunction(const EditorView::RequestFileNameFunction &function) +{ + m_requestFileNameFunction = function; +} + bool EditorView::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); @@ -308,6 +330,37 @@ } } +void EditorView::onAttachmentSelectionChanged() +{ + if (!m_model) + return; + + const auto selectionModel = ui->attachmentList->selectionModel(); + const auto selectedIndexes = selectionModel->selectedIndexes(); + ui->removeAttachmentButton->setEnabled(!selectedIndexes.isEmpty()); +} + +void EditorView::onAddAttachmentClicked() +{ + if (!m_model) + return; + + auto fileName = m_requestFileNameFunction(this); + if (!fileName.isEmpty()) + QMetaObject::invokeMethod(m_model, "addAttachment", Q_ARG(QString, fileName)); +} + +void EditorView::onRemoveAttachmentClicked() +{ + if (!m_model) + return; + + const auto selectionModel = ui->attachmentList->selectionModel(); + const auto selectedIndexes = selectionModel->selectedIndexes(); + if (!selectedIndexes.isEmpty()) + QMetaObject::invokeMethod(m_model, "removeAttachment", Q_ARG(QModelIndex, selectedIndexes.first())); +} + void EditorView::onAttachmentDoubleClicked(const QModelIndex &index) { if (!m_model) diff --git a/src/widgets/editorview.ui b/src/widgets/editorview.ui --- a/src/widgets/editorview.ui +++ b/src/widgets/editorview.ui @@ -7,7 +7,7 @@ 0 0 343 - 390 + 448 @@ -64,7 +64,50 @@ - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add Attachment... + + + + + + true + + + + + + + Remove Attachment + + + + + + true + + + + + + St&art Date @@ -77,7 +120,7 @@ - + @@ -91,7 +134,7 @@ - + D&ue Date @@ -104,10 +147,10 @@ - + - + &Recurrence @@ -120,10 +163,10 @@ - + - + De&legate to @@ -133,7 +176,7 @@ - + @@ -151,7 +194,7 @@ - + 3 diff --git a/tests/units/widgets/editorviewtest.cpp b/tests/units/widgets/editorviewtest.cpp --- a/tests/units/widgets/editorviewtest.cpp +++ b/tests/units/widgets/editorviewtest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -96,6 +97,18 @@ delegateEmails << email; } + void addAttachment(const QString &fileName) + { + auto item = new QStandardItem(fileName); + attachmentModel.appendRow(QList() << item); + } + + void removeAttachment(const QModelIndex &index) + { + if (index.isValid()) + attachmentModel.removeRows(index.row(), 1, QModelIndex()); + } + signals: void artifactChanged(const Domain::Artifact::Ptr &artifact); void hasTaskPropertiesChanged(bool hasTaskProperties); @@ -154,6 +167,14 @@ QVERIFY(attachmentList); QVERIFY(!attachmentList->isVisibleTo(&editor)); + auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); + QVERIFY(addAttachmentButton); + QVERIFY(!addAttachmentButton->isVisibleTo(&editor)); + + auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); + QVERIFY(removeAttachmentButton); + QVERIFY(!removeAttachmentButton->isVisibleTo(&editor)); + auto delegateLabel = editor.findChild(QStringLiteral("delegateLabel")); QVERIFY(delegateLabel); QVERIFY(!delegateLabel->isVisibleTo(&editor)); @@ -192,6 +213,12 @@ QVERIFY(attachmentList); QCOMPARE(attachmentList->model(), &model.attachmentModel); + auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); + QVERIFY(addAttachmentButton); + + auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); + QVERIFY(removeAttachmentButton); + auto delegateLabel = editor.findChild(QStringLiteral("delegateLabel")); QVERIFY(delegateLabel); @@ -210,6 +237,8 @@ QVERIFY(!doneButton->isVisibleTo(&editor)); QVERIFY(!attachmentList->isVisibleTo(&editor)); QVERIFY(attachmentList->model() == nullptr); + QVERIFY(!addAttachmentButton->isVisibleTo(&editor)); + QVERIFY(!removeAttachmentButton->isVisibleTo(&editor)); QVERIFY(!delegateLabel->isVisibleTo(&editor)); QVERIFY(!delegateEdit->isVisibleTo(&editor)); } @@ -236,6 +265,12 @@ auto attachmentList = editor.findChild(QStringLiteral("attachmentList")); QVERIFY(attachmentList); + auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); + QVERIFY(addAttachmentButton); + + auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); + QVERIFY(removeAttachmentButton); + auto delegateLabel = editor.findChild(QStringLiteral("delegateLabel")); QVERIFY(!delegateLabel->isVisibleTo(&editor)); @@ -251,6 +286,8 @@ QVERIFY(recurrenceCombo->isVisibleTo(&editor)); QVERIFY(doneButton->isVisibleTo(&editor)); QVERIFY(attachmentList->isVisibleTo(&editor)); + QVERIFY(addAttachmentButton->isVisibleTo(&editor)); + QVERIFY(removeAttachmentButton->isVisibleTo(&editor)); QVERIFY(!delegateLabel->isVisibleTo(&editor)); QVERIFY(delegateEdit->isVisibleTo(&editor)); } @@ -691,6 +728,57 @@ QCOMPARE(model.property("recurrence").value(), Domain::Task::RecursWeekly); } + void shouldAddAttachments() + { + // GIVEN + Widgets::EditorView editor; + editor.setRequestFileNameFunction([](QWidget*) { return "/tmp/foobar"; }); + EditorModelStub model; + model.makeTaskAvailable(); + editor.setModel(&model); + + auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); + + // WHEN + addAttachmentButton->click(); + + // THEN + QCOMPARE(model.attachmentModel.rowCount(), 1); + QCOMPARE(model.attachmentModel.data(model.attachmentModel.index(0, 0)).toString(), + QStringLiteral("/tmp/foobar")); + } + + void shouldRemoveAttachments() + { + // GIVEN + Widgets::EditorView editor; + EditorModelStub model; + model.makeTaskAvailable(); + model.addAttachment("/tmp/foo"); + model.addAttachment("/tmp/bar"); + editor.setModel(&model); + + auto attachmentList = editor.findChild(QStringLiteral("attachmentList")); + auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); + + // THEN + QVERIFY(!removeAttachmentButton->isEnabled()); + + // WHEN + attachmentList->selectionModel()->select(model.attachmentModel.index(0, 0), QItemSelectionModel::ClearAndSelect); + + // THEN + QVERIFY(removeAttachmentButton->isEnabled()); + + // WHEN + removeAttachmentButton->click(); + + // THEN + QCOMPARE(model.attachmentModel.rowCount(), 1); + QCOMPARE(model.attachmentModel.data(model.attachmentModel.index(0, 0)).toString(), + QStringLiteral("/tmp/bar")); + } + void shouldReactToDelegateTextChanges() { // GIVEN