KHtml

khtmlfind.cpp
1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 2008 Bernhard Beschow <bbeschow cs tu berlin de>
4  * (C) 2009 Germain Garand <[email protected]>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "khtmlfind_p.h"
23 
24 #include "khtml_part.h"
25 #include "khtmlviewbar.h"
26 #include "khtmlfindbar.h"
27 
28 #include "dom/html_document.h"
29 #include "html/html_documentimpl.h"
30 #include "rendering/render_text.h"
31 #include "rendering/render_replaced.h"
32 #include "xml/dom_selection.h"
33 
34 #include "khtmlview.h"
35 
36 #include <QClipboard>
37 
38 #include "rendering/render_form.h"
39 
40 #define d this
41 
42 using khtml::RenderPosition;
43 
44 using namespace DOM;
45 
46 KHTMLFind::KHTMLFind(KHTMLPart *part, KHTMLFind *parent) :
47  m_part(part),
48  m_find(nullptr),
49  m_parent(parent),
50  m_findDialog(nullptr)
51 {
52  connect(part, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
53 }
54 
55 KHTMLFind::~KHTMLFind()
56 {
57  d->m_find = nullptr; // deleted by its parent, the view.
58 }
59 
60 void KHTMLFind::findTextBegin()
61 {
62  d->m_findPos = -1;
63  d->m_findNode = nullptr;
64  d->m_findPosEnd = -1;
65  d->m_findNodeEnd = nullptr;
66  d->m_findPosStart = -1;
67  d->m_findNodeStart = nullptr;
68  d->m_findNodePrevious = nullptr;
69  delete d->m_find;
70  d->m_find = nullptr;
71 }
72 
73 bool KHTMLFind::initFindNode(bool selection, bool reverse, bool fromCursor)
74 {
75  if (m_part->document().isNull()) {
76  return false;
77  }
78 
79  DOM::NodeImpl *firstNode = nullptr;
80  if (m_part->document().isHTMLDocument()) {
81  firstNode = m_part->htmlDocument().body().handle();
82  } else {
83  firstNode = m_part->document().handle();
84  }
85 
86  if (!firstNode) {
87  //qCDebug(KHTML_LOG) << "no first node (body or doc) -> return false";
88  return false;
89  }
90  if (selection && m_part->hasSelection()) {
91  //qCDebug(KHTML_LOG) << "using selection";
92  const Selection &sel = m_part->caret();
93  if (!fromCursor) {
94  d->m_findNode = reverse ? sel.end().node() : sel.start().node();
95  d->m_findPos = reverse ? sel.end().offset() : sel.start().offset();
96  }
97  d->m_findNodeEnd = reverse ? sel.start().node() : sel.end().node();
98  d->m_findPosEnd = reverse ? sel.start().offset() : sel.end().offset();
99  d->m_findNodeStart = !reverse ? sel.start().node() : sel.end().node();
100  d->m_findPosStart = !reverse ? sel.start().offset() : sel.end().offset();
101  d->m_findNodePrevious = d->m_findNodeStart;
102  } else { // whole document
103  //qCDebug(KHTML_LOG) << "whole doc";
104  if (!fromCursor) {
105  d->m_findNode = firstNode;
106  d->m_findPos = reverse ? -1 : 0;
107  }
108  d->m_findNodeEnd = reverse ? firstNode : nullptr;
109  d->m_findPosEnd = reverse ? 0 : -1;
110  d->m_findNodeStart = !reverse ? firstNode : nullptr;
111  d->m_findPosStart = !reverse ? 0 : -1;
112  d->m_findNodePrevious = d->m_findNodeStart;
113  if (reverse) {
114  // Need to find out the really last object, to start from it
115  khtml::RenderObject *obj = d->m_findNode ? d->m_findNode->renderer() : nullptr;
116  if (obj) {
117  // find the last object in the render tree
118  while (obj->lastChild()) {
119  obj = obj->lastChild();
120  }
121  // now get the last object with a NodeImpl associated
122  while (!obj->element() && obj->objectAbove()) {
123  obj = obj->objectAbove();
124  }
125  d->m_findNode = obj->element();
126  }
127  }
128  }
129  return true;
130 }
131 
132 void KHTMLFind::deactivate()
133 {
134  // qCDebug(KHTML_LOG);
135  d->m_lastFindState.options = d->m_findDialog->options();
136  d->m_lastFindState.history = d->m_findDialog->findHistory();
137  if (!m_parent) {
138  d->m_findDialog->hide();
139  d->m_findDialog->disconnect();
140  d->m_findDialog->deleteLater();
141  }
142  d->m_findDialog = nullptr;
143 
144  // if the selection is limited to a single link, that link gets focus
145  const DOM::Selection sel = m_part->caret();
146  if (sel.start().node() == sel.end().node()) {
147  bool isLink = false;
148 
149  // checks whether the node has a <A> parent
150  DOM::NodeImpl *parent = sel.start().node();
151  while (parent) {
152  if (parent->nodeType() == Node::ELEMENT_NODE && parent->id() == ID_A) {
153  isLink = true;
154  break;
155  }
156  parent = parent->parentNode();
157  }
158 
159  if (isLink == true) {
160  static_cast<DOM::DocumentImpl *>(m_part->document().handle())->setFocusNode(parent);
161  }
162  }
163 }
164 
165 void KHTMLFind::slotFindDestroyed()
166 {
167  d->m_find = nullptr;
168 }
169 
170 void KHTMLFind::activate()
171 {
172  // First do some init to make sure we can search in this frame
173  if (m_part->document().isNull()) {
174  return;
175  }
176 
177  // Raise if already opened
178  if (d->m_findDialog && !m_parent) {
179  m_part->pBottomViewBar()->showBarWidget(d->m_findDialog);
180  return;
181  }
182 
183  // The lineedit of the dialog would make khtml lose its selection, otherwise
184 #ifndef QT_NO_CLIPBOARD
185  disconnect(qApp->clipboard(), SIGNAL(selectionChanged()), m_part, SLOT(slotClearSelection()));
186 #endif
187 
188  if (m_parent) {
189  d->m_findDialog = m_parent->findBar();
190  } else {
191  // Now show the dialog in which the user can choose options.
192  d->m_findDialog = new KHTMLFindBar(m_part->widget());
193  d->m_findDialog->setHasSelection(m_part->hasSelection());
194  d->m_findDialog->setHasCursor(d->m_findNode != nullptr);
195 #if 0
196  if (d->m_findNode) { // has a cursor -> default to 'FromCursor'
197  d->m_lastFindState.options |= KFind::FromCursor;
198  }
199 #endif
200 
201  // TODO? optionsDialog.setPattern( d->m_lastFindState.text );
202  d->m_findDialog->setFindHistory(d->m_lastFindState.history);
203  d->m_findDialog->setOptions(d->m_lastFindState.options);
204  d->m_findDialog->setFocus();
205 
206  d->m_lastFindState.options = -1; // force update in findTextNext
207  d->m_lastFindState.last_dir = -1;
208 
209  m_part->pBottomViewBar()->addBarWidget(d->m_findDialog);
210  m_part->pBottomViewBar()->showBarWidget(d->m_findDialog);
211  connect(d->m_findDialog, SIGNAL(searchChanged()), this, SLOT(slotSearchChanged()));
212  connect(d->m_findDialog, SIGNAL(findNextClicked()), this, SLOT(slotFindNext()));
213  connect(d->m_findDialog, SIGNAL(findPreviousClicked()), this, SLOT(slotFindPrevious()));
214  connect(d->m_findDialog, SIGNAL(hideMe()), this, SLOT(deactivate()));
215  }
216 #ifndef QT_NO_CLIPBOARD
217  connect(qApp->clipboard(), SIGNAL(selectionChanged()), m_part, SLOT(slotClearSelection()));
218 #endif
219  if (m_findDialog) {
220  createNewKFind(m_findDialog->pattern(), 0 /*options*/, m_findDialog, nullptr);
221  } else if (m_parent && m_parent->find()) {
222  createNewKFind(m_parent->find()->pattern(), m_parent->find()->options(), static_cast<QWidget *>(m_parent->find()->parent()), nullptr);
223  }
224 }
225 
226 // ### this crawling through the render tree sucks. There should be another way to
227 // do that.
228 static inline KHTMLPart *innerPart(khtml::RenderObject *ro)
229 {
230  if (!ro || !ro->isWidget() || ro->isFormElement()) {
231  return nullptr;
232  }
233  KHTMLView *v = qobject_cast<KHTMLView *>(static_cast<khtml::RenderWidget *>(ro)->widget());
234  return v ? v->part() : nullptr;
235 }
236 static inline KHTMLPart *innerPartFromNode(DOM::NodeImpl *node)
237 {
238  return (node && node->renderer() ? innerPart(node->renderer()) : nullptr);
239 }
240 
241 void KHTMLFind::createNewKFind(const QString &str, long options, QWidget *parent, KFindDialog *findDialog)
242 {
243  // First do some init to make sure we can search in this frame
244  if (m_part->document().isNull()) {
245  return;
246  }
247 
248  if (m_findNode) {
249  if (KHTMLPart *p = innerPartFromNode(m_findNode)) {
250  p->clearSelection();
251  p->findTextBegin();
252  }
253  }
254 
255  // Create the KFind object
256  delete d->m_find;
257  d->m_find = new KFind(str, options, parent, findDialog);
258  d->m_find->closeFindNextDialog(); // we use KFindDialog non-modal, so we don't want other dlg popping up
259  connect(d->m_find, SIGNAL(highlight(QString,int,int)),
260  this, SLOT(slotHighlight(QString,int,int)));
261  connect(d->m_find, SIGNAL(destroyed()),
262  this, SLOT(slotFindDestroyed()));
263  //connect(d->m_find, SIGNAL(findNext()),
264  // this, SLOT(slotFindNext()) );
265 
266  if (!findDialog) {
267  d->m_lastFindState.options = options;
268  initFindNode(options & KFind::SelectedText,
269  options & KFind::FindBackwards,
270  options & KFind::FromCursor);
271  }
272 }
273 
274 bool KHTMLFind::findTextNext(bool reverse)
275 {
276  if (!d->m_find) {
277  // We didn't show the find dialog yet, let's do it then (#49442)
278  activate();
279 
280  // FIXME Ugly hack: activate() may not create KFind object, so check whether it was created
281  if (!d->m_find) {
282  return false;
283  }
284 
285  // It also means the user is trying to match a previous pattern, so try and
286  // restore the last saved pattern.
287  if (!m_parent && (!d->m_findDialog || !d->m_findDialog->restoreLastPatternFromHistory())) {
288  return false;
289  }
290  }
291 
292  long options = 0;
293  if (d->m_findDialog) { // 0 when we close the dialog
294  // there is a search dialog
295  // make sure pattern from search dialog is used
296  // (### in fact pattern changes should always trigger a reconstruction of the KFind object cf. slotSearchChanged
297  // - so make this an assert)
298  if ((d->m_find->pattern() != d->m_findDialog->pattern())) {
299  d->m_find->setPattern(d->m_findDialog->pattern());
300  d->m_find->resetCounts();
301  }
302 
303  // make sure options from search dialog are used
304  options = d->m_findDialog->options();
305  if (d->m_lastFindState.options != options) {
306  d->m_find->setOptions(options);
307 
308  if (options & KFind::SelectedText) { //#### FIXME find in selection for frames!
309  Q_ASSERT(m_part->hasSelection());
310  }
311 
312  long difference = d->m_lastFindState.options ^ options;
313  if (difference & (KFind::SelectedText | KFind::FromCursor)) {
314  // Important options changed -> reset search range
315  (void) initFindNode(options & KFind::SelectedText,
316  options & KFind::FindBackwards,
317  options & KFind::FromCursor);
318  }
319  d->m_lastFindState.options = options;
320  }
321  } else {
322  // no dialog
323  options = d->m_lastFindState.options;
324  }
325 
326  // only adopt options for search direction manually
327  if (reverse) {
328  options = options ^ KFind::FindBackwards;
329  }
330 
331  // make sure our options are used by KFind
332  if (d->m_find->options() != options) {
333  d->m_find->setOptions(options);
334  }
335 
336  // Changing find direction. Start and end nodes must be switched.
337  // Additionally since d->m_findNode points after the last node
338  // that was searched, it needs to be "after" it in the opposite direction.
339  if (d->m_lastFindState.last_dir != -1
340  && bool(d->m_lastFindState.last_dir) != bool(options & KFind::FindBackwards)) {
341  qSwap(d->m_findNodeEnd, d->m_findNodeStart);
342  qSwap(d->m_findPosEnd, d->m_findPosStart);
343  qSwap(d->m_findNode, d->m_findNodePrevious);
344 
345  // d->m_findNode now point at the end of the last searched line - advance one node
346  khtml::RenderObject *obj = d->m_findNode ? d->m_findNode->renderer() : nullptr;
347  khtml::RenderObject *end = d->m_findNodeEnd ? d->m_findNodeEnd->renderer() : nullptr;
348  if (obj == end) {
349  obj = nullptr;
350  } else if (obj) {
351  do {
352  obj = (options & KFind::FindBackwards) ? obj->objectAbove() : obj->objectBelow();
353  } while (obj && (!obj->element() || obj->isInlineContinuation()));
354  }
355  if (obj) {
356  d->m_findNode = obj->element();
357  } else {
358  // already at end, start again
359  (void) initFindNode(options & KFind::SelectedText,
360  options & KFind::FindBackwards,
361  options & KFind::FromCursor);
362  }
363  }
364  d->m_lastFindState.last_dir = (options & KFind::FindBackwards) ? 1 : 0;
365 
366  int numMatchesOld = m_find->numMatches();
367  KFind::Result res = KFind::NoMatch;
368  khtml::RenderObject *obj = d->m_findNode ? d->m_findNode->renderer() : nullptr;
369  khtml::RenderObject *end = d->m_findNodeEnd ? d->m_findNodeEnd->renderer() : nullptr;
370  //qCDebug(KHTML_LOG) << "obj=" << obj << " end=" << end;
371  while (res == KFind::NoMatch) {
372  if (d->m_find->needData()) {
373  if (!obj) {
374  //qCDebug(KHTML_LOG) << "obj=0 -> done";
375  break; // we're done
376  }
377  //qCDebug(KHTML_LOG) << " gathering data";
378  // First make up the QString for the current 'line' (i.e. up to \n)
379  // We also want to remember the DOMNode for every portion of the string.
380  // We store this in an index->node list.
381 
382  d->m_stringPortions.clear();
383  bool newLine = false;
384  QString str;
385  DOM::NodeImpl *lastNode = d->m_findNode;
386  while (obj && !newLine) {
387  // Grab text from render object
388  QString s;
389  if (obj->renderName() == QLatin1String("RenderTextArea")) {
390  s = static_cast<khtml::RenderTextArea *>(obj)->text();
391  s = s.replace(0xa0, ' ');
392  } else if (obj->renderName() == QLatin1String("RenderLineEdit")) {
393  khtml::RenderLineEdit *parentLine = static_cast<khtml::RenderLineEdit *>(obj);
394  if (parentLine->widget()->echoMode() == QLineEdit::Normal) {
395  s = parentLine->widget()->text();
396  }
397  s = s.replace(0xa0, ' ');
398  } else if (obj->isText()) {
399  bool isLink = false;
400 
401  // checks whether the node has a <A> parent
402  if (options & KHTMLPart::FindLinksOnly) {
403  DOM::NodeImpl *parent = obj->element();
404  while (parent) {
405  if (parent->nodeType() == Node::ELEMENT_NODE && parent->id() == ID_A) {
406  isLink = true;
407  break;
408  }
409  parent = parent->parentNode();
410  }
411  } else {
412  isLink = true;
413  }
414 
415  if (isLink) {
416  s = static_cast<khtml::RenderText *>(obj)->data().string();
417  s = s.replace(0xa0, ' ');
418  }
419  } else if (KHTMLPart *p = innerPart(obj)) {
420  if (p->pFindTextNextInThisFrame(reverse)) {
421  numMatchesOld++;
422  res = KFind::Match;
423  lastNode = obj->element();
424  break;
425  }
426 
427  } else if (obj->isBR()) {
428  s = '\n';
429  } else if (!obj->isInline() && !str.isEmpty()) {
430  s = '\n';
431  }
432 
433  if (lastNode == d->m_findNodeEnd) {
434  s.truncate(d->m_findPosEnd);
435  }
436  if (!s.isEmpty()) {
437  newLine = s.indexOf('\n') != -1; // did we just get a newline?
438  if (!(options & KFind::FindBackwards)) {
439  //qCDebug(KHTML_LOG) << "StringPortion: " << index << "-" << index+s.length()-1 << " -> " << lastNode;
440  d->m_stringPortions.append(StringPortion(str.length(), lastNode));
441  str += s;
442  } else { // KFind itself can search backwards, so str must not be built backwards
443  for (QList<StringPortion>::Iterator it = d->m_stringPortions.begin();
444  it != d->m_stringPortions.end();
445  ++it) {
446  (*it).index += s.length();
447  }
448  d->m_stringPortions.prepend(StringPortion(0, lastNode));
449  str.prepend(s);
450  }
451  }
452  // Compare obj and end _after_ we processed the 'end' node itself
453  if (obj == end) {
454  obj = nullptr;
455  } else {
456  // Move on to next object (note: if we found a \n already, then obj (and lastNode)
457  // will point to the _next_ object, i.e. they are in advance.
458  do {
459  // We advance until the next RenderObject that has a NodeImpl as its element().
460  // Otherwise (if we keep the 'last node', and it has a '\n') we might be stuck
461  // on that object forever...
462  obj = (options & KFind::FindBackwards) ? obj->objectAbove() : obj->objectBelow();
463  } while (obj && (!obj->element() || obj->isInlineContinuation()));
464  }
465  if (obj) {
466  lastNode = obj->element();
467  } else {
468  lastNode = nullptr;
469  }
470  } // end while
471 
472  if (!str.isEmpty()) {
473  d->m_find->setData(str, d->m_findPos);
474  }
475  d->m_findPos = -1; // not used during the findnext loops. Only during init.
476  d->m_findNodePrevious = d->m_findNode;
477  d->m_findNode = lastNode;
478  }
479  if (!d->m_find->needData() && !(res == KFind::Match)) { // happens if str was empty
480  // Let KFind inspect the text fragment, and emit highlighted if a match is found
481  res = d->m_find->find();
482  }
483  } // end while
484 
485  if (res == KFind::NoMatch) { // i.e. we're done
486  // qCDebug(KHTML_LOG) << "No more matches.";
487  if (!(options & KHTMLPart::FindNoPopups) && d->m_find->shouldRestart()) {
488  // qCDebug(KHTML_LOG) << "Restarting";
489  initFindNode(false, options & KFind::FindBackwards, false);
490  d->m_find->resetCounts();
491  findTextNext(reverse);
492  } else { // really done
493  // qCDebug(KHTML_LOG) << "Finishing";
494  //delete d->m_find;
495  //d->m_find = 0L;
496  initFindNode(false, options & KFind::FindBackwards, false);
497  d->m_find->resetCounts();
498  d->m_part->clearSelection();
499  }
500  // qCDebug(KHTML_LOG) << "Dialog closed.";
501  }
502 
503  if (m_findDialog != nullptr) {
504  m_findDialog->setFoundMatch(res == KFind::Match);
505  m_findDialog->setAtEnd(m_find->numMatches() < numMatchesOld);
506  }
507 
508  return res == KFind::Match;
509 }
510 
511 void KHTMLFind::slotHighlight(const QString & /*text*/, int index, int length)
512 {
513  //qCDebug(KHTML_LOG) << "slotHighlight index=" << index << " length=" << length;
514  QList<StringPortion>::Iterator it = d->m_stringPortions.begin();
515  const QList<StringPortion>::Iterator itEnd = d->m_stringPortions.end();
517  // We stop at the first portion whose index is 'greater than', and then use the previous one
518  while (it != itEnd && (*it).index <= index) {
519  prev = it;
520  ++it;
521  }
522  Q_ASSERT(prev != itEnd);
523  DOM::NodeImpl *node = (*prev).node;
524  Q_ASSERT(node);
525 
526  Selection sel(RenderPosition(node, index - (*prev).index).position());
527 
528  khtml::RenderObject *obj = node->renderer();
529  khtml::RenderTextArea *renderTextArea = nullptr;
530  khtml::RenderLineEdit *renderLineEdit = nullptr;
531 
532  Q_ASSERT(obj);
533  if (obj) {
534  int x = 0, y = 0;
535 
536  if (obj->renderName() == QLatin1String("RenderTextArea")) {
537  renderTextArea = static_cast<khtml::RenderTextArea *>(obj);
538  }
539  if (obj->renderName() == QLatin1String("RenderLineEdit")) {
540  renderLineEdit = static_cast<khtml::RenderLineEdit *>(obj);
541  }
542  if (!renderLineEdit && !renderTextArea)
543  //if (static_cast<khtml::RenderText *>(node->renderer())
544  // ->posOfChar(d->m_startOffset, x, y))
545  {
546  int dummy;
547  static_cast<khtml::RenderText *>(node->renderer())
548  ->caretPos(RenderPosition::fromDOMPosition(sel.start()).renderedOffset(), false, x, y, dummy, dummy); // more precise than posOfChar
549  //qCDebug(KHTML_LOG) << "topleft: " << x << "," << y;
550  if (x != -1 || y != -1) {
551  int gox = m_part->view()->contentsX();
552  if (x + 50 > m_part->view()->contentsX() + m_part->view()->visibleWidth()) {
553  gox = x - m_part->view()->visibleWidth() + 50;
554  }
555  if (x - 10 < m_part->view()->contentsX()) {
556  gox = x - m_part->view()->visibleWidth() - 10;
557  }
558  if (gox < 0) {
559  gox = 0;
560  }
561  m_part->view()->setContentsPos(gox, y - 50);
562  }
563  }
564  }
565  // Now look for end node
566  it = prev; // no need to start from beginning again
567  while (it != itEnd && (*it).index < index + length) {
568  prev = it;
569  ++it;
570  }
571  Q_ASSERT(prev != itEnd);
572 
573  sel.moveTo(sel.start(), RenderPosition((*prev).node, index + length - (*prev).index).position());
574 
575 #if 0
576  // qCDebug(KHTML_LOG) << "slotHighlight: " << d->m_selectionStart.handle() << "," << d->m_startOffset << " - " <<
577  d->m_selectionEnd.handle() << "," << d->m_endOffset;
578  it = d->m_stringPortions.begin();
579  for (; it != d->m_stringPortions.end(); ++it)
580  // qCDebug(KHTML_LOG) << " StringPortion: from index=" << (*it).index << " -> node=" << (*it).node;
581 #endif
582  if (renderTextArea) {
583  renderTextArea->highLightWord(length, sel.end().offset() - length);
584  } else if (renderLineEdit) {
585  renderLineEdit->highLightWord(length, sel.end().offset() - length);
586  } else {
587  m_part->setCaret(sel);
588 // d->m_doc->updateSelection();
589  if (sel.end().node()->renderer()) {
590  int x, y, height, dummy;
591  static_cast<khtml::RenderText *>(sel.end().node()->renderer())
592  ->caretPos(RenderPosition::fromDOMPosition(sel.end()).renderedOffset(), false, x, y, dummy, height); // more precise than posOfChar
593  //qCDebug(KHTML_LOG) << "bottomright: " << x << "," << y+height;
594  }
595  }
596  m_part->emitSelectionChanged();
597 
598 }
599 
600 void KHTMLFind::slotSelectionChanged()
601 {
602  if (d->m_findDialog) {
603  d->m_findDialog->setHasSelection(m_part->hasSelection());
604  }
605 }
606 
607 void KHTMLFind::slotSearchChanged()
608 {
609  createNewKFind(m_findDialog->pattern(), m_findDialog->options(), m_findDialog, nullptr);
610  findTextNext();
611 }
612 
613 void KHTMLFind::slotFindNext()
614 {
615  findTextNext();
616 }
617 
618 void KHTMLFind::slotFindPrevious()
619 {
620  findTextNext(true); // find backwards
621 }
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
void truncate(int position)
QString & prepend(QChar ch)
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
Renders and displays HTML in a QScrollArea.
Definition: khtmlview.h:97
KHTMLPart * part() const
Returns a pointer to the KHTMLPart that is rendering the page.
Definition: khtmlview.h:139
bool isEmpty() const const
QString & replace(int position, int n, QChar after)
This library provides a full-featured HTML parser and widget.
const QList< QKeySequence > & end()
Base Class for all rendering tree objects.
int length() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:04 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.