File indexing completed on 2024-05-12 15:39:36

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2008 Bernhard Beschow <bbeschow cs tu berlin de>
0004  *           (C) 2009 Germain Garand <germain@ebooksfrance.org>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public License
0017  * along with this library; see the file COPYING.LIB.  If not, write to
0018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020  */
0021 
0022 #include "khtmlfind_p.h"
0023 
0024 #include "khtml_part.h"
0025 #include "khtmlviewbar.h"
0026 #include "khtmlfindbar.h"
0027 
0028 #include "dom/html_document.h"
0029 #include "html/html_documentimpl.h"
0030 #include "rendering/render_text.h"
0031 #include "rendering/render_replaced.h"
0032 #include "xml/dom_selection.h"
0033 
0034 #include "khtmlview.h"
0035 
0036 #include <QClipboard>
0037 
0038 #include "rendering/render_form.h"
0039 
0040 #define d this
0041 
0042 using khtml::RenderPosition;
0043 
0044 using namespace DOM;
0045 
0046 KHTMLFind::KHTMLFind(KHTMLPart *part, KHTMLFind *parent) :
0047     m_part(part),
0048     m_find(nullptr),
0049     m_parent(parent),
0050     m_findDialog(nullptr)
0051 {
0052     connect(part, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
0053 }
0054 
0055 KHTMLFind::~KHTMLFind()
0056 {
0057     d->m_find = nullptr; // deleted by its parent, the view.
0058 }
0059 
0060 void KHTMLFind::findTextBegin()
0061 {
0062     d->m_findPos = -1;
0063     d->m_findNode = nullptr;
0064     d->m_findPosEnd = -1;
0065     d->m_findNodeEnd = nullptr;
0066     d->m_findPosStart = -1;
0067     d->m_findNodeStart = nullptr;
0068     d->m_findNodePrevious = nullptr;
0069     delete d->m_find;
0070     d->m_find = nullptr;
0071 }
0072 
0073 bool KHTMLFind::initFindNode(bool selection, bool reverse, bool fromCursor)
0074 {
0075     if (m_part->document().isNull()) {
0076         return false;
0077     }
0078 
0079     DOM::NodeImpl *firstNode = nullptr;
0080     if (m_part->document().isHTMLDocument()) {
0081         firstNode = m_part->htmlDocument().body().handle();
0082     } else {
0083         firstNode = m_part->document().handle();
0084     }
0085 
0086     if (!firstNode) {
0087         //qCDebug(KHTML_LOG) << "no first node (body or doc) -> return false";
0088         return false;
0089     }
0090     if (selection && m_part->hasSelection()) {
0091         //qCDebug(KHTML_LOG) << "using selection";
0092         const Selection &sel = m_part->caret();
0093         if (!fromCursor) {
0094             d->m_findNode = reverse ? sel.end().node() : sel.start().node();
0095             d->m_findPos = reverse ? sel.end().offset() : sel.start().offset();
0096         }
0097         d->m_findNodeEnd = reverse ? sel.start().node() : sel.end().node();
0098         d->m_findPosEnd = reverse ? sel.start().offset() : sel.end().offset();
0099         d->m_findNodeStart = !reverse ? sel.start().node() : sel.end().node();
0100         d->m_findPosStart = !reverse ? sel.start().offset() : sel.end().offset();
0101         d->m_findNodePrevious = d->m_findNodeStart;
0102     } else { // whole document
0103         //qCDebug(KHTML_LOG) << "whole doc";
0104         if (!fromCursor) {
0105             d->m_findNode = firstNode;
0106             d->m_findPos = reverse ? -1 : 0;
0107         }
0108         d->m_findNodeEnd = reverse ? firstNode : nullptr;
0109         d->m_findPosEnd = reverse ? 0 : -1;
0110         d->m_findNodeStart = !reverse ? firstNode : nullptr;
0111         d->m_findPosStart = !reverse ? 0 : -1;
0112         d->m_findNodePrevious = d->m_findNodeStart;
0113         if (reverse) {
0114             // Need to find out the really last object, to start from it
0115             khtml::RenderObject *obj = d->m_findNode ? d->m_findNode->renderer() : nullptr;
0116             if (obj) {
0117                 // find the last object in the render tree
0118                 while (obj->lastChild()) {
0119                     obj = obj->lastChild();
0120                 }
0121                 // now get the last object with a NodeImpl associated
0122                 while (!obj->element() && obj->objectAbove()) {
0123                     obj = obj->objectAbove();
0124                 }
0125                 d->m_findNode = obj->element();
0126             }
0127         }
0128     }
0129     return true;
0130 }
0131 
0132 void KHTMLFind::deactivate()
0133 {
0134     // qCDebug(KHTML_LOG);
0135     d->m_lastFindState.options = d->m_findDialog->options();
0136     d->m_lastFindState.history = d->m_findDialog->findHistory();
0137     if (!m_parent) {
0138         d->m_findDialog->hide();
0139         d->m_findDialog->disconnect();
0140         d->m_findDialog->deleteLater();
0141     }
0142     d->m_findDialog = nullptr;
0143 
0144     // if the selection is limited to a single link, that link gets focus
0145     const DOM::Selection sel = m_part->caret();
0146     if (sel.start().node() == sel.end().node()) {
0147         bool isLink = false;
0148 
0149         // checks whether the node has a <A> parent
0150         DOM::NodeImpl *parent = sel.start().node();
0151         while (parent) {
0152             if (parent->nodeType() == Node::ELEMENT_NODE && parent->id() == ID_A) {
0153                 isLink = true;
0154                 break;
0155             }
0156             parent = parent->parentNode();
0157         }
0158 
0159         if (isLink == true) {
0160             static_cast<DOM::DocumentImpl *>(m_part->document().handle())->setFocusNode(parent);
0161         }
0162     }
0163 }
0164 
0165 void KHTMLFind::slotFindDestroyed()
0166 {
0167     d->m_find = nullptr;
0168 }
0169 
0170 void KHTMLFind::activate()
0171 {
0172     // First do some init to make sure we can search in this frame
0173     if (m_part->document().isNull()) {
0174         return;
0175     }
0176 
0177     // Raise if already opened
0178     if (d->m_findDialog && !m_parent) {
0179         m_part->pBottomViewBar()->showBarWidget(d->m_findDialog);
0180         return;
0181     }
0182 
0183     // The lineedit of the dialog would make khtml lose its selection, otherwise
0184 #ifndef QT_NO_CLIPBOARD
0185     disconnect(qApp->clipboard(), SIGNAL(selectionChanged()), m_part, SLOT(slotClearSelection()));
0186 #endif
0187 
0188     if (m_parent) {
0189         d->m_findDialog  = m_parent->findBar();
0190     } else {
0191         // Now show the dialog in which the user can choose options.
0192         d->m_findDialog = new KHTMLFindBar(m_part->widget());
0193         d->m_findDialog->setHasSelection(m_part->hasSelection());
0194         d->m_findDialog->setHasCursor(d->m_findNode != nullptr);
0195 #if 0
0196         if (d->m_findNode) { // has a cursor -> default to 'FromCursor'
0197             d->m_lastFindState.options |= KFind::FromCursor;
0198         }
0199 #endif
0200 
0201         // TODO? optionsDialog.setPattern( d->m_lastFindState.text );
0202         d->m_findDialog->setFindHistory(d->m_lastFindState.history);
0203         d->m_findDialog->setOptions(d->m_lastFindState.options);
0204         d->m_findDialog->setFocus();
0205 
0206         d->m_lastFindState.options = -1; // force update in findTextNext
0207         d->m_lastFindState.last_dir = -1;
0208 
0209         m_part->pBottomViewBar()->addBarWidget(d->m_findDialog);
0210         m_part->pBottomViewBar()->showBarWidget(d->m_findDialog);
0211         connect(d->m_findDialog, SIGNAL(searchChanged()), this, SLOT(slotSearchChanged()));
0212         connect(d->m_findDialog, SIGNAL(findNextClicked()), this, SLOT(slotFindNext()));
0213         connect(d->m_findDialog, SIGNAL(findPreviousClicked()), this, SLOT(slotFindPrevious()));
0214         connect(d->m_findDialog, SIGNAL(hideMe()), this, SLOT(deactivate()));
0215     }
0216 #ifndef QT_NO_CLIPBOARD
0217     connect(qApp->clipboard(), SIGNAL(selectionChanged()), m_part, SLOT(slotClearSelection()));
0218 #endif
0219     if (m_findDialog) {
0220         createNewKFind(m_findDialog->pattern(), 0 /*options*/, m_findDialog, nullptr);
0221     } else if (m_parent && m_parent->find()) {
0222         createNewKFind(m_parent->find()->pattern(), m_parent->find()->options(), static_cast<QWidget *>(m_parent->find()->parent()), nullptr);
0223     }
0224 }
0225 
0226 // ### this crawling through the render tree sucks. There should be another way to
0227 //     do that.
0228 static inline KHTMLPart *innerPart(khtml::RenderObject *ro)
0229 {
0230     if (!ro || !ro->isWidget() || ro->isFormElement()) {
0231         return nullptr;
0232     }
0233     KHTMLView *v = qobject_cast<KHTMLView *>(static_cast<khtml::RenderWidget *>(ro)->widget());
0234     return v ? v->part() : nullptr;
0235 }
0236 static inline KHTMLPart *innerPartFromNode(DOM::NodeImpl *node)
0237 {
0238     return (node && node->renderer() ? innerPart(node->renderer()) : nullptr);
0239 }
0240 
0241 void KHTMLFind::createNewKFind(const QString &str, long options, QWidget *parent, KFindDialog *findDialog)
0242 {
0243     // First do some init to make sure we can search in this frame
0244     if (m_part->document().isNull()) {
0245         return;
0246     }
0247 
0248     if (m_findNode) {
0249         if (KHTMLPart *p = innerPartFromNode(m_findNode)) {
0250             p->clearSelection();
0251             p->findTextBegin();
0252         }
0253     }
0254 
0255     // Create the KFind object
0256     delete d->m_find;
0257     d->m_find = new KFind(str, options, parent, findDialog);
0258     d->m_find->closeFindNextDialog(); // we use KFindDialog non-modal, so we don't want other dlg popping up
0259     connect(d->m_find, SIGNAL(highlight(QString,int,int)),
0260             this, SLOT(slotHighlight(QString,int,int)));
0261     connect(d->m_find, SIGNAL(destroyed()),
0262             this, SLOT(slotFindDestroyed()));
0263     //connect(d->m_find, SIGNAL(findNext()),
0264     //        this, SLOT(slotFindNext()) );
0265 
0266     if (!findDialog) {
0267         d->m_lastFindState.options = options;
0268         initFindNode(options & KFind::SelectedText,
0269                      options & KFind::FindBackwards,
0270                      options & KFind::FromCursor);
0271     }
0272 }
0273 
0274 bool KHTMLFind::findTextNext(bool reverse)
0275 {
0276     if (!d->m_find) {
0277         // We didn't show the find dialog yet, let's do it then (#49442)
0278         activate();
0279 
0280         // FIXME Ugly hack: activate() may not create KFind object, so check whether it was created
0281         if (!d->m_find) {
0282             return false;
0283         }
0284 
0285         // It also means the user is trying to match a previous pattern, so try and
0286         // restore the last saved pattern.
0287         if (!m_parent && (!d->m_findDialog || !d->m_findDialog->restoreLastPatternFromHistory())) {
0288             return false;
0289         }
0290     }
0291 
0292     long options = 0;
0293     if (d->m_findDialog) { // 0 when we close the dialog
0294         // there is a search dialog
0295         // make sure pattern from search dialog is used
0296         // (### in fact pattern changes should always trigger a reconstruction of the KFind object cf. slotSearchChanged
0297         //   - so make this an assert)
0298         if ((d->m_find->pattern() != d->m_findDialog->pattern())) {
0299             d->m_find->setPattern(d->m_findDialog->pattern());
0300             d->m_find->resetCounts();
0301         }
0302 
0303         // make sure options from search dialog are used
0304         options = d->m_findDialog->options();
0305         if (d->m_lastFindState.options != options) {
0306             d->m_find->setOptions(options);
0307 
0308             if (options & KFind::SelectedText) { //#### FIXME find in selection for frames!
0309                 Q_ASSERT(m_part->hasSelection());
0310             }
0311 
0312             long difference = d->m_lastFindState.options ^ options;
0313             if (difference & (KFind::SelectedText | KFind::FromCursor)) {
0314                 // Important options changed -> reset search range
0315                 (void) initFindNode(options & KFind::SelectedText,
0316                                     options & KFind::FindBackwards,
0317                                     options & KFind::FromCursor);
0318             }
0319             d->m_lastFindState.options = options;
0320         }
0321     } else {
0322         // no dialog
0323         options = d->m_lastFindState.options;
0324     }
0325 
0326     // only adopt options for search direction manually
0327     if (reverse) {
0328         options = options ^ KFind::FindBackwards;
0329     }
0330 
0331     // make sure our options are used by KFind
0332     if (d->m_find->options() != options) {
0333         d->m_find->setOptions(options);
0334     }
0335 
0336     // Changing find direction. Start and end nodes must be switched.
0337     // Additionally since d->m_findNode points after the last node
0338     // that was searched, it needs to be "after" it in the opposite direction.
0339     if (d->m_lastFindState.last_dir != -1
0340             && bool(d->m_lastFindState.last_dir) != bool(options & KFind::FindBackwards)) {
0341         qSwap(d->m_findNodeEnd, d->m_findNodeStart);
0342         qSwap(d->m_findPosEnd, d->m_findPosStart);
0343         qSwap(d->m_findNode, d->m_findNodePrevious);
0344 
0345         // d->m_findNode now point at the end of the last searched line - advance one node
0346         khtml::RenderObject *obj = d->m_findNode ? d->m_findNode->renderer() : nullptr;
0347         khtml::RenderObject *end = d->m_findNodeEnd ? d->m_findNodeEnd->renderer() : nullptr;
0348         if (obj == end) {
0349             obj = nullptr;
0350         } else if (obj) {
0351             do {
0352                 obj = (options & KFind::FindBackwards) ? obj->objectAbove() : obj->objectBelow();
0353             } while (obj && (!obj->element() || obj->isInlineContinuation()));
0354         }
0355         if (obj) {
0356             d->m_findNode = obj->element();
0357         } else {
0358             // already at end, start again
0359             (void) initFindNode(options & KFind::SelectedText,
0360                                 options & KFind::FindBackwards,
0361                                 options & KFind::FromCursor);
0362         }
0363     }
0364     d->m_lastFindState.last_dir = (options & KFind::FindBackwards) ? 1 : 0;
0365 
0366     int numMatchesOld = m_find->numMatches();
0367     KFind::Result res = KFind::NoMatch;
0368     khtml::RenderObject *obj = d->m_findNode ? d->m_findNode->renderer() : nullptr;
0369     khtml::RenderObject *end = d->m_findNodeEnd ? d->m_findNodeEnd->renderer() : nullptr;
0370     //qCDebug(KHTML_LOG) << "obj=" << obj << " end=" << end;
0371     while (res == KFind::NoMatch) {
0372         if (d->m_find->needData()) {
0373             if (!obj) {
0374                 //qCDebug(KHTML_LOG) << "obj=0 -> done";
0375                 break; // we're done
0376             }
0377             //qCDebug(KHTML_LOG) << " gathering data";
0378             // First make up the QString for the current 'line' (i.e. up to \n)
0379             // We also want to remember the DOMNode for every portion of the string.
0380             // We store this in an index->node list.
0381 
0382             d->m_stringPortions.clear();
0383             bool newLine = false;
0384             QString str;
0385             DOM::NodeImpl *lastNode = d->m_findNode;
0386             while (obj && !newLine) {
0387                 // Grab text from render object
0388                 QString s;
0389                 if (obj->renderName() == QLatin1String("RenderTextArea")) {
0390                     s = static_cast<khtml::RenderTextArea *>(obj)->text();
0391                     s = s.replace(0xa0, ' ');
0392                 } else if (obj->renderName() ==  QLatin1String("RenderLineEdit")) {
0393                     khtml::RenderLineEdit *parentLine = static_cast<khtml::RenderLineEdit *>(obj);
0394                     if (parentLine->widget()->echoMode() == QLineEdit::Normal) {
0395                         s = parentLine->widget()->text();
0396                     }
0397                     s = s.replace(0xa0, ' ');
0398                 } else if (obj->isText()) {
0399                     bool isLink = false;
0400 
0401                     // checks whether the node has a <A> parent
0402                     if (options & KHTMLPart::FindLinksOnly) {
0403                         DOM::NodeImpl *parent = obj->element();
0404                         while (parent) {
0405                             if (parent->nodeType() == Node::ELEMENT_NODE && parent->id() == ID_A) {
0406                                 isLink = true;
0407                                 break;
0408                             }
0409                             parent = parent->parentNode();
0410                         }
0411                     } else {
0412                         isLink = true;
0413                     }
0414 
0415                     if (isLink) {
0416                         s = static_cast<khtml::RenderText *>(obj)->data().string();
0417                         s = s.replace(0xa0, ' ');
0418                     }
0419                 } else if (KHTMLPart *p = innerPart(obj)) {
0420                     if (p->pFindTextNextInThisFrame(reverse)) {
0421                         numMatchesOld++;
0422                         res = KFind::Match;
0423                         lastNode = obj->element();
0424                         break;
0425                     }
0426 
0427                 } else if (obj->isBR()) {
0428                     s = '\n';
0429                 } else if (!obj->isInline() && !str.isEmpty()) {
0430                     s = '\n';
0431                 }
0432 
0433                 if (lastNode == d->m_findNodeEnd) {
0434                     s.truncate(d->m_findPosEnd);
0435                 }
0436                 if (!s.isEmpty()) {
0437                     newLine = s.indexOf('\n') != -1;   // did we just get a newline?
0438                     if (!(options & KFind::FindBackwards)) {
0439                         //qCDebug(KHTML_LOG) << "StringPortion: " << index << "-" << index+s.length()-1 << " -> " << lastNode;
0440                         d->m_stringPortions.append(StringPortion(str.length(), lastNode));
0441                         str += s;
0442                     } else { // KFind itself can search backwards, so str must not be built backwards
0443                         for (QList<StringPortion>::Iterator it = d->m_stringPortions.begin();
0444                                 it != d->m_stringPortions.end();
0445                                 ++it) {
0446                             (*it).index += s.length();
0447                         }
0448                         d->m_stringPortions.prepend(StringPortion(0, lastNode));
0449                         str.prepend(s);
0450                     }
0451                 }
0452                 // Compare obj and end _after_ we processed the 'end' node itself
0453                 if (obj == end) {
0454                     obj = nullptr;
0455                 } else {
0456                     // Move on to next object (note: if we found a \n already, then obj (and lastNode)
0457                     // will point to the _next_ object, i.e. they are in advance.
0458                     do {
0459                         // We advance until the next RenderObject that has a NodeImpl as its element().
0460                         // Otherwise (if we keep the 'last node', and it has a '\n') we might be stuck
0461                         // on that object forever...
0462                         obj = (options & KFind::FindBackwards) ? obj->objectAbove() : obj->objectBelow();
0463                     } while (obj && (!obj->element() || obj->isInlineContinuation()));
0464                 }
0465                 if (obj) {
0466                     lastNode = obj->element();
0467                 } else {
0468                     lastNode = nullptr;
0469                 }
0470             } // end while
0471 
0472             if (!str.isEmpty()) {
0473                 d->m_find->setData(str, d->m_findPos);
0474             }
0475             d->m_findPos = -1; // not used during the findnext loops. Only during init.
0476             d->m_findNodePrevious = d->m_findNode;
0477             d->m_findNode = lastNode;
0478         }
0479         if (!d->m_find->needData() && !(res == KFind::Match)) { // happens if str was empty
0480             // Let KFind inspect the text fragment, and emit highlighted if a match is found
0481             res = d->m_find->find();
0482         }
0483     } // end while
0484 
0485     if (res == KFind::NoMatch) { // i.e. we're done
0486         // qCDebug(KHTML_LOG) << "No more matches.";
0487         if (!(options & KHTMLPart::FindNoPopups) && d->m_find->shouldRestart()) {
0488             // qCDebug(KHTML_LOG) << "Restarting";
0489             initFindNode(false, options & KFind::FindBackwards, false);
0490             d->m_find->resetCounts();
0491             findTextNext(reverse);
0492         } else { // really done
0493             // qCDebug(KHTML_LOG) << "Finishing";
0494             //delete d->m_find;
0495             //d->m_find = 0L;
0496             initFindNode(false, options & KFind::FindBackwards, false);
0497             d->m_find->resetCounts();
0498             d->m_part->clearSelection();
0499         }
0500         // qCDebug(KHTML_LOG) << "Dialog closed.";
0501     }
0502 
0503     if (m_findDialog != nullptr) {
0504         m_findDialog->setFoundMatch(res == KFind::Match);
0505         m_findDialog->setAtEnd(m_find->numMatches() < numMatchesOld);
0506     }
0507 
0508     return res == KFind::Match;
0509 }
0510 
0511 void KHTMLFind::slotHighlight(const QString & /*text*/, int index, int length)
0512 {
0513     //qCDebug(KHTML_LOG) << "slotHighlight index=" << index << " length=" << length;
0514     QList<StringPortion>::Iterator it = d->m_stringPortions.begin();
0515     const QList<StringPortion>::Iterator itEnd = d->m_stringPortions.end();
0516     QList<StringPortion>::Iterator prev = it;
0517     // We stop at the first portion whose index is 'greater than', and then use the previous one
0518     while (it != itEnd && (*it).index <= index) {
0519         prev = it;
0520         ++it;
0521     }
0522     Q_ASSERT(prev != itEnd);
0523     DOM::NodeImpl *node = (*prev).node;
0524     Q_ASSERT(node);
0525 
0526     Selection sel(RenderPosition(node, index - (*prev).index).position());
0527 
0528     khtml::RenderObject *obj = node->renderer();
0529     khtml::RenderTextArea *renderTextArea = nullptr;
0530     khtml::RenderLineEdit *renderLineEdit = nullptr;
0531 
0532     Q_ASSERT(obj);
0533     if (obj) {
0534         int x = 0, y = 0;
0535 
0536         if (obj->renderName() == QLatin1String("RenderTextArea")) {
0537             renderTextArea = static_cast<khtml::RenderTextArea *>(obj);
0538         }
0539         if (obj->renderName() == QLatin1String("RenderLineEdit")) {
0540             renderLineEdit = static_cast<khtml::RenderLineEdit *>(obj);
0541         }
0542         if (!renderLineEdit && !renderTextArea)
0543             //if (static_cast<khtml::RenderText *>(node->renderer())
0544             //    ->posOfChar(d->m_startOffset, x, y))
0545         {
0546             int dummy;
0547             static_cast<khtml::RenderText *>(node->renderer())
0548             ->caretPos(RenderPosition::fromDOMPosition(sel.start()).renderedOffset(), false, x, y, dummy, dummy);   // more precise than posOfChar
0549             //qCDebug(KHTML_LOG) << "topleft: " << x << "," << y;
0550             if (x != -1 || y != -1) {
0551                 int gox = m_part->view()->contentsX();
0552                 if (x + 50 > m_part->view()->contentsX() + m_part->view()->visibleWidth()) {
0553                     gox = x - m_part->view()->visibleWidth() + 50;
0554                 }
0555                 if (x - 10 < m_part->view()->contentsX()) {
0556                     gox = x - m_part->view()->visibleWidth() - 10;
0557                 }
0558                 if (gox < 0) {
0559                     gox = 0;
0560                 }
0561                 m_part->view()->setContentsPos(gox, y - 50);
0562             }
0563         }
0564     }
0565     // Now look for end node
0566     it = prev; // no need to start from beginning again
0567     while (it != itEnd && (*it).index < index + length) {
0568         prev = it;
0569         ++it;
0570     }
0571     Q_ASSERT(prev != itEnd);
0572 
0573     sel.moveTo(sel.start(), RenderPosition((*prev).node, index + length - (*prev).index).position());
0574 
0575 #if 0
0576     // qCDebug(KHTML_LOG) << "slotHighlight: " << d->m_selectionStart.handle() << "," << d->m_startOffset << " - " <<
0577     d->m_selectionEnd.handle() << "," << d->m_endOffset;
0578     it = d->m_stringPortions.begin();
0579     for (; it != d->m_stringPortions.end(); ++it)
0580         // qCDebug(KHTML_LOG) << "  StringPortion: from index=" << (*it).index << " -> node=" << (*it).node;
0581 #endif
0582         if (renderTextArea) {
0583             renderTextArea->highLightWord(length, sel.end().offset() - length);
0584         } else if (renderLineEdit) {
0585             renderLineEdit->highLightWord(length, sel.end().offset() - length);
0586         } else {
0587             m_part->setCaret(sel);
0588 //    d->m_doc->updateSelection();
0589             if (sel.end().node()->renderer()) {
0590                 int x, y, height, dummy;
0591                 static_cast<khtml::RenderText *>(sel.end().node()->renderer())
0592                 ->caretPos(RenderPosition::fromDOMPosition(sel.end()).renderedOffset(), false, x, y, dummy, height);   // more precise than posOfChar
0593                 //qCDebug(KHTML_LOG) << "bottomright: " << x << "," << y+height;
0594             }
0595         }
0596     m_part->emitSelectionChanged();
0597 
0598 }
0599 
0600 void KHTMLFind::slotSelectionChanged()
0601 {
0602     if (d->m_findDialog) {
0603         d->m_findDialog->setHasSelection(m_part->hasSelection());
0604     }
0605 }
0606 
0607 void KHTMLFind::slotSearchChanged()
0608 {
0609     createNewKFind(m_findDialog->pattern(), m_findDialog->options(), m_findDialog, nullptr);
0610     findTextNext();
0611 }
0612 
0613 void KHTMLFind::slotFindNext()
0614 {
0615     findTextNext();
0616 }
0617 
0618 void KHTMLFind::slotFindPrevious()
0619 {
0620     findTextNext(true);    // find backwards
0621 }
0622 
0623 #include "moc_khtmlfind_p.cpp"