00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "khtmlfind_p.h"
00022
00023 #include "khtml_part.h"
00024 #include "khtmlviewbar.h"
00025 #include "khtmlfindbar.h"
00026
00027 #include "dom/html_document.h"
00028 #include "html/html_documentimpl.h"
00029 #include "rendering/render_text.h"
00030 #include "misc/htmlhashes.h"
00031 #include "xml/dom_selection.h"
00032
00033 #include "khtmlview.h"
00034
00035 #include <config.h>
00036
00037 #include <QtGui/QClipboard>
00038
00039 #include "rendering/render_form.h"
00040
00041 #define d this
00042
00043 using namespace DOM;
00044
00045 KHTMLFind::KHTMLFind( KHTMLPart *part ) :
00046 m_part( part ),
00047 m_find( 0 ),
00048 m_findDialog( 0 )
00049 {
00050 connect( part, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()) );
00051 }
00052
00053
00054 KHTMLFind::~KHTMLFind()
00055 {
00056 d->m_find = 0;
00057 }
00058
00059 void KHTMLFind::findTextBegin()
00060 {
00061 d->m_findPos = -1;
00062 d->m_findNode = 0;
00063 d->m_findPosEnd = -1;
00064 d->m_findNodeEnd= 0;
00065 d->m_findPosStart = -1;
00066 d->m_findNodeStart = 0;
00067 d->m_findNodePrevious = 0;
00068 delete d->m_find;
00069 d->m_find = 0L;
00070 }
00071
00072 bool KHTMLFind::initFindNode( bool selection, bool reverse, bool fromCursor )
00073 {
00074 if ( m_part->document().isNull() )
00075 return false;
00076
00077 DOM::NodeImpl* firstNode = 0L;
00078 if (m_part->document().isHTMLDocument())
00079 firstNode = m_part->htmlDocument().body().handle();
00080 else
00081 firstNode = m_part->document().handle();
00082
00083 if ( !firstNode )
00084 {
00085
00086 return false;
00087 }
00088 if ( firstNode->id() == ID_FRAMESET )
00089 {
00090
00091 return false;
00092 }
00093
00094 if ( selection && m_part->hasSelection() )
00095 {
00096
00097 const Selection &sel = m_part->caret();
00098 if ( !fromCursor )
00099 {
00100 d->m_findNode = reverse ? sel.end().node() : sel.start().node();
00101 d->m_findPos = reverse ? sel.end().offset() : sel.start().offset();
00102 }
00103 d->m_findNodeEnd = reverse ? sel.start().node() : sel.end().node();
00104 d->m_findPosEnd = reverse ? sel.start().offset() : sel.end().offset();
00105 d->m_findNodeStart = !reverse ? sel.start().node() : sel.end().node();
00106 d->m_findPosStart = !reverse ? sel.start().offset() : sel.end().offset();
00107 d->m_findNodePrevious = d->m_findNodeStart;
00108 }
00109 else
00110 {
00111
00112 if ( !fromCursor )
00113 {
00114 d->m_findNode = firstNode;
00115 d->m_findPos = reverse ? -1 : 0;
00116 }
00117 d->m_findNodeEnd = reverse ? firstNode : 0;
00118 d->m_findPosEnd = reverse ? 0 : -1;
00119 d->m_findNodeStart = !reverse ? firstNode : 0;
00120 d->m_findPosStart = !reverse ? 0 : -1;
00121 d->m_findNodePrevious = d->m_findNodeStart;
00122 if ( reverse )
00123 {
00124
00125 khtml::RenderObject* obj = d->m_findNode ? d->m_findNode->renderer() : 0;
00126 if ( obj )
00127 {
00128
00129 while ( obj->lastChild() )
00130 {
00131 obj = obj->lastChild();
00132 }
00133
00134 while ( !obj->element() && obj->objectAbove() )
00135 {
00136 obj = obj->objectAbove();
00137 }
00138 d->m_findNode = obj->element();
00139 }
00140 }
00141 }
00142 return true;
00143 }
00144
00145 void KHTMLFind::deactivate()
00146 {
00147 kDebug(6050);
00148 d->m_lastFindState.options = d->m_findDialog->options();
00149 d->m_lastFindState.history = d->m_findDialog->findHistory();
00150 d->m_findDialog->deleteLater();
00151 d->m_findDialog = 0L;
00152
00153
00154 const DOM::Selection sel = m_part->caret();
00155 if(sel.start().node() == sel.end().node())
00156 {
00157 bool isLink = false;
00158
00159
00160 DOM::NodeImpl *parent = sel.start().node();
00161 while ( parent )
00162 {
00163 if ( parent->nodeType() == Node::ELEMENT_NODE && parent->id() == ID_A )
00164 {
00165 isLink = true;
00166 break;
00167 }
00168 parent = parent->parentNode();
00169 }
00170
00171 if(isLink == true)
00172 {
00173 static_cast<DOM::DocumentImpl *>( m_part->document().handle() )->setFocusNode( parent );
00174 }
00175 }
00176 }
00177
00178 void KHTMLFind::slotFindDestroyed()
00179 {
00180 d->m_find = 0;
00181 }
00182
00183 void KHTMLFind::activate()
00184 {
00185
00186 if ( m_part->document().isNull() )
00187 return;
00188
00189
00190 if ( d->m_findDialog )
00191 {
00192 m_part->pBottomViewBar()->showBarWidget( d->m_findDialog );
00193 return;
00194 }
00195
00196
00197 #ifndef QT_NO_CLIPBOARD
00198 disconnect( qApp->clipboard(), SIGNAL(selectionChanged()), m_part, SLOT(slotClearSelection()) );
00199 #endif
00200
00201
00202 d->m_findDialog = new KHTMLFindBar( m_part->widget() );
00203 d->m_findDialog->setHasSelection( m_part->hasSelection() );
00204 d->m_findDialog->setHasCursor( d->m_findNode != 0 );
00205 #if 0
00206 if ( d->m_findNode )
00207 d->m_lastFindState.options |= KFind::FromCursor;
00208 #endif
00209
00210
00211 d->m_findDialog->setFindHistory( d->m_lastFindState.history );
00212 d->m_findDialog->setOptions( d->m_lastFindState.options );
00213 d->m_findDialog->setFocus();
00214
00215 d->m_lastFindState.options = -1;
00216 d->m_lastFindState.last_dir = -1;
00217
00218 m_part->pBottomViewBar()->addBarWidget( d->m_findDialog );
00219 m_part->pBottomViewBar()->showBarWidget( d->m_findDialog );
00220 connect( d->m_findDialog, SIGNAL(searchChanged()), this, SLOT(slotSearchChanged()) );
00221 connect( d->m_findDialog, SIGNAL(findNextClicked()), this, SLOT(slotFindNext()) );
00222 connect( d->m_findDialog, SIGNAL(findPreviousClicked()), this, SLOT(slotFindPrevious()) );
00223 connect( d->m_findDialog, SIGNAL(hideMe()), this, SLOT(deactivate()) );
00224
00225 #ifndef QT_NO_CLIPBOARD
00226 connect( qApp->clipboard(), SIGNAL(selectionChanged()), m_part, SLOT(slotClearSelection()) );
00227 #endif
00228
00229 createNewKFind( d->m_findDialog->pattern(), 0 , d->m_findDialog, 0 );
00230 }
00231
00232 void KHTMLFind::createNewKFind( const QString &str, long options, QWidget *parent, KFindDialog *findDialog )
00233 {
00234
00235 if ( m_part->document().isNull() )
00236 return;
00237
00238 kDebug();
00239
00240 delete d->m_find;
00241 d->m_find = new KFind( str, options, parent, findDialog );
00242 d->m_find->closeFindNextDialog();
00243 connect( d->m_find, SIGNAL( highlight( const QString &, int, int ) ),
00244 this, SLOT( slotHighlight( const QString &, int, int ) ) );
00245 connect( d->m_find, SIGNAL( destroyed() ),
00246 this, SLOT( slotFindDestroyed() ) );
00247
00248
00249
00250 if ( !findDialog )
00251 {
00252 d->m_lastFindState.options = options;
00253 initFindNode( options & KFind::SelectedText,
00254 options & KFind::FindBackwards,
00255 options & KFind::FromCursor );
00256 }
00257 }
00258
00259 bool KHTMLFind::findTextNext( bool reverse )
00260 {
00261 if (!d->m_find)
00262 {
00263
00264 activate();
00265
00266
00267 if (!d->m_find)
00268 return false;
00269
00270
00271
00272 if (!d->m_findDialog || !d->m_findDialog->restoreLastPatternFromHistory())
00273 return false;
00274 }
00275
00276 m_part->view()->updateFindAheadTimeout();
00277 long options = 0;
00278 if ( d->m_findDialog )
00279 {
00280
00281
00282 if ( d->m_find->pattern() != d->m_findDialog->pattern() ) {
00283 d->m_find->setPattern( d->m_findDialog->pattern() );
00284 d->m_find->resetCounts();
00285 }
00286
00287
00288 options = d->m_findDialog->options();
00289 if ( d->m_lastFindState.options != options )
00290 {
00291 d->m_find->setOptions( options );
00292
00293 if ( options & KFind::SelectedText )
00294 Q_ASSERT( m_part->hasSelection() );
00295
00296 long difference = d->m_lastFindState.options ^ options;
00297 if ( difference & (KFind::SelectedText | KFind::FromCursor ) )
00298 {
00299
00300 (void) initFindNode( options & KFind::SelectedText,
00301 options & KFind::FindBackwards,
00302 options & KFind::FromCursor );
00303 }
00304 d->m_lastFindState.options = options;
00305 }
00306 } else {
00307
00308 options = d->m_lastFindState.options;
00309 }
00310
00311
00312 if( reverse )
00313 options = options ^ KFind::FindBackwards;
00314
00315
00316 if( d->m_find->options() != options )
00317 d->m_find->setOptions( options );
00318
00319
00320
00321
00322 if( d->m_lastFindState.last_dir != -1
00323 && bool( d->m_lastFindState.last_dir ) != bool( options & KFind::FindBackwards ))
00324 {
00325 qSwap( d->m_findNodeEnd, d->m_findNodeStart );
00326 qSwap( d->m_findPosEnd, d->m_findPosStart );
00327 qSwap( d->m_findNode, d->m_findNodePrevious );
00328
00329 khtml::RenderObject* obj = d->m_findNode ? d->m_findNode->renderer() : 0;
00330 khtml::RenderObject* end = d->m_findNodeEnd ? d->m_findNodeEnd->renderer() : 0;
00331 if ( obj == end )
00332 obj = 0L;
00333 else if ( obj )
00334 {
00335 do {
00336 obj = (options & KFind::FindBackwards) ? obj->objectAbove() : obj->objectBelow();
00337 } while ( obj && ( !obj->element() || obj->isInlineContinuation() ) );
00338 }
00339 if ( obj )
00340 d->m_findNode = obj->element();
00341 else
00342 d->m_findNode = 0;
00343 }
00344 d->m_lastFindState.last_dir = ( options & KFind::FindBackwards ) ? 1 : 0;
00345
00346 const int numMatchesOld = m_find->numMatches();
00347 KFind::Result res = KFind::NoMatch;
00348 khtml::RenderObject* obj = d->m_findNode ? d->m_findNode->renderer() : 0;
00349 khtml::RenderObject* end = d->m_findNodeEnd ? d->m_findNodeEnd->renderer() : 0;
00350
00351 while( res == KFind::NoMatch )
00352 {
00353 if ( d->m_find->needData() )
00354 {
00355 if ( !obj ) {
00356
00357 break;
00358 }
00359
00360
00361
00362
00363
00364 d->m_stringPortions.clear();
00365 bool newLine = false;
00366 QString str;
00367 DOM::NodeImpl* lastNode = d->m_findNode;
00368 while ( obj && !newLine )
00369 {
00370
00371 QString s;
00372 if ( obj->renderName() == QLatin1String("RenderTextArea") )
00373 {
00374 s = static_cast<khtml::RenderTextArea *>(obj)->text();
00375 s = s.replace(0xa0, ' ');
00376 }
00377 else if ( obj->renderName() == QLatin1String("RenderLineEdit") )
00378 {
00379 khtml::RenderLineEdit *parentLine= static_cast<khtml::RenderLineEdit *>(obj);
00380 if (parentLine->widget()->echoMode() == QLineEdit::Normal)
00381 s = parentLine->widget()->text();
00382 s = s.replace(0xa0, ' ');
00383 }
00384 else if ( obj->isText() )
00385 {
00386 bool isLink = false;
00387
00388
00389 if ( options & KHTMLPart::FindLinksOnly )
00390 {
00391 DOM::NodeImpl *parent = obj->element();
00392 while ( parent )
00393 {
00394 if ( parent->nodeType() == Node::ELEMENT_NODE && parent->id() == ID_A )
00395 {
00396 isLink = true;
00397 break;
00398 }
00399 parent = parent->parentNode();
00400 }
00401 }
00402 else
00403 {
00404 isLink = true;
00405 }
00406
00407 if ( isLink )
00408 {
00409 s = static_cast<khtml::RenderText *>(obj)->data().string();
00410 s = s.replace(0xa0, ' ');
00411 }
00412 }
00413 else if ( obj->isBR() )
00414 s = '\n';
00415 else if ( !obj->isInline() && !str.isEmpty() )
00416 s = '\n';
00417
00418 if ( lastNode == d->m_findNodeEnd )
00419 s.truncate( d->m_findPosEnd );
00420 if ( !s.isEmpty() )
00421 {
00422 newLine = s.indexOf( '\n' ) != -1;
00423 if( !( options & KFind::FindBackwards ))
00424 {
00425
00426 d->m_stringPortions.append( StringPortion( str.length(), lastNode ) );
00427 str += s;
00428 }
00429 else
00430 {
00431 for( QList<StringPortion>::Iterator it = d->m_stringPortions.begin();
00432 it != d->m_stringPortions.end();
00433 ++it )
00434 (*it).index += s.length();
00435 d->m_stringPortions.prepend( StringPortion( 0, lastNode ) );
00436 str.prepend( s );
00437 }
00438 }
00439
00440 if ( obj == end )
00441 obj = 0L;
00442 else
00443 {
00444
00445
00446 do {
00447
00448
00449
00450 obj = (options & KFind::FindBackwards) ? obj->objectAbove() : obj->objectBelow();
00451 } while ( obj && ( !obj->element() || obj->isInlineContinuation() ) );
00452 }
00453 if ( obj )
00454 lastNode = obj->element();
00455 else
00456 lastNode = 0;
00457 }
00458
00459 if ( !str.isEmpty() )
00460 {
00461 d->m_find->setData( str, d->m_findPos );
00462 }
00463
00464 d->m_findPos = -1;
00465 d->m_findNodePrevious = d->m_findNode;
00466 d->m_findNode = lastNode;
00467 }
00468 if ( !d->m_find->needData() )
00469 {
00470
00471 res = d->m_find->find();
00472 }
00473 }
00474
00475 if ( res == KFind::NoMatch )
00476 {
00477 kDebug(6050) << "No more matches.";
00478 if ( !(options & KHTMLPart::FindNoPopups) && d->m_find->shouldRestart() )
00479 {
00480 kDebug(6050) << "Restarting";
00481 initFindNode( false, options & KFind::FindBackwards, false );
00482 d->m_find->resetCounts();
00483 findTextNext( reverse );
00484 }
00485 else
00486 {
00487 kDebug(6050) << "Finishing";
00488
00489
00490 initFindNode( false, options & KFind::FindBackwards, false );
00491 d->m_find->resetCounts();
00492 d->m_part->clearSelection();
00493 }
00494 kDebug(6050) << "Dialog closed.";
00495 }
00496
00497 if ( m_findDialog != 0 )
00498 {
00499 m_findDialog->setFoundMatch( res == KFind::Match );
00500 m_findDialog->setAtEnd( m_find->numMatches() < numMatchesOld );
00501 }
00502
00503 return res == KFind::Match;
00504 }
00505
00506 void KHTMLFind::slotHighlight( const QString& , int index, int length )
00507 {
00508
00509 QList<StringPortion>::Iterator it = d->m_stringPortions.begin();
00510 const QList<StringPortion>::Iterator itEnd = d->m_stringPortions.end();
00511 QList<StringPortion>::Iterator prev = it;
00512
00513 while ( it != itEnd && (*it).index <= index )
00514 {
00515 prev = it;
00516 ++it;
00517 }
00518 Q_ASSERT ( prev != itEnd );
00519 DOM::NodeImpl* node = (*prev).node;
00520 Q_ASSERT( node );
00521
00522 Selection sel(Position(node, index - (*prev).index));
00523
00524 khtml::RenderObject* obj = node->renderer();
00525 khtml::RenderTextArea *renderTextArea = 0L;
00526 khtml::RenderLineEdit *renderLineEdit = 0L;
00527
00528 QRect highlightedRect;
00529 Q_ASSERT( obj );
00530 if ( obj )
00531 {
00532 int x = 0, y = 0;
00533
00534 if ( obj->renderName() == QLatin1String("RenderTextArea") )
00535 renderTextArea = static_cast<khtml::RenderTextArea *>(obj);
00536 if ( obj->renderName() == QLatin1String("RenderLineEdit") )
00537 renderLineEdit = static_cast<khtml::RenderLineEdit *>(obj);
00538 if ( !renderLineEdit && !renderTextArea )
00539
00540
00541 {
00542 int dummy;
00543 static_cast<khtml::RenderText *>(node->renderer())
00544 ->caretPos( sel.start().offset(), false, x, y, dummy, dummy );
00545
00546 if ( x != -1 || y != -1 )
00547 {
00548 int gox = m_part->view()->contentsX();
00549 if (x+50 > m_part->view()->contentsX() + m_part->view()->visibleWidth())
00550 gox = x - m_part->view()->visibleWidth() + 50;
00551 if (x-10 < m_part->view()->contentsX())
00552 gox = x - m_part->view()->visibleWidth() - 10;
00553 if (gox < 0) gox = 0;
00554 m_part->view()->setContentsPos(gox, y-50);
00555 highlightedRect.setTopLeft( m_part->view()->mapToGlobal(QPoint(x, y)) );
00556 }
00557 }
00558 }
00559
00560 it = prev;
00561 while ( it != itEnd && (*it).index < index + length )
00562 {
00563 prev = it;
00564 ++it;
00565 }
00566 Q_ASSERT ( prev != itEnd );
00567
00568 sel.moveTo(sel.start(), Position((*prev).node, index + length - (*prev).index));
00569
00570 #if 0
00571 kDebug(6050) << "slotHighlight: " << d->m_selectionStart.handle() << "," << d->m_startOffset << " - " <<
00572 d->m_selectionEnd.handle() << "," << d->m_endOffset << endl;
00573 it = d->m_stringPortions.begin();
00574 for ( ; it != d->m_stringPortions.end() ; ++it )
00575 kDebug(6050) << " StringPortion: from index=" << (*it).index << " -> node=" << (*it).node;
00576 #endif
00577 if ( renderTextArea )
00578 renderTextArea->highLightWord( length, sel.end().offset()-length );
00579 else if ( renderLineEdit )
00580 renderLineEdit->highLightWord( length, sel.end().offset()-length );
00581 else
00582 {
00583 m_part->setCaret( sel );
00584
00585 if (sel.end().node()->renderer() )
00586 {
00587 int x, y, height, dummy;
00588 static_cast<khtml::RenderText *>(sel.end().node()->renderer())
00589 ->caretPos( sel.end().offset(), false, x, y, dummy, height );
00590
00591 if ( x != -1 || y != -1 )
00592 {
00593
00594
00595 highlightedRect.setBottomRight( m_part->view()->mapToGlobal( QPoint(x, y+height) ) );
00596 }
00597 }
00598 }
00599 m_part->emitSelectionChanged();
00600
00601 #if 0
00602
00603 if ( d->m_findDialog && !highlightedRect.isNull() )
00604 {
00605 highlightedRect.translate( -m_part->view()->contentsX(), -m_part->view()->contentsY() );
00606
00607 KDialog::avoidArea( d->m_findDialog, highlightedRect );
00608 }
00609 #endif
00610 }
00611
00612 void KHTMLFind::slotSelectionChanged()
00613 {
00614 if ( d->m_findDialog )
00615 d->m_findDialog->setHasSelection( m_part->hasSelection() );
00616 }
00617
00618 void KHTMLFind::slotSearchChanged()
00619 {
00620 createNewKFind( m_findDialog->pattern(), m_findDialog->options(), m_findDialog, 0 );
00621 findTextNext();
00622 }
00623
00624 void KHTMLFind::slotFindNext()
00625 {
00626 findTextNext();
00627 }
00628
00629 void KHTMLFind::slotFindPrevious()
00630 {
00631 findTextNext( true );
00632 }