1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is the Mozilla SVG project.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Crocodile Clips Ltd..
20 : * Portions created by the Initial Developer are Copyright (C) 2001
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
25 : * Brian Birtles <birtles@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "DOMSVGTransform.h"
42 : #include "DOMSVGMatrix.h"
43 : #include "SVGAnimatedTransformList.h"
44 : #include "nsDOMError.h"
45 : #include <math.h>
46 : #include "nsContentUtils.h"
47 :
48 : namespace mozilla {
49 :
50 : //----------------------------------------------------------------------
51 : // nsISupports methods:
52 :
53 : // We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
54 : // clear our list's weak ref to us to be safe. (The other option would be to
55 : // not unlink and rely on the breaking of the other edges in the cycle, as
56 : // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
57 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGTransform)
58 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransform)
59 : // We may not belong to a list, so we must null check tmp->mList.
60 0 : if (tmp->mList) {
61 0 : tmp->mList->mItems[tmp->mListIndex] = nsnull;
62 : }
63 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mList)
64 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
65 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransform)
66 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mList)
67 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
68 :
69 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGTransform)
70 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGTransform)
71 :
72 : } // namespace mozilla
73 : DOMCI_DATA(SVGTransform, mozilla::DOMSVGTransform)
74 : namespace mozilla {
75 :
76 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTransform)
77 0 : NS_INTERFACE_MAP_ENTRY(mozilla::DOMSVGTransform)
78 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMSVGTransform)
79 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMSVGTransform)
80 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGTransform)
81 0 : NS_INTERFACE_MAP_END
82 :
83 :
84 : //----------------------------------------------------------------------
85 : // Ctors:
86 :
87 0 : DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList *aList,
88 : PRUint32 aListIndex,
89 : bool aIsAnimValItem)
90 : : mList(aList)
91 : , mListIndex(aListIndex)
92 : , mIsAnimValItem(aIsAnimValItem)
93 : , mTransform(nsnull)
94 0 : , mMatrixTearoff(nsnull)
95 : {
96 : // These shifts are in sync with the members in the header.
97 0 : NS_ABORT_IF_FALSE(aList &&
98 : aListIndex <= MaxListIndex() &&
99 : aIsAnimValItem < (1 << 1), "bad arg");
100 :
101 0 : NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGNumber!");
102 0 : }
103 :
104 0 : DOMSVGTransform::DOMSVGTransform()
105 : : mList(nsnull)
106 : , mListIndex(0)
107 : , mIsAnimValItem(false)
108 : , mTransform(new SVGTransform()) // Default ctor for objects not in a list
109 : // initialises to matrix type with identity
110 : // matrix
111 0 : , mMatrixTearoff(nsnull)
112 : {
113 0 : }
114 :
115 0 : DOMSVGTransform::DOMSVGTransform(const gfxMatrix &aMatrix)
116 : : mList(nsnull)
117 : , mListIndex(0)
118 : , mIsAnimValItem(false)
119 0 : , mTransform(new SVGTransform(aMatrix))
120 0 : , mMatrixTearoff(nsnull)
121 : {
122 0 : }
123 :
124 0 : DOMSVGTransform::DOMSVGTransform(const SVGTransform &aTransform)
125 : : mList(nsnull)
126 : , mListIndex(0)
127 : , mIsAnimValItem(false)
128 : , mTransform(new SVGTransform(aTransform))
129 0 : , mMatrixTearoff(nsnull)
130 : {
131 0 : }
132 :
133 :
134 : //----------------------------------------------------------------------
135 : // nsIDOMSVGTransform methods:
136 :
137 : /* readonly attribute unsigned short type; */
138 : NS_IMETHODIMP
139 0 : DOMSVGTransform::GetType(PRUint16 *aType)
140 : {
141 0 : *aType = Transform().Type();
142 0 : return NS_OK;
143 : }
144 :
145 : /* readonly attribute nsIDOMSVGMatrix matrix; */
146 : NS_IMETHODIMP
147 0 : DOMSVGTransform::GetMatrix(nsIDOMSVGMatrix * *aMatrix)
148 : {
149 0 : if (!mMatrixTearoff) {
150 0 : mMatrixTearoff = new DOMSVGMatrix(*this);
151 : }
152 :
153 0 : NS_ADDREF(*aMatrix = mMatrixTearoff);
154 0 : return NS_OK;
155 : }
156 :
157 : /* readonly attribute float angle; */
158 : NS_IMETHODIMP
159 0 : DOMSVGTransform::GetAngle(float *aAngle)
160 : {
161 0 : *aAngle = Transform().Angle();
162 0 : return NS_OK;
163 : }
164 :
165 : /* void setMatrix (in nsIDOMSVGMatrix matrix); */
166 : NS_IMETHODIMP
167 0 : DOMSVGTransform::SetMatrix(nsIDOMSVGMatrix *matrix)
168 : {
169 0 : if (mIsAnimValItem)
170 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
171 :
172 0 : nsCOMPtr<DOMSVGMatrix> domMatrix = do_QueryInterface(matrix);
173 0 : if (!domMatrix)
174 0 : return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
175 :
176 0 : SetMatrix(domMatrix->Matrix());
177 0 : return NS_OK;
178 : }
179 :
180 : /* void setTranslate (in float tx, in float ty); */
181 : NS_IMETHODIMP
182 0 : DOMSVGTransform::SetTranslate(float tx, float ty)
183 : {
184 0 : if (mIsAnimValItem) {
185 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
186 : }
187 0 : NS_ENSURE_FINITE2(tx, ty, NS_ERROR_ILLEGAL_VALUE);
188 :
189 0 : if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE &&
190 0 : Matrix().x0 == tx && Matrix().y0 == ty) {
191 0 : return NS_OK;
192 : }
193 :
194 0 : nsAttrValue emptyOrOldValue = NotifyElementWillChange();
195 0 : Transform().SetTranslate(tx, ty);
196 0 : NotifyElementDidChange(emptyOrOldValue);
197 :
198 0 : return NS_OK;
199 : }
200 :
201 : /* void setScale (in float sx, in float sy); */
202 : NS_IMETHODIMP
203 0 : DOMSVGTransform::SetScale(float sx, float sy)
204 : {
205 0 : if (mIsAnimValItem) {
206 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
207 : }
208 0 : NS_ENSURE_FINITE2(sx, sy, NS_ERROR_ILLEGAL_VALUE);
209 :
210 0 : if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SCALE &&
211 0 : Matrix().xx == sx && Matrix().yy == sy) {
212 0 : return NS_OK;
213 : }
214 :
215 0 : nsAttrValue emptyOrOldValue = NotifyElementWillChange();
216 0 : Transform().SetScale(sx, sy);
217 0 : NotifyElementDidChange(emptyOrOldValue);
218 :
219 0 : return NS_OK;
220 : }
221 :
222 : /* void setRotate (in float angle, in float cx, in float cy); */
223 : NS_IMETHODIMP
224 0 : DOMSVGTransform::SetRotate(float angle, float cx, float cy)
225 : {
226 0 : if (mIsAnimValItem) {
227 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
228 : }
229 0 : NS_ENSURE_FINITE3(angle, cx, cy, NS_ERROR_ILLEGAL_VALUE);
230 :
231 0 : if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE) {
232 : float currentCx, currentCy;
233 0 : Transform().GetRotationOrigin(currentCx, currentCy);
234 0 : if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
235 0 : return NS_OK;
236 : }
237 : }
238 :
239 0 : nsAttrValue emptyOrOldValue = NotifyElementWillChange();
240 0 : Transform().SetRotate(angle, cx, cy);
241 0 : NotifyElementDidChange(emptyOrOldValue);
242 :
243 0 : return NS_OK;
244 : }
245 :
246 : /* void setSkewX (in float angle); */
247 : NS_IMETHODIMP
248 0 : DOMSVGTransform::SetSkewX(float angle)
249 : {
250 0 : if (mIsAnimValItem) {
251 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
252 : }
253 0 : NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
254 :
255 0 : if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX &&
256 0 : Transform().Angle() == angle) {
257 0 : return NS_OK;
258 : }
259 :
260 0 : nsAttrValue emptyOrOldValue = NotifyElementWillChange();
261 0 : nsresult rv = Transform().SetSkewX(angle);
262 0 : if (NS_FAILED(rv))
263 0 : return rv;
264 0 : NotifyElementDidChange(emptyOrOldValue);
265 :
266 0 : return NS_OK;
267 : }
268 :
269 : /* void setSkewY (in float angle); */
270 : NS_IMETHODIMP
271 0 : DOMSVGTransform::SetSkewY(float angle)
272 : {
273 0 : if (mIsAnimValItem) {
274 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
275 : }
276 0 : NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
277 :
278 0 : if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY &&
279 0 : Transform().Angle() == angle) {
280 0 : return NS_OK;
281 : }
282 :
283 0 : nsAttrValue emptyOrOldValue = NotifyElementWillChange();
284 0 : nsresult rv = Transform().SetSkewY(angle);
285 0 : if (NS_FAILED(rv))
286 0 : return rv;
287 0 : NotifyElementDidChange(emptyOrOldValue);
288 :
289 0 : return NS_OK;
290 : }
291 :
292 :
293 : //----------------------------------------------------------------------
294 : // List management methods:
295 :
296 : void
297 0 : DOMSVGTransform::InsertingIntoList(DOMSVGTransformList *aList,
298 : PRUint32 aListIndex,
299 : bool aIsAnimValItem)
300 : {
301 0 : NS_ABORT_IF_FALSE(!HasOwner(), "Inserting item that is already in a list");
302 :
303 0 : mList = aList;
304 0 : mListIndex = aListIndex;
305 0 : mIsAnimValItem = aIsAnimValItem;
306 0 : mTransform = nsnull;
307 :
308 0 : NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGLength!");
309 0 : }
310 :
311 : void
312 0 : DOMSVGTransform::RemovingFromList()
313 : {
314 0 : NS_ABORT_IF_FALSE(!mTransform,
315 : "Item in list also has another non-list value associated with it");
316 :
317 0 : mTransform = new SVGTransform(InternalItem());
318 0 : mList = nsnull;
319 0 : mIsAnimValItem = false;
320 0 : }
321 :
322 : SVGTransform&
323 0 : DOMSVGTransform::InternalItem()
324 : {
325 0 : SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
326 0 : return mIsAnimValItem && alist->mAnimVal ?
327 0 : (*alist->mAnimVal)[mListIndex] :
328 0 : alist->mBaseVal[mListIndex];
329 : }
330 :
331 : const SVGTransform&
332 0 : DOMSVGTransform::InternalItem() const
333 : {
334 0 : return const_cast<DOMSVGTransform *>(this)->InternalItem();
335 : }
336 :
337 : #ifdef DEBUG
338 : bool
339 0 : DOMSVGTransform::IndexIsValid()
340 : {
341 0 : SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
342 : return (mIsAnimValItem &&
343 0 : mListIndex < alist->GetAnimValue().Length()) ||
344 0 : (!mIsAnimValItem &&
345 0 : mListIndex < alist->GetBaseValue().Length());
346 : }
347 : #endif // DEBUG
348 :
349 :
350 : //----------------------------------------------------------------------
351 : // Interface for DOMSVGMatrix's use
352 :
353 : void
354 0 : DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix)
355 : {
356 0 : NS_ABORT_IF_FALSE(!mIsAnimValItem,
357 : "Attempting to modify read-only transform");
358 :
359 0 : if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX &&
360 0 : SVGTransform::MatricesEqual(Matrix(), aMatrix)) {
361 0 : return;
362 : }
363 :
364 0 : nsAttrValue emptyOrOldValue = NotifyElementWillChange();
365 0 : Transform().SetMatrix(aMatrix);
366 0 : NotifyElementDidChange(emptyOrOldValue);
367 : }
368 :
369 : void
370 0 : DOMSVGTransform::ClearMatrixTearoff(DOMSVGMatrix* aMatrix)
371 : {
372 0 : NS_ABORT_IF_FALSE(mMatrixTearoff == aMatrix,
373 : "Unexpected matrix pointer to be cleared");
374 0 : mMatrixTearoff = nsnull;
375 0 : }
376 :
377 :
378 : //----------------------------------------------------------------------
379 : // Implementation helpers
380 :
381 : void
382 0 : DOMSVGTransform::NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue)
383 : {
384 0 : if (HasOwner()) {
385 0 : Element()->DidChangeTransformList(aEmptyOrOldValue);
386 0 : if (mList->mAList->IsAnimating()) {
387 0 : Element()->AnimationNeedsResample();
388 : }
389 : }
390 0 : }
391 :
392 4392 : } // namespace mozilla
|