editor/libeditor/html/nsHTMLObjectResizer.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:aabfc03aaae0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "mozilla/LookAndFeel.h"
7 #include "mozilla/MathAlgorithms.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/mozalloc.h"
10 #include "nsAString.h"
11 #include "nsAlgorithm.h"
12 #include "nsAutoPtr.h"
13 #include "nsCOMArray.h"
14 #include "nsCOMPtr.h"
15 #include "nsDebug.h"
16 #include "nsEditProperty.h"
17 #include "nsEditorUtils.h"
18 #include "nsError.h"
19 #include "nsHTMLCSSUtils.h"
20 #include "nsHTMLEditUtils.h"
21 #include "nsHTMLEditor.h"
22 #include "nsHTMLObjectResizer.h"
23 #include "nsIAtom.h"
24 #include "nsIContent.h"
25 #include "nsID.h"
26 #include "nsIDOMDocument.h"
27 #include "nsIDOMElement.h"
28 #include "nsIDOMEvent.h"
29 #include "nsIDOMEventTarget.h"
30 #include "nsIDOMMouseEvent.h"
31 #include "nsIDOMNode.h"
32 #include "nsIDOMText.h"
33 #include "nsIDocument.h"
34 #include "nsIEditor.h"
35 #include "nsIHTMLEditor.h"
36 #include "nsIHTMLObjectResizeListener.h"
37 #include "nsIHTMLObjectResizer.h"
38 #include "nsIPresShell.h"
39 #include "nsISupportsUtils.h"
40 #include "nsPIDOMWindow.h"
41 #include "nsReadableUtils.h"
42 #include "nsString.h"
43 #include "nsStringFwd.h"
44 #include "nsSubstringTuple.h"
45 #include "nscore.h"
46 #include <algorithm>
47
48 class nsISelection;
49
50 using namespace mozilla;
51
52 class nsHTMLEditUtils;
53
54 // ==================================================================
55 // DocumentResizeEventListener
56 // ==================================================================
57 NS_IMPL_ISUPPORTS(DocumentResizeEventListener, nsIDOMEventListener)
58
59 DocumentResizeEventListener::DocumentResizeEventListener(nsIHTMLEditor * aEditor)
60 {
61 mEditor = do_GetWeakReference(aEditor);
62 }
63
64 DocumentResizeEventListener::~DocumentResizeEventListener()
65 {
66 }
67
68 NS_IMETHODIMP
69 DocumentResizeEventListener::HandleEvent(nsIDOMEvent* aMouseEvent)
70 {
71 nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryReferent(mEditor);
72 if (objectResizer)
73 return objectResizer->RefreshResizers();
74 return NS_OK;
75 }
76
77 // ==================================================================
78 // ResizerSelectionListener
79 // ==================================================================
80
81 NS_IMPL_ISUPPORTS(ResizerSelectionListener, nsISelectionListener)
82
83 ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor * aEditor)
84 {
85 mEditor = do_GetWeakReference(aEditor);
86 }
87
88 ResizerSelectionListener::~ResizerSelectionListener()
89 {
90 }
91
92 NS_IMETHODIMP
93 ResizerSelectionListener::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aSelection, int16_t aReason)
94 {
95 if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
96 nsISelectionListener::KEYPRESS_REASON |
97 nsISelectionListener::SELECTALL_REASON)) && aSelection)
98 {
99 // the selection changed and we need to check if we have to
100 // hide and/or redisplay resizing handles
101 nsCOMPtr<nsIHTMLEditor> editor = do_QueryReferent(mEditor);
102 if (editor)
103 editor->CheckSelectionStateForAnonymousButtons(aSelection);
104 }
105
106 return NS_OK;
107 }
108
109 // ==================================================================
110 // ResizerMouseMotionListener
111 // ==================================================================
112
113 NS_IMPL_ISUPPORTS(ResizerMouseMotionListener, nsIDOMEventListener)
114
115 ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor * aEditor)
116 {
117 mEditor = do_GetWeakReference(aEditor);
118 }
119
120 ResizerMouseMotionListener::~ResizerMouseMotionListener()
121 {
122 }
123
124 NS_IMETHODIMP
125 ResizerMouseMotionListener::HandleEvent(nsIDOMEvent* aMouseEvent)
126 {
127 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
128 if (!mouseEvent) {
129 //non-ui event passed in. bad things.
130 return NS_OK;
131 }
132
133 // Don't do anything special if not an HTML object resizer editor
134 nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryReferent(mEditor);
135 if (objectResizer)
136 {
137 // check if we have to redisplay a resizing shadow
138 objectResizer->MouseMove(aMouseEvent);
139 }
140
141 return NS_OK;
142 }
143
144 // ==================================================================
145 // nsHTMLEditor
146 // ==================================================================
147
148 nsresult
149 nsHTMLEditor::CreateResizer(nsIDOMElement ** aReturn, int16_t aLocation, nsIDOMNode * aParentNode)
150 {
151 nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"),
152 aParentNode,
153 NS_LITERAL_STRING("mozResizer"),
154 false,
155 aReturn);
156
157 NS_ENSURE_SUCCESS(res, res);
158 NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE);
159
160 // add the mouse listener so we can detect a click on a resizer
161 nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(*aReturn));
162 evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mEventListener,
163 true);
164
165 nsAutoString locationStr;
166 switch (aLocation) {
167 case nsIHTMLObjectResizer::eTopLeft:
168 locationStr = kTopLeft;
169 break;
170 case nsIHTMLObjectResizer::eTop:
171 locationStr = kTop;
172 break;
173 case nsIHTMLObjectResizer::eTopRight:
174 locationStr = kTopRight;
175 break;
176
177 case nsIHTMLObjectResizer::eLeft:
178 locationStr = kLeft;
179 break;
180 case nsIHTMLObjectResizer::eRight:
181 locationStr = kRight;
182 break;
183
184 case nsIHTMLObjectResizer::eBottomLeft:
185 locationStr = kBottomLeft;
186 break;
187 case nsIHTMLObjectResizer::eBottom:
188 locationStr = kBottom;
189 break;
190 case nsIHTMLObjectResizer::eBottomRight:
191 locationStr = kBottomRight;
192 break;
193 }
194
195 res = (*aReturn)->SetAttribute(NS_LITERAL_STRING("anonlocation"),
196 locationStr);
197 return res;
198 }
199
200 nsresult
201 nsHTMLEditor::CreateShadow(nsIDOMElement ** aReturn, nsIDOMNode * aParentNode,
202 nsIDOMElement * aOriginalObject)
203 {
204 // let's create an image through the element factory
205 nsAutoString name;
206 if (nsHTMLEditUtils::IsImage(aOriginalObject))
207 name.AssignLiteral("img");
208 else
209 name.AssignLiteral("span");
210 nsresult res = CreateAnonymousElement(name,
211 aParentNode,
212 NS_LITERAL_STRING("mozResizingShadow"),
213 true,
214 aReturn);
215
216 NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE);
217
218 return res;
219 }
220
221 nsresult
222 nsHTMLEditor::CreateResizingInfo(nsIDOMElement ** aReturn, nsIDOMNode * aParentNode)
223 {
224 // let's create an info box through the element factory
225 nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"),
226 aParentNode,
227 NS_LITERAL_STRING("mozResizingInfo"),
228 true,
229 aReturn);
230
231 NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE);
232
233 return res;
234 }
235
236 nsresult
237 nsHTMLEditor::SetAllResizersPosition()
238 {
239 NS_ENSURE_TRUE(mTopLeftHandle, NS_ERROR_FAILURE);
240
241 int32_t x = mResizedObjectX;
242 int32_t y = mResizedObjectY;
243 int32_t w = mResizedObjectWidth;
244 int32_t h = mResizedObjectHeight;
245
246 // now let's place all the resizers around the image
247
248 // get the size of resizers
249 nsAutoString value;
250 float resizerWidth, resizerHeight;
251 nsCOMPtr<nsIAtom> dummyUnit;
252 mHTMLCSSUtils->GetComputedProperty(mTopLeftHandle, nsEditProperty::cssWidth, value);
253 mHTMLCSSUtils->ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit));
254 mHTMLCSSUtils->GetComputedProperty(mTopLeftHandle, nsEditProperty::cssHeight, value);
255 mHTMLCSSUtils->ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit));
256
257 int32_t rw = (int32_t)((resizerWidth + 1) / 2);
258 int32_t rh = (int32_t)((resizerHeight+ 1) / 2);
259
260 SetAnonymousElementPosition(x-rw, y-rh, mTopLeftHandle);
261 SetAnonymousElementPosition(x+w/2-rw, y-rh, mTopHandle);
262 SetAnonymousElementPosition(x+w-rw-1, y-rh, mTopRightHandle);
263
264 SetAnonymousElementPosition(x-rw, y+h/2-rh, mLeftHandle);
265 SetAnonymousElementPosition(x+w-rw-1, y+h/2-rh, mRightHandle);
266
267 SetAnonymousElementPosition(x-rw, y+h-rh-1, mBottomLeftHandle);
268 SetAnonymousElementPosition(x+w/2-rw, y+h-rh-1, mBottomHandle);
269 SetAnonymousElementPosition(x+w-rw-1, y+h-rh-1, mBottomRightHandle);
270
271 return NS_OK;
272 }
273
274 NS_IMETHODIMP
275 nsHTMLEditor::RefreshResizers()
276 {
277 // nothing to do if resizers are not displayed...
278 NS_ENSURE_TRUE(mResizedObject, NS_OK);
279
280 nsresult res = GetPositionAndDimensions(mResizedObject,
281 mResizedObjectX,
282 mResizedObjectY,
283 mResizedObjectWidth,
284 mResizedObjectHeight,
285 mResizedObjectBorderLeft,
286 mResizedObjectBorderTop,
287 mResizedObjectMarginLeft,
288 mResizedObjectMarginTop);
289
290 NS_ENSURE_SUCCESS(res, res);
291 res = SetAllResizersPosition();
292 NS_ENSURE_SUCCESS(res, res);
293 return SetShadowPosition(mResizingShadow, mResizedObject,
294 mResizedObjectX, mResizedObjectY);
295 }
296
297 NS_IMETHODIMP
298 nsHTMLEditor::ShowResizers(nsIDOMElement *aResizedElement)
299 {
300 nsresult res = ShowResizersInner(aResizedElement);
301 if (NS_FAILED(res))
302 HideResizers();
303 return res;
304 }
305
306 nsresult
307 nsHTMLEditor::ShowResizersInner(nsIDOMElement *aResizedElement)
308 {
309 NS_ENSURE_ARG_POINTER(aResizedElement);
310 nsresult res;
311
312 nsCOMPtr<nsIDOMNode> parentNode;
313 res = aResizedElement->GetParentNode(getter_AddRefs(parentNode));
314 NS_ENSURE_SUCCESS(res, res);
315
316 if (mResizedObject) {
317 NS_ERROR("call HideResizers first");
318 return NS_ERROR_UNEXPECTED;
319 }
320 mResizedObject = aResizedElement;
321
322 // The resizers and the shadow will be anonymous siblings of the element.
323 res = CreateResizer(getter_AddRefs(mTopLeftHandle),
324 nsIHTMLObjectResizer::eTopLeft, parentNode);
325 NS_ENSURE_SUCCESS(res, res);
326 res = CreateResizer(getter_AddRefs(mTopHandle),
327 nsIHTMLObjectResizer::eTop, parentNode);
328 NS_ENSURE_SUCCESS(res, res);
329 res = CreateResizer(getter_AddRefs(mTopRightHandle),
330 nsIHTMLObjectResizer::eTopRight, parentNode);
331 NS_ENSURE_SUCCESS(res, res);
332
333 res = CreateResizer(getter_AddRefs(mLeftHandle),
334 nsIHTMLObjectResizer::eLeft, parentNode);
335 NS_ENSURE_SUCCESS(res, res);
336 res = CreateResizer(getter_AddRefs(mRightHandle),
337 nsIHTMLObjectResizer::eRight, parentNode);
338 NS_ENSURE_SUCCESS(res, res);
339
340 res = CreateResizer(getter_AddRefs(mBottomLeftHandle),
341 nsIHTMLObjectResizer::eBottomLeft, parentNode);
342 NS_ENSURE_SUCCESS(res, res);
343 res = CreateResizer(getter_AddRefs(mBottomHandle),
344 nsIHTMLObjectResizer::eBottom, parentNode);
345 NS_ENSURE_SUCCESS(res, res);
346 res = CreateResizer(getter_AddRefs(mBottomRightHandle),
347 nsIHTMLObjectResizer::eBottomRight, parentNode);
348 NS_ENSURE_SUCCESS(res, res);
349
350 res = GetPositionAndDimensions(aResizedElement,
351 mResizedObjectX,
352 mResizedObjectY,
353 mResizedObjectWidth,
354 mResizedObjectHeight,
355 mResizedObjectBorderLeft,
356 mResizedObjectBorderTop,
357 mResizedObjectMarginLeft,
358 mResizedObjectMarginTop);
359 NS_ENSURE_SUCCESS(res, res);
360
361 // and let's set their absolute positions in the document
362 res = SetAllResizersPosition();
363 NS_ENSURE_SUCCESS(res, res);
364
365 // now, let's create the resizing shadow
366 res = CreateShadow(getter_AddRefs(mResizingShadow), parentNode,
367 aResizedElement);
368 NS_ENSURE_SUCCESS(res, res);
369 // and set its position
370 res = SetShadowPosition(mResizingShadow, mResizedObject,
371 mResizedObjectX, mResizedObjectY);
372 NS_ENSURE_SUCCESS(res, res);
373
374 // and then the resizing info tooltip
375 res = CreateResizingInfo(getter_AddRefs(mResizingInfo), parentNode);
376 NS_ENSURE_SUCCESS(res, res);
377
378 // and listen to the "resize" event on the window first, get the
379 // window from the document...
380 nsCOMPtr<nsIDocument> doc = GetDocument();
381 NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
382
383 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(doc->GetWindow());
384 if (!target) { return NS_ERROR_NULL_POINTER; }
385
386 mResizeEventListenerP = new DocumentResizeEventListener(this);
387 if (!mResizeEventListenerP) { return NS_ERROR_OUT_OF_MEMORY; }
388 res = target->AddEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
389
390 aResizedElement->SetAttribute(NS_LITERAL_STRING("_moz_resizing"), NS_LITERAL_STRING("true"));
391 return res;
392 }
393
394 NS_IMETHODIMP
395 nsHTMLEditor::HideResizers(void)
396 {
397 NS_ENSURE_TRUE(mResizedObject, NS_OK);
398
399 // get the presshell's document observer interface.
400 nsCOMPtr<nsIPresShell> ps = GetPresShell();
401 // We allow the pres shell to be null; when it is, we presume there
402 // are no document observers to notify, but we still want to
403 // UnbindFromTree.
404
405 nsresult res;
406 nsCOMPtr<nsIDOMNode> parentNode;
407 nsCOMPtr<nsIContent> parentContent;
408
409 if (mTopLeftHandle) {
410 res = mTopLeftHandle->GetParentNode(getter_AddRefs(parentNode));
411 NS_ENSURE_SUCCESS(res, res);
412 parentContent = do_QueryInterface(parentNode);
413 }
414
415 NS_NAMED_LITERAL_STRING(mousedown, "mousedown");
416
417 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
418 mTopLeftHandle, parentContent, ps);
419 mTopLeftHandle = nullptr;
420
421 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
422 mTopHandle, parentContent, ps);
423 mTopHandle = nullptr;
424
425 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
426 mTopRightHandle, parentContent, ps);
427 mTopRightHandle = nullptr;
428
429 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
430 mLeftHandle, parentContent, ps);
431 mLeftHandle = nullptr;
432
433 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
434 mRightHandle, parentContent, ps);
435 mRightHandle = nullptr;
436
437 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
438 mBottomLeftHandle, parentContent, ps);
439 mBottomLeftHandle = nullptr;
440
441 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
442 mBottomHandle, parentContent, ps);
443 mBottomHandle = nullptr;
444
445 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
446 mBottomRightHandle, parentContent, ps);
447 mBottomRightHandle = nullptr;
448
449 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
450 mResizingShadow, parentContent, ps);
451 mResizingShadow = nullptr;
452
453 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
454 mResizingInfo, parentContent, ps);
455 mResizingInfo = nullptr;
456
457 if (mActivatedHandle) {
458 mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated"));
459 mActivatedHandle = nullptr;
460 }
461
462 // don't forget to remove the listeners !
463
464 nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
465
466 if (target && mMouseMotionListenerP)
467 {
468 res = target->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
469 mMouseMotionListenerP, true);
470 NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove mouse motion listener");
471 }
472 mMouseMotionListenerP = nullptr;
473
474 nsCOMPtr<nsIDocument> doc = GetDocument();
475 if (!doc) { return NS_ERROR_NULL_POINTER; }
476 target = do_QueryInterface(doc->GetWindow());
477 if (!target) { return NS_ERROR_NULL_POINTER; }
478
479 if (mResizeEventListenerP) {
480 res = target->RemoveEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
481 NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove resize event listener");
482 }
483 mResizeEventListenerP = nullptr;
484
485 mResizedObject->RemoveAttribute(NS_LITERAL_STRING("_moz_resizing"));
486 mResizedObject = nullptr;
487
488 return NS_OK;
489 }
490
491 void
492 nsHTMLEditor::HideShadowAndInfo()
493 {
494 if (mResizingShadow)
495 mResizingShadow->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden"));
496 if (mResizingInfo)
497 mResizingInfo->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden"));
498 }
499
500 nsresult
501 nsHTMLEditor::StartResizing(nsIDOMElement *aHandle)
502 {
503 // First notify the listeners if any
504 int32_t listenersCount = objectResizeEventListeners.Count();
505 if (listenersCount) {
506 nsCOMPtr<nsIHTMLObjectResizeListener> listener;
507 int32_t index;
508 for (index = 0; index < listenersCount; index++) {
509 listener = objectResizeEventListeners[index];
510 listener->OnStartResizing(mResizedObject);
511 }
512 }
513
514 mIsResizing = true;
515 mActivatedHandle = aHandle;
516 mActivatedHandle->SetAttribute(NS_LITERAL_STRING("_moz_activated"), NS_LITERAL_STRING("true"));
517
518 // do we want to preserve ratio or not?
519 bool preserveRatio = nsHTMLEditUtils::IsImage(mResizedObject) &&
520 Preferences::GetBool("editor.resizing.preserve_ratio", true);
521
522 // the way we change the position/size of the shadow depends on
523 // the handle
524 nsAutoString locationStr;
525 aHandle->GetAttribute(NS_LITERAL_STRING("anonlocation"), locationStr);
526 if (locationStr.Equals(kTopLeft)) {
527 SetResizeIncrements(1, 1, -1, -1, preserveRatio);
528 }
529 else if (locationStr.Equals(kTop)) {
530 SetResizeIncrements(0, 1, 0, -1, false);
531 }
532 else if (locationStr.Equals(kTopRight)) {
533 SetResizeIncrements(0, 1, 1, -1, preserveRatio);
534 }
535 else if (locationStr.Equals(kLeft)) {
536 SetResizeIncrements(1, 0, -1, 0, false);
537 }
538 else if (locationStr.Equals(kRight)) {
539 SetResizeIncrements(0, 0, 1, 0, false);
540 }
541 else if (locationStr.Equals(kBottomLeft)) {
542 SetResizeIncrements(1, 0, -1, 1, preserveRatio);
543 }
544 else if (locationStr.Equals(kBottom)) {
545 SetResizeIncrements(0, 0, 0, 1, false);
546 }
547 else if (locationStr.Equals(kBottomRight)) {
548 SetResizeIncrements(0, 0, 1, 1, preserveRatio);
549 }
550
551 // make the shadow appear
552 mResizingShadow->RemoveAttribute(NS_LITERAL_STRING("class"));
553
554 // position it
555 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow,
556 NS_LITERAL_STRING("width"),
557 mResizedObjectWidth);
558 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow,
559 NS_LITERAL_STRING("height"),
560 mResizedObjectHeight);
561
562 // add a mouse move listener to the editor
563 nsresult result = NS_OK;
564 if (!mMouseMotionListenerP) {
565 mMouseMotionListenerP = new ResizerMouseMotionListener(this);
566 if (!mMouseMotionListenerP) {
567 return NS_ERROR_OUT_OF_MEMORY;
568 }
569
570 nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
571 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
572
573 result = target->AddEventListener(NS_LITERAL_STRING("mousemove"),
574 mMouseMotionListenerP, true);
575 NS_ASSERTION(NS_SUCCEEDED(result),
576 "failed to register mouse motion listener");
577 }
578 return result;
579 }
580
581
582 NS_IMETHODIMP
583 nsHTMLEditor::MouseDown(int32_t aClientX, int32_t aClientY,
584 nsIDOMElement *aTarget, nsIDOMEvent* aEvent)
585 {
586 bool anonElement = false;
587 if (aTarget && NS_SUCCEEDED(aTarget->HasAttribute(NS_LITERAL_STRING("_moz_anonclass"), &anonElement)))
588 // we caught a click on an anonymous element
589 if (anonElement) {
590 nsAutoString anonclass;
591 nsresult res = aTarget->GetAttribute(NS_LITERAL_STRING("_moz_anonclass"), anonclass);
592 NS_ENSURE_SUCCESS(res, res);
593 if (anonclass.EqualsLiteral("mozResizer")) {
594 // and that element is a resizer, let's start resizing!
595 aEvent->PreventDefault();
596
597 mOriginalX = aClientX;
598 mOriginalY = aClientY;
599 return StartResizing(aTarget);
600 }
601 if (anonclass.EqualsLiteral("mozGrabber")) {
602 // and that element is a grabber, let's start moving the element!
603 mOriginalX = aClientX;
604 mOriginalY = aClientY;
605 return GrabberClicked();
606 }
607 }
608 return NS_OK;
609 }
610
611 NS_IMETHODIMP
612 nsHTMLEditor::MouseUp(int32_t aClientX, int32_t aClientY,
613 nsIDOMElement *aTarget)
614 {
615 if (mIsResizing) {
616 // we are resizing and release the mouse button, so let's
617 // end the resizing process
618 mIsResizing = false;
619 HideShadowAndInfo();
620 SetFinalSize(aClientX, aClientY);
621 }
622 else if (mIsMoving || mGrabberClicked) {
623 if (mIsMoving) {
624 mPositioningShadow->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden"));
625 SetFinalPosition(aClientX, aClientY);
626 }
627 if (mGrabberClicked) {
628 EndMoving();
629 }
630 }
631 return NS_OK;
632 }
633
634
635 void
636 nsHTMLEditor::SetResizeIncrements(int32_t aX, int32_t aY,
637 int32_t aW, int32_t aH,
638 bool aPreserveRatio)
639 {
640 mXIncrementFactor = aX;
641 mYIncrementFactor = aY;
642 mWidthIncrementFactor = aW;
643 mHeightIncrementFactor = aH;
644 mPreserveRatio = aPreserveRatio;
645 }
646
647 nsresult
648 nsHTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW, int32_t aH)
649 {
650 nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
651
652 NS_NAMED_LITERAL_STRING(leftStr, "left");
653 NS_NAMED_LITERAL_STRING(topStr, "top");
654
655 // Determine the position of the resizing info box based upon the new
656 // position and size of the element (aX, aY, aW, aH), and which
657 // resizer is the "activated handle". For example, place the resizing
658 // info box at the bottom-right corner of the new element, if the element
659 // is being resized by the bottom-right resizer.
660 int32_t infoXPosition;
661 int32_t infoYPosition;
662
663 if (mActivatedHandle == mTopLeftHandle ||
664 mActivatedHandle == mLeftHandle ||
665 mActivatedHandle == mBottomLeftHandle)
666 infoXPosition = aX;
667 else if (mActivatedHandle == mTopHandle ||
668 mActivatedHandle == mBottomHandle)
669 infoXPosition = aX + (aW / 2);
670 else
671 // should only occur when mActivatedHandle is one of the 3 right-side
672 // handles, but this is a reasonable default if it isn't any of them (?)
673 infoXPosition = aX + aW;
674
675 if (mActivatedHandle == mTopLeftHandle ||
676 mActivatedHandle == mTopHandle ||
677 mActivatedHandle == mTopRightHandle)
678 infoYPosition = aY;
679 else if (mActivatedHandle == mLeftHandle ||
680 mActivatedHandle == mRightHandle)
681 infoYPosition = aY + (aH / 2);
682 else
683 // should only occur when mActivatedHandle is one of the 3 bottom-side
684 // handles, but this is a reasonable default if it isn't any of them (?)
685 infoYPosition = aY + aH;
686
687 // Offset info box by 20 so it's not directly under the mouse cursor.
688 const int mouseCursorOffset = 20;
689 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingInfo, leftStr,
690 infoXPosition + mouseCursorOffset);
691 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingInfo, topStr,
692 infoYPosition + mouseCursorOffset);
693
694 nsCOMPtr<nsIDOMNode> textInfo;
695 nsresult res = mResizingInfo->GetFirstChild(getter_AddRefs(textInfo));
696 NS_ENSURE_SUCCESS(res, res);
697 nsCOMPtr<nsIDOMNode> junk;
698 if (textInfo) {
699 res = mResizingInfo->RemoveChild(textInfo, getter_AddRefs(junk));
700 NS_ENSURE_SUCCESS(res, res);
701 textInfo = nullptr;
702 junk = nullptr;
703 }
704
705 nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr;
706 widthStr.AppendInt(aW);
707 heightStr.AppendInt(aH);
708 int32_t diffWidth = aW - mResizedObjectWidth;
709 int32_t diffHeight = aH - mResizedObjectHeight;
710 if (diffWidth > 0)
711 diffWidthStr.AssignLiteral("+");
712 if (diffHeight > 0)
713 diffHeightStr.AssignLiteral("+");
714 diffWidthStr.AppendInt(diffWidth);
715 diffHeightStr.AppendInt(diffHeight);
716
717 nsAutoString info(widthStr + NS_LITERAL_STRING(" x ") + heightStr +
718 NS_LITERAL_STRING(" (") + diffWidthStr +
719 NS_LITERAL_STRING(", ") + diffHeightStr +
720 NS_LITERAL_STRING(")"));
721
722 nsCOMPtr<nsIDOMText> nodeAsText;
723 res = domdoc->CreateTextNode(info, getter_AddRefs(nodeAsText));
724 NS_ENSURE_SUCCESS(res, res);
725 textInfo = do_QueryInterface(nodeAsText);
726 res = mResizingInfo->AppendChild(textInfo, getter_AddRefs(junk));
727 NS_ENSURE_SUCCESS(res, res);
728
729 bool hasClass = false;
730 if (NS_SUCCEEDED(mResizingInfo->HasAttribute(NS_LITERAL_STRING("class"), &hasClass )) && hasClass)
731 res = mResizingInfo->RemoveAttribute(NS_LITERAL_STRING("class"));
732
733 return res;
734 }
735
736 nsresult
737 nsHTMLEditor::SetShadowPosition(nsIDOMElement * aShadow,
738 nsIDOMElement * aOriginalObject,
739 int32_t aOriginalObjectX,
740 int32_t aOriginalObjectY)
741 {
742 SetAnonymousElementPosition(aOriginalObjectX, aOriginalObjectY, aShadow);
743
744 if (nsHTMLEditUtils::IsImage(aOriginalObject)) {
745 nsAutoString imageSource;
746 nsresult res = aOriginalObject->GetAttribute(NS_LITERAL_STRING("src"),
747 imageSource);
748 NS_ENSURE_SUCCESS(res, res);
749 res = aShadow->SetAttribute(NS_LITERAL_STRING("src"), imageSource);
750 NS_ENSURE_SUCCESS(res, res);
751 }
752 return NS_OK;
753 }
754
755 int32_t
756 nsHTMLEditor::GetNewResizingIncrement(int32_t aX, int32_t aY, int32_t aID)
757 {
758 int32_t result = 0;
759 if (!mPreserveRatio) {
760 switch (aID) {
761 case kX:
762 case kWidth:
763 result = aX - mOriginalX;
764 break;
765 case kY:
766 case kHeight:
767 result = aY - mOriginalY;
768 break;
769 }
770 return result;
771 }
772
773 int32_t xi = (aX - mOriginalX) * mWidthIncrementFactor;
774 int32_t yi = (aY - mOriginalY) * mHeightIncrementFactor;
775 float objectSizeRatio =
776 ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight);
777 result = (xi > yi) ? xi : yi;
778 switch (aID) {
779 case kX:
780 case kWidth:
781 if (result == yi)
782 result = (int32_t) (((float) result) * objectSizeRatio);
783 result = (int32_t) (((float) result) * mWidthIncrementFactor);
784 break;
785 case kY:
786 case kHeight:
787 if (result == xi)
788 result = (int32_t) (((float) result) / objectSizeRatio);
789 result = (int32_t) (((float) result) * mHeightIncrementFactor);
790 break;
791 }
792 return result;
793 }
794
795 int32_t
796 nsHTMLEditor::GetNewResizingX(int32_t aX, int32_t aY)
797 {
798 int32_t resized = mResizedObjectX +
799 GetNewResizingIncrement(aX, aY, kX) * mXIncrementFactor;
800 int32_t max = mResizedObjectX + mResizedObjectWidth;
801 return std::min(resized, max);
802 }
803
804 int32_t
805 nsHTMLEditor::GetNewResizingY(int32_t aX, int32_t aY)
806 {
807 int32_t resized = mResizedObjectY +
808 GetNewResizingIncrement(aX, aY, kY) * mYIncrementFactor;
809 int32_t max = mResizedObjectY + mResizedObjectHeight;
810 return std::min(resized, max);
811 }
812
813 int32_t
814 nsHTMLEditor::GetNewResizingWidth(int32_t aX, int32_t aY)
815 {
816 int32_t resized = mResizedObjectWidth +
817 GetNewResizingIncrement(aX, aY, kWidth) *
818 mWidthIncrementFactor;
819 return std::max(resized, 1);
820 }
821
822 int32_t
823 nsHTMLEditor::GetNewResizingHeight(int32_t aX, int32_t aY)
824 {
825 int32_t resized = mResizedObjectHeight +
826 GetNewResizingIncrement(aX, aY, kHeight) *
827 mHeightIncrementFactor;
828 return std::max(resized, 1);
829 }
830
831
832 NS_IMETHODIMP
833 nsHTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent)
834 {
835 NS_NAMED_LITERAL_STRING(leftStr, "left");
836 NS_NAMED_LITERAL_STRING(topStr, "top");
837
838 if (mIsResizing) {
839 // we are resizing and the mouse pointer's position has changed
840 // we have to resdisplay the shadow
841 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
842 int32_t clientX, clientY;
843 mouseEvent->GetClientX(&clientX);
844 mouseEvent->GetClientY(&clientY);
845
846 int32_t newX = GetNewResizingX(clientX, clientY);
847 int32_t newY = GetNewResizingY(clientX, clientY);
848 int32_t newWidth = GetNewResizingWidth(clientX, clientY);
849 int32_t newHeight = GetNewResizingHeight(clientX, clientY);
850
851 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow,
852 leftStr,
853 newX);
854 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow,
855 topStr,
856 newY);
857 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow,
858 NS_LITERAL_STRING("width"),
859 newWidth);
860 mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow,
861 NS_LITERAL_STRING("height"),
862 newHeight);
863
864 return SetResizingInfoPosition(newX, newY, newWidth, newHeight);
865 }
866
867 if (mGrabberClicked) {
868 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
869 int32_t clientX, clientY;
870 mouseEvent->GetClientX(&clientX);
871 mouseEvent->GetClientY(&clientY);
872
873 int32_t xThreshold =
874 LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 1);
875 int32_t yThreshold =
876 LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 1);
877
878 if (DeprecatedAbs(clientX - mOriginalX) * 2 >= xThreshold ||
879 DeprecatedAbs(clientY - mOriginalY) * 2 >= yThreshold) {
880 mGrabberClicked = false;
881 StartMoving(nullptr);
882 }
883 }
884 if (mIsMoving) {
885 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
886 int32_t clientX, clientY;
887 mouseEvent->GetClientX(&clientX);
888 mouseEvent->GetClientY(&clientY);
889
890 int32_t newX = mPositionedObjectX + clientX - mOriginalX;
891 int32_t newY = mPositionedObjectY + clientY - mOriginalY;
892
893 SnapToGrid(newX, newY);
894
895 mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, leftStr, newX);
896 mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, topStr, newY);
897 }
898 return NS_OK;
899 }
900
901 void
902 nsHTMLEditor::SetFinalSize(int32_t aX, int32_t aY)
903 {
904 if (!mResizedObject) {
905 // paranoia
906 return;
907 }
908
909 if (mActivatedHandle) {
910 mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated"));
911 mActivatedHandle = nullptr;
912 }
913
914 // we have now to set the new width and height of the resized object
915 // we don't set the x and y position because we don't control that in
916 // a normal HTML layout
917 int32_t left = GetNewResizingX(aX, aY);
918 int32_t top = GetNewResizingY(aX, aY);
919 int32_t width = GetNewResizingWidth(aX, aY);
920 int32_t height = GetNewResizingHeight(aX, aY);
921 bool setWidth = !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth);
922 bool setHeight = !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight);
923
924 int32_t x, y;
925 x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft+mResizedObjectMarginLeft : 0);
926 y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop+mResizedObjectMarginTop : 0);
927
928 // we want one transaction only from a user's point of view
929 nsAutoEditBatch batchIt(this);
930
931 NS_NAMED_LITERAL_STRING(widthStr, "width");
932 NS_NAMED_LITERAL_STRING(heightStr, "height");
933
934 bool hasAttr = false;
935 if (mResizedObjectIsAbsolutelyPositioned) {
936 if (setHeight)
937 mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject,
938 nsEditProperty::cssTop,
939 y,
940 false);
941 if (setWidth)
942 mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject,
943 nsEditProperty::cssLeft,
944 x,
945 false);
946 }
947 if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) {
948 if (setWidth && NS_SUCCEEDED(mResizedObject->HasAttribute(widthStr, &hasAttr)) && hasAttr)
949 RemoveAttribute(mResizedObject, widthStr);
950
951 hasAttr = false;
952 if (setHeight && NS_SUCCEEDED(mResizedObject->HasAttribute(heightStr, &hasAttr)) && hasAttr)
953 RemoveAttribute(mResizedObject, heightStr);
954
955 if (setWidth)
956 mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject,
957 nsEditProperty::cssWidth,
958 width,
959 false);
960 if (setHeight)
961 mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject,
962 nsEditProperty::cssHeight,
963 height,
964 false);
965 }
966 else {
967 // we use HTML size and remove all equivalent CSS properties
968
969 // we set the CSS width and height to remove it later,
970 // triggering an immediate reflow; otherwise, we have problems
971 // with asynchronous reflow
972 if (setWidth)
973 mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject,
974 nsEditProperty::cssWidth,
975 width,
976 false);
977 if (setHeight)
978 mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject,
979 nsEditProperty::cssHeight,
980 height,
981 false);
982
983 if (setWidth) {
984 nsAutoString w;
985 w.AppendInt(width);
986 SetAttribute(mResizedObject, widthStr, w);
987 }
988 if (setHeight) {
989 nsAutoString h;
990 h.AppendInt(height);
991 SetAttribute(mResizedObject, heightStr, h);
992 }
993
994 if (setWidth)
995 mHTMLCSSUtils->RemoveCSSProperty(mResizedObject,
996 nsEditProperty::cssWidth,
997 EmptyString(),
998 false);
999 if (setHeight)
1000 mHTMLCSSUtils->RemoveCSSProperty(mResizedObject,
1001 nsEditProperty::cssHeight,
1002 EmptyString(),
1003 false);
1004 }
1005 // finally notify the listeners if any
1006 int32_t listenersCount = objectResizeEventListeners.Count();
1007 if (listenersCount) {
1008 nsCOMPtr<nsIHTMLObjectResizeListener> listener;
1009 int32_t index;
1010 for (index = 0; index < listenersCount; index++) {
1011 listener = objectResizeEventListeners[index];
1012 listener->OnEndResizing(mResizedObject,
1013 mResizedObjectWidth, mResizedObjectHeight,
1014 width, height);
1015 }
1016 }
1017
1018 // keep track of that size
1019 mResizedObjectWidth = width;
1020 mResizedObjectHeight = height;
1021
1022 RefreshResizers();
1023 }
1024
1025 NS_IMETHODIMP
1026 nsHTMLEditor::GetResizedObject(nsIDOMElement * *aResizedObject)
1027 {
1028 *aResizedObject = mResizedObject;
1029 NS_IF_ADDREF(*aResizedObject);
1030 return NS_OK;
1031 }
1032
1033 NS_IMETHODIMP
1034 nsHTMLEditor::GetObjectResizingEnabled(bool *aIsObjectResizingEnabled)
1035 {
1036 *aIsObjectResizingEnabled = mIsObjectResizingEnabled;
1037 return NS_OK;
1038 }
1039
1040 NS_IMETHODIMP
1041 nsHTMLEditor::SetObjectResizingEnabled(bool aObjectResizingEnabled)
1042 {
1043 mIsObjectResizingEnabled = aObjectResizingEnabled;
1044 return NS_OK;
1045 }
1046
1047 NS_IMETHODIMP
1048 nsHTMLEditor::AddObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener)
1049 {
1050 NS_ENSURE_ARG_POINTER(aListener);
1051 if (objectResizeEventListeners.Count() &&
1052 objectResizeEventListeners.IndexOf(aListener) != -1) {
1053 /* listener already registered */
1054 NS_ASSERTION(false,
1055 "trying to register an already registered object resize event listener");
1056 return NS_OK;
1057 }
1058 objectResizeEventListeners.AppendObject(aListener);
1059 return NS_OK;
1060 }
1061
1062 NS_IMETHODIMP
1063 nsHTMLEditor::RemoveObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener)
1064 {
1065 NS_ENSURE_ARG_POINTER(aListener);
1066 if (!objectResizeEventListeners.Count() ||
1067 objectResizeEventListeners.IndexOf(aListener) == -1) {
1068 /* listener was not registered */
1069 NS_ASSERTION(false,
1070 "trying to remove an object resize event listener that was not already registered");
1071 return NS_OK;
1072 }
1073 objectResizeEventListeners.RemoveObject(aListener);
1074 return NS_OK;
1075 }
1076

mercurial