1/** 2 * Copyright (C) 2003, 2006 Apple Inc. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "EllipsisBox.h" 22 23#include "Document.h" 24#include "Font.h" 25#include "GraphicsContext.h" 26#include "HitTestResult.h" 27#include "InlineTextBox.h" 28#include "PaintInfo.h" 29#include "RootInlineBox.h" 30#include "TextRun.h" 31 32namespace WebCore { 33 34EllipsisBox::EllipsisBox(RenderBlockFlow& renderer, const AtomicString& ellipsisStr, InlineFlowBox* parent, int width, int height, int y, bool firstLine, bool isVertical, InlineBox* markupBox) 35 : InlineElementBox(renderer, FloatPoint(0, y), width, firstLine, true, false, false, isVertical, 0, 0, parent) 36 , m_shouldPaintMarkupBox(markupBox) 37 , m_height(height) 38 , m_str(ellipsisStr) 39 , m_selectionState(RenderObject::SelectionNone) 40{ 41} 42 43void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 44{ 45 GraphicsContext* context = paintInfo.context; 46 const RenderStyle& lineStyle = this->lineStyle(); 47 Color textColor = lineStyle.visitedDependentColor(CSSPropertyWebkitTextFillColor); 48 if (textColor != context->fillColor()) 49 context->setFillColor(textColor, lineStyle.colorSpace()); 50 bool setShadow = false; 51 if (lineStyle.textShadow()) { 52 context->setShadow(LayoutSize(lineStyle.textShadow()->x(), lineStyle.textShadow()->y()), 53 lineStyle.textShadow()->radius(), lineStyle.textShadow()->color(), lineStyle.colorSpace()); 54 setShadow = true; 55 } 56 57 const Font& font = lineStyle.font(); 58 if (selectionState() != RenderObject::SelectionNone) { 59 paintSelection(context, paintOffset, lineStyle, font); 60 61 // Select the correct color for painting the text. 62 Color foreground = paintInfo.forceBlackText() ? Color::black : blockFlow().selectionForegroundColor(); 63 if (foreground.isValid() && foreground != textColor) 64 context->setFillColor(foreground, lineStyle.colorSpace()); 65 } 66 67 // FIXME: Why is this always LTR? Fix by passing correct text run flags below. 68 context->drawText(font, RenderBlock::constructTextRun(&blockFlow(), font, m_str, lineStyle, TextRun::AllowTrailingExpansion), LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + lineStyle.fontMetrics().ascent())); 69 70 // Restore the regular fill color. 71 if (textColor != context->fillColor()) 72 context->setFillColor(textColor, lineStyle.colorSpace()); 73 74 if (setShadow) 75 context->clearShadow(); 76 77 paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, lineStyle); 78} 79 80InlineBox* EllipsisBox::markupBox() const 81{ 82 if (!m_shouldPaintMarkupBox) 83 return 0; 84 85 RootInlineBox* lastLine = blockFlow().lineAtIndex(blockFlow().lineCount() - 1); 86 if (!lastLine) 87 return 0; 88 89 // If the last line-box on the last line of a block is a link, -webkit-line-clamp paints that box after the ellipsis. 90 // It does not actually move the link. 91 InlineBox* anchorBox = lastLine->lastChild(); 92 if (!anchorBox || !anchorBox->renderer().style().isLink()) 93 return 0; 94 95 return anchorBox; 96} 97 98void EllipsisBox::paintMarkupBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, const RenderStyle& style) 99{ 100 InlineBox* markupBox = this->markupBox(); 101 if (!markupBox) 102 return; 103 104 LayoutPoint adjustedPaintOffset = paintOffset; 105 adjustedPaintOffset.move(x() + m_logicalWidth - markupBox->x(), 106 y() + style.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent())); 107 markupBox->paint(paintInfo, adjustedPaintOffset, lineTop, lineBottom); 108} 109 110IntRect EllipsisBox::selectionRect() 111{ 112 const RenderStyle& lineStyle = this->lineStyle(); 113 const Font& font = lineStyle.font(); 114 const RootInlineBox& rootBox = root(); 115 // FIXME: Why is this always LTR? Fix by passing correct text run flags below. 116 LayoutRect selectionRect = LayoutRect(x(), y() + rootBox.selectionTopAdjustedForPrecedingBlock(), 0, rootBox.selectionHeightAdjustedForPrecedingBlock()); 117 font.adjustSelectionRectForText(RenderBlock::constructTextRun(&blockFlow(), font, m_str, lineStyle, TextRun::AllowTrailingExpansion), selectionRect); 118 // FIXME: use directional pixel snapping instead. 119 return enclosingIntRect(selectionRect); 120} 121 122void EllipsisBox::paintSelection(GraphicsContext* context, const LayoutPoint& paintOffset, const RenderStyle& style, const Font& font) 123{ 124 Color textColor = style.visitedDependentColor(CSSPropertyColor); 125 Color c = blockFlow().selectionBackgroundColor(); 126 if (!c.isValid() || !c.alpha()) 127 return; 128 129 // If the text color ends up being the same as the selection background, invert the selection 130 // background. 131 if (textColor == c) 132 c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); 133 134 const RootInlineBox& rootBox = root(); 135 GraphicsContextStateSaver stateSaver(*context); 136 // FIXME: Why is this always LTR? Fix by passing correct text run flags below. 137 LayoutRect selectionRect = LayoutRect(x() + paintOffset.x(), y() + paintOffset.y() + rootBox.selectionTop(), 0, rootBox.selectionHeight()); 138 TextRun run = RenderBlock::constructTextRun(&blockFlow(), font, m_str, style, TextRun::AllowTrailingExpansion); 139 font.adjustSelectionRectForText(run, selectionRect, 0, -1); 140 context->fillRect(directionalPixelSnappedForPainting(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), c, style.colorSpace()); 141} 142 143bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 144{ 145 LayoutPoint adjustedLocation = accumulatedOffset + roundedLayoutPoint(topLeft()); 146 147 // Hit test the markup box. 148 if (InlineBox* markupBox = this->markupBox()) { 149 const RenderStyle& lineStyle = this->lineStyle(); 150 LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x(); 151 LayoutUnit mty = adjustedLocation.y() + lineStyle.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent()); 152 if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom)) { 153 blockFlow().updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty)); 154 return true; 155 } 156 } 157 158 LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height)); 159 if (visibleToHitTesting() && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) { 160 blockFlow().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); 161 if (!result.addNodeToRectBasedTestResult(blockFlow().element(), request, locationInContainer, boundsRect)) 162 return true; 163 } 164 165 return false; 166} 167 168} // namespace WebCore 169