editor/libeditor/html/nsHTMLObjectResizer.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     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>
    48 class nsISelection;
    50 using namespace mozilla;
    52 class nsHTMLEditUtils;
    54 // ==================================================================
    55 // DocumentResizeEventListener
    56 // ==================================================================
    57 NS_IMPL_ISUPPORTS(DocumentResizeEventListener, nsIDOMEventListener)
    59 DocumentResizeEventListener::DocumentResizeEventListener(nsIHTMLEditor * aEditor) 
    60 {
    61   mEditor = do_GetWeakReference(aEditor);
    62 }
    64 DocumentResizeEventListener::~DocumentResizeEventListener()
    65 {
    66 }
    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 }
    77 // ==================================================================
    78 // ResizerSelectionListener
    79 // ==================================================================
    81 NS_IMPL_ISUPPORTS(ResizerSelectionListener, nsISelectionListener)
    83 ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor * aEditor)
    84 {
    85   mEditor = do_GetWeakReference(aEditor);
    86 }
    88 ResizerSelectionListener::~ResizerSelectionListener()
    89 {
    90 }
    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   }
   106   return NS_OK;
   107 }
   109 // ==================================================================
   110 // ResizerMouseMotionListener
   111 // ==================================================================
   113 NS_IMPL_ISUPPORTS(ResizerMouseMotionListener, nsIDOMEventListener)
   115 ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor * aEditor)
   116 {
   117   mEditor = do_GetWeakReference(aEditor);
   118 }
   120 ResizerMouseMotionListener::~ResizerMouseMotionListener() 
   121 {
   122 }
   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   }
   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   }
   141   return NS_OK;
   142 }
   144 // ==================================================================
   145 // nsHTMLEditor
   146 // ==================================================================
   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);
   157   NS_ENSURE_SUCCESS(res, res);
   158   NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE);
   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);
   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;
   177     case nsIHTMLObjectResizer::eLeft:
   178       locationStr = kLeft;
   179       break;
   180     case nsIHTMLObjectResizer::eRight:
   181       locationStr = kRight;
   182       break;
   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   }
   195   res = (*aReturn)->SetAttribute(NS_LITERAL_STRING("anonlocation"),
   196                                  locationStr);
   197   return res;
   198 }
   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);
   216   NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE);
   218   return res;
   219 }
   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);
   231   NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE);
   233   return res;
   234 }
   236 nsresult
   237 nsHTMLEditor::SetAllResizersPosition()
   238 {
   239   NS_ENSURE_TRUE(mTopLeftHandle, NS_ERROR_FAILURE);
   241   int32_t x = mResizedObjectX;
   242   int32_t y = mResizedObjectY;
   243   int32_t w = mResizedObjectWidth;
   244   int32_t h = mResizedObjectHeight;
   246   // now let's place all the resizers around the image
   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));
   257   int32_t rw  = (int32_t)((resizerWidth + 1) / 2);
   258   int32_t rh =  (int32_t)((resizerHeight+ 1) / 2);
   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);
   264   SetAnonymousElementPosition(x-rw,     y+h/2-rh, mLeftHandle);
   265   SetAnonymousElementPosition(x+w-rw-1, y+h/2-rh, mRightHandle);
   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);
   271   return NS_OK;
   272 }
   274 NS_IMETHODIMP
   275 nsHTMLEditor::RefreshResizers()
   276 {
   277   // nothing to do if resizers are not displayed...
   278   NS_ENSURE_TRUE(mResizedObject, NS_OK);
   280   nsresult res = GetPositionAndDimensions(mResizedObject,
   281                                           mResizedObjectX,
   282                                           mResizedObjectY,
   283                                           mResizedObjectWidth,
   284                                           mResizedObjectHeight,
   285                                           mResizedObjectBorderLeft,
   286                                           mResizedObjectBorderTop,
   287                                           mResizedObjectMarginLeft,
   288                                           mResizedObjectMarginTop);
   290   NS_ENSURE_SUCCESS(res, res);
   291   res = SetAllResizersPosition();
   292   NS_ENSURE_SUCCESS(res, res);
   293   return SetShadowPosition(mResizingShadow, mResizedObject,
   294                            mResizedObjectX, mResizedObjectY);
   295 }
   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 }
   306 nsresult 
   307 nsHTMLEditor::ShowResizersInner(nsIDOMElement *aResizedElement)
   308 {
   309   NS_ENSURE_ARG_POINTER(aResizedElement);
   310   nsresult res;
   312   nsCOMPtr<nsIDOMNode> parentNode;
   313   res = aResizedElement->GetParentNode(getter_AddRefs(parentNode));
   314   NS_ENSURE_SUCCESS(res, res);
   316   if (mResizedObject) {
   317     NS_ERROR("call HideResizers first");
   318     return NS_ERROR_UNEXPECTED;
   319   }
   320   mResizedObject = aResizedElement;
   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);
   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);
   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);
   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);
   361   // and let's set their absolute positions in the document
   362   res = SetAllResizersPosition();
   363   NS_ENSURE_SUCCESS(res, res);
   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);
   374   // and then the resizing info tooltip
   375   res = CreateResizingInfo(getter_AddRefs(mResizingInfo), parentNode);
   376   NS_ENSURE_SUCCESS(res, res);
   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);
   383   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(doc->GetWindow());
   384   if (!target) { return NS_ERROR_NULL_POINTER; }
   386   mResizeEventListenerP = new DocumentResizeEventListener(this);
   387   if (!mResizeEventListenerP) { return NS_ERROR_OUT_OF_MEMORY; }
   388   res = target->AddEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
   390   aResizedElement->SetAttribute(NS_LITERAL_STRING("_moz_resizing"), NS_LITERAL_STRING("true"));
   391   return res;
   392 }
   394 NS_IMETHODIMP 
   395 nsHTMLEditor::HideResizers(void)
   396 {
   397   NS_ENSURE_TRUE(mResizedObject, NS_OK);
   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.
   405   nsresult res;
   406   nsCOMPtr<nsIDOMNode> parentNode;
   407   nsCOMPtr<nsIContent> parentContent;
   409   if (mTopLeftHandle) {
   410     res = mTopLeftHandle->GetParentNode(getter_AddRefs(parentNode));
   411     NS_ENSURE_SUCCESS(res, res);
   412     parentContent = do_QueryInterface(parentNode);
   413   }
   415   NS_NAMED_LITERAL_STRING(mousedown, "mousedown");
   417   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   418                              mTopLeftHandle, parentContent, ps);
   419   mTopLeftHandle = nullptr;
   421   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   422                              mTopHandle, parentContent, ps);
   423   mTopHandle = nullptr;
   425   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   426                              mTopRightHandle, parentContent, ps);
   427   mTopRightHandle = nullptr;
   429   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   430                              mLeftHandle, parentContent, ps);
   431   mLeftHandle = nullptr;
   433   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   434                              mRightHandle, parentContent, ps);
   435   mRightHandle = nullptr;
   437   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   438                              mBottomLeftHandle, parentContent, ps);
   439   mBottomLeftHandle = nullptr;
   441   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   442                              mBottomHandle, parentContent, ps);
   443   mBottomHandle = nullptr;
   445   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   446                              mBottomRightHandle, parentContent, ps);
   447   mBottomRightHandle = nullptr;
   449   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   450                              mResizingShadow, parentContent, ps);
   451   mResizingShadow = nullptr;
   453   RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
   454                              mResizingInfo, parentContent, ps);
   455   mResizingInfo = nullptr;
   457   if (mActivatedHandle) {
   458     mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated"));
   459     mActivatedHandle = nullptr;
   460   }
   462   // don't forget to remove the listeners !
   464   nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
   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;
   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; }
   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;
   485   mResizedObject->RemoveAttribute(NS_LITERAL_STRING("_moz_resizing"));
   486   mResizedObject = nullptr;
   488   return NS_OK;
   489 }
   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 }
   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   }
   514   mIsResizing = true;
   515   mActivatedHandle = aHandle;
   516   mActivatedHandle->SetAttribute(NS_LITERAL_STRING("_moz_activated"), NS_LITERAL_STRING("true"));
   518   // do we want to preserve ratio or not?
   519   bool preserveRatio = nsHTMLEditUtils::IsImage(mResizedObject) &&
   520     Preferences::GetBool("editor.resizing.preserve_ratio", true);
   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   }
   551   // make the shadow appear
   552   mResizingShadow->RemoveAttribute(NS_LITERAL_STRING("class"));
   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);
   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     }
   570     nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
   571     NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
   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 }
   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();
   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 }
   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 }
   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 }
   647 nsresult
   648 nsHTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW, int32_t aH)
   649 {
   650   nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   652   NS_NAMED_LITERAL_STRING(leftStr, "left");
   653   NS_NAMED_LITERAL_STRING(topStr, "top");
   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;
   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;
   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;
   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);
   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   }
   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);
   717   nsAutoString info(widthStr + NS_LITERAL_STRING(" x ") + heightStr +
   718                     NS_LITERAL_STRING(" (") + diffWidthStr +
   719                     NS_LITERAL_STRING(", ") + diffHeightStr +
   720                     NS_LITERAL_STRING(")"));
   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);
   729   bool hasClass = false;
   730   if (NS_SUCCEEDED(mResizingInfo->HasAttribute(NS_LITERAL_STRING("class"), &hasClass )) && hasClass)
   731     res = mResizingInfo->RemoveAttribute(NS_LITERAL_STRING("class"));
   733   return res;
   734 }
   736 nsresult
   737 nsHTMLEditor::SetShadowPosition(nsIDOMElement * aShadow,
   738                                 nsIDOMElement * aOriginalObject,
   739                                 int32_t aOriginalObjectX,
   740                                 int32_t aOriginalObjectY)
   741 {
   742   SetAnonymousElementPosition(aOriginalObjectX, aOriginalObjectY, aShadow);
   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 }
   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   }
   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 }
   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 }
   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 }
   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 }
   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 }
   832 NS_IMETHODIMP
   833 nsHTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent)
   834 {
   835   NS_NAMED_LITERAL_STRING(leftStr, "left");
   836   NS_NAMED_LITERAL_STRING(topStr, "top");
   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);
   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);
   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);
   864     return SetResizingInfoPosition(newX, newY, newWidth, newHeight);
   865   }
   867   if (mGrabberClicked) {
   868     nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
   869     int32_t clientX, clientY;
   870     mouseEvent->GetClientX(&clientX);
   871     mouseEvent->GetClientY(&clientY);
   873     int32_t xThreshold =
   874       LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 1);
   875     int32_t yThreshold =
   876       LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 1);
   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);
   890     int32_t newX = mPositionedObjectX + clientX - mOriginalX;
   891     int32_t newY = mPositionedObjectY + clientY - mOriginalY;
   893     SnapToGrid(newX, newY);
   895     mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, leftStr, newX);
   896     mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, topStr, newY);
   897   }
   898   return NS_OK;
   899 }
   901 void
   902 nsHTMLEditor::SetFinalSize(int32_t aX, int32_t aY)
   903 {
   904   if (!mResizedObject) {
   905     // paranoia
   906     return;
   907   }
   909   if (mActivatedHandle) {
   910     mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated"));
   911     mActivatedHandle = nullptr;
   912   }
   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);
   924   int32_t x, y;
   925   x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft+mResizedObjectMarginLeft : 0);
   926   y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop+mResizedObjectMarginTop : 0);
   928   // we want one transaction only from a user's point of view
   929   nsAutoEditBatch batchIt(this);
   931   NS_NAMED_LITERAL_STRING(widthStr,  "width");
   932   NS_NAMED_LITERAL_STRING(heightStr, "height");
   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);
   951     hasAttr = false;
   952     if (setHeight && NS_SUCCEEDED(mResizedObject->HasAttribute(heightStr, &hasAttr)) && hasAttr)
   953       RemoveAttribute(mResizedObject, heightStr);
   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
   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);
   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     }
   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);
  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);
  1018   // keep track of that size
  1019   mResizedObjectWidth  = width;
  1020   mResizedObjectHeight = height;
  1022   RefreshResizers();
  1025 NS_IMETHODIMP
  1026 nsHTMLEditor::GetResizedObject(nsIDOMElement * *aResizedObject)
  1028   *aResizedObject = mResizedObject;
  1029   NS_IF_ADDREF(*aResizedObject);
  1030   return NS_OK;
  1033 NS_IMETHODIMP
  1034 nsHTMLEditor::GetObjectResizingEnabled(bool *aIsObjectResizingEnabled)
  1036   *aIsObjectResizingEnabled = mIsObjectResizingEnabled;
  1037   return NS_OK;
  1040 NS_IMETHODIMP
  1041 nsHTMLEditor::SetObjectResizingEnabled(bool aObjectResizingEnabled)
  1043   mIsObjectResizingEnabled = aObjectResizingEnabled;
  1044   return NS_OK;
  1047 NS_IMETHODIMP
  1048 nsHTMLEditor::AddObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener)
  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;
  1058   objectResizeEventListeners.AppendObject(aListener);
  1059   return NS_OK;
  1062 NS_IMETHODIMP
  1063 nsHTMLEditor::RemoveObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener)
  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;
  1073   objectResizeEventListeners.RemoveObject(aListener);
  1074   return NS_OK;

mercurial