2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
23 #include "RenderTextControl.h"
25 #include "HTMLTextFormControlElement.h"
26 #include "HitTestResult.h"
27 #include "RenderText.h"
28 #include "RenderTheme.h"
29 #include "ScrollbarTheme.h"
30 #include "TextIterator.h"
31 #include "VisiblePosition.h"
32 #include <wtf/unicode/CharacterNames.h>
34 #if ENABLE(TIZEN_ISF_PORT)
35 #include "EditorClient.h"
42 RenderTextControl::RenderTextControl(Node* node)
45 ASSERT(toTextFormControl(node));
48 RenderTextControl::~RenderTextControl()
52 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
54 return static_cast<HTMLTextFormControlElement*>(node());
57 HTMLElement* RenderTextControl::innerTextElement() const
59 return textFormControlElement()->innerTextElement();
62 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
64 RenderBlock::styleDidChange(diff, oldStyle);
65 Element* innerText = innerTextElement();
68 RenderBlock* innerTextRenderer = toRenderBlock(innerText->renderer());
69 if (innerTextRenderer) {
70 #if ENABLE(TIZEN_ISF_PORT)
71 EUserModify userModify = innerTextRenderer->style()->userModify();
73 // We may have set the width and the height in the old style in layout().
74 // Reset them now to avoid getting a spurious layout hint.
75 innerTextRenderer->style()->setHeight(Length());
76 innerTextRenderer->style()->setWidth(Length());
77 innerTextRenderer->setStyle(createInnerTextStyle(style()));
78 innerText->setNeedsStyleRecalc();
79 #if ENABLE(TIZEN_ISF_PORT)
80 if (userModify != innerTextRenderer->style()->userModify() && frame()->editor()->client())
81 frame()->editor()->client()->respondToChangedSelection(frame());
84 textFormControlElement()->updatePlaceholderVisibility(false);
87 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
89 bool isEnabled = true;
90 bool isReadOnlyControl = false;
92 if (node->isElementNode()) {
93 Element* element = static_cast<Element*>(node);
94 isEnabled = element->isEnabledFormControl();
95 isReadOnlyControl = element->isReadOnlyFormControl();
98 style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
102 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
104 // The inner block, if present, always has its direction set to LTR,
105 // so we need to inherit the direction and unicode-bidi style from the element.
106 textBlockStyle->setDirection(style()->direction());
107 textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
109 bool disabled = updateUserModifyProperty(node(), textBlockStyle);
111 textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
114 int RenderTextControl::textBlockHeight() const
116 return height() - borderAndPaddingHeight();
119 int RenderTextControl::textBlockWidth() const
121 Element* innerText = innerTextElement();
123 return width() - borderAndPaddingWidth() - innerText->renderBox()->paddingLeft() - innerText->renderBox()->paddingRight();
126 void RenderTextControl::updateFromElement()
128 Element* innerText = innerTextElement();
130 updateUserModifyProperty(node(), innerText->renderer()->style());
133 VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
136 return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
137 ExceptionCode ec = 0;
138 RefPtr<Range> range = Range::create(document());
139 range->selectNodeContents(innerTextElement(), ec);
141 CharacterIterator it(range.get());
142 it.advance(index - 1);
143 return VisiblePosition(it.range()->endPosition(), UPSTREAM);
146 int RenderTextControl::scrollbarThickness() const
148 // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
149 return ScrollbarTheme::theme()->scrollbarThickness();
152 void RenderTextControl::computeLogicalHeight()
154 HTMLElement* innerText = innerTextElement();
156 RenderBox* innerTextBox = innerText->renderBox();
157 LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
158 setHeight(computeControlHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight());
160 // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
161 if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))
162 setHeight(height() + scrollbarThickness());
164 RenderBlock::computeLogicalHeight();
167 void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
169 LayoutPoint adjustedLocation = accumulatedOffset + location();
170 HTMLElement* innerText = innerTextElement();
171 result.setInnerNode(innerText);
172 result.setInnerNonSharedNode(innerText);
173 result.setLocalPoint(pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location()));
176 static const char* fontFamiliesWithInvalidCharWidth[] = {
177 "American Typewriter",
213 // For font families where any of the fonts don't have a valid entry in the OS/2 table
214 // for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
215 // from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
216 // but, in order to get similar rendering across platforms, we do this check for
218 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
220 static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
222 if (family.isEmpty())
225 if (!fontFamiliesWithInvalidCharWidthMap) {
226 fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
228 for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
229 fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
232 return !fontFamiliesWithInvalidCharWidthMap->contains(family);
235 float RenderTextControl::getAvgCharWidth(AtomicString family)
237 if (hasValidAvgCharWidth(family))
238 return roundf(style()->font().primaryFont()->avgCharWidth());
240 const UChar ch = '0';
241 const String str = String(&ch, 1);
242 const Font& font = style()->font();
243 TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
244 textRun.disableRoundingHacks();
245 return font.width(textRun);
248 float RenderTextControl::scaleEmToUnits(int x) const
250 // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
251 float unitsPerEm = 2048.0f;
252 return roundf(style()->font().size() * x / unitsPerEm);
255 void RenderTextControl::computePreferredLogicalWidths()
257 ASSERT(preferredLogicalWidthsDirty());
259 m_minPreferredLogicalWidth = 0;
260 m_maxPreferredLogicalWidth = 0;
262 if (style()->width().isFixed() && style()->width().value() >= 0)
263 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
265 // Use average character width. Matches IE.
266 AtomicString family = style()->font().family().family();
267 RenderBox* innerTextRenderBox = innerTextElement()->renderBox();
268 m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)) + innerTextRenderBox->paddingLeft() + innerTextRenderBox->paddingRight();
271 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
272 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
273 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
274 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
275 m_minPreferredLogicalWidth = 0;
277 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
279 if (style()->maxWidth().isFixed()) {
280 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
281 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
284 LayoutUnit toAdd = borderAndPaddingWidth();
286 m_minPreferredLogicalWidth += toAdd;
287 m_maxPreferredLogicalWidth += toAdd;
289 setPreferredLogicalWidthsDirty(false);
292 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset)
294 if (!size().isEmpty())
295 rects.append(pixelSnappedIntRect(additionalOffset, size()));
298 RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
300 HTMLElement* placeholder = toTextFormControl(node())->placeholderElement();
301 RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
302 if (!placeholderRenderer)
304 if (relayoutChildren) {
305 // The markParents arguments should be false because this function is
306 // called from layout() of the parent and the placeholder layout doesn't
307 // affect the parent layout.
308 placeholderRenderer->setChildNeedsLayout(true, MarkOnlyThis);
310 return placeholderRenderer;
313 } // namespace WebCore