/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla.org. * * The Initial Developer of the Original Code is * Netscape Communications Corp. * Portions created by the Initial Developer are Copyright (C) 2003 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Daniel Glazman (glazman@netscape.com) (Original author) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsHTMLObjectResizer.h" #include "nsIDOMEventTarget.h" #include "nsIDOMNSHTMLElement.h" #include "nsPIDOMEventTarget.h" #include "nsIDOMText.h" #include "nsIDOMCSSValue.h" #include "nsIDOMCSSPrimitiveValue.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsIDocumentObserver.h" #include "nsIEditor.h" #include "nsIPresShell.h" #include "nsPIDOMWindow.h" #include "nsHTMLEditor.h" #include "nsEditor.h" #include "nsEditorUtils.h" #include "nsHTMLEditUtils.h" #include "nsPoint.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsIServiceManager.h" #include "nsPresContext.h" #include "nsILookAndFeel.h" #include "nsWidgetsCID.h" class nsHTMLEditUtils; static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); // ================================================================== // DocumentResizeEventListener // ================================================================== NS_IMPL_ISUPPORTS1(DocumentResizeEventListener, nsIDOMEventListener) DocumentResizeEventListener::DocumentResizeEventListener(nsIHTMLEditor * aEditor) { mEditor = do_GetWeakReference(aEditor); } DocumentResizeEventListener::~DocumentResizeEventListener() { } NS_IMETHODIMP DocumentResizeEventListener::HandleEvent(nsIDOMEvent* aMouseEvent) { nsCOMPtr objectResizer = do_QueryReferent(mEditor); if (objectResizer) return objectResizer->RefreshResizers(); return NS_OK; } // ================================================================== // ResizerSelectionListener // ================================================================== NS_IMPL_ISUPPORTS1(ResizerSelectionListener, nsISelectionListener) ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor * aEditor) { mEditor = do_GetWeakReference(aEditor); } ResizerSelectionListener::~ResizerSelectionListener() { } NS_IMETHODIMP ResizerSelectionListener::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aSelection, PRInt16 aReason) { if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON | nsISelectionListener::KEYPRESS_REASON | nsISelectionListener::SELECTALL_REASON)) && aSelection) { // the selection changed and we need to check if we have to // hide and/or redisplay resizing handles nsCOMPtr editor = do_QueryReferent(mEditor); if (editor) editor->CheckSelectionStateForAnonymousButtons(aSelection); } return NS_OK; } // ================================================================== // ResizerMouseMotionListener // ================================================================== NS_IMPL_ISUPPORTS2(ResizerMouseMotionListener, nsIDOMEventListener, nsIDOMMouseMotionListener) ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor * aEditor) { mEditor = do_GetWeakReference(aEditor); } ResizerMouseMotionListener::~ResizerMouseMotionListener() { } NS_IMETHODIMP ResizerMouseMotionListener::MouseMove(nsIDOMEvent* aMouseEvent) { nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); if (!mouseEvent) { //non-ui event passed in. bad things. return NS_OK; } // Don't do anything special if not an HTML object resizer editor nsCOMPtr objectResizer = do_QueryReferent(mEditor); if (objectResizer) { // check if we have to redisplay a resizing shadow objectResizer->MouseMove(aMouseEvent); } return NS_OK; } NS_IMETHODIMP ResizerMouseMotionListener::HandleEvent(nsIDOMEvent* aMouseEvent) { return NS_OK; } NS_IMETHODIMP ResizerMouseMotionListener::DragMove(nsIDOMEvent* aMouseEvent) { return NS_OK; } // ================================================================== // nsHTMLEditor // ================================================================== nsresult nsHTMLEditor::CreateResizer(nsIDOMElement ** aReturn, PRInt16 aLocation, nsIDOMNode * aParentNode) { nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"), aParentNode, NS_LITERAL_STRING("mozResizer"), PR_FALSE, aReturn); if (NS_FAILED(res)) return res; if (!*aReturn) return NS_ERROR_FAILURE; // add the mouse listener so we can detect a click on a resizer nsCOMPtr evtTarget(do_QueryInterface(*aReturn)); evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMouseListenerP, PR_TRUE); nsAutoString locationStr; switch (aLocation) { case nsIHTMLObjectResizer::eTopLeft: locationStr = kTopLeft; break; case nsIHTMLObjectResizer::eTop: locationStr = kTop; break; case nsIHTMLObjectResizer::eTopRight: locationStr = kTopRight; break; case nsIHTMLObjectResizer::eLeft: locationStr = kLeft; break; case nsIHTMLObjectResizer::eRight: locationStr = kRight; break; case nsIHTMLObjectResizer::eBottomLeft: locationStr = kBottomLeft; break; case nsIHTMLObjectResizer::eBottom: locationStr = kBottom; break; case nsIHTMLObjectResizer::eBottomRight: locationStr = kBottomRight; break; } res = (*aReturn)->SetAttribute(NS_LITERAL_STRING("anonlocation"), locationStr); return res; } nsresult nsHTMLEditor::CreateShadow(nsIDOMElement ** aReturn, nsIDOMNode * aParentNode, nsIDOMElement * aOriginalObject) { // let's create an image through the element factory nsAutoString name; if (nsHTMLEditUtils::IsImage(aOriginalObject)) name.AssignLiteral("img"); else name.AssignLiteral("span"); nsresult res = CreateAnonymousElement(name, aParentNode, NS_LITERAL_STRING("mozResizingShadow"), PR_TRUE, aReturn); if (!*aReturn) return NS_ERROR_FAILURE; return res; } nsresult nsHTMLEditor::CreateResizingInfo(nsIDOMElement ** aReturn, nsIDOMNode * aParentNode) { // let's create an info box through the element factory nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"), aParentNode, NS_LITERAL_STRING("mozResizingInfo"), PR_TRUE, aReturn); if (!*aReturn) return NS_ERROR_FAILURE; return res; } nsresult nsHTMLEditor::SetAllResizersPosition() { if (!mTopLeftHandle) return NS_ERROR_FAILURE; PRInt32 x = mResizedObjectX; PRInt32 y = mResizedObjectY; PRInt32 w = mResizedObjectWidth; PRInt32 h = mResizedObjectHeight; // now let's place all the resizers around the image // get the size of resizers nsAutoString value; float resizerWidth, resizerHeight; nsCOMPtr dummyUnit; mHTMLCSSUtils->GetComputedProperty(mTopLeftHandle, nsEditProperty::cssWidth, value); mHTMLCSSUtils->ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit)); mHTMLCSSUtils->GetComputedProperty(mTopLeftHandle, nsEditProperty::cssHeight, value); mHTMLCSSUtils->ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit)); PRInt32 rw = (PRInt32)((resizerWidth + 1) / 2); PRInt32 rh = (PRInt32)((resizerHeight+ 1) / 2); SetAnonymousElementPosition(x-rw, y-rh, mTopLeftHandle); SetAnonymousElementPosition(x+w/2-rw, y-rh, mTopHandle); SetAnonymousElementPosition(x+w-rw-1, y-rh, mTopRightHandle); SetAnonymousElementPosition(x-rw, y+h/2-rh, mLeftHandle); SetAnonymousElementPosition(x+w-rw-1, y+h/2-rh, mRightHandle); SetAnonymousElementPosition(x-rw, y+h-rh-1, mBottomLeftHandle); SetAnonymousElementPosition(x+w/2-rw, y+h-rh-1, mBottomHandle); SetAnonymousElementPosition(x+w-rw-1, y+h-rh-1, mBottomRightHandle); return NS_OK; } NS_IMETHODIMP nsHTMLEditor::RefreshResizers() { // nothing to do if resizers are not displayed... if (!mResizedObject) return NS_OK; nsresult res = GetPositionAndDimensions(mResizedObject, mResizedObjectX, mResizedObjectY, mResizedObjectWidth, mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop, mResizedObjectMarginLeft, mResizedObjectMarginTop); if (NS_FAILED(res)) return res; res = SetAllResizersPosition(); if (NS_FAILED(res)) return res; return SetShadowPosition(mResizingShadow, mResizedObject, mResizedObjectX, mResizedObjectY); } NS_IMETHODIMP nsHTMLEditor::ShowResizers(nsIDOMElement *aResizedElement) { NS_ENSURE_ARG_POINTER(aResizedElement); if (mResizedObject) { NS_ERROR("call HideResizers first"); return NS_ERROR_UNEXPECTED; } mResizedObject = aResizedElement; // The resizers and the shadow will be anonymous siblings of the element. nsresult res; nsCOMPtr parentNode; res = aResizedElement->GetParentNode(getter_AddRefs(parentNode)); NS_ENSURE_SUCCESS(res, res); res = CreateResizer(getter_AddRefs(mTopLeftHandle), nsIHTMLObjectResizer::eTopLeft, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mTopHandle), nsIHTMLObjectResizer::eTop, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mTopRightHandle), nsIHTMLObjectResizer::eTopRight, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mLeftHandle), nsIHTMLObjectResizer::eLeft, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mRightHandle), nsIHTMLObjectResizer::eRight, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mBottomLeftHandle), nsIHTMLObjectResizer::eBottomLeft, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mBottomHandle), nsIHTMLObjectResizer::eBottom, parentNode); if (NS_FAILED(res)) return res; res = CreateResizer(getter_AddRefs(mBottomRightHandle), nsIHTMLObjectResizer::eBottomRight, parentNode); if (NS_FAILED(res)) return res; res = GetPositionAndDimensions(aResizedElement, mResizedObjectX, mResizedObjectY, mResizedObjectWidth, mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop, mResizedObjectMarginLeft, mResizedObjectMarginTop); if (NS_FAILED(res)) return res; // and let's set their absolute positions in the document res = SetAllResizersPosition(); if (NS_FAILED(res)) return res; // now, let's create the resizing shadow res = CreateShadow(getter_AddRefs(mResizingShadow), parentNode, aResizedElement); if (NS_FAILED(res)) return res; // and set its position res = SetShadowPosition(mResizingShadow, mResizedObject, mResizedObjectX, mResizedObjectY); if (NS_FAILED(res)) return res; // and then the resizing info tooltip res = CreateResizingInfo(getter_AddRefs(mResizingInfo), parentNode); if (NS_FAILED(res)) return res; // and listen to the "resize" event on the window first, get the // window from the document... nsCOMPtr domDoc; GetDocument(getter_AddRefs(domDoc)); nsCOMPtr doc = do_QueryInterface(domDoc); if (!doc) return NS_ERROR_NULL_POINTER; nsCOMPtr target = do_QueryInterface(doc->GetWindow()); if (!target) { return NS_ERROR_NULL_POINTER; } mResizeEventListenerP = new DocumentResizeEventListener(this); if (!mResizeEventListenerP) { return NS_ERROR_OUT_OF_MEMORY; } res = target->AddEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, PR_FALSE); aResizedElement->SetAttribute(NS_LITERAL_STRING("_moz_resizing"), NS_LITERAL_STRING("true")); return res; } NS_IMETHODIMP nsHTMLEditor::HideResizers(void) { if (!mResizedObject) return NS_OK; // get the presshell's document observer interface. nsCOMPtr ps = do_QueryReferent(mPresShellWeak); if (!ps) return NS_ERROR_NOT_INITIALIZED; nsresult res; nsCOMPtr parentNode; res = mTopLeftHandle->GetParentNode(getter_AddRefs(parentNode)); NS_ENSURE_SUCCESS(res, res); nsCOMPtr parentContent = do_QueryInterface(parentNode); NS_NAMED_LITERAL_STRING(mousedown, "mousedown"); RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mTopLeftHandle, parentContent, ps); mTopLeftHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mTopHandle, parentContent, ps); mTopHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mTopRightHandle, parentContent, ps); mTopRightHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mLeftHandle, parentContent, ps); mLeftHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mRightHandle, parentContent, ps); mRightHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mBottomLeftHandle, parentContent, ps); mBottomLeftHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mBottomHandle, parentContent, ps); mBottomHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mBottomRightHandle, parentContent, ps); mBottomRightHandle = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mResizingShadow, parentContent, ps); mResizingShadow = nsnull; RemoveListenerAndDeleteRef(mousedown, mMouseListenerP, PR_TRUE, mResizingInfo, parentContent, ps); mResizingInfo = nsnull; if (mActivatedHandle) { mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated")); mActivatedHandle = nsnull; } // don't forget to remove the listeners ! nsCOMPtr piTarget = GetPIDOMEventTarget(); nsCOMPtr target = do_QueryInterface(piTarget); if (target && mMouseMotionListenerP) { res = target->RemoveEventListener(NS_LITERAL_STRING("mousemove"), mMouseMotionListenerP, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove mouse motion listener"); } mMouseMotionListenerP = nsnull; nsCOMPtr domDoc; GetDocument(getter_AddRefs(domDoc)); nsCOMPtr doc = do_QueryInterface(domDoc); if (!doc) { return NS_ERROR_NULL_POINTER; } target = do_QueryInterface(doc->GetWindow()); if (!target) { return NS_ERROR_NULL_POINTER; } if (mResizeEventListenerP) { res = target->RemoveEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove resize event listener"); } mResizeEventListenerP = nsnull; mResizedObject->RemoveAttribute(NS_LITERAL_STRING("_moz_resizing")); mResizedObject = nsnull; return NS_OK; } void nsHTMLEditor::HideShadowAndInfo() { if (mResizingShadow) mResizingShadow->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden")); if (mResizingInfo) mResizingInfo->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden")); } nsresult nsHTMLEditor::StartResizing(nsIDOMElement *aHandle) { // First notify the listeners if any PRInt32 listenersCount = objectResizeEventListeners.Count(); if (listenersCount) { nsCOMPtr listener; PRInt32 index; for (index = 0; index < listenersCount; index++) { listener = objectResizeEventListeners[index]; listener->OnStartResizing(mResizedObject); } } mIsResizing = PR_TRUE; mActivatedHandle = aHandle; mActivatedHandle->SetAttribute(NS_LITERAL_STRING("_moz_activated"), NS_LITERAL_STRING("true")); // do we want to preserve ratio or not? PRBool preserveRatio = nsHTMLEditUtils::IsImage(mResizedObject); nsresult result; nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &result); if (NS_SUCCEEDED(result) && prefBranch && preserveRatio) { result = prefBranch->GetBoolPref("editor.resizing.preserve_ratio", &preserveRatio); if (NS_FAILED(result)) { // just in case Anvil does not update its prefs file // and because it really does not make sense to me to allow free // resizing on corners without a modifier key preserveRatio = PR_TRUE; } } // the way we change the position/size of the shadow depends on // the handle nsAutoString locationStr; aHandle->GetAttribute(NS_LITERAL_STRING("anonlocation"), locationStr); if (locationStr.Equals(kTopLeft)) { SetResizeIncrements(1, 1, -1, -1, preserveRatio); } else if (locationStr.Equals(kTop)) { SetResizeIncrements(0, 1, 0, -1, PR_FALSE); } else if (locationStr.Equals(kTopRight)) { SetResizeIncrements(0, 1, 1, -1, preserveRatio); } else if (locationStr.Equals(kLeft)) { SetResizeIncrements(1, 0, -1, 0, PR_FALSE); } else if (locationStr.Equals(kRight)) { SetResizeIncrements(0, 0, 1, 0, PR_FALSE); } else if (locationStr.Equals(kBottomLeft)) { SetResizeIncrements(1, 0, -1, 1, preserveRatio); } else if (locationStr.Equals(kBottom)) { SetResizeIncrements(0, 0, 0, 1, PR_FALSE); } else if (locationStr.Equals(kBottomRight)) { SetResizeIncrements(0, 0, 1, 1, preserveRatio); } // make the shadow appear mResizingShadow->RemoveAttribute(NS_LITERAL_STRING("class")); // position it mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, NS_LITERAL_STRING("width"), mResizedObjectWidth); mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, NS_LITERAL_STRING("height"), mResizedObjectHeight); // add a mouse move listener to the editor if (!mMouseMotionListenerP) { mMouseMotionListenerP = new ResizerMouseMotionListener(this); if (!mMouseMotionListenerP) { return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr piTarget = GetPIDOMEventTarget(); nsCOMPtr target = do_QueryInterface(piTarget); NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); result = target->AddEventListener(NS_LITERAL_STRING("mousemove"), mMouseMotionListenerP, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(result), "failed to register mouse motion listener"); } return result; } NS_IMETHODIMP nsHTMLEditor::MouseDown(PRInt32 aClientX, PRInt32 aClientY, nsIDOMElement *aTarget) { PRBool anonElement = PR_FALSE; if (aTarget && NS_SUCCEEDED(aTarget->HasAttribute(NS_LITERAL_STRING("_moz_anonclass"), &anonElement))) // we caught a click on an anonymous element if (anonElement) { nsAutoString anonclass; nsresult res = aTarget->GetAttribute(NS_LITERAL_STRING("_moz_anonclass"), anonclass); if (NS_FAILED(res)) return res; if (anonclass.EqualsLiteral("mozResizer")) { // and that element is a resizer, let's start resizing! mOriginalX = aClientX; mOriginalY = aClientY; return StartResizing(aTarget); } if (anonclass.EqualsLiteral("mozGrabber")) { // and that element is a grabber, let's start moving the element! mOriginalX = aClientX; mOriginalY = aClientY; return GrabberClicked(); } } return NS_OK; } NS_IMETHODIMP nsHTMLEditor::MouseUp(PRInt32 aClientX, PRInt32 aClientY, nsIDOMElement *aTarget) { if (mIsResizing) { // we are resizing and release the mouse button, so let's // end the resizing process mIsResizing = PR_FALSE; HideShadowAndInfo(); SetFinalSize(aClientX, aClientY); } else if (mIsMoving || mGrabberClicked) { if (mIsMoving) { mPositioningShadow->SetAttribute(NS_LITERAL_STRING("class"), NS_LITERAL_STRING("hidden")); SetFinalPosition(aClientX, aClientY); } if (mGrabberClicked) { EndMoving(); } } return NS_OK; } void nsHTMLEditor::SetResizeIncrements(PRInt32 aX, PRInt32 aY, PRInt32 aW, PRInt32 aH, PRBool aPreserveRatio) { mXIncrementFactor = aX; mYIncrementFactor = aY; mWidthIncrementFactor = aW; mHeightIncrementFactor = aH; mPreserveRatio = aPreserveRatio; } nsresult nsHTMLEditor::SetResizingInfoPosition(PRInt32 aX, PRInt32 aY, PRInt32 aW, PRInt32 aH) { nsCOMPtr domdoc; nsEditor::GetDocument(getter_AddRefs(domdoc)); NS_NAMED_LITERAL_STRING(leftStr, "left"); NS_NAMED_LITERAL_STRING(topStr, "top"); // Determine the position of the resizing info box based upon the new // position and size of the element (aX, aY, aW, aH), and which // resizer is the "activated handle". For example, place the resizing // info box at the bottom-right corner of the new element, if the element // is being resized by the bottom-right resizer. PRInt32 infoXPosition; PRInt32 infoYPosition; if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mLeftHandle || mActivatedHandle == mBottomLeftHandle) infoXPosition = aX; else if (mActivatedHandle == mTopHandle || mActivatedHandle == mBottomHandle) infoXPosition = aX + (aW / 2); else // should only occur when mActivatedHandle is one of the 3 right-side // handles, but this is a reasonable default if it isn't any of them (?) infoXPosition = aX + aW; if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mTopHandle || mActivatedHandle == mTopRightHandle) infoYPosition = aY; else if (mActivatedHandle == mLeftHandle || mActivatedHandle == mRightHandle) infoYPosition = aY + (aH / 2); else // should only occur when mActivatedHandle is one of the 3 bottom-side // handles, but this is a reasonable default if it isn't any of them (?) infoYPosition = aY + aH; // Offset info box by 20 so it's not directly under the mouse cursor. const int mouseCursorOffset = 20; mHTMLCSSUtils->SetCSSPropertyPixels(mResizingInfo, leftStr, infoXPosition + mouseCursorOffset); mHTMLCSSUtils->SetCSSPropertyPixels(mResizingInfo, topStr, infoYPosition + mouseCursorOffset); nsCOMPtr textInfo; nsresult res = mResizingInfo->GetFirstChild(getter_AddRefs(textInfo)); if (NS_FAILED(res)) return res; nsCOMPtr junk; if (textInfo) { res = mResizingInfo->RemoveChild(textInfo, getter_AddRefs(junk)); if (NS_FAILED(res)) return res; textInfo = nsnull; junk = nsnull; } nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr; widthStr.AppendInt(aW); heightStr.AppendInt(aH); PRInt32 diffWidth = aW - mResizedObjectWidth; PRInt32 diffHeight = aH - mResizedObjectHeight; if (diffWidth > 0) diffWidthStr.AssignLiteral("+"); if (diffHeight > 0) diffHeightStr.AssignLiteral("+"); diffWidthStr.AppendInt(diffWidth); diffHeightStr.AppendInt(diffHeight); nsAutoString info(widthStr + NS_LITERAL_STRING(" x ") + heightStr + NS_LITERAL_STRING(" (") + diffWidthStr + NS_LITERAL_STRING(", ") + diffHeightStr + NS_LITERAL_STRING(")")); nsCOMPtr nodeAsText; res = domdoc->CreateTextNode(info, getter_AddRefs(nodeAsText)); if (NS_FAILED(res)) return res; textInfo = do_QueryInterface(nodeAsText); res = mResizingInfo->AppendChild(textInfo, getter_AddRefs(junk)); if (NS_FAILED(res)) return res; PRBool hasClass = PR_FALSE; if (NS_SUCCEEDED(mResizingInfo->HasAttribute(NS_LITERAL_STRING("class"), &hasClass )) && hasClass) res = mResizingInfo->RemoveAttribute(NS_LITERAL_STRING("class")); return res; } nsresult nsHTMLEditor::SetShadowPosition(nsIDOMElement * aShadow, nsIDOMElement * aOriginalObject, PRInt32 aOriginalObjectX, PRInt32 aOriginalObjectY) { SetAnonymousElementPosition(aOriginalObjectX, aOriginalObjectY, aShadow); if (nsHTMLEditUtils::IsImage(aOriginalObject)) { nsAutoString imageSource; nsresult res = aOriginalObject->GetAttribute(NS_LITERAL_STRING("src"), imageSource); if (NS_FAILED(res)) return res; res = aShadow->SetAttribute(NS_LITERAL_STRING("src"), imageSource); if (NS_FAILED(res)) return res; } return NS_OK; } PRInt32 nsHTMLEditor::GetNewResizingIncrement(PRInt32 aX, PRInt32 aY, PRInt32 aID) { PRInt32 result = 0; if (!mPreserveRatio) { switch (aID) { case kX: case kWidth: result = aX - mOriginalX; break; case kY: case kHeight: result = aY - mOriginalY; break; } return result; } PRInt32 xi = (aX - mOriginalX) * mWidthIncrementFactor; PRInt32 yi = (aY - mOriginalY) * mHeightIncrementFactor; float objectSizeRatio = ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight); result = (xi > yi) ? xi : yi; switch (aID) { case kX: case kWidth: if (result == yi) result = (PRInt32) (((float) result) * objectSizeRatio); result = (PRInt32) (((float) result) * mWidthIncrementFactor); break; case kY: case kHeight: if (result == xi) result = (PRInt32) (((float) result) / objectSizeRatio); result = (PRInt32) (((float) result) * mHeightIncrementFactor); break; } return result; } PRInt32 nsHTMLEditor::GetNewResizingX(PRInt32 aX, PRInt32 aY) { PRInt32 resized = mResizedObjectX + GetNewResizingIncrement(aX, aY, kX) * mXIncrementFactor; PRInt32 max = mResizedObjectX + mResizedObjectWidth; return PR_MIN(resized, max); } PRInt32 nsHTMLEditor::GetNewResizingY(PRInt32 aX, PRInt32 aY) { PRInt32 resized = mResizedObjectY + GetNewResizingIncrement(aX, aY, kY) * mYIncrementFactor; PRInt32 max = mResizedObjectY + mResizedObjectHeight; return PR_MIN(resized, max); } PRInt32 nsHTMLEditor::GetNewResizingWidth(PRInt32 aX, PRInt32 aY) { PRInt32 resized = mResizedObjectWidth + GetNewResizingIncrement(aX, aY, kWidth) * mWidthIncrementFactor; return PR_MAX(resized, 1); } PRInt32 nsHTMLEditor::GetNewResizingHeight(PRInt32 aX, PRInt32 aY) { PRInt32 resized = mResizedObjectHeight + GetNewResizingIncrement(aX, aY, kHeight) * mHeightIncrementFactor; return PR_MAX(resized, 1); } NS_IMETHODIMP nsHTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent) { NS_NAMED_LITERAL_STRING(leftStr, "left"); NS_NAMED_LITERAL_STRING(topStr, "top"); if (mIsResizing) { // we are resizing and the mouse pointer's position has changed // we have to resdisplay the shadow nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); PRInt32 clientX, clientY; mouseEvent->GetClientX(&clientX); mouseEvent->GetClientY(&clientY); PRInt32 newX = GetNewResizingX(clientX, clientY); PRInt32 newY = GetNewResizingY(clientX, clientY); PRInt32 newWidth = GetNewResizingWidth(clientX, clientY); PRInt32 newHeight = GetNewResizingHeight(clientX, clientY); mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, leftStr, newX); mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, topStr, newY); mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, NS_LITERAL_STRING("width"), newWidth); mHTMLCSSUtils->SetCSSPropertyPixels(mResizingShadow, NS_LITERAL_STRING("height"), newHeight); return SetResizingInfoPosition(newX, newY, newWidth, newHeight); } if (mGrabberClicked) { nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); PRInt32 clientX, clientY; mouseEvent->GetClientX(&clientX); mouseEvent->GetClientY(&clientY); nsCOMPtr look = do_GetService(kLookAndFeelCID); NS_ASSERTION(look, "Look and feel service must be implemented for this toolkit"); PRInt32 xThreshold=1, yThreshold=1; look->GetMetric(nsILookAndFeel::eMetric_DragThresholdX, xThreshold); look->GetMetric(nsILookAndFeel::eMetric_DragThresholdY, yThreshold); if (PR_ABS(clientX - mOriginalX ) * 2 >= xThreshold || PR_ABS(clientY - mOriginalY ) * 2 >= yThreshold) { mGrabberClicked = PR_FALSE; StartMoving(nsnull); } } if (mIsMoving) { nsCOMPtr mouseEvent ( do_QueryInterface(aMouseEvent) ); PRInt32 clientX, clientY; mouseEvent->GetClientX(&clientX); mouseEvent->GetClientY(&clientY); PRInt32 newX = mPositionedObjectX + clientX - mOriginalX; PRInt32 newY = mPositionedObjectY + clientY - mOriginalY; SnapToGrid(newX, newY); mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, leftStr, newX); mHTMLCSSUtils->SetCSSPropertyPixels(mPositioningShadow, topStr, newY); } return NS_OK; } void nsHTMLEditor::SetFinalSize(PRInt32 aX, PRInt32 aY) { if (!mResizedObject) { // paranoia return; } if (mActivatedHandle) { mActivatedHandle->RemoveAttribute(NS_LITERAL_STRING("_moz_activated")); mActivatedHandle = nsnull; } // we have now to set the new width and height of the resized object // we don't set the x and y position because we don't control that in // a normal HTML layout PRInt32 left = GetNewResizingX(aX, aY); PRInt32 top = GetNewResizingY(aX, aY); PRInt32 width = GetNewResizingWidth(aX, aY); PRInt32 height = GetNewResizingHeight(aX, aY); PRBool setWidth = !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth); PRBool setHeight = !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight); PRInt32 x, y; x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft+mResizedObjectMarginLeft : 0); y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop+mResizedObjectMarginTop : 0); // we need to know if we're in a CSS-enabled editor or not PRBool useCSS; GetIsCSSEnabled(&useCSS); // we want one transaction only from a user's point of view nsAutoEditBatch batchIt(this); NS_NAMED_LITERAL_STRING(widthStr, "width"); NS_NAMED_LITERAL_STRING(heightStr, "height"); PRBool hasAttr = PR_FALSE; if (mResizedObjectIsAbsolutelyPositioned) { if (setHeight) mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, nsEditProperty::cssTop, y, PR_FALSE); if (setWidth) mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, nsEditProperty::cssLeft, x, PR_FALSE); } if (useCSS || mResizedObjectIsAbsolutelyPositioned) { if (setWidth && NS_SUCCEEDED(mResizedObject->HasAttribute(widthStr, &hasAttr)) && hasAttr) RemoveAttribute(mResizedObject, widthStr); hasAttr = PR_FALSE; if (setHeight && NS_SUCCEEDED(mResizedObject->HasAttribute(heightStr, &hasAttr)) && hasAttr) RemoveAttribute(mResizedObject, heightStr); if (setWidth) mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, nsEditProperty::cssWidth, width, PR_FALSE); if (setHeight) mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, nsEditProperty::cssHeight, height, PR_FALSE); } else { // we use HTML size and remove all equivalent CSS properties // we set the CSS width and height to remove it later, // triggering an immediate reflow; otherwise, we have problems // with asynchronous reflow if (setWidth) mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, nsEditProperty::cssWidth, width, PR_FALSE); if (setHeight) mHTMLCSSUtils->SetCSSPropertyPixels(mResizedObject, nsEditProperty::cssHeight, height, PR_FALSE); if (setWidth) { nsAutoString w; w.AppendInt(width); SetAttribute(mResizedObject, widthStr, w); } if (setHeight) { nsAutoString h; h.AppendInt(height); SetAttribute(mResizedObject, heightStr, h); } if (setWidth) mHTMLCSSUtils->RemoveCSSProperty(mResizedObject, nsEditProperty::cssWidth, EmptyString(), PR_FALSE); if (setHeight) mHTMLCSSUtils->RemoveCSSProperty(mResizedObject, nsEditProperty::cssHeight, EmptyString(), PR_FALSE); } // finally notify the listeners if any PRInt32 listenersCount = objectResizeEventListeners.Count(); if (listenersCount) { nsCOMPtr listener; PRInt32 index; for (index = 0; index < listenersCount; index++) { listener = objectResizeEventListeners[index]; listener->OnEndResizing(mResizedObject, mResizedObjectWidth, mResizedObjectHeight, width, height); } } // keep track of that size mResizedObjectWidth = width; mResizedObjectHeight = height; RefreshResizers(); } NS_IMETHODIMP nsHTMLEditor::GetResizedObject(nsIDOMElement * *aResizedObject) { *aResizedObject = mResizedObject; NS_IF_ADDREF(*aResizedObject); return NS_OK; } NS_IMETHODIMP nsHTMLEditor::GetObjectResizingEnabled(PRBool *aIsObjectResizingEnabled) { *aIsObjectResizingEnabled = mIsObjectResizingEnabled; return NS_OK; } NS_IMETHODIMP nsHTMLEditor::SetObjectResizingEnabled(PRBool aObjectResizingEnabled) { mIsObjectResizingEnabled = aObjectResizingEnabled; return NS_OK; } NS_IMETHODIMP nsHTMLEditor::AddObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener) { NS_ENSURE_ARG_POINTER(aListener); if (objectResizeEventListeners.Count() && objectResizeEventListeners.IndexOf(aListener) != -1) { /* listener already registered */ NS_ASSERTION(PR_FALSE, "trying to register an already registered object resize event listener"); return NS_OK; } objectResizeEventListeners.AppendObject(aListener); return NS_OK; } NS_IMETHODIMP nsHTMLEditor::RemoveObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener) { NS_ENSURE_ARG_POINTER(aListener); if (!objectResizeEventListeners.Count() || objectResizeEventListeners.IndexOf(aListener) == -1) { /* listener was not registered */ NS_ASSERTION(PR_FALSE, "trying to remove an object resize event listener that was not already registered"); return NS_OK; } objectResizeEventListeners.RemoveObject(aListener); return NS_OK; }