michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/mozalloc.h" michael@0: #include "nsAString.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsDebug.h" michael@0: #include "nsEditProperty.h" michael@0: #include "nsEditorUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsHTMLCSSUtils.h" michael@0: #include "nsHTMLEditUtils.h" michael@0: #include "nsHTMLEditor.h" michael@0: #include "nsHTMLObjectResizer.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIContent.h" michael@0: #include "nsID.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDOMEventTarget.h" michael@0: #include "nsIDOMMouseEvent.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMText.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIEditor.h" michael@0: #include "nsIHTMLEditor.h" michael@0: #include "nsIHTMLObjectResizeListener.h" michael@0: #include "nsIHTMLObjectResizer.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsStringFwd.h" michael@0: #include "nsSubstringTuple.h" michael@0: #include "nscore.h" michael@0: #include michael@0: michael@0: class nsISelection; michael@0: michael@0: using namespace mozilla; michael@0: michael@0: class nsHTMLEditUtils; michael@0: michael@0: // ================================================================== michael@0: // DocumentResizeEventListener michael@0: // ================================================================== michael@0: NS_IMPL_ISUPPORTS(DocumentResizeEventListener, nsIDOMEventListener) michael@0: michael@0: DocumentResizeEventListener::DocumentResizeEventListener(nsIHTMLEditor * aEditor) michael@0: { michael@0: mEditor = do_GetWeakReference(aEditor); michael@0: } michael@0: michael@0: DocumentResizeEventListener::~DocumentResizeEventListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DocumentResizeEventListener::HandleEvent(nsIDOMEvent* aMouseEvent) michael@0: { michael@0: nsCOMPtr objectResizer = do_QueryReferent(mEditor); michael@0: if (objectResizer) michael@0: return objectResizer->RefreshResizers(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // ================================================================== michael@0: // ResizerSelectionListener michael@0: // ================================================================== michael@0: michael@0: NS_IMPL_ISUPPORTS(ResizerSelectionListener, nsISelectionListener) michael@0: michael@0: ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor * aEditor) michael@0: { michael@0: mEditor = do_GetWeakReference(aEditor); michael@0: } michael@0: michael@0: ResizerSelectionListener::~ResizerSelectionListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ResizerSelectionListener::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aSelection, int16_t aReason) michael@0: { michael@0: if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON | michael@0: nsISelectionListener::KEYPRESS_REASON | michael@0: nsISelectionListener::SELECTALL_REASON)) && aSelection) michael@0: { michael@0: // the selection changed and we need to check if we have to michael@0: // hide and/or redisplay resizing handles michael@0: nsCOMPtr editor = do_QueryReferent(mEditor); michael@0: if (editor) michael@0: editor->CheckSelectionStateForAnonymousButtons(aSelection); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // ================================================================== michael@0: // ResizerMouseMotionListener michael@0: // ================================================================== michael@0: michael@0: NS_IMPL_ISUPPORTS(ResizerMouseMotionListener, nsIDOMEventListener) michael@0: michael@0: ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor * aEditor) michael@0: { michael@0: mEditor = do_GetWeakReference(aEditor); michael@0: } michael@0: michael@0: ResizerMouseMotionListener::~ResizerMouseMotionListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ResizerMouseMotionListener::HandleEvent(nsIDOMEvent* aMouseEvent) michael@0: { michael@0: nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); michael@0: if (!mouseEvent) { michael@0: //non-ui event passed in. bad things. michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Don't do anything special if not an HTML object resizer editor michael@0: nsCOMPtr objectResizer = do_QueryReferent(mEditor); michael@0: if (objectResizer) michael@0: { michael@0: // check if we have to redisplay a resizing shadow michael@0: objectResizer->MouseMove(aMouseEvent); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // ================================================================== michael@0: // nsHTMLEditor michael@0: // ================================================================== michael@0: michael@0: nsresult michael@0: nsHTMLEditor::CreateResizer(nsIDOMElement ** aReturn, int16_t aLocation, nsIDOMNode * aParentNode) michael@0: { michael@0: nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"), michael@0: aParentNode, michael@0: NS_LITERAL_STRING("mozResizer"), michael@0: false, michael@0: aReturn); michael@0: michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE); michael@0: michael@0: // add the mouse listener so we can detect a click on a resizer michael@0: nsCOMPtr evtTarget(do_QueryInterface(*aReturn)); michael@0: evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mEventListener, michael@0: true); michael@0: michael@0: nsAutoString locationStr; michael@0: switch (aLocation) { michael@0: case nsIHTMLObjectResizer::eTopLeft: michael@0: locationStr = kTopLeft; michael@0: break; michael@0: case nsIHTMLObjectResizer::eTop: michael@0: locationStr = kTop; michael@0: break; michael@0: case nsIHTMLObjectResizer::eTopRight: michael@0: locationStr = kTopRight; michael@0: break; michael@0: michael@0: case nsIHTMLObjectResizer::eLeft: michael@0: locationStr = kLeft; michael@0: break; michael@0: case nsIHTMLObjectResizer::eRight: michael@0: locationStr = kRight; michael@0: break; michael@0: michael@0: case nsIHTMLObjectResizer::eBottomLeft: michael@0: locationStr = kBottomLeft; michael@0: break; michael@0: case nsIHTMLObjectResizer::eBottom: michael@0: locationStr = kBottom; michael@0: break; michael@0: case nsIHTMLObjectResizer::eBottomRight: michael@0: locationStr = kBottomRight; michael@0: break; michael@0: } michael@0: michael@0: res = (*aReturn)->SetAttribute(NS_LITERAL_STRING("anonlocation"), michael@0: locationStr); michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::CreateShadow(nsIDOMElement ** aReturn, nsIDOMNode * aParentNode, michael@0: nsIDOMElement * aOriginalObject) michael@0: { michael@0: // let's create an image through the element factory michael@0: nsAutoString name; michael@0: if (nsHTMLEditUtils::IsImage(aOriginalObject)) michael@0: name.AssignLiteral("img"); michael@0: else michael@0: name.AssignLiteral("span"); michael@0: nsresult res = CreateAnonymousElement(name, michael@0: aParentNode, michael@0: NS_LITERAL_STRING("mozResizingShadow"), michael@0: true, michael@0: aReturn); michael@0: michael@0: NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::CreateResizingInfo(nsIDOMElement ** aReturn, nsIDOMNode * aParentNode) michael@0: { michael@0: // let's create an info box through the element factory michael@0: nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"), michael@0: aParentNode, michael@0: NS_LITERAL_STRING("mozResizingInfo"), michael@0: true, michael@0: aReturn); michael@0: michael@0: NS_ENSURE_TRUE(*aReturn, NS_ERROR_FAILURE); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::SetAllResizersPosition() michael@0: { michael@0: NS_ENSURE_TRUE(mTopLeftHandle, NS_ERROR_FAILURE); michael@0: michael@0: int32_t x = mResizedObjectX; michael@0: int32_t y = mResizedObjectY; michael@0: int32_t w = mResizedObjectWidth; michael@0: int32_t h = mResizedObjectHeight; michael@0: michael@0: // now let's place all the resizers around the image michael@0: michael@0: // get the size of resizers michael@0: nsAutoString value; michael@0: float resizerWidth, resizerHeight; michael@0: nsCOMPtr dummyUnit; michael@0: mHTMLCSSUtils->GetComputedProperty(mTopLeftHandle, nsEditProperty::cssWidth, value); michael@0: mHTMLCSSUtils->ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit)); michael@0: mHTMLCSSUtils->GetComputedProperty(mTopLeftHandle, nsEditProperty::cssHeight, value); michael@0: mHTMLCSSUtils->ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit)); michael@0: michael@0: int32_t rw = (int32_t)((resizerWidth + 1) / 2); michael@0: int32_t rh = (int32_t)((resizerHeight+ 1) / 2); michael@0: michael@0: SetAnonymousElementPosition(x-rw, y-rh, mTopLeftHandle); michael@0: SetAnonymousElementPosition(x+w/2-rw, y-rh, mTopHandle); michael@0: SetAnonymousElementPosition(x+w-rw-1, y-rh, mTopRightHandle); michael@0: michael@0: SetAnonymousElementPosition(x-rw, y+h/2-rh, mLeftHandle); michael@0: SetAnonymousElementPosition(x+w-rw-1, y+h/2-rh, mRightHandle); michael@0: michael@0: SetAnonymousElementPosition(x-rw, y+h-rh-1, mBottomLeftHandle); michael@0: SetAnonymousElementPosition(x+w/2-rw, y+h-rh-1, mBottomHandle); michael@0: SetAnonymousElementPosition(x+w-rw-1, y+h-rh-1, mBottomRightHandle); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::RefreshResizers() michael@0: { michael@0: // nothing to do if resizers are not displayed... michael@0: NS_ENSURE_TRUE(mResizedObject, NS_OK); michael@0: michael@0: nsresult res = GetPositionAndDimensions(mResizedObject, michael@0: mResizedObjectX, michael@0: mResizedObjectY, michael@0: mResizedObjectWidth, michael@0: mResizedObjectHeight, michael@0: mResizedObjectBorderLeft, michael@0: mResizedObjectBorderTop, michael@0: mResizedObjectMarginLeft, michael@0: mResizedObjectMarginTop); michael@0: michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = SetAllResizersPosition(); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: return SetShadowPosition(mResizingShadow, mResizedObject, michael@0: mResizedObjectX, mResizedObjectY); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::ShowResizers(nsIDOMElement *aResizedElement) michael@0: { michael@0: nsresult res = ShowResizersInner(aResizedElement); michael@0: if (NS_FAILED(res)) michael@0: HideResizers(); michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::ShowResizersInner(nsIDOMElement *aResizedElement) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResizedElement); michael@0: nsresult res; michael@0: michael@0: nsCOMPtr parentNode; michael@0: res = aResizedElement->GetParentNode(getter_AddRefs(parentNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (mResizedObject) { michael@0: NS_ERROR("call HideResizers first"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: mResizedObject = aResizedElement; michael@0: michael@0: // The resizers and the shadow will be anonymous siblings of the element. michael@0: res = CreateResizer(getter_AddRefs(mTopLeftHandle), michael@0: nsIHTMLObjectResizer::eTopLeft, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = CreateResizer(getter_AddRefs(mTopHandle), michael@0: nsIHTMLObjectResizer::eTop, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = CreateResizer(getter_AddRefs(mTopRightHandle), michael@0: nsIHTMLObjectResizer::eTopRight, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: res = CreateResizer(getter_AddRefs(mLeftHandle), michael@0: nsIHTMLObjectResizer::eLeft, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = CreateResizer(getter_AddRefs(mRightHandle), michael@0: nsIHTMLObjectResizer::eRight, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: res = CreateResizer(getter_AddRefs(mBottomLeftHandle), michael@0: nsIHTMLObjectResizer::eBottomLeft, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = CreateResizer(getter_AddRefs(mBottomHandle), michael@0: nsIHTMLObjectResizer::eBottom, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = CreateResizer(getter_AddRefs(mBottomRightHandle), michael@0: nsIHTMLObjectResizer::eBottomRight, parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: res = GetPositionAndDimensions(aResizedElement, michael@0: mResizedObjectX, michael@0: mResizedObjectY, michael@0: mResizedObjectWidth, michael@0: mResizedObjectHeight, michael@0: mResizedObjectBorderLeft, michael@0: mResizedObjectBorderTop, michael@0: mResizedObjectMarginLeft, michael@0: mResizedObjectMarginTop); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // and let's set their absolute positions in the document michael@0: res = SetAllResizersPosition(); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // now, let's create the resizing shadow michael@0: res = CreateShadow(getter_AddRefs(mResizingShadow), parentNode, michael@0: aResizedElement); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // and set its position michael@0: res = SetShadowPosition(mResizingShadow, mResizedObject, michael@0: mResizedObjectX, mResizedObjectY); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // and then the resizing info tooltip michael@0: res = CreateResizingInfo(getter_AddRefs(mResizingInfo), parentNode); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // and listen to the "resize" event on the window first, get the michael@0: // window from the document... michael@0: nsCOMPtr doc = GetDocument(); michael@0: NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr target = do_QueryInterface(doc->GetWindow()); michael@0: if (!target) { return NS_ERROR_NULL_POINTER; } michael@0: michael@0: mResizeEventListenerP = new DocumentResizeEventListener(this); michael@0: if (!mResizeEventListenerP) { return NS_ERROR_OUT_OF_MEMORY; } michael@0: res = target->AddEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false); michael@0: michael@0: aResizedElement->SetAttribute(NS_LITERAL_STRING("_moz_resizing"), NS_LITERAL_STRING("true")); michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::HideResizers(void) michael@0: { michael@0: NS_ENSURE_TRUE(mResizedObject, NS_OK); michael@0: michael@0: // get the presshell's document observer interface. michael@0: nsCOMPtr ps = GetPresShell(); michael@0: // We allow the pres shell to be null; when it is, we presume there michael@0: // are no document observers to notify, but we still want to michael@0: // UnbindFromTree. michael@0: michael@0: nsresult res; michael@0: nsCOMPtr parentNode; michael@0: nsCOMPtr parentContent; michael@0: michael@0: if (mTopLeftHandle) { michael@0: res = mTopLeftHandle->GetParentNode(getter_AddRefs(parentNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: parentContent = do_QueryInterface(parentNode); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(mousedown, "mousedown"); michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mTopLeftHandle, parentContent, ps); michael@0: mTopLeftHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mTopHandle, parentContent, ps); michael@0: mTopHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mTopRightHandle, parentContent, ps); michael@0: mTopRightHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mLeftHandle, parentContent, ps); michael@0: mLeftHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mRightHandle, parentContent, ps); michael@0: mRightHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mBottomLeftHandle, parentContent, ps); michael@0: mBottomLeftHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mBottomHandle, parentContent, ps); michael@0: mBottomHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mBottomRightHandle, parentContent, ps); michael@0: mBottomRightHandle = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mResizingShadow, parentContent, ps); michael@0: mResizingShadow = nullptr; michael@0: michael@0: RemoveListenerAndDeleteRef(mousedown, mEventListener, true, michael@0: mResizingInfo, parentContent, ps); michael@0: mResizingInfo = nullptr; michael@0: michael@0: if (mActivatedHandle) { michael@0: mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated")); michael@0: mActivatedHandle = nullptr; michael@0: } michael@0: michael@0: // don't forget to remove the listeners ! michael@0: michael@0: nsCOMPtr target = GetDOMEventTarget(); michael@0: michael@0: if (target && mMouseMotionListenerP) michael@0: { michael@0: res = target->RemoveEventListener(NS_LITERAL_STRING("mousemove"), michael@0: mMouseMotionListenerP, true); michael@0: NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove mouse motion listener"); michael@0: } michael@0: mMouseMotionListenerP = nullptr; michael@0: michael@0: nsCOMPtr doc = GetDocument(); michael@0: if (!doc) { return NS_ERROR_NULL_POINTER; } michael@0: target = do_QueryInterface(doc->GetWindow()); michael@0: if (!target) { return NS_ERROR_NULL_POINTER; } michael@0: michael@0: if (mResizeEventListenerP) { michael@0: res = target->RemoveEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false); michael@0: NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove resize event listener"); michael@0: } michael@0: mResizeEventListenerP = nullptr; michael@0: michael@0: mResizedObject->RemoveAttribute(NS_LITERAL_STRING("_moz_resizing")); michael@0: mResizedObject = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsHTMLEditor::HideShadowAndInfo() michael@0: { michael@0: if (mResizingShadow) michael@0: mResizingShadow->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden")); michael@0: if (mResizingInfo) michael@0: mResizingInfo->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden")); michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::StartResizing(nsIDOMElement *aHandle) michael@0: { michael@0: // First notify the listeners if any michael@0: int32_t listenersCount = objectResizeEventListeners.Count(); michael@0: if (listenersCount) { michael@0: nsCOMPtr listener; michael@0: int32_t index; michael@0: for (index = 0; index < listenersCount; index++) { michael@0: listener = objectResizeEventListeners[index]; michael@0: listener->OnStartResizing(mResizedObject); michael@0: } michael@0: } michael@0: michael@0: mIsResizing = true; michael@0: mActivatedHandle = aHandle; michael@0: mActivatedHandle->SetAttribute(NS_LITERAL_STRING("_moz_activated"), NS_LITERAL_STRING("true")); michael@0: michael@0: // do we want to preserve ratio or not? michael@0: bool preserveRatio = nsHTMLEditUtils::IsImage(mResizedObject) && michael@0: Preferences::GetBool("editor.resizing.preserve_ratio", true); michael@0: michael@0: // the way we change the position/size of the shadow depends on michael@0: // the handle michael@0: nsAutoString locationStr; michael@0: aHandle->GetAttribute(NS_LITERAL_STRING("anonlocation"), locationStr); michael@0: if (locationStr.Equals(kTopLeft)) { michael@0: SetResizeIncrements(1, 1, -1, -1, preserveRatio); michael@0: } michael@0: else if (locationStr.Equals(kTop)) { michael@0: SetResizeIncrements(0, 1, 0, -1, false); michael@0: } michael@0: else if (locationStr.Equals(kTopRight)) { michael@0: SetResizeIncrements(0, 1, 1, -1, preserveRatio); michael@0: } michael@0: else if (locationStr.Equals(kLeft)) { michael@0: SetResizeIncrements(1, 0, -1, 0, false); michael@0: } michael@0: else if (locationStr.Equals(kRight)) { michael@0: SetResizeIncrements(0, 0, 1, 0, false); michael@0: } michael@0: else if (locationStr.Equals(kBottomLeft)) { michael@0: SetResizeIncrements(1, 0, -1, 1, preserveRatio); michael@0: } michael@0: else if (locationStr.Equals(kBottom)) { michael@0: SetResizeIncrements(0, 0, 0, 1, false); michael@0: } michael@0: else if (locationStr.Equals(kBottomRight)) { michael@0: SetResizeIncrements(0, 0, 1, 1, preserveRatio); michael@0: } michael@0: michael@0: // make the shadow appear michael@0: mResizingShadow->RemoveAttribute(NS_LITERAL_STRING("class")); michael@0: michael@0: // position it michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, michael@0: NS_LITERAL_STRING("width"), michael@0: mResizedObjectWidth); michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, michael@0: NS_LITERAL_STRING("height"), michael@0: mResizedObjectHeight); michael@0: michael@0: // add a mouse move listener to the editor michael@0: nsresult result = NS_OK; michael@0: if (!mMouseMotionListenerP) { michael@0: mMouseMotionListenerP = new ResizerMouseMotionListener(this); michael@0: if (!mMouseMotionListenerP) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsCOMPtr target = GetDOMEventTarget(); michael@0: NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); michael@0: michael@0: result = target->AddEventListener(NS_LITERAL_STRING("mousemove"), michael@0: mMouseMotionListenerP, true); michael@0: NS_ASSERTION(NS_SUCCEEDED(result), michael@0: "failed to register mouse motion listener"); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::MouseDown(int32_t aClientX, int32_t aClientY, michael@0: nsIDOMElement *aTarget, nsIDOMEvent* aEvent) michael@0: { michael@0: bool anonElement = false; michael@0: if (aTarget && NS_SUCCEEDED(aTarget->HasAttribute(NS_LITERAL_STRING("_moz_anonclass"), &anonElement))) michael@0: // we caught a click on an anonymous element michael@0: if (anonElement) { michael@0: nsAutoString anonclass; michael@0: nsresult res = aTarget->GetAttribute(NS_LITERAL_STRING("_moz_anonclass"), anonclass); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (anonclass.EqualsLiteral("mozResizer")) { michael@0: // and that element is a resizer, let's start resizing! michael@0: aEvent->PreventDefault(); michael@0: michael@0: mOriginalX = aClientX; michael@0: mOriginalY = aClientY; michael@0: return StartResizing(aTarget); michael@0: } michael@0: if (anonclass.EqualsLiteral("mozGrabber")) { michael@0: // and that element is a grabber, let's start moving the element! michael@0: mOriginalX = aClientX; michael@0: mOriginalY = aClientY; michael@0: return GrabberClicked(); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::MouseUp(int32_t aClientX, int32_t aClientY, michael@0: nsIDOMElement *aTarget) michael@0: { michael@0: if (mIsResizing) { michael@0: // we are resizing and release the mouse button, so let's michael@0: // end the resizing process michael@0: mIsResizing = false; michael@0: HideShadowAndInfo(); michael@0: SetFinalSize(aClientX, aClientY); michael@0: } michael@0: else if (mIsMoving || mGrabberClicked) { michael@0: if (mIsMoving) { michael@0: mPositioningShadow->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden")); michael@0: SetFinalPosition(aClientX, aClientY); michael@0: } michael@0: if (mGrabberClicked) { michael@0: EndMoving(); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsHTMLEditor::SetResizeIncrements(int32_t aX, int32_t aY, michael@0: int32_t aW, int32_t aH, michael@0: bool aPreserveRatio) michael@0: { michael@0: mXIncrementFactor = aX; michael@0: mYIncrementFactor = aY; michael@0: mWidthIncrementFactor = aW; michael@0: mHeightIncrementFactor = aH; michael@0: mPreserveRatio = aPreserveRatio; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW, int32_t aH) michael@0: { michael@0: nsCOMPtr domdoc = GetDOMDocument(); michael@0: michael@0: NS_NAMED_LITERAL_STRING(leftStr, "left"); michael@0: NS_NAMED_LITERAL_STRING(topStr, "top"); michael@0: michael@0: // Determine the position of the resizing info box based upon the new michael@0: // position and size of the element (aX, aY, aW, aH), and which michael@0: // resizer is the "activated handle". For example, place the resizing michael@0: // info box at the bottom-right corner of the new element, if the element michael@0: // is being resized by the bottom-right resizer. michael@0: int32_t infoXPosition; michael@0: int32_t infoYPosition; michael@0: michael@0: if (mActivatedHandle == mTopLeftHandle || michael@0: mActivatedHandle == mLeftHandle || michael@0: mActivatedHandle == mBottomLeftHandle) michael@0: infoXPosition = aX; michael@0: else if (mActivatedHandle == mTopHandle || michael@0: mActivatedHandle == mBottomHandle) michael@0: infoXPosition = aX + (aW / 2); michael@0: else michael@0: // should only occur when mActivatedHandle is one of the 3 right-side michael@0: // handles, but this is a reasonable default if it isn't any of them (?) michael@0: infoXPosition = aX + aW; michael@0: michael@0: if (mActivatedHandle == mTopLeftHandle || michael@0: mActivatedHandle == mTopHandle || michael@0: mActivatedHandle == mTopRightHandle) michael@0: infoYPosition = aY; michael@0: else if (mActivatedHandle == mLeftHandle || michael@0: mActivatedHandle == mRightHandle) michael@0: infoYPosition = aY + (aH / 2); michael@0: else michael@0: // should only occur when mActivatedHandle is one of the 3 bottom-side michael@0: // handles, but this is a reasonable default if it isn't any of them (?) michael@0: infoYPosition = aY + aH; michael@0: michael@0: // Offset info box by 20 so it's not directly under the mouse cursor. michael@0: const int mouseCursorOffset = 20; michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingInfo, leftStr, michael@0: infoXPosition + mouseCursorOffset); michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingInfo, topStr, michael@0: infoYPosition + mouseCursorOffset); michael@0: michael@0: nsCOMPtr textInfo; michael@0: nsresult res = mResizingInfo->GetFirstChild(getter_AddRefs(textInfo)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: nsCOMPtr junk; michael@0: if (textInfo) { michael@0: res = mResizingInfo->RemoveChild(textInfo, getter_AddRefs(junk)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: textInfo = nullptr; michael@0: junk = nullptr; michael@0: } michael@0: michael@0: nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr; michael@0: widthStr.AppendInt(aW); michael@0: heightStr.AppendInt(aH); michael@0: int32_t diffWidth = aW - mResizedObjectWidth; michael@0: int32_t diffHeight = aH - mResizedObjectHeight; michael@0: if (diffWidth > 0) michael@0: diffWidthStr.AssignLiteral("+"); michael@0: if (diffHeight > 0) michael@0: diffHeightStr.AssignLiteral("+"); michael@0: diffWidthStr.AppendInt(diffWidth); michael@0: diffHeightStr.AppendInt(diffHeight); michael@0: michael@0: nsAutoString info(widthStr + NS_LITERAL_STRING(" x ") + heightStr + michael@0: NS_LITERAL_STRING(" (") + diffWidthStr + michael@0: NS_LITERAL_STRING(", ") + diffHeightStr + michael@0: NS_LITERAL_STRING(")")); michael@0: michael@0: nsCOMPtr nodeAsText; michael@0: res = domdoc->CreateTextNode(info, getter_AddRefs(nodeAsText)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: textInfo = do_QueryInterface(nodeAsText); michael@0: res = mResizingInfo->AppendChild(textInfo, getter_AddRefs(junk)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: bool hasClass = false; michael@0: if (NS_SUCCEEDED(mResizingInfo->HasAttribute(NS_LITERAL_STRING("class"), &hasClass )) && hasClass) michael@0: res = mResizingInfo->RemoveAttribute(NS_LITERAL_STRING("class")); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::SetShadowPosition(nsIDOMElement * aShadow, michael@0: nsIDOMElement * aOriginalObject, michael@0: int32_t aOriginalObjectX, michael@0: int32_t aOriginalObjectY) michael@0: { michael@0: SetAnonymousElementPosition(aOriginalObjectX, aOriginalObjectY, aShadow); michael@0: michael@0: if (nsHTMLEditUtils::IsImage(aOriginalObject)) { michael@0: nsAutoString imageSource; michael@0: nsresult res = aOriginalObject->GetAttribute(NS_LITERAL_STRING("src"), michael@0: imageSource); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = aShadow->SetAttribute(NS_LITERAL_STRING("src"), imageSource); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: nsHTMLEditor::GetNewResizingIncrement(int32_t aX, int32_t aY, int32_t aID) michael@0: { michael@0: int32_t result = 0; michael@0: if (!mPreserveRatio) { michael@0: switch (aID) { michael@0: case kX: michael@0: case kWidth: michael@0: result = aX - mOriginalX; michael@0: break; michael@0: case kY: michael@0: case kHeight: michael@0: result = aY - mOriginalY; michael@0: break; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: int32_t xi = (aX - mOriginalX) * mWidthIncrementFactor; michael@0: int32_t yi = (aY - mOriginalY) * mHeightIncrementFactor; michael@0: float objectSizeRatio = michael@0: ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight); michael@0: result = (xi > yi) ? xi : yi; michael@0: switch (aID) { michael@0: case kX: michael@0: case kWidth: michael@0: if (result == yi) michael@0: result = (int32_t) (((float) result) * objectSizeRatio); michael@0: result = (int32_t) (((float) result) * mWidthIncrementFactor); michael@0: break; michael@0: case kY: michael@0: case kHeight: michael@0: if (result == xi) michael@0: result = (int32_t) (((float) result) / objectSizeRatio); michael@0: result = (int32_t) (((float) result) * mHeightIncrementFactor); michael@0: break; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: int32_t michael@0: nsHTMLEditor::GetNewResizingX(int32_t aX, int32_t aY) michael@0: { michael@0: int32_t resized = mResizedObjectX + michael@0: GetNewResizingIncrement(aX, aY, kX) * mXIncrementFactor; michael@0: int32_t max = mResizedObjectX + mResizedObjectWidth; michael@0: return std::min(resized, max); michael@0: } michael@0: michael@0: int32_t michael@0: nsHTMLEditor::GetNewResizingY(int32_t aX, int32_t aY) michael@0: { michael@0: int32_t resized = mResizedObjectY + michael@0: GetNewResizingIncrement(aX, aY, kY) * mYIncrementFactor; michael@0: int32_t max = mResizedObjectY + mResizedObjectHeight; michael@0: return std::min(resized, max); michael@0: } michael@0: michael@0: int32_t michael@0: nsHTMLEditor::GetNewResizingWidth(int32_t aX, int32_t aY) michael@0: { michael@0: int32_t resized = mResizedObjectWidth + michael@0: GetNewResizingIncrement(aX, aY, kWidth) * michael@0: mWidthIncrementFactor; michael@0: return std::max(resized, 1); michael@0: } michael@0: michael@0: int32_t michael@0: nsHTMLEditor::GetNewResizingHeight(int32_t aX, int32_t aY) michael@0: { michael@0: int32_t resized = mResizedObjectHeight + michael@0: GetNewResizingIncrement(aX, aY, kHeight) * michael@0: mHeightIncrementFactor; michael@0: return std::max(resized, 1); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent) michael@0: { michael@0: NS_NAMED_LITERAL_STRING(leftStr, "left"); michael@0: NS_NAMED_LITERAL_STRING(topStr, "top"); michael@0: michael@0: if (mIsResizing) { michael@0: // we are resizing and the mouse pointer's position has changed michael@0: // we have to resdisplay the shadow michael@0: nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); michael@0: int32_t clientX, clientY; michael@0: mouseEvent->GetClientX(&clientX); michael@0: mouseEvent->GetClientY(&clientY); michael@0: michael@0: int32_t newX = GetNewResizingX(clientX, clientY); michael@0: int32_t newY = GetNewResizingY(clientX, clientY); michael@0: int32_t newWidth = GetNewResizingWidth(clientX, clientY); michael@0: int32_t newHeight = GetNewResizingHeight(clientX, clientY); michael@0: michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, michael@0: leftStr, michael@0: newX); michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, michael@0: topStr, michael@0: newY); michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, michael@0: NS_LITERAL_STRING("width"), michael@0: newWidth); michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, michael@0: NS_LITERAL_STRING("height"), michael@0: newHeight); michael@0: michael@0: return SetResizingInfoPosition(newX, newY, newWidth, newHeight); michael@0: } michael@0: michael@0: if (mGrabberClicked) { michael@0: nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); michael@0: int32_t clientX, clientY; michael@0: mouseEvent->GetClientX(&clientX); michael@0: mouseEvent->GetClientY(&clientY); michael@0: michael@0: int32_t xThreshold = michael@0: LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 1); michael@0: int32_t yThreshold = michael@0: LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 1); michael@0: michael@0: if (DeprecatedAbs(clientX - mOriginalX) * 2 >= xThreshold || michael@0: DeprecatedAbs(clientY - mOriginalY) * 2 >= yThreshold) { michael@0: mGrabberClicked = false; michael@0: StartMoving(nullptr); michael@0: } michael@0: } michael@0: if (mIsMoving) { michael@0: nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); michael@0: int32_t clientX, clientY; michael@0: mouseEvent->GetClientX(&clientX); michael@0: mouseEvent->GetClientY(&clientY); michael@0: michael@0: int32_t newX = mPositionedObjectX + clientX - mOriginalX; michael@0: int32_t newY = mPositionedObjectY + clientY - mOriginalY; michael@0: michael@0: SnapToGrid(newX, newY); michael@0: michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, leftStr, newX); michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, topStr, newY); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsHTMLEditor::SetFinalSize(int32_t aX, int32_t aY) michael@0: { michael@0: if (!mResizedObject) { michael@0: // paranoia michael@0: return; michael@0: } michael@0: michael@0: if (mActivatedHandle) { michael@0: mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated")); michael@0: mActivatedHandle = nullptr; michael@0: } michael@0: michael@0: // we have now to set the new width and height of the resized object michael@0: // we don't set the x and y position because we don't control that in michael@0: // a normal HTML layout michael@0: int32_t left = GetNewResizingX(aX, aY); michael@0: int32_t top = GetNewResizingY(aX, aY); michael@0: int32_t width = GetNewResizingWidth(aX, aY); michael@0: int32_t height = GetNewResizingHeight(aX, aY); michael@0: bool setWidth = !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth); michael@0: bool setHeight = !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight); michael@0: michael@0: int32_t x, y; michael@0: x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft+mResizedObjectMarginLeft : 0); michael@0: y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop+mResizedObjectMarginTop : 0); michael@0: michael@0: // we want one transaction only from a user's point of view michael@0: nsAutoEditBatch batchIt(this); michael@0: michael@0: NS_NAMED_LITERAL_STRING(widthStr, "width"); michael@0: NS_NAMED_LITERAL_STRING(heightStr, "height"); michael@0: michael@0: bool hasAttr = false; michael@0: if (mResizedObjectIsAbsolutelyPositioned) { michael@0: if (setHeight) michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, michael@0: nsEditProperty::cssTop, michael@0: y, michael@0: false); michael@0: if (setWidth) michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, michael@0: nsEditProperty::cssLeft, michael@0: x, michael@0: false); michael@0: } michael@0: if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) { michael@0: if (setWidth && NS_SUCCEEDED(mResizedObject->HasAttribute(widthStr, &hasAttr)) && hasAttr) michael@0: RemoveAttribute(mResizedObject, widthStr); michael@0: michael@0: hasAttr = false; michael@0: if (setHeight && NS_SUCCEEDED(mResizedObject->HasAttribute(heightStr, &hasAttr)) && hasAttr) michael@0: RemoveAttribute(mResizedObject, heightStr); michael@0: michael@0: if (setWidth) michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, michael@0: nsEditProperty::cssWidth, michael@0: width, michael@0: false); michael@0: if (setHeight) michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, michael@0: nsEditProperty::cssHeight, michael@0: height, michael@0: false); michael@0: } michael@0: else { michael@0: // we use HTML size and remove all equivalent CSS properties michael@0: michael@0: // we set the CSS width and height to remove it later, michael@0: // triggering an immediate reflow; otherwise, we have problems michael@0: // with asynchronous reflow michael@0: if (setWidth) michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, michael@0: nsEditProperty::cssWidth, michael@0: width, michael@0: false); michael@0: if (setHeight) michael@0: mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, michael@0: nsEditProperty::cssHeight, michael@0: height, michael@0: false); michael@0: michael@0: if (setWidth) { michael@0: nsAutoString w; michael@0: w.AppendInt(width); michael@0: SetAttribute(mResizedObject, widthStr, w); michael@0: } michael@0: if (setHeight) { michael@0: nsAutoString h; michael@0: h.AppendInt(height); michael@0: SetAttribute(mResizedObject, heightStr, h); michael@0: } michael@0: michael@0: if (setWidth) michael@0: mHTMLCSSUtils->RemoveCSSProperty(mResizedObject, michael@0: nsEditProperty::cssWidth, michael@0: EmptyString(), michael@0: false); michael@0: if (setHeight) michael@0: mHTMLCSSUtils->RemoveCSSProperty(mResizedObject, michael@0: nsEditProperty::cssHeight, michael@0: EmptyString(), michael@0: false); michael@0: } michael@0: // finally notify the listeners if any michael@0: int32_t listenersCount = objectResizeEventListeners.Count(); michael@0: if (listenersCount) { michael@0: nsCOMPtr listener; michael@0: int32_t index; michael@0: for (index = 0; index < listenersCount; index++) { michael@0: listener = objectResizeEventListeners[index]; michael@0: listener->OnEndResizing(mResizedObject, michael@0: mResizedObjectWidth, mResizedObjectHeight, michael@0: width, height); michael@0: } michael@0: } michael@0: michael@0: // keep track of that size michael@0: mResizedObjectWidth = width; michael@0: mResizedObjectHeight = height; michael@0: michael@0: RefreshResizers(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetResizedObject(nsIDOMElement * *aResizedObject) michael@0: { michael@0: *aResizedObject = mResizedObject; michael@0: NS_IF_ADDREF(*aResizedObject); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetObjectResizingEnabled(bool *aIsObjectResizingEnabled) michael@0: { michael@0: *aIsObjectResizingEnabled = mIsObjectResizingEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SetObjectResizingEnabled(bool aObjectResizingEnabled) michael@0: { michael@0: mIsObjectResizingEnabled = aObjectResizingEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::AddObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: if (objectResizeEventListeners.Count() && michael@0: objectResizeEventListeners.IndexOf(aListener) != -1) { michael@0: /* listener already registered */ michael@0: NS_ASSERTION(false, michael@0: "trying to register an already registered object resize event listener"); michael@0: return NS_OK; michael@0: } michael@0: objectResizeEventListeners.AppendObject(aListener); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::RemoveObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: if (!objectResizeEventListeners.Count() || michael@0: objectResizeEventListeners.IndexOf(aListener) == -1) { michael@0: /* listener was not registered */ michael@0: NS_ASSERTION(false, michael@0: "trying to remove an object resize event listener that was not already registered"); michael@0: return NS_OK; michael@0: } michael@0: objectResizeEventListeners.RemoveObject(aListener); michael@0: return NS_OK; michael@0: } michael@0: