Warning, file /office/calligra/libs/main/KoFindText.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 * 0003 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 * Copyright (C) 2011 Thorsten Zachmann <zachmann@kde.org> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "KoFindText.h" 0023 #include "KoFindText_p.h" 0024 0025 #include <QTextDocument> 0026 #include <QTextCursor> 0027 #include <QTextBlock> 0028 #include <QTextLayout> 0029 #include <QPalette> 0030 #include <QStyle> 0031 #include <QApplication> 0032 #include <QAbstractTextDocumentLayout> 0033 0034 #include <MainDebug.h> 0035 #include <klocalizedstring.h> 0036 0037 #include <KoText.h> 0038 #include <KoTextDocument.h> 0039 #include <KoShape.h> 0040 #include <KoShapeContainer.h> 0041 #include <KoTextShapeData.h> 0042 0043 #include "KoFindOptionSet.h" 0044 #include "KoFindOption.h" 0045 #include "KoDocument.h" 0046 0047 QTextCharFormat KoFindText::Private::highlightFormat; 0048 QTextCharFormat KoFindText::Private::currentMatchFormat; 0049 QTextCharFormat KoFindText::Private::currentSelectionFormat; 0050 QTextCharFormat KoFindText::Private::replacedFormat; 0051 bool KoFindText::Private::formatsInitialized = false; 0052 0053 KoFindText::KoFindText(QObject* parent) 0054 : KoFindBase(parent), d(new Private(this)) 0055 { 0056 d->initializeFormats(); 0057 0058 KoFindOptionSet *options = new KoFindOptionSet(); 0059 options->addOption("caseSensitive", i18n("Case Sensitive"), i18n("Match cases when searching"), QVariant::fromValue<bool>(false)); 0060 options->addOption("wholeWords", i18n("Whole Words Only"), i18n("Match only whole words"), QVariant::fromValue<bool>(false)); 0061 options->addOption("fromCursor", i18n("Find from Cursor"), i18n("Start searching from the current cursor"), QVariant::fromValue<bool>(true)); 0062 setOptions(options); 0063 } 0064 0065 KoFindText::~KoFindText() 0066 { 0067 delete d; 0068 } 0069 0070 void KoFindText::findImplementation(const QString &pattern, QList<KoFindMatch> & matchList) 0071 { 0072 KoFindOptionSet *opts = options(); 0073 QTextDocument::FindFlags flags = 0; 0074 0075 if(opts->option("caseSensitive")->value().toBool()) { 0076 flags |= QTextDocument::FindCaseSensitively; 0077 } 0078 if(opts->option("wholeWords")->value().toBool()) { 0079 flags |= QTextDocument::FindWholeWords; 0080 } 0081 0082 int start = 0; 0083 bool findInSelection = false; 0084 0085 if(d->documents.size() == 0) { 0086 qWarning() << "No document available for searching!"; 0087 return; 0088 } 0089 0090 bool before = opts->option("fromCursor")->value().toBool() && !d->currentCursor.isNull(); 0091 QList<KoFindMatch> matchBefore; 0092 foreach(QTextDocument* document, d->documents) { 0093 QTextCursor cursor = document->find(pattern, start, flags); 0094 cursor.setKeepPositionOnInsert(true); 0095 0096 QVector<QAbstractTextDocumentLayout::Selection> selections; 0097 while(!cursor.isNull()) { 0098 if(findInSelection && d->selectionEnd <= cursor.position()) { 0099 break; 0100 } 0101 0102 if (before && document == d->currentCursor.document() && d->currentCursor < cursor) { 0103 before = false; 0104 } 0105 0106 QAbstractTextDocumentLayout::Selection selection; 0107 selection.cursor = cursor; 0108 selection.format = d->highlightFormat; 0109 selections.append(selection); 0110 0111 KoFindMatch match; 0112 match.setContainer(QVariant::fromValue(document)); 0113 match.setLocation(QVariant::fromValue(cursor)); 0114 if (before) { 0115 matchBefore.append(match); 0116 } 0117 else { 0118 matchList.append(match); 0119 } 0120 0121 cursor = document->find(pattern, cursor, flags); 0122 cursor.setKeepPositionOnInsert(true); 0123 } 0124 if (before && document == d->currentCursor.document()) { 0125 before = false; 0126 } 0127 d->selections.insert(document, selections); 0128 } 0129 matchList.append(matchBefore); 0130 0131 if (hasMatches()) { 0132 setCurrentMatch(0); 0133 d->updateCurrentMatch(0); 0134 } 0135 0136 d->updateSelections(); 0137 } 0138 0139 void KoFindText::replaceImplementation(const KoFindMatch &match, const QVariant &value) 0140 { 0141 if (!match.isValid() || !match.location().canConvert<QTextCursor>() || !match.container().canConvert<QTextDocument*>()) { 0142 return; 0143 } 0144 0145 QTextCursor cursor = match.location().value<QTextCursor>(); 0146 cursor.setKeepPositionOnInsert(true); 0147 0148 //Search for the selection matching this match. 0149 QVector<QAbstractTextDocumentLayout::Selection> selections = d->selections.value(match.container().value<QTextDocument*>()); 0150 int index = 0; 0151 foreach(const QAbstractTextDocumentLayout::Selection &sel, selections) { 0152 if(sel.cursor == cursor) { 0153 break; 0154 } 0155 index++; 0156 } 0157 0158 cursor.insertText(value.toString()); 0159 cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, value.toString().length()); 0160 0161 selections[index].cursor = cursor; 0162 selections[index].format = d->replacedFormat; 0163 d->selections.insert(match.container().value<QTextDocument*>(), selections); 0164 0165 d->updateCurrentMatch(0); 0166 d->updateSelections(); 0167 } 0168 0169 void KoFindText::clearMatches() 0170 { 0171 d->selections.clear(); 0172 foreach(QTextDocument* doc, d->documents) { 0173 d->selections.insert(doc, QVector<QAbstractTextDocumentLayout::Selection>()); 0174 } 0175 d->updateSelections(); 0176 0177 d->selectionStart = -1; 0178 d->selectionEnd = -1; 0179 0180 setCurrentMatch(0); 0181 d->currentMatch.first = 0; 0182 } 0183 0184 QList< QTextDocument* > KoFindText::documents() const 0185 { 0186 return d->documents; 0187 } 0188 0189 void KoFindText::findNext() 0190 { 0191 if(d->selections.size() == 0) { 0192 return; 0193 } 0194 0195 KoFindBase::findNext(); 0196 d->updateCurrentMatch(currentMatchIndex()); 0197 d->updateSelections(); 0198 } 0199 0200 void KoFindText::findPrevious() 0201 { 0202 if(d->selections.size() == 0) { 0203 return; 0204 } 0205 0206 KoFindBase::findPrevious(); 0207 d->updateCurrentMatch(currentMatchIndex()); 0208 d->updateSelections(); 0209 } 0210 0211 void KoFindText::setCurrentCursor(const QTextCursor &cursor) 0212 { 0213 d->currentCursor = cursor; 0214 } 0215 0216 void KoFindText::setDocuments(const QList<QTextDocument*> &documents) 0217 { 0218 clearMatches(); 0219 d->documents = documents; 0220 d->updateDocumentList(); 0221 } 0222 0223 void KoFindText::findTextInShapes(const QList<KoShape*> &shapes, QList<QTextDocument*> &append) 0224 { 0225 foreach(KoShape* shape, shapes) { 0226 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape); 0227 if(container) { 0228 findTextInShapes(container->shapes(), append); 0229 } 0230 0231 KoTextShapeData *shapeData = dynamic_cast<KoTextShapeData*>(shape->userData()); 0232 if (!shapeData) 0233 continue; 0234 0235 if(shapeData->document()) { 0236 if(!append.contains(shapeData->document())) { 0237 append.append(shapeData->document()); 0238 } 0239 } 0240 } 0241 } 0242 0243 void KoFindText::Private::updateSelections() 0244 { 0245 QHash< QTextDocument*, QVector<QAbstractTextDocumentLayout::Selection> >::ConstIterator itr; 0246 for(itr = selections.constBegin(); itr != selections.constEnd(); ++itr) { 0247 KoTextDocument doc(itr.key()); 0248 doc.setSelections(itr.value()); 0249 } 0250 } 0251 0252 void KoFindText::Private::updateDocumentList() 0253 { 0254 foreach(QTextDocument *document, documents) { 0255 connect(document, SIGNAL(destroyed(QObject*)), q, SLOT(documentDestroyed(QObject*)), Qt::UniqueConnection); 0256 } 0257 } 0258 0259 void KoFindText::Private::documentDestroyed(QObject *document) 0260 { 0261 QTextDocument* doc = qobject_cast<QTextDocument*>(document); 0262 if(doc) { 0263 selections.remove(doc); 0264 documents.removeOne(doc); 0265 } 0266 } 0267 0268 void KoFindText::Private::updateCurrentMatch(int position) 0269 { 0270 Q_UNUSED(position); 0271 if (currentMatch.first != 0) { 0272 QVector<QAbstractTextDocumentLayout::Selection> sel = selections.value(currentMatch.first); 0273 Q_ASSERT(currentMatch.second < sel.count()); 0274 if(sel[currentMatch.second].format == currentMatchFormat) { 0275 sel[currentMatch.second].format = highlightFormat; 0276 } 0277 selections.insert(currentMatch.first, sel); 0278 } 0279 0280 const KoFindMatch match = q->currentMatch(); 0281 if (match.isValid() && match.location().canConvert<QTextCursor>() && match.container().canConvert<QTextDocument*>()) { 0282 QTextCursor cursor = match.location().value<QTextCursor>(); 0283 QTextDocument *document = match.container().value<QTextDocument*>(); 0284 QVector<QAbstractTextDocumentLayout::Selection> sel = selections.value(document); 0285 for (int i = 0; i < sel.size(); ++i) { 0286 if (sel[i].cursor == cursor) { 0287 sel[i].format = currentMatchFormat; 0288 selections.insert(document, sel); 0289 currentMatch.first = document; 0290 currentMatch.second = i; 0291 break; 0292 } 0293 } 0294 } 0295 } 0296 0297 void KoFindText::Private::initializeFormats() 0298 { 0299 if (!formatsInitialized) { 0300 highlightFormat.setBackground(Qt::yellow); 0301 currentMatchFormat.setBackground(qApp->palette().highlight()); 0302 currentMatchFormat.setForeground(qApp->palette().highlightedText()); 0303 currentSelectionFormat.setBackground(qApp->palette().alternateBase()); 0304 replacedFormat.setBackground(Qt::green); 0305 formatsInitialized = true; 0306 } 0307 } 0308 0309 void KoFindText::setFormat(FormatType formatType, const QTextCharFormat &format) 0310 { 0311 KoFindText::Private::initializeFormats(); 0312 0313 switch (formatType) { 0314 case HighlightFormat: 0315 KoFindText::Private::highlightFormat = format; 0316 break; 0317 case CurrentMatchFormat: 0318 KoFindText::Private::currentMatchFormat = format; 0319 break; 0320 case SelectionFormat: 0321 KoFindText::Private::currentSelectionFormat = format; 0322 break; 0323 case ReplacedFormat: 0324 KoFindText::Private::replacedFormat = format; 0325 break; 0326 } 0327 } 0328 0329 // have to include this because of Q_PRIVATE_SLOT 0330 #include "moc_KoFindText.cpp"