2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
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.
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.
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.
20 #ifndef SVGListProperty_h
21 #define SVGListProperty_h
24 #include "SVGAnimatedProperty.h"
25 #include "SVGException.h"
26 #include "SVGPropertyTearOff.h"
27 #include "SVGPropertyTraits.h"
31 template<typename PropertyType>
32 class SVGAnimatedListPropertyTearOff;
34 template<typename PropertyType>
35 class SVGListProperty : public SVGProperty {
37 typedef SVGListProperty<PropertyType> Self;
39 typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType;
40 typedef SVGPropertyTearOff<ListItemType> ListItemTearOff;
41 typedef PassRefPtr<ListItemTearOff> PassListItemTearOff;
42 typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff;
43 typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache;
45 bool canAlterList(ExceptionCode& ec) const
47 if (m_role == AnimValRole) {
48 ec = NO_MODIFICATION_ALLOWED_ERR;
56 void clearValues(PropertyType& values, ExceptionCode& ec)
58 if (!canAlterList(ec))
65 void clearValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, ExceptionCode& ec)
68 if (!canAlterList(ec))
71 animatedList->detachListWrappers(0);
72 animatedList->values().clear();
76 // SVGList::numberOfItems()
77 unsigned numberOfItemsValues(PropertyType& values) const
82 unsigned numberOfItemsValuesAndWrappers(AnimatedListPropertyTearOff* animatedList) const
85 return animatedList->values().size();
88 // SVGList::initialize()
89 ListItemType initializeValues(PropertyType& values, const ListItemType& newItem, ExceptionCode& ec)
91 if (!canAlterList(ec))
92 return ListItemType();
94 // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list.
95 processIncomingListItemValue(newItem, 0);
97 // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter.
99 values.append(newItem);
105 PassListItemTearOff initializeValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, ExceptionCode& ec)
107 ASSERT(animatedList);
108 if (!canAlterList(ec))
111 // Not specified, but FF/Opera do it this way, and it's just sane.
113 ec = SVGException::SVG_WRONG_TYPE_ERR;
117 PropertyType& values = animatedList->values();
118 ListWrapperCache& wrappers = animatedList->wrappers();
120 RefPtr<ListItemTearOff> newItem = passNewItem;
121 ASSERT(values.size() == wrappers.size());
123 // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list.
124 processIncomingListItemWrapper(newItem, 0);
126 // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter.
127 animatedList->detachListWrappers(0);
130 values.append(newItem->propertyReference());
131 wrappers.append(newItem);
134 return newItem.release();
137 // SVGList::getItem()
138 bool canGetItem(PropertyType& values, unsigned index, ExceptionCode& ec)
140 if (index >= values.size()) {
148 ListItemType getItemValues(PropertyType& values, unsigned index, ExceptionCode& ec)
150 if (!canGetItem(values, index, ec))
151 return ListItemType();
153 // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy.
154 return values.at(index);
157 PassListItemTearOff getItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec)
159 ASSERT(animatedList);
160 PropertyType& values = animatedList->values();
161 if (!canGetItem(values, index, ec))
164 ListWrapperCache& wrappers = animatedList->wrappers();
166 // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy.
167 // Any changes made to the item are immediately reflected in the list.
168 ASSERT(values.size() == wrappers.size());
169 RefPtr<ListItemTearOff> wrapper = wrappers.at(index);
171 // Create new wrapper, which is allowed to directly modify the item in the list, w/o copying and cache the wrapper in our map.
172 // It is also associated with our animated property, so it can notify the SVG Element which holds the SVGAnimated*List
173 // that it has been modified (and thus can call svgAttributeChanged(associatedAttributeName)).
174 wrapper = ListItemTearOff::create(animatedList, UndefinedRole, values.at(index));
175 wrappers.at(index) = wrapper;
178 return wrapper.release();
181 // SVGList::insertItemBefore()
182 ListItemType insertItemBeforeValues(PropertyType& values, const ListItemType& newItem, unsigned index, ExceptionCode& ec)
184 if (!canAlterList(ec))
185 return ListItemType();
187 // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
188 if (index > values.size())
189 index = values.size();
191 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
192 processIncomingListItemValue(newItem, &index);
194 // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be
195 // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list.
196 values.insert(index, newItem);
202 PassListItemTearOff insertItemBeforeValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec)
204 ASSERT(animatedList);
205 if (!canAlterList(ec))
208 // Not specified, but FF/Opera do it this way, and it's just sane.
210 ec = SVGException::SVG_WRONG_TYPE_ERR;
214 PropertyType& values = animatedList->values();
215 ListWrapperCache& wrappers = animatedList->wrappers();
217 // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
218 if (index > values.size())
219 index = values.size();
221 RefPtr<ListItemTearOff> newItem = passNewItem;
222 ASSERT(values.size() == wrappers.size());
224 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
225 processIncomingListItemWrapper(newItem, &index);
227 // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be
228 // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list.
229 values.insert(index, newItem->propertyReference());
231 // Store new wrapper at position 'index', change its underlying value, so mutations of newItem, directly affect the item in the list.
232 wrappers.insert(index, newItem);
235 return newItem.release();
238 // SVGList::replaceItem()
239 bool canReplaceItem(PropertyType& values, unsigned index, ExceptionCode& ec)
241 if (!canAlterList(ec))
244 if (index >= values.size()) {
252 ListItemType replaceItemValues(PropertyType& values, const ListItemType& newItem, unsigned index, ExceptionCode& ec)
254 if (!canReplaceItem(values, index, ec))
255 return ListItemType();
257 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
258 // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item.
259 processIncomingListItemValue(newItem, &index);
261 if (values.isEmpty()) {
262 // 'newItem' already lived in our list, we removed it, and now we're empty, which means there's nothing to replace.
264 return ListItemType();
267 // Update the value at the desired position 'index'.
268 values.at(index) = newItem;
274 PassListItemTearOff replaceItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec)
276 ASSERT(animatedList);
277 PropertyType& values = animatedList->values();
278 if (!canReplaceItem(values, index, ec))
281 // Not specified, but FF/Opera do it this way, and it's just sane.
283 ec = SVGException::SVG_WRONG_TYPE_ERR;
287 ListWrapperCache& wrappers = animatedList->wrappers();
288 ASSERT(values.size() == wrappers.size());
289 RefPtr<ListItemTearOff> newItem = passNewItem;
291 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
292 // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item.
293 processIncomingListItemWrapper(newItem, &index);
295 if (values.isEmpty()) {
296 ASSERT(wrappers.isEmpty());
297 // 'passNewItem' already lived in our list, we removed it, and now we're empty, which means there's nothing to replace.
302 // Detach the existing wrapper.
303 RefPtr<ListItemTearOff> oldItem = wrappers.at(index);
305 oldItem->detachWrapper();
307 // Update the value and the wrapper at the desired position 'index'.
308 values.at(index) = newItem->propertyReference();
309 wrappers.at(index) = newItem;
312 return newItem.release();
315 // SVGList::removeItem()
316 bool canRemoveItem(PropertyType& values, unsigned index, ExceptionCode& ec)
318 if (!canAlterList(ec))
321 if (index >= values.size()) {
329 ListItemType removeItemValues(PropertyType& values, unsigned index, ExceptionCode& ec)
331 if (!canRemoveItem(values, index, ec))
332 return ListItemType();
334 ListItemType oldItem = values.at(index);
335 values.remove(index);
341 PassListItemTearOff removeItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec)
343 ASSERT(animatedList);
344 PropertyType& values = animatedList->values();
345 if (!canRemoveItem(values, index, ec))
348 ListWrapperCache& wrappers = animatedList->wrappers();
349 ASSERT(values.size() == wrappers.size());
351 // Detach the existing wrapper.
352 RefPtr<ListItemTearOff> oldItem = wrappers.at(index);
354 oldItem = ListItemTearOff::create(animatedList, UndefinedRole, values.at(index));
356 oldItem->detachWrapper();
357 wrappers.remove(index);
358 values.remove(index);
361 return oldItem.release();
364 // SVGList::appendItem()
365 ListItemType appendItemValues(PropertyType& values, const ListItemType& newItem, ExceptionCode& ec)
367 if (!canAlterList(ec))
368 return ListItemType();
370 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
371 processIncomingListItemValue(newItem, 0);
373 // Append the value at the end of the list.
374 values.append(newItem);
380 PassListItemTearOff appendItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, ExceptionCode& ec)
382 ASSERT(animatedList);
383 if (!canAlterList(ec))
386 // Not specified, but FF/Opera do it this way, and it's just sane.
388 ec = SVGException::SVG_WRONG_TYPE_ERR;
392 PropertyType& values = animatedList->values();
393 ListWrapperCache& wrappers = animatedList->wrappers();
395 RefPtr<ListItemTearOff> newItem = passNewItem;
396 ASSERT(values.size() == wrappers.size());
398 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
399 processIncomingListItemWrapper(newItem, 0);
401 // Append the value and wrapper at the end of the list.
402 values.append(newItem->propertyReference());
403 wrappers.append(newItem);
406 return newItem.release();
409 virtual SVGPropertyRole role() const { return m_role; }
412 SVGListProperty(SVGPropertyRole role)
417 virtual void commitChange() = 0;
418 virtual void processIncomingListItemValue(const ListItemType& newItem, unsigned* indexToModify) = 0;
419 virtual void processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify) = 0;
422 SVGPropertyRole m_role;
427 #endif // ENABLE(SVG)
428 #endif // SVGListProperty_h