blob: 2de20d117d843cbf431a989d280327bc50fa7fc2 [file] [log] [blame]
/**
* This file is part of the html renderer for KDE.
*
* Copyright (C) 2003, 2006 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.
*/
#include "config.h"
#include "RootInlineBox.h"
#include "EllipsisBox.h"
#include "RenderBlock.h"
#include "GraphicsContext.h"
#include "Document.h"
#if PLATFORM(MAC)
#include "FrameMac.h"
#endif
using namespace std;
namespace WebCore {
void RootInlineBox::destroy(RenderArena* arena)
{
detachEllipsisBox(arena);
InlineFlowBox::destroy(arena);
}
void RootInlineBox::detachEllipsisBox(RenderArena* arena)
{
if (m_ellipsisBox) {
m_ellipsisBox->destroy(arena);
m_ellipsisBox = 0;
}
}
void RootInlineBox::clearTruncation()
{
if (m_ellipsisBox) {
detachEllipsisBox(m_object->renderArena());
InlineFlowBox::clearTruncation();
}
}
bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth)
{
// First sanity-check the unoverflowed width of the whole line to see if there is sufficient room.
int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge;
if (width() - delta < ellipsisWidth)
return false;
// Next iterate over all the line boxes on the line. If we find a replaced element that intersects
// then we refuse to accommodate the ellipsis. Otherwise we're ok.
return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth);
}
void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, int blockEdge, int ellipsisWidth,
InlineBox* markupBox)
{
// Create an ellipsis box.
m_ellipsisBox = new (m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this,
ellipsisWidth - (markupBox ? markupBox->width() : 0),
yPos(), height(), baseline(), !prevRootBox(),
markupBox);
if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) {
m_ellipsisBox->m_x = xPos() + width();
return;
}
// Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL)
// of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being
// truncated).
bool foundBox = false;
m_ellipsisBox->m_x = placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
}
int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
{
int result = InlineFlowBox::placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
if (result == -1)
result = ltr ? blockEdge - ellipsisWidth : blockEdge;
return result;
}
void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& i, int _tx, int _ty) const
{
if (m_ellipsisBox && object()->shouldPaintWithinRoot(i) && object()->style()->visibility() == VISIBLE &&
i.phase == PaintPhaseForeground)
m_ellipsisBox->paint(i, _tx, _ty);
}
#if PLATFORM(MAC)
void RootInlineBox::addHighlightOverflow()
{
// Highlight acts as a selection inflation.
FloatRect rootRect(0, selectionTop(), width(), selectionHeight());
IntRect inflatedRect = enclosingIntRect(Mac(object()->document()->frame())->customHighlightLineRect(object()->style()->highlight(), rootRect));
m_leftOverflow = min(m_leftOverflow, inflatedRect.x());
m_rightOverflow = max(m_rightOverflow, inflatedRect.right());
m_topOverflow = min(m_topOverflow, inflatedRect.y());
m_bottomOverflow = max(m_bottomOverflow, inflatedRect.bottom());
}
void RootInlineBox::paintCustomHighlight(RenderObject::PaintInfo& i, int tx, int ty, const AtomicString& highlightType)
{
if (!object()->shouldPaintWithinRoot(i) || object()->style()->visibility() != VISIBLE || i.phase != PaintPhaseForeground)
return;
// Get the inflated rect so that we can properly hit test.
FloatRect rootRect(tx + xPos(), ty + selectionTop(), width(), selectionHeight());
FloatRect inflatedRect = Mac(object()->document()->frame())->customHighlightLineRect(highlightType, rootRect);
if (inflatedRect.intersects(i.r))
Mac(object()->document()->frame())->paintCustomHighlight(highlightType, rootRect, rootRect, false, true);
}
#endif
void RootInlineBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
{
InlineFlowBox::paint(i, tx, ty);
paintEllipsisBox(i, tx, ty);
#if PLATFORM(MAC)
RenderStyle* styleToUse = object()->style(m_firstLine);
if (styleToUse->highlight() != nullAtom && !i.p->paintingDisabled())
paintCustomHighlight(i, tx, ty, styleToUse->highlight());
#endif
}
bool RootInlineBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
{
if (m_ellipsisBox && object()->style()->visibility() == VISIBLE) {
if (m_ellipsisBox->nodeAtPoint(i, x, y, tx, ty)) {
object()->setInnerNode(i);
return true;
}
}
return InlineFlowBox::nodeAtPoint(i, x, y, tx, ty);
}
void RootInlineBox::adjustPosition(int dx, int dy)
{
InlineFlowBox::adjustPosition(dx, dy);
m_topOverflow += dy;
m_bottomOverflow += dy;
m_blockHeight += dy;
m_selectionTop += dy;
m_selectionBottom += dy;
}
void RootInlineBox::childRemoved(InlineBox* box)
{
if (box->object() == m_lineBreakObj)
setLineBreakInfo(0, 0, 0, 0);
RootInlineBox* prev = prevRootBox();
if (prev && prev->lineBreakObj() == box->object()) {
prev->setLineBreakInfo(0, 0, 0, 0);
prev->markDirty();
}
}
GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty,
const RenderObject::PaintInfo* i)
{
GapRects result;
RenderObject::SelectionState lineState = selectionState();
bool leftGap, rightGap;
block()->getHorizontalSelectionGapInfo(lineState, leftGap, rightGap);
InlineBox* firstBox = firstSelectedBox();
InlineBox* lastBox = lastSelectedBox();
if (leftGap)
result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->object(),
firstBox->xPos(), selTop, selHeight,
rootBlock, blockX, blockY, tx, ty, i));
if (rightGap)
result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->object(),
lastBox->xPos() + lastBox->width(), selTop, selHeight,
rootBlock, blockX, blockY, tx, ty, i));
if (firstBox && firstBox != lastBox) {
// Now fill in any gaps on the line that occurred between two selected elements.
int lastX = firstBox->xPos() + firstBox->width();
for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
if (box->selectionState() != RenderObject::SelectionNone) {
result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->object(),
lastX + tx, selTop + ty,
box->xPos() - lastX, selHeight, i));
lastX = box->xPos() + box->width();
}
if (box == lastBox)
break;
}
}
return result;
}
void RootInlineBox::setHasSelectedChildren(bool b)
{
if (m_hasSelectedChildren == b)
return;
m_hasSelectedChildren = b;
}
RenderObject::SelectionState RootInlineBox::selectionState()
{
// Walk over all of the selected boxes.
RenderObject::SelectionState state = RenderObject::SelectionNone;
for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
RenderObject::SelectionState boxState = box->selectionState();
if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) ||
(boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart))
state = RenderObject::SelectionBoth;
else if (state == RenderObject::SelectionNone ||
((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) &&
(state == RenderObject::SelectionNone || state == RenderObject::SelectionInside)))
state = boxState;
if (state == RenderObject::SelectionBoth)
break;
}
return state;
}
InlineBox* RootInlineBox::firstSelectedBox()
{
for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild())
if (box->selectionState() != RenderObject::SelectionNone)
return box;
return 0;
}
InlineBox* RootInlineBox::lastSelectedBox()
{
for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild())
if (box->selectionState() != RenderObject::SelectionNone)
return box;
return 0;
}
int RootInlineBox::selectionTop()
{
if (!prevRootBox())
return m_selectionTop;
int prevBottom = prevRootBox()->selectionBottom();
if (prevBottom < m_selectionTop && block()->containsFloats()) {
// This line has actually been moved further down, probably from a large line-height, but possibly because the
// line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous
// line's bottom overflow if the offsets are greater on both sides.
int prevLeft = block()->leftOffset(prevBottom);
int prevRight = block()->rightOffset(prevBottom);
int newLeft = block()->leftOffset(m_selectionTop);
int newRight = block()->rightOffset(m_selectionTop);
if (prevLeft > newLeft || prevRight < newRight)
return m_selectionTop;
}
return prevBottom;
}
RenderBlock* RootInlineBox::block() const
{
return static_cast<RenderBlock*>(m_object);
}
InlineBox* RootInlineBox::closestLeafChildForXPos(int _x, int _tx)
{
InlineBox *firstLeaf = firstLeafChildAfterBox();
InlineBox *lastLeaf = lastLeafChildBeforeBox();
if (firstLeaf == lastLeaf)
return firstLeaf;
// Avoid returning a list marker when possible.
if (_x <= _tx + firstLeaf->m_x && !firstLeaf->object()->isListMarker())
// The x coordinate is less or equal to left edge of the firstLeaf.
// Return it.
return firstLeaf;
if (_x >= _tx + lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker())
// The x coordinate is greater or equal to right edge of the lastLeaf.
// Return it.
return lastLeaf;
for (InlineBox *leaf = firstLeaf; leaf && leaf != lastLeaf; leaf = leaf->nextLeafChild()) {
if (!leaf->object()->isListMarker()) {
int leafX = _tx + leaf->m_x;
if (_x < leafX + leaf->m_width)
// The x coordinate is less than the right edge of the box.
// Return it.
return leaf;
}
}
return lastLeaf;
}
void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, BidiStatus* status, BidiContext* context)
{
m_lineBreakObj = obj;
m_lineBreakPos = breakPos;
m_lineBreakContext = context;
if (status)
m_lineBreakBidiStatus = *status;
}
}