Mozilla/mozilla/widget/src/mac/nsDragService.cpp
pinkerton%netscape.com 56fa3c56a7 Fix for 40890, pin mouse to center of translucent drag to avoid large gaps between the mouse cursor and the item being dragged. r=sfraser
git-svn-id: svn://10.0.0.236/trunk@75459 18797224-902f-48f8-a5cc-f745e15eee43
2000-08-03 00:43:28 +00:00

817 lines
29 KiB
C++
Raw Blame History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
//
// Mike Pinkerton
// Netscape Communications
//
// See associated header file for details
//
#include "nsDragService.h"
#include "nsITransferable.h"
#include "nsString.h"
#include "nsMimeMapper.h"
#include "nsClipboard.h"
#include "nsIRegion.h"
#include "nsVoidArray.h"
#include "nsISupportsPrimitives.h"
#include "nsCOMPtr.h"
#include "nsXPIDLString.h"
#include "nsPrimitiveHelpers.h"
#include "nsILocalFileMac.h"
// rjc
#include <Gestalt.h>
#include "nsIContent.h"
#include "nsIDOMNode.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIFrame.h"
#include "nsIView.h"
#include "nsRect.h"
#include "nsPoint.h"
#include "nsIWidget.h"
#include "nsIXULContent.h"
#include "nsIDOMElement.h"
#if !TARGET_CARBON
DragSendDataUPP nsDragService::sDragSendDataUPP = NewDragSendDataProc(DragSendDataProc);
#endif
// we need our own stuff for MacOS because of nsIDragSessionMac.
NS_IMPL_ADDREF_INHERITED(nsDragService, nsBaseDragService)
NS_IMPL_RELEASE_INHERITED(nsDragService, nsBaseDragService)
NS_IMPL_QUERY_INTERFACE3(nsDragService, nsIDragService, nsIDragSession, nsIDragSessionMac)
//
// DragService constructor
//
nsDragService::nsDragService()
: mDragRef(0), mDataItems(nsnull), mImageDraggingSupported(PR_FALSE)
{
// rjc - check if the Drag Manager supports image dragging
long response;
OSErr err = Gestalt(gestaltDragMgrAttr, &response);
if (err == noErr && (response & (1L << gestaltDragMgrHasImageSupport)))
{
mImageDraggingSupported = PR_TRUE;
}
}
//
// DragService destructor
//
nsDragService::~nsDragService()
{
}
PRBool
nsDragService :: ComputeGlobalRectFromFrame ( nsIDOMNode* aDOMNode, Rect & outScreenRect )
{
NS_ASSERTION ( aDOMNode, "Oopps, no DOM node" );
// until bug 41237 is fixed, only do translucent dragging if the drag is in
// the chrome or it's a link.
nsCOMPtr<nsIXULContent> xulContent ( do_QueryInterface(aDOMNode) );
if ( !xulContent ) {
// the link node is the parent of the node we have (which is probably text or image).
nsCOMPtr<nsIDOMNode> parent;
aDOMNode->GetParentNode ( getter_AddRefs(parent) );
if ( parent ) {
nsAutoString localName;
parent->GetLocalName ( localName );
if ( ! localName.Equals(NS_LITERAL_STRING("A")) )
return PR_FALSE;
}
else
return FALSE;
}
PRBool haveRectFlag = PR_FALSE;
outScreenRect.left = outScreenRect.right = outScreenRect.top = outScreenRect.bottom = 0;
// Get the frame for this content node (note: frames are not refcounted)
nsIFrame *aFrame = nsnull;
nsCOMPtr<nsIContent> contentNode;
if (aDOMNode) contentNode = do_QueryInterface(aDOMNode);
nsCOMPtr<nsIDocument> doc;
if (contentNode) contentNode->GetDocument(*getter_AddRefs(doc));
nsCOMPtr<nsIPresShell> presShell;
nsCOMPtr<nsIPresContext> presContext;
if (doc)
presShell = getter_AddRefs(doc->GetShellAt(0));
if (presShell) {
presShell->GetPresContext(getter_AddRefs(presContext));
presShell->GetPrimaryFrameFor(contentNode, &aFrame);
}
NS_ASSERTION ( aFrame, "Can't get frame for this dom node" );
NS_ASSERTION ( presContext, "Can't get prescontext for this dom node" );
if ( !aFrame || !presContext )
return PR_FALSE;
//
// Now that we have the frame, we have to convert its coordinates into global screen
// coordinates.
//
nsRect aRect(0,0,0,0);
nsIView *parentView = nsnull;
aFrame->GetRect(aRect);
// Find offset from our view
nsIView *containingView = nsnull;
nsPoint viewOffset(0,0);
aFrame->GetOffsetFromView(presContext, viewOffset, &containingView);
NS_ASSERTION(containingView, "No containing view!");
if ( !containingView )
return PR_FALSE;
// get the widget associated with the containing view.
nsCOMPtr<nsIWidget> aWidget;
nscoord widgetOffsetX = 0, widgetOffsetY = 0;
containingView->GetOffsetFromWidget ( &widgetOffsetX, &widgetOffsetY, *getter_AddRefs(aWidget) );
if (aWidget) {
float t2p = 1.0;
presContext->GetTwipsToPixels(&t2p);
// GetOffsetFromWidget() actually returns the _parent's_ offset from its widget, so we
// still have to add in the offset to |containingView|'s parent ourselves.
nscoord viewOffsetToParentX = 0, viewOffsetToParentY = 0;
containingView->GetPosition ( &viewOffsetToParentX, &viewOffsetToParentY );
// Shift our offset rect by offset into our view, the view's offset to its parent, and
// the parent's offset to the closest widget. Then convert that to global coordinates.
// Recall that WidgetToScreen() will give us the global coordinates of the rectangle we
// give it, but it expects everything to be in pixels.
nsRect screenOffset;
screenOffset.MoveBy ( NSTwipsToIntPixels(widgetOffsetX + viewOffsetToParentX + viewOffset.x, t2p),
NSTwipsToIntPixels(widgetOffsetY + viewOffsetToParentY + viewOffset.y, t2p) );
aWidget->WidgetToScreen ( screenOffset, screenOffset );
// stash it all in a mac rect
outScreenRect.left = screenOffset.x;
outScreenRect.top = screenOffset.y;
outScreenRect.right = outScreenRect.left + NSTwipsToIntPixels(aRect.width, t2p);
outScreenRect.bottom = outScreenRect.top + NSTwipsToIntPixels(aRect.height, t2p);
haveRectFlag = PR_TRUE;
}
return haveRectFlag;
} // ComputeGlobalRectFromFrame
//
// StartDragSession
//
// Do all the work to kick it off.
//
NS_IMETHODIMP
nsDragService :: InvokeDragSession (nsIDOMNode *aDOMNode, nsISupportsArray * aTransferableArray, nsIScriptableRegion * aDragRgn, PRUint32 aActionType)
{
::InitCursor();
nsBaseDragService::InvokeDragSession ( aDOMNode, aTransferableArray, aDragRgn, aActionType );
Rect frameRect = { 0, 0, 0, 0 };
PRBool useRectFromFrame = PR_FALSE;
if ( aDOMNode )
useRectFromFrame = ComputeGlobalRectFromFrame ( aDOMNode, frameRect );
DragReference theDragRef;
OSErr result = ::NewDrag(&theDragRef);
if ( result != noErr )
return NS_ERROR_FAILURE;
mDragRef = theDragRef;
#if DEBUG_DD
printf("**** created drag ref %ld\n", theDragRef);
#endif
// add the flavors from the transferables. Cache this array for the send data proc
mDataItems = aTransferableArray;
RegisterDragItemsAndFlavors ( aTransferableArray ) ;
// we have to synthesize the native event because we may be called from JavaScript
// through XPConnect. In that case, we only have a DOM event and no way to
// get to the native event. As a consequence, we just always fake it.
EventRecord theEvent;
theEvent.what = mouseDown;
theEvent.message = 0L;
theEvent.when = 0L;
if ( useRectFromFrame ) {
// since 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?
theEvent.where.v = round(frameRect.top + (frameRect.bottom - frameRect.top) / 2);
theEvent.where.h = round(frameRect.left + (frameRect.right - frameRect.left) / 2);
}
else {
Point globalMouseLoc;
::GetMouse(&globalMouseLoc);
::LocalToGlobal(&globalMouseLoc);
theEvent.where = globalMouseLoc;
}
theEvent.modifiers = 0L;
RgnHandle theDragRgn = ::NewRgn();
::RectRgn(theDragRgn, &frameRect); // rjc: set rect
if ( useRectFromFrame && mImageDraggingSupported ) {
Point imgOffsetPt;
imgOffsetPt.v = imgOffsetPt.h = 0;
// rjc - note: passing in null for image's PixMapHandle
// to SetDragImage() means use bits on screen
::SetDragImage (theDragRef, nsnull, theDragRgn, imgOffsetPt, kDragDarkerTranslucency);
}
else
BuildDragRegion ( aDragRgn, theEvent.where, theDragRgn );
// 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)
#if !TARGET_CARBON
::SetDragSendProc ( theDragRef, sDragSendDataUPP, this );
#endif
// start the drag. Be careful, mDragRef will be invalid AFTER this call (it is
// reset by the dragTrackingHandler).
StartDragSession();
::TrackDrag ( theDragRef, &theEvent, theDragRgn );
EndDragSession();
// clean up after ourselves
::DisposeRgn ( theDragRgn );
result = ::DisposeDrag ( theDragRef );
#if DEBUG_DD
printf("**** disposing drag ref %ld\n", theDragRef);
#endif
NS_ASSERTION ( result == noErr, "Error disposing drag" );
mDragRef = 0L;
mDataItems = nsnull;
return NS_OK;
} // StartDragSession
//
// BuildDragRegion
//
// Given the XP region describing the drag rectangles, build up an appropriate drag region. If
// the region we're given is null, create our own placeholder.
//
void
nsDragService :: BuildDragRegion ( nsIScriptableRegion* inRegion, Point inGlobalMouseLoc, RgnHandle ioDragRgn )
{
nsCOMPtr<nsIRegion> geckoRegion ( do_QueryInterface(inRegion) );
// create the drag region. Pull out the native mac region from the nsIRegion we're
// given, copy it, inset it one pixel, and subtract them so we're left with just an
// outline. Too bad we can't do this with gfx api's.
//
// At the end, we are left with an outline of the region in global coordinates.
if ( geckoRegion ) {
RgnHandle dragRegion = nsnull;
geckoRegion->GetNativeRegion((void*&)dragRegion);
if ( dragRegion && ioDragRgn ) {
::CopyRgn ( dragRegion, ioDragRgn );
::InsetRgn ( ioDragRgn, 1, 1 );
::DiffRgn ( dragRegion, ioDragRgn, ioDragRgn );
// now shift the region into global coordinates.
Point offsetFromLocalToGlobal = { 0, 0 };
::LocalToGlobal ( &offsetFromLocalToGlobal );
::OffsetRgn ( ioDragRgn, offsetFromLocalToGlobal.h, offsetFromLocalToGlobal.v );
}
}
else {
// no region provided, so we create a default one that is 50 x 50, with the topLeft
// being at the cursor location.
NS_WARNING ( "you MUST pass a drag region for MacOS. This is a warning" );
if ( ioDragRgn ) {
Rect placeHolderRect = { inGlobalMouseLoc.v, inGlobalMouseLoc.h, inGlobalMouseLoc.v + 50,
inGlobalMouseLoc.h + 50 };
RgnHandle placeHolderRgn = ::NewRgn();
if ( placeHolderRgn ) {
::RectRgn ( placeHolderRgn, &placeHolderRect );
::CopyRgn ( placeHolderRgn, ioDragRgn );
::InsetRgn ( ioDragRgn, 1, 1 );
::DiffRgn ( placeHolderRgn, ioDragRgn, ioDragRgn );
::DisposeRgn ( placeHolderRgn );
}
}
}
} // BuildDragRegion
//
// RegisterDragItemsAndFlavors
//
// Takes the multiple drag items from an array of transferables and registers them
// and their flavors with the MacOS DragManager. Note that we don't actually place
// any of the data there yet, but will rely on a sendDataProc to get the data as
// requested.
//
void
nsDragService :: RegisterDragItemsAndFlavors ( nsISupportsArray * inArray )
{
const FlavorFlags flags = 0;
unsigned int numDragItems = 0;
inArray->Count ( &numDragItems ) ;
for ( int itemIndex = 0; itemIndex < numDragItems; ++itemIndex ) {
nsMimeMapperMac theMapper;
nsCOMPtr<nsISupports> genericItem;
inArray->GetElementAt ( itemIndex, getter_AddRefs(genericItem) );
nsCOMPtr<nsITransferable> currItem ( do_QueryInterface(genericItem) );
if ( currItem ) {
nsCOMPtr<nsISupportsArray> flavorList;
if ( NS_SUCCEEDED(currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList))) ) {
PRUint32 numFlavors;
flavorList->Count ( &numFlavors );
for ( int flavorIndex = 0; flavorIndex < numFlavors; ++flavorIndex ) {
nsCOMPtr<nsISupports> genericWrapper;
flavorList->GetElementAt ( flavorIndex, getter_AddRefs(genericWrapper) );
nsCOMPtr<nsISupportsString> currentFlavor ( do_QueryInterface(genericWrapper) );
if ( currentFlavor ) {
nsXPIDLCString flavorStr;
currentFlavor->ToString ( getter_Copies(flavorStr) );
FlavorType macOSFlavor = theMapper.MapMimeTypeToMacOSType(flavorStr);
::AddDragItemFlavor ( mDragRef, itemIndex, macOSFlavor, NULL, 0, flags );
// If we advertise text/unicode, then make sure we add 'TEXT' to the list
// of flavors supported since we will do the conversion ourselves in GetDataForFlavor()
if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
theMapper.MapMimeTypeToMacOSType(kTextMime);
::AddDragItemFlavor ( mDragRef, itemIndex, 'TEXT', NULL, 0, flags );
}
}
} // foreach flavor in item
} // if valid flavor list
} // if item is a transferable
// put the mime mapping data for this item in a special flavor. Unlike the other data,
// we have to put the data in now (rather than defer it) or the mappings will go out
// of scope by the time they are asked for. Remember that the |mappingLen|
// includes the null.
short mappingLen;
char* mapping = theMapper.ExportMapping(&mappingLen);
if ( mapping && mappingLen ) {
::AddDragItemFlavor ( mDragRef, itemIndex, nsMimeMapperMac::MappingFlavor(),
mapping, mappingLen - 1, flags );
nsCRT::free ( mapping );
}
} // foreach drag item
} // RegisterDragItemsAndFlavors
//
// GetData
//
// Pull data out of the OS drag item at the requested index and stash it into the
// given transferable. Only put in the data with the highest fidelity asked for and
// stop as soon as we find a match.
//
NS_IMETHODIMP
nsDragService :: GetData ( nsITransferable * aTransferable, PRUint32 aItemIndex )
{
nsresult errCode = NS_ERROR_FAILURE;
// make sure we have a good transferable
if ( !aTransferable )
return NS_ERROR_INVALID_ARG;
// get flavor list that includes all acceptable flavors (including ones obtained through
// conversion). Flavors are nsISupportsStrings so that they can be seen from JS.
nsCOMPtr<nsISupportsArray> flavorList;
errCode = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
if ( NS_FAILED(errCode) )
return errCode;
// get the data for the requested drag item. Remember that GetDragItemReferenceNumber()
// is one-based NOT zero-based like |aItemIndex| is.
ItemReference itemRef;
::GetDragItemReferenceNumber ( mDragRef, aItemIndex + 1, &itemRef );
// create a mime mapper to help us out based on data in a special flavor for this item
char* mappings = LookupMimeMappingsForItem(mDragRef, itemRef);
nsMimeMapperMac theMapper ( mappings );
nsCRT::free ( mappings );
// Now walk down the list of flavors. When we find one that is actually present,
// copy out the data into the transferable in that format. SetTransferData()
// implicitly handles conversions.
PRUint32 cnt;
flavorList->Count ( &cnt );
for ( int i = 0; i < cnt; ++i ) {
nsCOMPtr<nsISupports> genericWrapper;
flavorList->GetElementAt ( i, getter_AddRefs(genericWrapper) );
nsCOMPtr<nsISupportsString> currentFlavor ( do_QueryInterface(genericWrapper) );
if ( currentFlavor ) {
// find MacOS flavor (but don't add it if it's not there)
nsXPIDLCString flavorStr;
currentFlavor->ToString ( getter_Copies(flavorStr) );
FlavorType macOSFlavor = theMapper.MapMimeTypeToMacOSType(flavorStr, PR_FALSE);
#if DEBUG_DD
printf("looking for data in type %s, mac flavor %ld\n", NS_STATIC_CAST(const char*,flavorStr), macOSFlavor);
#endif
// check if it is present in the current drag item.
FlavorFlags unused;
PRBool dataFound = PR_FALSE;
void* dataBuff;
PRInt32 dataSize = 0;
if ( macOSFlavor && ::GetFlavorFlags(mDragRef, itemRef, macOSFlavor, &unused) == noErr ) {
nsresult loadResult = ExtractDataFromOS(mDragRef, itemRef, macOSFlavor, &dataBuff, &dataSize);
if ( NS_SUCCEEDED(loadResult) && dataBuff )
dataFound = PR_TRUE;
}
else {
// if we are looking for text/unicode and we fail to find it on the clipboard first,
// try again with text/plain. If that is present, convert it to unicode.
if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
if ( ::GetFlavorFlags(mDragRef, itemRef, 'TEXT', &unused) == noErr ) {
nsresult loadResult = ExtractDataFromOS(mDragRef, itemRef, 'TEXT', &dataBuff, &dataSize);
if ( NS_SUCCEEDED(loadResult) && dataBuff ) {
const char* castedText = NS_REINTERPRET_CAST(char*, dataBuff);
PRUnichar* convertedText = nsnull;
PRInt32 convertedTextLen = 0;
nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, dataSize,
&convertedText, &convertedTextLen );
if ( convertedText ) {
// out with the old, in with the new
nsMemory::Free(dataBuff);
dataBuff = convertedText;
dataSize = convertedTextLen * 2;
dataFound = PR_TRUE;
}
} // if plain text data on clipboard
} // if plain text flavor present
} // if looking for text/unicode
} // else we try one last ditch effort to find our data
if ( dataFound ) {
nsCOMPtr<nsISupports> genericDataWrapper;
if ( strcmp(flavorStr, kFileMime) == 0 ) {
// we have a HFSFlavor struct in |dataBuff|. Create an nsLocalFileMac object.
HFSFlavor* fileData = NS_REINTERPRET_CAST(HFSFlavor*, dataBuff);
NS_ASSERTION ( sizeof(HFSFlavor) == dataSize, "Ooops, we realy don't have a HFSFlavor" );
nsCOMPtr<nsILocalFileMac> file;
if ( NS_SUCCEEDED(NS_NewLocalFileWithFSSpec(&fileData->fileSpec, PR_TRUE, getter_AddRefs(file))) )
genericDataWrapper = do_QueryInterface(file);
}
else {
// we probably have some form of text. The DOM only wants LF, so convert k
// from MacOS line endings to DOM line endings.
nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &dataBuff, NS_REINTERPRET_CAST(int*, &dataSize) );
nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, dataBuff, dataSize, getter_AddRefs(genericDataWrapper) );
}
// put it into the transferable.
errCode = aTransferable->SetTransferData ( flavorStr, genericDataWrapper, dataSize );
#ifdef NS_DEBUG
if ( errCode != NS_OK ) printf("nsDragService:: Error setting data into transferable\n");
#endif
nsMemory::Free ( dataBuff );
errCode = NS_OK;
// we found one, get out of this loop!
break;
}
}
} // foreach flavor
return errCode;
}
//
// IsDataFlavorSupported
//
// Check the OS to see if the given drag flavor is in the list. Oddly returns
// NS_OK for success and NS_ERROR_FAILURE if flavor is not present.
//
// Handle the case where we ask for unicode and it's not there, but plain text is. We
// say "yes" in that case, knowing that we will have to perform a conversion when we actually
// pull the data out of the drag.
//
// <20><><EFBFBD> this is obviously useless with more than one drag item. Need to specify
// <20><><EFBFBD><EFBFBD>and index to this API
//
NS_IMETHODIMP
nsDragService :: IsDataFlavorSupported(const char *aDataFlavor, PRBool *_retval)
{
if ( !_retval )
return NS_ERROR_INVALID_ARG;
#ifdef NS_DEBUG
if ( strcmp(aDataFlavor, kTextMime) == 0 )
NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
#endif
*_retval = PR_FALSE;
// convert to 4 character MacOS type
//<2F><><EFBFBD> this is wrong because it doesn't take the mime mappings present in the
//<2F><><EFBFBD> drag item flavor into account. FIX ME!
nsMimeMapperMac theMapper;
FlavorType macFlavor = theMapper.MapMimeTypeToMacOSType(aDataFlavor);
// search through all drag items looking for something with this flavor. Recall
// that drag item indices are 1-based.
unsigned short numDragItems = 0;
::CountDragItems ( mDragRef, &numDragItems );
for ( int i = 1; i <= numDragItems; ++i ) {
ItemReference currItem;
OSErr res = ::GetDragItemReferenceNumber ( mDragRef, i, &currItem );
if ( res != noErr )
return NS_ERROR_FAILURE;
FlavorFlags ignored;
if ( ::GetFlavorFlags(mDragRef, currItem, macFlavor, &ignored) == noErr )
*_retval = PR_TRUE;
else {
// if the client asked for unicode and it wasn't present, check if we have TEXT.
// We'll handle the actual data substitution in GetDataForFlavor().
if ( strcmp(aDataFlavor, kUnicodeMime) == 0 ) {
if ( ::GetFlavorFlags(mDragRef, currItem, 'TEXT', &ignored) == noErr )
*_retval = PR_TRUE;
}
}
} // for each item in drag
return NS_OK;
} // IsDataFlavorSupported
//
// GetNumDropItems
//
// Returns the number of drop items present in the current drag.
//
NS_IMETHODIMP
nsDragService :: GetNumDropItems ( PRUint32 * aNumItems )
{
// we have to put it into a short first because that's what the MacOS API's expect.
// After it's in a short, getting it into a long is no problem. Oh well.
unsigned short numDragItems = 0;
OSErr result = ::CountDragItems ( mDragRef, &numDragItems );
*aNumItems = numDragItems;
return ( result == noErr ? NS_OK : NS_ERROR_FAILURE );
} // GetNumDropItems
//
// SetDragReference
//
// An API to allow the drag manager callback functions to tell the session about the
// current dragRef w/out resorting to knowing the internals of the implementation
//
NS_IMETHODIMP
nsDragService :: SetDragReference ( DragReference aDragRef )
{
mDragRef = aDragRef;
return NS_OK;
} // SetDragReference
//
// DragSendDataProc
//
// Called when a drop occurs and the drop site asks for the data.
//
// This will only get called when Mozilla is the originator of the drag, so we can
// make certain assumptions. One is that we've cached the list of transferables in the
// drag session. The other is that the item ref is the index into this list so we
// can easily pull out the item asked for.
//
// We cannot rely on |mDragRef| at this point so we have to use what was passed in
// and pass that along.
//
pascal OSErr
nsDragService :: DragSendDataProc ( FlavorType inFlavor, void* inRefCon, ItemReference inItemRef,
DragReference inDragRef )
{
OSErr retVal = noErr;
nsDragService* self = NS_STATIC_CAST(nsDragService*, inRefCon);
NS_ASSERTION ( self, "Refcon not set correctly for DragSendDataProc" );
if ( self ) {
void* data = nsnull;
PRUint32 dataSize = 0;
retVal = self->GetDataForFlavor ( self->mDataItems, inDragRef, inItemRef, inFlavor, &data, &dataSize );
if ( retVal == noErr ) {
// make the data accessable to the DragManager
retVal = ::SetDragItemFlavorData ( inDragRef, inItemRef, inFlavor, data, dataSize, 0 );
NS_ASSERTION ( retVal == noErr, "SDIFD failed in DragSendDataProc" );
}
nsMemory::Free ( data );
} // if valid refcon
return retVal;
} // DragSendDataProc
//
// GetDataForFlavor
//
// Given a MacOS flavor and an index for which drag item to lookup, get the information from the
// drag item corresponding to this flavor.
//
// This routine also handles the conversions between text/plain and text/unicode to take platform
// charset encodings into account. If someone asks for text/plain, we convert the unicode (we assume
// it is present because that's how we knew to advertise text/plain in the first place) and give it
// to them. If someone asks for text/unicode, and it isn't there, we need to convert text/plain and
// hand them back the unicode. Again, we can assume that text/plain is there because otherwise we
// wouldn't have been allowed to look for unicode.
//
OSErr
nsDragService :: GetDataForFlavor ( nsISupportsArray* inDragItems, DragReference inDragRef, unsigned int inItemIndex,
FlavorType inFlavor, void** outData, unsigned int* outDataSize )
{
if ( !inDragItems || !inDragRef )
return paramErr;
OSErr retVal = noErr;
// (assumes that the items were placed into the transferable as nsITranferable*'s, not nsISupports*'s.)
nsCOMPtr<nsISupports> genericItem;
inDragItems->GetElementAt ( inItemIndex, getter_AddRefs(genericItem) );
nsCOMPtr<nsITransferable> item ( do_QueryInterface(genericItem) );
if ( item ) {
nsCAutoString mimeFlavor;
// create a mime mapper to help us out based on data in a special flavor for this item.
char* mappings = LookupMimeMappingsForItem(inDragRef, inItemIndex) ;
nsMimeMapperMac theMapper ( mappings );
theMapper.MapMacOSTypeToMimeType ( inFlavor, mimeFlavor );
nsMemory::Free ( mappings );
// if someone was asking for text/plain, lookup unicode instead so we can convert it.
PRBool needToDoConversionToPlainText = PR_FALSE;
char* actualFlavor = mimeFlavor;
if ( strcmp(mimeFlavor,kTextMime) == 0 ) {
actualFlavor = kUnicodeMime;
needToDoConversionToPlainText = PR_TRUE;
}
else
actualFlavor = mimeFlavor;
*outDataSize = 0;
nsCOMPtr<nsISupports> data;
if ( NS_SUCCEEDED(item->GetTransferData(actualFlavor, getter_AddRefs(data), outDataSize)) ) {
nsPrimitiveHelpers::CreateDataFromPrimitive ( actualFlavor, data, outData, *outDataSize );
// if required, do the extra work to convert unicode to plain text and replace the output
// values with the plain text.
if ( needToDoConversionToPlainText ) {
char* plainTextData = nsnull;
PRUnichar* castedUnicode = NS_REINTERPRET_CAST(PRUnichar*, *outData);
PRInt32 plainTextLen = 0;
nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( castedUnicode, *outDataSize / 2, &plainTextData, &plainTextLen );
if ( *outData ) {
nsMemory::Free(*outData);
*outData = plainTextData;
*outDataSize = plainTextLen;
}
else
retVal = cantGetFlavorErr;
}
}
else
retVal = cantGetFlavorErr;
} // if valid item
return retVal;
} // GetDataForFlavor
//
// LookupMimeMappingsForItem
//
// load up the flavor data for the mime mapper from the drag item and create a
// mime mapper to help us go between mime types and macOS flavors. It may not be
// there (such as when the drag originated from another app), but that's ok.
//
// Caller is responsible for deleting the memory.
//
char*
nsDragService :: LookupMimeMappingsForItem ( DragReference inDragRef, ItemReference inItemRef )
{
char* mapperData = nsnull;
PRInt32 mapperSize = 0;
ExtractDataFromOS(inDragRef, inItemRef, nsMimeMapperMac::MappingFlavor(), (void**)&mapperData, &mapperSize);
return mapperData;
#if 0
OSErr err = ::GetFlavorDataSize ( inDragRef, itemRef, nsMimeMapperMac::MappingFlavor(), &mapperSize );
if ( !err && mapperSize > 0 ) {
mapperData = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(mapperSize + 1));
if ( !mapperData )
return nsnull;
err = ::GetFlavorData ( inDragRef, itemRef, nsMimeMapperMac::MappingFlavor(), mapperData, &mapperSize, 0 );
if ( err ) {
#ifdef NS_DEBUG
printf("nsDragService: Error getting data out of drag manager for mime mapper, #%ld\n", err);
#endif
return nsnull;
}
else
mapperData[mapperSize] = '\0'; // null terminate the data
}
return mapperData;
#endif
}
//
// ExtractDataFromOS
//
// Handles pulling the data from the DragManager. Will return NS_ERROR_FAILURE if it can't get
// the data for whatever reason.
//
nsresult
nsDragService :: ExtractDataFromOS ( DragReference inDragRef, ItemReference inItemRef, ResType inFlavor,
void** outBuffer, PRInt32* outBuffSize )
{
if ( !outBuffer || !outBuffSize || !inFlavor )
return NS_ERROR_FAILURE;
nsresult retval = NS_OK;
char* buff = nsnull;
Size buffSize = 0;
OSErr err = ::GetFlavorDataSize ( inDragRef, inItemRef, inFlavor, &buffSize );
if ( !err && buffSize > 0 ) {
buff = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(buffSize + 1));
if ( buff ) {
err = ::GetFlavorData ( inDragRef, inItemRef, inFlavor, buff, &buffSize, 0 );
if ( err ) {
#ifdef NS_DEBUG
printf("nsDragService: Error getting data out of drag manager, #%ld\n", err);
#endif
retval = NS_ERROR_FAILURE;
}
}
else
retval = NS_ERROR_FAILURE;
}
if ( NS_FAILED(retval) ) {
if ( buff )
nsMemory::Free(buff);
}
else {
*outBuffer = buff;
*outBuffSize = buffSize;
}
return retval;
} // ExtractDataFromOS