3 * Copyright (C) 2006 Apple Computer, Inc.
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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
23 #include "RenderFileUploadControl.h"
25 #include "FrameView.h"
26 #include "GraphicsContext.h"
27 #include "HTMLInputElement.h"
28 #include "HTMLNames.h"
30 #include "LocalizedStrings.h"
31 #include "RenderButton.h"
32 #include "RenderText.h"
33 #include "RenderTheme.h"
34 #include "RenderView.h"
35 #include "TextStyle.h"
42 const int afterButtonSpacing = 4;
43 const int iconHeight = 16;
44 const int iconWidth = 16;
45 const int iconFilenameSpacing = 2;
46 const int defaultWidthNumChars = 34;
48 using namespace HTMLNames;
50 class HTMLFileUploadInnerButtonElement : public HTMLInputElement {
52 HTMLFileUploadInnerButtonElement(Document*, Node* shadowParent = 0);
54 virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
56 virtual bool isShadowNode() const { return true; }
58 virtual Node* shadowParentNode() { return m_shadowParent; }
64 RenderFileUploadControl::RenderFileUploadControl(Node* node)
67 , m_fileChooser(FileChooser::create(document(), this))
71 RenderFileUploadControl::~RenderFileUploadControl()
76 m_fileChooser->disconnectUploadControl();
79 void RenderFileUploadControl::setStyle(RenderStyle* newStyle)
81 // Force text-align to match the direction
82 if (newStyle->direction() == LTR)
83 newStyle->setTextAlign(LEFT);
85 newStyle->setTextAlign(RIGHT);
87 RenderBlock::setStyle(newStyle);
89 m_button->renderer()->setStyle(createButtonStyle(newStyle));
91 setReplaced(isInline());
94 void RenderFileUploadControl::valueChanged()
96 static_cast<HTMLInputElement*>(node())->setValueFromRenderer(m_fileChooser->filename());
97 static_cast<HTMLInputElement*>(node())->onChange();
101 void RenderFileUploadControl::click(bool sendMouseEvents)
103 m_fileChooser->openFileChooser();
106 void RenderFileUploadControl::updateFromElement()
109 m_button = new HTMLFileUploadInnerButtonElement(document(), node());
110 RenderStyle* buttonStyle = createButtonStyle(style());
111 m_button->setRenderer(m_button->createRenderer(renderArena(), buttonStyle));
112 m_button->renderer()->setStyle(buttonStyle);
113 static_cast<RenderButton*>(m_button->renderer())->setText(fileButtonChooseFileLabel());
114 m_button->setAttached();
115 m_button->setInDocument(true);
117 addChild(m_button->renderer());
121 int RenderFileUploadControl::maxFilenameWidth()
123 return max(0, contentWidth() - m_button->renderer()->width() - afterButtonSpacing - (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0));
126 RenderStyle* RenderFileUploadControl::createButtonStyle(RenderStyle* parentStyle)
128 RenderStyle* style = getPseudoStyle(RenderStyle::FILE_UPLOAD_BUTTON);
130 style = new (renderArena()) RenderStyle();
133 style->inheritFrom(parentStyle);
135 // Button text will wrap on file upload controls with widths smaller than the intrinsic button width
136 // without this setWhiteSpace.
137 style->setWhiteSpace(NOWRAP);
142 void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty)
144 const int buttonShadowHeight = 2;
147 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
148 IntRect clipRect(tx + borderLeft(), ty + borderTop(),
149 width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight);
150 if (clipRect.width() == 0 || clipRect.height() == 0)
152 paintInfo.context->save();
153 paintInfo.context->clip(clipRect);
156 if (paintInfo.phase == PaintPhaseForeground) {
157 const String& displayedFilename = m_fileChooser->basenameForWidth(maxFilenameWidth());
158 TextRun textRun(displayedFilename.characters(), displayedFilename.length());
160 // Determine where the filename should be placed
161 int contentLeft = tx + borderLeft() + paddingLeft();
162 int buttonAndIconWidth = m_button->renderer()->width() + afterButtonSpacing + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0);
164 if (style()->direction() == LTR)
165 textX = contentLeft + buttonAndIconWidth;
167 textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun);
168 // We want to match the button's baseline
169 RenderButton* buttonRenderer = static_cast<RenderButton*>(m_button->renderer());
170 int textY = buttonRenderer->absoluteBoundingBoxRect().y() + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() + buttonRenderer->baselinePosition(true, false);
172 paintInfo.context->setFont(style()->font());
173 paintInfo.context->setPen(style()->color());
176 paintInfo.context->drawText(textRun, IntPoint(textX, textY));
178 if (m_fileChooser->icon()) {
179 // Determine where the icon should be placed
180 int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2;
182 if (style()->direction() == LTR)
183 iconX = contentLeft + m_button->renderer()->width() + afterButtonSpacing;
185 iconX = contentLeft + contentWidth() - m_button->renderer()->width() - afterButtonSpacing - iconWidth;
187 // Draw the file icon
188 m_fileChooser->icon()->paint(paintInfo.context, IntRect(iconX, iconY, iconWidth, iconHeight));
192 // Paint the children.
193 RenderBlock::paintObject(paintInfo, tx, ty);
196 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds)
197 paintInfo.context->restore();
200 void RenderFileUploadControl::calcMinMaxWidth()
205 if (style()->width().isFixed() && style()->width().value() > 0)
206 m_minWidth = m_maxWidth = calcContentBoxWidth(style()->width().value());
208 // Figure out how big the filename space needs to be for a given number of characters
209 // (using "0" as the nominal character).
210 const UChar ch = '0';
211 float charWidth = style()->font().floatWidth(TextRun(&ch, 1), TextStyle(0, 0, 0, false, false, false));
212 m_maxWidth = (int)ceilf(charWidth * defaultWidthNumChars);
215 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
216 m_maxWidth = max(m_maxWidth, calcContentBoxWidth(style()->minWidth().value()));
217 m_minWidth = max(m_minWidth, calcContentBoxWidth(style()->minWidth().value()));
218 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
221 m_minWidth = m_maxWidth;
223 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
224 m_maxWidth = min(m_maxWidth, calcContentBoxWidth(style()->maxWidth().value()));
225 m_minWidth = min(m_minWidth, calcContentBoxWidth(style()->maxWidth().value()));
228 int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
235 HTMLFileUploadInnerButtonElement::HTMLFileUploadInnerButtonElement(Document* doc, Node* shadowParent)
236 : HTMLInputElement(doc)
237 , m_shadowParent(shadowParent)
239 setInputType("button");
242 RenderObject* HTMLFileUploadInnerButtonElement::createRenderer(RenderArena* arena, RenderStyle* style)
244 return HTMLInputElement::createRenderer(arena, style);
247 } // namespace WebCore