From 165d2d762a5a8aa8e307bb7b5b16e29ab94a18d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josep=20Llodr=C3=A0?= <jlg.hrtc@gmail.com> Date: Sat, 31 Mar 2018 16:29:04 +0200 Subject: [PATCH] linguist: Add Regular expression checkbox in Find Dialog Now it is possible to search using a Perl-like regular expression. This enables the user to perform powerful searches. There are many scenarios in which this is convenient, let me show an example: Source (Eng) | Translation (Spa) ------------------------------------ From Desde From De ... from ... ... de ... ... of ... ... de ... from 1 to 10 de 1 a 10 (plus many other strings also containing "de" or "desde") Even simple Regular Expressions like ^Desde$ ^De$ from \d+ to \d+ become very convenient for fine-grained searches. If the regular expression written in the Find Dialog is not valid, text is highlighted in red and Find button is disabled. Change-Id: I90bb55d190d46263ec6adc27256d9c62a69e7f87 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> --- src/linguist/linguist/finddialog.cpp | 20 +++++-- src/linguist/linguist/finddialog.h | 10 +++- src/linguist/linguist/finddialog.ui | 84 +++++++++++++--------------- src/linguist/linguist/mainwindow.cpp | 19 +++++-- src/linguist/linguist/mainwindow.h | 3 +- 5 files changed, 78 insertions(+), 58 deletions(-) diff --git a/src/linguist/linguist/finddialog.cpp b/src/linguist/linguist/finddialog.cpp index 53bb34801..9dd801a71 100644 --- a/src/linguist/linguist/finddialog.cpp +++ b/src/linguist/linguist/finddialog.cpp @@ -44,14 +44,25 @@ FindDialog::FindDialog(QWidget *parent) findNxt->setEnabled(false); connect(findNxt, SIGNAL(clicked()), this, SLOT(emitFindNext())); - connect(led, SIGNAL(textChanged(QString)), this, SLOT(verifyText(QString))); + connect(useRegExp, SIGNAL(stateChanged(int)), this, SLOT(verify())); + connect(led, SIGNAL(textChanged(QString)), this, SLOT(verify())); led->setFocus(); } -void FindDialog::verifyText(const QString &text) +void FindDialog::verify() { - findNxt->setEnabled(!text.isEmpty()); + bool validRegExp = true; + if (useRegExp->isChecked() && !led->text().isEmpty()) { + m_regExp.setPattern(led->text()); + validRegExp = m_regExp.isValid(); + } + if (validRegExp && m_redText) + led->setStyleSheet(QStringLiteral("color: auto;")); + else if (!validRegExp && !m_redText) + led->setStyleSheet(QStringLiteral("color: red;")); + m_redText = !validRegExp; + findNxt->setEnabled(!led->text().isEmpty() && validRegExp); } void FindDialog::emitFindNext() @@ -65,7 +76,8 @@ void FindDialog::emitFindNext() (comments->isChecked() ? DataModel::Comments : 0)); else where = DataModel::Translations; - emit findNext(led->text(), where, matchCase->isChecked(), ignoreAccelerators->isChecked(), skipObsolete->isChecked()); + emit findNext(led->text(), where, matchCase->isChecked(), ignoreAccelerators->isChecked(), + skipObsolete->isChecked(), useRegExp->isChecked()); led->selectAll(); } diff --git a/src/linguist/linguist/finddialog.h b/src/linguist/linguist/finddialog.h index 5b6555874..039f0b9e7 100644 --- a/src/linguist/linguist/finddialog.h +++ b/src/linguist/linguist/finddialog.h @@ -33,6 +33,7 @@ #include "messagemodel.h" #include <QDialog> +#include <QRegularExpression> QT_BEGIN_NAMESPACE @@ -41,15 +42,20 @@ class FindDialog : public QDialog, public Ui::FindDialog Q_OBJECT public: FindDialog(QWidget *parent = 0); + QRegularExpression &getRegExp() { return m_regExp; } signals: void findNext(const QString& text, DataModel::FindLocation where, - bool matchCase, bool ignoreAccelerators, bool skipObsolete); + bool matchCase, bool ignoreAccelerators, bool skipObsolete, bool useRegExp); private slots: void emitFindNext(); - void verifyText(const QString &); + void verify(); void find(); + +private: + QRegularExpression m_regExp; + bool m_redText = false; }; QT_END_NAMESPACE diff --git a/src/linguist/linguist/finddialog.ui b/src/linguist/linguist/finddialog.ui index f405a45be..20dceb7e1 100644 --- a/src/linguist/linguist/finddialog.ui +++ b/src/linguist/linguist/finddialog.ui @@ -50,28 +50,22 @@ <string>This window allows you to search for some text in the translation source file.</string> </property> <layout class="QHBoxLayout"> - <property name="spacing"> - <number>6</number> + <property name="leftMargin"> + <number>11</number> + </property> + <property name="topMargin"> + <number>11</number> </property> - <property name="margin"> + <property name="rightMargin"> + <number>11</number> + </property> + <property name="bottomMargin"> <number>11</number> </property> <item> <layout class="QVBoxLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="margin"> - <number>0</number> - </property> <item> <layout class="QHBoxLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="margin"> - <number>0</number> - </property> <item> <widget class="QLabel" name="findWhat"> <property name="text"> @@ -97,45 +91,46 @@ <string>Options</string> </property> <layout class="QGridLayout"> - <property name="margin"> - <number>9</number> - </property> - <property name="spacing"> - <number>6</number> - </property> - <item row="1" column="0"> - <widget class="QCheckBox" name="sourceText"> + <item row="1" column="1"> + <widget class="QCheckBox" name="useRegExp"> <property name="whatsThis"> - <string>Source texts are searched when checked.</string> + <string>Lets you use a Perl-compatible regular expression</string> </property> <property name="text"> - <string>&Source texts</string> + <string>Regular &expression</string> </property> - <property name="checked"> - <bool>true</bool> + </widget> + </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="matchCase"> + <property name="whatsThis"> + <string>Texts such as 'TeX' and 'tex' are considered as different when checked.</string> + </property> + <property name="text"> + <string>&Match case</string> </property> </widget> </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="translations"> + <item row="1" column="0"> + <widget class="QCheckBox" name="sourceText"> <property name="whatsThis"> - <string>Translations are searched when checked.</string> + <string>Source texts are searched when checked.</string> </property> <property name="text"> - <string>&Translations</string> + <string>&Source texts</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="matchCase"> + <item row="3" column="1"> + <widget class="QCheckBox" name="skipObsolete"> <property name="whatsThis"> - <string>Texts such as 'TeX' and 'tex' are considered as different when checked.</string> + <string>Obsoleted messages are skipped when checked.</string> </property> <property name="text"> - <string>&Match case</string> + <string>Skip &obsolete</string> </property> </widget> </item> @@ -152,7 +147,7 @@ </property> </widget> </item> - <item row="1" column="1"> + <item row="2" column="1"> <widget class="QCheckBox" name="ignoreAccelerators"> <property name="text"> <string>Ignore &accelerators</string> @@ -162,13 +157,16 @@ </property> </widget> </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="skipObsolete"> + <item row="2" column="0"> + <widget class="QCheckBox" name="translations"> <property name="whatsThis"> - <string>Obsoleted messages are skipped when checked.</string> + <string>Translations are searched when checked.</string> </property> <property name="text"> - <string>Skip &obsolete</string> + <string>&Translations</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> @@ -179,12 +177,6 @@ </item> <item> <layout class="QVBoxLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="margin"> - <number>0</number> - </property> <item> <widget class="QPushButton" name="findNxt"> <property name="whatsThis"> diff --git a/src/linguist/linguist/mainwindow.cpp b/src/linguist/linguist/mainwindow.cpp index 8cc55d44b..9f7109c85 100644 --- a/src/linguist/linguist/mainwindow.cpp +++ b/src/linguist/linguist/mainwindow.cpp @@ -277,6 +277,7 @@ MainWindow::MainWindow() m_findMatchCase(Qt::CaseInsensitive), m_findIgnoreAccelerators(true), m_findSkipObsolete(false), + m_findUseRegExp(false), m_findWhere(DataModel::NoLocation), m_translationSettingsDialog(0), m_settingCurrentMessage(false), @@ -483,8 +484,8 @@ MainWindow::MainWindow() this, SLOT(updateTranslation(QStringList))); connect(m_messageEditor, SIGNAL(translatorCommentChanged(QString)), this, SLOT(updateTranslatorComment(QString))); - connect(m_findDialog, SIGNAL(findNext(QString,DataModel::FindLocation,bool,bool,bool)), - this, SLOT(findNext(QString,DataModel::FindLocation,bool,bool,bool))); + connect(m_findDialog, SIGNAL(findNext(QString,DataModel::FindLocation,bool,bool,bool,bool)), + this, SLOT(findNext(QString,DataModel::FindLocation,bool,bool,bool,bool))); connect(m_translateDialog, SIGNAL(requestMatchUpdate(bool&)), SLOT(updateTranslateHit(bool&))); connect(m_translateDialog, SIGNAL(activated(int)), SLOT(translate(int))); @@ -986,8 +987,10 @@ bool MainWindow::searchItem(DataModel::FindLocation where, const QString &search // FIXME: This removes too much. The proper solution might be too slow, though. text.remove(QLatin1Char('&')); - int foundOffset = text.indexOf(m_findText, 0, m_findMatchCase); - return foundOffset >= 0; + if (m_findUseRegExp) + return m_findDialog->getRegExp().match(text).hasMatch(); + else + return text.indexOf(m_findText, 0, m_findMatchCase) >= 0; } void MainWindow::findAgain() @@ -1763,7 +1766,7 @@ bool MainWindow::next(bool checkUnfinished) } void MainWindow::findNext(const QString &text, DataModel::FindLocation where, - bool matchCase, bool ignoreAccelerators, bool skipObsolete) + bool matchCase, bool ignoreAccelerators, bool skipObsolete, bool useRegExp) { if (text.isEmpty()) return; @@ -1772,6 +1775,12 @@ void MainWindow::findNext(const QString &text, DataModel::FindLocation where, m_findMatchCase = matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive; m_findIgnoreAccelerators = ignoreAccelerators; m_findSkipObsolete = skipObsolete; + m_findUseRegExp = useRegExp; + if (m_findUseRegExp) { + m_findDialog->getRegExp().setPatternOptions(matchCase + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption); + } m_ui.actionFindNext->setEnabled(true); findAgain(); } diff --git a/src/linguist/linguist/mainwindow.h b/src/linguist/linguist/mainwindow.h index 63deaa329..b871b635e 100644 --- a/src/linguist/linguist/mainwindow.h +++ b/src/linguist/linguist/mainwindow.h @@ -150,7 +150,7 @@ private slots: void prevUnfinished(); void nextUnfinished(); void findNext(const QString &text, DataModel::FindLocation where, - bool matchCase, bool ignoreAccelerators, bool skipObsolete); + bool matchCase, bool ignoreAccelerators, bool skipObsolete, bool regularExp); void revalidate(); void toggleStatistics(); void toggleVisualizeWhitespace(); @@ -225,6 +225,7 @@ private: Qt::CaseSensitivity m_findMatchCase; bool m_findIgnoreAccelerators; bool m_findSkipObsolete; + bool m_findUseRegExp; DataModel::FindLocation m_findWhere; TranslateDialog *m_translateDialog; -- GitLab