1 /*
2 * Copyright (C) 2010, 2013, 2017 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
reset()35 void SearchingNotifier::reset()
36 {
37 dotToDisplay_ = 0;
38 startTime_ = QTime::currentTime();
39 }
40
sendNotification(qint64 current_line,qint64 nb_lines)41 void SearchingNotifier::sendNotification( qint64 current_line, qint64 nb_lines )
42 {
43 qint64 progress;
44 if ( current_line < 0 )
45 progress = ( nb_lines + current_line ) * 100 / nb_lines;
46 else
47 progress = current_line * 100 / nb_lines;
48 emit notify( QFNotificationProgress( progress ) );
49
50 QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
51 startTime_ = QTime::currentTime();
52 }
53
set(int line,int column)54 void QuickFind::LastMatchPosition::set( int line, int column )
55 {
56 if ( ( line_ == -1 ) ||
57 ( ( line <= line_ ) && ( column < column_ ) ) )
58 {
59 line_ = line;
60 column_ = column;
61 }
62 }
63
set(const FilePosition & position)64 void QuickFind::LastMatchPosition::set( const FilePosition &position )
65 {
66 set( position.line(), position.column() );
67 }
68
isLater(int line,int column) const69 bool QuickFind::LastMatchPosition::isLater( int line, int column ) const
70 {
71 if ( line_ == -1 )
72 return false;
73 else if ( ( line == line_ ) && ( column >= column_ ) )
74 return true;
75 else if ( line > line_ )
76 return true;
77 else
78 return false;
79 }
80
isLater(const FilePosition & position) const81 bool QuickFind::LastMatchPosition::isLater( const FilePosition &position ) const
82 {
83 return isLater( position.line(), position.column() );
84 }
85
isSooner(int line,int column) const86 bool QuickFind::LastMatchPosition::isSooner( int line, int column ) const
87 {
88 if ( line_ == -1 )
89 return false;
90 else if ( ( line == line_ ) && ( column <= column_ ) )
91 return true;
92 else if ( line < line_ )
93 return true;
94 else
95 return false;
96 }
97
isSooner(const FilePosition & position) const98 bool QuickFind::LastMatchPosition::isSooner( const FilePosition &position ) const
99 {
100 return isSooner( position.line(), position.column() );
101 }
102
QuickFind(const AbstractLogData * const logData,Selection * selection,const QuickFindPattern * const quickFindPattern)103 QuickFind::QuickFind( const AbstractLogData* const logData,
104 Selection* selection,
105 const QuickFindPattern* const quickFindPattern ) :
106 logData_( logData ), selection_( selection ),
107 quickFindPattern_( quickFindPattern ),
108 lastMatch_(), firstMatch_(), searchingNotifier_(),
109 incrementalSearchStatus_()
110 {
111 connect( &searchingNotifier_, SIGNAL( notify( const QFNotification& ) ),
112 this, SIGNAL( notify( const QFNotification& ) ) );
113 }
114
incrementalSearchStop()115 void QuickFind::incrementalSearchStop()
116 {
117 if ( incrementalSearchStatus_.isOngoing() ) {
118 if ( selection_->isEmpty() ) {
119 // Nothing found?
120 // We reset the selection to what it was
121 *selection_ = incrementalSearchStatus_.initialSelection();
122 }
123
124 incrementalSearchStatus_ = IncrementalSearchStatus();
125 }
126 }
127
incrementalSearchAbort()128 void QuickFind::incrementalSearchAbort()
129 {
130 if ( incrementalSearchStatus_.isOngoing() ) {
131 // We reset the selection to what it was
132 *selection_ = incrementalSearchStatus_.initialSelection();
133 incrementalSearchStatus_ = IncrementalSearchStatus();
134 }
135 }
136
incrementallySearchForward()137 qint64 QuickFind::incrementallySearchForward()
138 {
139 LOG( logDEBUG ) << "QuickFind::incrementallySearchForward";
140
141 // Position where we start the search from
142 FilePosition start_position = selection_->getNextPosition();
143
144 if ( incrementalSearchStatus_.direction() == Forward ) {
145 // An incremental search is active, we restart the search
146 // from the initial point
147 LOG( logDEBUG ) << "Restart search from initial point";
148 start_position = incrementalSearchStatus_.position();
149 }
150 else {
151 // It's a new search so we search from the selection
152 incrementalSearchStatus_ = IncrementalSearchStatus(
153 Forward,
154 start_position,
155 *selection_ );
156 }
157
158 qint64 line_found = doSearchForward( start_position );
159
160 if ( line_found >= 0 ) {
161 // We have found a result...
162 // ... the caller will jump to this line.
163 return line_found;
164 }
165 else {
166 // No result...
167 // ... we want the client to show the initial line.
168 selection_->clear();
169 return incrementalSearchStatus_.position().line();
170 }
171 }
172
incrementallySearchBackward()173 qint64 QuickFind::incrementallySearchBackward()
174 {
175 LOG( logDEBUG ) << "QuickFind::incrementallySearchBackward";
176
177 // Position where we start the search from
178 FilePosition start_position = selection_->getPreviousPosition();
179
180 if ( incrementalSearchStatus_.direction() == Backward ) {
181 // An incremental search is active, we restart the search
182 // from the initial point
183 LOG( logDEBUG ) << "Restart search from initial point";
184 start_position = incrementalSearchStatus_.position();
185 }
186 else {
187 // It's a new search so we search from the selection
188 incrementalSearchStatus_ = IncrementalSearchStatus(
189 Backward,
190 start_position,
191 *selection_ );
192 }
193
194 qint64 line_found = doSearchBackward( start_position );
195
196 if ( line_found >= 0 ) {
197 // We have found a result...
198 // ... the caller will jump to this line.
199 return line_found;
200 }
201 else {
202 // No result...
203 // ... we want the client to show the initial line.
204 selection_->clear();
205 return incrementalSearchStatus_.position().line();
206 }
207 }
208
searchForward()209 qint64 QuickFind::searchForward()
210 {
211 incrementalSearchStatus_ = IncrementalSearchStatus();
212
213 // Position where we start the search from
214 FilePosition start_position = selection_->getNextPosition();
215
216 return doSearchForward( start_position );
217 }
218
219
searchBackward()220 qint64 QuickFind::searchBackward()
221 {
222 incrementalSearchStatus_ = IncrementalSearchStatus();
223
224 // Position where we start the search from
225 FilePosition start_position = selection_->getPreviousPosition();
226
227 return doSearchBackward( start_position );
228 }
229
230 // Internal implementation of forward search,
231 // returns the line where the pattern is found or -1 if not found.
232 // Parameters are the position the search shall start
doSearchForward(const FilePosition & start_position)233 qint64 QuickFind::doSearchForward( const FilePosition &start_position )
234 {
235 bool found = false;
236 int found_start_col;
237 int found_end_col;
238
239 if ( searchState_ == SearchState::OnGoing ) {
240 // This happens if this function is re-entered via the notifier
241 // We want the top most call to restart the search
242 searchState_ = SearchState::RestartNeeded;
243 return -1;
244 }
245
246 if ( ! quickFindPattern_->isActive() )
247 return -1;
248
249 // Optimisation: if we are already after the last match,
250 // we don't do any search at all.
251 if ( lastMatch_.isLater( start_position ) ) {
252 // Send a notification
253 emit notify( QFNotificationReachedEndOfFile() );
254
255 return -1;
256 }
257
258 qint64 line = start_position.line();
259 LOG( logDEBUG ) << "Start searching at line " << line;
260 // We look at the rest of the first line
261 if ( quickFindPattern_->isLineMatching(
262 logData_->getExpandedLineString( line ),
263 start_position.column() ) ) {
264 quickFindPattern_->getLastMatch( &found_start_col, &found_end_col );
265 found = true;
266 }
267 else {
268 searchingNotifier_.reset();
269 searchState_ = SearchState::OnGoing;
270 // And then the rest of the file
271 qint64 nb_lines = logData_->getNbLine();
272 int start_line = ++line;
273 while ( line < nb_lines ) {
274 // Check if someone has changed the search within this loop
275 if ( searchState_ == SearchState::RestartNeeded ) {
276 line = start_line;
277 searchState_ = SearchState::OnGoing;
278 LOG( logDEBUG ) << "QuickFind restarted at line " << line;
279 }
280
281 if ( quickFindPattern_->isLineMatching(
282 logData_->getExpandedLineString( line ) ) ) {
283 quickFindPattern_->getLastMatch(
284 &found_start_col, &found_end_col );
285 found = true;
286 LOG( logDEBUG ) << "QuickFind found!";
287 break;
288 }
289 line++;
290
291 // See if we need to notify of the ongoing search
292 // Note this could re-enter this function if the pattern is
293 // modified whilst the search is on-going, this is okay.
294 searchingNotifier_.ping( line, nb_lines );
295 }
296
297 searchState_ = SearchState::Idle;
298 }
299
300 if ( found ) {
301 selection_->selectPortion(
302 line, found_start_col, found_end_col );
303
304 // Clear any notification
305 emit clearNotification();
306
307 return line;
308 }
309 else {
310 // Update the position of the last match
311 FilePosition last_match_position = selection_->getPreviousPosition();
312 lastMatch_.set( last_match_position );
313
314 // Send a notification
315 emit notify( QFNotificationReachedEndOfFile() );
316
317 return -1;
318 }
319 }
320
321 // Internal implementation of backward search,
322 // returns the line where the pattern is found or -1 if not found.
323 // Parameters are the position the search shall start
doSearchBackward(const FilePosition & start_position)324 qint64 QuickFind::doSearchBackward( const FilePosition &start_position )
325 {
326 bool found = false;
327 int start_col;
328 int end_col;
329
330 if ( ! quickFindPattern_->isActive() )
331 return -1;
332
333 // Optimisation: if we are already before the first match,
334 // we don't do any search at all.
335 if ( firstMatch_.isSooner( start_position ) ) {
336 // Send a notification
337 emit notify( QFNotificationReachedBegininningOfFile() );
338
339 return -1;
340 }
341
342 qint64 line = start_position.line();
343 LOG( logDEBUG ) << "Start searching at line " << line;
344 // We look at the beginning of the first line
345 if ( ( start_position.column() > 0 )
346 && ( quickFindPattern_->isLineMatchingBackward(
347 logData_->getExpandedLineString( line ),
348 start_position.column() ) )
349 ) {
350 quickFindPattern_->getLastMatch( &start_col, &end_col );
351 found = true;
352 }
353 else {
354 searchingNotifier_.reset();
355 // And then the rest of the file
356 qint64 nb_lines = logData_->getNbLine();
357 line--;
358 while ( line >= 0 ) {
359 if ( quickFindPattern_->isLineMatchingBackward(
360 logData_->getExpandedLineString( line ) ) ) {
361 quickFindPattern_->getLastMatch( &start_col, &end_col );
362 found = true;
363 break;
364 }
365 line--;
366
367 // See if we need to notify of the ongoing search
368 searchingNotifier_.ping( -line, nb_lines );
369 }
370 }
371
372 if ( found )
373 {
374 selection_->selectPortion( line, start_col, end_col );
375
376 // Clear any notification
377 emit clearNotification();
378
379 return line;
380 }
381 else {
382 // Update the position of the first match
383 FilePosition first_match_position = selection_->getNextPosition();
384 firstMatch_.set( first_match_position );
385
386 // Send a notification
387 LOG( logDEBUG ) << "QF: Send BOF notification.";
388 emit notify( QFNotificationReachedBegininningOfFile() );
389
390 return -1;
391 }
392 }
393
resetLimits()394 void QuickFind::resetLimits()
395 {
396 lastMatch_.reset();
397 firstMatch_.reset();
398 }
399