⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 render_text.cpp

📁 手机浏览器源码程序,功能强大
💻 CPP
📖 第 1 页 / 共 5 页
字号:
/**
 * This file is part of the DOM implementation for KDE.
 *
 * (C) 1999 Lars Knoll (knoll@kde.org)
 * (C) 2000 Dirk Mueller (mueller@kde.org)
 * Copyright (C) 2004 Apple Computer, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */
//#define DEBUG_LAYOUT
//#define BIDI_DEBUG

#include "rendering/render_canvas.h"
#include "rendering/render_object.h"
#include "rendering/render_text.h"
#include "rendering/break_lines.h"
#include "dom/dom2_range.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_docimpl.h"
#include "xml/dom_position.h"
#include "render_arena.h"

#include "misc/loader.h"

#include "khtml_part.h"

#include <qpainter.h>
#include <kdebug.h>
#include <assert.h>

// You may have to turn this to 0 to compile without the headers for ICU installed.
#define HAVE_ICU_LIBRARY 0

#if HAVE_ICU_LIBRARY
#include <unicode/ubrk.h>
#include <unicode/uloc.h>
#include <unicode/utypes.h>
#include <unicode/parseerr.h>
#endif

using namespace khtml;
using namespace DOM;

#ifndef NDEBUG
static bool inInlineTextBoxDetach;
#endif

void InlineTextBox::detach(RenderArena* renderArena)
{
#ifndef NDEBUG
    inInlineTextBoxDetach = true;
#endif
    delete this;
#ifndef NDEBUG
    inInlineTextBoxDetach = false;
#endif

    // Recover the size left there for us by operator delete and free the memory.
    renderArena->free(*(size_t *)this, this);
}

void* InlineTextBox::operator new(size_t sz, RenderArena* renderArena) throw()
{
    return renderArena->allocate(sz);
}

void InlineTextBox::operator delete(void* ptr, size_t sz)
{
    assert(inInlineTextBoxDetach);

    // Stash size where detach can find it.
    *(size_t *)ptr = sz;
}

RenderText* InlineTextBox::textObject()
{
    return static_cast<RenderText*>(m_object);
}

bool InlineTextBox::checkVerticalPoint(int _y, int _ty, int _h)
{
    int topY = m_y;
    int bottomY = m_y + m_height;
    if (root()->hasSelectedChildren()) {
        topY = kMin(root()->selectionTop(), topY);
        bottomY = kMax(bottomY, root()->bottomOverflow());
    }
    if ((_ty + topY >= _y + _h) || (_ty + bottomY <= _y))
        return false;
    return true;
}

bool InlineTextBox::isSelected(int startPos, int endPos) const
{
    int sPos = kMax(startPos - m_start, 0);
    int ePos = kMin(endPos - m_start, (int)m_len);
    return (sPos < ePos);
}

RenderObject::SelectionState InlineTextBox::selectionState()
{
    RenderObject::SelectionState state = object()->selectionState();
    if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd ||
        state == RenderObject::SelectionBoth) {
        int startPos, endPos;
        object()->selectionStartEnd(startPos, endPos);

        bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
        bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= m_start + m_len);
        if (start && end)
            state = RenderObject::SelectionBoth;
        else if (start)
            state = RenderObject::SelectionStart;
        else if (end)
            state = RenderObject::SelectionEnd;
        else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
                 (state == RenderObject::SelectionStart || endPos > m_start + m_len))
            state = RenderObject::SelectionInside;
    }
    return state;
}

QRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
{
    int sPos = kMax(startPos - m_start, 0);
    int ePos = kMin(endPos - m_start, (int)m_len);

    if (sPos >= ePos)
        return QRect();

    RootInlineBox* rootBox = root();
    int selStart = m_reversed ? m_x + m_width : m_x;
    int selEnd = selStart;
    int selTop = rootBox->selectionTop();
    int selHeight = rootBox->selectionHeight();

    // FIXME: For justified text, just return the entire text box's rect.  At the moment there's still no easy
    // way to get the width of a run including the justification padding.
    if (sPos > 0 && !m_toAdd) {
        // The selection begins in the middle of our run.
        int w = textObject()->width(m_start, sPos, m_firstLine);
        if (m_reversed)
            selStart -= w;
        else
            selStart += w;
    }

    if (m_toAdd || (sPos == 0 && ePos == m_len)) {
        if (m_reversed)
            selEnd = m_x;
        else
            selEnd = m_x + m_width;
    }
    else {
        // Our run is partially selected, and so we have to actually do a measurement.
        int w = textObject()->width(sPos + m_start, ePos - sPos, m_firstLine);
        if (m_reversed)
            selEnd = selStart - w;
        else
            selEnd = selStart + w;
    }

    int selLeft = m_reversed ? selEnd : selStart;
    int selRight = m_reversed ? selStart : selEnd;

    return QRect(selLeft + tx, selTop + ty, selRight - selLeft, selHeight);
}

void InlineTextBox::deleteLine(RenderArena* arena)
{
    static_cast<RenderText*>(m_object)->removeTextBox(this);
    detach(arena);
}

void InlineTextBox::extractLine()
{
    if (m_extracted)
        return;

    static_cast<RenderText*>(m_object)->extractTextBox(this);
}

void InlineTextBox::attachLine()
{
    if (!m_extracted)
        return;

    static_cast<RenderText*>(m_object)->attachTextBox(this);
}

