Commit fc93977b authored by hyatt's avatar hyatt
Browse files

Meant to land this ages ago. Make radio buttons work dynamically

	when name and type change (make them pick up the correct new
	group).

	Also fix a bug in the tokenizer where trailing spaces/newlines at
	the end of a document were getting lost.  Messed up .innerHTML
	on DHTML sites.

        Reviewed by darin

        * khtml/css/html4.css:
        * khtml/html/html_formimpl.cpp:
        (DOM::HTMLFormElementImpl::radioButtonChecked):
        (DOM::HTMLGenericFormElementImpl::name):
        (DOM::HTMLInputElementImpl::name):
        (DOM::HTMLInputElementImpl::setInputType):
        (DOM::HTMLInputElementImpl::parseMappedAttribute):
        (DOM::HTMLInputElementImpl::attach):
        (DOM::HTMLInputElementImpl::preDispatchEventHandler):
        (DOM::HTMLInputElementImpl::postDispatchEventHandler):
        (DOM::HTMLIsIndexElementImpl::HTMLIsIndexElementImpl):
        * khtml/html/html_formimpl.h:
        * khtml/html/htmltokenizer.cpp:
        (khtml::HTMLTokenizer::finish):
        * khtml/xml/dom_nodeimpl.cpp:
        (DOM::NodeImpl::dispatchGenericEvent):
        * khtml/xml/dom_nodeimpl.h:
        (DOM::NodeImpl::preDispatchEventHandler):
        (DOM::NodeImpl::postDispatchEventHandler):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@10579 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 7e9be09c
