Add QRegularExpression support to QTextDocument
Currently QTextDocuement only provides find using QRegExp. This patch aims to add support for QRegularExpression. [ChangeLog][QtGui][QTextDocument] Support for searching with a QRegularExpression in a document has been added. Change-Id: I6dba10545b83995d093407038a821fe54db3d261 Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
parent
4e8720d413
commit
4066a6a893
@ -48,6 +48,7 @@
|
||||
#include "qtextlist.h"
|
||||
#include <qdebug.h>
|
||||
#include <qregexp.h>
|
||||
#include <qregularexpression.h>
|
||||
#include <qvarlengtharray.h>
|
||||
#include <qtextcodec.h>
|
||||
#include <qthread.h>
|
||||
@ -1432,6 +1433,131 @@ QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, Fi
|
||||
return find(expr, pos, options);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_REGULAREXPRESSION
|
||||
static bool findInBlock(const QTextBlock &block, const QRegularExpression &expression, int offset,
|
||||
QTextDocument::FindFlags options, QTextCursor *cursor)
|
||||
{
|
||||
QRegularExpression expr(expression);
|
||||
if (!(options & QTextDocument::FindCaseSensitively))
|
||||
expr.setPatternOptions(expr.patternOptions() | QRegularExpression::CaseInsensitiveOption);
|
||||
else
|
||||
expr.setPatternOptions(expr.patternOptions() & ~QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
QString text = block.text();
|
||||
text.replace(QChar::Nbsp, QLatin1Char(' '));
|
||||
QRegularExpressionMatch match;
|
||||
int idx = -1;
|
||||
|
||||
while (offset >= 0 && offset <= text.length()) {
|
||||
idx = (options & QTextDocument::FindBackward) ?
|
||||
text.lastIndexOf(expr, offset, &match) : text.indexOf(expr, offset, &match);
|
||||
if (idx == -1)
|
||||
return false;
|
||||
|
||||
if (options & QTextDocument::FindWholeWords) {
|
||||
const int start = idx;
|
||||
const int end = start + match.capturedLength();
|
||||
if ((start != 0 && text.at(start - 1).isLetterOrNumber())
|
||||
|| (end != text.length() && text.at(end).isLetterOrNumber())) {
|
||||
//if this is not a whole word, continue the search in the string
|
||||
offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
|
||||
idx = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//we have a hit, return the cursor for that.
|
||||
break;
|
||||
}
|
||||
if (idx == -1)
|
||||
return false;
|
||||
*cursor = qMove(QTextCursor(block.docHandle(), block.position() + idx));
|
||||
cursor->setPosition(cursor->position() + match.capturedLength(), QTextCursor::KeepAnchor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.5
|
||||
|
||||
Finds the next occurrence, matching the regular expression, \a expr, in the document.
|
||||
The search starts at the given \a from position, and proceeds forwards
|
||||
through the document unless specified otherwise in the search options.
|
||||
The \a options control the type of search performed.
|
||||
|
||||
Returns a cursor with the match selected if a match was found; otherwise
|
||||
returns a null cursor.
|
||||
|
||||
If the \a from position is 0 (the default) the search begins from the beginning
|
||||
of the document; otherwise it begins at the specified position.
|
||||
*/
|
||||
QTextCursor QTextDocument::find(const QRegularExpression &expr, int from, FindFlags options) const
|
||||
{
|
||||
Q_D(const QTextDocument);
|
||||
|
||||
if (!expr.isValid())
|
||||
return QTextCursor();
|
||||
|
||||
int pos = from;
|
||||
//the cursor is positioned between characters, so for a backward search
|
||||
//do not include the character given in the position.
|
||||
if (options & FindBackward) {
|
||||
--pos ;
|
||||
if (pos < 0)
|
||||
return QTextCursor();
|
||||
}
|
||||
|
||||
QTextCursor cursor;
|
||||
QTextBlock block = d->blocksFind(pos);
|
||||
|
||||
if (!(options & FindBackward)) {
|
||||
int blockOffset = qMax(0, pos - block.position());
|
||||
while (block.isValid()) {
|
||||
if (findInBlock(block, expr, blockOffset, options, &cursor))
|
||||
return cursor;
|
||||
blockOffset = 0;
|
||||
block = block.next();
|
||||
}
|
||||
} else {
|
||||
int blockOffset = pos - block.position();
|
||||
while (block.isValid()) {
|
||||
if (findInBlock(block, expr, blockOffset, options, &cursor))
|
||||
return cursor;
|
||||
block = block.previous();
|
||||
blockOffset = block.length() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return QTextCursor();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.5
|
||||
|
||||
Finds the next occurrence, matching the regular expression, \a expr, in the document.
|
||||
The search starts at the position of the given \a from cursor, and proceeds
|
||||
forwards through the document unless specified otherwise in the search
|
||||
options. The \a options control the type of search performed.
|
||||
|
||||
Returns a cursor with the match selected if a match was found; otherwise
|
||||
returns a null cursor.
|
||||
|
||||
If the given \a from cursor has a selection, the search begins after the
|
||||
selection; otherwise it begins at the cursor's position.
|
||||
|
||||
By default the search is case-sensitive, and can match text anywhere in the
|
||||
document.
|
||||
*/
|
||||
QTextCursor QTextDocument::find(const QRegularExpression &expr, const QTextCursor &from, FindFlags options) const
|
||||
{
|
||||
int pos = 0;
|
||||
if (!from.isNull()) {
|
||||
if (options & QTextDocument::FindBackward)
|
||||
pos = from.selectionStart();
|
||||
else
|
||||
pos = from.selectionEnd();
|
||||
}
|
||||
return find(expr, pos, options);
|
||||
}
|
||||
#endif // QT_NO_REGULAREXPRESSION
|
||||
|
||||
/*!
|
||||
\fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
|
||||
|
@ -169,10 +169,14 @@ public:
|
||||
|
||||
QTextCursor find(const QString &subString, int from = 0, FindFlags options = 0) const;
|
||||
QTextCursor find(const QString &subString, const QTextCursor &from, FindFlags options = 0) const;
|
||||
|
||||
QTextCursor find(const QRegExp &expr, int from = 0, FindFlags options = 0) const;
|
||||
QTextCursor find(const QRegExp &expr, const QTextCursor &from, FindFlags options = 0) const;
|
||||
|
||||
#ifndef QT_NO_REGULAREXPRESSION
|
||||
QTextCursor find(const QRegularExpression &expr, int from = 0, FindFlags options = 0) const;
|
||||
QTextCursor find(const QRegularExpression &expr, const QTextCursor &from, FindFlags options = 0) const;
|
||||
#endif
|
||||
|
||||
QTextFrame *frameAt(int pos) const;
|
||||
QTextFrame *rootFrame() const;
|
||||
|
||||
|
@ -86,6 +86,8 @@ private slots:
|
||||
void find2();
|
||||
void findWithRegExp_data();
|
||||
void findWithRegExp();
|
||||
void findWithRegularExpression_data();
|
||||
void findWithRegularExpression();
|
||||
void findMultiple();
|
||||
void basicIsModifiedChecks();
|
||||
void moreIsModified();
|
||||
@ -196,6 +198,7 @@ private slots:
|
||||
|
||||
private:
|
||||
void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
|
||||
void buildRegExpData();
|
||||
|
||||
QTextDocument *doc;
|
||||
QTextCursor cursor;
|
||||
@ -352,21 +355,7 @@ void tst_QTextDocument::find()
|
||||
|
||||
void tst_QTextDocument::findWithRegExp_data()
|
||||
{
|
||||
QTest::addColumn<QString>("haystack");
|
||||
QTest::addColumn<QString>("needle");
|
||||
QTest::addColumn<int>("flags");
|
||||
QTest::addColumn<int>("from");
|
||||
QTest::addColumn<int>("anchor");
|
||||
QTest::addColumn<int>("position");
|
||||
|
||||
// match integers 0 to 99
|
||||
QTest::newRow("1") << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2;
|
||||
// match ampersands but not &
|
||||
QTest::newRow("2") << "His & hers & theirs" << "&(?!amp;)"<< int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16;
|
||||
//backward search
|
||||
QTest::newRow("3") << QString::fromLatin1("HelloBlahWorld Blah Hah")
|
||||
<< "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9;
|
||||
|
||||
buildRegExpData();
|
||||
}
|
||||
|
||||
void tst_QTextDocument::findWithRegExp()
|
||||
@ -393,6 +382,34 @@ void tst_QTextDocument::findWithRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextDocument::findWithRegularExpression_data()
|
||||
{
|
||||
buildRegExpData();
|
||||
}
|
||||
|
||||
void tst_QTextDocument::findWithRegularExpression()
|
||||
{
|
||||
QFETCH(QString, haystack);
|
||||
QFETCH(QString, needle);
|
||||
QFETCH(int, flags);
|
||||
QFETCH(int, from);
|
||||
QFETCH(int, anchor);
|
||||
QFETCH(int, position);
|
||||
|
||||
cursor.insertText(haystack);
|
||||
//search using a regular expression
|
||||
QRegularExpression expr(needle);
|
||||
QTextDocument::FindFlags flg(flags);
|
||||
cursor = doc->find(expr, from, flg);
|
||||
|
||||
if (anchor != -1) {
|
||||
QCOMPARE(cursor.anchor(), anchor);
|
||||
QCOMPARE(cursor.position(), position);
|
||||
} else {
|
||||
QVERIFY(cursor.isNull());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextDocument::find2()
|
||||
{
|
||||
doc->setPlainText("aaa");
|
||||
@ -2602,6 +2619,24 @@ void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &d
|
||||
QCOMPARE(doc.toHtml(), expectedHtml);
|
||||
}
|
||||
|
||||
void tst_QTextDocument::buildRegExpData()
|
||||
{
|
||||
QTest::addColumn<QString>("haystack");
|
||||
QTest::addColumn<QString>("needle");
|
||||
QTest::addColumn<int>("flags");
|
||||
QTest::addColumn<int>("from");
|
||||
QTest::addColumn<int>("anchor");
|
||||
QTest::addColumn<int>("position");
|
||||
|
||||
// match integers 0 to 99
|
||||
QTest::newRow("1") << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2;
|
||||
// match ampersands but not &
|
||||
QTest::newRow("2") << "His & hers & theirs" << "&(?!amp;)"<< int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16;
|
||||
//backward search
|
||||
QTest::newRow("3") << QString::fromLatin1("HelloBlahWorld Blah Hah")
|
||||
<< "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9;
|
||||
}
|
||||
|
||||
void tst_QTextDocument::backgroundImage_toHtml()
|
||||
{
|
||||
CREATE_DOC_AND_CURSOR();
|
||||
|
Loading…
Reference in New Issue
Block a user