Mozilla/mozilla/widget/src/mac/nsDragService.cpp
pinkerton%netscape.com 4deec81575 fixing some off-by-one errors, some nsISupportsArray confusions with the calling code, removing some printf's.
git-svn-id: svn://10.0.0.236/trunk@39434 18797224-902f-48f8-a5cc-f745e15eee43
1999-07-14 22:57:22 +00:00

440 lines
14 KiB
C++

/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
//
// Mike Pinkerton
// Netscape Communications
//
// See associated header file for details
//
#include "nsDragService.h"
#include "nsITransferable.h"
#include "nsString.h"
#include "nsMimeMapper.h"
#include "nsWidgetsCID.h"
#include "nsClipboard.h"
#include "nsIRegion.h"
#include "nsVoidArray.h"
DragSendDataUPP nsDragService::sDragSendDataUPP = NewDragSendDataProc(DragSendDataProc);
//
// DragService constructor
//
nsDragService::nsDragService()
: mDragRef(0), mDataItems(nsnull)
{
}
//
// DragService destructor
//
nsDragService::~nsDragService()
{
}
//
// AddRef
// Release
// QueryInterface
//
// handle the QI for nsIDragSessionMac and farm off anything else to the parent
// class.
//
NS_IMPL_ISUPPORTS_INHERITED(nsDragService,nsBaseDragService,nsIDragSessionMac);
//
// StartDragSession
//
// Do all the work to kick it off.
//
NS_IMETHODIMP
nsDragService :: InvokeDragSession (nsISupportsArray * aTransferableArray, nsIRegion * aDragRgn, PRUint32 aActionType)
{
DragReference theDragRef;
OSErr result = ::NewDrag(&theDragRef);
if ( result )
return NS_ERROR_FAILURE;
mDragRef = theDragRef;
// 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.
Point globalMouseLoc;
::GetMouse(&globalMouseLoc);
::LocalToGlobal(&globalMouseLoc);
WindowPtr theWindow = nsnull;
if ( ::FindWindow(globalMouseLoc, &theWindow) != inContent ) {
// debugging sanity check
#ifdef NS_DEBUG
DebugStr("\pAbout to start drag, but FindWindow() != inContent; g");
#endif
}
EventRecord theEvent;
theEvent.what = mouseDown;
theEvent.message = reinterpret_cast<UInt32>(theWindow);
theEvent.when = 0;
theEvent.where = globalMouseLoc;
theEvent.modifiers = 0;
RgnHandle theDragRgn = ::NewRgn();
BuildDragRegion ( aDragRgn, globalMouseLoc, 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)
::SetDragSendProc ( theDragRef, sDragSendDataUPP, this );
// start the drag. Be careful, mDragRef will be invalid AFTER this call (it is
// reset by the dragTrackingHandler).
::TrackDrag ( theDragRef, &theEvent, theDragRgn );
// clean up after ourselves
::DisposeRgn ( theDragRgn );
result = ::DisposeDrag ( theDragRef );
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 ( nsIRegion* inRegion, Point inGlobalMouseLoc, RgnHandle ioDragRgn )
{
// 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 ( inRegion ) {
RgnHandle dragRegion = nsnull;
inRegion->GetNativeRegion(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 ) {
// (assumes that the items were placed into the transferable as nsITranferable*'s, not
// nsISupports*'s. Don't forget ElementAt() addRefs for us)
nsCOMPtr<nsITransferable> currItem = dont_AddRef(NS_STATIC_CAST(nsITransferable*,inArray->ElementAt(itemIndex)));
if ( currItem ) {
nsVoidArray* flavorList = nsnull;
if ( NS_SUCCEEDED(currItem->FlavorsTransferableCanExport(&flavorList)) ) {
for ( int flavorIndex = 0; flavorIndex < flavorList->Count(); ++flavorIndex ) {
nsString* currentFlavor = NS_STATIC_CAST(nsString*, (*flavorList)[flavorIndex]);
if ( nsnull != currentFlavor ) {
FlavorType macOSFlavor = nsMimeMapperMac::MapMimeTypeToMacOSType(*currentFlavor);
::AddDragItemFlavor ( mDragRef, itemIndex, macOSFlavor, NULL, 0, flags );
}
} // foreach flavor in item
} // if valid flavor list
} // if item is a transferable
} // 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)
nsVoidArray* flavorList;
errCode = aTransferable->FlavorsTransferableCanImport ( &flavorList );
if ( errCode != NS_OK )
return NS_ERROR_FAILURE;
// 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 );
// 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();
for ( int i = 0; i < cnt; ++i ) {
nsString * currentFlavor = NS_STATIC_CAST(nsString*, (*flavorList)[i]);
if ( nsnull != currentFlavor ) {
// find MacOS flavor
FlavorType macOSFlavor = nsMimeMapperMac::MapMimeTypeToMacOSType(*currentFlavor);
printf("looking for data in type %s, mac flavor %ld\n", currentFlavor->ToNewCString(), macOSFlavor);
// check if it is present in the current drag item.
FlavorFlags unused;
if ( ::GetFlavorFlags(mDragRef, itemRef, macOSFlavor, &unused) == noErr ) {
printf("flavor found\n");
// we have it, pull it out of the drag manager. Put it into memory that we allocate
// with new[] so that the tranferable can own it (and then later use delete[]
// on it).
Size dataSize = 0;
OSErr err = ::GetFlavorDataSize ( mDragRef, itemRef, macOSFlavor, &dataSize );
printf("flavor data size is %ld\n", dataSize);
if ( !err && dataSize > 0 ) {
char* dataBuff = new char[dataSize];
if ( !dataBuff )
return NS_ERROR_OUT_OF_MEMORY;
err = ::GetFlavorData ( mDragRef, itemRef, macOSFlavor, dataBuff, &dataSize, 0 );
if ( err ) {
#ifdef NS_DEBUG
printf("nsClipboard: Error getting data out of drag manager, #%ld\n", err);
#endif
return NS_ERROR_FAILURE;
}
// put it into the transferable
errCode = aTransferable->SetTransferData ( currentFlavor, dataBuff, dataSize );
#ifdef NS_DEBUG
if ( errCode != NS_OK ) printf("nsDragService:: Error setting data into transferable\n");
#endif
}
else {
#ifdef NS_DEBUG
printf("nsDragService: Error getting data out of drag manager, #%ld\n", err);
#endif
errCode = NS_ERROR_FAILURE;
}
// we found one, get out of this loop!
break;
} // if a flavor found
}
} // foreach flavor
delete flavorList;
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.
//
NS_IMETHODIMP
nsDragService :: IsDataFlavorSupported(nsString * aDataFlavor)
{
nsresult flavorSupported = NS_ERROR_FAILURE;
// convert to 4 character MacOS type
FlavorType macFlavor = nsMimeMapperMac::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;
char foundFlavor = ::GetFlavorFlags(mDragRef, currItem, macFlavor, &ignored) == noErr;
if ( foundFlavor )
flavorSupported = NS_OK;
} // for each item in drag
return flavorSupported;
} // 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.
//
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, 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" );
}
} // 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.
//
OSErr
nsDragService :: GetDataForFlavor ( nsISupportsArray* inDragItems, unsigned int inItemIndex,
FlavorType inFlavor, void** outData, unsigned int* outDataSize )
{
if ( !inDragItems )
return paramErr;
OSErr retVal = noErr;
// (assumes that the items were placed into the transferable as nsITranferable*'s, not
// nsISupports*'s. Don't forget ElementAt() addRefs for us.)
nsCOMPtr<nsITransferable> item = dont_AddRef(NS_STATIC_CAST(nsITransferable*,inDragItems->ElementAt(inItemIndex)));
if ( item ) {
nsString mimeFlavor;
nsMimeMapperMac::MapMacOSTypeToMimeType ( inFlavor, mimeFlavor );
*outDataSize = 0;
if ( NS_FAILED(item->GetTransferData(&mimeFlavor, outData, outDataSize)) )
retVal = cantGetFlavorErr;
} // if valid item
return retVal;
} // GetDataForFlavor