2010-11-10 Nikolas Zimmermann <nzimmermann@rim.com>
[WebKit-https.git] / WebCore / svg / properties / SVGListPropertyTearOff.h
1 /*
2  * Copyright (C) Research In Motion Limited 2010. 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 #ifndef SVGListPropertyTearOff_h
21 #define SVGListPropertyTearOff_h
22
23 #if ENABLE(SVG)
24 #include "SVGListProperty.h"
25
26 namespace WebCore {
27
28 template<typename PropertyType>
29 class SVGListPropertyTearOff : public SVGListProperty<PropertyType> {
30 public:
31     typedef SVGListProperty<PropertyType> Base;
32
33     typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType;
34     typedef SVGPropertyTearOff<ListItemType> ListItemTearOff;
35     typedef PassRefPtr<ListItemTearOff> PassListItemTearOff;
36     typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff;
37     typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache;
38
39     static PassRefPtr<SVGListPropertyTearOff<PropertyType> > create(AnimatedListPropertyTearOff* animatedProperty, SVGPropertyRole role)
40     {
41         ASSERT(animatedProperty);
42         return adoptRef(new SVGListPropertyTearOff<PropertyType>(animatedProperty, role));
43     }
44
45     int removeItemFromList(ListItemTearOff* removeItem, bool shouldSynchronizeWrappers)
46     {
47         PropertyType& values = m_animatedProperty->values();
48         ListWrapperCache& wrappers = m_animatedProperty->wrappers();
49
50         // Lookup item in cache and remove its corresponding wrapper.
51         unsigned size = wrappers.size();
52         ASSERT(size == values.size());
53         for (unsigned i = 0; i < size; ++i) {
54             RefPtr<ListItemTearOff>& item = wrappers.at(i);
55             if (item != removeItem)
56                 continue;
57
58             item->detachWrapper();
59             wrappers.remove(i);
60             values.remove(i);
61
62             if (shouldSynchronizeWrappers)
63                 commitChange();
64
65             return i;
66         }
67
68         return -1;
69     }
70
71     // SVGList API
72     void clear(ExceptionCode& ec)
73     {
74         Base::clearValuesAndWrappers(m_animatedProperty.get(), ec);
75     }
76
77     unsigned numberOfItems() const
78     {
79         return Base::numberOfItemsValuesAndWrappers(m_animatedProperty.get());
80     }
81
82     PassListItemTearOff initialize(PassListItemTearOff passNewItem, ExceptionCode& ec)
83     {
84         return Base::initializeValuesAndWrappers(m_animatedProperty.get(), passNewItem, ec);
85     }
86
87     PassListItemTearOff getItem(unsigned index, ExceptionCode& ec)
88     {
89         return Base::getItemValuesAndWrappers(m_animatedProperty.get(), index, ec);
90     }
91
92     PassListItemTearOff insertItemBefore(PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec)
93     {
94         return Base::insertItemBeforeValuesAndWrappers(m_animatedProperty.get(), passNewItem, index, ec);
95     }
96
97     PassListItemTearOff replaceItem(PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec)
98     {
99         return Base::replaceItemValuesAndWrappers(m_animatedProperty.get(), passNewItem, index, ec);
100     }
101
102     PassListItemTearOff removeItem(unsigned index, ExceptionCode& ec)
103     {
104         return Base::removeItemValuesAndWrappers(m_animatedProperty.get(), index, ec);
105     }
106
107     PassListItemTearOff appendItem(PassListItemTearOff passNewItem, ExceptionCode& ec)
108     {
109         return Base::appendItemValuesAndWrappers(m_animatedProperty.get(), passNewItem, ec);
110     }
111
112 protected:
113     SVGListPropertyTearOff(AnimatedListPropertyTearOff* animatedProperty, SVGPropertyRole role)
114         : SVGListProperty<PropertyType>(role)
115         , m_animatedProperty(animatedProperty)
116     {
117     }
118
119     virtual void commitChange()
120     {
121         PropertyType& values = m_animatedProperty->values();
122         ListWrapperCache& wrappers = m_animatedProperty->wrappers();
123
124         // Update existing wrappers, as the index in the values list has changed.
125         unsigned size = wrappers.size();
126         ASSERT(size == values.size());
127         for (unsigned i = 0; i < size; ++i) {
128             RefPtr<ListItemTearOff>& item = wrappers.at(i);
129             if (!item)
130                 continue;
131             item->setAnimatedProperty(m_animatedProperty.get());
132             item->setValue(values.at(i));
133         }
134
135         m_animatedProperty->commitChange();
136     }
137
138     virtual void processIncomingListItemValue(const ListItemType&, unsigned*)
139     {
140         ASSERT_NOT_REACHED();
141     }
142
143     virtual void processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify)
144     {
145         SVGAnimatedProperty* animatedPropertyOfItem = newItem->animatedProperty();
146
147         // newItem has been created manually, it doesn't belong to any SVGElement.
148         // (for example: "textElement.x.baseVal.appendItem(svgsvgElement.createSVGLength())")
149         if (!animatedPropertyOfItem)
150             return;
151
152         // newItem belongs to a SVGElement, but its associated SVGAnimatedProperty is not an animated list tear off.
153         // (for example: "textElement.x.baseVal.appendItem(rectElement.width.baseVal)")
154         if (!animatedPropertyOfItem->isAnimatedListTearOff()) {
155             // We have to copy the incoming newItem, as we're not allowed to insert this tear off as is into our wrapper cache.
156             // Otherwhise we'll end up having two SVGAnimatedPropertys that operate on the same SVGPropertyTearOff. Consider the example above:
157             // SVGRectElements SVGAnimatedLength 'width' property baseVal points to the same tear off object
158             // that's inserted into SVGTextElements SVGAnimatedLengthList 'x'. textElement.x.baseVal.getItem(0).value += 150 would
159             // mutate the rectElement width _and_ the textElement x list. That's obviously wrong, take care of that.
160             newItem = ListItemTearOff::create(newItem->propertyReference());
161             return;
162         }
163
164         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
165         // 'newItem' is already living in another list. If it's not our list, synchronize the other lists wrappers after the removal.
166         bool livesInOtherList = animatedPropertyOfItem != m_animatedProperty;
167         int removedIndex = static_cast<AnimatedListPropertyTearOff*>(animatedPropertyOfItem)->removeItemFromList(newItem.get(), livesInOtherList);
168         ASSERT(removedIndex != -1);
169
170         if (!indexToModify)
171             return;
172
173         // If the item lived in our list, adjust the insertion index.
174         if (!livesInOtherList) {
175             unsigned& index = *indexToModify;
176             // Spec: If the item is already in this list, note that the index of the item to (replace|insert before) is before the removal of the item.
177             if (static_cast<unsigned>(removedIndex) < index)
178                 --index;
179         }
180     }
181
182     // Back pointer to the animated property that created us
183     // For example (text.x.baseVal): m_animatedProperty points to the 'x' SVGAnimatedLengthList object
184     RefPtr<AnimatedListPropertyTearOff> m_animatedProperty;
185 };
186
187 }
188
189 #endif // ENABLE(SVG)
190 #endif // SVGListPropertyTearOff_h