xref: /glogg/src/quickfind.cpp (revision 84af0c9b75fb9369ab66df476dd881cd6d30efcf)
1 /*
2  * Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors
3  *
4  * This file is part of glogg.
5  *
6  * glogg is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glogg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 // This file implements QuickFind.
21 // This class implements the Quick Find mechanism using references
22 // to the logData, the QFP and the selection passed.
23 // Search is started just after the selection and the selection is updated
24 // if a match is found.
25 
26 #include <QApplication>
27 
28 #include "log.h"
29 #include "quickfindpattern.h"
30 #include "selection.h"
31 #include "data/abstractlogdata.h"
32 
33 #include "quickfind.h"
34 
35 void SearchingNotifier::reset()
36 {
37     dotToDisplay_ = 0;
38     startTime_ = QTime::currentTime();
39 }
40 
41 void SearchingNotifier::sendNotification( qint64 current_line, qint64 nb_lines )
42 {
43     LOG( logDEBUG ) << "Emitting Searching....";
44     qint64 progress;
45     if ( current_line < 0 )
46         progress = ( nb_lines + current_line ) * 100 / nb_lines;
47     else
48         progress = current_line * 100 / nb_lines;
49     emit notify( QFNotificationProgress( progress ) );
50 
51     QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
52     startTime_ = QTime::currentTime().addMSecs( -800 );
53 }
54 
55 void QuickFind::LastMatchPosition::set( int line, int column )
56 {
57     if ( ( line_ == -1 ) ||
58             ( ( line <= line_ ) && ( column < column_ ) ) )
59     {
60         line_ = line;
61         column_ = column;
62     }
63 }
64 
65 void QuickFind::LastMatchPosition::set( const FilePosition &position )
66 {
67     set( position.line(), position.column() );
68 }
69 
70 bool QuickFind::LastMatchPosition::isLater( int line, int column ) const
71 {
72     if ( line_ == -1 )
73         return false;
74     else if ( ( line == line_ ) && ( column >= column_ ) )
75         return true;
76     else if ( line > line_ )
77         return true;
78     else
79         return false;
80 }
81 
82 bool QuickFind::LastMatchPosition::isLater( const FilePosition &position ) const
83 {
84     return isLater( position.line(), position.column() );
85 }
86 
87 bool QuickFind::LastMatchPosition::isSooner( int line, int column ) const
88 {
89     if ( line_ == -1 )
90         return false;
91     else if ( ( line == line_ ) && ( column <= column_ ) )
92         return true;
93     else if ( line < line_ )
94         return true;
95     else
96         return false;
97 }
98 
99 bool QuickFind::LastMatchPosition::isSooner( const FilePosition &position ) const
100 {
101     return isSooner( position.line(), position.column() );
102 }
103 
104 QuickFind::QuickFind( const AbstractLogData* const logData,
105         Selection* selection,
106         const QuickFindPattern* const quickFindPattern ) :
107     logData_( logData ), selection_( selection ),
108     quickFindPattern_( quickFindPattern ),
109     lastMatch_(), firstMatch_(), searchingNotifier_(),
110     incrementalSearchStatus_()
111 {
112     connect( &searchingNotifier_, SIGNAL( notify( const QFNotification& ) ),
113             this, SIGNAL( notify( const QFNotification& ) ) );
114 }
115 
116 void QuickFind::incrementalSearchStop()
117 {
118     if ( incrementalSearchStatus_.isOngoing() ) {
119         if ( selection_->isEmpty() ) {
120             // Nothing found?
121             // We reset the selection to what it was
122             *selection_ = incrementalSearchStatus_.initialSelection();
123         }
124 
125         incrementalSearchStatus_ = IncrementalSearchStatus();
126     }
127 }
128 
129 void QuickFind::incrementalSearchAbort()
130 {
131     if ( incrementalSearchStatus_.isOngoing() ) {
132         // We reset the selection to what it was
133         *selection_ = incrementalSearchStatus_.initialSelection();
134         incrementalSearchStatus_ = IncrementalSearchStatus();
135     }
136 }
137 
138 qint64 QuickFind::incrementallySearchForward()
139 {
140     LOG( logDEBUG ) << "QuickFind::incrementallySearchForward";
141 
142     // Position where we start the search from
143     FilePosition start_position = selection_->getNextPosition();
144 
145     if ( incrementalSearchStatus_.direction() == Forward ) {
146         // An incremental search is active, we restart the search
147         // from the initial point
148         LOG( logDEBUG ) << "Restart search from initial point";
149         start_position = incrementalSearchStatus_.position();
150     }
151     else {
152         // It's a new search so we search from the selection
153         incrementalSearchStatus_ = IncrementalSearchStatus(
154                 Forward,
155                 start_position,
156                 *selection_ );
157     }
158 
159     qint64 line_found = doSearchForward( start_position );
160 
161     if ( line_found >= 0 ) {
162         // We have found a result...
163         // ... the caller will jump to this line.
164         return line_found;
165     }
166     else {
167         // No result...
168         // ... we want the client to show the initial line.
169         selection_->clear();
170         return incrementalSearchStatus_.position().line();
171     }
172 }
173 
174 qint64 QuickFind::incrementallySearchBackward()
175 {
176     LOG( logDEBUG ) << "QuickFind::incrementallySearchBackward";
177 
178     // Position where we start the search from
179     FilePosition start_position = selection_->getPreviousPosition();
180 
181     if ( incrementalSearchStatus_.direction() == Backward ) {
182         // An incremental search is active, we restart the search
183         // from the initial point
184         LOG( logDEBUG ) << "Restart search from initial point";
185         start_position = incrementalSearchStatus_.position();
186     }
187     else {
188         // It's a new search so we search from the selection
189         incrementalSearchStatus_ = IncrementalSearchStatus(
190                 Backward,
191                 start_position,
192                 *selection_ );
193     }
194 
195     qint64 line_found = doSearchBackward( start_position );
196 
197     if ( line_found >= 0 ) {
198         // We have found a result...
199         // ... the caller will jump to this line.
200         return line_found;
201     }
202     else {
203         // No result...
204         // ... we want the client to show the initial line.
205         selection_->clear();
206         return incrementalSearchStatus_.position().line();
207     }
208 }
209 
210 qint64 QuickFind::searchForward()
211 {
212     incrementalSearchStatus_ = IncrementalSearchStatus();
213 
214     // Position where we start the search from
215     FilePosition start_position = selection_->getNextPosition();
216 
217     return doSearchForward( start_position );
218 }
219 
220 
221 qint64 QuickFind::searchBackward()
222 {
223     incrementalSearchStatus_ = IncrementalSearchStatus();
224 
225     // Position where we start the search from
226     FilePosition start_position = selection_->getPreviousPosition();
227 
228     return doSearchBackward( start_position );
229 }
230 
231 // Internal implementation of forward search,
232 // returns the line where the pattern is found or -1 if not found.
233 // Parameters are the position the search shall start
234 qint64 QuickFind::doSearchForward( const FilePosition &start_position )
235 {
236     bool found = false;
237     int found_start_col;
238     int found_end_col;
239 
240     if ( ! quickFindPattern_->isActive() )
241         return -1;
242 
243     // Optimisation: if we are already after the last match,
244     // we don't do any search at all.
245     if ( lastMatch_.isLater( start_position ) ) {
246         // Send a notification
247         emit notify( QFNotificationReachedEndOfFile() );
248 
249         return -1;
250     }
251 
252     qint64 line = start_position.line();
253     LOG( logDEBUG ) << "Start searching at line " << line;
254     // We look at the rest of the first line
255     if ( quickFindPattern_->isLineMatching(
256                 logData_->getExpandedLineString( line ),
257                 start_position.column() ) ) {
258         quickFindPattern_->getLastMatch( &found_start_col, &found_end_col );
259         found = true;
260     }
261     else {
262         searchingNotifier_.reset();
263         // And then the rest of the file
264         qint64 nb_lines = logData_->getNbLine();
265         line++;
266         while ( line < nb_lines ) {
267             if ( quickFindPattern_->isLineMatching(
268                         logData_->getExpandedLineString( line ) ) ) {
269                 quickFindPattern_->getLastMatch(
270                         &found_start_col, &found_end_col );
271                 found = true;
272                 break;
273             }
274             line++;
275 
276             // See if we need to notify of the ongoing search
277             searchingNotifier_.ping( line, nb_lines );
278         }
279     }
280 
281     if ( found ) {
282         selection_->selectPortion(
283                 line, found_start_col, found_end_col );
284 
285         // Clear any notification
286         emit clearNotification();
287 
288         return line;
289     }
290     else {
291         // Update the position of the last match
292         FilePosition last_match_position = selection_->getPreviousPosition();
293         lastMatch_.set( last_match_position );
294 
295         // Send a notification
296         emit notify( QFNotificationReachedEndOfFile() );
297 
298         return -1;
299     }
300 }
301 
302 // Internal implementation of backward search,
303 // returns the line where the pattern is found or -1 if not found.
304 // Parameters are the position the search shall start
305 qint64 QuickFind::doSearchBackward( const FilePosition &start_position )
306 {
307     bool found = false;
308     int start_col;
309     int end_col;
310 
311     if ( ! quickFindPattern_->isActive() )
312         return -1;
313 
314     // Optimisation: if we are already before the first match,
315     // we don't do any search at all.
316     if ( firstMatch_.isSooner( start_position ) ) {
317         // Send a notification
318         emit notify( QFNotificationReachedBegininningOfFile() );
319 
320         return -1;
321     }
322 
323     qint64 line = start_position.line();
324     LOG( logDEBUG ) << "Start searching at line " << line;
325     // We look at the beginning of the first line
326     if (    ( start_position.column() > 0 )
327          && ( quickFindPattern_->isLineMatchingBackward(
328                  logData_->getExpandedLineString( line ),
329                  start_position.column() ) )
330        ) {
331         quickFindPattern_->getLastMatch( &start_col, &end_col );
332         found = true;
333     }
334     else {
335         searchingNotifier_.reset();
336         // And then the rest of the file
337         qint64 nb_lines = logData_->getNbLine();
338         line--;
339         while ( line >= 0 ) {
340             if ( quickFindPattern_->isLineMatchingBackward(
341                         logData_->getExpandedLineString( line ) ) ) {
342                 quickFindPattern_->getLastMatch( &start_col, &end_col );
343                 found = true;
344                 break;
345             }
346             line--;
347 
348             // See if we need to notify of the ongoing search
349             searchingNotifier_.ping( -line, nb_lines );
350         }
351     }
352 
353     if ( found )
354     {
355         selection_->selectPortion( line, start_col, end_col );
356 
357         // Clear any notification
358         emit clearNotification();
359 
360         return line;
361     }
362     else {
363         // Update the position of the first match
364         FilePosition first_match_position = selection_->getNextPosition();
365         firstMatch_.set( first_match_position );
366 
367         // Send a notification
368         LOG( logDEBUG ) << "QF: Send BOF notification.";
369         emit notify( QFNotificationReachedBegininningOfFile() );
370 
371         return -1;
372     }
373 }
374 
375 void QuickFind::resetLimits()
376 {
377     lastMatch_.reset();
378     firstMatch_.reset();
379 }
380