diff --git a/krusader/Filter/generalfilter.cpp b/krusader/Filter/generalfilter.cpp index 5f9ff209..d15199b7 100644 --- a/krusader/Filter/generalfilter.cpp +++ b/krusader/Filter/generalfilter.cpp @@ -1,666 +1,666 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "generalfilter.h" #include "filtertabs.h" #include "../krglobal.h" #include "../krservices.h" #include "../FileSystem/filesystem.h" // QtWidgets #include #include #include #include #include #include #include #include #include #include typedef struct { const char *description; const char *regExp; int cursorAdjustment; } term; static const term items[] = { { I18N_NOOP("Any Character"), ".", 0 }, { I18N_NOOP("Start of Line"), "^", 0 }, { I18N_NOOP("End of Line"), "$", 0 }, { I18N_NOOP("Set of Characters"), "[]", -1 }, { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 }, { I18N_NOOP("Repeats, One or More Times"), "+", 0 }, { I18N_NOOP("Optional"), "?", 0 }, { I18N_NOOP("Escape"), "\\", 0 }, { I18N_NOOP("TAB"), "\\t", 0 }, { I18N_NOOP("Newline"), "\\n", 0 }, { I18N_NOOP("Carriage Return"), "\\r", 0 }, { I18N_NOOP("White Space"), "\\s", 0 }, { I18N_NOOP("Digit"), "\\d", 0 }, }; class RegExpAction : public QAction { public: RegExpAction(QObject *parent, const QString &text, const QString ®Exp, int cursor) : QAction(text, parent), mText(text), mRegExp(regExp), mCursor(cursor) { } QString text() const { return mText; } QString regExp() const { return mRegExp; } int cursor() const { return mCursor; } private: QString mText; QString mRegExp; int mCursor; }; GeneralFilter::GeneralFilter(FilterTabs *tabs, int properties, QWidget *parent, QStringList extraOptions) : QWidget(parent), profileManager(nullptr), fltTabs(tabs) { auto *filterLayout = new QGridLayout(this); filterLayout->setSpacing(6); filterLayout->setContentsMargins(11, 11, 11, 11); this->properties = properties; // Options for name filtering auto *nameGroup = new QGroupBox(this); nameGroup->setTitle(i18n("File Name")); auto *nameGroupLayout = new QGridLayout(nameGroup); nameGroupLayout->setAlignment(Qt::AlignTop); nameGroupLayout->setSpacing(6); nameGroupLayout->setContentsMargins(11, 11, 11, 11); searchForCase = new QCheckBox(nameGroup); searchForCase->setText(i18n("&Case sensitive")); searchForCase->setChecked(false); nameGroupLayout->addWidget(searchForCase, 1, 2); QLabel *searchForLabel = new QLabel(nameGroup); searchForLabel->setText(i18n("Search &for:")); nameGroupLayout->addWidget(searchForLabel, 0, 0); - searchFor = new KHistoryComboBox(false, nameGroup/*, "searchFor"*/); + searchFor = new KrHistoryComboBox(false, nameGroup/*, "searchFor"*/); QSizePolicy searchForPolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); searchForPolicy.setHeightForWidth(searchFor->sizePolicy().hasHeightForWidth()); searchFor->setSizePolicy(searchForPolicy); searchFor->setEditable(true); searchFor->setDuplicatesEnabled(false); searchFor->setMaxCount(25); searchFor->setMinimumContentsLength(10); searchForLabel->setBuddy(searchFor); nameGroupLayout->addWidget(searchFor, 0, 1, 1, 2); const QString s = "

" + i18n("

The filename filtering criteria is defined here.

" "

You can make use of wildcards. Multiple patterns are separated by " "space (means logical OR) and patterns are excluded from the search " "using the pipe symbol.

" "

If the pattern is ended with a slash (*pattern*/), " "that means that pattern relates to recursive search of folders." "

  • pattern - means to search those files/folders " "that name is pattern, recursive search goes through all " "subfolders independently of the value of pattern
  • " "
  • pattern/ - means to search all files/folders, but " "recursive search goes through/excludes the folders that name is " "pattern

" "

