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 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 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 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 64 void QuickFind::LastMatchPosition::set( const FilePosition &position ) 65 { 66 set( position.line(), position.column() ); 67 } 68 69 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 81 bool QuickFind::LastMatchPosition::isLater( const FilePosition &position ) const 82 { 83 return isLater( position.line(), position.column() ); 84 } 85 86 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 98 bool QuickFind::LastMatchPosition::isSooner( const FilePosition &position ) const 99 { 100 return isSooner( position.line(), position.column() ); 101 } 102 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 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 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 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 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 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 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 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 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 394 void QuickFind::resetLimits() 395 { 396 lastMatch_.reset(); 397 firstMatch_.reset(); 398 } 399