File indexing completed on 2024-04-28 11:38:52
0001 /** 0002 * This file is part of the html renderer for KDE. 0003 * 0004 * Copyright (C) 2009 Vyacheslav Tokarev (tsjoker@gmail.com) 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 "rendering/render_position.h" 0023 #include "rendering/render_text.h" 0024 #include "rendering/render_line.h" 0025 #include "rendering/render_block.h" 0026 0027 using namespace DOM; 0028 using namespace khtml; 0029 0030 RenderPosition::RenderPosition(NodeImpl *node, int offset) 0031 { 0032 if (node && node->renderer() && node->renderer()->isText()) { 0033 offset = static_cast<RenderText *>(node->renderer())->convertToDOMPosition(offset); 0034 } 0035 m_position = Position(node, offset); 0036 } 0037 0038 RenderPosition RenderPosition::fromDOMPosition(const Position &position) 0039 { 0040 if (position.isEmpty()) { 0041 return RenderPosition(); 0042 } 0043 0044 NodeImpl *node = position.node(); 0045 RenderObject *renderObject = node->renderer(); 0046 // if no renderer -> no position in the rendering is possible 0047 if (!renderObject) { 0048 return RenderPosition(); 0049 } 0050 0051 if (!renderObject->isText()) { 0052 return RenderPosition(position); 0053 } 0054 0055 if (renderObject->isBR()) { 0056 return (!position.offset() && renderObject->inlineBox(0)) ? RenderPosition(Position(node, 0)) : RenderPosition(); 0057 } 0058 // return renderObject->inlineBox(0) ? RenderPosition(position) : RenderPosition(); 0059 0060 // qCDebug(KHTML_LOG) << "[text position]" << position; 0061 const RenderText *renderText = static_cast<const RenderText *>(renderObject); 0062 int domOffset = position.offset(); 0063 int renderOffset = renderText->convertToRenderedPosition(domOffset); 0064 domOffset = renderText->convertToDOMPosition(renderOffset); 0065 // now we need to modify original position 0066 // qCDebug(KHTML_LOG) << "[equivalent offset]" << domOffset; 0067 RenderPosition result(Position(node, domOffset)); 0068 if (!result.getInlineBoxAndOffset(renderOffset)) { 0069 return RenderPosition(); 0070 } 0071 return result; 0072 } 0073 0074 InlineBox *RenderPosition::getInlineBoxAndOffset(int &offset) const 0075 { 0076 // default value 0077 offset = 0; 0078 if (!renderer()) { 0079 // qCDebug(KHTML_LOG) << "[EMPTY POSITION]"; 0080 return nullptr; 0081 } 0082 // qCDebug(KHTML_LOG) << "[find inline box]" << m_position; 0083 0084 const NodeImpl *node = m_position.node(); 0085 /*const*/ RenderObject *renderObject = node->renderer(); 0086 if (!renderObject->isText()) { 0087 offset = m_position.offset(); 0088 return renderObject->inlineBox(offset); 0089 } 0090 if (renderObject->isBR()) { 0091 offset = m_position.offset(); 0092 return renderObject->inlineBox(0); 0093 } 0094 int domOffset = m_position.offset(); 0095 0096 /*const*/ RenderText *renderText = static_cast<RenderText *>(renderObject); 0097 int renderOffset = renderText->convertToRenderedPosition(domOffset); 0098 InlineTextBox *textBox; 0099 for (textBox = renderText->firstTextBox(); textBox; textBox = textBox->nextTextBox()) { 0100 if (renderOffset >= textBox->start() && renderOffset <= textBox->end()) { 0101 offset = renderOffset; // - textBox->start(); 0102 // qCDebug(KHTML_LOG) << "[result]" << offset << textBox; 0103 return textBox; 0104 } else if (renderOffset < textBox->start()) { 0105 offset = textBox->start(); 0106 // qCDebug(KHTML_LOG) << "[result]" << offset << textBox; 0107 return textBox; 0108 } else if (!textBox->nextTextBox()) { 0109 offset = textBox->start() + textBox->len(); 0110 // qCDebug(KHTML_LOG) << "[result]" << offset << textBox; 0111 return textBox; 0112 } 0113 // choose right box we're at 0114 // if we're not we should probably return 0, but set offset properly 0115 } 0116 return nullptr; 0117 } 0118 0119 bool RenderPosition::rendersInDifferentPosition(const RenderPosition &self, const RenderPosition &other) 0120 { 0121 // qCDebug(KHTML_LOG) << "[compare]" << self.position() << other.position(); 0122 if (self == other) { 0123 return false; 0124 } 0125 if (self.isEmpty() || other.isEmpty()) { 0126 return false; 0127 } 0128 // if (self.renderer() != other.renderer()) return true; 0129 if (!self.renderer() || !other.renderer()) { 0130 return false; 0131 } 0132 int selfOffset; 0133 const InlineBox *selfBox = self.getInlineBoxAndOffset(selfOffset); 0134 int otherOffset; 0135 const InlineBox *otherBox = other.getInlineBoxAndOffset(otherOffset); 0136 if (selfBox == otherBox && selfOffset == otherOffset) { 0137 return false; 0138 } 0139 0140 // FIXME remove caret rects comparing - it's slow, or leave as rare fall back 0141 int x1, y1, x2, y2, w1, h1, w2, h2; 0142 self.renderer()->caretPos(const_cast<RenderPosition &>(self).renderedOffset(), 0, x1, y1, w1, h1); 0143 other.renderer()->caretPos(const_cast<RenderPosition &>(other).renderedOffset(), 0, x2, y2, w2, h2); 0144 if (x1 == x2 && y1 == y2 && w1 == w2 && h1 == h2) { 0145 return false; 0146 } 0147 0148 // compare containing blocks 0149 // flow block elements (DOM) 0150 // bound positions etc 0151 return true; 0152 } 0153 0154 bool RenderPosition::rendersOnSameLine(const RenderPosition &self, const RenderPosition &other) 0155 { 0156 if (self == other) { 0157 return true; 0158 } 0159 if (self.isEmpty() || other.isEmpty()) { 0160 return false; 0161 } 0162 if (self.renderer() != other.renderer()) { 0163 return false; 0164 } 0165 int tempOffset; 0166 /*const */InlineBox *selfBox = self.getInlineBoxAndOffset(tempOffset); 0167 /*const */InlineBox *otherBox = other.getInlineBoxAndOffset(tempOffset); 0168 return selfBox == otherBox || (selfBox && otherBox && selfBox->root() == otherBox->root()); 0169 } 0170 0171 RenderPosition RenderPosition::previousLinePosition(int x) 0172 { 0173 // qCDebug(KHTML_LOG) << "[Previous line at x]" << x; 0174 if (!renderer()) { 0175 return *this; 0176 } 0177 0178 int rOffset; 0179 NodeImpl *node = m_position.node(); 0180 InlineBox *box = getInlineBoxAndOffset(rOffset); 0181 // qCDebug(KHTML_LOG) << "[box;offset]" << box << rOffset; 0182 0183 RenderBlock *containingBlock = nullptr; 0184 RootInlineBox *root = nullptr; 0185 if (box) { 0186 root = box->root()->prevRootBox(); 0187 } 0188 // qCDebug(KHTML_LOG) << "[root]" << root; 0189 if (root) { 0190 containingBlock = node->renderer()->containingBlock(); 0191 } else { 0192 // This containing editable block does not have a previous line. 0193 // Need to move back to previous containing editable block in this root editable 0194 // block and find the last root line box in that block. 0195 NodeImpl *startBlock = node->enclosingBlockFlowElement(); 0196 NodeImpl *n = node->previousEditable(); 0197 // qCDebug(KHTML_LOG) << "[StartBlock]" << startBlock << (startBlock ? startBlock->renderer() : 0); 0198 while (n && startBlock == n->enclosingBlockFlowElement()) { 0199 n = n->previousEditable(); 0200 } 0201 // qCDebug(KHTML_LOG) << "[n]" << n << (n ? n->renderer() : 0) << (n ? n->nodeName() : ""); 0202 printEnclosingBlockTree(n); 0203 if (n) { 0204 while (n && !Position(n, n->caretMaxOffset()).inRenderedContent()) { 0205 // qCDebug(KHTML_LOG) << "[previous]" << n; 0206 n = n->previousEditable(); 0207 } 0208 // qCDebug(KHTML_LOG) << "[n]" << n << (n ? n->renderer() : 0); 0209 if (n && inSameRootNavigableElement(n, node)) { 0210 assert(n->renderer()); 0211 // box = n->renderer()->inlineBox(n->caretMaxOffset()); 0212 int offset; 0213 box = RenderPosition::fromDOMPosition(Position(n, n->caretMaxOffset())).getInlineBoxAndOffset(offset); 0214 // qCDebug(KHTML_LOG) << "[box]" << box << offset; 0215 // previous root line box found 0216 if (box) { 0217 root = box->root(); 0218 containingBlock = n->renderer()->containingBlock(); 0219 // qCDebug(KHTML_LOG) << "[root,block]" << root << containingBlock; 0220 } 0221 return RenderPosition::fromDOMPosition(Position(n, n->caretMaxOffset())).position(); 0222 } 0223 } 0224 } 0225 0226 if (root) { 0227 int absx, absy; 0228 containingBlock->absolutePosition(absx, absy); 0229 // qCDebug(KHTML_LOG) << "[cb]" << containingBlock << absx << absy; 0230 RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object(); 0231 // qCDebug(KHTML_LOG) << "[renderer]" << renderer; 0232 return renderer->positionForCoordinates(x, absy + root->topOverflow()); 0233 } 0234 0235 return *this; 0236 } 0237 0238 RenderPosition RenderPosition::nextLinePosition(int x) 0239 { 0240 // qCDebug(KHTML_LOG) << "[Next line at x]" << x; 0241 if (!renderer()) { 0242 return *this; 0243 } 0244 0245 int rOffset; 0246 NodeImpl *node = m_position.node(); 0247 InlineBox *box = getInlineBoxAndOffset(rOffset); 0248 // qCDebug(KHTML_LOG) << "[box;offset]" << box << rOffset; 0249 0250 RenderBlock *containingBlock = nullptr; 0251 RootInlineBox *root = nullptr; 0252 if (box) { 0253 root = box->root()->nextRootBox(); 0254 } 0255 if (root) { 0256 containingBlock = node->renderer()->containingBlock(); 0257 } else { 0258 // This containing editable block does not have a next line. 0259 // Need to move forward to next containing editable block in this root editable 0260 // block and find the first root line box in that block. 0261 NodeImpl *startBlock = node->enclosingBlockFlowElement(); 0262 NodeImpl *n = node->nextEditable(); 0263 while (n && startBlock == n->enclosingBlockFlowElement()) { 0264 n = n->nextEditable(); 0265 } 0266 if (n) { 0267 while (n && !Position(n, n->caretMinOffset()).inRenderedContent()) { 0268 n = n->nextEditable(); 0269 } 0270 if (n && inSameRootNavigableElement(n, node)) { 0271 assert(n->renderer()); 0272 box = n->renderer()->inlineBox(n->caretMinOffset()); 0273 // previous root line box found 0274 if (box) { 0275 root = box->root(); 0276 containingBlock = n->renderer()->containingBlock(); 0277 } 0278 return Position(n, n->caretMinOffset()); 0279 } 0280 } 0281 } 0282 0283 if (root) { 0284 int absx, absy; 0285 containingBlock->absolutePosition(absx, absy); 0286 RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object(); 0287 return renderer->positionForCoordinates(x, absy + root->topOverflow()); 0288 } 0289 0290 return *this; 0291 } 0292 0293 /*bool RenderPosition::haveRenderPosition() 0294 { 0295 // qCDebug(KHTML_LOG) << *this; 0296 if (isEmpty()) 0297 return false; 0298 0299 RenderObject *renderer = node()->renderer(); 0300 if (!renderer || !(node()->document()->part()->isCaretMode() || renderer->isEditable())) 0301 return false; 0302 0303 if (renderer->style()->visibility() != khtml::VISIBLE) 0304 return false; 0305 0306 if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) { 0307 return offset() == 0; 0308 } 0309 else if (renderer->isText()) { 0310 RenderText *textRenderer = static_cast<RenderText *>(renderer); 0311 // qCDebug(KHTML_LOG) << "text" << textRenderer; 0312 unsigned rOffset = textRenderer->convertToRenderedPosition(offset()); 0313 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { 0314 // qCDebug(KHTML_LOG) << "box" << box << box->m_start << box->m_start + box->m_len; 0315 if (rOffset >= box->m_start && rOffset <= box->m_start + box->m_len) { 0316 return true; 0317 } 0318 else if (rOffset < box->m_start) { 0319 // The offset we're looking for is before this node 0320 // this means the offset must be in content that is 0321 // not rendered. Return false. 0322 return false; 0323 } 0324 } 0325 } 0326 else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) { 0327 // don't return containing editable blocks unless they are empty 0328 if (node()->enclosingBlockFlowElement() == node() && node()->firstChild()) 0329 return false; 0330 return true; 0331 } 0332 0333 return false; 0334 }*/ 0335 0336 /*bool RenderPosition::isFirstOnRenderedLine() const 0337 { 0338 } 0339 0340 bool RenderPosition::isLastOnRenderedLine() const 0341 { 0342 }*/ 0343