/**************************************************************************** ** ** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo ** Copyright (C) 2016 Samuel Gaist ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "regularexpressiondialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QRegularExpression::MatchType) static QString rawStringLiteral(QString pattern) { pattern.prepend(QLatin1String("R\"RX(")); pattern.append(QLatin1String(")RX\"")); return pattern; } static QString patternToCode(QString pattern) { pattern.replace(QLatin1String("\\"), QLatin1String("\\\\")); pattern.replace(QLatin1String("\""), QLatin1String("\\\"")); pattern.prepend(QLatin1Char('"')); pattern.append(QLatin1Char('"')); return pattern; } static QString codeToPattern(QString code) { for (int i = 0; i < code.size(); ++i) { if (code.at(i) == QLatin1Char('\\')) code.remove(i, 1); } if (code.startsWith(QLatin1Char('"')) && code.endsWith(QLatin1Char('"'))) { code.chop(1); code.remove(0, 1); } return code; } class PatternLineEdit : public QLineEdit { Q_OBJECT public: explicit PatternLineEdit(QWidget *parent = nullptr); private slots: void copyToCode(); void pasteFromCode(); void escapeSelection(); protected: void contextMenuEvent(QContextMenuEvent *event) override; private: QAction *escapeSelectionAction; QAction *copyToCodeAction; QAction *pasteFromCodeAction; }; PatternLineEdit::PatternLineEdit(QWidget *parent) : QLineEdit(parent), escapeSelectionAction(new QAction(tr("Escape Selection"), this)), copyToCodeAction(new QAction(tr("Copy to Code"), this)), pasteFromCodeAction(new QAction(tr("Paste from Code"), this)) { setClearButtonEnabled(true); connect(escapeSelectionAction, &QAction::triggered, this, &PatternLineEdit::escapeSelection); connect(copyToCodeAction, &QAction::triggered, this, &PatternLineEdit::copyToCode); connect(pasteFromCodeAction, &QAction::triggered, this, &PatternLineEdit::pasteFromCode); #if !QT_CONFIG(clipboard) copyToCodeAction->setEnabled(false); pasteFromCodeAction->setEnabled(false); #endif } void PatternLineEdit::escapeSelection() { const QString selection = selectedText(); const QString escapedSelection = QRegularExpression::escape(selection); if (escapedSelection != selection) { QString t = text(); t.replace(selectionStart(), selection.size(), escapedSelection); setText(t); } } void PatternLineEdit::copyToCode() { #if QT_CONFIG(clipboard) QGuiApplication::clipboard()->setText(patternToCode(text())); #endif } void PatternLineEdit::pasteFromCode() { #if QT_CONFIG(clipboard) setText(codeToPattern(QGuiApplication::clipboard()->text())); #endif } void PatternLineEdit::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = createStandardContextMenu(); menu->setAttribute(Qt::WA_DeleteOnClose); menu->addSeparator(); escapeSelectionAction->setEnabled(hasSelectedText()); menu->addAction(escapeSelectionAction); menu->addSeparator(); menu->addAction(copyToCodeAction); menu->addAction(pasteFromCodeAction); menu->popup(event->globalPos()); } class DisplayLineEdit : public QLineEdit { public: explicit DisplayLineEdit(QWidget *parent = nullptr); }; DisplayLineEdit::DisplayLineEdit(QWidget *parent) : QLineEdit(parent) { setReadOnly(true); QPalette disabledPalette = palette(); disabledPalette.setBrush(QPalette::Base, disabledPalette.brush(QPalette::Disabled, QPalette::Base)); setPalette(disabledPalette); #if QT_CONFIG(clipboard) QAction *copyAction = new QAction(this); copyAction->setText(RegularExpressionDialog::tr("Copy to clipboard")); copyAction->setIcon(QIcon(QStringLiteral(":/images/copy.png"))); connect(copyAction, &QAction::triggered, this, [this] () { QGuiApplication::clipboard()->setText(text()); }); addAction(copyAction, QLineEdit::TrailingPosition); #endif } RegularExpressionDialog::RegularExpressionDialog(QWidget *parent) : QDialog(parent) { setupUi(); setWindowTitle(tr("QRegularExpression Example")); connect(patternLineEdit, &QLineEdit::textChanged, this, &RegularExpressionDialog::refresh); connect(subjectTextEdit, &QPlainTextEdit::textChanged, this, &RegularExpressionDialog::refresh); connect(caseInsensitiveOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(dotMatchesEverythingOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(multilineOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(extendedPatternSyntaxOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(invertedGreedinessOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(dontCaptureOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(useUnicodePropertiesOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(optimizeOnFirstUsageOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(dontAutomaticallyOptimizeOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(offsetSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &RegularExpressionDialog::refresh); connect(matchTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &RegularExpressionDialog::refresh); connect(anchoredMatchOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); connect(dontCheckSubjectStringMatchOptionCheckBox, &QCheckBox::toggled, this, &RegularExpressionDialog::refresh); patternLineEdit->setText(tr("(\\+?\\d+)-(?\\d+)-(?\\w+)")); subjectTextEdit->setPlainText(tr("My office number is +43-152-0123456, my mobile is 001-41-255512 instead.")); refresh(); } void RegularExpressionDialog::setResultUiEnabled(bool enabled) { matchDetailsTreeWidget->setEnabled(enabled); namedGroupsTreeWidget->setEnabled(enabled); } static void setTextColor(QWidget *widget, const QColor &color) { QPalette palette = widget->palette(); palette.setColor(QPalette::Text, color); widget->setPalette(palette); } void RegularExpressionDialog::refresh() { setUpdatesEnabled(false); const QString pattern = patternLineEdit->text(); const QString text = subjectTextEdit->toPlainText(); offsetSpinBox->setMaximum(qMax(0, text.length() - 1)); escapedPatternLineEdit->setText(patternToCode(pattern)); rawStringLiteralLineEdit->setText(rawStringLiteral(pattern)); setTextColor(patternLineEdit, subjectTextEdit->palette().color(QPalette::Text)); matchDetailsTreeWidget->clear(); namedGroupsTreeWidget->clear(); regexpStatusLabel->setText(QString()); if (pattern.isEmpty()) { setResultUiEnabled(false); setUpdatesEnabled(true); return; } QRegularExpression rx(pattern); if (!rx.isValid()) { setTextColor(patternLineEdit, Qt::red); regexpStatusLabel->setText(tr("Invalid: syntax error at position %1 (%2)") .arg(rx.patternErrorOffset()) .arg(rx.errorString())); setResultUiEnabled(false); setUpdatesEnabled(true); return; } setResultUiEnabled(true); QRegularExpression::MatchType matchType = matchTypeComboBox->currentData().value(); QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption; QRegularExpression::MatchOptions matchOptions = QRegularExpression::NoMatchOption; if (anchoredMatchOptionCheckBox->isChecked()) matchOptions |= QRegularExpression::AnchoredMatchOption; if (dontCheckSubjectStringMatchOptionCheckBox->isChecked()) matchOptions |= QRegularExpression::DontCheckSubjectStringMatchOption; if (caseInsensitiveOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::CaseInsensitiveOption; if (dotMatchesEverythingOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::DotMatchesEverythingOption; if (multilineOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::MultilineOption; if (extendedPatternSyntaxOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::ExtendedPatternSyntaxOption; if (invertedGreedinessOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::InvertedGreedinessOption; if (dontCaptureOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::DontCaptureOption; if (useUnicodePropertiesOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::UseUnicodePropertiesOption; if (optimizeOnFirstUsageOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::OptimizeOnFirstUsageOption; if (dontAutomaticallyOptimizeOptionCheckBox->isChecked()) patternOptions |= QRegularExpression::DontAutomaticallyOptimizeOption; rx.setPatternOptions(patternOptions); const int capturingGroupsCount = rx.captureCount() + 1; QRegularExpressionMatchIterator iterator = rx.globalMatch(text, offsetSpinBox->value(), matchType, matchOptions); int i = 0; while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); QTreeWidgetItem *matchDetailTopItem = new QTreeWidgetItem(matchDetailsTreeWidget); matchDetailTopItem->setText(0, QString::number(i)); for (int captureGroupIndex = 0; captureGroupIndex < capturingGroupsCount; ++captureGroupIndex) { QTreeWidgetItem *matchDetailItem = new QTreeWidgetItem(matchDetailTopItem); matchDetailItem->setText(1, QString::number(captureGroupIndex)); matchDetailItem->setText(2, match.captured(captureGroupIndex)); } ++i; } matchDetailsTreeWidget->expandAll(); regexpStatusLabel->setText(tr("Valid")); const QStringList namedCaptureGroups = rx.namedCaptureGroups(); for (int i = 0; i < namedCaptureGroups.size(); ++i) { const QString currentNamedCaptureGroup = namedCaptureGroups.at(i); QTreeWidgetItem *namedGroupItem = new QTreeWidgetItem(namedGroupsTreeWidget); namedGroupItem->setText(0, QString::number(i)); namedGroupItem->setText(1, currentNamedCaptureGroup.isNull() ? tr("") : currentNamedCaptureGroup); } setUpdatesEnabled(true); } void RegularExpressionDialog::setupUi() { QWidget *leftHalfContainer = setupLeftUi(); QFrame *verticalSeparator = new QFrame; verticalSeparator->setFrameStyle(QFrame::VLine | QFrame::Sunken); QWidget *rightHalfContainer = setupRightUi(); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addWidget(leftHalfContainer); mainLayout->addWidget(verticalSeparator); mainLayout->addWidget(rightHalfContainer); setLayout(mainLayout); } QWidget *RegularExpressionDialog::setupLeftUi() { QWidget *container = new QWidget; QFormLayout *layout = new QFormLayout(container); layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); layout->setContentsMargins(QMargins()); QLabel *regexpAndSubjectLabel = new QLabel(tr("

Regular expression and text input

")); layout->addRow(regexpAndSubjectLabel); patternLineEdit = new PatternLineEdit; patternLineEdit->setClearButtonEnabled(true); layout->addRow(tr("&Pattern:"), patternLineEdit); rawStringLiteralLineEdit = new DisplayLineEdit; layout->addRow(tr("&Raw string literal:"), rawStringLiteralLineEdit); escapedPatternLineEdit = new DisplayLineEdit; layout->addRow(tr("&Escaped pattern:"), escapedPatternLineEdit); subjectTextEdit = new QPlainTextEdit; layout->addRow(tr("&Subject text:"), subjectTextEdit); caseInsensitiveOptionCheckBox = new QCheckBox(tr("Case insensitive (/i)")); dotMatchesEverythingOptionCheckBox = new QCheckBox(tr("Dot matches everything (/s)")); multilineOptionCheckBox = new QCheckBox(tr("Multiline (/m)")); extendedPatternSyntaxOptionCheckBox = new QCheckBox(tr("Extended pattern (/x)")); invertedGreedinessOptionCheckBox = new QCheckBox(tr("Inverted greediness")); dontCaptureOptionCheckBox = new QCheckBox(tr("Don't capture")); useUnicodePropertiesOptionCheckBox = new QCheckBox(tr("Use unicode properties (/u)")); optimizeOnFirstUsageOptionCheckBox = new QCheckBox(tr("Optimize on first usage")); dontAutomaticallyOptimizeOptionCheckBox = new QCheckBox(tr("Don't automatically optimize")); QGridLayout *patternOptionsCheckBoxLayout = new QGridLayout; int gridRow = 0; patternOptionsCheckBoxLayout->addWidget(caseInsensitiveOptionCheckBox, gridRow, 1); patternOptionsCheckBoxLayout->addWidget(dotMatchesEverythingOptionCheckBox, gridRow, 2); ++gridRow; patternOptionsCheckBoxLayout->addWidget(multilineOptionCheckBox, gridRow, 1); patternOptionsCheckBoxLayout->addWidget(extendedPatternSyntaxOptionCheckBox, gridRow, 2); ++gridRow; patternOptionsCheckBoxLayout->addWidget(invertedGreedinessOptionCheckBox, gridRow, 1); patternOptionsCheckBoxLayout->addWidget(dontCaptureOptionCheckBox, gridRow, 2); ++gridRow; patternOptionsCheckBoxLayout->addWidget(useUnicodePropertiesOptionCheckBox, gridRow, 1); patternOptionsCheckBoxLayout->addWidget(optimizeOnFirstUsageOptionCheckBox, gridRow, 2); ++gridRow; patternOptionsCheckBoxLayout->addWidget(dontAutomaticallyOptimizeOptionCheckBox, gridRow, 1); layout->addRow(tr("Pattern options:"), patternOptionsCheckBoxLayout); offsetSpinBox = new QSpinBox; layout->addRow(tr("Match &offset:"), offsetSpinBox); matchTypeComboBox = new QComboBox; matchTypeComboBox->addItem(tr("Normal"), QVariant::fromValue(QRegularExpression::NormalMatch)); matchTypeComboBox->addItem(tr("Partial prefer complete"), QVariant::fromValue(QRegularExpression::PartialPreferCompleteMatch)); matchTypeComboBox->addItem(tr("Partial prefer first"), QVariant::fromValue(QRegularExpression::PartialPreferFirstMatch)); matchTypeComboBox->addItem(tr("No match"), QVariant::fromValue(QRegularExpression::NoMatch)); layout->addRow(tr("Match &type:"), matchTypeComboBox); dontCheckSubjectStringMatchOptionCheckBox = new QCheckBox(tr("Don't check subject string")); anchoredMatchOptionCheckBox = new QCheckBox(tr("Anchored match")); QGridLayout *matchOptionsCheckBoxLayout = new QGridLayout; matchOptionsCheckBoxLayout->addWidget(dontCheckSubjectStringMatchOptionCheckBox, 0, 0); matchOptionsCheckBoxLayout->addWidget(anchoredMatchOptionCheckBox, 0, 1); layout->addRow(tr("Match options:"), matchOptionsCheckBoxLayout); return container; } QWidget *RegularExpressionDialog::setupRightUi() { QWidget *container = new QWidget; QFormLayout *layout = new QFormLayout(container); layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); layout->setContentsMargins(QMargins()); QLabel *matchInfoLabel = new QLabel(tr("

Match information

")); layout->addRow(matchInfoLabel); matchDetailsTreeWidget = new QTreeWidget; matchDetailsTreeWidget->setHeaderLabels(QStringList() << tr("Match index") << tr("Group index") << tr("Captured string")); matchDetailsTreeWidget->setSizeAdjustPolicy(QTreeWidget::AdjustToContents); layout->addRow(tr("Match details:"), matchDetailsTreeWidget); QFrame *horizontalSeparator = new QFrame; horizontalSeparator->setFrameStyle(QFrame::HLine | QFrame::Sunken); layout->addRow(horizontalSeparator); QLabel *regexpInfoLabel = new QLabel(tr("

Regular expression information

")); layout->addRow(regexpInfoLabel); regexpStatusLabel = new QLabel(tr("Valid")); regexpStatusLabel->setWordWrap(true); layout->addRow(tr("Pattern status:"), regexpStatusLabel); namedGroupsTreeWidget = new QTreeWidget; namedGroupsTreeWidget->setHeaderLabels(QStringList() << tr("Index") << tr("Named group")); namedGroupsTreeWidget->setSizeAdjustPolicy(QTreeWidget::AdjustToContents); namedGroupsTreeWidget->setRootIsDecorated(false); layout->addRow(tr("Named groups:"), namedGroupsTreeWidget); return container; } #include "regularexpressiondialog.moc"