2005-09-20 David Hyatt <hyatt@apple.com>
Meant to land this ages ago. Make radio buttons work dynamically
when name and type change (make them pick up the correct new
group).
Also fix a bug in the tokenizer where trailing spaces/newlines at
the end of a document were getting lost. Messed up .innerHTML
on DHTML sites.
Reviewed by darin
* khtml/css/html4.css:
* khtml/html/html_formimpl.cpp:
(DOM::HTMLFormElementImpl::radioButtonChecked):
(DOM::HTMLGenericFormElementImpl::name):
(DOM::HTMLInputElementImpl::name):
(DOM::HTMLInputElementImpl::setInputType):
(DOM::HTMLInputElementImpl::parseMappedAttribute):
(DOM::HTMLInputElementImpl::attach):
(DOM::HTMLInputElementImpl::preDispatchEventHandler):
(DOM::HTMLInputElementImpl::postDispatchEventHandler):
(DOM::HTMLIsIndexElementImpl::HTMLIsIndexElementImpl):
* khtml/html/html_formimpl.h:
* khtml/html/htmltokenizer.cpp:
(khtml::HTMLTokenizer::finish):
* khtml/xml/dom_nodeimpl.cpp:
(DOM::NodeImpl::dispatchGenericEvent):
* khtml/xml/dom_nodeimpl.h:
(DOM::NodeImpl::preDispatchEventHandler):
(DOM::NodeImpl::postDispatchEventHandler):
2005-09-20 Eric Seidel <eseidel@apple.com>
Reviewed by mjs.
......
......@@ -273,7 +273,7 @@ fieldset {
display: block;
margin-left: 2px;
margin-right: 2px;
padding: 0.75em 0.625em;
padding: 0.35em 0.75em 0.625em;
border: 2px groove ThreeDFace
}
......
......@@ -684,6 +684,10 @@ void HTMLFormElementImpl::parseMappedAttribute(MappedAttributeImpl *attr)
void HTMLFormElementImpl::radioButtonChecked(HTMLInputElementImpl *caller)
{
// Without a name, there is no group.
if (caller->name().isEmpty())
return;
// Uncheck the currently selected item
if (!m_selectedRadioButtons)
m_selectedRadioButtons = new HashMap<DOMStringImpl*, HTMLInputElementImpl*, PointerHash<DOMStringImpl*> >;
......@@ -985,9 +989,6 @@ HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const
DOMString HTMLGenericFormElementImpl::name() const
{
if (!m_overrideName.isNull())
return m_overrideName;
DOMString n = getAttribute(nameAttr);
return n.isNull() ? "" : n;
}
......@@ -997,11 +998,6 @@ void HTMLGenericFormElementImpl::setName(const DOMString &value)
setAttribute(nameAttr, value);
}
void HTMLGenericFormElementImpl::setOverrideName(const DOMString& value)
{
m_overrideName = value;
}
void HTMLGenericFormElementImpl::onSelect()
{
// ### make this work with new form events architecture
......@@ -1393,6 +1389,11 @@ HTMLInputElementImpl::~HTMLInputElementImpl()
delete m_imageLoader;
}
DOMString HTMLInputElementImpl::name() const
{
return m_name.isNull() ? "" : m_name;
}
bool HTMLInputElementImpl::isKeyboardFocusable() const
{
// If the base class says we can't be focused, then we can stop now.
......@@ -1492,6 +1493,11 @@ void HTMLInputElementImpl::setInputType(const DOMString& t)
}
if (wasAttached)
attach();
// If our type morphs into a radio button and we are checked, then go ahead
// and signal this to the form.
if (m_type == RADIO && checked() && m_form)
m_form->radioButtonChecked(this);
}
}
m_haveType = true;
......@@ -1786,7 +1792,20 @@ bool HTMLInputElementImpl::mapToEntry(const QualifiedName& attrName, MappedAttri
void HTMLInputElementImpl::parseMappedAttribute(MappedAttributeImpl *attr)
{
if (attr->name() == autocompleteAttr) {
if (attr->name() == nameAttr) {
if (m_type == RADIO && checked() && m_form) {
// Remove the radio from its old group.
if (m_form && m_type == RADIO && !m_name.isEmpty())
m_form->removeRadioButtonGroup(m_name.impl());
}
// Update our cached reference to the name.
m_name = attr->value();
// Add it to its new group.
if (m_type == RADIO && checked() && m_form)
m_form->radioButtonChecked(this);
} else if (attr->name() == autocompleteAttr) {
m_autocomplete = strcasecmp( attr->value(), "off" );
} else if (attr->name() == typeAttr) {
setInputType(attr->value());
......@@ -1936,8 +1955,6 @@ void HTMLInputElementImpl::attach()
setAttribute(valueAttr, nvalue);
}
m_defaultChecked = (!getAttribute(checkedAttr).isNull());
m_inited = true;
}
......@@ -2277,12 +2294,68 @@ void HTMLInputElementImpl::focus()
getDocument()->setFocusNode(this);
}
void HTMLInputElementImpl::preDispatchEventHandler(EventImpl *evt)
void* HTMLInputElementImpl::preDispatchEventHandler(EventImpl *evt)
{
if (evt->isMouseEvent() && evt->type() == clickEvent && static_cast<MouseEventImpl*>(evt)->button() == 0) {
if (m_type == CHECKBOX || m_type == RADIO)
// preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
// This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
void* result = 0;
if ((m_type == CHECKBOX || m_type == RADIO) && evt->isMouseEvent() && evt->type() == clickEvent &&
static_cast<MouseEventImpl*>(evt)->button() == 0) {
if (m_type == CHECKBOX) {
// As a way to store the boolean, we return our node pointer if we were checked and 0 if we were unchecked.
if (checked()) {
ref();
result = this;
}
setChecked(!checked());
} else {
// For radio buttons, store the current selected radio object.
if (name().isEmpty() || checked() || !form())
return 0; // Unnamed radio buttons dont get checked. Checked buttons just stay checked.
// FIXME: Need to learn to work without a form.
// We really want radio groups to end up in sane states, i.e., to have something checked.
// Therefore if nothing is currently selected, we won't allow this action to be "undone", since
// we want some object in the radio group to actually get selected.
HTMLInputElementImpl* currRadio = form()->checkedRadioButtonForGroup(name().impl());
if (currRadio) {
// We have a radio button selected that is not us. Cache it in our result field and ref it so
// that it can't be destroyed.
currRadio->ref();
result = currRadio;
}
setChecked(true);
}
}
return result;
}
void HTMLInputElementImpl::postDispatchEventHandler(EventImpl *evt, void* data)
{
HTMLInputElementImpl* input = static_cast<HTMLInputElementImpl*>(data);
if ((m_type == CHECKBOX || m_type == RADIO) && evt->isMouseEvent() && evt->type() == clickEvent &&
static_cast<MouseEventImpl*>(evt)->button() == 0) {
if (m_type == CHECKBOX) {
// Reverse the checking we did in preDispatch.
if (evt->propagationStopped() || evt->defaultPrevented() || evt->defaultHandled())
setChecked(input);
} else if (input) {
if (evt->propagationStopped() || evt->defaultPrevented() || evt->defaultHandled()) {
// Restore the original selected radio button if possible.
// Make sure it is still a radio button and only do the restoration if it still
// belongs to our group.
if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
// Ok, the old radio button is still in our form and in our group and is still a
// radio button, so it's safe to restore selection to it.
input->setChecked(true);
}
}
}
}
if (input)
input->deref();
}
void HTMLInputElementImpl::defaultEventHandler(EventImpl *evt)
......@@ -3747,7 +3820,7 @@ HTMLIsIndexElementImpl::HTMLIsIndexElementImpl(DocumentPtr *doc, HTMLFormElement
: HTMLInputElementImpl(isindexTag, doc, f)
{
m_type = TEXT;
setOverrideName("isindex");
m_name = "isindex";
}
void HTMLIsIndexElementImpl::parseMappedAttribute(MappedAttributeImpl* attr)
......
......@@ -202,11 +202,9 @@ public:
virtual void recalcStyle( StyleChange );
DOMString name() const;
virtual DOMString name() const;
void setName(const DOMString& name);
void setOverrideName(const DOMString& name);
virtual bool isGenericFormElement() const { return true; }
/*
......@@ -232,7 +230,6 @@ public:
protected:
HTMLFormElementImpl *getForm() const;
DOMString m_overrideName;
HTMLFormElementImpl *m_form;
bool m_disabled : 1;
bool m_readOnly: 1;
......@@ -343,6 +340,8 @@ public:
virtual bool isKeyboardFocusable() const;
virtual bool isEnumeratable() const { return inputType() != IMAGE; }
virtual DOMString name() const;
bool autoComplete() const { return m_autocomplete; }
virtual bool isChecked() const { return checked(); }
......@@ -405,7 +404,8 @@ public:
int clickX() const { return xPos; }
int clickY() const { return yPos; }
virtual void preDispatchEventHandler(EventImpl *evt);
virtual void* preDispatchEventHandler(EventImpl *evt);
virtual void postDispatchEventHandler(EventImpl *evt, void* data);
virtual void defaultEventHandler(EventImpl *evt);
virtual bool isEditable();
......@@ -446,6 +446,7 @@ public:
protected:
bool storesValueSeparateFromAttribute() const;
AtomicString m_name;
DOMString m_value;
int xPos;
short m_maxLen;
......
......@@ -1817,6 +1817,8 @@ void HTMLTokenizer::finish()
// this indicates we will not receive any more data... but if we are waiting on
// an external script to load, we can't finish parsing until that is done
noMoreData = true;
if (pending) // Flush any remaining whitespace.
addPending();
if (!inWrite && !loadingExtScript && !m_executingScript && !onHold && !timerId)
end(); // this actually causes us to be deleted
}
......
......@@ -564,10 +564,9 @@ bool NodeImpl::dispatchGenericEvent( EventImpl *evt, int &/*exceptioncode */)
QPtrListIterator<NodeImpl> it(nodeChain);
// Before we begin dispatching events, give each node a chance to do some work prior
// Before we begin dispatching events, give the target node a chance to do some work prior
// to the DOM event handlers getting a crack.
for (; it.current() && !evt->propagationStopped(); ++it)
it.current()->preDispatchEventHandler(evt);
void* data = preDispatchEventHandler(evt);
// trigger any capturing event handlers on our way down
evt->setEventPhase(Event::CAPTURING_PHASE);
......@@ -613,6 +612,9 @@ bool NodeImpl::dispatchGenericEvent( EventImpl *evt, int &/*exceptioncode */)
// anything about the default event handler phase.
// Now call the post dispatch.
postDispatchEventHandler(evt, data);
if (evt->bubbles()) {
// now we call all default event handlers (this is not part of DOM - it is internal to khtml)
......
......@@ -290,8 +290,10 @@ public:
void handleLocalEvents(EventImpl *evt, bool useCapture);
// A handler to do actions before an event is dispatched.
virtual void preDispatchEventHandler(EventImpl *evt) {};
// Handlers to do/undo actions on the target node before an event is dispatched to it and after the event
// has been dispatched. The data pointer is handed back by the preDispatch and passed to postDispatch.
virtual void* preDispatchEventHandler(EventImpl *evt) { return 0; }
virtual void postDispatchEventHandler(EventImpl *evt, void* data) {}
/**
* Perform the default action for an event e.g. submitting a form
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment