2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Dirk Mueller (mueller@kde.org)
7 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
28 #include "HTMLTextAreaElement.h"
32 #include "EventNames.h"
33 #include "FormDataList.h"
35 #include "HTMLNames.h"
36 #include "RenderTextControl.h"
38 #include "RenderStyle.h"
42 using namespace EventNames;
43 using namespace HTMLNames;
45 HTMLTextAreaElement::HTMLTextAreaElement(Document *doc, HTMLFormElement *f)
46 : HTMLGenericFormElement(textareaTag, doc, f)
53 setValueMatchesRenderer();
54 document()->registerFormElementWithState(this);
57 HTMLTextAreaElement::~HTMLTextAreaElement()
59 document()->deregisterFormElementWithState(this);
62 const AtomicString& HTMLTextAreaElement::type() const
64 static const AtomicString textarea("textarea");
68 String HTMLTextAreaElement::stateValue() const
73 void HTMLTextAreaElement::restoreState(const String& state)
75 setDefaultValue(state);
78 int HTMLTextAreaElement::selectionStart()
81 if (document()->focusNode() != this && cachedSelStart >= 0)
82 return cachedSelStart;
83 return static_cast<RenderTextControl *>(renderer())->selectionStart();
88 int HTMLTextAreaElement::selectionEnd()
91 if (document()->focusNode() != this && cachedSelEnd >= 0)
93 return static_cast<RenderTextControl *>(renderer())->selectionEnd();
98 void HTMLTextAreaElement::setSelectionStart(int start)
101 static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
104 void HTMLTextAreaElement::setSelectionEnd(int end)
107 static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
110 void HTMLTextAreaElement::select()
113 static_cast<RenderTextControl *>(renderer())->select();
116 void HTMLTextAreaElement::setSelectionRange(int start, int end)
119 static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
122 void HTMLTextAreaElement::childrenChanged()
124 setValue(defaultValue());
127 void HTMLTextAreaElement::parseMappedAttribute(MappedAttribute *attr)
129 if (attr->name() == rowsAttr) {
130 m_rows = !attr->isNull() ? attr->value().toInt() : 3;
132 renderer()->setNeedsLayoutAndMinMaxRecalc();
133 } else if (attr->name() == colsAttr) {
134 m_cols = !attr->isNull() ? attr->value().toInt() : 60;
136 renderer()->setNeedsLayoutAndMinMaxRecalc();
137 } else if (attr->name() == wrapAttr) {
138 // virtual / physical is Netscape extension of HTML 3.0, now deprecated
139 // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4
140 if (equalIgnoringCase(attr->value(), "virtual") || equalIgnoringCase(attr->value(), "soft"))
142 else if (equalIgnoringCase(attr->value(), "physical") || equalIgnoringCase(attr->value(), "hard"))
143 m_wrap = ta_Physical;
144 else if (equalIgnoringCase(attr->value(), "on" ))
145 m_wrap = ta_Physical;
146 else if (equalIgnoringCase(attr->value(), "off"))
149 renderer()->setNeedsLayoutAndMinMaxRecalc();
150 } else if (attr->name() == accesskeyAttr) {
151 // ignore for the moment
152 } else if (attr->name() == onfocusAttr)
153 setHTMLEventListener(focusEvent, attr);
154 else if (attr->name() == onblurAttr)
155 setHTMLEventListener(blurEvent, attr);
156 else if (attr->name() == onselectAttr)
157 setHTMLEventListener(selectEvent, attr);
158 else if (attr->name() == onchangeAttr)
159 setHTMLEventListener(changeEvent, attr);
161 HTMLGenericFormElement::parseMappedAttribute(attr);
164 RenderObject* HTMLTextAreaElement::createRenderer(RenderArena* arena, RenderStyle* style)
166 return new (arena) RenderTextControl(this, true);
169 bool HTMLTextAreaElement::appendFormData(FormDataList& encoding, bool)
171 if (name().isEmpty())
174 bool hardWrap = renderer() && wrap() == ta_Physical;
175 String v = hardWrap ? static_cast<RenderTextControl*>(renderer())->textWithHardLineBreaks() : value();
176 encoding.appendData(name(), v);
180 void HTMLTextAreaElement::reset()
182 setValue(defaultValue());
185 bool HTMLTextAreaElement::isKeyboardFocusable(KeyboardEvent*) const
187 // If text areas can be focused, then they should always be keyboard focusable
188 return HTMLGenericFormElement::isFocusable();
191 bool HTMLTextAreaElement::isMouseFocusable() const
193 return HTMLGenericFormElement::isFocusable();
196 void HTMLTextAreaElement::focus()
198 Document* doc = document();
199 if (doc->focusNode() == this)
202 if (!supportsFocus())
204 doc->setFocusNode(this);
205 // FIXME: Should isFocusable do the updateLayout?
206 if (!isFocusable()) {
207 setNeedsFocusAppearanceUpdate(true);
210 updateFocusAppearance();
213 void HTMLTextAreaElement::updateFocusAppearance()
217 if (cachedSelStart == -1) {
218 ASSERT(cachedSelEnd == -1);
219 // If this is the first focus, set a caret at the end of the text.
220 // This matches other browsers' behavior.
221 int max = static_cast<RenderTextControl*>(renderer())->text().length();
222 setSelectionRange(max, max);
224 // Restore the cached selection. This matches other browsers' behavior.
225 setSelectionRange(cachedSelStart, cachedSelEnd);
227 if (document() && document()->frame())
228 document()->frame()->revealSelection();
231 void HTMLTextAreaElement::defaultEventHandler(Event *evt)
233 if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent))
234 static_cast<RenderTextControl*>(renderer())->forwardEvent(evt);
236 HTMLGenericFormElement::defaultEventHandler(evt);
239 void HTMLTextAreaElement::rendererWillBeDestroyed()
244 void HTMLTextAreaElement::updateValue() const
246 if (!valueMatchesRenderer()) {
248 m_value = static_cast<RenderTextControl*>(renderer())->text();
249 setValueMatchesRenderer();
253 String HTMLTextAreaElement::value() const
259 void HTMLTextAreaElement::setValue(const String& value)
261 // Code elsewhere normalizes line endings added by the user via the keyboard or pasting.
262 // We must normalize line endings coming from JS.
263 DeprecatedString valueWithNormalizedLineEndings = value.deprecatedString();
264 valueWithNormalizedLineEndings.replace("\r\n", "\n");
265 valueWithNormalizedLineEndings.replace("\r", "\n");
267 m_value = valueWithNormalizedLineEndings;
268 setValueMatchesRenderer();
270 renderer()->updateFromElement();
272 // Restore a caret at the starting point of the old selection.
273 // This matches Safari 2.0 behavior.
274 if (document()->focusNode() == this && cachedSelStart >= 0) {
275 ASSERT(cachedSelEnd >= 0);
276 setSelectionRange(cachedSelStart, cachedSelStart);
281 String HTMLTextAreaElement::defaultValue() const
285 // Since there may be comments, ignore nodes other than text nodes.
286 for (Node* n = firstChild(); n; n = n->nextSibling())
288 val += static_cast<Text*>(n)->data();
290 // FIXME: We should only drop the first carriage return for the default
291 // value in the original source, not defaultValues set from JS. This code
293 if (val.length() >= 2 && val[0] == '\r' && val[1] == '\n')
295 else if (val.length() >= 1 && (val[0] == '\r' || val[0] == '\n'))
301 void HTMLTextAreaElement::setDefaultValue(const String& defaultValue)
303 // To preserve comments, remove all the text nodes, then add a single one.
304 Vector<RefPtr<Node> > textNodes;
305 for (Node* n = firstChild(); n; n = n->nextSibling())
308 ExceptionCode ec = 0;
309 size_t size = textNodes.size();
310 for (size_t i = 0; i < size; ++i)
311 removeChild(textNodes[i].get(), ec);
312 insertBefore(document()->createTextNode(defaultValue), firstChild(), ec);
313 setValue(defaultValue);
316 void HTMLTextAreaElement::accessKeyAction(bool sendToAnyElement)
321 String HTMLTextAreaElement::accessKey() const
323 return getAttribute(accesskeyAttr);
326 void HTMLTextAreaElement::setAccessKey(const String& value)
328 setAttribute(accesskeyAttr, value);
331 void HTMLTextAreaElement::setCols(int cols)
333 setAttribute(colsAttr, String::number(cols));
336 void HTMLTextAreaElement::setRows(int rows)
338 setAttribute(rowsAttr, String::number(rows));