KHtml

render_position.cpp
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 2009 Vyacheslav Tokarev ([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 "rendering/render_position.h"
23 #include "rendering/render_text.h"
24 #include "rendering/render_line.h"
25 #include "rendering/render_block.h"
26 
27 using namespace DOM;
28 using namespace khtml;
29 
30 RenderPosition::RenderPosition(NodeImpl *node, int offset)
31 {
32  if (node && node->renderer() && node->renderer()->isText()) {
33  offset = static_cast<RenderText *>(node->renderer())->convertToDOMPosition(offset);
34  }
35  m_position = Position(node, offset);
36 }
37 
38 RenderPosition RenderPosition::fromDOMPosition(const Position &position)
39 {
40  if (position.isEmpty()) {
41  return RenderPosition();
42  }
43 
44  NodeImpl *node = position.node();
45  RenderObject *renderObject = node->renderer();
46  // if no renderer -> no position in the rendering is possible
47  if (!renderObject) {
48  return RenderPosition();
49  }
50 
51  if (!renderObject->isText()) {
52  return RenderPosition(position);
53  }
54 
55  if (renderObject->isBR()) {
56  return (!position.offset() && renderObject->inlineBox(0)) ? RenderPosition(Position(node, 0)) : RenderPosition();
57  }
58  // return renderObject->inlineBox(0) ? RenderPosition(position) : RenderPosition();
59 
60  // qCDebug(KHTML_LOG) << "[text position]" << position;
61  const RenderText *renderText = static_cast<const RenderText *>(renderObject);
62  int domOffset = position.offset();
63  int renderOffset = renderText->convertToRenderedPosition(domOffset);
64  domOffset = renderText->convertToDOMPosition(renderOffset);
65  // now we need to modify original position
66  // qCDebug(KHTML_LOG) << "[equivalent offset]" << domOffset;
67  RenderPosition result(Position(node, domOffset));
68  if (!result.getInlineBoxAndOffset(renderOffset)) {
69  return RenderPosition();
70  }
71  return result;
72 }
73 
74 InlineBox *RenderPosition::getInlineBoxAndOffset(int &offset) const
75 {
76  // default value
77  offset = 0;
78  if (!renderer()) {
79  // qCDebug(KHTML_LOG) << "[EMPTY POSITION]";
80  return nullptr;
81  }
82  // qCDebug(KHTML_LOG) << "[find inline box]" << m_position;
83 
84  const NodeImpl *node = m_position.node();
85  /*const*/ RenderObject *renderObject = node->renderer();
86  if (!renderObject->isText()) {
87  offset = m_position.offset();
88  return renderObject->inlineBox(offset);
89  }
90  if (renderObject->isBR()) {
91  offset = m_position.offset();
92  return renderObject->inlineBox(0);
93  }
94  int domOffset = m_position.offset();
95 
96  /*const*/ RenderText *renderText = static_cast<RenderText *>(renderObject);
97  int renderOffset = renderText->convertToRenderedPosition(domOffset);
98  InlineTextBox *textBox;
99  for (textBox = renderText->firstTextBox(); textBox; textBox = textBox->nextTextBox()) {
100  if (renderOffset >= textBox->start() && renderOffset <= textBox->end()) {
101  offset = renderOffset; // - textBox->start();
102  // qCDebug(KHTML_LOG) << "[result]" << offset << textBox;
103  return textBox;
104  } else if (renderOffset < textBox->start()) {
105  offset = textBox->start();
106  // qCDebug(KHTML_LOG) << "[result]" << offset << textBox;
107  return textBox;
108  } else if (!textBox->nextTextBox()) {
109  offset = textBox->start() + textBox->len();
110  // qCDebug(KHTML_LOG) << "[result]" << offset << textBox;
111  return textBox;
112  }
113  // choose right box we're at
114  // if we're not we should probably return 0, but set offset properly
115  }
116  return nullptr;
117 }
118 
119 bool RenderPosition::rendersInDifferentPosition(const RenderPosition &self, const RenderPosition &other)
120 {
121  // qCDebug(KHTML_LOG) << "[compare]" << self.position() << other.position();
122  if (self == other) {
123  return false;
124  }
125  if (self.isEmpty() || other.isEmpty()) {
126  return false;
127  }
128  // if (self.renderer() != other.renderer()) return true;
129  if (!self.renderer() || !other.renderer()) {
130  return false;
131  }
132  int selfOffset;
133  const InlineBox *selfBox = self.getInlineBoxAndOffset(selfOffset);
134  int otherOffset;
135  const InlineBox *otherBox = other.getInlineBoxAndOffset(otherOffset);
136  if (selfBox == otherBox && selfOffset == otherOffset) {
137  return false;
138  }
139 
140  // FIXME remove caret rects comparing - it's slow, or leave as rare fall back
141  int x1, y1, x2, y2, w1, h1, w2, h2;
142  self.renderer()->caretPos(const_cast<RenderPosition &>(self).renderedOffset(), 0, x1, y1, w1, h1);
143  other.renderer()->caretPos(const_cast<RenderPosition &>(other).renderedOffset(), 0, x2, y2, w2, h2);
144  if (x1 == x2 && y1 == y2 && w1 == w2 && h1 == h2) {
145  return false;
146  }
147 
148  // compare containing blocks
149  // flow block elements (DOM)
150  // bound positions etc
151  return true;
152 }
153 
154 bool RenderPosition::rendersOnSameLine(const RenderPosition &self, const RenderPosition &other)
155 {
156  if (self == other) {
157  return true;
158  }
159  if (self.isEmpty() || other.isEmpty()) {
160  return false;
161  }
162  if (self.renderer() != other.renderer()) {
163  return false;
164  }
165  int tempOffset;
166  /*const */InlineBox *selfBox = self.getInlineBoxAndOffset(tempOffset);
167  /*const */InlineBox *otherBox = other.getInlineBoxAndOffset(tempOffset);
168  return selfBox == otherBox || (selfBox && otherBox && selfBox->root() == otherBox->root());
169 }
170 
171 RenderPosition RenderPosition::previousLinePosition(int x)
172 {
173  // qCDebug(KHTML_LOG) << "[Previous line at x]" << x;
174  if (!renderer()) {
175  return *this;
176  }
177 
178  int rOffset;
179  NodeImpl *node = m_position.node();
180  InlineBox *box = getInlineBoxAndOffset(rOffset);
181  // qCDebug(KHTML_LOG) << "[box;offset]" << box << rOffset;
182 
183  RenderBlock *containingBlock = nullptr;
184  RootInlineBox *root = nullptr;
185  if (box) {
186  root = box->root()->prevRootBox();
187  }
188  // qCDebug(KHTML_LOG) << "[root]" << root;
189  if (root) {
190  containingBlock = node->renderer()->containingBlock();
191  } else {
192  // This containing editable block does not have a previous line.
193  // Need to move back to previous containing editable block in this root editable
194  // block and find the last root line box in that block.
195  NodeImpl *startBlock = node->enclosingBlockFlowElement();
196  NodeImpl *n = node->previousEditable();
197  // qCDebug(KHTML_LOG) << "[StartBlock]" << startBlock << (startBlock ? startBlock->renderer() : 0);
198  while (n && startBlock == n->enclosingBlockFlowElement()) {
199  n = n->previousEditable();
200  }
201  // qCDebug(KHTML_LOG) << "[n]" << n << (n ? n->renderer() : 0) << (n ? n->nodeName() : "");
202  printEnclosingBlockTree(n);
203  if (n) {
204  while (n && !Position(n, n->caretMaxOffset()).inRenderedContent()) {
205  // qCDebug(KHTML_LOG) << "[previous]" << n;
206  n = n->previousEditable();
207  }
208  // qCDebug(KHTML_LOG) << "[n]" << n << (n ? n->renderer() : 0);
209  if (n && inSameRootNavigableElement(n, node)) {
210  assert(n->renderer());
211  // box = n->renderer()->inlineBox(n->caretMaxOffset());
212  int offset;
213  box = RenderPosition::fromDOMPosition(Position(n, n->caretMaxOffset())).getInlineBoxAndOffset(offset);
214  // qCDebug(KHTML_LOG) << "[box]" << box << offset;
215  // previous root line box found
216  if (box) {
217  root = box->root();
218  containingBlock = n->renderer()->containingBlock();
219  // qCDebug(KHTML_LOG) << "[root,block]" << root << containingBlock;
220  }
221  return RenderPosition::fromDOMPosition(Position(n, n->caretMaxOffset())).position();
222  }
223  }
224  }
225 
226  if (root) {
227  int absx, absy;
228  containingBlock->absolutePosition(absx, absy);
229  // qCDebug(KHTML_LOG) << "[cb]" << containingBlock << absx << absy;
230  RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
231  // qCDebug(KHTML_LOG) << "[renderer]" << renderer;
232  return renderer->positionForCoordinates(x, absy + root->topOverflow());
233  }
234 
235  return *this;
236 }
237 
238 RenderPosition RenderPosition::nextLinePosition(int x)
239 {
240  // qCDebug(KHTML_LOG) << "[Next line at x]" << x;
241  if (!renderer()) {
242  return *this;
243  }
244 
245  int rOffset;
246  NodeImpl *node = m_position.node();
247  InlineBox *box = getInlineBoxAndOffset(rOffset);
248  // qCDebug(KHTML_LOG) << "[box;offset]" << box << rOffset;
249 
250  RenderBlock *containingBlock = nullptr;
251  RootInlineBox *root = nullptr;
252  if (box) {
253  root = box->root()->nextRootBox();
254  }
255  if (root) {
256  containingBlock = node->renderer()->containingBlock();
257  } else {
258  // This containing editable block does not have a next line.
259  // Need to move forward to next containing editable block in this root editable
260  // block and find the first root line box in that block.
261  NodeImpl *startBlock = node->enclosingBlockFlowElement();
262  NodeImpl *n = node->nextEditable();
263  while (n && startBlock == n->enclosingBlockFlowElement()) {
264  n = n->nextEditable();
265  }
266  if (n) {
267  while (n && !Position(n, n->caretMinOffset()).inRenderedContent()) {
268  n = n->nextEditable();
269  }
270  if (n && inSameRootNavigableElement(n, node)) {
271  assert(n->renderer());
272  box = n->renderer()->inlineBox(n->caretMinOffset());
273  // previous root line box found
274  if (box) {
275  root = box->root();
276  containingBlock = n->renderer()->containingBlock();
277  }
278  return Position(n, n->caretMinOffset());
279  }
280  }
281  }
282 
283  if (root) {
284  int absx, absy;
285  containingBlock->absolutePosition(absx, absy);
286  RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
287  return renderer->positionForCoordinates(x, absy + root->topOverflow());
288  }
289 
290  return *this;
291 }
292 
293 /*bool RenderPosition::haveRenderPosition()
294 {
295  // qCDebug(KHTML_LOG) << *this;
296  if (isEmpty())
297  return false;
298 
299  RenderObject *renderer = node()->renderer();
300  if (!renderer || !(node()->document()->part()->isCaretMode() || renderer->isEditable()))
301  return false;
302 
303  if (renderer->style()->visibility() != khtml::VISIBLE)
304  return false;
305 
306  if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) {
307  return offset() == 0;
308  }
309  else if (renderer->isText()) {
310  RenderText *textRenderer = static_cast<RenderText *>(renderer);
311  // qCDebug(KHTML_LOG) << "text" << textRenderer;
312  unsigned rOffset = textRenderer->convertToRenderedPosition(offset());
313  for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
314  // qCDebug(KHTML_LOG) << "box" << box << box->m_start << box->m_start + box->m_len;
315  if (rOffset >= box->m_start && rOffset <= box->m_start + box->m_len) {
316  return true;
317  }
318  else if (rOffset < box->m_start) {
319  // The offset we're looking for is before this node
320  // this means the offset must be in content that is
321  // not rendered. Return false.
322  return false;
323  }
324  }
325  }
326  else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) {
327  // don't return containing editable blocks unless they are empty
328  if (node()->enclosingBlockFlowElement() == node() && node()->firstChild())
329  return false;
330  return true;
331  }
332 
333  return false;
334 }*/
335 
336 /*bool RenderPosition::isFirstOnRenderedLine() const
337 {
338 }
339 
340 bool RenderPosition::isLastOnRenderedLine() const
341 {
342 }*/
343 
This file is part of the HTML rendering engine for KDE.
This library provides a full-featured HTML parser and widget.
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:08 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.