It is allowed to use quotation marks for names that contain space." " Filter \"Program Files\" searches out those " "files/folders that name is Program Files.

" "

Examples:

    " "
  • *.o
  • " "
  • *.h *.c\?\?
  • " "
  • *.cpp *.h | *.moc.cpp
  • " "
  • * | .svn/ .git/

" "Note: the search term 'text' is equivalent to " "'*text*'.

"); searchFor->setWhatsThis(s); searchForLabel->setWhatsThis(s); QLabel *searchType = new QLabel(nameGroup); searchType->setText(i18n("&Of type:")); nameGroupLayout->addWidget(searchType, 1, 0); ofType = new KComboBox(false, nameGroup); ofType->setObjectName("ofType"); QSizePolicy ofTypePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); ofTypePolicy.setHeightForWidth(ofType->sizePolicy().hasHeightForWidth()); ofType->setSizePolicy(ofTypePolicy); ofType->setEditable(false); searchType->setBuddy(ofType); ofType->addItem(i18n("All Files")); ofType->addItem(i18n("Archives")); ofType->addItem(i18n("Folders")); ofType->addItem(i18n("Image Files")); ofType->addItem(i18n("Text Files")); ofType->addItem(i18n("Video Files")); ofType->addItem(i18n("Audio Files")); connect(ofType, QOverload::of(&KComboBox::currentIndexChanged), this, &GeneralFilter::slotDisable); nameGroupLayout->addWidget(ofType, 1, 1); filterLayout->addWidget(nameGroup, 0, 0); middleLayout = new QHBoxLayout(); middleLayout->setSpacing(6); middleLayout->setContentsMargins(0, 0, 0, 0); if (properties & FilterTabs::HasProfileHandler) { // The profile handler auto *profileHandler = new QGroupBox(this); profileHandler->setTitle(i18n("&Profile handler")); auto *profileLayout = new QGridLayout(profileHandler); profileLayout->setAlignment(Qt::AlignTop); profileLayout->setSpacing(6); profileLayout->setContentsMargins(11, 11, 11, 11); profileListBox = new KrListWidget(profileHandler); profileLayout->addWidget(profileListBox, 0, 0, 4, 1); profileAddBtn = new QPushButton(profileHandler); KStandardGuiItem::assign(profileAddBtn, KStandardGuiItem::Add); profileLayout->addWidget(profileAddBtn, 0, 1); profileLoadBtn = new QPushButton(i18n("&Load"), profileHandler); profileLoadBtn->setEnabled(false); profileLayout->addWidget(profileLoadBtn, 1, 1); profileOverwriteBtn = new QPushButton(profileHandler); profileOverwriteBtn->setEnabled(false); KStandardGuiItem::assign(profileOverwriteBtn, KStandardGuiItem::Overwrite); profileLayout->addWidget(profileOverwriteBtn, 2, 1); profileRemoveBtn = new QPushButton(profileHandler); profileRemoveBtn->setEnabled(false); KStandardGuiItem::assign(profileRemoveBtn, KStandardGuiItem::Remove); profileLayout->addWidget(profileRemoveBtn, 3, 1); profileManager = new ProfileManager("SelectionProfile", this); profileManager->hide(); middleLayout->addWidget(profileHandler); refreshProfileListBox(); } if (properties & FilterTabs::HasSearchIn) { // Options for search in QGroupBox *searchGroupBox = new QGroupBox(i18n("Searc&h in"), this); auto *searchLayout = new QGridLayout(searchGroupBox); searchLayout->setAlignment(Qt::AlignTop); searchLayout->setSpacing(6); searchLayout->setContentsMargins(11, 11, 11, 11); searchIn = new KURLListRequester(KURLListRequester::RequestDirs, searchGroupBox); searchLayout->addWidget(searchIn, 0, 0); connect(searchIn, &KURLListRequester::changed, this, &GeneralFilter::slotDisable); middleLayout->addWidget(searchGroupBox); } if (properties & FilterTabs::HasDontSearchIn) { // Options for don't search in QGroupBox *searchGroupBox = new QGroupBox(i18n("&Do not search in"), this); auto *searchLayout = new QGridLayout(searchGroupBox); searchLayout->setAlignment(Qt::AlignTop); searchLayout->setSpacing(6); searchLayout->setContentsMargins(11, 11, 11, 11); dontSearchIn = new KURLListRequester(KURLListRequester::RequestDirs, searchGroupBox); searchLayout->addWidget(dontSearchIn, 0, 0, 1, 2); if (properties & FilterTabs::HasRecurseOptions) { KConfigGroup group(krConfig, "Search"); useExcludeFolderNames = createExcludeCheckBox(group); searchLayout->addWidget(useExcludeFolderNames, 1, 0, 1, 1); excludeFolderNames = createExcludeComboBox(group); searchLayout->addWidget(excludeFolderNames, 1, 1, 1, 1); if (!useExcludeFolderNames->isChecked()) { excludeFolderNames->setDisabled(true); } connect(useExcludeFolderNames, &QCheckBox::toggled, excludeFolderNames, &KHistoryComboBox::setEnabled); } middleLayout->addWidget(searchGroupBox); } filterLayout->addLayout(middleLayout, 1, 0); // Options for containing text auto *containsGroup = new QGroupBox(this); containsGroup->setTitle(i18n("Containing text")); auto *containsLayout = new QGridLayout(containsGroup); containsLayout->setAlignment(Qt::AlignTop); containsLayout->setSpacing(6); containsLayout->setContentsMargins(11, 11, 11, 11); auto *containsTextLayout = new QHBoxLayout(); containsTextLayout->setSpacing(6); containsTextLayout->setContentsMargins(0, 0, 0, 0); containsLabel = new QLabel(containsGroup); QSizePolicy containsLabelPolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); containsLabelPolicy.setHeightForWidth(containsLabel->sizePolicy().hasHeightForWidth()); containsLabel->setSizePolicy(containsLabelPolicy); containsLabel->setText(i18n("&Text:")); containsTextLayout->addWidget(containsLabel); containsText = new KHistoryComboBox(false, containsGroup/*, "containsText"*/); QSizePolicy containsTextPolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); containsTextPolicy.setHeightForWidth(containsText->sizePolicy().hasHeightForWidth()); containsText->setSizePolicy(containsTextPolicy); containsText->setDuplicatesEnabled(false); containsText->setMaxCount(25); containsText->setMinimumContentsLength(10); containsTextLayout->addWidget(containsText); containsLabel->setBuddy(containsText); containsRegExp = new QToolButton(containsGroup); containsRegExp->setPopupMode(QToolButton::MenuButtonPopup); containsRegExp->setCheckable(true); containsRegExp->setText(i18n("RegExp")); // Populate the popup menu. auto *patterns = new QMenu(containsRegExp); for (int i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++) { patterns->addAction(new RegExpAction(patterns, i18n(items[i].description), items[i].regExp, items[i].cursorAdjustment)); } connect(containsRegExp, &QToolButton::toggled, this, &GeneralFilter::slotDisable); connect(containsRegExp, &QToolButton::triggered, this, &GeneralFilter::slotRegExpTriggered); containsRegExp->setMenu(patterns); patterns->setEnabled(false); containsTextLayout->addWidget(containsRegExp); containsLayout->addLayout(containsTextLayout, 0, 0); auto *containsCbsLayout = new QHBoxLayout(); containsCbsLayout->setSpacing(6); containsCbsLayout->setContentsMargins(0, 0, 0, 0); encLabel = new QLabel(i18n("Encoding:"), containsGroup); containsCbsLayout->addWidget(encLabel); contentEncoding = new KComboBox(containsGroup); contentEncoding->setEditable(false); contentEncoding->addItem(i18nc("Default encoding", "Default")); contentEncoding->addItems(KCharsets::charsets()->descriptiveEncodingNames()); containsCbsLayout->addWidget(contentEncoding); auto* cbSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); containsCbsLayout->addItem(cbSpacer); containsWholeWord = new QCheckBox(containsGroup); QSizePolicy containsWholeWordPolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); containsWholeWordPolicy.setHeightForWidth(containsWholeWord->sizePolicy().hasHeightForWidth()); containsWholeWord->setSizePolicy(containsWholeWordPolicy); containsWholeWord->setText(i18n("&Match whole word only")); containsWholeWord->setChecked(false); containsCbsLayout->addWidget(containsWholeWord); containsTextCase = new QCheckBox(containsGroup); QSizePolicy containsTextCasePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); containsTextCasePolicy.setHeightForWidth(containsTextCase->sizePolicy().hasHeightForWidth()); containsTextCase->setSizePolicy(containsTextCasePolicy); containsTextCase->setText(i18n("Cas&e sensitive")); containsTextCase->setChecked(true); containsCbsLayout->addWidget(containsTextCase); containsLayout->addLayout(containsCbsLayout, 1, 0); filterLayout->addWidget(containsGroup, 2, 0); auto *recurseLayout = new QHBoxLayout(); recurseLayout->setSpacing(6); recurseLayout->setContentsMargins(0, 0, 0, 0); auto* recurseSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); recurseLayout->addItem(recurseSpacer); if (properties & FilterTabs::HasRecurseOptions) { // Options for recursive searching searchInDirs = new QCheckBox(this); searchInDirs->setText(i18n("Search in s&ub folders")); searchInDirs->setChecked(true); recurseLayout->addWidget(searchInDirs); searchInArchives = new QCheckBox(this); searchInArchives->setText(i18n("Search in arch&ives")); recurseLayout->addWidget(searchInArchives); followLinks = new QCheckBox(this); followLinks->setText(i18n("Follow &links")); recurseLayout->addWidget(followLinks); } filterLayout->addLayout(recurseLayout, 3, 0); for(int i = 0; i < extraOptions.length(); i++) { auto *option = new QCheckBox(this); option->setText(extraOptions[i]); recurseLayout->addWidget(option); this->extraOptions.insert(extraOptions[i], option); } // Connection table if (properties & FilterTabs::HasProfileHandler) { connect(profileAddBtn, &QPushButton::clicked, this, &GeneralFilter::slotAddBtnClicked); connect(profileLoadBtn, &QPushButton::clicked, this, &GeneralFilter::slotLoadBtnClicked); connect(profileOverwriteBtn, &QPushButton::clicked, this, &GeneralFilter::slotOverwriteBtnClicked); connect(profileRemoveBtn, &QPushButton::clicked, this, &GeneralFilter::slotRemoveBtnClicked); connect(profileListBox, &KrListWidget::itemDoubleClicked, this, &GeneralFilter::slotProfileDoubleClicked); connect(profileManager, &ProfileManager::loadFromProfile, fltTabs, &FilterTabs::loadFromProfile); connect(profileManager, &ProfileManager::saveToProfile, fltTabs, &FilterTabs::saveToProfile); } connect(searchFor, QOverload::of(&KHistoryComboBox::activated), searchFor, &KHistoryComboBox::addToHistory); connect(containsText, QOverload::of(&KHistoryComboBox::activated), containsText, &KHistoryComboBox::addToHistory); // load the completion and history lists // ==> search for KConfigGroup group(krConfig, "Search"); QStringList list = group.readEntry("SearchFor Completion", QStringList()); searchFor->completionObject()->setItems(list); list = group.readEntry("SearchFor History", QStringList()); searchFor->setHistoryItems(list); // ==> grep list = group.readEntry("ContainsText Completion", QStringList()); containsText->completionObject()->setItems(list); list = group.readEntry("ContainsText History", QStringList()); containsText->setHistoryItems(list); setTabOrder(searchFor, containsText); // search for -> content setTabOrder(containsText, searchType); // content -> search type slotDisable(); } GeneralFilter::~GeneralFilter() { // save the history combos // ==> search for QStringList list = searchFor->completionObject()->items(); KConfigGroup group(krConfig, "Search"); group.writeEntry("SearchFor Completion", list); list = searchFor->historyItems(); group.writeEntry("SearchFor History", list); // ==> grep text list = containsText->completionObject()->items(); group.writeEntry("ContainsText Completion", list); list = containsText->historyItems(); group.writeEntry("ContainsText History", list); if ((properties & FilterTabs::HasDontSearchIn) && (properties & FilterTabs::HasRecurseOptions)) { list = excludeFolderNames->historyItems(); group.writeEntry("ExcludeFolderNamesHistory", list); group.writeEntry("ExcludeFolderNames", excludeFolderNames->currentText()); group.writeEntry("ExcludeFolderNamesUse", static_cast(useExcludeFolderNames->checkState())); } krConfig->sync(); } bool GeneralFilter::isExtraOptionChecked(const QString& name) { QCheckBox *option = extraOptions[name]; return option ? option->isChecked() : false; } void GeneralFilter::checkExtraOption(const QString& name, bool check) { QCheckBox *option = extraOptions[name]; if (option) option->setChecked(check); } void GeneralFilter::queryAccepted() { searchFor->addToHistory(searchFor->currentText()); containsText->addToHistory(containsText->currentText()); if ((properties & FilterTabs::HasDontSearchIn) && (properties & FilterTabs::HasRecurseOptions)) { excludeFolderNames->addToHistory(excludeFolderNames->currentText()); } } void GeneralFilter::refreshProfileListBox() { profileListBox->clear(); profileListBox->addItems(ProfileManager::availableProfiles("SelectionProfile")); if (profileListBox->count() != 0) { profileLoadBtn->setEnabled(true); profileOverwriteBtn->setEnabled(true); profileRemoveBtn->setEnabled(true); } else { profileLoadBtn->setEnabled(false); profileOverwriteBtn->setEnabled(false); profileRemoveBtn->setEnabled(false); } } QCheckBox *GeneralFilter::createExcludeCheckBox(const KConfigGroup &group) { auto *excludeCheckBox = new QCheckBox(this); excludeCheckBox->setText(i18n("Exclude Folder Names")); excludeCheckBox->setToolTip(i18n("Filters out specified directory names from the results.")); excludeCheckBox->setChecked(static_cast(group.readEntry("ExcludeFolderNamesUse", 0))); return excludeCheckBox; } KHistoryComboBox *GeneralFilter::createExcludeComboBox(const KConfigGroup &group) { auto *excludeComboBox = new KHistoryComboBox(false, this); QSizePolicy excludeFolderNamesPolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); excludeFolderNamesPolicy.setHeightForWidth(excludeComboBox->sizePolicy().hasHeightForWidth()); excludeComboBox->setSizePolicy(excludeFolderNamesPolicy); excludeComboBox->setEditable(true); excludeComboBox->setDuplicatesEnabled(false); excludeComboBox->setMaxCount(25); excludeComboBox->setMinimumContentsLength(10); excludeComboBox->lineEdit()->setPlaceholderText(i18n("Enter space-separated folder names")); excludeComboBox->lineEdit()->setWhatsThis( i18n("You can insert names with escaped spaces or quoted.\nExample: .git \"target " "build\" build\\ krusader")); excludeComboBox->setHistoryItems(group.readEntry("ExcludeFolderNamesHistory", QStringList())); excludeComboBox->setEditText(group.readEntry("ExcludeFolderNames", "")); return excludeComboBox; } void GeneralFilter::slotAddBtnClicked() { profileManager->newProfile(searchFor->currentText().simplified()); refreshProfileListBox(); } void GeneralFilter::slotOverwriteBtnClicked() { QListWidgetItem *item = profileListBox->currentItem(); if (item != nullptr) profileManager->overwriteProfile(item->text()); } void GeneralFilter::slotRemoveBtnClicked() { QListWidgetItem *item = profileListBox->currentItem(); if (item != nullptr) { profileManager->deleteProfile(item->text()); refreshProfileListBox(); } } void GeneralFilter::slotProfileDoubleClicked(QListWidgetItem *item) { if (item != nullptr) { QString profileName = item->text(); profileManager->loadProfile(profileName); fltTabs->close(true); } } void GeneralFilter::slotLoadBtnClicked() { QListWidgetItem *item = profileListBox->currentItem(); if (item != nullptr) profileManager->loadProfile(item->text()); } void GeneralFilter::slotDisable() { bool state = containsRegExp->isChecked(); bool global = ofType->currentText() != i18n("Folders"); bool remoteOnly = false; if (properties & FilterTabs::HasSearchIn) { QList urlList = searchIn->urlList(); remoteOnly = urlList.count() != 0; foreach(const QUrl &url, urlList) if (url.scheme() == "file") remoteOnly = false; } containsWholeWord->setEnabled(!state && global); containsRegExp->menu()->setEnabled(state && global); encLabel->setEnabled(global); contentEncoding->setEnabled(global); containsTextCase->setEnabled(global); containsRegExp->setEnabled(global); if (properties & FilterTabs::HasRecurseOptions) searchInArchives->setEnabled(global && !remoteOnly); containsLabel->setEnabled(global); containsText->setEnabled(global); } void GeneralFilter::slotRegExpTriggered(QAction * act) { if (act == nullptr) return; auto *regAct = dynamic_cast(act); if (regAct == nullptr) return; containsText->lineEdit()->insert(regAct->regExp()); containsText->lineEdit()->setCursorPosition(containsText->lineEdit()->cursorPosition() + regAct->cursor()); containsText->lineEdit()->setFocus(); } bool GeneralFilter::getSettings(FilterSettings &s) { // check that we have (at least) what to search, and where to search in if (searchFor->currentText().simplified().isEmpty()) { KMessageBox::error(this , i18n("No search criteria entered.")); searchFor->setFocus(); return false; } s.searchFor = searchFor->currentText().trimmed(); s.searchForCase = searchForCase->isChecked(); if (ofType->currentText() != i18n("All Files")) s.mimeType = ofType->currentText(); if (containsText->isEnabled()) { s.containsText = containsText->currentText(); s.containsTextCase = containsTextCase->isChecked(); s.containsWholeWord = containsWholeWord->isChecked(); s.containsRegExp = containsRegExp->isChecked(); } if (contentEncoding->currentIndex() != 0) s.contentEncoding = KCharsets::charsets()->encodingForName(contentEncoding->currentText()); if (properties & FilterTabs::HasRecurseOptions) { s.recursive = searchInDirs->isChecked(); s.searchInArchives = searchInArchives->isChecked(); s.followLinks = followLinks->isChecked(); } if (properties & FilterTabs::HasSearchIn) { s.searchIn = searchIn->urlList(); if (s.searchIn.isEmpty()) { // we need a place to search in KMessageBox::error(this , i18n("Please specify a location to search in.")); searchIn->lineEdit()->setFocus(); return false; } } if (properties & FilterTabs::HasDontSearchIn) { s.dontSearchIn = dontSearchIn->urlList(); if (properties & FilterTabs::HasRecurseOptions) { if (useExcludeFolderNames->isChecked()) { s.excludeFolderNames = KShell::splitArgs(excludeFolderNames->currentText()); } else { s.excludeFolderNames = QStringList(); } } } return true; } void GeneralFilter::applySettings(const FilterSettings &s) { searchFor->setEditText(s.searchFor); searchForCase->setChecked(s.searchForCase); setComboBoxValue(ofType, s.mimeType); containsText->setEditText(s.containsText); containsTextCase->setChecked(s.containsTextCase); containsWholeWord->setChecked(s.containsWholeWord); containsRegExp->setChecked(s.containsRegExp); setComboBoxValue(contentEncoding, KCharsets::charsets()->descriptionForEncoding(s.contentEncoding)); if (properties & FilterTabs::HasRecurseOptions) { searchInDirs->setChecked(s.recursive); searchInArchives->setChecked(s.searchInArchives); followLinks->setChecked(s.followLinks); } if (properties & FilterTabs::HasSearchIn) { searchIn->lineEdit()->clear(); searchIn->listBox()->clear(); searchIn->listBox()->addItems(KrServices::toStringList(s.searchIn)); } if (properties & FilterTabs::HasDontSearchIn) { dontSearchIn->lineEdit()->clear(); dontSearchIn->listBox()->clear(); dontSearchIn->listBox()->addItems(KrServices::toStringList(s.dontSearchIn)); } } diff --git a/krusader/Filter/generalfilter.h b/krusader/Filter/generalfilter.h index 2d21b526..83fa26b7 100644 --- a/krusader/Filter/generalfilter.h +++ b/krusader/Filter/generalfilter.h @@ -1,118 +1,119 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef GENERALFILTER_H #define GENERALFILTER_H // QtWidgets #include #include #include #include #include #include #include #include #include #include "filterbase.h" #include "../Dialogs/kurllistrequester.h" #include "../GUI/profilemanager.h" #include "../GUI/krlistwidget.h" +#include "../GUI/krhistorycombobox.h" class GeneralFilter : public QWidget, public FilterBase { Q_OBJECT public: GeneralFilter(FilterTabs *tabs, int properties, QWidget *parent = nullptr, QStringList extraOptions = QStringList()); ~GeneralFilter() override; void queryAccepted() Q_DECL_OVERRIDE; QString name() Q_DECL_OVERRIDE { return "GeneralFilter"; } FilterTabs * filterTabs() Q_DECL_OVERRIDE { return fltTabs; } bool getSettings(FilterSettings&) Q_DECL_OVERRIDE; void applySettings(const FilterSettings&) Q_DECL_OVERRIDE; bool isExtraOptionChecked(const QString& name); void checkExtraOption(const QString& name, bool check); public slots: void slotAddBtnClicked(); void slotLoadBtnClicked(); void slotOverwriteBtnClicked(); void slotRemoveBtnClicked(); void slotDisable(); void slotRegExpTriggered(QAction * act); void slotProfileDoubleClicked(QListWidgetItem *); void refreshProfileListBox(); public: KComboBox* contentEncoding; QCheckBox* searchForCase; QCheckBox* containsTextCase; QCheckBox* containsWholeWord; QCheckBox* useExcludeFolderNames; QCheckBox* searchInDirs; QCheckBox* searchInArchives; QCheckBox* followLinks; QHash extraOptions; KURLListRequester *searchIn; KURLListRequester *dontSearchIn; QLayout *middleLayout; - KHistoryComboBox* searchFor; + KrHistoryComboBox* searchFor; KHistoryComboBox* containsText; KHistoryComboBox* excludeFolderNames; QToolButton* containsRegExp; KComboBox* ofType; QLabel *encLabel; QLabel *containsLabel; KShellCompletion completion; KrListWidget *profileListBox; QPushButton *profileAddBtn; QPushButton *profileLoadBtn; QPushButton *profileOverwriteBtn; QPushButton *profileRemoveBtn; ProfileManager *profileManager; int properties; FilterTabs *fltTabs; private: QCheckBox *createExcludeCheckBox(const KConfigGroup &group); KHistoryComboBox *createExcludeComboBox(const KConfigGroup &group); }; #endif /* GENERALFILTER_H */ diff --git a/krusader/GUI/CMakeLists.txt b/krusader/GUI/CMakeLists.txt index 3e233074..82a4082c 100644 --- a/krusader/GUI/CMakeLists.txt +++ b/krusader/GUI/CMakeLists.txt @@ -1,26 +1,27 @@ set(GUI_SRCS dirhistorybutton.cpp krusaderstatus.cpp kfnkeys.cpp kcmdline.cpp profilemanager.cpp + krhistorycombobox.cpp krremoteencodingmenu.cpp krtreewidget.cpp krstyleproxy.cpp krlistwidget.cpp mediabutton.cpp kcmdmodebutton.cpp terminaldock.cpp) add_library(GUI STATIC ${GUI_SRCS}) target_link_libraries(GUI KF5::ConfigCore KF5::CoreAddons KF5::I18n KF5::IconThemes KF5::Parts KF5::Service KF5::Solid KF5::WidgetsAddons ) diff --git a/krusader/GUI/krhistorycombobox.cpp b/krusader/GUI/krhistorycombobox.cpp new file mode 100755 index 00000000..9cdd77e9 --- /dev/null +++ b/krusader/GUI/krhistorycombobox.cpp @@ -0,0 +1,118 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 Shie Erlich * + * Copyright (C) 2018-2019 Rafi Yanai * + * Copyright (C) 2018-2019 Krusader Krew [https://krusader.org] * + * * + * This file is part of Krusader [https://krusader.org]. * + * * + * Krusader 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. * + * * + * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * + *****************************************************************************/ + +#include "krhistorycombobox.h" + +// QtCore +#include +// QtGui +#include +// QtWidgets +#include + +/** + * A KrHistoryComboBox event filter that e.g. deletes the current item when Shift+Del is pressed + * There was more information in https://doc.qt.io/qt-5/qobject.html#installEventFilter, + * https://forum.qt.io/post/160618 and + * https://stackoverflow.com/questions/17820947/remove-items-from-qcombobox-from-ui/52459337#52459337 + */ +class KHBoxEventFilter : public QObject +{ + Q_OBJECT + +public: + explicit KHBoxEventFilter(QObject *parent = nullptr) : QObject(parent) {} + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +}; + +bool KHBoxEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + auto keyEvent = static_cast(event); + if (keyEvent->modifiers() == Qt::ShiftModifier && keyEvent->key() == Qt::Key::Key_Delete) { + auto comboBox = dynamic_cast(obj); + if (comboBox != nullptr) { + QString entryToDelete = comboBox->currentText(); + // Delete the current item + comboBox->removeItem(comboBox->currentIndex()); + // The item has to be deleted also from the completion list + comboBox->completionObject()->removeItem(entryToDelete); + return true; + } + } + } + // Perform the usual event processing + return QObject::eventFilter(obj, event); +} + +/** + * An event filter for the popup list of a KrHistoryComboBox, e.g. it deletes the current + * item when the user presses Shift+Del + */ +class KHBoxListEventFilter : public QObject +{ + Q_OBJECT + +public: + explicit KHBoxListEventFilter(QObject *parent = nullptr) : QObject(parent) {} + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +}; + +bool KHBoxListEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + auto keyEvent = static_cast(event); + if (keyEvent->modifiers() == Qt::ShiftModifier && keyEvent->key() == Qt::Key::Key_Delete) { + auto itemView = dynamic_cast(obj); + if (itemView->model() != nullptr) { + QString entryToDelete = itemView->currentIndex().data().toString(); + // Delete the current item from the popup list + itemView->model()->removeRow(itemView->currentIndex().row()); + // The item has to be deleted also from the completion list of the KHistoryComboBox + if (itemView->parent() != nullptr) { + auto comboBox = dynamic_cast(itemView->parent()->parent()); + if (comboBox != nullptr) { + comboBox->completionObject()->removeItem(entryToDelete); + return true; + } + } + } + } + } + // Perform the usual event processing + return QObject::eventFilter(obj, event); +} + +#include "krhistorycombobox.moc" // required for class definitions with Q_OBJECT macro in implementation files + +KrHistoryComboBox::KrHistoryComboBox(bool useCompletion, QWidget *parent) + : KHistoryComboBox(useCompletion, parent) +{ + installEventFilter(new KHBoxEventFilter(this)); + + QAbstractItemView *itemView = view(); + if (itemView != nullptr) + itemView->installEventFilter(new KHBoxListEventFilter(this)); +} diff --git a/krusader/GUI/krhistorycombobox.h b/krusader/GUI/krhistorycombobox.h new file mode 100755 index 00000000..49056cd3 --- /dev/null +++ b/krusader/GUI/krhistorycombobox.h @@ -0,0 +1,39 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 Shie Erlich * + * Copyright (C) 2018-2019 Rafi Yanai * + * Copyright (C) 2018-2019 Krusader Krew [https://krusader.org] * + * * + * This file is part of Krusader [https://krusader.org]. * + * * + * Krusader 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. * + * * + * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * + *****************************************************************************/ + +#ifndef KRHISTORYCOMBOBOX_H +#define KRHISTORYCOMBOBOX_H + +#include + +/** + * A specialized version of a KHistoryComboBox, e.g. it deletes the current + * item when the user presses Shift+Del + */ +class KrHistoryComboBox : public KHistoryComboBox +{ + Q_OBJECT + +public: + explicit KrHistoryComboBox(bool useCompletion, QWidget *parent = nullptr); +}; + +#endif // KRHISTORYCOMBOBOX_H