From a9cf7685c9ebe3ef5e53c84f8542b3c16a77b4c1 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 26 Jan 2021 14:47:08 +0100 Subject: [PATCH] Make commands for File Name Index locator filter configurable Change-Id: I4a110ed9184345eb6992f4fda59a76fc843b2f1e Reviewed-by: Cristian Adam Reviewed-by: David Schulz --- .../coreplugin/locator/ilocatorfilter.cpp | 84 ++++---- .../coreplugin/locator/ilocatorfilter.h | 1 + .../locator/spotlightlocatorfilter.cpp | 183 +++++++++++++++--- .../locator/spotlightlocatorfilter.h | 12 +- 4 files changed, 218 insertions(+), 62 deletions(-) diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp index 31f63cf72c5..ec064bba522 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp @@ -180,39 +180,7 @@ void ILocatorFilter::restoreState(const QByteArray &state) bool ILocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh) { Q_UNUSED(needsRefresh) - - QDialog dialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); - dialog.setWindowTitle(msgConfigureDialogTitle()); - - auto vlayout = new QVBoxLayout(&dialog); - auto hlayout = new QHBoxLayout; - QLineEdit *shortcutEdit = new QLineEdit(shortcutString()); - QCheckBox *includeByDefault = new QCheckBox(msgIncludeByDefault()); - includeByDefault->setToolTip(msgIncludeByDefaultToolTip()); - includeByDefault->setChecked(isIncludedByDefault()); - - auto prefixLabel = new QLabel(msgPrefixLabel()); - prefixLabel->setToolTip(msgPrefixToolTip()); - hlayout->addWidget(prefixLabel); - hlayout->addWidget(shortcutEdit); - hlayout->addWidget(includeByDefault); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | - QDialogButtonBox::Cancel); - connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - - vlayout->addLayout(hlayout); - vlayout->addStretch(); - vlayout->addWidget(buttonBox); - - if (dialog.exec() == QDialog::Accepted) { - setShortcutString(shortcutEdit->text().trimmed()); - setIncludedByDefault(includeByDefault->isChecked()); - return true; - } - - return false; + return openConfigDialog(parent, nullptr); } /*! @@ -456,6 +424,56 @@ void ILocatorFilter::setConfigurable(bool configurable) m_isConfigurable = configurable; } +/*! + Shows the standard configuration dialog with options for the prefix string + and for isIncludedByDefault(). The \a additionalWidget is added at the top. + Ownership of \a additionalWidget stays with the caller, but its parent is + reset to \c nullptr. + + Returns \c false if the user canceled the dialog. +*/ +bool ILocatorFilter::openConfigDialog(QWidget *parent, QWidget *additionalWidget) +{ + QDialog dialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + dialog.setWindowTitle(msgConfigureDialogTitle()); + + auto vlayout = new QVBoxLayout(&dialog); + auto hlayout = new QHBoxLayout; + QLineEdit *shortcutEdit = new QLineEdit(shortcutString()); + QCheckBox *includeByDefault = new QCheckBox(msgIncludeByDefault()); + includeByDefault->setToolTip(msgIncludeByDefaultToolTip()); + includeByDefault->setChecked(isIncludedByDefault()); + + auto prefixLabel = new QLabel(msgPrefixLabel()); + prefixLabel->setToolTip(msgPrefixToolTip()); + hlayout->addWidget(prefixLabel); + hlayout->addWidget(shortcutEdit); + hlayout->addWidget(includeByDefault); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + if (additionalWidget) + vlayout->addWidget(additionalWidget); + vlayout->addLayout(hlayout); + vlayout->addStretch(); + vlayout->addWidget(buttonBox); + + bool accepted = false; + if (dialog.exec() == QDialog::Accepted) { + setShortcutString(shortcutEdit->text().trimmed()); + setIncludedByDefault(includeByDefault->isChecked()); + accepted = true; + } + if (additionalWidget) { + additionalWidget->setVisible(false); + additionalWidget->setParent(nullptr); + } + return accepted; +} + /*! \fn QList Core::ILocatorFilter::matchesFor(QFutureInterface &future, const QString &entry) diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h index 20b129e0b16..5e09697162a 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.h +++ b/src/plugins/coreplugin/locator/ilocatorfilter.h @@ -174,6 +174,7 @@ protected: void setPriority(Priority priority); void setDisplayName(const QString &displayString); void setConfigurable(bool configurable); + bool openConfigDialog(QWidget *parent, QWidget *additionalWidget); private: Utils::Id m_id; diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp index f0295126e25..89b82b0c812 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp @@ -31,9 +31,17 @@ #include #include #include +#include +#include +#include #include +#include #include +#include +#include +#include +#include #include #include #include @@ -171,37 +179,75 @@ void SpotlightIterator::ensureNext() // #pragma mark -- SpotlightLocatorFilter +static QString defaultCommand() +{ + if (HostOsInfo::isMacHost()) + return "mdfind"; + if (HostOsInfo::isWindowsHost()) + return "es.exe"; + return "locate"; +} + +static QString defaultArguments() +{ + if (HostOsInfo::isMacHost()) + return "\"kMDItemFSName = '*%{Query:Escaped}*'c\""; + if (HostOsInfo::isWindowsHost()) + return "-n 10000 -r \"%{Query:Regex}\""; + return "-i -l 10000 -r \"%{Query:Regex}\""; +} + +static QString defaultCaseSensitiveArguments() +{ + if (HostOsInfo::isMacHost()) + return "\"kMDItemFSName = '*%{Query:Escaped}*'\""; + if (HostOsInfo::isWindowsHost()) + return "-i -n 10000 -r \"%{Query:Regex}\""; + return "-l 10000 -r \"%{Query:Regex}\""; +} + +const char kShortcutStringDefault[] = "md"; +const bool kIncludedByDefaultDefault = false; + +const char kShortcutStringKey[] = "shortcut"; +const char kIncludedByDefaultKey[] = "includeByDefault"; +const char kCommandKey[] = "command"; +const char kArgumentsKey[] = "arguments"; +const char kCaseSensitiveKey[] = "caseSensitive"; + +static MacroExpander *createMacroExpander(const QString &query) +{ + MacroExpander *expander = new MacroExpander; + expander->registerVariable("Query", + SpotlightLocatorFilter::tr("Locator query string."), + [query] { return query; }); + expander->registerVariable("Query:Escaped", + SpotlightLocatorFilter::tr( + "Locator query string with quotes escaped with backslash."), + [query] { + QString quoted = query; + quoted.replace('\\', "\\\\") + .replace('\'', "\\\'") + .replace('\"', "\\\""); + return quoted; + }); + expander->registerVariable("Query:Regex", + SpotlightLocatorFilter::tr( + "Locator query string as regular expression."), + [query] { + QString regex = query; + regex = regex.replace('*', ".*"); + return regex; + }); + return expander; +} + SpotlightLocatorFilter::SpotlightLocatorFilter() { - if (HostOsInfo::isMacHost()) { - command = [](const QString &query, Qt::CaseSensitivity sensitivity) { - QString quoted = query; - quoted.replace('\\', "\\\\").replace('\'', "\\\'").replace('\"', "\\\""); - return QStringList( - {"mdfind", - QString("kMDItemFSName = '*%1*'%2") - .arg(quoted, sensitivity == Qt::CaseInsensitive ? QString("c") : QString())}); - }; - } else if (HostOsInfo::isLinuxHost()) { - command = [](const QString &query, Qt::CaseSensitivity sensitivity) { - QString regex = query; - regex = regex.replace('*', ".*"); - return QStringList({"locate"}) - + (sensitivity == Qt::CaseInsensitive ? QStringList({"-i"}) : QStringList()) - + QStringList({"-l", "10000", "-r", regex}); - }; - } else if (HostOsInfo::isWindowsHost()) { - command = [](const QString &query, Qt::CaseSensitivity sensitivity) { - QString regex = query; - regex = regex.replace('*', ".*"); - return QStringList({"es.exe"}) - + (sensitivity == Qt::CaseSensitive ? QStringList({"-i"}) : QStringList()) - + QStringList({"-n", "10000", "-r", regex}); - }; - } setId("SpotlightFileNamesLocatorFilter"); setDisplayName(tr("File Name Index")); - setShortcutString("md"); + setConfigurable(true); + reset(); } void SpotlightLocatorFilter::prepareSearch(const QString &entry) @@ -213,7 +259,12 @@ void SpotlightLocatorFilter::prepareSearch(const QString &entry) // only pass the file name part to allow searches like "somepath/*foo" int lastSlash = fp.filePath.lastIndexOf(QLatin1Char('/')); const QString query = fp.filePath.mid(lastSlash + 1); - setFileIterator(new SpotlightIterator(command(query, caseSensitivity(fp.filePath)))); + std::unique_ptr expander(createMacroExpander(query)); + const QString argumentString = expander->expand( + caseSensitivity(fp.filePath) == Qt::CaseInsensitive ? m_arguments + : m_caseSensitiveArguments); + setFileIterator( + new SpotlightIterator(QStringList(m_command) + QtcProcess::splitArgs(argumentString))); } BaseFileFilter::prepareSearch(entry); } @@ -223,5 +274,81 @@ void SpotlightLocatorFilter::refresh(QFutureInterface &future) Q_UNUSED(future) } +bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh) +{ + Q_UNUSED(needsRefresh) + QWidget configWidget; + QFormLayout *layout = new QFormLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + configWidget.setLayout(layout); + PathChooser *commandEdit = new PathChooser; + commandEdit->setExpectedKind(PathChooser::ExistingCommand); + commandEdit->lineEdit()->setText(m_command); + FancyLineEdit *argumentsEdit = new FancyLineEdit; + argumentsEdit->setText(m_arguments); + FancyLineEdit *caseSensitiveArgumentsEdit = new FancyLineEdit; + caseSensitiveArgumentsEdit->setText(m_caseSensitiveArguments); + layout->addRow(tr("Executable:"), commandEdit); + layout->addRow(tr("Arguments:"), argumentsEdit); + layout->addRow(tr("Case sensitive:"), caseSensitiveArgumentsEdit); + std::unique_ptr expander(createMacroExpander("")); + auto chooser = new VariableChooser(&configWidget); + chooser->addMacroExpanderProvider([expander = expander.get()] { return expander; }); + chooser->addSupportedWidget(argumentsEdit); + chooser->addSupportedWidget(caseSensitiveArgumentsEdit); + const bool accepted = openConfigDialog(parent, &configWidget); + if (accepted) { + m_command = commandEdit->rawFilePath().toString(); + m_arguments = argumentsEdit->text(); + m_caseSensitiveArguments = caseSensitiveArgumentsEdit->text(); + } + return accepted; +} + +QByteArray SpotlightLocatorFilter::saveState() const +{ + QJsonObject obj; + if (shortcutString() != kShortcutStringDefault) + obj.insert(kShortcutStringKey, shortcutString()); + if (isIncludedByDefault() != kIncludedByDefaultDefault) + obj.insert(kIncludedByDefaultKey, isIncludedByDefault()); + if (m_command != defaultCommand()) + obj.insert(kCommandKey, m_command); + if (m_arguments != defaultArguments()) + obj.insert(kArgumentsKey, m_arguments); + if (m_caseSensitiveArguments != defaultCaseSensitiveArguments()) + obj.insert(kCaseSensitiveKey, m_caseSensitiveArguments); + QJsonDocument doc; + doc.setObject(obj); + return doc.toJson(QJsonDocument::Compact); +} + +void SpotlightLocatorFilter::restoreState(const QByteArray &state) +{ + QJsonDocument doc = QJsonDocument::fromJson(state); + if (doc.isNull() || !doc.isObject()) { + reset(); + ILocatorFilter::restoreState(state); // legacy settings from Qt Creator < 4.15 + } else { + const QJsonObject obj = doc.object(); + setShortcutString(obj.value(kShortcutStringKey).toString(kShortcutStringDefault)); + setIncludedByDefault(obj.value(kIncludedByDefaultKey).toBool(kIncludedByDefaultDefault)); + m_command = obj.value(kCommandKey).toString(defaultCommand()); + m_arguments = obj.value(kArgumentsKey).toString(defaultArguments()); + m_caseSensitiveArguments = obj.value(kCaseSensitiveKey) + .toString(defaultCaseSensitiveArguments()); + } +} + +void SpotlightLocatorFilter::reset() +{ + setShortcutString(kShortcutStringDefault); + setIncludedByDefault(kIncludedByDefaultDefault); + m_command = defaultCommand(); + m_arguments = defaultArguments(); + m_caseSensitiveArguments = defaultCaseSensitiveArguments(); +} + } // Internal } // Core diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h index 5b1b4bef95d..874f54af7f2 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h @@ -41,8 +41,18 @@ public: void prepareSearch(const QString &entry) override; void refresh(QFutureInterface &future) override; + using ILocatorFilter::openConfigDialog; + bool openConfigDialog(QWidget *parent, bool &needsRefresh) final; + + QByteArray saveState() const final; + void restoreState(const QByteArray &state) final; + private: - std::function command; + void reset(); + + QString m_command; + QString m_arguments; + QString m_caseSensitiveArguments; }; } // Internal