source: terepaima/terepaima-0.4.16/sources/searchmodel.cpp

desarrollostretch
Last change on this file was 1f4adec, checked in by aosorio <aosorio@…>, 8 years ago

Agregado proyecto base, esto luego del dh_make -f

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2
3Copyright 2014 S. Razi Alavizadeh
4Copyright 2014-2015 Adam Reichold
5
6This file is part of qpdfview.
7
8qpdfview is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 of the License, or
11(at your option) any later version.
12
13qpdfview is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with qpdfview.  If not, see <http://www.gnu.org/licenses/>.
20
21*/
22
23#include "searchmodel.h"
24
25#include <QApplication>
26#include <QtConcurrentRun>
27
28#include "documentview.h"
29
30static inline bool operator<(int page, const QPair< int, QRectF >& result) { return page < result.first; }
31static inline bool operator<(const QPair< int, QRectF >& result, int page) { return result.first < page; }
32
33namespace qpdfview
34{
35
36SearchModel* SearchModel::s_instance = 0;
37
38SearchModel* SearchModel::instance()
39{
40    if(s_instance == 0)
41    {
42        s_instance = new SearchModel(qApp);
43    }
44
45    return s_instance;
46}
47
48SearchModel::~SearchModel()
49{
50    foreach(TextWatcher* watcher, m_textWatchers)
51    {
52        watcher->waitForFinished();
53        watcher->deleteLater();
54    }
55
56    qDeleteAll(m_results);
57
58    s_instance = 0;
59}
60
61QModelIndex SearchModel::index(int row, int column, const QModelIndex& parent) const
62{
63    if(hasIndex(row, column, parent))
64    {
65        if(!parent.isValid())
66        {
67            return createIndex(row, column);
68        }
69        else
70        {
71            DocumentView* view = m_views.value(parent.row(), 0);
72
73            return createIndex(row, column, view);
74        }
75    }
76
77    return QModelIndex();
78}
79
80QModelIndex SearchModel::parent(const QModelIndex& child) const
81{
82    if(child.internalPointer() != 0)
83    {
84        DocumentView* view = static_cast< DocumentView* >(child.internalPointer());
85
86        return findView(view);
87    }
88
89    return QModelIndex();
90}
91
92int SearchModel::rowCount(const QModelIndex& parent) const
93{
94    if(!parent.isValid())
95    {
96        return m_views.count();
97    }
98    else
99    {
100        if(parent.internalPointer() == 0)
101        {
102            DocumentView* view = m_views.value(parent.row(), 0);
103            const Results* results = m_results.value(view, 0);
104
105            if(results != 0)
106            {
107                return results->count();
108            }
109        }
110    }
111
112    return 0;
113}
114
115int SearchModel::columnCount(const QModelIndex&) const
116{
117    return 1;
118}
119
120QVariant SearchModel::data(const QModelIndex& index, int role) const
121{
122    if(!index.isValid())
123    {
124        return QVariant();
125    }
126
127    if(index.internalPointer() == 0)
128    {
129        DocumentView* view = m_views.value(index.row(), 0);
130        const Results* results = m_results.value(view, 0);
131
132        if(results == 0)
133        {
134            return QVariant();
135        }
136
137        switch(role)
138        {
139        default:
140            return QVariant();
141        case CountRole:
142            return results->count();
143        case ProgressRole:
144            return view->searchProgress();
145        case Qt::DisplayRole:
146            return view->title();
147        case Qt::ToolTipRole:
148            return tr("<b>%1</b> occurrences").arg(results->count());
149        }
150    }
151    else
152    {
153        DocumentView* view = static_cast< DocumentView* >(index.internalPointer());
154        const Results* results = m_results.value(view, 0);
155
156        if(results == 0 || index.row() >= results->count())
157        {
158            return QVariant();
159        }
160
161        const Result& result = results->at(index.row());
162
163        switch(role)
164        {
165        default:
166            return QVariant();
167        case PageRole:
168            return result.first;
169        case RectRole:
170            return result.second;
171        case TextRole:
172            return view->searchText();
173        case MatchCaseRole:
174            return view->searchMatchCase();
175        case WholeWordsRole:
176            return view->searchWholeWords();
177        case MatchedTextRole:
178            return fetchMatchedText(view, result);
179        case SurroundingTextRole:
180            return fetchSurroundingText(view, result);
181        case Qt::ToolTipRole:
182            return tr("<b>%1</b> occurrences on page <b>%2</b>").arg(numberOfResultsOnPage(view, result.first)).arg(result.first);
183        }
184    }
185
186    return QVariant();
187}
188
189DocumentView* SearchModel::viewForIndex(const QModelIndex& index) const
190{
191    if(index.internalPointer() == 0)
192    {
193        return m_views.value(index.row(), 0);
194    }
195    else
196    {
197        return static_cast< DocumentView* >(index.internalPointer());
198    }
199}
200
201bool SearchModel::hasResults(DocumentView* view) const
202{
203    const Results* results = m_results.value(view, 0);
204
205    return results != 0 && !results->isEmpty();
206}
207
208bool SearchModel::hasResultsOnPage(DocumentView* view, int page) const
209{
210    const Results* results = m_results.value(view, 0);
211
212    return results != 0 && qBinaryFind(results->begin(), results->end(), page) != results->end();
213}
214
215int SearchModel::numberOfResultsOnPage(DocumentView* view, int page) const
216{
217    const Results* results = m_results.value(view, 0);
218
219    if(results == 0)
220    {
221        return 0;
222    }
223
224    const Results::const_iterator pageBegin = qLowerBound(results->constBegin(), results->constEnd(), page);
225    const Results::const_iterator pageEnd = qUpperBound(pageBegin, results->constEnd(), page);
226
227    return pageEnd - pageBegin;
228}
229
230QList< QRectF > SearchModel::resultsOnPage(DocumentView* view, int page) const
231{
232    QList< QRectF > resultsOnPage;
233
234    const Results* results = m_results.value(view, 0);
235
236    if(results != 0)
237    {
238        const Results::const_iterator pageBegin = qLowerBound(results->constBegin(), results->constEnd(), page);
239        const Results::const_iterator pageEnd = qUpperBound(pageBegin, results->constEnd(), page);
240
241        for(Results::const_iterator iterator = pageBegin; iterator != pageEnd; ++iterator)
242        {
243            resultsOnPage.append(iterator->second);
244        }
245    }
246
247    return resultsOnPage;
248}
249
250QPersistentModelIndex SearchModel::findResult(DocumentView* view, const QPersistentModelIndex& currentResult, int currentPage, FindDirection direction) const
251{
252    const Results* results = m_results.value(view, 0);
253
254    if(results == 0 || results->isEmpty())
255    {
256        return QPersistentModelIndex();
257    }
258
259    const int rows = results->count();
260    int row;
261
262    if(currentResult.isValid())
263    {
264        switch(direction)
265        {
266        default:
267        case FindNext:
268            row = (currentResult.row() + 1) % rows;
269            break;
270        case FindPrevious:
271            row = (currentResult.row() + rows - 1) % rows;
272            break;
273        }
274    }
275    else
276    {
277        switch(direction)
278        {
279        default:
280        case FindNext:
281        {
282            Results::const_iterator lowerBound = qLowerBound(results->constBegin(), results->constEnd(), currentPage);
283
284            row = (lowerBound - results->constBegin()) % rows;
285            break;
286        }
287        case FindPrevious:
288        {
289            Results::const_iterator upperBound = qUpperBound(results->constBegin(), results->constEnd(), currentPage);
290
291            row = ((upperBound - results->constBegin()) + rows - 1) % rows;
292            break;
293        }
294        }
295    }
296
297    return createIndex(row, 0, view);
298}
299
300void SearchModel::insertResults(DocumentView* view, int page, const QList< QRectF >& resultsOnPage)
301{
302    if(resultsOnPage.isEmpty())
303    {
304        return;
305    }
306
307    const QModelIndex parent = findOrInsertView(view);
308
309    Results* results = m_results.value(view);
310
311    Results::iterator at = qLowerBound(results->begin(), results->end(), page);
312    const int row = at - results->begin();
313
314    beginInsertRows(parent, row, row + resultsOnPage.size() - 1);
315
316    for(int index = resultsOnPage.size() - 1; index >= 0; --index)
317    {
318        at = results->insert(at, qMakePair(page, resultsOnPage.at(index)));
319    }
320
321    endInsertRows();
322}
323
324void SearchModel::clearResults(DocumentView* view)
325{
326    foreach(const TextCacheKey& key, m_textWatchers.keys())
327    {
328        if(key.first == view)
329        {
330            TextWatcher* watcher = m_textWatchers.take(key);
331
332            watcher->cancel();
333            watcher->waitForFinished();
334            watcher->deleteLater();
335        }
336    }
337
338    foreach(const TextCacheKey& key, m_textCache.keys())
339    {
340        if(key.first == view)
341        {
342            m_textCache.remove(key);
343        }
344    }
345
346    const QList< DocumentView* >::iterator at = qBinaryFind(m_views.begin(), m_views.end(), view);
347    const int row = at - m_views.begin();
348
349    if(at == m_views.end())
350    {
351        return;
352    }
353
354    beginRemoveRows(QModelIndex(), row, row);
355
356    m_views.erase(at);
357    delete m_results.take(view);
358
359    endRemoveRows();
360}
361
362void SearchModel::updateProgress(DocumentView* view)
363{
364    QModelIndex index = findView(view);
365
366    if(index.isValid())
367    {
368        emit dataChanged(index, index);
369    }
370}
371
372void SearchModel::on_fetchSurroundingText_finished()
373{
374    TextWatcher* watcher = dynamic_cast< TextWatcher* >(sender());
375
376    if(watcher == 0 || watcher->isCanceled())
377    {
378        return;
379    }
380
381    const TextJob job = watcher->result();
382
383    m_textWatchers.remove(job.key);
384    watcher->deleteLater();
385
386    DocumentView* view = job.key.first;
387    const Results* results = m_results.value(view, 0);
388
389    if(results == 0)
390    {
391        return;
392    }
393
394    const int cost = job.object->first.length() + job.object->second.length();
395
396    m_textCache.insert(job.key, job.object, cost);
397
398    emit dataChanged(createIndex(0, 0, view), createIndex(results->count() - 1, 0, view));
399}
400
401SearchModel::SearchModel(QObject* parent) : QAbstractItemModel(parent),
402    m_views(),
403    m_results(),
404    m_textCache(1 << 16),
405    m_textWatchers()
406{
407}
408
409QModelIndex SearchModel::findView(DocumentView *view) const
410{
411    const QList< DocumentView* >::const_iterator at = qBinaryFind(m_views.constBegin(), m_views.constEnd(), view);
412    const int row = at - m_views.constBegin();
413
414    if(at == m_views.constEnd())
415    {
416        return QModelIndex();
417    }
418
419    return createIndex(row, 0);
420}
421
422QModelIndex SearchModel::findOrInsertView(DocumentView* view)
423{
424    QList< DocumentView* >::iterator at = qBinaryFind(m_views.begin(), m_views.end(), view);
425    int row = at - m_views.begin();
426
427    if(at == m_views.end())
428    {
429        at = qUpperBound(m_views.begin(), m_views.end(), view);
430        row = at - m_views.begin();
431
432        beginInsertRows(QModelIndex(), row, row);
433
434        m_views.insert(at, view);
435        m_results.insert(view, new Results);
436
437        endInsertRows();
438    }
439
440    return createIndex(row, 0);
441}
442
443QString SearchModel::fetchMatchedText(DocumentView* view, const SearchModel::Result& result) const
444{
445    const TextCacheObject* object = fetchText(view, result);
446
447    return object != 0 ? object->first : QString();
448}
449
450QString SearchModel::fetchSurroundingText(DocumentView* view, const Result& result) const
451{
452    const TextCacheObject* object = fetchText(view, result);
453
454    return object != 0 ? object->second : QString();
455}
456
457const SearchModel::TextCacheObject* SearchModel::fetchText(DocumentView* view, const SearchModel::Result& result) const
458{
459    const TextCacheKey key = textCacheKey(view, result);
460
461    if(const TextCacheObject* object = m_textCache.object(key))
462    {
463        return object;
464    }
465
466    if(m_textWatchers.size() < 20 && !m_textWatchers.contains(key))
467    {
468        TextWatcher* watcher = new TextWatcher();
469        m_textWatchers.insert(key, watcher);
470
471        connect(watcher, SIGNAL(finished()), SLOT(on_fetchSurroundingText_finished()));
472
473        watcher->setFuture(QtConcurrent::run(textJob, key, result));
474    }
475
476    return 0;
477}
478
479inline SearchModel::TextCacheKey SearchModel::textCacheKey(DocumentView* view, const Result& result)
480{
481    QByteArray key;
482
483    QDataStream(&key, QIODevice::WriteOnly)
484            << result.first
485            << result.second;
486
487    return qMakePair(view, key);
488}
489
490SearchModel::TextJob SearchModel::textJob(const TextCacheKey& key, const Result& result)
491{
492    const QPair< QString, QString >& text = key.first->searchContext(result.first, result.second);
493
494    return TextJob(key, new TextCacheObject(text));
495}
496
497} // qpdfview
Note: See TracBrowser for help on using the repository browser.