diff --git a/src/attendeeline.cpp b/src/attendeeline.cpp index 8e2879b..1245e17 100644 --- a/src/attendeeline.cpp +++ b/src/attendeeline.cpp @@ -1,413 +1,413 @@ /* Copyright (C) 2010 Casey Link Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "attendeeline.h" #include "attendeedata.h" #include #include #include #include "incidenceeditor_debug.h" #include #include #include #include using namespace IncidenceEditorNG; typedef QPair TextIconPair; AttendeeComboBox::AttendeeComboBox(QWidget *parent) : QToolButton(parent) , mMenu(new QMenu(this)) , mCurrentIndex(-1) { setPopupMode(QToolButton::InstantPopup); setToolButtonStyle(Qt::ToolButtonIconOnly); setMenu(mMenu); } void AttendeeComboBox::addItem(const QIcon &icon, const QString &text) { mList.append(TextIconPair(text, icon)); if (mCurrentIndex == -1) { setCurrentIndex(0); } int index = mList.size() - 1; QAction *act = menu()->addAction(icon, text, this, &AttendeeComboBox::slotActionTriggered); act->setData(index); } void AttendeeComboBox::addItems(const QStringList &texts) { for (const QString &str : texts) { addItem(QIcon(), str); } if (mCurrentIndex == -1) { setCurrentIndex(0); } } int AttendeeComboBox::currentIndex() const { return mCurrentIndex; } void AttendeeComboBox::clear() { mCurrentIndex = -1; mMenu->clear(); mList.clear(); } void AttendeeComboBox::setCurrentIndex(int index) { Q_ASSERT(index < mList.size()); const int old = mCurrentIndex; mCurrentIndex = index; setIcon(mList.at(index).second); setToolTip(mList.at(index).first); if (old != index) { Q_EMIT itemChanged(); } } void AttendeeComboBox::slotActionTriggered() { int index = qobject_cast(sender())->data().toInt(); setCurrentIndex(index); } void AttendeeComboBox::keyPressEvent(QKeyEvent *ev) { if (ev->key() == Qt::Key_Left) { Q_EMIT leftPressed(); } else if (ev->key() == Qt::Key_Right) { Q_EMIT rightPressed(); } else if (!mMenu->isVisible() && ( ev->key() == Qt::Key_Down || ev->key() == Qt::Key_Space)) { showMenu(); } else { QToolButton::keyPressEvent(ev); } } AttendeeLineEdit::AttendeeLineEdit(QWidget *parent) : AddresseeLineEdit(parent, true) { } void AttendeeLineEdit::keyPressEvent(QKeyEvent *ev) { if ((ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) && !completionBox()->isVisible()) { Q_EMIT downPressed(); KPIM::AddresseeLineEdit::keyPressEvent(ev); } else if (ev->key() == Qt::Key_Backspace && text().isEmpty()) { ev->accept(); Q_EMIT deleteMe(); } else if (ev->key() == Qt::Key_Left && cursorPosition() == 0 && !ev->modifiers().testFlag(Qt::ShiftModifier)) { // Shift would be pressed during selection Q_EMIT leftPressed(); } else if (ev->key() == Qt::Key_Right && cursorPosition() == text().length() && !ev->modifiers().testFlag(Qt::ShiftModifier)) { // Shift would be pressed during selection Q_EMIT rightPressed(); } else if (ev->key() == Qt::Key_Down) { Q_EMIT downPressed(); } else if (ev->key() == Qt::Key_Up) { Q_EMIT upPressed(); } else { KPIM::AddresseeLineEdit::keyPressEvent(ev); } } AttendeeLine::AttendeeLine(QWidget *parent) : MultiplyingLine(parent) , mRoleCombo(new AttendeeComboBox(this)) , mStateCombo(new AttendeeComboBox(this)) , mResponseCombo(new AttendeeComboBox(this)) , mEdit(new AttendeeLineEdit(this)) , mData(new AttendeeData(QString(), QString())) , mModified(false) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QBoxLayout *topLayout = new QHBoxLayout(this); - topLayout->setMargin(0); + topLayout->setContentsMargins(0, 0, 0, 0); mRoleCombo->addItem(QIcon::fromTheme(QStringLiteral("meeting-participant")), KCalUtils::Stringify::attendeeRole(KCalCore::Attendee::ReqParticipant)); mRoleCombo->addItem(QIcon::fromTheme(QStringLiteral("meeting-participant-optional")), KCalUtils::Stringify::attendeeRole(KCalCore::Attendee::OptParticipant)); mRoleCombo->addItem(QIcon::fromTheme(QStringLiteral("meeting-observer")), KCalUtils::Stringify::attendeeRole(KCalCore::Attendee::NonParticipant)); mRoleCombo->addItem(QIcon::fromTheme(QStringLiteral("meeting-chair")), KCalUtils::Stringify::attendeeRole(KCalCore::Attendee::Chair)); mResponseCombo->addItem(QIcon::fromTheme(QStringLiteral( "meeting-participant-request-response")), i18nc("@item:inlistbox", "Request Response")); mResponseCombo->addItem(QIcon::fromTheme(QStringLiteral("meeting-participant-no-response")), i18nc("@item:inlistbox", "Request No Response")); mEdit->setToolTip(i18nc("@info:tooltip", "Enter the name or email address of the attendee.")); mEdit->setClearButtonEnabled(true); mStateCombo->setWhatsThis(i18nc("@info:whatsthis", "Edits the current attendance status of the attendee.")); mRoleCombo->setWhatsThis(i18nc("@info:whatsthis", "Edits the role of the attendee.")); mEdit->setWhatsThis(i18nc("@info:whatsthis", "The email address or name of the attendee. An invitation " "can be sent to the user if an email address is provided.")); setActions(EventActions); mResponseCombo->setToolTip(i18nc("@info:tooltip", "Request a response from the attendee")); mResponseCombo->setWhatsThis(i18nc("@info:whatsthis", "Edits whether to send an email to the " "attendee to request a response concerning " "attendance.")); // add them to the layout in the correct order topLayout->addWidget(mRoleCombo); topLayout->addWidget(mEdit); topLayout->addWidget(mStateCombo); topLayout->addWidget(mResponseCombo); connect(mEdit, &AttendeeLineEdit::returnPressed, this, &AttendeeLine::slotReturnPressed); connect(mEdit, &AttendeeLineEdit::deleteMe, this, &AttendeeLine::slotPropagateDeletion, Qt::QueuedConnection); connect(mEdit, &AttendeeLineEdit::textChanged, this, &AttendeeLine::slotTextChanged, Qt::QueuedConnection); connect(mEdit, &AttendeeLineEdit::upPressed, this, &AttendeeLine::slotFocusUp); connect(mEdit, &AttendeeLineEdit::downPressed, this, &AttendeeLine::slotFocusDown); connect(mRoleCombo, &AttendeeComboBox::rightPressed, mEdit, static_cast(&AttendeeLineEdit::setFocus)); connect(mEdit, &AttendeeLineEdit::leftPressed, mRoleCombo, static_cast(&AttendeeComboBox::setFocus)); connect(mEdit, &AttendeeLineEdit::rightPressed, mStateCombo, static_cast(&AttendeeComboBox::setFocus)); connect(mStateCombo, &AttendeeComboBox::leftPressed, mEdit, static_cast(&AttendeeLineEdit::setFocus)); connect(mStateCombo, &AttendeeComboBox::rightPressed, mResponseCombo, static_cast(&AttendeeComboBox::setFocus)); connect(mResponseCombo, &AttendeeComboBox::leftPressed, mStateCombo, static_cast(&AttendeeComboBox::setFocus)); connect(mResponseCombo, &AttendeeComboBox::rightPressed, this, &AttendeeLine::rightPressed); connect(mEdit, &AttendeeLineEdit::editingFinished, this, &AttendeeLine::slotHandleChange, Qt::QueuedConnection); connect(mEdit, &AttendeeLineEdit::textCompleted, this, &AttendeeLine::slotHandleChange, Qt::QueuedConnection); connect(mEdit, &AttendeeLineEdit::clearButtonClicked, this, &AttendeeLine::slotPropagateDeletion, Qt::QueuedConnection); connect(mRoleCombo, &AttendeeComboBox::itemChanged, this, &AttendeeLine::slotComboChanged); connect(mStateCombo, &AttendeeComboBox::itemChanged, this, &AttendeeLine::slotComboChanged); connect(mResponseCombo, &AttendeeComboBox::itemChanged, this, &AttendeeLine::slotComboChanged); } void AttendeeLine::activate() { mEdit->setFocus(); } void AttendeeLine::clear() { mEdit->clear(); mRoleCombo->setCurrentIndex(0); mStateCombo->setCurrentIndex(0); mResponseCombo->setCurrentIndex(0); mUid.clear(); } void AttendeeLine::clearModified() { mModified = false; mEdit->setModified(false); } KPIM::MultiplyingLineData::Ptr AttendeeLine::data() const { if (isModified()) { const_cast(this)->dataFromFields(); } return mData; } void AttendeeLine::dataFromFields() { if (!mData) { return; } KCalCore::Attendee::Ptr oldAttendee(mData->attendee()); QString email, name; KEmailAddress::extractEmailAddressAndName(mEdit->text(), email, name); mData->setName(name); mData->setEmail(email); mData->setRole(AttendeeData::Role(mRoleCombo->currentIndex())); mData->setStatus(AttendeeData::PartStat(mStateCombo->currentIndex())); mData->setRSVP(mResponseCombo->currentIndex() == 0); mData->setUid(mUid); clearModified(); if (!(oldAttendee == mData->attendee()) && !email.isEmpty()) { // if email is empty, we don't want to update anything qCDebug(INCIDENCEEDITOR_LOG) << oldAttendee->email() << mData->email(); Q_EMIT changed(oldAttendee, mData->attendee()); } } void AttendeeLine::fieldsFromData() { if (!mData) { return; } mEdit->setText(mData->fullName()); mRoleCombo->setCurrentIndex(mData->role()); AttendeeData::PartStat partStat = mData->status(); if (partStat != AttendeeData::None) { mStateCombo->setCurrentIndex(partStat); } else { mStateCombo->setCurrentIndex(AttendeeData::NeedsAction); } mResponseCombo->setCurrentIndex(mData->RSVP() ? 0 : 1); mUid = mData->uid(); } void AttendeeLine::fixTabOrder(QWidget *previous) { setTabOrder(previous, mRoleCombo); setTabOrder(mRoleCombo, mEdit); setTabOrder(mEdit, mStateCombo); setTabOrder(mStateCombo, mResponseCombo); } QWidget *AttendeeLine::tabOut() const { return mResponseCombo; } bool AttendeeLine::isActive() const { return mEdit->hasFocus(); } bool AttendeeLine::isEmpty() const { return mEdit->text().isEmpty(); } bool AttendeeLine::isModified() const { return mModified || mEdit->isModified(); } int AttendeeLine::setColumnWidth(int w) { w = qMax(w, mRoleCombo->sizeHint().width()); mRoleCombo->setFixedWidth(w); mRoleCombo->updateGeometry(); parentWidget()->updateGeometry(); return w; } void AttendeeLine::setActions(AttendeeActions actions) { mStateCombo->clear(); } void AttendeeLine::setCompletionMode(KCompletion::CompletionMode mode) { mEdit->setCompletionMode(mode); } void AttendeeLine::setData(const KPIM::MultiplyingLineData::Ptr &data) { AttendeeData::Ptr attendee = qSharedPointerDynamicCast(data); if (!attendee) { return; } mData = attendee; fieldsFromData(); } void AttendeeLine::slotHandleChange() { if (mEdit->text().isEmpty()) { Q_EMIT deleteLine(this); } else { // has bad side-effects, and I have no idea what this was supposed to be doing // mEdit->setCursorPosition( 0 ); Q_EMIT editingFinished(this); dataFromFields(); } } void AttendeeLine::slotTextChanged(const QString &str) { Q_UNUSED(str); mModified = true; Q_EMIT changed(); // TODO: This doesn't seem connected to anywhere in incidenceattendee.cpp. // but the important code is run in slotHandleChange() anyway so we don't see any bug } void AttendeeLine::slotComboChanged() { mModified = true; // If mUid is empty, we're still populating the widget, don't write fields to data yet if (!mUid.isEmpty()) { dataFromFields(); } } void AttendeeLine::aboutToBeDeleted() { if (!mData) { return; } Q_EMIT changed(mData->attendee(), KCalCore::Attendee::Ptr(new KCalCore::Attendee(QLatin1String(""), QLatin1String("")))); } bool AttendeeLine::canDeleteLineEdit() const { return mEdit->canDeleteLineEdit(); } diff --git a/src/categorydialog.cpp b/src/categorydialog.cpp index 47d0f78..f934d7f 100644 --- a/src/categorydialog.cpp +++ b/src/categorydialog.cpp @@ -1,325 +1,325 @@ /* Copyright (c) 2000, 2001, 2002 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "categorydialog.h" #include "categoryhierarchyreader.h" #include "ui_categorydialog_base.h" #include #include #include #include #include #include using namespace IncidenceEditorNG; using namespace CalendarSupport; class CategoryWidgetBase : public QWidget, public Ui::CategoryDialog_base { public: CategoryWidgetBase(QWidget *parent) : QWidget(parent) { setupUi(this); } }; CategoryWidget::CategoryWidget(CategoryConfig *cc, QWidget *parent) : QWidget(parent) , mCategoryConfig(cc) { QHBoxLayout *topL = new QHBoxLayout(this); - topL->setMargin(0); + topL->setContentsMargins(0, 0, 0, 0); mWidgets = new CategoryWidgetBase(this); topL->addWidget(mWidgets); mWidgets->mButtonAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); mWidgets->mButtonRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); mWidgets->mLineEdit->setPlaceholderText(i18n("Click to add a new category")); connect(mWidgets->mLineEdit, &QLineEdit::textChanged, this, &CategoryWidget::handleTextChanged); mWidgets->mButtonAdd->setEnabled(false); mWidgets->mButtonRemove->setEnabled(false); mWidgets->mColorCombo->setEnabled(false); connect(mWidgets->mCategories, &QTreeWidget::itemSelectionChanged, this, &CategoryWidget::handleSelectionChanged); connect(mWidgets->mButtonAdd, &QAbstractButton::clicked, this, &CategoryWidget::addCategory); connect(mWidgets->mButtonRemove, &QAbstractButton::clicked, this, &CategoryWidget::removeCategory); connect(mWidgets->mColorCombo, &KColorCombo::activated, this, &CategoryWidget::handleColorChanged); } CategoryWidget::~CategoryWidget() { } AutoCheckTreeWidget *CategoryWidget::listView() const { return mWidgets->mCategories; } void CategoryWidget::hideButton() { } void CategoryWidget::setCategories(const QStringList &categoryList) { mWidgets->mCategories->clear(); mCategoryList.clear(); QStringList cats = mCategoryConfig->customCategories(); for (QStringList::ConstIterator it = categoryList.begin(), end = categoryList.end(); it != end; ++it) { if (!cats.contains(*it)) { cats.append(*it); } } mCategoryConfig->setCustomCategories(cats); CategoryHierarchyReaderQTreeWidget(mWidgets->mCategories).read(cats); } void CategoryWidget::setSelected(const QStringList &selList) { clear(); const bool remAutoCheckChildren = mWidgets->mCategories->autoCheckChildren(); mWidgets->mCategories->setAutoCheckChildren(false); for (QStringList::ConstIterator it = selList.begin(), end = selList.end(); it != end; ++it) { QStringList path = CategoryHierarchyReader::path(*it); QTreeWidgetItem *item = mWidgets->mCategories->itemByPath(path); if (item) { item->setCheckState(0, Qt::Checked); } } mWidgets->mCategories->setAutoCheckChildren(remAutoCheckChildren); } static QStringList getSelectedCategories(AutoCheckTreeWidget *categoriesView) { QStringList categories; QTreeWidgetItemIterator it(categoriesView, QTreeWidgetItemIterator::Checked); while (*it) { QStringList path = categoriesView->pathByItem(*it++); if (path.count()) { path.replaceInStrings(CategoryConfig::categorySeparator, QLatin1Char('\\') +CategoryConfig::categorySeparator); categories.append(path.join(CategoryConfig::categorySeparator)); } } return categories; } void CategoryWidget::clear() { const bool remAutoCheckChildren = mWidgets->mCategories->autoCheckChildren(); mWidgets->mCategories->setAutoCheckChildren(false); QTreeWidgetItemIterator it(mWidgets->mCategories); while (*it) { (*it++)->setCheckState(0, Qt::Unchecked); } mWidgets->mCategories->setAutoCheckChildren(remAutoCheckChildren); } void CategoryWidget::setAutoselectChildren(bool autoselectChildren) { mWidgets->mCategories->setAutoCheckChildren(autoselectChildren); } void CategoryWidget::hideHeader() { mWidgets->mCategories->header()->hide(); } QStringList CategoryWidget::selectedCategories(QString &categoriesStr) { mCategoryList = getSelectedCategories(listView()); categoriesStr = mCategoryList.join(QStringLiteral(", ")); return mCategoryList; } QStringList CategoryWidget::selectedCategories() const { return mCategoryList; } void CategoryWidget::setCategoryList(const QStringList &categories) { mCategoryList = categories; } void CategoryWidget::addCategory() { QTreeWidgetItem *newItem = new QTreeWidgetItem(listView(), QStringList(mWidgets->mLineEdit->text())); listView()->scrollToItem(newItem); listView()->clearSelection(); newItem->setSelected(true); } void CategoryWidget::removeCategory() { // Multi-select not supported, only one selected QTreeWidgetItem *itemToDelete = listView()->selectedItems().first(); delete itemToDelete; } void CategoryWidget::handleTextChanged(const QString &newText) { mWidgets->mButtonAdd->setEnabled(!newText.isEmpty()); } void CategoryWidget::handleSelectionChanged() { const bool hasSelection = !listView()->selectedItems().isEmpty(); mWidgets->mButtonRemove->setEnabled(hasSelection); mWidgets->mColorCombo->setEnabled(hasSelection); if (hasSelection) { const QTreeWidgetItem *item = listView()->selectedItems().first(); const QColor &color = KCalPrefs::instance()->categoryColor(item->text(0)); if (color.isValid()) { mWidgets->mColorCombo->setColor(color); // update is needed. bug in KColorCombo? mWidgets->mColorCombo->update(); } } } void CategoryWidget::handleColorChanged(const QColor &newColor) { if (!listView()->selectedItems().isEmpty()) { const QTreeWidgetItem *item = listView()->selectedItems().first(); const QString category = item->text(0); if (newColor.isValid()) { KCalPrefs::instance()->setCategoryColor(category, newColor); } } } CategoryDialog::CategoryDialog(CategoryConfig *cc, QWidget *parent) : QDialog(parent) , d(nullptr) { setWindowTitle(i18n("Select Categories")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel /*| QDialogButtonBox::Help*/ | QDialogButtonBox::Apply, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &CategoryDialog::reject); QWidget *page = new QWidget; mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); QVBoxLayout *lay = new QVBoxLayout(page); - lay->setMargin(0); + lay->setContentsMargins(0, 0, 0, 0); mWidgets = new CategoryWidget(cc, this); mCategoryConfig = cc; mWidgets->setObjectName(QStringLiteral("CategorySelection")); mWidgets->hideHeader(); lay->addWidget(mWidgets); mWidgets->setCategories(); mWidgets->listView()->setFocus(); connect(okButton, &QPushButton::clicked, this, &CategoryDialog::slotOk); connect(buttonBox->button( QDialogButtonBox::Apply), &QPushButton::clicked, this, &CategoryDialog::slotApply); } CategoryDialog::~CategoryDialog() { delete mWidgets; } QStringList CategoryDialog::selectedCategories() const { return mWidgets->selectedCategories(); } void CategoryDialog::slotApply() { QStringList l; QStringList path; QTreeWidgetItemIterator it(mWidgets->listView()); while (*it) { path = mWidgets->listView()->pathByItem(*it++); path.replaceInStrings( CategoryConfig::categorySeparator, QLatin1Char('\\') + CategoryConfig::categorySeparator); l.append(path.join(CategoryConfig::categorySeparator)); } mCategoryConfig->setCustomCategories(l); mCategoryConfig->writeConfig(); QString categoriesStr; QStringList categories = mWidgets->selectedCategories(categoriesStr); Q_EMIT categoriesSelected(categories); Q_EMIT categoriesSelected(categoriesStr); } void CategoryDialog::slotOk() { slotApply(); accept(); } void CategoryDialog::updateCategoryConfig() { QString tmp; QStringList selected = mWidgets->selectedCategories(tmp); mWidgets->setCategories(); mWidgets->setSelected(selected); } void CategoryDialog::setAutoselectChildren(bool autoselectChildren) { mWidgets->setAutoselectChildren(autoselectChildren); } void CategoryDialog::setCategoryList(const QStringList &categories) { mWidgets->setCategoryList(categories); } void CategoryDialog::setSelected(const QStringList &selList) { mWidgets->setSelected(selList); } diff --git a/src/categoryselectdialog.cpp b/src/categoryselectdialog.cpp index 8ee98fc..07eb04e 100644 --- a/src/categoryselectdialog.cpp +++ b/src/categoryselectdialog.cpp @@ -1,247 +1,247 @@ /* Copyright (c) 2000, 2001, 2002 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "categoryselectdialog.h" #include "categoryhierarchyreader.h" #include "ui_categoryselectdialog_base.h" #include #include #include #include #include #include using namespace IncidenceEditorNG; using namespace CalendarSupport; class CategorySelectWidgetBase : public QWidget, public Ui::CategorySelectDialog_base { public: CategorySelectWidgetBase(QWidget *parent) : QWidget(parent) { setupUi(this); mButtonClear->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-rtl"))); mButtonEdit->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); } }; CategorySelectWidget::CategorySelectWidget(CategoryConfig *cc, QWidget *parent) : QWidget(parent) , mCategoryConfig(cc) { QHBoxLayout *topL = new QHBoxLayout(this); - topL->setMargin(0); + topL->setContentsMargins(0, 0, 0, 0); mWidgets = new CategorySelectWidgetBase(this); topL->addWidget(mWidgets); connect(mWidgets->mButtonEdit, &QAbstractButton::clicked, this, &CategorySelectWidget::editCategories); connect(mWidgets->mButtonClear, &QAbstractButton::clicked, this, &CategorySelectWidget::clear); } CategorySelectWidget::~CategorySelectWidget() { } AutoCheckTreeWidget *CategorySelectWidget::listView() const { return mWidgets->mCategories; } void CategorySelectWidget::hideButton() { mWidgets->mButtonEdit->hide(); mWidgets->mButtonClear->hide(); } void CategorySelectWidget::setCategories(const QStringList &categoryList) { mWidgets->mCategories->clear(); mCategoryList.clear(); QStringList cats = mCategoryConfig->customCategories(); for (QStringList::ConstIterator it = categoryList.begin(), end = categoryList.end(); it != end; ++it) { if (!cats.contains(*it)) { cats.append(*it); } } mCategoryConfig->setCustomCategories(cats); CategoryHierarchyReaderQTreeWidget(mWidgets->mCategories).read(cats); } void CategorySelectWidget::setSelected(const QStringList &selList) { clear(); bool remAutoCheckChildren = mWidgets->mCategories->autoCheckChildren(); mWidgets->mCategories->setAutoCheckChildren(false); for (QStringList::ConstIterator it = selList.begin(), end = selList.end(); it != end; ++it) { QStringList path = CategoryHierarchyReader::path(*it); QTreeWidgetItem *item = mWidgets->mCategories->itemByPath(path); if (item) { item->setCheckState(0, Qt::Checked); } } mWidgets->mCategories->setAutoCheckChildren(remAutoCheckChildren); } static QStringList getSelectedCategoriesFromCategoriesView(AutoCheckTreeWidget *categoriesView) { QStringList categories; QTreeWidgetItemIterator it(categoriesView, QTreeWidgetItemIterator::Checked); while (*it) { QStringList path = categoriesView->pathByItem(*it++); if (path.count()) { path.replaceInStrings(CategoryConfig::categorySeparator, QLatin1Char('\\') +CategoryConfig::categorySeparator); categories.append(path.join(CategoryConfig::categorySeparator)); } } return categories; } void CategorySelectWidget::clear() { bool remAutoCheckChildren = mWidgets->mCategories->autoCheckChildren(); mWidgets->mCategories->setAutoCheckChildren(false); QTreeWidgetItemIterator it(mWidgets->mCategories); while (*it) { (*it++)->setCheckState(0, Qt::Unchecked); } mWidgets->mCategories->setAutoCheckChildren(remAutoCheckChildren); } void CategorySelectWidget::setAutoselectChildren(bool autoselectChildren) { mWidgets->mCategories->setAutoCheckChildren(autoselectChildren); } void CategorySelectWidget::hideHeader() { mWidgets->mCategories->header()->hide(); } QStringList CategorySelectWidget::selectedCategories(QString &categoriesStr) { mCategoryList = getSelectedCategoriesFromCategoriesView(listView()); categoriesStr = mCategoryList.join(QStringLiteral(", ")); return mCategoryList; } QStringList CategorySelectWidget::selectedCategories() const { return mCategoryList; } void CategorySelectWidget::setCategoryList(const QStringList &categories) { mCategoryList = categories; } CategorySelectDialog::CategorySelectDialog(CategoryConfig *cc, QWidget *parent) : QDialog(parent) , d(nullptr) { setWindowTitle(i18n("Select Categories")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel /*| QDialogButtonBox::Help*/ | QDialogButtonBox::Apply, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &CategorySelectDialog::reject); QWidget *page = new QWidget; QVBoxLayout *lay = new QVBoxLayout(page); - lay->setMargin(0); + lay->setContentsMargins(0, 0, 0, 0); mWidgets = new CategorySelectWidget(cc, this); mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); mWidgets->setObjectName(QStringLiteral("CategorySelection")); mWidgets->hideHeader(); lay->addWidget(mWidgets); mWidgets->setCategories(); mWidgets->listView()->setFocus(); connect(mWidgets, &CategorySelectWidget::editCategories, this, &CategorySelectDialog::editCategories); connect(okButton, &QPushButton::clicked, this, &CategorySelectDialog::slotOk); connect(buttonBox->button( QDialogButtonBox::Apply), &QPushButton::clicked, this, &CategorySelectDialog::slotApply); } CategorySelectDialog::~CategorySelectDialog() { delete mWidgets; } QStringList CategorySelectDialog::selectedCategories() const { return mWidgets->selectedCategories(); } void CategorySelectDialog::slotApply() { QString categoriesStr; QStringList categories = mWidgets->selectedCategories(categoriesStr); Q_EMIT categoriesSelected(categories); Q_EMIT categoriesSelected(categoriesStr); } void CategorySelectDialog::slotOk() { slotApply(); accept(); } void CategorySelectDialog::updateCategoryConfig() { QString tmp; QStringList selected = mWidgets->selectedCategories(tmp); mWidgets->setCategories(); mWidgets->setSelected(selected); } void CategorySelectDialog::setAutoselectChildren(bool autoselectChildren) { mWidgets->setAutoselectChildren(autoselectChildren); } void CategorySelectDialog::setCategoryList(const QStringList &categories) { mWidgets->setCategoryList(categories); } void CategorySelectDialog::setSelected(const QStringList &selList) { mWidgets->setSelected(selList); } diff --git a/src/freebusyurldialog.cpp b/src/freebusyurldialog.cpp index cf87f42..2e23569 100644 --- a/src/freebusyurldialog.cpp +++ b/src/freebusyurldialog.cpp @@ -1,115 +1,115 @@ /* Copyright (c) 2004 Cornelius Schumacher 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) any later version. 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. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "freebusyurldialog.h" #include #include #include #include "incidenceeditor_debug.h" #include #include #include #include #include #include #include #include using namespace IncidenceEditorNG; FreeBusyUrlDialog::FreeBusyUrlDialog(const AttendeeData::Ptr &attendee, QWidget *parent) : QDialog(parent) { setModal(true); setWindowTitle(i18n("Edit Free/Busy Location")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QFrame *topFrame = new QFrame(this); QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); mainLayout->addWidget(topFrame); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &FreeBusyUrlDialog::reject); mainLayout->addWidget(buttonBox); okButton->setDefault(true); QBoxLayout *topLayout = new QVBoxLayout(topFrame); - topLayout->setMargin(0); + topLayout->setContentsMargins(0, 0, 0, 0); mWidget = new FreeBusyUrlWidget(attendee, topFrame); topLayout->addWidget(mWidget); mWidget->loadConfig(); connect(okButton, &QPushButton::clicked, this, &FreeBusyUrlDialog::slotOk); } void FreeBusyUrlDialog::slotOk() { mWidget->saveConfig(); accept(); } FreeBusyUrlWidget::FreeBusyUrlWidget(const AttendeeData::Ptr &attendee, QWidget *parent) : QWidget(parent) , mAttendee(attendee) { QBoxLayout *topLayout = new QVBoxLayout(this); QLabel *label = new QLabel(xi18n("Location of Free/Busy information for %1 %2:", mAttendee->name(), mAttendee->email()), this); topLayout->addWidget(label); mUrlEdit = new KLineEdit(this); mUrlEdit->setFocus(); mUrlEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the location of the Free/Busy information for the attendee.")); mUrlEdit->setToolTip(i18nc("@info:tooltip", "Enter the location of the information.")); topLayout->addWidget(mUrlEdit); } FreeBusyUrlWidget::~FreeBusyUrlWidget() { } static QString freeBusyUrlStore() { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/korganizer/freebusyurls"); } void FreeBusyUrlWidget::loadConfig() { KConfig config(freeBusyUrlStore()); mUrlEdit->setText(config.group(mAttendee->email()).readEntry("url")); } void FreeBusyUrlWidget::saveConfig() { const QString url = mUrlEdit->text(); KConfig config(freeBusyUrlStore()); config.group(mAttendee->email()).writeEntry("url", url); } diff --git a/src/incidenceattachment.cpp b/src/incidenceattachment.cpp index a389a87..5b9acee 100644 --- a/src/incidenceattachment.cpp +++ b/src/incidenceattachment.cpp @@ -1,621 +1,621 @@ /* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidenceattachment.h" #include "attachmenteditdialog.h" #include "attachmenticonview.h" #include "ui_dialogdesktop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace IncidenceEditorNG; IncidenceAttachment::IncidenceAttachment(Ui::EventOrTodoDesktop *ui) : IncidenceEditor(nullptr) , mUi(ui) , mPopupMenu(new QMenu) { setupActions(); setupAttachmentIconView(); setObjectName(QStringLiteral("IncidenceAttachment")); connect(mUi->mAddButton, &QPushButton::clicked, this, &IncidenceAttachment::addAttachment); connect(mUi->mRemoveButton, &QPushButton::clicked, this, &IncidenceAttachment::removeSelectedAttachments); } IncidenceAttachment::~IncidenceAttachment() { delete mPopupMenu; } void IncidenceAttachment::load(const KCalCore::Incidence::Ptr &incidence) { mLoadedIncidence = incidence; mAttachmentView->clear(); KCalCore::Attachment::List attachments = incidence->attachments(); for (KCalCore::Attachment::List::ConstIterator it = attachments.constBegin(), end = attachments.constEnd(); it != end; ++it) { new AttachmentIconItem((*it), mAttachmentView); } mWasDirty = false; } void IncidenceAttachment::save(const KCalCore::Incidence::Ptr &incidence) { incidence->clearAttachments(); for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); AttachmentIconItem *attitem = dynamic_cast(item); Q_ASSERT(item); incidence->addAttachment( KCalCore::Attachment::Ptr(new KCalCore::Attachment(*(attitem->attachment())))); } } bool IncidenceAttachment::isDirty() const { if (mLoadedIncidence) { if (mAttachmentView->count() != mLoadedIncidence->attachments().count()) { return true; } KCalCore::Attachment::List origAttachments = mLoadedIncidence->attachments(); for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); Q_ASSERT(dynamic_cast(item)); const KCalCore::Attachment::Ptr listAttachment = static_cast(item)->attachment(); for (int i = 0; i < origAttachments.count(); ++i) { const KCalCore::Attachment::Ptr attachment = origAttachments.at(i); if (*attachment == *listAttachment) { origAttachments.remove(i); break; } } } // All attachments are removed from the list, meaning, the items in mAttachmentView // are equal to the attachments set on mLoadedIncidence. return !origAttachments.isEmpty(); } else { // No incidence loaded, so if the user added attachments we're dirty. return mAttachmentView->count() != 0; } } int IncidenceAttachment::attachmentCount() const { return mAttachmentView->count(); } /// Private slots void IncidenceAttachment::addAttachment() { QPointer that(this); AttachmentIconItem *item = new AttachmentIconItem(KCalCore::Attachment::Ptr(), mAttachmentView); QPointer dialog(new AttachmentEditDialog(item, mAttachmentView)); dialog->setWindowTitle(i18nc("@title", "Add Attachment")); auto dialogResult = dialog->exec(); if (!that) { return; } if (dialogResult == QDialog::Rejected) { delete item; } else { Q_EMIT attachmentCountChanged(mAttachmentView->count()); } delete dialog; checkDirtyStatus(); } void IncidenceAttachment::copyToClipboard() { #ifndef QT_NO_CLIPBOARD QApplication::clipboard()->setMimeData(mAttachmentView->mimeData(), QClipboard::Clipboard); #endif } void IncidenceAttachment::openURL(const QUrl &url) { QString uri = url.url(); UriHandler::process(uri); } void IncidenceAttachment::pasteFromClipboard() { #ifndef QT_NO_CLIPBOARD handlePasteOrDrop(QApplication::clipboard()->mimeData()); #endif } void IncidenceAttachment::removeSelectedAttachments() { QList selected; QStringList labels; for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *it = mAttachmentView->item(itemIndex); if (it->isSelected()) { AttachmentIconItem *attitem = static_cast(it); if (attitem) { KCalCore::Attachment::Ptr att = attitem->attachment(); labels << att->label(); selected << it; } } } if (selected.isEmpty()) { return; } QString labelsStr = labels.join(QStringLiteral("")); if (KMessageBox::questionYesNo( nullptr, xi18nc("@info", "Do you really want to remove these attachments?%1", labelsStr), i18nc("@title:window", "Remove Attachments?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("calendarRemoveAttachments")) != KMessageBox::Yes) { return; } for (QList::iterator it(selected.begin()), end(selected.end()); it != end; ++it) { int row = mAttachmentView->row(*it); QListWidgetItem *next = mAttachmentView->item(++row); QListWidgetItem *prev = mAttachmentView->item(--row); if (next) { next->setSelected(true); } else if (prev) { prev->setSelected(true); } delete *it; } mAttachmentView->update(); Q_EMIT attachmentCountChanged(mAttachmentView->count()); checkDirtyStatus(); } void IncidenceAttachment::saveAttachment(QListWidgetItem *item) { Q_ASSERT(item); Q_ASSERT(dynamic_cast(item)); AttachmentIconItem *attitem = static_cast(item); if (!attitem->attachment()) { return; } KCalCore::Attachment::Ptr att = attitem->attachment(); // get the saveas file name const QString saveAsFile = QFileDialog::getSaveFileName(nullptr, i18nc("@title", "Save Attachment"), att->label()); if (saveAsFile.isEmpty()) { return; } QUrl sourceUrl; if (att->isUri()) { sourceUrl = QUrl(att->uri()); } else { sourceUrl = mAttachmentView->tempFileForAttachment(att); } // save the attachment url auto job = KIO::file_copy(sourceUrl, QUrl::fromLocalFile(saveAsFile)); if (!job->exec() && job->error()) { KMessageBox::error(nullptr, job->errorString()); } } void IncidenceAttachment::saveSelectedAttachments() { for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); if (item->isSelected()) { saveAttachment(item); } } } void IncidenceAttachment::showAttachment(QListWidgetItem *item) { Q_ASSERT(item); Q_ASSERT(dynamic_cast(item)); AttachmentIconItem *attitem = static_cast(item); if (!attitem->attachment()) { return; } KCalCore::Attachment::Ptr att = attitem->attachment(); if (att->isUri()) { openURL(QUrl(att->uri())); } else { KRun::RunFlags flags; flags |= KRun::DeleteTemporaryFiles; flags |= KRun::RunExecutables; KRun::runUrl(mAttachmentView->tempFileForAttachment(att), att->mimeType(), nullptr, flags); } } void IncidenceAttachment::showContextMenu(const QPoint &pos) { QListWidgetItem *item = mAttachmentView->itemAt(pos); const bool enable = item != nullptr; int numSelected = 0; for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); if (item->isSelected()) { numSelected++; } } mOpenAction->setEnabled(enable); //TODO: support saving multiple attachments into a directory mSaveAsAction->setEnabled(enable && numSelected == 1); #ifndef QT_NO_CLIPBOARD mCopyAction->setEnabled(enable && numSelected == 1); mCutAction->setEnabled(enable && numSelected == 1); #endif mDeleteAction->setEnabled(enable); mEditAction->setEnabled(enable); mPopupMenu->exec(mAttachmentView->mapToGlobal(pos)); } void IncidenceAttachment::showSelectedAttachments() { for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); if (item->isSelected()) { showAttachment(item); } } } void IncidenceAttachment::cutToClipboard() { #ifndef QT_NO_CLIPBOARD copyToClipboard(); removeSelectedAttachments(); #endif } void IncidenceAttachment::editSelectedAttachments() { for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); if (item->isSelected()) { Q_ASSERT(dynamic_cast(item)); AttachmentIconItem *attitem = static_cast(item); if (!attitem->attachment()) { return; } QPointer dialog( new AttachmentEditDialog(attitem, mAttachmentView, false)); dialog->setModal(false); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->show(); } } } void IncidenceAttachment::slotItemRenamed(QListWidgetItem *item) { Q_ASSERT(item); Q_ASSERT(dynamic_cast(item)); static_cast(item)->setLabel(item->text()); checkDirtyStatus(); } void IncidenceAttachment::slotSelectionChanged() { bool selected = false; for (int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex) { QListWidgetItem *item = mAttachmentView->item(itemIndex); if (item->isSelected()) { selected = true; break; } } mUi->mRemoveButton->setEnabled(selected); } /// Private functions void IncidenceAttachment::handlePasteOrDrop(const QMimeData *mimeData) { QList urls; bool probablyWeHaveUris = false; QStringList labels; if (KContacts::VCardDrag::canDecode(mimeData)) { KContacts::Addressee::List addressees; KContacts::VCardDrag::fromMimeData(mimeData, addressees); urls.reserve(addressees.count()); labels.reserve(addressees.count()); const KContacts::Addressee::List::ConstIterator end(addressees.constEnd()); for (KContacts::Addressee::List::ConstIterator it = addressees.constBegin(); it != end; ++it) { urls.append(QUrl(QStringLiteral("uid:") + (*it).uid())); // there is some weirdness about realName(), hence fromUtf8 labels.append(QString::fromUtf8((*it).realName().toLatin1())); } probablyWeHaveUris = true; } else if (mimeData->hasUrls()) { QMap metadata; //QT5 //urls = QList::fromMimeData( mimeData, &metadata ); probablyWeHaveUris = true; labels = metadata[QStringLiteral("labels")].split(QLatin1Char(':'), QString::SkipEmptyParts); const QStringList::Iterator end(labels.end()); for (QStringList::Iterator it = labels.begin(); it != end; ++it) { *it = QUrl::fromPercentEncoding((*it).toLatin1()); } } else if (mimeData->hasText()) { const QString text = mimeData->text(); QStringList lst = text.split(QLatin1Char('\n'), QString::SkipEmptyParts); urls.reserve(lst.count()); QStringList::ConstIterator end(lst.constEnd()); for (QStringList::ConstIterator it = lst.constBegin(); it != end; ++it) { urls.append(QUrl(*it)); } probablyWeHaveUris = true; } QMenu menu; QAction *linkAction = nullptr, *cancelAction = nullptr; if (probablyWeHaveUris) { linkAction = menu.addAction(QIcon::fromTheme(QStringLiteral("insert-link")), i18nc("@action:inmenu", "&Link here")); // we need to check if we can reasonably expect to copy the objects bool weCanCopy = true; QList::ConstIterator end(urls.constEnd()); for (QList::ConstIterator it = urls.constBegin(); it != end; ++it) { if (!(weCanCopy = KProtocolManager::supportsReading(*it))) { break; // either we can copy them all, or no copying at all } } if (weCanCopy) { menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu", "&Copy here")); } } else { menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu", "&Copy here")); } menu.addSeparator(); cancelAction = menu.addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18nc("@action:inmenu", "C&ancel")); QByteArray data; QString mimeType; QString label; if (!mimeData->formats().isEmpty() && !probablyWeHaveUris) { mimeType = mimeData->formats().first(); data = mimeData->data(mimeType); QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.isValid()) { label = mime.comment(); } } QAction *ret = menu.exec(QCursor::pos()); if (linkAction == ret) { QStringList::ConstIterator jt = labels.constBegin(); const QList::ConstIterator jtEnd = urls.constEnd(); for (QList::ConstIterator it = urls.constBegin(); it != jtEnd; ++it) { addUriAttachment((*it).url(), QString(), (jt == labels.constEnd() ? QString() : *(jt++)), true); } } else if (cancelAction != ret) { if (probablyWeHaveUris) { QList::ConstIterator end = urls.constEnd(); for (QList::ConstIterator it = urls.constBegin(); it != end; ++it) { KIO::Job *job = KIO::storedGet(*it); //TODO verify if slot exist ! connect(job, &KIO::Job::result, this, &IncidenceAttachment::downloadComplete); } } else { // we take anything addDataAttachment(data, mimeType, label); } } } void IncidenceAttachment::downloadComplete(KJob *) { //TODO } void IncidenceAttachment::setupActions() { KActionCollection *ac = new KActionCollection(this); // ac->addAssociatedWidget( this ); mOpenAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open")), i18nc("@action:inmenu open the attachment in a viewer", "&Open"), this); connect(mOpenAction, &QAction::triggered, this, &IncidenceAttachment::showSelectedAttachments); ac->addAction(QStringLiteral("view"), mOpenAction); mPopupMenu->addAction(mOpenAction); mSaveAsAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18nc("@action:inmenu save the attachment to a file", "Save As..."), this); connect(mSaveAsAction, &QAction::triggered, this, &IncidenceAttachment::saveSelectedAttachments); mPopupMenu->addAction(mSaveAsAction); mPopupMenu->addSeparator(); #ifndef QT_NO_CLIPBOARD mCopyAction = KStandardAction::copy(this, &IncidenceAttachment::copyToClipboard, ac); mPopupMenu->addAction(mCopyAction); mCutAction = KStandardAction::cut(this, &IncidenceAttachment::cutToClipboard, ac); mPopupMenu->addAction(mCutAction); QAction *action = KStandardAction::paste(this, &IncidenceAttachment::pasteFromClipboard, ac); mPopupMenu->addAction(action); mPopupMenu->addSeparator(); #endif mDeleteAction = new QAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18nc("@action:inmenu remove the attachment", "&Remove"), this); connect(mDeleteAction, &QAction::triggered, this, &IncidenceAttachment::removeSelectedAttachments); ac->addAction(QStringLiteral("remove"), mDeleteAction); mDeleteAction->setShortcut(Qt::Key_Delete); mPopupMenu->addAction(mDeleteAction); mPopupMenu->addSeparator(); mEditAction = new QAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action:inmenu show a dialog used to edit the attachment", "&Properties..."), this); connect(mEditAction, &QAction::triggered, this, &IncidenceAttachment::editSelectedAttachments); ac->addAction(QStringLiteral("edit"), mEditAction); mPopupMenu->addAction(mEditAction); } void IncidenceAttachment::setupAttachmentIconView() { mAttachmentView = new AttachmentIconView; mAttachmentView->setWhatsThis(i18nc("@info:whatsthis", "Displays items (files, mail, etc.) that " "have been associated with this event or to-do.")); connect(mAttachmentView, &AttachmentIconView::itemDoubleClicked, this, &IncidenceAttachment::showAttachment); connect(mAttachmentView, &AttachmentIconView::itemChanged, this, &IncidenceAttachment::slotItemRenamed); connect(mAttachmentView, &AttachmentIconView::itemSelectionChanged, this, &IncidenceAttachment::slotSelectionChanged); connect(mAttachmentView, &AttachmentIconView::customContextMenuRequested, this, &IncidenceAttachment::showContextMenu); QGridLayout *layout = new QGridLayout(mUi->mAttachmentViewPlaceHolder); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(mAttachmentView); } // void IncidenceAttachmentEditor::addAttachment( KCalCore::Attachment *attachment ) // { // new AttachmentIconItem( attachment, mAttachmentView ); // } void IncidenceAttachment::addDataAttachment(const QByteArray &data, const QString &mimeType, const QString &label) { AttachmentIconItem *item = new AttachmentIconItem(KCalCore::Attachment::Ptr(), mAttachmentView); QString nlabel = label; if (mimeType == QLatin1String("message/rfc822")) { // mail message. try to set the label from the mail Subject: KMime::Message msg; msg.setContent(data); msg.parse(); nlabel = msg.subject()->asUnicodeString(); } item->setData(data); item->setLabel(nlabel); if (mimeType.isEmpty()) { QMimeDatabase db; item->setMimeType(db.mimeTypeForData(data).name()); } else { item->setMimeType(mimeType); } checkDirtyStatus(); } void IncidenceAttachment::addUriAttachment(const QString &uri, const QString &mimeType, const QString &label, bool inLine) { if (!inLine) { AttachmentIconItem *item = new AttachmentIconItem(KCalCore::Attachment::Ptr(), mAttachmentView); item->setUri(uri); item->setLabel(label); if (mimeType.isEmpty()) { if (uri.startsWith(QStringLiteral("uid:"))) { item->setMimeType(QStringLiteral("text/directory")); } else if (uri.startsWith(QStringLiteral("kmail:"))) { item->setMimeType(QStringLiteral("message/rfc822")); } else if (uri.startsWith(QStringLiteral("urn:x-ical"))) { item->setMimeType(QStringLiteral("text/calendar")); } else if (uri.startsWith(QStringLiteral("news:"))) { item->setMimeType(QStringLiteral("message/news")); } else { QMimeDatabase db; item->setMimeType(db.mimeTypeForUrl(QUrl(uri)).name()); } } } else { auto job = KIO::storedGet(QUrl(uri)); KJobWidgets::setWindow(job, nullptr); if (job->exec()) { const QByteArray data = job->data(); addDataAttachment(data, mimeType, label); } } } diff --git a/src/incidencedialog.cpp b/src/incidencedialog.cpp index d7ad516..8d27858 100644 --- a/src/incidencedialog.cpp +++ b/src/incidencedialog.cpp @@ -1,873 +1,873 @@ /* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Copyright (C) 2012 Allen Winter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidencedialog.h" #include "incidenceeditor_debug.h" #include "combinedincidenceeditor.h" #include "editorconfig.h" #include "incidencealarm.h" #include "incidenceattachment.h" #include "incidenceattendee.h" #include "incidencecategories.h" #include "incidencecompletionpriority.h" #include "incidencedatetime.h" #include "incidencedescription.h" #include "incidencerecurrence.h" #include "incidenceresource.h" #include "incidencesecrecy.h" #include "incidencewhatwhere.h" #include "templatemanagementdialog.h" #include "ui_dialogdesktop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace IncidenceEditorNG; namespace IncidenceEditorNG { enum Tabs { GeneralTab = 0, AttendeesTab, ResourcesTab, AlarmsTab, RecurrenceTab, AttachmentsTab }; class IncidenceDialogPrivate : public ItemEditorUi { IncidenceDialog *q_ptr; Q_DECLARE_PUBLIC(IncidenceDialog) public: Ui::EventOrTodoDesktop *mUi = nullptr; Akonadi::CollectionComboBox *mCalSelector = nullptr; bool mCloseOnSave = false; EditorItemManager *mItemManager = nullptr; CombinedIncidenceEditor *mEditor = nullptr; IncidenceDateTime *mIeDateTime = nullptr; IncidenceAttendee *mIeAttendee = nullptr; IncidenceRecurrence *mIeRecurrence = nullptr; IncidenceResource *mIeResource = nullptr; bool mInitiallyDirty = false; Akonadi::Item mItem; QString typeToString(const int type) const; public: IncidenceDialogPrivate(Akonadi::IncidenceChanger *changer, IncidenceDialog *qq); ~IncidenceDialogPrivate(); /// General methods void handleAlarmCountChange(int newCount); void handleRecurrenceChange(IncidenceEditorNG::RecurrenceType type); void loadTemplate(const QString &templateName); void manageTemplates(); void saveTemplate(const QString &templateName); void storeTemplatesInConfig(const QStringList &newTemplates); void updateAttachmentCount(int newCount); void updateAttendeeCount(int newCount); void updateResourceCount(int newCount); void updateButtonStatus(bool isDirty); void showMessage(const QString &text, KMessageWidget::MessageType type); void slotInvalidCollection(); /// ItemEditorUi methods bool containsPayloadIdentifiers(const QSet &partIdentifiers) const override; void handleItemSaveFinish(EditorItemManager::SaveAction); void handleItemSaveFail(EditorItemManager::SaveAction, const QString &errorMessage); bool hasSupportedPayload(const Akonadi::Item &item) const override; bool isDirty() const override; bool isValid() const override; void load(const Akonadi::Item &item) override; Akonadi::Item save(const Akonadi::Item &item) override; Akonadi::Collection selectedCollection() const override; void reject(RejectReason reason, const QString &errorMessage = QString()) override; }; } IncidenceDialogPrivate::IncidenceDialogPrivate(Akonadi::IncidenceChanger *changer, IncidenceDialog *qq) : q_ptr(qq) , mUi(new Ui::EventOrTodoDesktop) , mCalSelector(new Akonadi::CollectionComboBox) , mCloseOnSave(false) , mItemManager(new EditorItemManager(this, changer)) , mEditor(new CombinedIncidenceEditor(qq)) , mInitiallyDirty(false) { Q_Q(IncidenceDialog); mUi->setupUi(q); QGridLayout *layout = new QGridLayout(mUi->mCalSelectorPlaceHolder); layout->setSpacing(0); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(mCalSelector); mCalSelector->setAccessRightsFilter(Akonadi::Collection::CanCreateItem); mUi->label->setBuddy(mCalSelector); q->connect(mCalSelector, &Akonadi::CollectionComboBox::currentChanged, q, &IncidenceDialog::handleSelectedCollectionChange); // Now instantiate the logic of the dialog. These editors update the ui, validate // fields and load/store incidences in the ui. IncidenceWhatWhere *ieGeneral = new IncidenceWhatWhere(mUi); mEditor->combine(ieGeneral); IncidenceCategories *ieCategories = new IncidenceCategories(mUi); mEditor->combine(ieCategories); mIeDateTime = new IncidenceDateTime(mUi); mEditor->combine(mIeDateTime); IncidenceCompletionPriority *ieCompletionPriority = new IncidenceCompletionPriority(mUi); mEditor->combine(ieCompletionPriority); IncidenceDescription *ieDescription = new IncidenceDescription(mUi); mEditor->combine(ieDescription); IncidenceAlarm *ieAlarm = new IncidenceAlarm(mIeDateTime, mUi); mEditor->combine(ieAlarm); IncidenceAttachment *ieAttachments = new IncidenceAttachment(mUi); mEditor->combine(ieAttachments); mIeRecurrence = new IncidenceRecurrence(mIeDateTime, mUi); mEditor->combine(mIeRecurrence); IncidenceSecrecy *ieSecrecy = new IncidenceSecrecy(mUi); mEditor->combine(ieSecrecy); mIeAttendee = new IncidenceAttendee(qq, mIeDateTime, mUi); mIeAttendee->setParent(qq); mEditor->combine(mIeAttendee); mIeResource = new IncidenceResource(mIeAttendee, mIeDateTime, mUi); mEditor->combine(mIeResource); q->connect(mEditor, SIGNAL(showMessage(QString,KMessageWidget::MessageType)), SLOT(showMessage(QString,KMessageWidget::MessageType))); q->connect(mEditor, SIGNAL(dirtyStatusChanged(bool)), SLOT(updateButtonStatus(bool))); q->connect(mItemManager, SIGNAL(itemSaveFinished(IncidenceEditorNG::EditorItemManager::SaveAction)), SLOT(handleItemSaveFinish(IncidenceEditorNG::EditorItemManager::SaveAction))); q->connect(mItemManager, SIGNAL(itemSaveFailed(IncidenceEditorNG::EditorItemManager::SaveAction,QString)), SLOT(handleItemSaveFail(IncidenceEditorNG::EditorItemManager::SaveAction,QString))); q->connect(ieAlarm, SIGNAL(alarmCountChanged(int)), SLOT(handleAlarmCountChange(int))); q->connect(mIeRecurrence, SIGNAL(recurrenceChanged(IncidenceEditorNG::RecurrenceType)), SLOT(handleRecurrenceChange(IncidenceEditorNG::RecurrenceType))); q->connect(ieAttachments, SIGNAL(attachmentCountChanged(int)), SLOT(updateAttachmentCount(int))); q->connect(mIeAttendee, SIGNAL(attendeeCountChanged(int)), SLOT(updateAttendeeCount(int))); q->connect(mIeResource, SIGNAL(resourceCountChanged(int)), SLOT(updateResourceCount(int))); } IncidenceDialogPrivate::~IncidenceDialogPrivate() { delete mItemManager; delete mEditor; delete mUi; } void IncidenceDialogPrivate::slotInvalidCollection() { showMessage(i18n("Select a valid collection first."), KMessageWidget::Warning); } void IncidenceDialogPrivate::showMessage(const QString &text, KMessageWidget::MessageType type) { mUi->mMessageWidget->setText(text); mUi->mMessageWidget->setMessageType(type); mUi->mMessageWidget->show(); } void IncidenceDialogPrivate::handleAlarmCountChange(int newCount) { QString tabText; if (newCount > 0) { tabText = i18nc("@title:tab Tab to configure the reminders of an event or todo", "Reminder (%1)", newCount); } else { tabText = i18nc("@title:tab Tab to configure the reminders of an event or todo", "Reminder"); } mUi->mTabWidget->setTabText(AlarmsTab, tabText); } void IncidenceDialogPrivate::handleRecurrenceChange(IncidenceEditorNG::RecurrenceType type) { QString tabText = i18nc("@title:tab Tab to configure the recurrence of an event or todo", "Rec&urrence"); // Keep this numbers in sync with the items in mUi->mRecurrenceTypeCombo. I // tried adding an enum to IncidenceRecurrence but for whatever reason I could // Qt not play nice with namespaced enums in signal/slot connections. // Anyways, I don't expect these values to change. switch (type) { case RecurrenceTypeNone: break; case RecurrenceTypeDaily: tabText += i18nc("@title:tab Daily recurring event, capital first letter only", " (D)"); break; case RecurrenceTypeWeekly: tabText += i18nc("@title:tab Weekly recurring event, capital first letter only", " (W)"); break; case RecurrenceTypeMonthly: tabText += i18nc("@title:tab Monthly recurring event, capital first letter only", " (M)"); break; case RecurrenceTypeYearly: tabText += i18nc("@title:tab Yearly recurring event, capital first letter only", " (Y)"); break; case RecurrenceTypeException: tabText += i18nc("@title:tab Exception to a recurring event, capital first letter only", " (E)"); break; default: Q_ASSERT_X(false, "handleRecurrenceChange", "Fix your program"); } mUi->mTabWidget->setTabText(RecurrenceTab, tabText); } QString IncidenceDialogPrivate::typeToString(const int type) const { // Do not translate. switch (type) { case KCalCore::Incidence::TypeEvent: return QStringLiteral("Event"); case KCalCore::Incidence::TypeTodo: return QStringLiteral("Todo"); case KCalCore::Incidence::TypeJournal: return QStringLiteral("Journal"); default: return QStringLiteral("Unknown"); } } void IncidenceDialogPrivate::loadTemplate(const QString &templateName) { Q_Q(IncidenceDialog); KCalCore::MemoryCalendar::Ptr cal(new KCalCore::MemoryCalendar(QTimeZone::systemTimeZone())); const QString fileName = QStandardPaths::locate( QStandardPaths::GenericDataLocation, QStringLiteral("/korganizer/templates/") +typeToString(mEditor->type()) + QLatin1Char('/') +templateName); if (fileName.isEmpty()) { KMessageBox::error( q, i18nc("@info", "Unable to find template '%1'.", templateName)); return; } KCalCore::ICalFormat format; if (!format.load(cal, fileName)) { KMessageBox::error( q, i18nc("@info", "Error loading template file '%1'.", fileName)); return; } KCalCore::Incidence::List incidences = cal->incidences(); if (incidences.isEmpty()) { KMessageBox::error( q, i18nc("@info", "Template does not contain a valid incidence.")); return; } mIeDateTime->setActiveDate(QDate()); KCalCore::Incidence::Ptr newInc = KCalCore::Incidence::Ptr(incidences.first()->clone()); newInc->setUid(KCalCore::CalFormat::createUniqueId()); // We add a custom property so that some fields aren't loaded, dates for example newInc->setCustomProperty(QByteArray("kdepim"), "isTemplate", QStringLiteral("true")); mEditor->load(newInc); newInc->removeCustomProperty(QByteArray(), "isTemplate"); } void IncidenceDialogPrivate::manageTemplates() { Q_Q(IncidenceDialog); QStringList &templates = IncidenceEditorNG::EditorConfig::instance()->templates(mEditor->type()); QPointer dialog( new IncidenceEditorNG::TemplateManagementDialog( q, templates, KCalUtils::Stringify::incidenceType(mEditor->type()))); q->connect(dialog, SIGNAL(loadTemplate(QString)), SLOT(loadTemplate(QString))); q->connect(dialog, SIGNAL(templatesChanged(QStringList)), SLOT(storeTemplatesInConfig(QStringList))); q->connect(dialog, SIGNAL(saveTemplate(QString)), SLOT(saveTemplate(QString))); dialog->exec(); delete dialog; } void IncidenceDialogPrivate::saveTemplate(const QString &templateName) { Q_ASSERT(!templateName.isEmpty()); KCalCore::MemoryCalendar::Ptr cal(new KCalCore::MemoryCalendar(QTimeZone::systemTimeZone())); switch (mEditor->type()) { case KCalCore::Incidence::TypeEvent: { KCalCore::Event::Ptr event(new KCalCore::Event()); mEditor->save(event); cal->addEvent(KCalCore::Event::Ptr(event->clone())); break; } case KCalCore::Incidence::TypeTodo: { KCalCore::Todo::Ptr todo(new KCalCore::Todo); mEditor->save(todo); cal->addTodo(KCalCore::Todo::Ptr(todo->clone())); break; } case KCalCore::Incidence::TypeJournal: { KCalCore::Journal::Ptr journal(new KCalCore::Journal); mEditor->save(journal); cal->addJournal(KCalCore::Journal::Ptr(journal->clone())); break; } default: Q_ASSERT_X(false, "saveTemplate", "Fix your program"); } QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +QStringLiteral("/korganizer/templates/") + typeToString(mEditor->type()) + QLatin1Char('/'); QDir().mkpath(fileName); fileName += templateName; KCalCore::ICalFormat format; format.save(cal, fileName); } void IncidenceDialogPrivate::storeTemplatesInConfig(const QStringList &templateNames) { // I find this somewhat broken. templates() returns a reference, maybe it should // be changed by adding a setTemplates method. const QStringList origTemplates = IncidenceEditorNG::EditorConfig::instance()->templates(mEditor->type()); const QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +QStringLiteral("korganizer/templates/") +typeToString(mEditor->type()) + QLatin1Char('/'); QDir().mkpath(defaultPath); for (const QString &tmpl : origTemplates) { if (!templateNames.contains(tmpl)) { const QString fileName = defaultPath + tmpl; QFile file(fileName); if (file.exists()) { file.remove(); } } } IncidenceEditorNG::EditorConfig::instance()->templates(mEditor->type()) = templateNames; IncidenceEditorNG::EditorConfig::instance()->config()->save(); } void IncidenceDialogPrivate::updateAttachmentCount(int newCount) { if (newCount > 0) { mUi->mTabWidget->setTabText( AttachmentsTab, i18nc("@title:tab Tab to modify attachments of an event or todo", "Attac&hments (%1)", newCount)); } else { mUi->mTabWidget->setTabText( AttachmentsTab, i18nc("@title:tab Tab to modify attachments of an event or todo", "Attac&hments")); } } void IncidenceDialogPrivate::updateAttendeeCount(int newCount) { if (newCount > 0) { mUi->mTabWidget->setTabText( AttendeesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Attendees (%1)", newCount)); } else { mUi->mTabWidget->setTabText( AttendeesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Attendees")); } } void IncidenceDialogPrivate::updateResourceCount(int newCount) { if (newCount > 0) { mUi->mTabWidget->setTabText( ResourcesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Resources (%1)", newCount)); } else { mUi->mTabWidget->setTabText( ResourcesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Resources")); } } void IncidenceDialogPrivate::updateButtonStatus(bool isDirty) { mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(isDirty || mInitiallyDirty); } bool IncidenceDialogPrivate::containsPayloadIdentifiers( const QSet &partIdentifiers) const { return partIdentifiers.contains(QByteArray("PLD:RFC822")); } void IncidenceDialogPrivate::handleItemSaveFail(EditorItemManager::SaveAction, const QString &errorMessage) { Q_Q(IncidenceDialog); bool retry = false; if (!errorMessage.isEmpty()) { const QString message = i18nc("@info", "Unable to store the incidence in the calendar. Try again?\n\n " "Reason: %1", errorMessage); retry = (KMessageBox::warningYesNo(q, message) == KMessageBox::Yes); } if (retry) { mItemManager->save(); } else { updateButtonStatus(isDirty()); mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); } } void IncidenceDialogPrivate::handleItemSaveFinish(EditorItemManager::SaveAction saveAction) { Q_Q(IncidenceDialog); if (mCloseOnSave) { q->accept(); } else { const Akonadi::Item item = mItemManager->item(); Q_ASSERT(item.isValid()); Q_ASSERT(item.hasPayload()); Q_ASSERT(item.hasPayload()); // Now the item is successfully saved, reload it in the editor in order to // reset the dirty status of the editor. mEditor->load(item.payload()); mEditor->load(item); // Set the buttons to a reasonable state as well (ok and apply should be // disabled at this point). mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(isDirty()); } if (saveAction == EditorItemManager::Create) { Q_EMIT q->incidenceCreated(mItemManager->item()); } } bool IncidenceDialogPrivate::hasSupportedPayload(const Akonadi::Item &item) const { return CalendarSupport::incidence(item); } bool IncidenceDialogPrivate::isDirty() const { if (mItem.isValid()) { return mEditor->isDirty() || mCalSelector->currentCollection().id() != mItem.storageCollectionId(); } else { return mEditor->isDirty(); } } bool IncidenceDialogPrivate::isValid() const { Q_Q(const IncidenceDialog); if (mEditor->isValid()) { // Check if there's a selected collection. if (mCalSelector->currentCollection().isValid()) { return true; } else { qCWarning(INCIDENCEEDITOR_LOG) << "Select a collection first"; Q_EMIT q->invalidCollection(); } } return false; } void IncidenceDialogPrivate::load(const Akonadi::Item &item) { Q_Q(IncidenceDialog); Q_ASSERT(hasSupportedPayload(item)); if (CalendarSupport::hasJournal(item)) { //mUi->mTabWidget->removeTab(5); mUi->mTabWidget->removeTab(AttachmentsTab); mUi->mTabWidget->removeTab(RecurrenceTab); mUi->mTabWidget->removeTab(AlarmsTab); mUi->mTabWidget->removeTab(AttendeesTab); mUi->mTabWidget->removeTab(ResourcesTab); } mEditor->load(CalendarSupport::incidence(item)); mEditor->load(item); const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence(item); const QStringList allEmails = IncidenceEditorNG::EditorConfig::instance()->allEmails(); KCalCore::Attendee::Ptr me = incidence->attendeeByMails(allEmails); if (incidence->attendeeCount() > 1 // >1 because you won't drink alone && me && (me->status() == KCalCore::Attendee::NeedsAction || me->status() == KCalCore::Attendee::Tentative || me->status() == KCalCore::Attendee::InProcess)) { // Show the invitation bar: "You are invited [accept] [decline]" mUi->mInvitationBar->show(); } else { mUi->mInvitationBar->hide(); } qCDebug(INCIDENCEEDITOR_LOG) << "Loading item " << item.id() << "; parent " << item.parentCollection().id() << "; storage " << item.storageCollectionId(); if (item.storageCollectionId() > -1) { mCalSelector->setDefaultCollection(Akonadi::Collection(item.storageCollectionId())); } if (!mCalSelector->mimeTypeFilter().contains(QStringLiteral("text/calendar")) || !mCalSelector->mimeTypeFilter().contains(incidence->mimeType())) { mCalSelector->setMimeTypeFilter(QStringList() << incidence->mimeType() << QStringLiteral("text/calendar")); } if (mEditor->type() == KCalCore::Incidence::TypeTodo) { q->setWindowIcon(QIcon::fromTheme(QStringLiteral("view-calendar-tasks"))); } else if (mEditor->type() == KCalCore::Incidence::TypeEvent) { q->setWindowIcon(QIcon::fromTheme(QStringLiteral("view-calendar-day"))); } else if (mEditor->type() == KCalCore::Incidence::TypeJournal) { q->setWindowIcon(QIcon::fromTheme(QStringLiteral("view-pim-journal"))); } // Initialize tab's titles updateAttachmentCount(incidence->attachments().size()); updateResourceCount(mIeResource->resourceCount()); updateAttendeeCount(mIeAttendee->attendeeCount()); handleRecurrenceChange(mIeRecurrence->currentRecurrenceType()); handleAlarmCountChange(incidence->alarms().count()); mItem = item; q->show(); } Akonadi::Item IncidenceDialogPrivate::save(const Akonadi::Item &item) { Q_ASSERT(mEditor->incidence()); KCalCore::Incidence::Ptr incidenceInEditor = mEditor->incidence(); KCalCore::Incidence::Ptr newIncidence(incidenceInEditor->clone()); Akonadi::Item result = item; result.setMimeType(newIncidence->mimeType()); // There's no editor that has the relatedTo property. We must set it here, by hand. // Otherwise it gets lost. // FIXME: Why don't we clone() incidenceInEditor then pass the clone to save(), // I wonder if we're not leaking other properties. newIncidence->setRelatedTo(incidenceInEditor->relatedTo()); mEditor->save(newIncidence); mEditor->save(result); // TODO: Remove this once we support moving of events/todo's mCalSelector->setEnabled(false); // Make sure that we don't loose uid for existing incidence newIncidence->setUid(mEditor->incidence()->uid()); // Mark the incidence as changed if (mItem.isValid()) { newIncidence->setRevision(newIncidence->revision() + 1); } result.setPayload(newIncidence); return result; } Akonadi::Collection IncidenceDialogPrivate::selectedCollection() const { return mCalSelector->currentCollection(); } void IncidenceDialogPrivate::reject(RejectReason reason, const QString &errorMessage) { Q_UNUSED(reason); Q_Q(IncidenceDialog); qCCritical(INCIDENCEEDITOR_LOG) << "Rejecting:" << errorMessage; q->deleteLater(); } /// IncidenceDialog IncidenceDialog::IncidenceDialog(Akonadi::IncidenceChanger *changer, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) , d_ptr(new IncidenceDialogPrivate(changer, this)) { Q_D(IncidenceDialog); setAttribute(Qt::WA_DeleteOnClose); d->mUi->mTabWidget->setCurrentIndex(0); d->mUi->mSummaryEdit->setFocus(); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setToolTip(i18nc("@info:tooltip", "Save current changes")); d->mUi->buttonBox->button(QDialogButtonBox::Ok)->setToolTip(i18nc("@action:button", "Save changes and close dialog")); d->mUi->buttonBox->button(QDialogButtonBox::Cancel)->setToolTip(i18nc("@action:button", "Discard changes and close dialog")); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); auto defaultButton = d->mUi->buttonBox->button(QDialogButtonBox::RestoreDefaults); defaultButton->setText(i18nc("@action:button", "&Templates...")); defaultButton->setIcon(QIcon::fromTheme(QStringLiteral("project-development-new-template"))); defaultButton->setToolTip(i18nc("@info:tooltip", "Manage templates for this item")); defaultButton->setWhatsThis( i18nc("@info:whatsthis", "Push this button to show a dialog that helps " "you manage a set of templates. Templates " "can make creating new items easier and faster " "by putting your favorite default values into " "the editor automatically.")); connect(d->mUi->buttonBox, &QDialogButtonBox::clicked, this, &IncidenceDialog::slotButtonClicked); setModal(false); connect(d->mUi->mAcceptInvitationButton, &QAbstractButton::clicked, d->mIeAttendee, &IncidenceAttendee::acceptForMe); connect(d->mUi->mAcceptInvitationButton, &QAbstractButton::clicked, d->mUi->mInvitationBar, &QWidget::hide); connect(d->mUi->mDeclineInvitationButton, &QAbstractButton::clicked, d->mIeAttendee, &IncidenceAttendee::declineForMe); connect(d->mUi->mDeclineInvitationButton, &QAbstractButton::clicked, d->mUi->mInvitationBar, &QWidget::hide); connect(this, SIGNAL(invalidCollection()), this, SLOT(slotInvalidCollection())); readConfig(); } IncidenceDialog::~IncidenceDialog() { writeConfig(); delete d_ptr; } void IncidenceDialog::writeConfig() { KConfigGroup group(KSharedConfig::openConfig(), "IncidenceDialog"); group.writeEntry("Size", size()); const Akonadi::Collection col = d_ptr->mCalSelector->currentCollection(); // col might not be valid if the collection wasn't found yet (the combo is async), skip saving in that case if (col.isValid() && col.id() != IncidenceEditorNG::IncidenceEditorSettings::self()->lastSelectedFolder()) { IncidenceEditorNG::IncidenceEditorSettings::self()->setLastSelectedFolder(col.id()); IncidenceEditorNG::IncidenceEditorSettings::self()->save(); } } void IncidenceDialog::readConfig() { KConfigGroup group(KSharedConfig::openConfig(), "IncidenceDialog"); const QSize size = group.readEntry("Size", QSize()); if (size.isValid()) { resize(size); } else { resize(QSize(500, 500).expandedTo(minimumSizeHint())); } } void IncidenceDialog::load(const Akonadi::Item &item, const QDate &activeDate) { Q_D(IncidenceDialog); d->mIeDateTime->setActiveDate(activeDate); if (item.isValid()) { // We're editing d->mItemManager->load(item); // TODO: Remove this once we support moving of events/todo's d->mCalSelector->setEnabled(false); } else { // We're creating Q_ASSERT(d->hasSupportedPayload(item)); d->load(item); show(); } } void IncidenceDialog::selectCollection(const Akonadi::Collection &collection) { Q_D(IncidenceDialog); if (collection.isValid()) { d->mCalSelector->setDefaultCollection(collection); } else { d->mCalSelector->setCurrentIndex(0); } } void IncidenceDialog::setIsCounterProposal(bool isCounterProposal) { Q_D(IncidenceDialog); d->mItemManager->setIsCounterProposal(isCounterProposal); } QObject *IncidenceDialog::typeAheadReceiver() const { Q_D(const IncidenceDialog); return d->mUi->mSummaryEdit; } void IncidenceDialog::slotButtonClicked(QAbstractButton *button) { Q_D(IncidenceDialog); if (d->mUi->buttonBox->button(QDialogButtonBox::Ok) == button) { if (d->isDirty() || d->mInitiallyDirty) { d->mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); d->mCloseOnSave = true; d->mInitiallyDirty = false; d->mItemManager->save(); } else { close(); } } else if (d->mUi->buttonBox->button(QDialogButtonBox::Apply) == button) { d->mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); d->mCloseOnSave = false; d->mInitiallyDirty = false; d->mItemManager->save(); } else if (d->mUi->buttonBox->button(QDialogButtonBox::Cancel) == button) { if (d->isDirty() && KMessageBox::questionYesNo( this, i18nc("@info", "Do you really want to cancel?"), i18nc("@title:window", "KOrganizer Confirmation")) == KMessageBox::Yes) { QDialog::reject(); // Discard current changes } else if (!d->isDirty()) { QDialog::reject(); // No pending changes, just close the dialog. } // else { // the user wasn't finished editing after all } } else if (d->mUi->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { d->manageTemplates(); } else { Q_ASSERT(false); // Shouldn't happen } } void IncidenceDialog::closeEvent(QCloseEvent *event) { Q_D(IncidenceDialog); if (d->isDirty() && KMessageBox::questionYesNo( this, i18nc("@info", "Do you really want to cancel?"), i18nc("@title:window", "KOrganizer Confirmation")) == KMessageBox::Yes) { QDialog::reject(); // Discard current changes QDialog::closeEvent(event); } else if (!d->isDirty()) { QDialog::reject(); // No pending changes, just close the dialog. QDialog::closeEvent(event); } else { event->ignore(); } } void IncidenceDialog::setInitiallyDirty(bool initiallyDirty) { Q_D(IncidenceDialog); d->mInitiallyDirty = initiallyDirty; } Akonadi::Item IncidenceDialog::item() const { Q_D(const IncidenceDialog); return d->mItemManager->item(); } void IncidenceDialog::handleSelectedCollectionChange(const Akonadi::Collection &collection) { Q_D(IncidenceDialog); if (d->mItem.parentCollection().isValid()) { d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled( collection.id() != d->mItem.parentCollection().id()); } } #include "moc_incidencedialog.cpp" diff --git a/src/templatemanagementdialog.cpp b/src/templatemanagementdialog.cpp index 629521a..12322c3 100644 --- a/src/templatemanagementdialog.cpp +++ b/src/templatemanagementdialog.cpp @@ -1,228 +1,226 @@ /****************************************************************************** ** ** Filename : templatemanagementdialog.cpp ** Created on : 05 June, 2005 ** Copyright : (c) 2005 Till Adam ** Copyright (C) 2009 Allen Winter ** ** ******************************************************************************/ /****************************************************************************** ** ** 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) any later version. ** ** It 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 ** ** In addition, as a special exception, the copyright holders give ** permission to link the code of this program with any edition of ** the Qt library by Trolltech AS, Norway (or with modified versions ** of Qt that use the same license as Qt), and distribute linked ** combinations including the two. You must obey the GNU General ** Public License in all respects for all of the code used other than ** Qt. If you modify this file, you may extend this exception to ** your version of the file, but you are not obligated to do so. If ** you do not wish to do so, delete this exception statement from ** your version. ** ******************************************************************************/ #include "templatemanagementdialog.h" #include #include #include #include #include #include #include #include #include #include using namespace IncidenceEditorNG; TemplateManagementDialog::TemplateManagementDialog( QWidget *parent, const QStringList &templates, const QString &incidenceType) : QDialog(parent) , m_templates(templates) , m_type(incidenceType) , m_changed(false) { QString m_type_translated = i18n(qPrintable(m_type)); setWindowTitle(i18n("Manage %1 Templates", m_type_translated)); QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, this); QVBoxLayout *mainLayout = new QVBoxLayout(this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &TemplateManagementDialog::reject); setObjectName(QStringLiteral("template_management_dialog")); connect(buttonBox->button( QDialogButtonBox::Help), &QPushButton::clicked, this, &TemplateManagementDialog::slotHelp); QWidget *widget = new QWidget(this); mainLayout->addWidget(widget); mainLayout->addWidget(buttonBox); widget->setObjectName(QStringLiteral("template_management_dialog_base")); m_base.setupUi(widget); m_base.m_listBox->addItems(m_templates); m_base.m_listBox->setSelectionMode(QAbstractItemView::SingleSelection); connect(m_base.m_buttonAdd, &QPushButton::clicked, this, &TemplateManagementDialog::slotAddTemplate); connect(m_base.m_buttonRemove, &QPushButton::clicked, this, &TemplateManagementDialog::slotRemoveTemplate); connect(m_base.m_buttonApply, &QPushButton::clicked, this, &TemplateManagementDialog::slotApplyTemplate); connect(m_base.m_listBox, &QListWidget::itemSelectionChanged, this, &TemplateManagementDialog::slotItemSelected); connect(m_base.m_listBox, &QListWidget::itemDoubleClicked, this, &TemplateManagementDialog::slotApplyTemplate); connect(okButton, &QPushButton::clicked, this, &TemplateManagementDialog::slotOk); m_base.m_buttonRemove->setEnabled(false); m_base.m_buttonApply->setEnabled(false); } void TemplateManagementDialog::slotHelp() { QUrl url = QUrl(QStringLiteral("help:/")).resolved(QUrl(QStringLiteral( "korganizer/entering-data.html"))); QUrlQuery query(url); query.addQueryItem(QStringLiteral("anchor"), QStringLiteral("entering-data-events-template-buttons")); url.setQuery(query); // launch khelpcenter, or a browser for URIs not handled by khelpcenter QDesktopServices::openUrl(url); } void TemplateManagementDialog::slotItemSelected() { m_base.m_buttonRemove->setEnabled(true); m_base.m_buttonApply->setEnabled(true); } void TemplateManagementDialog::slotAddTemplate() { bool ok; bool duplicate = false; QString m_type_translated = i18n(qPrintable(m_type)); const QString newTemplate = QInputDialog::getText(this, i18n("Template Name"), i18n( "Please enter a name for the new template:"), QLineEdit::Normal, i18n("New %1 Template", m_type_translated), &ok); if (newTemplate.isEmpty() || !ok) { return; } if (m_templates.contains(newTemplate)) { int rc = KMessageBox::warningContinueCancel( this, i18n("A template with that name already exists, do you want to overwrite it?"), i18n("Duplicate Template Name"), KStandardGuiItem::overwrite()); if (rc == KMessageBox::Cancel) { QTimer::singleShot(0, this, &TemplateManagementDialog::slotAddTemplate); return; } duplicate = true; } if (!duplicate) { int count = m_base.m_listBox->count(); m_templates.append(newTemplate); m_base.m_listBox->addItem(newTemplate); QListWidgetItem *item = m_base.m_listBox->item(count); - m_base.m_listBox->setItemSelected(item, true); + item->setSelected(true); } m_newTemplate = newTemplate; m_changed = true; // From this point on we need to keep the original event around until the // user has closed the dialog, applying a template would make little sense //buttonBox->button(QDialogButtonBox::Apply)->setEnabled( false ); // neither does adding it again m_base.m_buttonAdd->setEnabled(false); } void TemplateManagementDialog::slotRemoveTemplate() { QListWidgetItem *const item = m_base.m_listBox->selectedItems().first(); if (!item) { return; // can't happen (TM) } int rc = KMessageBox::warningContinueCancel( this, i18n("Are you sure that you want to remove the template %1?", item->text()), i18n("Remove Template"), KStandardGuiItem::remove()); if (rc == KMessageBox::Cancel) { return; } int current = m_base.m_listBox->row(item); m_templates.removeAll(item->text()); m_base.m_listBox->takeItem(current); - - m_base.m_listBox->setItemSelected( - m_base.m_listBox->item(qMax(current - 1, 0)), true); + m_base.m_listBox->item(qMax(current - 1, 0))->setSelected(true); updateButtons(); m_changed = true; } void TemplateManagementDialog::updateButtons() { m_base.m_buttonAdd->setEnabled(true); const bool isNotEmpty = m_base.m_listBox->count() != 0; m_base.m_buttonRemove->setEnabled(isNotEmpty); m_base.m_buttonApply->setEnabled(isNotEmpty); } void TemplateManagementDialog::slotApplyTemplate() { // Once the user has applied the current template to the event, // it makes no sense to add it again m_base.m_buttonAdd->setEnabled(false); QListWidgetItem *item = m_base.m_listBox->currentItem(); if (item) { const QString &cur = item->text(); if (!cur.isEmpty() && cur != m_newTemplate) { Q_EMIT loadTemplate(cur); slotOk(); } } } void TemplateManagementDialog::slotOk() { // failure is not an option *cough* if (!m_newTemplate.isEmpty()) { Q_EMIT saveTemplate(m_newTemplate); } if (m_changed) { Q_EMIT templatesChanged(m_templates); } accept(); }