diff --git a/src/widgets/editorview.cpp b/src/widgets/editorview.cpp index 9558fc89..45951611 100644 --- a/src/widgets/editorview.cpp +++ b/src/widgets/editorview.cpp @@ -1,819 +1,799 @@ /* 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 "recurrencewidget.h" #include "editorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdateedit.h" #include #include "addressline/addresseelineedit.h" #include "presentation/metatypes.h" #include #include #include #include #include #include "domain/artifact.h" #include "domain/task.h" #include "domain/relation.h" #include "domain/tag.h" using namespace Widgets; enum AlarmType { AlarmNone, AlarmDisplay, AlarmAudio }; enum AlarmPosition { AlarmStart, AlarmEnd }; EditorView::EditorView(QWidget *parent) : QWidget(parent), m_model(0), m_delegateLabel(new QLabel(this)), m_titleEdit(new QLineEdit(this)), m_textEdit(new EditorWidget(this)), m_taskGroup(new QWidget(this)), m_startDateEdit(new KPIM::KDateEdit(m_taskGroup)), m_startTimeEdit(new KTimeComboBox(m_taskGroup)), m_dueDateEdit(new KPIM::KDateEdit(m_taskGroup)), m_dueTimeEdit(new KTimeComboBox(m_taskGroup)), m_startTodayButton(new QPushButton(tr("Start today"), m_taskGroup)), m_delegateEdit(0), m_statusComboBox(new QComboBox(m_taskGroup)), m_progressEdit(new QSpinBox(m_taskGroup)), m_relationsLayout(new QVBoxLayout), m_attachmentsLayout(new QVBoxLayout), m_tagsLayout(new QHBoxLayout), m_addAttachmentButton(new QPushButton(tr("Add attachment"), m_taskGroup)), m_alarmTypeWidget(new QComboBox(m_taskGroup)), m_alarmPositionWidget(new QComboBox(m_taskGroup)), m_alarmOffsetEdit(new QSpinBox(m_taskGroup)), m_saveButton(new QPushButton()) { // To avoid having unit tests talking to akonadi // while we don't need the completion for them if (qgetenv("ZANSHIN_UNIT_TEST_RUN").isEmpty()) m_delegateEdit = new KPIM::AddresseeLineEdit(this); else m_delegateEdit = new KLineEdit(this); m_delegateLabel->setObjectName("delegateLabel"); m_delegateEdit->setObjectName("delegateEdit"); m_textEdit->setObjectName("textEdit"); m_titleEdit->setObjectName("titleEdit"); m_startDateEdit->setObjectName("startDateEdit"); m_startTimeEdit->setObjectName("startTimeEdit"); m_dueDateEdit->setObjectName("dueDateEdit"); m_dueTimeEdit->setObjectName("dueTimeEdit"); m_startTodayButton->setObjectName("startTodayButton"); m_statusComboBox->setObjectName("statusComboBox"); m_progressEdit->setObjectName("progressEdit"); m_alarmTypeWidget->setObjectName("alarmTypeWidget"); m_alarmPositionWidget->setObjectName("alarmPositionWidget"); m_alarmOffsetEdit->setObjectName("alarmOffsetEdit"); m_saveButton->setObjectName("saveButton"); m_startDateEdit->setMinimumContentsLength(10); m_dueDateEdit->setMinimumContentsLength(10); m_progressEdit->setRange(0, 100); m_alarmOffsetEdit->setRange(0,10000); m_alarmOffsetEdit->setSuffix(" seconds"); m_statusComboBox->addItem(tr("None"), Domain::Task::None); m_statusComboBox->addItem(tr("Needs action"), Domain::Task::NeedsAction); m_statusComboBox->addItem(tr("In process"), Domain::Task::InProcess); m_statusComboBox->addItem(tr("Completed"), Domain::Task::Complete); m_statusComboBox->addItem(tr("Cancelled"), Domain::Task::Cancelled); m_saveButton->setText(tr("Save")); m_saveButton->setEnabled(false); m_saveButton->setIcon(KIcon("document-save")); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(m_delegateLabel); layout->addWidget(m_titleEdit); layout->addWidget(m_textEdit); layout->addLayout(m_relationsLayout); layout->addWidget(m_taskGroup); QHBoxLayout *saveButtonHBox = new QHBoxLayout; saveButtonHBox->addStretch(); saveButtonHBox->addWidget(m_saveButton); layout->addLayout(saveButtonHBox); setLayout(layout); m_addAttachmentButton->setIcon(KIcon("list-add")); connect(m_addAttachmentButton, SIGNAL(clicked()), this, SLOT(onAddAttachmentClicked())); QVBoxLayout *vbox = new QVBoxLayout; vbox->addLayout(m_attachmentsLayout); QHBoxLayout *attachmentsHBox = new QHBoxLayout; attachmentsHBox->addWidget(m_addAttachmentButton); attachmentsHBox->addStretch(); vbox->addLayout(attachmentsHBox); auto delegateHBox = new QHBoxLayout; delegateHBox->addWidget(new QLabel(tr("Delegate to"), m_taskGroup)); delegateHBox->addWidget(m_delegateEdit); auto delegateButton = new QPushButton(tr("Delegate")); delegateButton->setIcon(KIcon("mail-forward")); connect(delegateButton, SIGNAL(pressed()), this, SLOT(onDelegateEntered())); delegateHBox->addWidget(delegateButton, 0); vbox->addLayout(delegateHBox); QHBoxLayout *datesHBox = new QHBoxLayout; datesHBox->addWidget(new QLabel(tr("Start date"), m_taskGroup)); datesHBox->addWidget(m_startDateEdit, 1); datesHBox->addWidget(m_startTimeEdit, 1); datesHBox->addWidget(new QLabel(tr("Due date"), m_taskGroup)); datesHBox->addWidget(m_dueDateEdit, 1); datesHBox->addWidget(m_dueTimeEdit, 1); vbox->addLayout(datesHBox); QHBoxLayout *bottomHBox = new QHBoxLayout; bottomHBox->addWidget(m_startTodayButton); bottomHBox->addStretch(); vbox->addLayout(bottomHBox); auto alarmHBox= new QHBoxLayout; alarmHBox->addWidget(new QLabel(tr("Alarm"), m_taskGroup)); alarmHBox->addWidget(m_alarmTypeWidget, 1); alarmHBox->addWidget(m_alarmOffsetEdit, 1); alarmHBox->addWidget(m_alarmPositionWidget, 1); vbox->addLayout(alarmHBox); auto progressHBox = new QHBoxLayout; progressHBox->addWidget(new QLabel(tr("Progress"), m_taskGroup)); progressHBox->addWidget(m_progressEdit, 1); vbox->addLayout(progressHBox); auto statusHBox = new QHBoxLayout; statusHBox->addWidget(new QLabel(tr("Status"), m_taskGroup)); statusHBox->addWidget(m_statusComboBox, 1); vbox->addLayout(statusHBox); auto tagHBox = new QHBoxLayout; tagHBox->addWidget(new QLabel(tr("Tags"))); tagHBox->addLayout(m_tagsLayout); m_newTagWidget = new QLineEdit(); m_newTagWidget->setPlaceholderText(tr("Add Tag...")); connect(m_newTagWidget, SIGNAL(returnPressed()), this, SLOT(onTagAdded())); tagHBox->addWidget(m_newTagWidget, 1); auto addTagButton = new QPushButton(tr("Add")); addTagButton->setIcon(KIcon("list-add")); connect(addTagButton, SIGNAL(pressed()), this, SLOT(onTagAdded())); tagHBox->addWidget(addTagButton, 0); vbox->addLayout(tagHBox); m_recurrenceWidget = new RecurrenceWidget; vbox->addWidget(m_recurrenceWidget); m_taskGroup->setLayout(vbox); // Make sure our minimum width is always the one with // the task group visible layout->activate(); setMinimumWidth(minimumSizeHint().width()); m_delegateLabel->setVisible(false); m_taskGroup->setVisible(false); auto editMenu = new QMenu; for(auto action : m_textEdit->editActions()) { editMenu->addAction(action); } auto menuAction = new QAction(0); menuAction->setText(tr("Edit")); menuAction->setMenu(editMenu); addAction(menuAction); for(auto action : m_textEdit->actions()) { addAction(action); } - connect(m_textEdit, SIGNAL(fullscreenToggled(bool)), SLOT(toggleFullscreenEditor())); - auto action = new QAction(this); - action->setText(tr("Fullscreen &Editor")); - action->setIcon(KIcon("go-up")); - action->setShortcut(QKeySequence(Qt::Key_F5)); - connect(action, SIGNAL(triggered()), SLOT(toggleFullscreenEditor())); - addAction(action); - connect(m_textEdit->editor(), SIGNAL(textChanged()), this, SLOT(onTextEditChanged())); connect(m_titleEdit, SIGNAL(editingFinished()), this, SLOT(onTextEditChanged())); connect(m_startDateEdit, SIGNAL(dateEntered(QDate)), this, SLOT(onStartEditEntered(QDate))); connect(m_startTimeEdit, SIGNAL(timeEntered(QTime)), this, SLOT(onStartTimeEntered(QTime))); connect(m_dueDateEdit, SIGNAL(dateEntered(QDate)), this, SLOT(onDueEditEntered(QDate))); connect(m_dueTimeEdit, SIGNAL(timeEntered(QTime)), this, SLOT(onDueTimeEntered(QTime))); connect(m_startTodayButton, SIGNAL(clicked()), this, SLOT(onStartTodayClicked())); connect(m_delegateEdit, SIGNAL(returnPressed()), this, SLOT(onDelegateEntered())); connect(m_progressEdit, SIGNAL(valueChanged(int)), this, SLOT(onProgressChanged(int))); connect(m_statusComboBox, SIGNAL(activated(int)), this, SLOT(onStatusChanged(int))); connect(m_alarmTypeWidget, SIGNAL(currentIndexChanged(int)), this, SLOT(onAlarmEnableChanged(int))); connect(m_alarmTypeWidget, SIGNAL(currentIndexChanged(int)), this, SLOT(onAlarmTypeChanged(int))); connect(m_alarmPositionWidget, SIGNAL(currentIndexChanged(int)), this, SLOT(onAlarmPositionChanged(int))); connect(m_alarmOffsetEdit, SIGNAL(valueChanged(int)), this, SLOT(onAlarmOffsetChanged(int))); connect(m_saveButton, SIGNAL(clicked()), this, SIGNAL(save())); setEnabled(false); fillAlarmCombo(); } void EditorView::fillAlarmCombo() { m_alarmTypeWidget->clear(); m_alarmPositionWidget->clear(); for (int i=AlarmNone; i <= AlarmAudio; ++i) { switch(i) { case AlarmNone: m_alarmTypeWidget->addItem(tr("None"), Domain::Alarm::None); break; case AlarmDisplay: m_alarmTypeWidget->addItem(tr("Message"), Domain::Alarm::Display); break; case AlarmAudio: m_alarmTypeWidget->addItem(tr("Audio"), Domain::Alarm::Audio); } } for(int i=AlarmStart; i <= AlarmEnd; ++i) { switch(i) { case AlarmStart: m_alarmPositionWidget->addItem(tr("before start"), Domain::Alarm::Start); break; case AlarmEnd: m_alarmPositionWidget->addItem(tr("before end"), Domain::Alarm::End); } } onAlarmTypeChanged(AlarmNone); } -void EditorView::toggleFullscreenEditor() -{ - if (m_textEdit->windowState() & Qt::WindowFullScreen) { - m_textEdit->setParent(this); - static_cast(layout())->insertWidget(2, m_textEdit); - } else { - m_textEdit->setParent(0); - } - m_textEdit->setWindowState(m_textEdit->windowState() ^ Qt::WindowFullScreen); - m_textEdit->show(); -} - QObject *EditorView::model() const { return m_model; } void EditorView::setModel(QObject *model) { if (model == m_model) return; if (m_model) { disconnect(m_model, 0, this, 0); disconnect(this, 0, m_model, 0); } m_model = model; onArtifactChanged(); onTextOrTitleChanged(); onHasTaskPropertiesChanged(); onStartDateChanged(); onDueDateChanged(); onDelegateTextChanged(); onProgressChanged(); onRecurrenceChanged(); onRelationsChanged(); onTagsChanged(); onAttachmentsChanged(); onStatusChanged(); onAlarmChanged(); onSaveNeededChanged(m_model->property("saveNeeded").toBool()); 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(startDateChanged(QDateTime)), m_recurrenceWidget, SLOT(setStartDate(QDateTime))); connect(m_model, SIGNAL(dueDateChanged(QDateTime)), this, SLOT(onDueDateChanged())); connect(m_model, SIGNAL(delegateTextChanged(QString)), this, SLOT(onDelegateTextChanged())); connect(m_model, SIGNAL(progressChanged(int)), this, SLOT(onProgressChanged())); connect(m_model, SIGNAL(statusChanged(int)), this, SLOT(onStatusChanged())); connect(m_model, SIGNAL(recurrenceChanged(Domain::Recurrence::Ptr)), this, SLOT(onRecurrenceChanged())); connect(m_model, SIGNAL(relationsChanged(QList)), this, SLOT(onRelationsChanged())); connect(m_model, SIGNAL(tagsChanged(Domain::Tag::List)), this, SLOT(onTagsChanged())); connect(m_model, SIGNAL(attachmentsChanged(Domain::Artifact::Attachment::List)), this, SLOT(onAttachmentsChanged())); connect(m_model, SIGNAL(alarmChanged(Domain::Alarm::Ptr)), this, SLOT(onAlarmChanged())); connect(m_model, SIGNAL(saveNeededChanged(bool)), this, SLOT(onSaveNeededChanged(bool))); 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(delegateChanged(QString, QString)), m_model, SLOT(setDelegate(QString, QString))); connect(this, SIGNAL(progressChanged(int)), m_model, SLOT(setProgress(int))); connect(this, SIGNAL(statusChanged(int)), m_model, SLOT(setStatus(int))); connect(this, SIGNAL(alarmTypeChanged(int)),m_model, SLOT(setAlarmType(int))); connect(this, SIGNAL(alarmPositionChanged(int)),m_model, SLOT(setAlarmPosition(int))); connect(this, SIGNAL(alarmOffsetChanged(int)),m_model, SLOT(setAlarmOffset(int))); connect(this, SIGNAL(save()),m_model, SLOT(save())); connect(m_recurrenceWidget, SIGNAL(frequencyChanged(Domain::Recurrence::Frequency,int)), m_model, SLOT(setFrequency(Domain::Recurrence::Frequency, int))); connect(m_recurrenceWidget, SIGNAL(endChanged(QDateTime)), m_model, SLOT(setRepeatEnd(QDateTime))); connect(m_recurrenceWidget, SIGNAL(endChanged(int)), m_model, SLOT(setRepeatEnd(int))); connect(m_recurrenceWidget, SIGNAL(noEnd()), m_model, SLOT(setRepeatEndless())); connect(m_recurrenceWidget, SIGNAL(exceptionDatesChanged(QList)), m_model, SLOT(setExceptionDates(QList))); connect(m_recurrenceWidget, SIGNAL(byDayChanged(QList)), m_model, SLOT(setByDay(QList))); connect(m_recurrenceWidget, SIGNAL(byMonthChanged(QList)), m_model, SLOT(setByMonth(QList))); connect(m_recurrenceWidget, SIGNAL(byMonthDaysChanged(QList)), m_model, SLOT(setByMonthDays(QList))); connect(m_recurrenceWidget, SIGNAL(byDayPositionChanged(Domain::Recurrence::WeekPosition)), m_model, SLOT(setByDayPosition(Domain::Recurrence::WeekPosition))); } void EditorView::onSaveNeededChanged(bool saveNeeded) { m_saveButton->setEnabled(saveNeeded); } void EditorView::onArtifactChanged() { auto artifact = m_model->property("artifact").value(); setEnabled(artifact); } void EditorView::onHasTaskPropertiesChanged() { m_taskGroup->setVisible(m_model->property("hasTaskProperties").toBool()); } void EditorView::onTextOrTitleChanged() { //We have to temporarilly disconnect these signals to avoid triggering a save when setting the content of the editor. disconnect(m_textEdit->editor(), SIGNAL(textChanged()), this, SLOT(onTextEditChanged())); disconnect(m_titleEdit, SIGNAL(editingFinished()), this, SLOT(onTextEditChanged())); const QString text = m_model->property("text").toString(); if (text != m_textEdit->editor()->toHtml()) m_textEdit->editor()->setText(text); const QString title = m_model->property("title").toString(); if (title != m_titleEdit->text()) m_titleEdit->setText(title); connect(m_textEdit->editor(), SIGNAL(textChanged()), this, SLOT(onTextEditChanged())); connect(m_titleEdit, SIGNAL(editingFinished()), this, SLOT(onTextEditChanged())); } void EditorView::onStartDateChanged() { m_startDateEdit->setDate(m_model->property("startDate").toDateTime().date()); m_startTimeEdit->setTime(m_model->property("startDate").toDateTime().time()); } void EditorView::onDueDateChanged() { m_dueDateEdit->setDate(m_model->property("dueDate").toDateTime().date()); m_dueTimeEdit->setTime(m_model->property("dueDate").toDateTime().time()); } void EditorView::onProgressChanged() { m_progressEdit->setValue(m_model->property("progress").toInt()); } void EditorView::onStatusChanged() { for (int i = 0; i < m_statusComboBox->count(); i++) { if (m_statusComboBox->itemData(i).toInt() == m_model->property("status").toInt()) { m_statusComboBox->setCurrentIndex(i); return; } } m_statusComboBox->setCurrentIndex(0); } void EditorView::onDelegateTextChanged() { const auto delegateText = m_model->property("delegateText").toString(); const auto labelText = delegateText.isEmpty() ? QString() : tr("Delegated to: %1").arg(delegateText); m_delegateLabel->setVisible(!labelText.isEmpty()); m_delegateLabel->setText(labelText); m_delegateEdit->clear(); } void EditorView::onRelationsChanged() { const auto relations = m_model->property("relations").value >(); for (auto widget : m_relationWidgets) { m_relationsLayout->removeWidget(widget); delete widget; } m_relationWidgets.clear(); for (auto relation : relations) { auto widget = new QWidget(this); auto layout = new QHBoxLayout(widget); widget->setLayout(layout); auto labelText = QString("%2").arg(relation->url().toString()).arg(relation->name()); auto label = new QLabel(widget); label->setTextInteractionFlags(Qt::LinksAccessibleByMouse); label->setTextFormat(Qt::RichText); label->setText(labelText); connect(label, SIGNAL(linkActivated(QString)), this, SLOT(onLinkActivated(QString))); layout->addWidget(label); auto button = new QPushButton(widget); button->setProperty("relation", QVariant::fromValue(relation)); button->setIcon(QIcon::fromTheme("list-remove")); connect(button, SIGNAL(clicked()), this, SLOT(onRemoveRelationClicked())); layout->addWidget(button); layout->addStretch(); m_relationsLayout->addWidget(widget); m_relationWidgets << widget; } } void EditorView::onTagsChanged() { const auto tags = m_model->property("tags").value(); for (auto widget : m_tagsWidgets) { m_tagsLayout->removeWidget(widget); delete widget; } m_tagsWidgets.clear(); for (const auto &tag : tags) { auto widget = new QWidget(this); auto layout = new QHBoxLayout(widget); widget->setLayout(layout); auto label = new QLabel(widget); label->setText(tag->name()); label->setProperty("tag", QVariant::fromValue(tag)); layout->addWidget(label); auto button = new QPushButton(widget); button->setProperty("tag", QVariant::fromValue(tag)); button->setIcon(QIcon::fromTheme("list-remove")); connect(button, SIGNAL(clicked()), this, SLOT(onRemoveTagClicked())); layout->addWidget(button); layout->addStretch(); m_tagsLayout->addWidget(widget); m_tagsWidgets << widget; } } void EditorView::onAttachmentsChanged() { const auto attachments = m_model->property("attachments").value(); for (auto widget : m_attachmentWidgets) { m_attachmentsLayout->removeWidget(widget); delete widget; } m_attachmentWidgets.clear(); for (const auto &attachment : attachments) { auto widget = new QWidget(this); auto layout = new QHBoxLayout(widget); widget->setLayout(layout); auto labelText = QString("%2").arg("attachmentDummyUrl").arg(attachment->label); auto label = new QLabel(widget); label->setTextInteractionFlags(Qt::LinksAccessibleByMouse); label->setTextFormat(Qt::RichText); label->setText(labelText); label->setProperty("attachment", QVariant::fromValue(attachment)); connect(label, SIGNAL(linkActivated(QString)), this, SLOT(onAttachmentLinkActivated(QString))); layout->addWidget(label); auto button = new QPushButton(widget); button->setProperty("attachment", QVariant::fromValue(attachment)); button->setIcon(QIcon::fromTheme("list-remove")); connect(button, SIGNAL(clicked()), this, SLOT(onRemoveAttachmentClicked())); layout->addWidget(button); layout->addStretch(); m_attachmentsLayout->addWidget(widget); m_attachmentWidgets << widget; } } void EditorView::onRecurrenceChanged() { const auto recurrence = m_model->property("recurrence").value(); m_recurrenceWidget->blockSignals(true); if (recurrence) { m_recurrenceWidget->setRecurrenceType(recurrence->frequency()); m_recurrenceWidget->setRecurrenceIntervall(recurrence->interval()); m_recurrenceWidget->setExceptionDateTimes(recurrence->exceptionDates()); m_recurrenceWidget->setByDayPosition(recurrence->byDayPosition()); m_recurrenceWidget->setByDay(recurrence->byday()); m_recurrenceWidget->setByMonth(recurrence->bymonth()); m_recurrenceWidget->setByMonthDay(recurrence->bymonthday()); if (recurrence->end().isValid()) { m_recurrenceWidget->setEnd(recurrence->end()); } else if (recurrence->count() >= 0) { m_recurrenceWidget->setEnd(recurrence->count()); } else if (recurrence->count() == -1) { m_recurrenceWidget->setNoEnd(); } } else { m_recurrenceWidget->clear(); } m_recurrenceWidget->blockSignals(false); if (recurrence && !m_statusComboBox->itemData(5).isValid()) { m_statusComboBox->addItem(tr("All ocurrences completed"), Domain::Task::FullComplete); onStatusChanged(); } else if (!recurrence && m_statusComboBox->itemData(5).isValid()) { m_statusComboBox->removeItem(5); } } void EditorView::onAlarmChanged() { const auto alarm = m_model->property("alarm").value(); if (!alarm) { m_alarmTypeWidget->setCurrentIndex(AlarmNone); onAlarmEnableChanged(AlarmNone); return; } switch(alarm->type()) { case Domain::Alarm::Display: m_alarmTypeWidget->setCurrentIndex(AlarmDisplay); onAlarmEnableChanged(AlarmDisplay); break; case Domain::Alarm::Audio: m_alarmTypeWidget->setCurrentIndex(AlarmAudio); onAlarmEnableChanged(AlarmAudio); break; } switch(alarm->position()) { case Domain::Alarm::Start: m_alarmPositionWidget->setCurrentIndex(AlarmStart); break; case Domain::Alarm::End: m_alarmPositionWidget->setCurrentIndex(AlarmEnd); break; } m_alarmOffsetEdit->setValue(-alarm->offset()); } void EditorView::onAlarmEnableChanged(int t) { AlarmType type = static_cast(t); bool enabled = (type != AlarmNone); m_alarmOffsetEdit->setEnabled(enabled); m_alarmPositionWidget->setEnabled(enabled); } void EditorView::onAlarmTypeChanged(int index) { emit alarmTypeChanged(m_alarmTypeWidget->itemData(index).toInt()); } void EditorView::onAlarmPositionChanged(int index) { emit alarmPositionChanged(m_alarmPositionWidget->itemData(index).toInt()); } void EditorView::onAlarmOffsetChanged(int offset) { emit alarmOffsetChanged(-offset); } void EditorView::onRemoveRelationClicked() { auto relation = sender()->property("relation").value(); QMetaObject::invokeMethod(m_model, "removeRelation", Q_ARG(Domain::Relation::Ptr, relation)); } void EditorView::onLinkActivated(const QString &link) { KRun::run("kmail --view %u", KUrl::List() << KUrl(link), 0); } void EditorView::onRemoveAttachmentClicked() { auto attachment = sender()->property("attachment").value(); QMetaObject::invokeMethod(m_model, "removeAttachment", Q_ARG(Domain::Artifact::Attachment::Ptr, attachment)); } void EditorView::onRemoveTagClicked() { auto tag = sender()->property("tag").value(); QMetaObject::invokeMethod(m_model, "removeTag", Q_ARG(Domain::Tag::Ptr, tag)); } void EditorView::onAddAttachmentClicked() { auto attachmentFileUrl = KFileDialog::getOpenUrl(KUrl(), QString(), 0, tr("Add Attachment")); auto attachment = Domain::Artifact::Attachment::Ptr::create(); attachment->label = attachmentFileUrl.fileName(KUrl::DirectoryOption::IgnoreTrailingSlash); auto mimeType = KMimeType::findByUrl(attachmentFileUrl); attachment->mimetype = mimeType->name(); QString tmpFile; if (KIO::NetAccess::download(attachmentFileUrl, tmpFile, this)) { QFile f(tmpFile); if (!f.open(QIODevice::ReadOnly)) { return; } QByteArray data = f.readAll(); f.close(); attachment->data = data; } KIO::NetAccess::removeTempFile(tmpFile); QMetaObject::invokeMethod(m_model, "addAttachment", Q_ARG(Domain::Artifact::Attachment::Ptr, attachment)); } KUrl tempFileForAttachment(const Domain::Artifact::Attachment::Ptr &attachment) { KTemporaryFile *file = new KTemporaryFile(); QStringList patterns = KMimeType::mimeType(attachment->mimetype)->patterns(); if (!patterns.empty()) { file->setSuffix(QString(patterns.first()).remove('*')); } file->setAutoRemove(true); file->open(); // read-only not to give the idea that it could be written to file->setPermissions(QFile::ReadUser); file->write(attachment->data); file->close(); return file->fileName(); } void EditorView::onAttachmentLinkActivated(const QString &link) { const auto attachment = sender()->property("attachment").value(); if (!attachment) { qWarning() << "Couldn't find attachment"; } QString saveAsFile = KFileDialog::getSaveFileName(attachment->label, QString(), 0, tr("Save Attachment")); if (saveAsFile.isEmpty() || (QFile(saveAsFile).exists() && (KMessageBox::warningYesNo(0,tr("%1 already exists. Do you want to overwrite it?").arg(saveAsFile)) == KMessageBox::No))) { return; } KUrl sourceUrl = tempFileForAttachment(attachment); // save the attachment url if (!KIO::NetAccess::file_copy(sourceUrl, KUrl(saveAsFile)) && KIO::NetAccess::lastError()) { KMessageBox::error(0, KIO::NetAccess::lastErrorString()); } } void EditorView::onTextEditChanged() { emit titleChanged(m_titleEdit->text()); emit textChanged(m_textEdit->editor()->toHtml()); } void EditorView::onStartEditEntered(const QDate &start) { emit startDateChanged(QDateTime(start, m_startTimeEdit->time())); } void EditorView::onStartTimeEntered(const QTime &start) { emit startDateChanged(QDateTime(m_startDateEdit->date() , start)); } void EditorView::onDueEditEntered(const QDate &due) { emit dueDateChanged(QDateTime(due, m_dueTimeEdit->time())); } void EditorView::onDueTimeEntered(const QTime &due) { emit dueDateChanged(QDateTime(m_dueDateEdit->date(), due)); } void EditorView::onStartTodayClicked() { QDate today(QDate::currentDate()); m_startDateEdit->setDate(today); emit startDateChanged(QDateTime(today)); } 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; } m_model->setProperty("sendMail", false); if (gotMatch) { QMetaObject::invokeMethod(m_model, "delegate", Q_ARG(QString, name), Q_ARG(QString, email)); } emit delegateChanged(name, email); emit save(); } void EditorView::onProgressChanged(int progress) { emit progressChanged(progress); } void EditorView::onStatusChanged(int index) { emit statusChanged(m_statusComboBox->itemData(index).toInt()); } void EditorView::onTagAdded() { QMetaObject::invokeMethod(m_model, "addTag", Q_ARG(QString, m_newTagWidget->text())); m_newTagWidget->clear(); } diff --git a/src/widgets/editorview.h b/src/widgets/editorview.h index 47337791..55a03f67 100644 --- a/src/widgets/editorview.h +++ b/src/widgets/editorview.h @@ -1,157 +1,155 @@ /* 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 QLineEdit; class QComboBox; class QSpinBox; class QVBoxLayout; class QHBoxLayout; class QListWidget; class QPushButton; class KLineEdit; class KTimeComboBox; namespace KPIM { class KDateEdit; } namespace Widgets { class RecurrenceWidget; class EditorWidget; class EditorView : public QWidget { Q_OBJECT public: explicit EditorView(QWidget *parent = 0); virtual ~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 delegateChanged(const QString &name, const QString &email); void progressChanged(int progress); void statusChanged(int status); void alarmTypeChanged(int type); void alarmPositionChanged(int position); void alarmOffsetChanged(int offset); void save(); private slots: void onArtifactChanged(); void onHasTaskPropertiesChanged(); void onTextOrTitleChanged(); void onStartDateChanged(); void onDueDateChanged(); void onDelegateTextChanged(); void onProgressChanged(); void onStatusChanged(); void onRelationsChanged(); void onTagsChanged(); void onAttachmentsChanged(); void onRecurrenceChanged(); void onTextEditChanged(); void onStartEditEntered(const QDate &start); void onDueEditEntered(const QDate &due); void onStartTodayClicked(); void onDelegateEntered(); void onProgressChanged(int progress); void onStatusChanged(int status); void onTagAdded(); void onLinkActivated(const QString &link); void onRemoveRelationClicked(); void onAttachmentLinkActivated(const QString &link); void onRemoveAttachmentClicked(); void onAddAttachmentClicked(); void onStartTimeEntered(const QTime &start); void onDueTimeEntered(const QTime &due); void onRemoveTagClicked(); void onAlarmChanged(); void onAlarmTypeChanged(int type); void onAlarmPositionChanged(int); void onAlarmOffsetChanged(int); void onAlarmEnableChanged(int); void onSaveNeededChanged(bool); - void toggleFullscreenEditor(); - private: void fillAlarmCombo(); QObject *m_model; QLabel *m_delegateLabel; QLineEdit *m_titleEdit; EditorWidget *m_textEdit; QWidget *m_taskGroup; KPIM::KDateEdit *m_startDateEdit; KTimeComboBox *m_startTimeEdit; KPIM::KDateEdit *m_dueDateEdit; KTimeComboBox *m_dueTimeEdit; QAbstractButton *m_startTodayButton; KLineEdit *m_delegateEdit; QComboBox *m_statusComboBox; QSpinBox *m_progressEdit; QList m_relationWidgets; QVBoxLayout *m_relationsLayout; QList m_attachmentWidgets; QVBoxLayout *m_attachmentsLayout; QList m_tagsWidgets; QHBoxLayout *m_tagsLayout; QAbstractButton *m_addAttachmentButton; RecurrenceWidget *m_recurrenceWidget; QComboBox *m_alarmTypeWidget; QComboBox *m_alarmPositionWidget; QSpinBox *m_alarmOffsetEdit; QLineEdit *m_newTagWidget; QPushButton *m_saveButton; }; } #endif // WIDGETS_EDITORVIEW_H diff --git a/src/widgets/editorwidget.cpp b/src/widgets/editorwidget.cpp index e2c96e0c..a6682497 100644 --- a/src/widgets/editorwidget.cpp +++ b/src/widgets/editorwidget.cpp @@ -1,177 +1,138 @@ /* This file is part of Zanshin Todo. Copyright 2011 Christian Mollekopf 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 "editorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Widgets; EditorWidget::EditorWidget(QWidget *parent) : QWidget(parent), m_editor(new KRichTextWidget(this)), - m_fullscreenButton(0), m_toolbar(0), m_defaultColor(palette().color(QPalette::Window)) { m_editor->setWordWrapMode(QTextOption::WordWrap); m_editor->setRichTextSupport( KRichTextWidget::FullTextFormattingSupport | KRichTextWidget::FullListSupport | KRichTextWidget::SupportAlignment | KRichTextWidget::SupportRuleLine | KRichTextWidget::SupportFormatPainting | KRichTextWidget::SupportHyperlinks ); /*QPalette pe = m_editor->palette(); pe.setColor(QPalette::Window, Qt::blue); m_editor->setPalette(pe); setAutoFillBackground(true);*/ QVBoxLayout *l = new QVBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); QHBoxLayout *h = new QHBoxLayout(this); h->setContentsMargins(0, 0, 0, 0); h->setSpacing(0); h->addStretch(); h->addWidget(m_editor, 1); h->addStretch(); l->addLayout(h); setLayout(l); setAutoFillBackground(true); auto actionCollection = new KActionCollection(0, KComponentData()); m_editor->createActions(actionCollection); //for the toolbar in this window { QHBoxLayout *l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); m_toolbar = new KToolBar("TextEditorToolbar", this); m_toolbar->addAction(actionCollection->action("format_text_bold")); m_toolbar->addAction(actionCollection->action("format_text_italic")); m_toolbar->addAction(actionCollection->action("format_text_underline")); m_toolbar->addAction(actionCollection->action("format_text_strikeout")); m_toolbar->addAction(actionCollection->action("format_text_foreground_color")); m_toolbar->addAction(actionCollection->action("format_text_background_color")); m_toolbar->addSeparator(); m_toolbar->addAction(actionCollection->action("format_list_style")); m_toolbar->addAction(actionCollection->action("format_list_indent_more")); m_toolbar->addAction(actionCollection->action("format_list_indent_less")); m_toolbar->addSeparator(); m_toolbar->addAction(actionCollection->action("format_font_family")); m_toolbar->addAction(actionCollection->action("format_font_size")); m_toolbar->addSeparator(); m_toolbar->addAction(actionCollection->action("format_painter")); m_toolbar->addAction(actionCollection->action("manage_link")); m_toolbar->addAction(actionCollection->action("insert_horizontal_rule")); m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); l->addWidget(m_toolbar); m_editActions = actionCollection->actions(); // KAction *action = actionCollection->addAction("hide_toolbar"); QAction *action = new QAction(this); action->setText(i18n("Hide &Toolbar")); action->setCheckable(true); action->setShortcut(QKeySequence(Qt::Key_F6)); connect(action, SIGNAL(triggered()), this, SLOT(toggleToolbarVisibility())); connect(this, SIGNAL(toolbarVisibilityToggled(bool)), action, SLOT(setChecked(bool))); addAction(action); - m_fullscreenButton = new QToolButton(this); - m_fullscreenButton->setArrowType(Qt::UpArrow); - connect(m_fullscreenButton, SIGNAL(clicked(bool)), this, SIGNAL(fullscreenToggled(bool))); - l->addWidget(m_fullscreenButton); static_cast(layout())->insertLayout(0,l); } } QList EditorWidget::editActions() const { return m_editActions; } -void EditorWidget::changeEvent(QEvent *event) -{ - if (!m_fullscreenButton) { - kWarning() << "not yet initialized"; - QWidget::changeEvent(event); - return; - } - if (event->type() & QEvent::WindowStateChange) { - QPalette p = palette(); - if (windowState() & Qt::WindowFullScreen) { - m_fullscreenButton->setArrowType(Qt::DownArrow); - p.setColor(QPalette::Window, Qt::white); - //The editor should use all available space, except in fullscreen mode where it should be like a piece of paper (to avoid having all the text on the left edge of the screen - m_editor->setMaximumWidth(1000); //FIXME calculate based on fontmetrics - } else { - m_fullscreenButton->setArrowType(Qt::UpArrow); - p.setColor(QPalette::Window, m_defaultColor); - m_editor->setMaximumWidth(QWIDGETSIZE_MAX); - } - setPalette(p); - } - QWidget::changeEvent(event); -} - void EditorWidget::toggleToolbarVisibility() { - m_fullscreenButton->setVisible(!m_toolbar->isVisible()); m_toolbar->setVisible(!m_toolbar->isVisible()); emit toolbarVisibilityToggled(!m_toolbar->isVisible()); } KRichTextWidget* EditorWidget::editor() { return m_editor; } -void EditorWidget::keyPressEvent(QKeyEvent *event) -{ - if ((event->key() == Qt::Key_Escape) && (windowState() & Qt::WindowFullScreen)) { - emit fullscreenToggled(false); - event->accept(); - } - QWidget::keyPressEvent(event); -} - diff --git a/src/widgets/editorwidget.h b/src/widgets/editorwidget.h index f75d355f..8f6b7f58 100644 --- a/src/widgets/editorwidget.h +++ b/src/widgets/editorwidget.h @@ -1,70 +1,64 @@ /* This file is part of Zanshin Todo. Copyright 2011 Christian Mollekopf 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 EDITORWIDGET_H #define EDITORWIDGET_H #include class KToolBar; class QToolButton; class KRichTextWidget; class KActionCollection; namespace Widgets { /** * An editorwidget, with the toolbox buttons */ class EditorWidget: public QWidget { Q_OBJECT public: EditorWidget(QWidget *parent = 0); KRichTextWidget *editor(); QList editActions() const; public slots: void toggleToolbarVisibility(); -protected: - virtual void changeEvent(QEvent* ); - virtual void keyPressEvent(QKeyEvent* ); - signals: - void fullscreenToggled(bool); void toolbarVisibilityToggled(bool); private: KRichTextWidget *m_editor; - QToolButton *m_fullscreenButton; KToolBar *m_toolbar; const QColor m_defaultColor; QList m_editActions; }; } #endif // EDITORWIDGET_H