diff --git a/src/folder/folderselectiondialog.cpp b/src/folder/folderselectiondialog.cpp index 6fe1f0b..e6a84c1 100644 --- a/src/folder/folderselectiondialog.cpp +++ b/src/folder/folderselectiondialog.cpp @@ -1,304 +1,303 @@ /* Copyright (c) 2009-2020 Laurent Montel 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 */ #include "folderselectiondialog.h" #include "foldersettings.h" #include "foldertreeview.h" #include "foldertreewidget.h" #include "foldertreewidgetproxymodel.h" #include "kernel/mailkernel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace MailCommon { class Q_DECL_HIDDEN FolderSelectionDialog::FolderSelectionDialogPrivate { public: FolderSelectionDialogPrivate() { } FolderTreeWidget *folderTreeWidget = nullptr; QPushButton *mUser1Button = nullptr; QPushButton *mOkButton = nullptr; bool mNotAllowToCreateNewFolder = false; bool mUseGlobalSettings = true; }; FolderSelectionDialog::FolderSelectionDialog(QWidget *parent, SelectionFolderOptions options) : QDialog(parent) , d(new FolderSelectionDialogPrivate()) { setObjectName(QStringLiteral("folder dialog")); d->mNotAllowToCreateNewFolder = (options & FolderSelectionDialog::NotAllowToCreateNewFolder); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QVBoxLayout *mainLayout = new QVBoxLayout(this); d->mOkButton = buttonBox->button(QDialogButtonBox::Ok); d->mOkButton->setDefault(true); d->mOkButton->setAutoDefault(true); d->mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &FolderSelectionDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &FolderSelectionDialog::reject); if (!d->mNotAllowToCreateNewFolder) { d->mUser1Button = new QPushButton; d->mUser1Button->setDefault(false); d->mUser1Button->setAutoDefault(false); buttonBox->addButton(d->mUser1Button, QDialogButtonBox::ActionRole); KGuiItem::assign(d->mUser1Button, KGuiItem(i18n("&New Subfolder..."), QStringLiteral("folder-new"), i18n("Create a new subfolder under the currently selected folder"))); } - FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::DontKeyFilter; - opt |= FolderTreeWidget::UseLineEditForFiltering; + FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::None; if (options & FolderSelectionDialog::ShowUnreadCount) { opt |= FolderTreeWidget::ShowUnreadCount; } opt |= FolderTreeWidget::UseDistinctSelectionModel; FolderTreeWidgetProxyModel::FolderTreeWidgetProxyModelOptions optReadableProxy = FolderTreeWidgetProxyModel::None; if (options & FolderSelectionDialog::HideVirtualFolder) { optReadableProxy |= FolderTreeWidgetProxyModel::HideVirtualFolder; } optReadableProxy |= FolderTreeWidgetProxyModel::HideSpecificFolder; if (options & FolderSelectionDialog::HideOutboxFolder) { optReadableProxy |= FolderTreeWidgetProxyModel::HideOutboxFolder; } d->folderTreeWidget = new FolderTreeWidget(this, nullptr, opt, optReadableProxy); d->folderTreeWidget->readConfig(); d->folderTreeWidget->disableContextMenuAndExtraColumn(); d->folderTreeWidget->folderTreeWidgetProxyModel()->setEnabledCheck((options & EnableCheck)); //Necessary otherwise we overwrite tooltip config for all application d->folderTreeWidget->folderTreeView()->disableSaveConfig(); d->folderTreeWidget->folderTreeView()->setTooltipsPolicy(FolderTreeWidget::DisplayNever); #ifndef QT_NO_DRAGANDDROP d->folderTreeWidget->folderTreeView()->setDragDropMode(QAbstractItemView::NoDragDrop); #endif mainLayout->addWidget(d->folderTreeWidget); mainLayout->addWidget(buttonBox); d->mOkButton->setEnabled(false); if (!d->mNotAllowToCreateNewFolder) { d->mUser1Button->setEnabled(false); connect(d->mUser1Button, &QPushButton::clicked, this, &FolderSelectionDialog::slotAddChildFolder); d->folderTreeWidget->folderTreeView()->setContextMenuPolicy(Qt::CustomContextMenu); connect(d->folderTreeWidget->folderTreeView(), &QWidget::customContextMenuRequested, this, &FolderSelectionDialog::slotFolderTreeWidgetContextMenuRequested); } connect(d->folderTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderSelectionDialog::slotSelectionChanged); connect(d->folderTreeWidget->folderTreeWidgetProxyModel(), &QAbstractItemModel::rowsInserted, this, &FolderSelectionDialog::rowsInserted); connect(d->folderTreeWidget->folderTreeView(), QOverload::of(&QAbstractItemView::doubleClicked), this, &FolderSelectionDialog::slotDoubleClick); d->mUseGlobalSettings = !(options & NotUseGlobalSettings); readConfig(); } FolderSelectionDialog::~FolderSelectionDialog() { writeConfig(); delete d; } void FolderSelectionDialog::slotFolderTreeWidgetContextMenuRequested(const QPoint &pos) { if (d->mUser1Button && d->mUser1Button->isEnabled() && d->folderTreeWidget->folderTreeView()->indexAt(pos).isValid()) { QMenu menu(this); menu.addAction(i18n("&New Subfolder..."), this, &FolderSelectionDialog::slotAddChildFolder); menu.exec(QCursor::pos()); } } void FolderSelectionDialog::slotDoubleClick(const QModelIndex &index) { Q_UNUSED(index); const bool hasSelectedCollection = (!d->folderTreeWidget->selectionModel()->selectedIndexes().isEmpty()); if (hasSelectedCollection) { accept(); } } void FolderSelectionDialog::focusTreeView() { d->folderTreeWidget->folderTreeView()->expandAll(); d->folderTreeWidget->folderTreeView()->setFocus(); } void FolderSelectionDialog::showEvent(QShowEvent *event) { if (!event->spontaneous()) { focusTreeView(); FolderTreeView *view = d->folderTreeWidget->folderTreeView(); view->scrollTo(view->currentIndex()); } QDialog::showEvent(event); } void FolderSelectionDialog::rowsInserted(const QModelIndex &, int, int) { d->folderTreeWidget->folderTreeView()->expandAll(); } bool FolderSelectionDialog::canCreateCollection(Akonadi::Collection &parentCol) { parentCol = selectedCollection(); if (!parentCol.isValid()) { return false; } if ((parentCol.rights() & Akonadi::Collection::CanCreateCollection) && parentCol.contentMimeTypes().contains(Akonadi::Collection::mimeType())) { return true; } return false; } void FolderSelectionDialog::slotAddChildFolder() { Akonadi::Collection parentCol; if (canCreateCollection(parentCol)) { const QString name = QInputDialog::getText(this, i18nc("@title:window", "New Folder"), i18nc("@label:textbox, name of a thing", "Name")); if (name.isEmpty()) { return; } Akonadi::Collection col; col.setName(name); col.parentCollection().setId(parentCol.id()); Akonadi::CollectionCreateJob *job = new Akonadi::CollectionCreateJob(col); connect(job, &Akonadi::CollectionCreateJob::result, this, &FolderSelectionDialog::collectionCreationResult); } } void FolderSelectionDialog::collectionCreationResult(KJob *job) { if (job->error()) { KMessageBox::error( this, i18n("Could not create folder: %1", job->errorString()), i18n("Folder creation failed")); } } void FolderSelectionDialog::slotSelectionChanged() { const bool enablebuttons = (!d->folderTreeWidget->selectionModel()->selectedIndexes().isEmpty()); d->mOkButton->setEnabled(enablebuttons); if (!d->mNotAllowToCreateNewFolder) { Akonadi::Collection parent; d->mUser1Button->setEnabled(canCreateCollection(parent)); if (parent.isValid()) { const QSharedPointer fd(FolderSettings::forCollection(parent, false)); d->mOkButton->setEnabled(fd->canCreateMessages()); } } } void FolderSelectionDialog::setSelectionMode(QAbstractItemView::SelectionMode mode) { d->folderTreeWidget->setSelectionMode(mode); } QAbstractItemView::SelectionMode FolderSelectionDialog::selectionMode() const { return d->folderTreeWidget->selectionMode(); } Akonadi::Collection FolderSelectionDialog::selectedCollection() const { return d->folderTreeWidget->selectedCollection(); } void FolderSelectionDialog::setSelectedCollection(const Akonadi::Collection &collection) { d->folderTreeWidget->selectCollectionFolder(collection); } Akonadi::Collection::List FolderSelectionDialog::selectedCollections() const { return d->folderTreeWidget->selectedCollections(); } static const char myConfigGroupName[] = "FolderSelectionDialog"; void FolderSelectionDialog::readConfig() { KConfigGroup group(KernelIf->config(), myConfigGroupName); const QSize size = group.readEntry("Size", QSize(500, 300)); if (size.isValid()) { resize(size); } if (d->mUseGlobalSettings) { const Akonadi::Collection::Id id = SettingsIf->lastSelectedFolder(); if (id > -1) { const Akonadi::Collection col = Kernel::self()->collectionFromId(id); d->folderTreeWidget->selectCollectionFolder(col); } } } void FolderSelectionDialog::writeConfig() { KConfigGroup group(KernelIf->config(), myConfigGroupName); group.writeEntry("Size", size()); if (d->mUseGlobalSettings) { Akonadi::Collection col = selectedCollection(); if (col.isValid()) { SettingsIf->setLastSelectedFolder(col.id()); } } } void FolderSelectionDialog::hideEvent(QHideEvent *) { d->folderTreeWidget->clearFilter(); } } diff --git a/src/folder/foldertreewidget.cpp b/src/folder/foldertreewidget.cpp index 008ee6c..b68084e 100644 --- a/src/folder/foldertreewidget.cpp +++ b/src/folder/foldertreewidget.cpp @@ -1,400 +1,407 @@ /* Copyright (c) 2009-2020 Laurent Montel 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 */ #include "foldertreewidget.h" #include "entitycollectionorderproxymodel.h" #include "foldertreeview.h" #include "foldertreewidgetproxymodel.h" #include "kernel/mailkernel.h" #include "util/mailutil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace MailCommon { class Q_DECL_HIDDEN FolderTreeWidget::FolderTreeWidgetPrivate { public: FolderTreeWidgetPrivate() { } QString filter; QString oldFilterStr; Akonadi::StatisticsProxyModel *filterModel = nullptr; FolderTreeView *folderTreeView = nullptr; FolderTreeWidgetProxyModel *readableproxy = nullptr; EntityCollectionOrderProxyModel *entityOrderProxy = nullptr; QLineEdit *filterFolderLineEdit = nullptr; QPointer saver; QStringList expandedItems; QString currentItem; QLabel *label = nullptr; bool dontKeyFilter = false; }; FolderTreeWidget::FolderTreeWidget( QWidget *parent, KXMLGUIClient *xmlGuiClient, FolderTreeWidget::TreeViewOptions options, FolderTreeWidgetProxyModel::FolderTreeWidgetProxyModelOptions optReadableProxy) : QWidget(parent) , d(new FolderTreeWidgetPrivate()) { Akonadi::AttributeFactory::registerAttribute(); d->folderTreeView = new FolderTreeView(xmlGuiClient, this, options & ShowUnreadCount); d->folderTreeView->showStatisticAnimation(options & ShowCollectionStatisticAnimation); connect(d->folderTreeView, &FolderTreeView::manualSortingChanged, this, &FolderTreeWidget::slotManualSortingChanged); QVBoxLayout *lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); d->label = new QLabel(i18n("You can start typing to filter the list of folders."), this); lay->addWidget(d->label); d->filterFolderLineEdit = new QLineEdit(this); d->filterFolderLineEdit->setClearButtonEnabled(true); d->filterFolderLineEdit->setPlaceholderText( i18nc("@info Displayed grayed-out inside the textbox, verb to search", "Search")); lay->addWidget(d->filterFolderLineEdit); if (!(options & HideStatistics)) { d->filterModel = new Akonadi::StatisticsProxyModel(this); d->filterModel->setSourceModel(KernelIf->collectionModel()); } d->readableproxy = new FolderTreeWidgetProxyModel(this, optReadableProxy); d->readableproxy->setSourceModel((options & HideStatistics) ? static_cast(KernelIf->collectionModel()) : static_cast(d->filterModel)); d->readableproxy->addContentMimeTypeInclusionFilter(KMime::Message::mimeType()); connect(d->folderTreeView, &FolderTreeView::changeTooltipsPolicy, this, &FolderTreeWidget::slotChangeTooltipsPolicy); d->folderTreeView->setSelectionMode(QAbstractItemView::SingleSelection); d->folderTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); d->folderTreeView->installEventFilter(this); //Order proxy d->entityOrderProxy = new EntityCollectionOrderProxyModel(this); d->entityOrderProxy->setSourceModel(d->readableproxy); d->entityOrderProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); KConfigGroup grp(KernelIf->config(), "CollectionTreeOrder"); d->entityOrderProxy->setOrderConfig(grp); d->folderTreeView->setModel(d->entityOrderProxy); if (options & UseDistinctSelectionModel) { d->folderTreeView->setSelectionModel(new QItemSelectionModel(d->entityOrderProxy, this)); } lay->addWidget(d->folderTreeView); d->dontKeyFilter = (options & DontKeyFilter); if ((options & UseLineEditForFiltering)) { connect(d->filterFolderLineEdit, &QLineEdit::textChanged, this, &FolderTreeWidget::slotFilterFixedString); d->label->hide(); } else { d->filterFolderLineEdit->hide(); + setAttribute(Qt::WA_InputMethodEnabled); } } FolderTreeWidget::~FolderTreeWidget() { delete d; } void FolderTreeWidget::slotFilterFixedString(const QString &text) { delete d->saver; if (d->oldFilterStr.isEmpty()) { //Save it. Akonadi::ETMViewStateSaver saver; saver.setView(folderTreeView()); d->expandedItems = saver.expansionKeys(); d->currentItem = saver.currentIndexKey(); } else if (text.isEmpty()) { d->saver = new Akonadi::ETMViewStateSaver; d->saver->setView(folderTreeView()); QString currentIndex = d->saver->currentIndexKey(); if (d->saver->selectionKeys().isEmpty()) { currentIndex = d->currentItem; } else if (!currentIndex.isEmpty()) { d->expandedItems << currentIndex; } d->saver->restoreExpanded(d->expandedItems); d->saver->restoreCurrentItem(currentIndex); } else { d->folderTreeView->expandAll(); } d->oldFilterStr = text; d->entityOrderProxy->setFilterWildcard(text); } void FolderTreeWidget::disableContextMenuAndExtraColumn() { d->folderTreeView->disableContextMenuAndExtraColumn(); } void FolderTreeWidget::selectCollectionFolder(const Akonadi::Collection &collection) { const QModelIndex index = Akonadi::EntityTreeModel::modelIndexForCollection(d->folderTreeView->model(), collection); d->folderTreeView->setCurrentIndex(index); d->folderTreeView->setExpanded(index, true); d->folderTreeView->scrollTo(index); } void FolderTreeWidget::setSelectionMode(QAbstractItemView::SelectionMode mode) { d->folderTreeView->setSelectionMode(mode); } QAbstractItemView::SelectionMode FolderTreeWidget::selectionMode() const { return d->folderTreeView->selectionMode(); } QItemSelectionModel *FolderTreeWidget::selectionModel() const { return d->folderTreeView->selectionModel(); } QModelIndex FolderTreeWidget::currentIndex() const { return d->folderTreeView->currentIndex(); } Akonadi::Collection FolderTreeWidget::selectedCollection() const { if (d->folderTreeView->selectionMode() == QAbstractItemView::SingleSelection) { Akonadi::Collection::List lstCollection = selectedCollections(); if (lstCollection.isEmpty()) { return Akonadi::Collection(); } else { return lstCollection.at(0); } } return Akonadi::Collection(); } Akonadi::Collection::List FolderTreeWidget::selectedCollections() const { Akonadi::Collection::List collections; const QItemSelectionModel *selectionModel = d->folderTreeView->selectionModel(); const QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); for (const QModelIndex &index : selectedIndexes) { if (index.isValid()) { const Akonadi::Collection collection = index.model()->data( index, Akonadi::EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { collections.append(collection); } } } return collections; } FolderTreeView *FolderTreeWidget::folderTreeView() const { return d->folderTreeView; } void FolderTreeWidget::slotGeneralFontChanged() { // Custom/System font support if (MessageCore::MessageCoreSettings::self()->useDefaultFonts()) { setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); } } void FolderTreeWidget::slotGeneralPaletteChanged() { d->readableproxy->updatePalette(); d->folderTreeView->updatePalette(); } void FolderTreeWidget::readConfig() { setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); d->folderTreeView->readConfig(); d->folderTreeView->setDropActionMenuEnabled(SettingsIf->showPopupAfterDnD()); d->readableproxy->setWarningThreshold(SettingsIf->closeToQuotaThreshold()); d->readableproxy->readConfig(); KConfigGroup readerConfig(KernelIf->config(), "AccountOrder"); QStringList listOrder; if (readerConfig.readEntry("EnableAccountOrder", true)) { listOrder = readerConfig.readEntry("order", QStringList()); } d->entityOrderProxy->setTopLevelOrder(listOrder); } void FolderTreeWidget::restoreHeaderState(const QByteArray &data) { d->folderTreeView->restoreHeaderState(data); } void FolderTreeWidget::slotChangeTooltipsPolicy(FolderTreeWidget::ToolTipDisplayPolicy policy) { changeToolTipsPolicyConfig(policy); } void FolderTreeWidget::changeToolTipsPolicyConfig(ToolTipDisplayPolicy policy) { switch (policy) { case DisplayAlways: case DisplayWhenTextElided: //Need to implement in the future if (d->filterModel) { d->filterModel->setToolTipEnabled(true); } break; case DisplayNever: if (d->filterModel) { d->filterModel->setToolTipEnabled(false); } } d->folderTreeView->setTooltipsPolicy(policy); } Akonadi::StatisticsProxyModel *FolderTreeWidget::statisticsProxyModel() const { return d->filterModel; } FolderTreeWidgetProxyModel *FolderTreeWidget::folderTreeWidgetProxyModel() const { return d->readableproxy; } EntityCollectionOrderProxyModel *FolderTreeWidget::entityOrderProxy() const { return d->entityOrderProxy; } QLineEdit *FolderTreeWidget::filterFolderLineEdit() const { return d->filterFolderLineEdit; } void FolderTreeWidget::applyFilter(const QString &filter) { d->label->setText( filter.isEmpty() ? i18n("You can start typing to filter the list of folders.") : i18n("Path: (%1)", filter)); d->entityOrderProxy->setFilterWildcard(filter); d->folderTreeView->expandAll(); QAbstractItemModel *model = d->folderTreeView->model(); QModelIndex current = d->folderTreeView->currentIndex(); QModelIndex start = current.isValid() ? current : model->index(0, 0); QModelIndexList list = model->match(start, Qt::DisplayRole, d->filter, 1 /* stop at first hit */, Qt::MatchContains | Qt::MatchWrap | Qt::MatchRecursive); if (!list.isEmpty()) { current = list.first(); d->folderTreeView->setCurrentIndex(current); d->folderTreeView->scrollTo(current); } } void FolderTreeWidget::clearFilter() { d->filter.clear(); applyFilter(d->filter); const QModelIndexList lst = d->folderTreeView->selectionModel()->selectedIndexes(); if (!lst.isEmpty()) { d->folderTreeView->scrollTo(lst.first()); } } void FolderTreeWidget::slotManualSortingChanged(bool active) { d->entityOrderProxy->setManualSortingActive(active); d->folderTreeView->setManualSortingActive(active); } bool FolderTreeWidget::eventFilter(QObject *o, QEvent *e) { Q_UNUSED(o); if (d->dontKeyFilter) { return false; } if (e->type() == QEvent::KeyPress) { const QKeyEvent *const ke = static_cast(e); switch (ke->key()) { case Qt::Key_Backspace: { const int filterLength(d->filter.length()); if (filterLength > 0) { d->filter.truncate(filterLength - 1); applyFilter(d->filter); } return false; } case Qt::Key_Delete: d->filter.clear(); applyFilter(d->filter); return false; default: { const QString s = ke->text(); if (!s.isEmpty() && s.at(0).isPrint()) { d->filter += s; applyFilter(d->filter); return false; } break; } } + } else if (e->type() == QEvent::InputMethod) { + const QInputMethodEvent *const ime = static_cast(e); + d->filter += ime->commitString(); + applyFilter(d->filter); + return false; + } return false; } }