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 ) << "Send 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