Bug 178513, support translucent drag feedback when dragging nodes and selections on Mac and GTK, r=vlad,josh,roc,sr=roc
git-svn-id: svn://10.0.0.236/trunk@222094 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -96,6 +96,8 @@
|
||||
#include "nsIMIMEService.h"
|
||||
#include "imgIRequest.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsFrameSelection.h"
|
||||
|
||||
// private clipboard data flavors for html copy, used by editor when pasting
|
||||
#define kHTMLContext "text/_moz_htmlcontext"
|
||||
@@ -117,14 +119,9 @@ NS_INTERFACE_MAP_END
|
||||
class nsTransferableFactory
|
||||
{
|
||||
public:
|
||||
static nsresult CreateFromEvent(nsIDOMEvent* inMouseEvent,
|
||||
nsIFlavorDataProvider *inFlavorDataProvider,
|
||||
nsITransferable** outTrans);
|
||||
|
||||
protected:
|
||||
nsTransferableFactory(nsIDOMEvent* inMouseEvent,
|
||||
nsIFlavorDataProvider *inFlavorDataProvider);
|
||||
nsresult Produce(nsITransferable** outTrans);
|
||||
nsresult Produce(PRBool *aDragSelection, nsITransferable** outTrans);
|
||||
|
||||
private:
|
||||
nsresult ConvertStringsToTransferable(nsITransferable** outTrans);
|
||||
@@ -652,15 +649,6 @@ nsContentAreaDragDrop::GetEventDocument(nsIDOMEvent* inEvent,
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentAreaDragDrop::CreateTransferable(nsIDOMEvent* inMouseEvent,
|
||||
nsITransferable** outTrans)
|
||||
{
|
||||
return nsTransferableFactory::CreateFromEvent(inMouseEvent,
|
||||
NS_STATIC_CAST(nsIFlavorDataProvider*, this),
|
||||
outTrans);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentAreaDragDrop::GetHookEnumeratorFromEvent(nsIDOMEvent* inEvent,
|
||||
nsISimpleEnumerator **outEnumerator)
|
||||
@@ -737,10 +725,10 @@ nsContentAreaDragDrop::DragGesture(nsIDOMEvent* inMouseEvent)
|
||||
}
|
||||
}
|
||||
|
||||
PRBool isSelection = PR_FALSE;
|
||||
nsCOMPtr<nsITransferable> trans;
|
||||
nsresult rv = CreateTransferable(inMouseEvent, getter_AddRefs(trans));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
nsTransferableFactory factory(inMouseEvent, NS_STATIC_CAST(nsIFlavorDataProvider*, this));
|
||||
factory.Produce(&isSelection, getter_AddRefs(trans));
|
||||
|
||||
if (trans) {
|
||||
// if the client has provided an override callback, let them manipulate
|
||||
@@ -784,7 +772,6 @@ nsContentAreaDragDrop::DragGesture(nsIDOMEvent* inMouseEvent)
|
||||
// kick off the drag
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
inMouseEvent->GetTarget(getter_AddRefs(target));
|
||||
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(target));
|
||||
nsCOMPtr<nsIDragService> dragService =
|
||||
do_GetService("@mozilla.org/widget/dragservice;1");
|
||||
|
||||
@@ -792,10 +779,31 @@ nsContentAreaDragDrop::DragGesture(nsIDOMEvent* inMouseEvent)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
dragService->InvokeDragSession(targetNode, transArray, nsnull,
|
||||
nsIDragService::DRAGDROP_ACTION_COPY +
|
||||
nsIDragService::DRAGDROP_ACTION_MOVE +
|
||||
nsIDragService::DRAGDROP_ACTION_LINK);
|
||||
PRUint32 action = nsIDragService::DRAGDROP_ACTION_COPY +
|
||||
nsIDragService::DRAGDROP_ACTION_MOVE +
|
||||
nsIDragService::DRAGDROP_ACTION_LINK;
|
||||
|
||||
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(inMouseEvent));
|
||||
|
||||
if (isSelection) {
|
||||
nsCOMPtr<nsIContent> targetContent(do_QueryInterface(target));
|
||||
nsIDocument* doc = targetContent->GetCurrentDoc();
|
||||
if (doc) {
|
||||
nsIPresShell* presShell = doc->GetShellAt(0);
|
||||
if (presShell) {
|
||||
nsISelection* selection =
|
||||
presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
|
||||
return dragService->InvokeDragSessionWithSelection(selection,
|
||||
transArray,
|
||||
action,
|
||||
mouseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(target));
|
||||
dragService->InvokeDragSessionWithImage(targetNode, transArray, nsnull,
|
||||
action, nsnull, 0, 0, mouseEvent);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -926,16 +934,6 @@ nsContentAreaDragDrop::GetFlavorData(nsITransferable *aTransferable,
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsTransferableFactory::CreateFromEvent(nsIDOMEvent* inMouseEvent,
|
||||
nsIFlavorDataProvider *dataProvider,
|
||||
nsITransferable** outTrans)
|
||||
{
|
||||
nsTransferableFactory factory(inMouseEvent, dataProvider);
|
||||
return factory.Produce(outTrans);
|
||||
}
|
||||
|
||||
nsTransferableFactory::nsTransferableFactory(nsIDOMEvent* inMouseEvent,
|
||||
nsIFlavorDataProvider *dataProvider)
|
||||
: mInstanceAlreadyUsed(PR_FALSE),
|
||||
@@ -1044,7 +1042,8 @@ nsTransferableFactory::GetNodeString(nsIDOMNode* inNode,
|
||||
|
||||
|
||||
nsresult
|
||||
nsTransferableFactory::Produce(nsITransferable** outTrans)
|
||||
nsTransferableFactory::Produce(PRBool* aDragSelection,
|
||||
nsITransferable** outTrans)
|
||||
{
|
||||
if (mInstanceAlreadyUsed) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@@ -1093,6 +1092,7 @@ nsTransferableFactory::Produce(nsITransferable** outTrans)
|
||||
// if set, serialize the content under this node
|
||||
nsCOMPtr<nsIDOMNode> nodeToSerialize;
|
||||
PRBool useSelectedText = PR_FALSE;
|
||||
*aDragSelection = PR_FALSE;
|
||||
|
||||
{
|
||||
PRBool haveSelectedContent = PR_FALSE;
|
||||
@@ -1149,6 +1149,7 @@ nsTransferableFactory::Produce(nsITransferable** outTrans)
|
||||
}
|
||||
|
||||
useSelectedText = PR_TRUE;
|
||||
*aDragSelection = PR_TRUE;
|
||||
} else if (selectedImageOrLinkNode) {
|
||||
// an image is selected
|
||||
image = do_QueryInterface(selectedImageOrLinkNode);
|
||||
|
||||
@@ -109,9 +109,7 @@ private:
|
||||
|
||||
static nsresult SaveURIToFile(nsAString& inSourceURIString,
|
||||
nsIFile* inDestFile);
|
||||
|
||||
nsresult CreateTransferable(nsIDOMEvent* inMouseEvent,
|
||||
nsITransferable** outTrans);
|
||||
|
||||
void ExtractURLFromData(const nsACString & inFlavor,
|
||||
nsISupports* inDataWrapper, PRUint32 inDataLen,
|
||||
nsAString & outURL);
|
||||
|
||||
@@ -90,6 +90,18 @@ nsresult NS_NewContentSubtreeIterator(nsIContentIterator** aInstancePtrResult);
|
||||
nsresult
|
||||
nsRange::CompareNodeToRange(nsIContent* aNode, nsIDOMRange* aRange,
|
||||
PRBool *outNodeBefore, PRBool *outNodeAfter)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIRange> range = do_QueryInterface(aRange, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return CompareNodeToRange(aNode, range, outNodeBefore, outNodeAfter);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsRange::CompareNodeToRange(nsIContent* aNode, nsIRange* aRange,
|
||||
PRBool *outNodeBefore, PRBool *outNodeAfter)
|
||||
{
|
||||
// create a pair of dom points that expresses location of node:
|
||||
// NODE(start), NODE(end)
|
||||
|
||||
@@ -127,6 +127,9 @@ public:
|
||||
static nsresult CompareNodeToRange(nsIContent* aNode, nsIDOMRange* aRange,
|
||||
PRBool *outNodeBefore,
|
||||
PRBool *outNodeAfter);
|
||||
static nsresult CompareNodeToRange(nsIContent* aNode, nsIRange* aRange,
|
||||
PRBool *outNodeBefore,
|
||||
PRBool *outNodeAfter);
|
||||
|
||||
protected:
|
||||
void DoSetRange(nsINode* aStartN, PRInt32 aStartOffset,
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
// Misc
|
||||
#include "nsEditorUtils.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsFrameSelection.h"
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
|
||||
{
|
||||
@@ -401,11 +403,18 @@ NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
|
||||
|
||||
nsCOMPtr<nsIScriptableRegion> selRegion;
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
rv = GetSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
unsigned int flags;
|
||||
// in some cases we'll want to cut rather than copy... hmmmmm...
|
||||
flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
|
||||
|
||||
rv = dragService->InvokeDragSession(domnode, transferableArray, nsnull, flags);
|
||||
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aDragEvent));
|
||||
rv = dragService->InvokeDragSessionWithSelection(selection, transferableArray,
|
||||
flags, mouseEvent);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
aDragEvent->StopPropagation();
|
||||
|
||||
@@ -88,6 +88,7 @@ class nsILayoutHistoryState;
|
||||
class nsIReflowCallback;
|
||||
class nsISupportsArray;
|
||||
class nsIDOMNode;
|
||||
class nsIRegion;
|
||||
class nsIStyleFrameConstruction;
|
||||
class nsIStyleSheet;
|
||||
class nsCSSFrameConstructor;
|
||||
@@ -95,13 +96,14 @@ class nsISelection;
|
||||
template<class E> class nsCOMArray;
|
||||
class nsWeakFrame;
|
||||
class nsIScrollableFrame;
|
||||
class gfxASurface;
|
||||
|
||||
typedef short SelectionType;
|
||||
|
||||
// c37fd598-ed7f-4f39-bdf9-96642c691c7b
|
||||
// A68CFCE1-2A15-47F0-B61B-F2C34079A909
|
||||
#define NS_IPRESSHELL_IID \
|
||||
{ 0xc37fd598, 0xed7f, 0x4f39, \
|
||||
{ 0xbd, 0xf9, 0x96, 0x64, 0x2c, 0x69, 0x1c, 0x7b } }
|
||||
{ 0xa78cfce1, 0x2a15, 0x47f0, \
|
||||
{ 0xb6, 0x1b, 0xf2, 0xc3, 0x40, 0x79, 0xa9, 0x09 } }
|
||||
|
||||
// Constants for ScrollContentIntoView() function
|
||||
#define NS_PRESSHELL_SCROLL_TOP 0
|
||||
@@ -681,6 +683,36 @@ public:
|
||||
nscolor aBackgroundColor,
|
||||
nsIRenderingContext** aRenderedContext) = 0;
|
||||
|
||||
/**
|
||||
* Renders a node aNode to a surface and returns it. The aRegion may be used
|
||||
* to clip the rendering. This region is measured in device pixels from the
|
||||
* edge of the presshell area. The aPoint, aScreenRect and aSurface
|
||||
* arguments function in a similar manner as RenderSelection.
|
||||
*/
|
||||
virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
|
||||
nsIRegion* aRegion,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect) = 0;
|
||||
|
||||
/*
|
||||
* Renders a selection to a surface and returns it. This method is primarily
|
||||
* intended to create the drag feedback when dragging a selection.
|
||||
*
|
||||
* aScreenRect will be filled in with the bounding rectangle of the
|
||||
* selection area on screen.
|
||||
*
|
||||
* If the area of the selection is large, the image will be scaled down.
|
||||
* The argument aPoint is used in this case as a reference point when
|
||||
* determining the new screen rectangle after scaling. Typically, this
|
||||
* will be the mouse position, so that the screen rectangle is positioned
|
||||
* such that the mouse is over the same point in the scaled image as in
|
||||
* the original. When scaling does not occur, the mouse point isn't used
|
||||
* as the position can be determined from the displayed frames.
|
||||
*/
|
||||
virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect) = 0;
|
||||
|
||||
void AddWeakFrame(nsWeakFrame* aWeakFrame);
|
||||
void RemoveWeakFrame(nsWeakFrame* aWeakFrame);
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsLayoutCID.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsIDOMRange.h"
|
||||
@@ -97,6 +98,7 @@
|
||||
#include "nsIDOM3Node.h"
|
||||
#include "nsIDOMNodeList.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsRange.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
@@ -142,6 +144,7 @@
|
||||
#include "nsIAttribute.h"
|
||||
#include "nsIGlobalHistory2.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsIRegion.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
#ifdef MOZ_REFLOW_PERF_DSP
|
||||
@@ -160,6 +163,8 @@
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsStyleSheetService.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfxContext.h"
|
||||
|
||||
// Drag & Drop, Clipboard
|
||||
#include "nsWidgetsCID.h"
|
||||
@@ -205,6 +210,26 @@ static void ColorToString(nscolor aColor, nsAutoString &aString);
|
||||
// Class ID's
|
||||
static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
|
||||
|
||||
// RangePaintInfo is used to paint ranges to offscreen buffers
|
||||
struct RangePaintInfo {
|
||||
nsCOMPtr<nsIRange> mRange;
|
||||
nsDisplayListBuilder mBuilder;
|
||||
nsDisplayList mList;
|
||||
|
||||
// offset of builder's reference frame to the root frame
|
||||
nsPoint mRootOffset;
|
||||
|
||||
RangePaintInfo(nsIRange* aRange, nsIFrame* aFrame)
|
||||
: mRange(aRange), mBuilder(aFrame, PR_FALSE, PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
~RangePaintInfo()
|
||||
{
|
||||
mList.DeleteAll();
|
||||
}
|
||||
};
|
||||
|
||||
#undef NOISY
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -899,6 +924,15 @@ public:
|
||||
nscolor aBackgroundColor,
|
||||
nsIRenderingContext** aRenderedContext);
|
||||
|
||||
virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
|
||||
nsIRegion* aRegion,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect);
|
||||
|
||||
virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect);
|
||||
|
||||
//nsIViewObserver interface
|
||||
|
||||
NS_IMETHOD Paint(nsIView *aView,
|
||||
@@ -1075,6 +1109,39 @@ protected:
|
||||
nsresult SetPrefNoScriptRule();
|
||||
nsresult SetPrefNoFramesRule(void);
|
||||
|
||||
// methods for painting a range to an offscreen buffer
|
||||
|
||||
// given a display list, clip the items within the list to
|
||||
// the range
|
||||
nsRect ClipListToRange(nsDisplayListBuilder *aBuilder,
|
||||
nsDisplayList* aList,
|
||||
nsIRange* aRange,
|
||||
nsIRenderingContext* aRenderingContext);
|
||||
|
||||
// create a RangePaintInfo for the range aRange containing the
|
||||
// display list needed to paint the range to a surface
|
||||
RangePaintInfo* CreateRangePaintInfo(nsIDOMRange* aRange,
|
||||
nsIRenderingContext* aRenderingContext,
|
||||
nsRect& aSurfaceRect);
|
||||
|
||||
/*
|
||||
* Paint the items to a new surface and return it.
|
||||
*
|
||||
* aSelection - selection being painted, if any
|
||||
* aRegion - clip region, if any
|
||||
* aArea - area that the surface occupies, relative to the root frame
|
||||
* aPoint - reference point, typically the mouse position
|
||||
* aScreenRect - [out] set to the area of the screen the painted area should
|
||||
* be displayed at
|
||||
*/
|
||||
already_AddRefed<gfxASurface>
|
||||
PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
|
||||
nsISelection* aSelection,
|
||||
nsIRegion* aRegion,
|
||||
nsRect aArea,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect);
|
||||
|
||||
/**
|
||||
* Methods to handle changes to user and UA sheet lists that we get
|
||||
* notified about.
|
||||
@@ -4906,6 +4973,363 @@ PresShell::RenderOffscreen(nsRect aRect, PRBool aUntrusted,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clip the display list aList to a range. Returns the clipped
|
||||
* rectangle surrounding the range.
|
||||
*/
|
||||
nsRect
|
||||
PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
|
||||
nsDisplayList* aList,
|
||||
nsIRange* aRange,
|
||||
nsIRenderingContext* aRenderingContext)
|
||||
{
|
||||
// iterate though the display items and add up the bounding boxes of each.
|
||||
// This will allow the total area of the frames within the range to be
|
||||
// determined. To do this, remove an item from the bottom of the list, check
|
||||
// whether it should be part of the range, and if so, append it to the top
|
||||
// of the temporary list tmpList. If the item is a text frame at the end of
|
||||
// the selection range, wrap it in an nsDisplayClip to clip the display to
|
||||
// the portion of the text frame that is part of the selection. Then, append
|
||||
// the wrapper to the top of the list. Otherwise, just delete the item and
|
||||
// don't append it.
|
||||
nsRect surfaceRect;
|
||||
nsDisplayList tmpList;
|
||||
|
||||
nsDisplayItem* i;
|
||||
while ((i = aList->RemoveBottom())) {
|
||||
// itemToInsert indiciates the item that should be inserted into the
|
||||
// temporary list. If null, no item should be inserted.
|
||||
nsDisplayItem* itemToInsert = nsnull;
|
||||
nsIFrame* frame = i->GetUnderlyingFrame();
|
||||
if (frame) {
|
||||
nsIContent* content = frame->GetContent();
|
||||
if (content) {
|
||||
PRBool atStart = (content == aRange->GetStartParent());
|
||||
PRBool atEnd = (content == aRange->GetEndParent());
|
||||
if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) {
|
||||
PRInt32 frameStartOffset, frameEndOffset;
|
||||
frame->GetOffsets(frameStartOffset, frameEndOffset);
|
||||
|
||||
PRInt32 hilightStart =
|
||||
atStart ? PR_MAX(aRange->StartOffset(), frameStartOffset) : frameStartOffset;
|
||||
PRInt32 hilightEnd =
|
||||
atEnd ? PR_MIN(aRange->EndOffset(), frameEndOffset) : frameEndOffset;
|
||||
if (hilightStart < hilightEnd) {
|
||||
// determine the location of the start and end edges of the range.
|
||||
nsPoint startPoint, endPoint;
|
||||
nsPresContext* presContext = GetPresContext();
|
||||
frame->GetPointFromOffset(presContext, aRenderingContext,
|
||||
hilightStart, &startPoint);
|
||||
frame->GetPointFromOffset(presContext, aRenderingContext,
|
||||
hilightEnd, &endPoint);
|
||||
|
||||
// the clip rectangle is determined by taking the the start and
|
||||
// end points of the range, offset from the reference frame.
|
||||
// Because of rtl, the end point may be to the left of the
|
||||
// start point, so x is set to the lowest value
|
||||
nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
|
||||
nscoord x = PR_MIN(startPoint.x, endPoint.x);
|
||||
textRect.x += x;
|
||||
textRect.width = PR_MAX(startPoint.x, endPoint.x) - x;
|
||||
surfaceRect.UnionRect(surfaceRect, textRect);
|
||||
|
||||
// wrap the item in an nsDisplayClip so that it can be clipped to
|
||||
// the selection. If the allocation fails, fall through and delete
|
||||
// the item below.
|
||||
itemToInsert = new (aBuilder)nsDisplayClip(frame, i, textRect);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if the node is within the range, append it to the temporary list
|
||||
PRBool before, after;
|
||||
nsRange::CompareNodeToRange(content, aRange, &before, &after);
|
||||
if (!before && !after) {
|
||||
itemToInsert = i;
|
||||
surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert the item into the list if necessary. If the item has a child
|
||||
// list, insert that as well
|
||||
nsDisplayList* sublist = i->GetList();
|
||||
if (itemToInsert || sublist) {
|
||||
tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
|
||||
// if the item is a list, iterate over it as well
|
||||
if (sublist)
|
||||
surfaceRect.UnionRect(surfaceRect,
|
||||
ClipListToRange(aBuilder, sublist, aRange, aRenderingContext));
|
||||
}
|
||||
else {
|
||||
// otherwise, just delete the item and don't readd it to the list
|
||||
i->~nsDisplayItem();
|
||||
}
|
||||
}
|
||||
|
||||
// now add all the items back onto the original list again
|
||||
aList->AppendToTop(&tmpList);
|
||||
|
||||
return surfaceRect;
|
||||
}
|
||||
|
||||
RangePaintInfo*
|
||||
PresShell::CreateRangePaintInfo(nsIDOMRange* aRange,
|
||||
nsIRenderingContext* aRenderingContext,
|
||||
nsRect& aSurfaceRect)
|
||||
{
|
||||
RangePaintInfo* info = nsnull;
|
||||
|
||||
nsCOMPtr<nsIRange> range = do_QueryInterface(aRange);
|
||||
if (!range)
|
||||
return nsnull;
|
||||
|
||||
// get the common ancestor of the two endpoints of the range
|
||||
nsINode* ancestor = nsContentUtils::GetCommonAncestor(range->GetStartParent(),
|
||||
range->GetEndParent());
|
||||
NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
|
||||
"common ancestor is not content");
|
||||
if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))
|
||||
return nsnull;
|
||||
|
||||
nsIContent* ancestorContent = NS_STATIC_CAST(nsIContent*, ancestor);
|
||||
|
||||
nsIFrame* ancestorFrame = GetPrimaryFrameFor(ancestorContent);
|
||||
|
||||
// use the nearest ancestor frame that includes all continuations as the
|
||||
// root for building the display list
|
||||
while (ancestorFrame && ancestorFrame->GetNextInFlow())
|
||||
ancestorFrame = ancestorFrame->GetParent();
|
||||
|
||||
if (!ancestorFrame)
|
||||
return nsnull;
|
||||
|
||||
info = new RangePaintInfo(range, ancestorFrame);
|
||||
if (!info)
|
||||
return nsnull;
|
||||
|
||||
nsRect ancestorRect = ancestorFrame->GetOverflowRect();
|
||||
|
||||
// get a display list containing the range
|
||||
info->mBuilder.SetPaintAllFrames();
|
||||
info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect);
|
||||
ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder,
|
||||
ancestorRect, &info->mList);
|
||||
info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect);
|
||||
|
||||
nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList,
|
||||
range, aRenderingContext);
|
||||
|
||||
// determine the offset of the reference frame for the display list
|
||||
// to the root frame. This will allow the coordinates used when painting
|
||||
// to all be offset from the same point
|
||||
info->mRootOffset = ancestorFrame->GetOffsetTo(GetRootFrame());
|
||||
rangeRect.MoveBy(info->mRootOffset);
|
||||
aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
|
||||
nsISelection* aSelection,
|
||||
nsIRegion* aRegion,
|
||||
nsRect aArea,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect)
|
||||
{
|
||||
nsPresContext* pc = GetPresContext();
|
||||
if (!pc)
|
||||
return nsnull;
|
||||
|
||||
nsIDeviceContext* deviceContext = pc->DeviceContext();
|
||||
|
||||
// use the rectangle to create the surface
|
||||
nsRect pixelArea = aArea;
|
||||
pixelArea.ScaleRoundOut(1.0 / pc->AppUnitsPerDevPixel());
|
||||
|
||||
// if the area of the image is larger than the maximum area, scale it down
|
||||
float scale = 0.0;
|
||||
nsIntRect rootScreenRect = GetRootFrame()->GetScreenRect();
|
||||
|
||||
// if the image is larger in one or both directions than half the size of
|
||||
// the available screen area, scale the image down to that size.
|
||||
nsRect maxSize;
|
||||
deviceContext->GetClientRect(maxSize);
|
||||
nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
|
||||
nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
|
||||
PRBool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight);
|
||||
if (resize) {
|
||||
scale = 1.0;
|
||||
// divide the maximum size by the image size in both directions. Whichever
|
||||
// direction produces the smallest result determines how much should be
|
||||
// scaled.
|
||||
if (pixelArea.width > maxWidth)
|
||||
scale = PR_MIN(scale, float(maxWidth) / pixelArea.width);
|
||||
if (pixelArea.height > maxHeight)
|
||||
scale = PR_MIN(scale, float(maxHeight) / pixelArea.height);
|
||||
|
||||
pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
|
||||
pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
|
||||
|
||||
// adjust the screen position based on the rescaled size
|
||||
nscoord left = rootScreenRect.x + pixelArea.x;
|
||||
nscoord top = rootScreenRect.y + pixelArea.y;
|
||||
aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
|
||||
aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
|
||||
}
|
||||
else {
|
||||
// move aScreenRect to the position of the surface in screen coordinates
|
||||
aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
|
||||
}
|
||||
aScreenRect->width = pixelArea.width;
|
||||
aScreenRect->height = pixelArea.height;
|
||||
|
||||
gfxImageSurface* surface =
|
||||
new gfxImageSurface(gfxIntSize(pixelArea.width, pixelArea.height),
|
||||
gfxImageSurface::ImageFormatARGB32);
|
||||
if (!surface)
|
||||
return nsnull;
|
||||
|
||||
// clear the image
|
||||
gfxContext context(surface);
|
||||
context.SetOperator(gfxContext::OPERATOR_CLEAR);
|
||||
context.Rectangle(gfxRect(0, 0, pixelArea.width, pixelArea.height));
|
||||
context.Fill();
|
||||
|
||||
nsCOMPtr<nsIRenderingContext> rc;
|
||||
deviceContext->CreateRenderingContextInstance(*getter_AddRefs(rc));
|
||||
rc->Init(deviceContext, surface);
|
||||
|
||||
if (aRegion)
|
||||
rc->SetClipRegion(*aRegion, nsClipCombine_kReplace);
|
||||
|
||||
if (resize)
|
||||
rc->Scale(scale, scale);
|
||||
|
||||
// translate so that points are relative to the surface area
|
||||
rc->Translate(-aArea.x, -aArea.y);
|
||||
|
||||
// temporarily hide the selection so that text is drawn normally. If a
|
||||
// selection is being rendered, use that, otherwise use the presshell's
|
||||
// selection.
|
||||
nsCOMPtr<nsFrameSelection> frameSelection;
|
||||
if (aSelection) {
|
||||
nsCOMPtr<nsISelectionPrivate> selpriv = do_QueryInterface(aSelection);
|
||||
selpriv->GetFrameSelection(getter_AddRefs(frameSelection));
|
||||
}
|
||||
else {
|
||||
frameSelection = FrameSelection();
|
||||
}
|
||||
PRInt16 oldDisplaySelection = frameSelection->GetDisplaySelection();
|
||||
frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
|
||||
|
||||
// next, paint each range in the selection
|
||||
PRInt32 count = aItems->Length();
|
||||
for (PRInt32 i = 0; i < count; i++) {
|
||||
RangePaintInfo* rangeInfo = (*aItems)[i];
|
||||
// the display lists paint relative to the offset from the reference
|
||||
// frame, so translate the rendering context
|
||||
nsIRenderingContext::AutoPushTranslation
|
||||
translate(rc, rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
|
||||
|
||||
aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
|
||||
rangeInfo->mList.Paint(&rangeInfo->mBuilder, rc, aArea);
|
||||
aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
|
||||
}
|
||||
|
||||
// restore the old selection display state
|
||||
frameSelection->SetDisplaySelection(oldDisplaySelection);
|
||||
|
||||
NS_ADDREF(surface);
|
||||
return surface;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
PresShell::RenderNode(nsIDOMNode* aNode,
|
||||
nsIRegion* aRegion,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect)
|
||||
{
|
||||
// create a temporary rendering context for text measuring
|
||||
nsCOMPtr<nsIRenderingContext> tmprc;
|
||||
nsresult rv = CreateRenderingContext(GetRootFrame(), getter_AddRefs(tmprc));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
// area will hold the size of the surface needed to draw the node, measured
|
||||
// from the root frame.
|
||||
nsRect area;
|
||||
nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
|
||||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
NS_NewRange(getter_AddRefs(range));
|
||||
range->SelectNode(aNode);
|
||||
|
||||
RangePaintInfo* info = CreateRangePaintInfo(range, tmprc, area);
|
||||
if (info && !rangeItems.AppendElement(info)) {
|
||||
delete info;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (aRegion) {
|
||||
// combine the area with the supplied region
|
||||
nsRect rrectPixels;
|
||||
aRegion->GetBoundingBox(&rrectPixels.x, &rrectPixels.y,
|
||||
&rrectPixels.width, &rrectPixels.height);
|
||||
|
||||
nsRect rrect = rrectPixels;
|
||||
rrect.ScaleRoundOut(nsPresContext::AppUnitsPerCSSPixel());
|
||||
area.IntersectRect(area, rrect);
|
||||
|
||||
nsPresContext* pc = GetPresContext();
|
||||
if (!pc)
|
||||
return nsnull;
|
||||
|
||||
// move the region so that it is offset from the topleft corner of the surface
|
||||
aRegion->Offset(-rrectPixels.x + (rrectPixels.x - pc->AppUnitsToDevPixels(area.x)),
|
||||
-rrectPixels.y + (rrectPixels.y - pc->AppUnitsToDevPixels(area.y)));
|
||||
}
|
||||
|
||||
return PaintRangePaintInfo(&rangeItems, nsnull, aRegion, area, aPoint,
|
||||
aScreenRect);
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
PresShell::RenderSelection(nsISelection* aSelection,
|
||||
nsPoint& aPoint,
|
||||
nsRect* aScreenRect)
|
||||
{
|
||||
// create a temporary rendering context for text measuring
|
||||
nsCOMPtr<nsIRenderingContext> tmprc;
|
||||
nsresult rv = CreateRenderingContext(GetRootFrame(), getter_AddRefs(tmprc));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
// area will hold the size of the surface needed to draw the selection,
|
||||
// measured from the root frame.
|
||||
nsRect area;
|
||||
nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
|
||||
|
||||
// iterate over each range and collect them into the rangeItems array.
|
||||
// This is done so that the size of selection can be determined so as
|
||||
// to allocate a surface area
|
||||
PRInt32 numRanges;
|
||||
aSelection->GetRangeCount(&numRanges);
|
||||
for (PRInt32 r = 0; r < numRanges; r++)
|
||||
{
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
aSelection->GetRangeAt(r, getter_AddRefs(range));
|
||||
|
||||
RangePaintInfo* info = CreateRangePaintInfo(range, tmprc, area);
|
||||
if (info && !rangeItems.AppendElement(info)) {
|
||||
delete info;
|
||||
return nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
return PaintRangePaintInfo(&rangeItems, aSelection, nsnull, area, aPoint,
|
||||
aScreenRect);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::Paint(nsIView* aView,
|
||||
nsIRenderingContext* aRenderingContext,
|
||||
|
||||
@@ -455,8 +455,10 @@ function BeginDragTree(event, tree, selArray, flavor)
|
||||
transArray.AppendElement(genTrans);
|
||||
}
|
||||
|
||||
dragService.invokeDragSession ( event.target, transArray, region, nsIDragService.DRAGDROP_ACTION_COPY +
|
||||
nsIDragService.DRAGDROP_ACTION_MOVE );
|
||||
dragService.invokeDragSessionWithImage(event.target, transArray, region,
|
||||
nsIDragService.DRAGDROP_ACTION_COPY +
|
||||
nsIDragService.DRAGDROP_ACTION_MOVE,
|
||||
null, 0, 0, event);
|
||||
|
||||
dragStarted = true;
|
||||
|
||||
|
||||
@@ -429,7 +429,9 @@ var nsDragAndDrop = {
|
||||
count < transferData.dataList.length);
|
||||
|
||||
try {
|
||||
this.mDragService.invokeDragSession(aEvent.target, transArray, region, dragAction.action);
|
||||
this.mDragService.invokeDragSessionWithImage(aEvent.target, transArray,
|
||||
region, dragAction.action,
|
||||
null, 0, 0, aEvent);
|
||||
}
|
||||
catch(ex) {
|
||||
// this could be because the user pressed escape to
|
||||
|
||||
@@ -44,16 +44,17 @@
|
||||
|
||||
|
||||
interface nsIDOMNode;
|
||||
interface nsIDOMMouseEvent;
|
||||
interface nsISelection;
|
||||
|
||||
|
||||
[scriptable, uuid(8B5314BB-DB01-11d2-96CE-0060B0FB9956)]
|
||||
[scriptable, uuid(D2875740-95D3-4F12-8E97-9FAB76C87093)]
|
||||
interface nsIDragService : nsISupports
|
||||
{
|
||||
const long DRAGDROP_ACTION_NONE = 0;
|
||||
const long DRAGDROP_ACTION_COPY = 1;
|
||||
const long DRAGDROP_ACTION_MOVE = 2;
|
||||
const long DRAGDROP_ACTION_LINK = 4;
|
||||
|
||||
|
||||
/**
|
||||
* Starts a modal drag session with an array of transaferables
|
||||
*
|
||||
@@ -65,6 +66,50 @@ interface nsIDragService : nsISupports
|
||||
void invokeDragSession ( in nsIDOMNode aDOMNode, in nsISupportsArray aTransferables,
|
||||
in nsIScriptableRegion aRegion, in unsigned long aActionType );
|
||||
|
||||
/**
|
||||
* Starts a modal drag session using an image. The first four arguments are
|
||||
* the same as invokeDragSession.
|
||||
*
|
||||
* A custom image may be specified using the aImage argument. If this is
|
||||
* supplied, the aImageX and aImageY arguments specify the offset within
|
||||
* the image where the cursor would be positioned. That is, when the image
|
||||
* is drawn, it is offset up and left the amount so that the cursor appears
|
||||
* at that location within the image.
|
||||
*
|
||||
* If aImage is null, aImageX and aImageY are not used and the image is instead
|
||||
* determined from the source node aDOMNode, and the offset calculated so that
|
||||
* the initial location for the image appears in the same screen position as
|
||||
* where the element is located. The node must be within a document.
|
||||
*
|
||||
* Currently, supported images are all DOM nodes. If this is an HTML <image> or
|
||||
* <canvas>, the drag image is taken from the image data. If the element is in
|
||||
* a document, it will be rendered at its displayed size, othewise, it will be
|
||||
* rendered at its real size. For other types of elements, the element is
|
||||
* rendered into an offscreen buffer in the same manner as it is currently
|
||||
* displayed. The document selection is hidden while drawing.
|
||||
*
|
||||
* The aDragEvent must be supplied as the current screen coordinates of the
|
||||
* event are needed to calculate the image location.
|
||||
*/
|
||||
void invokeDragSessionWithImage(in nsIDOMNode aDOMNode,
|
||||
in nsISupportsArray aTransferableArray,
|
||||
in nsIScriptableRegion aRegion,
|
||||
in unsigned long aActionType,
|
||||
in nsIDOMNode aImage,
|
||||
in long aImageX,
|
||||
in long aImageY,
|
||||
in nsIDOMMouseEvent aDragEvent);
|
||||
|
||||
/**
|
||||
* Start a modal drag session using the selection as the drag image.
|
||||
* The aDragEvent must be supplied as the current screen coordinates of the
|
||||
* event are needed to calculate the image location.
|
||||
*/
|
||||
void invokeDragSessionWithSelection(in nsISelection aSelection,
|
||||
in nsISupportsArray aTransferableArray,
|
||||
in unsigned long aActionType,
|
||||
in nsIDOMMouseEvent aDragEvent);
|
||||
|
||||
/**
|
||||
* Returns the current Drag Session
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,10 @@
|
||||
|
||||
#include "nsBaseDragService.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
class nsILocalFile;
|
||||
class nsIDOMDragEvent;
|
||||
|
||||
class nsDragService : public nsBaseDragService
|
||||
{
|
||||
@@ -63,6 +66,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
NSImage* ConstructDragImage(nsIDOMNode* aDOMNode,
|
||||
nsRect* aDragRect,
|
||||
nsIScriptableRegion* aRegion);
|
||||
};
|
||||
|
||||
#endif // nsDragService_h_
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIView.h"
|
||||
#include "nsIRegion.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "gfxContext.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@@ -82,90 +84,6 @@ nsDragService::~nsDragService()
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns the rect for the drag in native view coordinates.
|
||||
//
|
||||
// Note that for text drags, this returns an incorrect rect. This bug
|
||||
// exists in the old carbon implementation too.
|
||||
static NSRect GetDragRect(nsIDOMNode* node, nsIScriptableRegion* aDragRgn)
|
||||
{
|
||||
// Set up a default rect in case something goes wrong.
|
||||
// It'll at least indicate that *something* is getting dragged
|
||||
NSRect outRect = NSMakeRect(0, 0, 50, 50);
|
||||
|
||||
// we're going to need an nsIFrame* no matter what, so get it now
|
||||
if (!node)
|
||||
return outRect;
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
|
||||
if (!content)
|
||||
return outRect;
|
||||
nsIDocument* doc = content->GetCurrentDoc();
|
||||
if (!doc)
|
||||
return outRect;
|
||||
nsIPresShell* presShell = doc->GetShellAt(0);
|
||||
if (!presShell)
|
||||
return outRect;
|
||||
nsIFrame* frame = presShell->GetPrimaryFrameFor(content);
|
||||
if (!frame)
|
||||
return outRect;
|
||||
|
||||
if (aDragRgn) {
|
||||
nsCOMPtr<nsIRegion> geckoRegion;
|
||||
aDragRgn->GetRegion(getter_AddRefs(geckoRegion));
|
||||
if (!geckoRegion)
|
||||
return outRect;
|
||||
|
||||
// bounding box for the drag is in window coordinates
|
||||
PRInt32 x, y, width, height;
|
||||
geckoRegion->GetBoundingBox(&x, &y, &width, &height);
|
||||
nsRect rect = nsRect(x, y, width, height);
|
||||
// printf("drag region is x=%d, y=%d, width=%d, height=%d\n", x, y, width, height);
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = frame->GetWindow();
|
||||
if (!widget)
|
||||
return outRect;
|
||||
|
||||
nsRect widgetScreenBounds;
|
||||
widget->GetScreenBounds(widgetScreenBounds);
|
||||
|
||||
outRect.origin.x = (float)(rect.x - widgetScreenBounds.x);
|
||||
outRect.origin.y = (float)(rect.y - widgetScreenBounds.y + rect.height);
|
||||
outRect.size.width = (float)rect.width;
|
||||
outRect.size.height = (float)rect.height;
|
||||
}
|
||||
else {
|
||||
nsRect rect = frame->GetRect();
|
||||
|
||||
// find offset from our view
|
||||
nsIView *containingView = nsnull;
|
||||
nsPoint viewOffset(0,0);
|
||||
frame->GetOffsetFromView(viewOffset, &containingView);
|
||||
if (!containingView)
|
||||
return outRect;
|
||||
|
||||
// get the widget offset
|
||||
nsPoint widgetOffset;
|
||||
containingView->GetNearestWidget(&widgetOffset);
|
||||
|
||||
nsPresContext* presContext = frame->GetPresContext();
|
||||
|
||||
nsRect screenOffset;
|
||||
screenOffset.MoveBy(presContext->AppUnitsToDevPixels(widgetOffset.x +
|
||||
viewOffset.x),
|
||||
presContext->AppUnitsToDevPixels(widgetOffset.y +
|
||||
viewOffset.y));
|
||||
|
||||
outRect.origin.x = (float)screenOffset.x;
|
||||
outRect.origin.y = (float)screenOffset.y +
|
||||
(float)presContext->AppUnitsToDevPixels(rect.height);
|
||||
outRect.size.width = (float)presContext->AppUnitsToDevPixels(rect.width);
|
||||
outRect.size.height = (float)presContext->AppUnitsToDevPixels(rect.height);
|
||||
}
|
||||
|
||||
return outRect;
|
||||
}
|
||||
|
||||
|
||||
static nsresult SetUpDragClipboard(nsISupportsArray* aTransferableArray)
|
||||
{
|
||||
if (!aTransferableArray)
|
||||
@@ -315,6 +233,79 @@ static nsresult SetUpDragClipboard(nsISupportsArray* aTransferableArray)
|
||||
}
|
||||
|
||||
|
||||
NSImage*
|
||||
nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode,
|
||||
nsRect* aDragRect,
|
||||
nsIScriptableRegion* aRegion)
|
||||
{
|
||||
NSPoint screenPoint = [[globalDragView window] convertBaseToScreen:[globalDragEvent locationInWindow]];
|
||||
// Y coordinates are bottom to top, so reverse this
|
||||
if ([[NSScreen screens] count] > 0)
|
||||
screenPoint.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - screenPoint.y;
|
||||
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
nsresult rv = DrawDrag(aDOMNode, aRegion,
|
||||
NSToIntRound(screenPoint.x), NSToIntRound(screenPoint.y),
|
||||
aDragRect, getter_AddRefs(surface));
|
||||
if (NS_FAILED(rv) || !surface)
|
||||
return nsnull;
|
||||
|
||||
PRUint32 width = aDragRect->width;
|
||||
PRUint32 height = aDragRect->height;
|
||||
|
||||
nsRefPtr<gfxImageSurface> imgSurface = new gfxImageSurface(
|
||||
gfxIntSize(width, height), gfxImageSurface::ImageFormatARGB32);
|
||||
if (!imgSurface)
|
||||
return nsnull;
|
||||
|
||||
nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
|
||||
if (!context)
|
||||
return nsnull;
|
||||
|
||||
context->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
context->SetSource(surface);
|
||||
context->Paint();
|
||||
|
||||
PRUint32* imageData = (PRUint32*)imgSurface->Data();
|
||||
PRInt32 stride = imgSurface->Stride();
|
||||
|
||||
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:width
|
||||
pixelsHigh:height
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bytesPerRow:width * 4
|
||||
bitsPerPixel:32];
|
||||
|
||||
PRUint8* dest = [imageRep bitmapData];
|
||||
for (PRUint32 i = 0; i < height; ++i) {
|
||||
PRUint8* src = (PRUint8 *)imageData + i * stride;
|
||||
for (PRUint32 j = 0; j < width; ++j) {
|
||||
#ifdef IS_BIG_ENDIAN
|
||||
dest[0] = src[1];
|
||||
dest[1] = src[2];
|
||||
dest[2] = src[3]
|
||||
dest[3] = PRUint8(src[0] * 0.8); // reduce transparency overall
|
||||
#else
|
||||
dest[0] = src[2];
|
||||
dest[1] = src[1];
|
||||
dest[2] = src[0];
|
||||
dest[3] = PRUint8(src[3] * 0.8); // reduce transparency overall
|
||||
#endif
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
|
||||
NSImage* image = [NSImage alloc];
|
||||
[image addRepresentation:imageRep];
|
||||
|
||||
return [image autorelease];
|
||||
}
|
||||
|
||||
// We can only invoke NSView's 'dragImage:at:offset:event:pasteboard:source:slideBack:' from
|
||||
// within NSView's 'mouseDown:' or 'mouseDragged:'. Luckily 'mouseDragged' is always on the
|
||||
// stack when InvokeDragSession gets called.
|
||||
@@ -328,48 +319,47 @@ nsDragService::InvokeDragSession(nsIDOMNode* aDOMNode, nsISupportsArray* aTransf
|
||||
if (NS_FAILED(SetUpDragClipboard(aTransferableArray)))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// create the image for the drag, it isn't awesome but it'll do for now
|
||||
NSRect dragRect = GetDragRect(aDOMNode, aDragRgn);
|
||||
NSImage* image = [[[NSImage alloc] initWithSize:dragRect.size] autorelease];
|
||||
[image lockFocus];
|
||||
[[NSColor grayColor] set];
|
||||
NSBezierPath* path = [NSBezierPath bezierPath];
|
||||
[path setLineWidth:2.0];
|
||||
[path moveToPoint:NSMakePoint(0, 0)];
|
||||
[path lineToPoint:NSMakePoint(0, dragRect.size.height)];
|
||||
[path lineToPoint:NSMakePoint(dragRect.size.width, dragRect.size.height)];
|
||||
[path lineToPoint:NSMakePoint(dragRect.size.width, 0)];
|
||||
[path lineToPoint:NSMakePoint(0, 0)];
|
||||
[path stroke];
|
||||
[image unlockFocus];
|
||||
nsRect dragRect(0, 0, 20, 20);
|
||||
NSImage* image = ConstructDragImage(aDOMNode, &dragRect, aDragRgn);
|
||||
if (!image) {
|
||||
// if no image was returned, just draw a rectangle
|
||||
NSSize size;
|
||||
size.width = dragRect.width;
|
||||
size.height = dragRect.height;
|
||||
image = [[NSImage alloc] initWithSize:size];
|
||||
[image lockFocus];
|
||||
[[NSColor grayColor] set];
|
||||
NSBezierPath* path = [NSBezierPath bezierPath];
|
||||
[path setLineWidth:2.0];
|
||||
[path moveToPoint:NSMakePoint(0, 0)];
|
||||
[path lineToPoint:NSMakePoint(0, size.height)];
|
||||
[path lineToPoint:NSMakePoint(size.width, size.height)];
|
||||
[path lineToPoint:NSMakePoint(size.width, 0)];
|
||||
[path lineToPoint:NSMakePoint(0, 0)];
|
||||
[path stroke];
|
||||
[image unlockFocus];
|
||||
}
|
||||
|
||||
// Make sure that the drag rect encompasses the current mouse location.
|
||||
// This correction code has been very methodically tested, on every edge of
|
||||
// a drag rect. Do not change this code unless you've done that.
|
||||
//
|
||||
// Get the mouse coordinates in local view coords
|
||||
NSPoint localPoint = [globalDragView convertPoint:[globalDragEvent locationInWindow] fromView:nil];
|
||||
// fix up the X coords
|
||||
if (localPoint.x < dragRect.origin.x)
|
||||
dragRect.origin.x = localPoint.x;
|
||||
else if (localPoint.x > (dragRect.origin.x + dragRect.size.width))
|
||||
dragRect.origin.x = localPoint.x - dragRect.size.width;
|
||||
// now fix up the Y coords
|
||||
if (localPoint.y < (dragRect.origin.y - dragRect.size.height))
|
||||
dragRect.origin.y = localPoint.y + dragRect.size.height;
|
||||
else if (localPoint.y > dragRect.origin.y)
|
||||
dragRect.origin.y = localPoint.y;
|
||||
NSPoint point;
|
||||
point.x = dragRect.x;
|
||||
if ([[NSScreen screens] count] > 0)
|
||||
point.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - dragRect.YMost();
|
||||
else
|
||||
point.y = dragRect.y;
|
||||
|
||||
point = [[globalDragView window] convertScreenToBase: point];
|
||||
NSPoint localPoint = [globalDragView convertPoint:point fromView:nil];
|
||||
|
||||
nsBaseDragService::StartDragSession();
|
||||
[globalDragView dragImage:image
|
||||
at:dragRect.origin
|
||||
at:localPoint
|
||||
offset:NSMakeSize(0,0)
|
||||
event:globalDragEvent
|
||||
pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
|
||||
source:globalDragView
|
||||
slideBack:YES];
|
||||
nsBaseDragService::EndDragSession();
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
#include <gdk/gdkx.h>
|
||||
#include "nsCRT.h"
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
#include "gfxASurface.h"
|
||||
#include "nsImageToPixbuf.h"
|
||||
#endif
|
||||
|
||||
static PRLogModuleInfo *sDragLm = NULL;
|
||||
|
||||
@@ -198,8 +202,30 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
|
||||
action,
|
||||
1,
|
||||
&event);
|
||||
// make sure to set our default icon
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
GdkPixbuf* dragPixbuf = nsnull;
|
||||
nsRect dragRect;
|
||||
if (mHasImage || mSelection) {
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
DrawDrag(aDOMNode, aRegion, mScreenX, mScreenY,
|
||||
&dragRect, getter_AddRefs(surface));
|
||||
if (surface) {
|
||||
dragPixbuf =
|
||||
nsImageToPixbuf::SurfaceToPixbuf(surface, dragRect.width, dragRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
if (dragPixbuf)
|
||||
gtk_drag_set_icon_pixbuf(context, dragPixbuf,
|
||||
mScreenX - NSToIntRound(dragRect.x),
|
||||
mScreenY - NSToIntRound(dragRect.y));
|
||||
else
|
||||
gtk_drag_set_icon_default(context);
|
||||
#else
|
||||
// use a default icon
|
||||
gtk_drag_set_icon_default(context);
|
||||
#endif
|
||||
gtk_target_list_unref(sourceList);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,12 +77,25 @@ nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
aImage->GetSurface(getter_AddRefs(surface));
|
||||
|
||||
return SurfaceToPixbuf(surface, width, height);
|
||||
#else
|
||||
nsCOMPtr<nsIGdkPixbufImage> img(do_QueryInterface(aImage));
|
||||
if (img)
|
||||
return img->GetGdkPixbuf();
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
GdkPixbuf*
|
||||
nsImageToPixbuf::SurfaceToPixbuf(gfxASurface* aSurface, PRInt32 aWidth, PRInt32 aHeight)
|
||||
{
|
||||
nsRefPtr<gfxImageSurface> imgSurface;
|
||||
if (surface->GetType() == gfxASurface::SurfaceTypeImage) {
|
||||
if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
|
||||
imgSurface = NS_STATIC_CAST(gfxImageSurface*,
|
||||
NS_STATIC_CAST(gfxASurface*, surface.get()));
|
||||
NS_STATIC_CAST(gfxASurface*, aSurface));
|
||||
} else {
|
||||
imgSurface = new gfxImageSurface(gfxIntSize(width, height),
|
||||
imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
|
||||
gfxImageSurface::ImageFormatARGB32);
|
||||
|
||||
if (!imgSurface)
|
||||
@@ -93,12 +106,12 @@ nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
|
||||
return nsnull;
|
||||
|
||||
context->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
context->SetSource(surface);
|
||||
context->SetSource(aSurface);
|
||||
context->Paint();
|
||||
}
|
||||
|
||||
GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, PR_TRUE, 8,
|
||||
width, height);
|
||||
aWidth, aHeight);
|
||||
if (!pixbuf)
|
||||
return nsnull;
|
||||
|
||||
@@ -110,8 +123,8 @@ nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
|
||||
|
||||
gfxASurface::gfxImageFormat format = imgSurface->Format();
|
||||
|
||||
for (PRInt32 row = 0; row < height; ++row) {
|
||||
for (PRInt32 col = 0; col < width; ++col) {
|
||||
for (PRInt32 row = 0; row < aHeight; ++row) {
|
||||
for (PRInt32 col = 0; col < aWidth; ++col) {
|
||||
guchar* pixel = pixels + row * rowstride + 4 * col;
|
||||
|
||||
PRUint32* cairoPixel = NS_REINTERPRET_CAST(PRUint32*,
|
||||
@@ -143,11 +156,5 @@ nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
|
||||
}
|
||||
|
||||
return pixbuf;
|
||||
#else
|
||||
nsCOMPtr<nsIGdkPixbufImage> img(do_QueryInterface(aImage));
|
||||
if (img)
|
||||
return img->GetGdkPixbuf();
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
|
||||
#include "nsIImageToPixbuf.h"
|
||||
|
||||
class gfxASurface;
|
||||
|
||||
class nsImageToPixbuf : public nsIImageToPixbuf {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@@ -48,6 +50,10 @@ class nsImageToPixbuf : public nsIImageToPixbuf {
|
||||
// Friendlier version of ConvertImageToPixbuf for callers inside of
|
||||
// widget
|
||||
static GdkPixbuf* ImageToPixbuf(nsIImage* aImage);
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
static GdkPixbuf* SurfaceToPixbuf(gfxASurface* aSurface,
|
||||
PRInt32 aWidth, PRInt32 aHeight);
|
||||
#endif
|
||||
private:
|
||||
~nsImageToPixbuf() {}
|
||||
};
|
||||
|
||||
@@ -294,15 +294,21 @@ nsDragService::InvokeDragSession (nsIDOMNode *aDOMNode, nsISupportsArray * aTran
|
||||
theEvent.when = TickCount();
|
||||
theEvent.modifiers = 0L;
|
||||
|
||||
// since we don't have the original mouseDown location, just assume the drag
|
||||
// if we don't have the original mouseDown location, just assume the drag
|
||||
// started in the middle of the frame. This prevents us from having the mouse
|
||||
// and the region we're dragging separated by a big gap (which could happen if
|
||||
// we used the current mouse position). I know this isn't exactly right, and you can
|
||||
// see it if you're paying attention, but who pays such close attention?
|
||||
Rect dragRect;
|
||||
::GetRegionBounds(theDragRgn, &dragRect);
|
||||
theEvent.where.v = dragRect.top + ((dragRect.bottom - dragRect.top) / 2);
|
||||
theEvent.where.h = dragRect.left + ((dragRect.right - dragRect.left) / 2);
|
||||
if (mScreenX == -1 || mScreenY == -1) {
|
||||
Rect dragRect;
|
||||
::GetRegionBounds(theDragRgn, &dragRect);
|
||||
theEvent.where.v = dragRect.top + ((dragRect.bottom - dragRect.top) / 2);
|
||||
theEvent.where.h = dragRect.left + ((dragRect.right - dragRect.left) / 2);
|
||||
}
|
||||
else {
|
||||
theEvent.where.v = mScreenY;
|
||||
theEvent.where.h = mScreenX;
|
||||
}
|
||||
|
||||
// register drag send proc which will call us back when asked for the actual
|
||||
// flavor data (instead of placing it all into the drag manager)
|
||||
|
||||
@@ -52,6 +52,7 @@ REQUIRES = xpcom \
|
||||
layout \
|
||||
content \
|
||||
dom \
|
||||
imglib2 \
|
||||
pref \
|
||||
locale \
|
||||
necko \
|
||||
|
||||
@@ -47,14 +47,33 @@
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIViewManager.h"
|
||||
#include "nsIScrollableView.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsIDOMMouseEvent.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIEventStateManager.h"
|
||||
#include "nsICanvasElement.h"
|
||||
#include "nsIImage.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
#include "gfxIImageFrame.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "imgIRequest.h"
|
||||
#include "nsIViewObserver.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
#include "gfxContext.h"
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
#endif
|
||||
|
||||
NS_IMPL_ADDREF(nsBaseDragService)
|
||||
NS_IMPL_RELEASE(nsBaseDragService)
|
||||
@@ -67,8 +86,9 @@ NS_IMPL_QUERY_INTERFACE2(nsBaseDragService, nsIDragService, nsIDragSession)
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
nsBaseDragService::nsBaseDragService()
|
||||
: mCanDrop(PR_FALSE), mDoingDrag(PR_FALSE),
|
||||
mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0)
|
||||
: mCanDrop(PR_FALSE), mDoingDrag(PR_FALSE), mHasImage(PR_FALSE),
|
||||
mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0),
|
||||
mImageX(0), mImageY(0), mScreenX(-1), mScreenY(-1)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -188,12 +208,11 @@ nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
|
||||
nsISupportsArray * anArrayTransferables,
|
||||
nsIScriptableRegion * aRegion,
|
||||
nsISupportsArray* aTransferableArray,
|
||||
nsIScriptableRegion* aDragRgn,
|
||||
PRUint32 aActionType)
|
||||
{
|
||||
NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
|
||||
@@ -225,6 +244,53 @@ nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
|
||||
nsISupportsArray* aTransferableArray,
|
||||
nsIScriptableRegion* aRegion,
|
||||
PRUint32 aActionType,
|
||||
nsIDOMNode* aImage,
|
||||
PRInt32 aImageX, PRInt32 aImageY,
|
||||
nsIDOMMouseEvent* aDragEvent)
|
||||
{
|
||||
NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
|
||||
|
||||
mSelection = nsnull;
|
||||
mHasImage = PR_TRUE;
|
||||
mImage = aImage;
|
||||
mImageX = aImageX;
|
||||
mImageY = aImageY;
|
||||
|
||||
aDragEvent->GetScreenX(&mScreenX);
|
||||
aDragEvent->GetScreenY(&mScreenY);
|
||||
|
||||
return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
|
||||
nsISupportsArray* aTransferableArray,
|
||||
PRUint32 aActionType,
|
||||
nsIDOMMouseEvent* aDragEvent)
|
||||
{
|
||||
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
|
||||
NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
|
||||
|
||||
mSelection = aSelection;
|
||||
mHasImage = PR_TRUE;
|
||||
mImage = nsnull;
|
||||
mImageX = 0;
|
||||
mImageY = 0;
|
||||
|
||||
aDragEvent->GetScreenX(&mScreenX);
|
||||
aDragEvent->GetScreenY(&mScreenY);
|
||||
|
||||
// just get the focused node from the selection
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
aSelection->GetFocusNode(getter_AddRefs(node));
|
||||
|
||||
return InvokeDragSession(node, aTransferableArray, nsnull, aActionType);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
@@ -269,6 +335,219 @@ nsBaseDragService::EndDragSession()
|
||||
// release the source we've been holding on to.
|
||||
mSourceDocument = nsnull;
|
||||
mSourceNode = nsnull;
|
||||
mSelection = nsnull;
|
||||
mHasImage = PR_FALSE;
|
||||
mImage = nsnull;
|
||||
mImageX = 0;
|
||||
mImageY = 0;
|
||||
mScreenX = -1;
|
||||
mScreenY = -1;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
|
||||
static nsIPresShell*
|
||||
GetPresShellForContent(nsIDOMNode* aDOMNode)
|
||||
{
|
||||
nsIContent* content;
|
||||
CallQueryInterface(aDOMNode, &content);
|
||||
|
||||
nsIDocument *document = content->GetCurrentDoc();
|
||||
if (document) {
|
||||
document->FlushPendingNotifications(Flush_Display);
|
||||
|
||||
return document->GetShellAt(0);
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode,
|
||||
nsIScriptableRegion* aRegion,
|
||||
PRInt32 aScreenX, PRInt32 aScreenY,
|
||||
nsRect* aScreenDragRect,
|
||||
gfxASurface** aSurface)
|
||||
{
|
||||
*aSurface = nsnull;
|
||||
|
||||
// use a default size, in case of an error.
|
||||
aScreenDragRect->x = aScreenX - mImageX;
|
||||
aScreenDragRect->y = aScreenY - mImageY;
|
||||
aScreenDragRect->width = 20;
|
||||
aScreenDragRect->height = 20;
|
||||
|
||||
// if a drag image was specified, use that, otherwise, use the source node
|
||||
nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode;
|
||||
|
||||
// get the presshell for the node being dragged. If the drag image is not in
|
||||
// a document or has no frame, get the presshell from the source drag node
|
||||
nsIPresShell* presShell = GetPresShellForContent(dragNode);
|
||||
if (!presShell && mImage)
|
||||
presShell = GetPresShellForContent(aDOMNode);
|
||||
if (!presShell)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// didn't want an image, so just set the screen rectangle to the frame size
|
||||
if (!mHasImage) {
|
||||
// if a region was specified, set the screen rectangle to the area that
|
||||
// the region occupies
|
||||
if (aRegion) {
|
||||
// the region's coordinates are relative to the root frame
|
||||
nsPresContext* pc = presShell->GetPresContext();
|
||||
nsIFrame* rootFrame = presShell->GetRootFrame();
|
||||
if (rootFrame && pc) {
|
||||
nsRect dragRect;
|
||||
aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
|
||||
dragRect.ScaleRoundOut(nsPresContext::AppUnitsPerCSSPixel());
|
||||
dragRect.ScaleRoundOut(1.0 / pc->AppUnitsPerDevPixel());
|
||||
|
||||
nsIntRect screenRect = rootFrame->GetScreenRectExternal();
|
||||
aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y,
|
||||
dragRect.width, dragRect.height);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// otherwise, there was no region so just set the rectangle to
|
||||
// the size of the primary frame of the content.
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
|
||||
nsIFrame* frame = presShell->GetPrimaryFrameFor(content);
|
||||
if (frame) {
|
||||
nsIntRect screenRect = frame->GetScreenRectExternal();
|
||||
aScreenDragRect->SetRect(screenRect.x, screenRect.y,
|
||||
screenRect.width, screenRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// draw the image for selections
|
||||
if (mSelection) {
|
||||
nsPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
|
||||
nsRefPtr<gfxASurface> surface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect);
|
||||
*aSurface = surface;
|
||||
NS_IF_ADDREF(*aSurface);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// if an custom image was specified, check if it is an image node and draw
|
||||
// using the source rather than the displayed image. But if mImage isn't
|
||||
// an image, fall through to RenderNode below.
|
||||
if (mImage) {
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
|
||||
// for image nodes, create the drag image from the actual image data
|
||||
if (imageLoader) {
|
||||
nsPresContext* pc = presShell->GetPresContext();
|
||||
if (!pc)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return DrawDragForImage(pc, imageLoader, aScreenX, aScreenY,
|
||||
aScreenDragRect, aSurface);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, just draw the node
|
||||
nsCOMPtr<nsIRegion> clipRegion;
|
||||
if (aRegion)
|
||||
aRegion->GetRegion(getter_AddRefs(clipRegion));
|
||||
|
||||
nsPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
|
||||
nsRefPtr<gfxASurface> surface = presShell->RenderNode(dragNode, clipRegion,
|
||||
pnt, aScreenDragRect);
|
||||
|
||||
// if an image was specified, reposition the drag rectangle to
|
||||
// the supplied offset in mImageX and mImageY.
|
||||
if (mImage) {
|
||||
aScreenDragRect->x = aScreenX - mImageX;
|
||||
aScreenDragRect->y = aScreenY - mImageY;
|
||||
}
|
||||
|
||||
*aSurface = surface;
|
||||
NS_IF_ADDREF(*aSurface);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
|
||||
nsIImageLoadingContent* aImageLoader,
|
||||
PRInt32 aScreenX, PRInt32 aScreenY,
|
||||
nsRect* aScreenDragRect,
|
||||
gfxASurface** aSurface)
|
||||
{
|
||||
nsCOMPtr<imgIRequest> imgRequest;
|
||||
nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||
getter_AddRefs(imgRequest));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!imgRequest)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
nsCOMPtr<imgIContainer> imgContainer;
|
||||
rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!imgContainer)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// use the size of the image as the size of the drag image
|
||||
imgContainer->GetWidth(&aScreenDragRect->width);
|
||||
imgContainer->GetHeight(&aScreenDragRect->height);
|
||||
|
||||
nsRect srcRect = *aScreenDragRect;
|
||||
srcRect.MoveTo(0, 0);
|
||||
nsRect destRect = srcRect;
|
||||
|
||||
if (destRect.width == 0 || destRect.height == 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// if the image is larger than half the screen size, scale it down. This
|
||||
// scaling algorithm is the same as is used in nsPresShell::PaintRangePaintInfo
|
||||
nsIDeviceContext* deviceContext = aPresContext->DeviceContext();
|
||||
nsRect maxSize;
|
||||
deviceContext->GetClientRect(maxSize);
|
||||
nscoord maxWidth = aPresContext->AppUnitsToDevPixels(maxSize.width >> 1);
|
||||
nscoord maxHeight = aPresContext->AppUnitsToDevPixels(maxSize.height >> 1);
|
||||
if (destRect.width > maxWidth || destRect.height > maxHeight) {
|
||||
float scale = 1.0;
|
||||
if (destRect.width > maxWidth)
|
||||
scale = PR_MIN(scale, float(maxWidth) / destRect.width);
|
||||
if (destRect.height > maxHeight)
|
||||
scale = PR_MIN(scale, float(maxHeight) / destRect.height);
|
||||
|
||||
destRect.width = NSToIntFloor(float(destRect.width) * scale);
|
||||
destRect.height = NSToIntFloor(float(destRect.height) * scale);
|
||||
|
||||
aScreenDragRect->x = NSToIntFloor(aScreenX - float(mImageX) * scale);
|
||||
aScreenDragRect->y = NSToIntFloor(aScreenY - float(mImageY) * scale);
|
||||
aScreenDragRect->width = destRect.width;
|
||||
aScreenDragRect->height = destRect.height;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> surface =
|
||||
new gfxImageSurface(gfxIntSize(destRect.width, destRect.height),
|
||||
gfxImageSurface::ImageFormatARGB32);
|
||||
if (!surface)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
*aSurface = surface;
|
||||
NS_ADDREF(*aSurface);
|
||||
|
||||
nsCOMPtr<nsIRenderingContext> rc;
|
||||
deviceContext->CreateRenderingContextInstance(*getter_AddRefs(rc));
|
||||
rc->Init(deviceContext, surface);
|
||||
|
||||
// clear the image before drawing
|
||||
gfxContext context(surface);
|
||||
context.SetOperator(gfxContext::OPERATOR_CLEAR);
|
||||
context.Rectangle(gfxRect(0, 0, destRect.width, destRect.height));
|
||||
context.Fill();
|
||||
|
||||
PRInt32 upp = aPresContext->AppUnitsPerDevPixel();
|
||||
srcRect.ScaleRoundOut(upp);
|
||||
destRect.ScaleRoundOut(upp);
|
||||
return rc->DrawImage(imgContainer, srcRect, destRect);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,11 +44,16 @@
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIRenderingContext.h"
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
#include "gfxImageSurface.h"
|
||||
#endif
|
||||
|
||||
class nsIDOMNode;
|
||||
class nsIFrame;
|
||||
class nsPresContext;
|
||||
|
||||
class nsIImageLoadingContent;
|
||||
|
||||
/**
|
||||
* XP DragService wrapper base class
|
||||
@@ -71,13 +76,66 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
/**
|
||||
* Draw the drag image, if any, to a surface and return it. The drag image
|
||||
* is constructed from mImage if specified, or aDOMNode if mImage is null.
|
||||
*
|
||||
* aRegion may be used to draw only a subset of the element. This region
|
||||
* should be supplied using x and y coordinates measured in css pixels
|
||||
* that are relative to the upper-left corner of the window.
|
||||
*
|
||||
* aScreenX and aScreenY should be the screen coordinates of the mouse click
|
||||
* for the drag.
|
||||
*
|
||||
* On return, aScreenDragRect will contain the screen coordinates of the
|
||||
* area being dragged. This is used by the platform-specific part of the
|
||||
* drag service to determine the drag feedback.
|
||||
*
|
||||
* If there is no drag image, the returned surface will be null, but
|
||||
* aScreenDragRect will still be set to the drag area.
|
||||
*/
|
||||
nsresult DrawDrag(nsIDOMNode* aDOMNode,
|
||||
nsIScriptableRegion* aRegion,
|
||||
PRInt32 aScreenX, PRInt32 aScreenY,
|
||||
nsRect* aScreenDragRect,
|
||||
gfxASurface** aSurface);
|
||||
|
||||
/**
|
||||
* Draw a drag image for an image node. This is called by DrawDrag.
|
||||
*/
|
||||
nsresult DrawDragForImage(nsPresContext* aPresContext,
|
||||
nsIImageLoadingContent* aImageLoader,
|
||||
PRInt32 aScreenX, PRInt32 aScreenY,
|
||||
nsRect* aScreenDragRect,
|
||||
gfxASurface** aSurface);
|
||||
#endif
|
||||
|
||||
PRPackedBool mCanDrop;
|
||||
PRPackedBool mDoingDrag;
|
||||
// true if mImage should be used to set a drag image
|
||||
PRPackedBool mHasImage;
|
||||
|
||||
PRUint32 mDragAction;
|
||||
nsSize mTargetSize;
|
||||
nsCOMPtr<nsIDOMNode> mSourceNode;
|
||||
nsCOMPtr<nsIDOMDocument> mSourceDocument; // the document at the drag source. will be null
|
||||
// if it came from outside the app.
|
||||
|
||||
// used to determine the image to appear on the cursor while dragging
|
||||
nsCOMPtr<nsIDOMNode> mImage;
|
||||
// offset of cursor within the image
|
||||
PRInt32 mImageX;
|
||||
PRInt32 mImageY;
|
||||
|
||||
// set if a selection is being dragged
|
||||
nsCOMPtr<nsISelection> mSelection;
|
||||
|
||||
// the screen position where drag gesture occured, used for positioning the
|
||||
// drag image when no image is specified. If a value is -1, no event was
|
||||
// supplied so the screen position is not known
|
||||
PRInt32 mScreenX;
|
||||
PRInt32 mScreenY;
|
||||
};
|
||||
|
||||
#endif // nsBaseDragService_h__
|
||||
|
||||
@@ -429,7 +429,9 @@ var nsDragAndDrop = {
|
||||
count < transferData.dataList.length);
|
||||
|
||||
try {
|
||||
this.mDragService.invokeDragSession(aEvent.target, transArray, region, dragAction.action);
|
||||
this.mDragService.invokeDragSessionWithImage(aEvent.target, transArray,
|
||||
region, dragAction.action,
|
||||
null, 0, 0, aEvent);
|
||||
}
|
||||
catch(ex) {
|
||||
// this could be because the user pressed escape to
|
||||
|
||||
Reference in New Issue
Block a user