|
1 /* |
|
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. |
|
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 |
|
21 #include "config.h" |
|
22 #include "RenderFileUploadControl.h" |
|
23 |
|
24 #include "FrameView.h" |
|
25 #include "GraphicsContext.h" |
|
26 #include "HTMLInputElement.h" |
|
27 #include "HTMLNames.h" |
|
28 #include "Icon.h" |
|
29 #include "LocalizedStrings.h" |
|
30 #include "RenderButton.h" |
|
31 #include "RenderText.h" |
|
32 #include "RenderTheme.h" |
|
33 #include "RenderView.h" |
|
34 #include "TextStyle.h" |
|
35 #include <math.h> |
|
36 |
|
37 using namespace std; |
|
38 |
|
39 namespace WebCore { |
|
40 |
|
41 using namespace HTMLNames; |
|
42 |
|
43 const int afterButtonSpacing = 4; |
|
44 const int iconHeight = 16; |
|
45 const int iconWidth = 16; |
|
46 const int iconFilenameSpacing = 2; |
|
47 const int defaultWidthNumChars = 34; |
|
48 const int buttonShadowHeight = 2; |
|
49 |
|
50 class HTMLFileUploadInnerButtonElement : public HTMLInputElement { |
|
51 public: |
|
52 HTMLFileUploadInnerButtonElement(Document*, Node* shadowParent); |
|
53 |
|
54 virtual bool isShadowNode() const { return true; } |
|
55 virtual Node* shadowParentNode() { return m_shadowParent; } |
|
56 |
|
57 private: |
|
58 Node* m_shadowParent; |
|
59 }; |
|
60 |
|
61 RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input) |
|
62 : RenderBlock(input) |
|
63 , m_button(0) |
|
64 , m_fileChooser(FileChooser::create(this, input->value())) |
|
65 { |
|
66 } |
|
67 |
|
68 RenderFileUploadControl::~RenderFileUploadControl() |
|
69 { |
|
70 if (m_button) |
|
71 m_button->detach(); |
|
72 m_fileChooser->disconnectClient(); |
|
73 } |
|
74 |
|
75 void RenderFileUploadControl::setStyle(RenderStyle* newStyle) |
|
76 { |
|
77 // Force text-align to match the direction |
|
78 if (newStyle->direction() == LTR) |
|
79 newStyle->setTextAlign(LEFT); |
|
80 else |
|
81 newStyle->setTextAlign(RIGHT); |
|
82 |
|
83 RenderBlock::setStyle(newStyle); |
|
84 if (m_button) |
|
85 m_button->renderer()->setStyle(createButtonStyle(newStyle)); |
|
86 |
|
87 setReplaced(isInline()); |
|
88 } |
|
89 |
|
90 void RenderFileUploadControl::valueChanged() |
|
91 { |
|
92 // onChange may destroy this renderer |
|
93 RefPtr<FileChooser> fileChooser = m_fileChooser; |
|
94 |
|
95 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node()); |
|
96 inputElement->setValueFromRenderer(fileChooser->filename()); |
|
97 inputElement->onChange(); |
|
98 |
|
99 // only repaint if it doesn't seem we have been destroyed |
|
100 if (!fileChooser->disconnected()) |
|
101 repaint(); |
|
102 } |
|
103 |
|
104 void RenderFileUploadControl::click() |
|
105 { |
|
106 m_fileChooser->openFileChooser(node()->document()); |
|
107 } |
|
108 |
|
109 void RenderFileUploadControl::updateFromElement() |
|
110 { |
|
111 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node()); |
|
112 |
|
113 if (!m_button) { |
|
114 m_button = new HTMLFileUploadInnerButtonElement(document(), inputElement); |
|
115 m_button->setInputType("button"); |
|
116 m_button->setValue(fileButtonChooseFileLabel()); |
|
117 RenderStyle* buttonStyle = createButtonStyle(style()); |
|
118 RenderObject* renderer = m_button->createRenderer(renderArena(), buttonStyle); |
|
119 m_button->setRenderer(renderer); |
|
120 renderer->setStyle(buttonStyle); |
|
121 renderer->updateFromElement(); |
|
122 m_button->setAttached(); |
|
123 m_button->setInDocument(true); |
|
124 |
|
125 addChild(renderer); |
|
126 } |
|
127 |
|
128 m_button->setDisabled(!theme()->isEnabled(this)); |
|
129 |
|
130 // This only supports clearing out the filename, but that's OK because for |
|
131 // security reasons that's the only change the DOM is allowed to make. |
|
132 if (inputElement->value().isEmpty() && !m_fileChooser->filename().isEmpty()) { |
|
133 m_fileChooser->clear(); |
|
134 repaint(); |
|
135 } |
|
136 } |
|
137 |
|
138 int RenderFileUploadControl::maxFilenameWidth() const |
|
139 { |
|
140 return max(0, contentWidth() - m_button->renderer()->width() - afterButtonSpacing |
|
141 - (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0)); |
|
142 } |
|
143 |
|
144 RenderStyle* RenderFileUploadControl::createButtonStyle(RenderStyle* parentStyle) const |
|
145 { |
|
146 RenderStyle* style = getPseudoStyle(RenderStyle::FILE_UPLOAD_BUTTON); |
|
147 if (!style) { |
|
148 style = new (renderArena()) RenderStyle; |
|
149 if (parentStyle) |
|
150 style->inheritFrom(parentStyle); |
|
151 } |
|
152 |
|
153 // Button text will wrap on file upload controls with widths smaller than the intrinsic button width |
|
154 // without this setWhiteSpace. |
|
155 style->setWhiteSpace(NOWRAP); |
|
156 |
|
157 return style; |
|
158 } |
|
159 |
|
160 void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) |
|
161 { |
|
162 if (style()->visibility() != VISIBLE) |
|
163 return; |
|
164 |
|
165 // Push a clip. |
|
166 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { |
|
167 IntRect clipRect(tx + borderLeft(), ty + borderTop(), |
|
168 width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight); |
|
169 #if PLATFORM(SYMBIAN) |
|
170 clipRect = intersection(clipRect, paintInfo.rect); |
|
171 #endif |
|
172 if (clipRect.isEmpty()) |
|
173 return; |
|
174 paintInfo.context->save(); |
|
175 paintInfo.context->clip(clipRect); |
|
176 } |
|
177 |
|
178 if (paintInfo.phase == PaintPhaseForeground) { |
|
179 const String& displayedFilename = m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth()); |
|
180 unsigned length = displayedFilename.length(); |
|
181 const UChar* string = displayedFilename.characters(); |
|
182 TextStyle textStyle(0, 0, 0, style()->direction() == RTL, style()->unicodeBidi() == Override); |
|
183 TextRun textRun(string, length); |
|
184 |
|
185 // Determine where the filename should be placed |
|
186 int contentLeft = tx + borderLeft() + paddingLeft(); |
|
187 int buttonAndIconWidth = m_button->renderer()->width() + afterButtonSpacing |
|
188 + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0); |
|
189 int textX; |
|
190 if (style()->direction() == LTR) |
|
191 textX = contentLeft + buttonAndIconWidth; |
|
192 else |
|
193 textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun); |
|
194 // We want to match the button's baseline |
|
195 RenderButton* buttonRenderer = static_cast<RenderButton*>(m_button->renderer()); |
|
196 int textY = buttonRenderer->absoluteBoundingBoxRect().y() |
|
197 + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() |
|
198 + buttonRenderer->baselinePosition(true, false); |
|
199 |
|
200 paintInfo.context->setFont(style()->font()); |
|
201 paintInfo.context->setFillColor(style()->color()); |
|
202 |
|
203 // Draw the filename |
|
204 paintInfo.context->drawBidiText(textRun, IntPoint(textX, textY), textStyle); |
|
205 |
|
206 if (m_fileChooser->icon()) { |
|
207 // Determine where the icon should be placed |
|
208 int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2; |
|
209 int iconX; |
|
210 if (style()->direction() == LTR) |
|
211 iconX = contentLeft + m_button->renderer()->width() + afterButtonSpacing; |
|
212 else |
|
213 iconX = contentLeft + contentWidth() - m_button->renderer()->width() - afterButtonSpacing - iconWidth; |
|
214 |
|
215 // Draw the file icon |
|
216 m_fileChooser->icon()->paint(paintInfo.context, IntRect(iconX, iconY, iconWidth, iconHeight)); |
|
217 } |
|
218 } |
|
219 |
|
220 // Paint the children. |
|
221 RenderBlock::paintObject(paintInfo, tx, ty); |
|
222 |
|
223 // Pop the clip. |
|
224 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) |
|
225 paintInfo.context->restore(); |
|
226 } |
|
227 |
|
228 void RenderFileUploadControl::calcPrefWidths() |
|
229 { |
|
230 ASSERT(prefWidthsDirty()); |
|
231 |
|
232 m_minPrefWidth = 0; |
|
233 m_maxPrefWidth = 0; |
|
234 |
|
235 if (style()->width().isFixed() && style()->width().value() > 0) |
|
236 m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); |
|
237 else { |
|
238 // Figure out how big the filename space needs to be for a given number of characters |
|
239 // (using "0" as the nominal character). |
|
240 const UChar ch = '0'; |
|
241 float charWidth = style()->font().floatWidth(TextRun(&ch, 1), TextStyle(0, 0, 0, false, false, false)); |
|
242 m_maxPrefWidth = (int)ceilf(charWidth * defaultWidthNumChars); |
|
243 } |
|
244 |
|
245 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { |
|
246 m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); |
|
247 m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); |
|
248 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) |
|
249 m_minPrefWidth = 0; |
|
250 else |
|
251 m_minPrefWidth = m_maxPrefWidth; |
|
252 |
|
253 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { |
|
254 m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); |
|
255 m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); |
|
256 } |
|
257 |
|
258 int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); |
|
259 m_minPrefWidth += toAdd; |
|
260 m_maxPrefWidth += toAdd; |
|
261 |
|
262 setPrefWidthsDirty(false); |
|
263 } |
|
264 |
|
265 void RenderFileUploadControl::receiveDroppedFile(const String& filename) |
|
266 { |
|
267 m_fileChooser->chooseFile(filename); |
|
268 } |
|
269 |
|
270 HTMLFileUploadInnerButtonElement::HTMLFileUploadInnerButtonElement(Document* doc, Node* shadowParent) |
|
271 : HTMLInputElement(doc) |
|
272 , m_shadowParent(shadowParent) |
|
273 { |
|
274 } |
|
275 |
|
276 } // namespace WebCore |