File indexing completed on 2024-04-28 15:24:09

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