int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
{
    if (foundBox) {
        m_truncation = cFullTruncation;
        return -1;
    }

    int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;

    // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated.
    if (ltr) {
        if (ellipsisX <= m_x) {
            // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
            m_truncation = cFullTruncation;
            foundBox = true;
            return -1;
        }

        if (ellipsisX < m_x + m_width) {
            if (m_reversed)
                return -1; // FIXME: Support LTR truncation when the last run is RTL someday.

            foundBox = true;

            int offset = offsetForPosition(ellipsisX, false);
            if (offset == 0) {
                // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
                // and the ellipsis edge.
                m_truncation = cFullTruncation;
                return kMin(ellipsisX, m_x);
            }

            // Set the truncation index on the text run.  The ellipsis needs to be placed just after the last visible character.
            m_truncation = offset + m_start;
            return m_x + static_cast<RenderText*>(m_object)->width(m_start, offset, m_firstLine);
        }
    }
    else {
        // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
    }
    return -1;
}

static int
simpleDifferenceBetweenColors(QColor c1, QColor c2)
{
    // a distance could be computed by squaring the differences between components, but
    // this is faster and so far seems good enough for our purposes.
    return abs(c1.red() - c2.red()) + abs(c1.green() - c2.green()) + abs(c1.blue() - c2.blue());
}

static QColor
correctedTextColor(QColor textColor, QColor backgroundColor)
{
    // Adjust the text color if it is too close to the background color,
    // by darkening or lightening it to move it further away.

    int d = simpleDifferenceBetweenColors(textColor, backgroundColor);
    // semi-arbitrarily chose 255 value here after a few tests;
    if (d > 255) {
        return textColor;
    }

    int distanceFromWhite = simpleDifferenceBetweenColors(textColor, Qt::white);
    int distanceFromBlack = simpleDifferenceBetweenColors(textColor, Qt::black);

    if (distanceFromWhite < distanceFromBlack) {
        return textColor.dark();
    }

    return textColor.light();
}

bool InlineTextBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
{
    if (object()->isBR())
        return false;

    QRect rect(tx + m_x, ty + m_y, m_width, m_height);
    if (m_truncation != cFullTruncation &&
        object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
        object()->setInnerNode(i);
        return true;
    }
    return false;
}

void InlineTextBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
{
    if (object()->isBR() || !object()->shouldPaintWithinRoot(i) || object()->style()->visibility() != VISIBLE ||
        m_truncation == cFullTruncation || i.phase == PaintActionOutline)
        return;

    int xPos = tx + m_x;
    int w = width();
    if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x()))
        return;

    bool isPrinting = (i.p->device()->devType() == QInternal::Printer);

    // Determine whether or not we're selected.
    bool haveSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
    if (!haveSelection && i.phase == PaintActionSelection)
        // When only painting the selection, don't bother to paint if there is none.
        return;

    // Determine whether or not we have marked text.
    Range markedTextRange = KWQ(object()->document()->part())->markedTextRange();
    bool haveMarkedText = markedTextRange.handle() != 0 && markedTextRange.startContainer() == object()->node();
    bool markedTextUsesUnderlines = KWQ(object()->document()->part())->markedTextUsesUnderlines();


    // Set our font.
    RenderStyle* styleToUse = object()->style(m_firstLine);
    int d = styleToUse->textDecorationsInEffect();
    if (styleToUse->font() != i.p->font())
        i.p->setFont(styleToUse->font());
    const Font *font = &styleToUse->htmlFont();

    // 1. Paint backgrounds behind text if needed.  Examples of such backgrounds include selection
    // and marked text.
    if ((haveSelection || haveMarkedText) && !markedTextUsesUnderlines && i.phase != PaintActionSelection && !isPrinting) {
        if (haveMarkedText)
            paintMarkedTextBackground(i.p, tx, ty, styleToUse, font, markedTextRange.startOffset(), markedTextRange.endOffset());

        if (haveSelection)
            paintSelection(i.p, tx, ty, styleToUse, font);
    }

    // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
    if (m_len <= 0) return;
    QValueList<DocumentMarker> markers = object()->document()->markersForNode(object()->node());
    QValueListIterator <DocumentMarker> markerIt = markers.begin();

    QValueList<KWQKHTMLPart::MarkedTextUnderline> underlines;
    if (haveMarkedText && markedTextUsesUnderlines) {
        underlines = KWQ(object()->document()->part())->markedTextUnderlines();
    }
    QValueListIterator<KWQKHTMLPart::MarkedTextUnderline> underlineIt = underlines.begin();

    QColor textColor = styleToUse->color();
    
    // Make the text color legible against a white background
    if (styleToUse->forceBackgroundsToWhite())
        textColor = correctedTextColor(textColor, Qt::white);

    if (textColor != i.p->pen().color())
        i.p->setPen(textColor);

    // Set a text shadow if we have one.
    // FIXME: Support multiple shadow effects.  Need more from the CG API before
    // we can do this.
    bool setShadow = false;
    if (styleToUse->textShadow()) {
        i.p->setShadow(styleToUse->textShadow()->x, styleToUse->textShadow()->y,
                        styleToUse->textShadow()->blur, styleToUse->textShadow()->color);
        setShadow = true;
    }

    bool paintSelectedTextOnly = (i.phase == PaintActionSelection);
    bool paintSelectedTextSeparately = false; // Whether or not we have to do multiple paints.  Only
                                              // necessary when a custom ::selection foreground color is applied.
    QColor selectionColor = i.p->pen().color();
    ShadowData* selectionTextShadow = 0;
    if (haveSelection) {
        RenderStyle* pseudoStyle = object()->getPseudoStyle(RenderStyle::SELECTION);
        if (pseudoStyle) {
            if (pseudoStyle->color() != selectionColor || pseudoStyle->textShadow()) {
                if (!paintSelectedTextOnly)
                    paintSelectedTextSeparately = true;
                if (pseudoStyle->color() != selectionColor)
                    selectionColor = pseudoStyle->color();
                if (pseudoStyle->textShadow())

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -