OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008, 2009 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 "core/dom/CheckedRadioButtons.h" | |
23 | |
24 #include "core/html/HTMLInputElement.h" | |
25 #include "wtf/HashSet.h" | |
26 | |
27 namespace WebCore { | |
28 | |
29 class RadioButtonGroup { | |
30 WTF_MAKE_FAST_ALLOCATED; | |
31 public: | |
32 static PassOwnPtr<RadioButtonGroup> create(); | |
33 bool isEmpty() const { return m_members.isEmpty(); } | |
34 bool isRequired() const { return m_requiredCount; } | |
35 HTMLInputElement* checkedButton() const { return m_checkedButton; } | |
36 void add(HTMLInputElement*); | |
37 void updateCheckedState(HTMLInputElement*); | |
38 void requiredAttributeChanged(HTMLInputElement*); | |
39 void remove(HTMLInputElement*); | |
40 bool contains(HTMLInputElement*) const; | |
41 | |
42 private: | |
43 RadioButtonGroup(); | |
44 void setNeedsValidityCheckForAllButtons(); | |
45 bool isValid() const; | |
46 void setCheckedButton(HTMLInputElement*); | |
47 | |
48 HashSet<HTMLInputElement*> m_members; | |
49 HTMLInputElement* m_checkedButton; | |
50 size_t m_requiredCount; | |
51 }; | |
52 | |
53 RadioButtonGroup::RadioButtonGroup() | |
54 : m_checkedButton(0) | |
55 , m_requiredCount(0) | |
56 { | |
57 } | |
58 | |
59 PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create() | |
60 { | |
61 return adoptPtr(new RadioButtonGroup); | |
62 } | |
63 | |
64 inline bool RadioButtonGroup::isValid() const | |
65 { | |
66 return !isRequired() || m_checkedButton; | |
67 } | |
68 | |
69 void RadioButtonGroup::setCheckedButton(HTMLInputElement* button) | |
70 { | |
71 HTMLInputElement* oldCheckedButton = m_checkedButton; | |
72 if (oldCheckedButton == button) | |
73 return; | |
74 m_checkedButton = button; | |
75 if (oldCheckedButton) | |
76 oldCheckedButton->setChecked(false); | |
77 } | |
78 | |
79 void RadioButtonGroup::add(HTMLInputElement* button) | |
80 { | |
81 ASSERT(button->isRadioButton()); | |
82 if (!m_members.add(button).isNewEntry) | |
83 return; | |
84 bool groupWasValid = isValid(); | |
85 if (button->isRequired()) | |
86 ++m_requiredCount; | |
87 if (button->checked()) | |
88 setCheckedButton(button); | |
89 | |
90 bool groupIsValid = isValid(); | |
91 if (groupWasValid != groupIsValid) | |
92 setNeedsValidityCheckForAllButtons(); | |
93 else if (!groupIsValid) { | |
94 // A radio button not in a group is always valid. We need to make it | |
95 // invalid only if the group is invalid. | |
96 button->setNeedsValidityCheck(); | |
97 } | |
98 } | |
99 | |
100 void RadioButtonGroup::updateCheckedState(HTMLInputElement* button) | |
101 { | |
102 ASSERT(button->isRadioButton()); | |
103 ASSERT(m_members.contains(button)); | |
104 bool wasValid = isValid(); | |
105 if (button->checked()) | |
106 setCheckedButton(button); | |
107 else { | |
108 if (m_checkedButton == button) | |
109 m_checkedButton = 0; | |
110 } | |
111 if (wasValid != isValid()) | |
112 setNeedsValidityCheckForAllButtons(); | |
113 } | |
114 | |
115 void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button) | |
116 { | |
117 ASSERT(button->isRadioButton()); | |
118 ASSERT(m_members.contains(button)); | |
119 bool wasValid = isValid(); | |
120 if (button->isRequired()) | |
121 ++m_requiredCount; | |
122 else { | |
123 ASSERT(m_requiredCount); | |
124 --m_requiredCount; | |
125 } | |
126 if (wasValid != isValid()) | |
127 setNeedsValidityCheckForAllButtons(); | |
128 } | |
129 | |
130 void RadioButtonGroup::remove(HTMLInputElement* button) | |
131 { | |
132 ASSERT(button->isRadioButton()); | |
133 HashSet<HTMLInputElement*>::iterator it = m_members.find(button); | |
134 if (it == m_members.end()) | |
135 return; | |
136 bool wasValid = isValid(); | |
137 m_members.remove(it); | |
138 if (button->isRequired()) { | |
139 ASSERT(m_requiredCount); | |
140 --m_requiredCount; | |
141 } | |
142 if (m_checkedButton == button) | |
143 m_checkedButton = 0; | |
144 | |
145 if (m_members.isEmpty()) { | |
146 ASSERT(!m_requiredCount); | |
147 ASSERT(!m_checkedButton); | |
148 } else if (wasValid != isValid()) | |
149 setNeedsValidityCheckForAllButtons(); | |
150 if (!wasValid) { | |
151 // A radio button not in a group is always valid. We need to make it | |
152 // valid only if the group was invalid. | |
153 button->setNeedsValidityCheck(); | |
154 } | |
155 } | |
156 | |
157 void RadioButtonGroup::setNeedsValidityCheckForAllButtons() | |
158 { | |
159 typedef HashSet<HTMLInputElement*>::const_iterator Iterator; | |
160 Iterator end = m_members.end(); | |
161 for (Iterator it = m_members.begin(); it != end; ++it) { | |
162 HTMLInputElement* button = *it; | |
163 ASSERT(button->isRadioButton()); | |
164 button->setNeedsValidityCheck(); | |
165 } | |
166 } | |
167 | |
168 bool RadioButtonGroup::contains(HTMLInputElement* button) const | |
169 { | |
170 return m_members.contains(button); | |
171 } | |
172 | |
173 // ---------------------------------------------------------------- | |
174 | |
175 // Explicity define empty constructor and destructor in order to prevent the | |
176 // compiler from generating them as inlines. So we don't need to to define | |
177 // RadioButtonGroup in the header. | |
178 CheckedRadioButtons::CheckedRadioButtons() | |
179 { | |
180 } | |
181 | |
182 CheckedRadioButtons::~CheckedRadioButtons() | |
183 { | |
184 } | |
185 | |
186 void CheckedRadioButtons::addButton(HTMLInputElement* element) | |
187 { | |
188 ASSERT(element->isRadioButton()); | |
189 if (element->name().isEmpty()) | |
190 return; | |
191 | |
192 if (!m_nameToGroupMap) | |
193 m_nameToGroupMap = adoptPtr(new NameToGroupMap); | |
194 | |
195 OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name().impl
(), PassOwnPtr<RadioButtonGroup>()).iterator->value; | |
196 if (!group) | |
197 group = RadioButtonGroup::create(); | |
198 group->add(element); | |
199 } | |
200 | |
201 void CheckedRadioButtons::updateCheckedState(HTMLInputElement* element) | |
202 { | |
203 ASSERT(element->isRadioButton()); | |
204 if (element->name().isEmpty()) | |
205 return; | |
206 ASSERT(m_nameToGroupMap); | |
207 if (!m_nameToGroupMap) | |
208 return; | |
209 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); | |
210 ASSERT(group); | |
211 group->updateCheckedState(element); | |
212 } | |
213 | |
214 void CheckedRadioButtons::requiredAttributeChanged(HTMLInputElement* element) | |
215 { | |
216 ASSERT(element->isRadioButton()); | |
217 if (element->name().isEmpty()) | |
218 return; | |
219 ASSERT(m_nameToGroupMap); | |
220 if (!m_nameToGroupMap) | |
221 return; | |
222 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); | |
223 ASSERT(group); | |
224 group->requiredAttributeChanged(element); | |
225 } | |
226 | |
227 HTMLInputElement* CheckedRadioButtons::checkedButtonForGroup(const AtomicString&
name) const | |
228 { | |
229 if (!m_nameToGroupMap) | |
230 return 0; | |
231 RadioButtonGroup* group = m_nameToGroupMap->get(name.impl()); | |
232 return group ? group->checkedButton() : 0; | |
233 } | |
234 | |
235 bool CheckedRadioButtons::isInRequiredGroup(HTMLInputElement* element) const | |
236 { | |
237 ASSERT(element->isRadioButton()); | |
238 if (element->name().isEmpty()) | |
239 return false; | |
240 if (!m_nameToGroupMap) | |
241 return false; | |
242 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); | |
243 return group && group->isRequired() && group->contains(element); | |
244 } | |
245 | |
246 void CheckedRadioButtons::removeButton(HTMLInputElement* element) | |
247 { | |
248 ASSERT(element->isRadioButton()); | |
249 if (element->name().isEmpty()) | |
250 return; | |
251 if (!m_nameToGroupMap) | |
252 return; | |
253 | |
254 NameToGroupMap::iterator it = m_nameToGroupMap->find(element->name().impl())
; | |
255 if (it == m_nameToGroupMap->end()) | |
256 return; | |
257 it->value->remove(element); | |
258 if (it->value->isEmpty()) { | |
259 // FIXME: We may skip deallocating the empty RadioButtonGroup for | |
260 // performance improvement. If we do so, we need to change the key type | |
261 // of m_nameToGroupMap from StringImpl* to AtomicString. | |
262 m_nameToGroupMap->remove(it); | |
263 if (m_nameToGroupMap->isEmpty()) | |
264 m_nameToGroupMap.clear(); | |
265 } | |
266 } | |
267 | |
268 } // namespace | |
OLD | NEW |