KHtml

dom_position.cpp
1 /*
2  * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "dom_position.h"
27 
28 #include "misc/helper.h"
29 #include "rendering/render_block.h"
30 #include "rendering/render_line.h"
31 #include "rendering/render_object.h"
32 #include "rendering/render_style.h"
33 #include "rendering/render_text.h"
34 #include "xml/dom_positioniterator.h"
35 #include "xml/dom_elementimpl.h"
36 #include "xml/dom_docimpl.h"
37 #include "xml/dom_nodeimpl.h"
38 #include "html/html_documentimpl.h"
39 #include "rendering/render_position.h"
40 
41 #include "khtml_part.h"
42 
43 #include <qstring.h>
44 
45 #define DEBUG_CARET
46 
47 // FIXME shouldn't use any rendereing related here except for RenderPosition
48 using khtml::InlineBox;
49 using khtml::InlineFlowBox;
50 using khtml::InlineTextBox;
51 using khtml::RenderBlock;
53 using khtml::RenderText;
54 using khtml::RootInlineBox;
55 using khtml::RenderPosition;
56 
57 namespace DOM
58 {
59 
60 static NodeImpl *nextRenderedEditable(NodeImpl *node)
61 {
62  while (1) {
63  node = node->nextEditable();
64  if (!node) {
65  return nullptr;
66  }
67  if (!node->renderer()) {
68  continue;
69  }
70  if (node->renderer()->inlineBox(0)) {
71  return node;
72  }
73  }
74  return nullptr;
75 }
76 
77 static NodeImpl *previousRenderedEditable(NodeImpl *node)
78 {
79  while (1) {
80  node = node->previousEditable();
81  if (!node) {
82  return nullptr;
83  }
84  if (!node->renderer()) {
85  continue;
86  }
87  if (node->renderer()->inlineBox(0)) {
88  return node;
89  }
90  }
91  return nullptr;
92 }
93 
94 /*static*/ NodeImpl *rootNavigableElement(NodeImpl *node)
95 {
96  DocumentImpl *doc = node->document();
97  if (doc && doc->part()->isCaretMode()) {
98  if (doc->isHTMLDocument()) {
99  return static_cast<HTMLDocumentImpl *>(doc)->body();
100  } else {
101  return doc->documentElement();
102  }
103  }
104 
105  return node->rootEditableElement();
106 }
107 
108 /*inline*/ /*static*/ bool inSameRootNavigableElement(NodeImpl *n1, NodeImpl *n2)
109 {
110  return n1 && n2 && rootNavigableElement(n1) == rootNavigableElement(n2);
111 }
112 
113 static void printSubTree(NodeImpl *node, int indent = 0)
114 {
115  QString temp;
116  temp.fill(' ', indent);
117  // qCDebug(KHTML_LOG) << temp << node << node->nodeName() << node->renderer()
118  // << (node->renderer() ? node->renderer()->renderName() : "")
119  // << (node->isTextNode() ? static_cast<TextImpl*>(node)->toString() : "");
120  for (NodeImpl *subNode = node->firstChild(); subNode; subNode = subNode->nextSibling()) {
121  printSubTree(subNode, indent + 1);
122  }
123 }
124 
125 void printEnclosingBlockTree(NodeImpl *node)
126 {
127  if (!node || !node->enclosingBlockFlowElement()) {
128  // qCDebug(KHTML_LOG) << "[null node]" << node;
129  return;
130  }
131  printSubTree(node->enclosingBlockFlowElement());
132 }
133 
134 void printRootEditableTree(NodeImpl *node)
135 {
136  if (!node || !node->rootEditableElement()) {
137  // qCDebug(KHTML_LOG) << "[null node]" << node;
138  return;
139  }
140  printSubTree(node->rootEditableElement());
141 }
142 
143 Position::Position(NodeImpl *node, long offset)
144  : m_node(nullptr), m_offset(offset)
145 {
146  if (node) {
147  m_node = node;
148  m_node->ref();
149  }
150 }
151 
152 Position::Position(const Position &o)
153  : m_node(nullptr), m_offset(o.offset())
154 {
155  if (o.node()) {
156  m_node = o.node();
157  m_node->ref();
158  }
159 }
160 
161 Position::~Position()
162 {
163  if (m_node) {
164  m_node->deref();
165  }
166 }
167 
168 Position &Position::operator=(const Position &o)
169 {
170  if (m_node) {
171  m_node->deref();
172  }
173  m_node = o.node();
174  if (m_node) {
175  m_node->ref();
176  }
177 
178  m_offset = o.offset();
179 
180  return *this;
181 }
182 
183 ElementImpl *Position::element() const
184 {
185  if (isEmpty()) {
186  return nullptr;
187  }
188 
189  NodeImpl *n = node();
190  for (; n && !n->isElementNode(); n = n->parentNode()) {}; //loop
191 
192  return static_cast<ElementImpl *>(n);
193 }
194 
195 long Position::renderedOffset() const
196 {
197  return RenderPosition::fromDOMPosition(*this).renderedOffset();
198 }
199 
200 Position Position::equivalentLeafPosition() const
201 {
202 #ifdef DEBUG_CARET
203  // qCDebug(KHTML_LOG) << *this;
204 #endif
205  if (isEmpty()) {
206  return Position();
207  }
208 
209  if (!node()->renderer() || !node()->renderer()->firstChild()) {
210  return *this;
211  }
212 
213 #ifdef DEBUG_CARET
214  // qCDebug(KHTML_LOG) << "[Position]" << this;
215 #endif
216  NodeImpl *n = node();
217  int count = 0;
218  while (1) {
219  n = n->nextLeafNode();
220  if (!n || !n->inSameContainingBlockFlowElement(node())) {
221  return *this;
222  }
223 #ifdef DEBUG_CARET
224  // qCDebug(KHTML_LOG) << "[iterate]" << n << count << n->maxOffset();
225 #endif
226  if (count + n->maxOffset() >= offset()) {
227  count = offset() - count;
228  break;
229  }
230  count += n->maxOffset();
231  }
232  return Position(n, count);
233 }
234 
235 Position Position::previousRenderedEditablePosition() const
236 {
237 #ifdef DEBUG_CARET
238  // qCDebug(KHTML_LOG) << *this;
239 #endif
240  if (isEmpty()) {
241  return Position();
242  }
243 
244  if ((node()->document()->part()->isCaretMode() || node()->isContentEditable()) && node()->hasChildNodes() == false && inRenderedContent()) {
245  return *this;
246  }
247 
248  NodeImpl *n = node();
249  while (1) {
250  n = n->previousEditable();
251  if (!n) {
252  return Position();
253  }
254  if (n->renderer() && n->renderer()->style()->visibility() == khtml::VISIBLE) {
255  break;
256  }
257  }
258 
259  return Position(n, 0);
260 }
261 
262 Position Position::nextRenderedEditablePosition() const
263 {
264 #ifdef DEBUG_CARET
265  // qCDebug(KHTML_LOG) << *this;
266 #endif
267  if (isEmpty()) {
268  return Position();
269  }
270 
271  if ((node()->document()->part()->isCaretMode() || node()->isContentEditable()) && node()->hasChildNodes() == false && inRenderedContent()) {
272  return *this;
273  }
274 
275  NodeImpl *n = node();
276  while (1) {
277  n = n->nextEditable();
278  if (!n) {
279  return Position();
280  }
281  if (n->renderer() && n->renderer()->style()->visibility() == khtml::VISIBLE) {
282  break;
283  }
284  }
285 
286  return Position(n, 0);
287 }
288 
289 Position Position::previousCharacterPosition() const
290 {
291 #ifdef DEBUG_CARET
292  // qCDebug(KHTML_LOG) << *this;
293 #endif
294  if (isEmpty()) {
295  return Position();
296  }
297 
298  NodeImpl *fromRootNavigableElement = rootNavigableElement(node());
299 #ifdef DEBUG_CARET
300  // qCDebug(KHTML_LOG) << "RootElement" << fromRootNavigableElement;
301 #endif
302  PositionIterator it(*this);
303 
304  RenderPosition originalRPosition = RenderPosition::fromDOMPosition(*this);
305 
306  while (!it.atStart()) {
307  Position pos = it.previous();
308 #ifdef DEBUG_CARET
309  // qCDebug(KHTML_LOG) << "iterate" << pos;
310 #endif
311 
312  if (rootNavigableElement(pos.node()) != fromRootNavigableElement) {
313 #ifdef DEBUG_CARET
314  // qCDebug(KHTML_LOG) << "different root" << rootNavigableElement(pos.node());
315 #endif
316  return *this;
317  }
318  RenderPosition currentRPosition = RenderPosition::fromDOMPosition(pos);
319  if (RenderPosition::rendersInDifferentPosition(originalRPosition, currentRPosition)) {
320  return currentRPosition.position();
321  }
322  }
323 #ifdef DEBUG_CARET
324  // qCDebug(KHTML_LOG) << "no previous position";
325 #endif
326  return *this;
327 }
328 
329 Position Position::nextCharacterPosition() const
330 {
331 #ifdef DEBUG_CARET
332  // qCDebug(KHTML_LOG) << *this;
333 #endif
334  if (isEmpty()) {
335  return Position();
336  }
337 
338  NodeImpl *fromRootNavigableElement = rootNavigableElement(node());
339  PositionIterator it(*this);
340 
341  RenderPosition originalRPosition = RenderPosition::fromDOMPosition(*this);
342 
343  while (!it.atEnd()) {
344  Position pos = it.next();
345 
346  if (rootNavigableElement(pos.node()) != fromRootNavigableElement) {
347  return *this;
348  }
349  RenderPosition currentRPosition = RenderPosition::fromDOMPosition(pos);
350  if (RenderPosition::rendersInDifferentPosition(originalRPosition, currentRPosition)) {
351  return currentRPosition.position();
352  }
353  }
354 
355  return *this;
356 }
357 
358 Position Position::previousWordPosition() const
359 {
360  if (isEmpty()) {
361  return Position();
362  }
363 
364  Position pos = *this;
365  for (PositionIterator it(*this); !it.atStart(); it.previous()) {
366  if (it.current().node()->nodeType() == Node::TEXT_NODE || it.current().node()->nodeType() == Node::CDATA_SECTION_NODE) {
367  // use RenderPosition here
368  DOMString t = it.current().node()->nodeValue();
369  QChar *chars = t.unicode();
370  uint len = t.length();
371  int start, end;
372  khtml::findWordBoundary(chars, len, it.current().offset(), &start, &end);
373  pos = Position(it.current().node(), start);
374  } else {
375  pos = Position(it.current().node(), it.current().node()->caretMinOffset());
376  }
377  if (pos != *this) {
378  return pos;
379  }
380  it.setPosition(pos);
381  }
382 
383  return *this;
384 }
385 
386 Position Position::nextWordPosition() const
387 {
388  if (isEmpty()) {
389  return Position();
390  }
391 
392  Position pos = *this;
393  for (PositionIterator it(*this); !it.atEnd(); it.next()) {
394  if (it.current().node()->nodeType() == Node::TEXT_NODE || it.current().node()->nodeType() == Node::CDATA_SECTION_NODE) {
395  // use RenderPosition here
396  DOMString t = it.current().node()->nodeValue();
397  QChar *chars = t.unicode();
398  uint len = t.length();
399  int start, end;
400  khtml::findWordBoundary(chars, len, it.current().offset(), &start, &end);
401  pos = Position(it.current().node(), end);
402  } else {
403  pos = Position(it.current().node(), it.current().node()->caretMaxOffset());
404  }
405  if (pos != *this) {
406  return pos;
407  }
408  it.setPosition(pos);
409  }
410 
411  return *this;
412 }
413 
414 Position Position::previousLinePosition(int x) const
415 {
416  return RenderPosition::fromDOMPosition(*this).previousLinePosition(x).position();
417 }
418 
419 Position Position::nextLinePosition(int x) const
420 {
421  return RenderPosition::fromDOMPosition(*this).nextLinePosition(x).position();
422 }
423 
424 Position Position::equivalentUpstreamPosition() const
425 {
426 #ifdef DEBUG_CARET
427  // qCDebug(KHTML_LOG) << *this;
428 #endif
429  if (!node()) {
430  return Position();
431  }
432 
433  NodeImpl *block = node()->enclosingBlockFlowElement();
434 
435  PositionIterator it(*this);
436  for (; !it.atStart(); it.previous()) {
437 #ifdef DEBUG_CARET
438  // qCDebug(KHTML_LOG) << "[iterate]" << it.current();
439 #endif
440  NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement();
441  if (block != currentBlock) {
442  return it.next();
443  }
444 
445  RenderObject *renderer = it.current().node()->renderer();
446  if (!renderer) {
447  continue;
448  }
449 
450  if (renderer->style()->visibility() != khtml::VISIBLE) {
451  continue;
452  }
453 
454  if (renderer->isBlockFlow() || renderer->isReplaced() || renderer->isBR()) {
455  if (it.current().offset() >= renderer->caretMaxOffset()) {
456  return Position(it.current().node(), renderer->caretMaxOffset());
457  } else {
458  continue;
459  }
460  }
461 
462  if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) {
463  if (it.current().node() != node()) {
464  Position result(it.current().node(), renderer->caretMaxOffset());
465  if (rendersInDifferentPosition(result)) {
466  return it.next();
467  }
468  return result;
469  }
470 
471  if (it.current().offset() < 0) {
472  continue;
473  }
474  uint textOffset = it.current().offset();
475 
476  RenderText *textRenderer = static_cast<RenderText *>(renderer);
477  textOffset = textRenderer->convertToRenderedPosition(textOffset);
478  // textRenderer->firstTextBox()->parent()->printTree();
479  for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
480  if (textOffset > box->start() && textOffset <= box->start() + box->len()) {
481  return it.current();
482  }
483  }
484  }
485  }
486 
487  if (it.current().node()->enclosingBlockFlowElement() != block) {
488  return it.next();
489  }
490 
491  return it.current();
492 }
493 
494 Position Position::equivalentDownstreamPosition() const
495 {
496  // qCDebug(KHTML_LOG) << *this;
497  if (!node()) {
498  return Position();
499  }
500 
501  NodeImpl *block = node()->enclosingBlockFlowElement();
502 
503  PositionIterator it(*this);
504  for (; !it.atEnd(); it.next()) {
505  // qCDebug(KHTML_LOG) << "[iterate]" << it.current();
506  NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement();
507  if (block != currentBlock) {
508  return it.previous();
509  }
510 
511  RenderObject *renderer = it.current().node()->renderer();
512  if (!renderer) {
513  continue;
514  }
515 
516  if (renderer->style()->visibility() != khtml::VISIBLE) {
517  continue;
518  }
519 
520  if (renderer->isBlockFlow() || renderer->isReplaced() || renderer->isBR()) {
521  if (it.current().offset() <= renderer->caretMinOffset()) {
522  return Position(it.current().node(), renderer->caretMinOffset());
523  } else {
524  continue;
525  }
526  }
527 
528  if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) {
529  if (it.current().node() != node()) {
530  Position result(it.current().node(), renderer->caretMinOffset());
531  if (rendersInDifferentPosition(result)) {
532  return it.previous();
533  }
534  return result;
535  }
536 
537  if (it.current().offset() < 0) {
538  continue;
539  }
540  uint textOffset = it.current().offset();
541 
542  RenderText *textRenderer = static_cast<RenderText *>(renderer);
543  textOffset = textRenderer->convertToRenderedPosition(textOffset);
544  // textRenderer->firstTextBox()->parent()->printTree();
545  for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
546  if (textOffset >= box->start() && textOffset <= box->end()) {
547  return it.current();
548  }
549  }
550  }
551  }
552 
553  if (it.current().node()->enclosingBlockFlowElement() != block) {
554  return it.previous();
555  }
556 
557  return it.current();
558 }
559 
560 Position Position::equivalentRangeCompliantPosition() const
561 {
562  if (isEmpty()) {
563  return *this;
564  }
565 
566  if (!node()->parentNode()) {
567  return *this;
568  }
569 
570  RenderObject *renderer = node()->renderer();
571  if (!renderer) {
572  return *this;
573  }
574 
575  if (!renderer->isReplaced() && !renderer->isBR()) {
576  return *this;
577  }
578 
579  int o = 0;
580  const NodeImpl *n = node();
581  while ((n = n->previousSibling())) {
582  o++;
583  }
584 
585  return Position(node()->parentNode(), o + offset());
586 }
587 
588 Position Position::equivalentShallowPosition() const
589 {
590  if (isEmpty()) {
591  return *this;
592  }
593 
594  Position pos(*this);
595  while (pos.offset() == pos.node()->caretMinOffset() && pos.node()->parentNode() && pos.node() == pos.node()->parentNode()->firstChild()) {
596  pos = Position(pos.node()->parentNode(), 0);
597  }
598  return pos;
599 }
600 
601 bool Position::atStartOfContainingEditableBlock() const
602 {
603  return renderedOffset() == 0 && inFirstEditableInContainingEditableBlock();
604 }
605 
606 bool Position::atStartOfRootEditableElement() const
607 {
608  return renderedOffset() == 0 && inFirstEditableInRootEditableElement();
609 }
610 
611 bool Position::inRenderedContent() const
612 {
613  return RenderPosition::fromDOMPosition(*this).inRenderedContent();
614 }
615 
616 bool Position::inRenderedText() const
617 {
618  return RenderPosition::fromDOMPosition(*this).inRenderedContent() && node() && node()->renderer() && node()->renderer()->isText();
619 }
620 
621 bool Position::rendersOnSameLine(const Position &pos) const
622 {
623  if (isEmpty() || pos.isEmpty()) {
624  return false;
625  }
626 
627  if (*this == pos) {
628  return true;
629  }
630 
631  if (node()->enclosingBlockFlowElement() != pos.node()->enclosingBlockFlowElement()) {
632  return false;
633  }
634 
635  RenderPosition self = RenderPosition::fromDOMPosition(*this);
636  RenderPosition other = RenderPosition::fromDOMPosition(pos);
637  return RenderPosition::rendersOnSameLine(self, other);
638 }
639 
640 bool Position::rendersInDifferentPosition(const Position &pos) const
641 {
642  return RenderPosition::rendersInDifferentPosition(RenderPosition::fromDOMPosition(*this), RenderPosition::fromDOMPosition(pos));
643  /*// qCDebug(KHTML_LOG) << *this << pos;
644  if (isEmpty() || pos.isEmpty())
645  return false;
646 
647  RenderObject *renderer = node()->renderer();
648  if (!renderer)
649  return false;
650 
651  RenderObject *posRenderer = pos.node()->renderer();
652  if (!posRenderer)
653  return false;
654 
655  if (renderer->style()->visibility() != khtml::VISIBLE ||
656  posRenderer->style()->visibility() != khtml::VISIBLE)
657  return false;
658 
659  if (node() == pos.node()) {
660  if (node()->id() == ID_BR)
661  return false;
662 
663  if (offset() == pos.offset())
664  return false;
665 
666  if (!node()->isTextNode() && !pos.node()->isTextNode()) {
667  if (offset() != pos.offset())
668  return true;
669  }
670  }
671 
672  if (node()->id() == ID_BR && pos.inRenderedContent())
673  return true;
674 
675  if (pos.node()->id() == ID_BR && inRenderedContent())
676  return true;
677 
678  if (node()->enclosingBlockFlowElement() != pos.node()->enclosingBlockFlowElement())
679  return true;
680 
681  if (node()->isTextNode() && !inRenderedText())
682  return false;
683 
684  if (pos.node()->isTextNode() && !pos.inRenderedText())
685  return false;
686 
687  long thisRenderedOffset = renderedOffset();
688  long posRenderedOffset = pos.renderedOffset();
689 
690  if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
691  return false;
692 
693  // qCDebug(KHTML_LOG) << "onDifferentLine:" << (renderersOnDifferentLine(renderer, offset(), posRenderer, pos.offset()) ? "YES" : "NO");
694  // qCDebug(KHTML_LOG) << "renderer:" << renderer << "[" << (renderer ? renderer->inlineBox(offset()) : 0) << "]";
695  // qCDebug(KHTML_LOG) << "thisRenderedOffset:" << thisRenderedOffset;
696  // qCDebug(KHTML_LOG) << "posRenderer:" << posRenderer << "[" << (posRenderer ? posRenderer->inlineBox(offset()) : 0) << "]";
697  // qCDebug(KHTML_LOG) << "posRenderedOffset:"<< posRenderedOffset;
698  // qCDebug(KHTML_LOG) << "node min/max:"<< node()->caretMinOffset() << "/" << node()->caretMaxRenderedOffset();
699  // qCDebug(KHTML_LOG) << "pos node min/max:"<< pos.node()->caretMinOffset() << "/" << pos.node()->caretMaxRenderedOffset();
700  // qCDebug(KHTML_LOG) << "----------------------------------------------------------------------";
701 
702  InlineBox *b1 = renderer ? renderer->inlineBox(offset()) : 0;
703  InlineBox *b2 = posRenderer ? posRenderer->inlineBox(pos.offset()) : 0;
704 
705  if (!b1 || !b2) {
706  return false;
707  }
708 
709  if (b1->root() != b2->root()) {
710  return true;
711  }
712 
713  if (nextRenderedEditable(node()) == pos.node() &&
714  thisRenderedOffset == (long)node()->caretMaxRenderedOffset() && posRenderedOffset == 0) {
715  return false;
716  }
717 
718  if (previousRenderedEditable(node()) == pos.node() &&
719  thisRenderedOffset == 0 && posRenderedOffset == (long)pos.node()->caretMaxRenderedOffset()) {
720  return false;
721  }
722 
723  return true;*/
724 }
725 
726 bool Position::isFirstRenderedPositionOnLine() const
727 {
728  // return RenderPosition::fromDOMPosition(*this).isFirst*/
729  // qCDebug(KHTML_LOG) << *this;
730  if (isEmpty()) {
731  return false;
732  }
733 
734  RenderObject *renderer = node()->renderer();
735  if (!renderer) {
736  return false;
737  }
738  // qCDebug(KHTML_LOG) << "Renderer" << renderer << renderer->renderName();
739 
740  if (renderer->style()->visibility() != khtml::VISIBLE) {
741  return false;
742  }
743 
744  Position pos(node(), offset());
745  PositionIterator it(pos);
746  while (!it.atStart()) {
747  it.previous();
748  // qCDebug(KHTML_LOG) << "To previous" << it.current();
749  if (it.current().inRenderedContent()) {
750  return !rendersOnSameLine(it.current());
751  }
752  // return renderersOnDifferentLine(renderer, offset(), it.current().node()->renderer(), it.current().offset());
753  }
754 
755  return true;
756 }
757 
758 bool Position::isLastRenderedPositionOnLine() const
759 {
760  if (isEmpty()) {
761  return false;
762  }
763 
764  RenderObject *renderer = node()->renderer();
765  if (!renderer) {
766  return false;
767  }
768 
769  if (renderer->style()->visibility() != khtml::VISIBLE) {
770  return false;
771  }
772 
773  if (node()->id() == ID_BR) {
774  return true;
775  }
776 
777  Position pos(node(), offset());
778  PositionIterator it(pos);
779  while (!it.atEnd()) {
780  it.next();
781  if (it.current().inRenderedContent()) {
782  return !rendersOnSameLine(it.current());
783  }
784  // return renderersOnDifferentLine(renderer, offset(), it.current().node()->renderer(), it.current().offset());
785  }
786 
787  return true;
788 }
789 
790 bool Position::isLastRenderedPositionInEditableBlock() const
791 {
792  if (isEmpty()) {
793  return false;
794  }
795 
796  RenderObject *renderer = node()->renderer();
797  if (!renderer) {
798  return false;
799  }
800 
801  if (renderer->style()->visibility() != khtml::VISIBLE) {
802  return false;
803  }
804 
805  if (renderedOffset() != (long)node()->caretMaxRenderedOffset()) {
806  return false;
807  }
808 
809  Position pos(node(), offset());
810  PositionIterator it(pos);
811  while (!it.atEnd()) {
812  it.next();
813  if (!it.current().node()->inSameContainingBlockFlowElement(node())) {
814  return true;
815  }
816  if (it.current().inRenderedContent()) {
817  return false;
818  }
819  }
820  return true;
821 }
822 
823 bool Position::inFirstEditableInRootEditableElement() const
824 {
825  if (isEmpty() || !inRenderedContent()) {
826  return false;
827  }
828 
829  PositionIterator it(*this);
830  while (!it.atStart()) {
831  if (it.previous().inRenderedContent()) {
832  return false;
833  }
834  }
835 
836  return true;
837 }
838 
839 bool Position::inLastEditableInRootEditableElement() const
840 {
841  if (isEmpty() || !inRenderedContent()) {
842  return false;
843  }
844 
845  PositionIterator it(*this);
846  while (!it.atEnd()) {
847  if (it.next().inRenderedContent()) {
848  return false;
849  }
850  }
851 
852  return true;
853 }
854 
855 bool Position::inFirstEditableInContainingEditableBlock() const
856 {
857  if (isEmpty() || !RenderPosition::inRenderedContent(*this)) {
858  return false;
859  }
860 
861  NodeImpl *block = node()->enclosingBlockFlowElement();
862 
863  PositionIterator it(*this);
864  while (!it.atStart()) {
865  it.previous();
866  if (!RenderPosition::inRenderedContent(it.current())) {
867  continue;
868  }
869  return block != it.current().node()->enclosingBlockFlowElement();
870  }
871 
872  return true;
873 }
874 
875 bool Position::inLastEditableInContainingEditableBlock() const
876 {
877  if (isEmpty() || !RenderPosition::inRenderedContent(*this)) {
878  return false;
879  }
880 
881  NodeImpl *block = node()->enclosingBlockFlowElement();
882 
883  PositionIterator it(*this);
884  while (!it.atEnd()) {
885  it.next();
886  if (!RenderPosition::inRenderedContent(it.current())) {
887  continue;
888  }
889  return block != it.current().node()->enclosingBlockFlowElement();
890  }
891 
892  return true;
893 }
894 
895 QDebug operator<<(QDebug stream, const Position &position)
896 {
897  const NodeImpl *node = position.node();
898  stream << "Position(" << node << (node ? node->nodeName() : QString()) << ":" << position.offset() << ")";
899  return stream;
900 }
901 
902 } // namespace DOM
QString & fill(QChar ch, int size)
virtual long caretMinOffset() const
returns the lowest possible value the caret offset may have to still point to a valid position...
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
virtual long caretMaxOffset() const
returns the highest possible value the caret offset may have to still point to a valid position...
This library provides a full-featured HTML parser and widget.
const QList< QKeySequence > & end()
QDataStream & operator<<(QDataStream &out, const KDateTime::Spec &spec)
Base Class for all rendering tree objects.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.