Back to index

lightning-sunbird  0.9+nobinonly
nsDragService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00039 //
00040 // Mike Pinkerton
00041 // Netscape Communications
00042 //
00043 // See associated header file for details
00044 //
00045 
00046 
00047 #include <Gestalt.h>
00048 
00049 #include "nsDragService.h"
00050 
00051 #include "nsITransferable.h"
00052 #include "nsString.h"
00053 #include "nsMimeMapper.h"
00054 #include "nsClipboard.h"
00055 #include "nsIRegion.h"
00056 #include "nsXPCOM.h"
00057 #include "nsISupportsPrimitives.h"
00058 #include "nsCOMPtr.h"
00059 #include "nsCRT.h"
00060 #include "nsPrimitiveHelpers.h"
00061 #include "nsLinebreakConverter.h"
00062 #include "nsIMacUtils.h"
00063 
00064 #include "nsIContent.h"
00065 #include "nsIDOMNode.h"
00066 #include "nsIDocument.h"
00067 #include "nsIPresShell.h"
00068 #include "nsPresContext.h"
00069 #include "nsIFrame.h"
00070 #include "nsIView.h"
00071 #include "nsRect.h"
00072 #include "nsPoint.h"
00073 #include "nsIWidget.h"
00074 #include "nsCarbonHelpers.h"
00075 #include "nsGfxUtils.h"
00076 
00077 // file save stuff
00078 #include "nsNetUtil.h"
00079 #include "nsILocalFileMac.h"
00080 
00081 #include "nsIDOMElement.h"
00082 #include "nsIImageMac.h"
00083 #include "nsIImage.h"
00084 #include "nsMacNativeUnicodeConverter.h"
00085 #include "nsICharsetConverterManager.h"
00086 #include "nsStylClipboardUtils.h"
00087 
00088 static const PRUint32 kPrivateFlavorMask = 0xffff0000;
00089 static const PRUint32 kPrivateFlavorTag = 'MZ..' & kPrivateFlavorMask;
00090 
00091 
00092 // we need our own stuff for MacOS because of nsIDragSessionMac.
00093 NS_IMPL_ADDREF_INHERITED(nsDragService, nsBaseDragService)
00094 NS_IMPL_RELEASE_INHERITED(nsDragService, nsBaseDragService)
00095 NS_IMPL_QUERY_INTERFACE4(nsDragService,
00096                          nsIDragService,
00097                          nsIDragService_1_8_BRANCH,
00098                          nsIDragSession,
00099                          nsIDragSessionMac)
00100 
00101 
00102 //
00103 // DragService constructor
00104 //
00105 nsDragService::nsDragService()
00106   : mDragSendDataUPP(nsnull)
00107   , mDragRef(0)
00108   , mDataItems(nsnull)
00109   , mImageDraggingSupported(PR_FALSE)
00110 {
00111 #if USE_TRANSLUCENT_DRAGS
00112   // check if the Drag Manager supports image dragging
00113   // however, it doesn't really work too well, so let's back off (pinkerton)
00114   long response;
00115   OSErr err = Gestalt(gestaltDragMgrAttr, &response); 
00116   if (err == noErr && (response & (1L << gestaltDragMgrHasImageSupport))) {
00117     mImageDraggingSupported = PR_TRUE;
00118   }
00119 #endif
00120   
00121   mDragSendDataUPP = NewDragSendDataUPP(DragSendDataProc);
00122 }
00123 
00124 
00125 //
00126 // DragService destructor
00127 //
00128 nsDragService::~nsDragService()
00129 {
00130   if ( mDragSendDataUPP )
00131     ::DisposeDragSendDataUPP(mDragSendDataUPP);
00132 }
00133 
00134 
00135 PRBool
00136 nsDragService::ComputeGlobalRectFromFrame ( nsIDOMNode* aDOMNode, Rect & outScreenRect )
00137 {
00138   NS_ASSERTION ( aDOMNode, "Oopps, no DOM node" );
00139 
00140 // this isn't so much of an issue as long as we're just dragging around outlines,
00141 // but it is when we are showing the text being drawn. Comment it out for now
00142 // but leave it around when we turn this all back on (pinkerton).
00143 #if USE_TRANSLUCENT_DRAGGING && defined(MOZ_XUL)
00144   // until bug 41237 is fixed, only do translucent dragging if the drag is in
00145   // the chrome or it's a link.
00146   nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
00147   if (!content || !content->IsContentOfType(nsIContent::eXUL)) {
00148     // the link node is the parent of the node we have (which is probably text or image).
00149     nsCOMPtr<nsIDOMNode> parent;
00150     aDOMNode->GetParentNode ( getter_AddRefs(parent) );
00151     if ( parent ) {
00152       nsAutoString localName;
00153       parent->GetLocalName ( localName );
00154       if ( ! localName.EqualsLiteral("A") )
00155         return PR_FALSE;
00156     }
00157     else
00158       return FALSE;
00159   }
00160 #endif
00161   
00162   outScreenRect.left = outScreenRect.right = outScreenRect.top = outScreenRect.bottom = 0;
00163 
00164   // Get the frame for this content node (note: frames are not refcounted)
00165   nsIFrame *aFrame = nsnull;
00166   nsCOMPtr<nsPresContext> presContext;
00167   GetFrameFromNode ( aDOMNode, &aFrame, getter_AddRefs(presContext) );
00168   if ( !aFrame || !presContext )
00169     return PR_FALSE;
00170   
00171   //
00172   // Now that we have the frame, we have to convert its coordinates into global screen
00173   // coordinates.
00174   //
00175   
00176   nsRect rect = aFrame->GetRect();
00177 
00178   // Find offset from our view
00179        nsIView *containingView = nsnull;
00180        nsPoint       viewOffset(0,0);
00181        aFrame->GetOffsetFromView(viewOffset, &containingView);
00182   NS_ASSERTION(containingView, "No containing view!");
00183   if ( !containingView )
00184     return PR_FALSE;
00185 
00186   // get the widget associated with the containing view. 
00187   nsPoint widgetOffset;
00188   nsIWidget* aWidget = containingView->GetNearestWidget ( &widgetOffset );
00189 
00190   float t2p = 1.0;
00191   t2p = presContext->TwipsToPixels();
00192 
00193   // Shift our offset rect by offset into our view, and
00194   // the view's offset to the closest widget. Then convert that to global coordinates.
00195   // Recall that WidgetToScreen() will give us the global coordinates of the rectangle we 
00196   // give it, but it expects  everything to be in pixels.
00197   nsRect screenOffset;                                
00198   screenOffset.MoveBy ( NSTwipsToIntPixels(widgetOffset.x + viewOffset.x, t2p),
00199                         NSTwipsToIntPixels(widgetOffset.y + viewOffset.y, t2p) );
00200   aWidget->WidgetToScreen ( screenOffset, screenOffset );
00201 
00202   // stash it all in a mac rect
00203   outScreenRect.left = screenOffset.x;
00204   outScreenRect.top = screenOffset.y;
00205   outScreenRect.right = outScreenRect.left + NSTwipsToIntPixels(rect.width, t2p);
00206   outScreenRect.bottom = outScreenRect.top + NSTwipsToIntPixels(rect.height, t2p);
00207             
00208   return PR_TRUE;
00209 } // ComputeGlobalRectFromFrame
00210 
00211 
00212 //
00213 // InvokeDragSession
00214 //
00215 // Do all the work to kick it off.
00216 //
00217 NS_IMETHODIMP
00218 nsDragService::InvokeDragSession (nsIDOMNode *aDOMNode, nsISupportsArray * aTransferableArray, nsIScriptableRegion * aDragRgn, PRUint32 aActionType)
00219 {
00220 #ifdef MOZ_WIDGET_COCOA
00221   nsGraphicsUtils::SetPortToKnownGoodPort();
00222   GrafPtr port;
00223   GDHandle handle;
00224   ::GetGWorld(&port, &handle);
00225   if (!IsValidPort(port))
00226   return NS_ERROR_FAILURE;
00227 #endif
00228 
00229   ::InitCursor();
00230   nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode,
00231                                                      aTransferableArray,
00232                                                      aDragRgn, aActionType);
00233   NS_ENSURE_SUCCESS(rv, rv);
00234   
00235   DragReference theDragRef;
00236   OSErr result = ::NewDrag(&theDragRef);
00237   if ( result != noErr )
00238     return NS_ERROR_FAILURE;
00239   mDragRef = theDragRef;
00240 #if DEBUG_DD
00241   printf("**** created drag ref %ld\n", theDragRef);
00242 #endif
00243   
00244   Rect frameRect = { 0, 0, 0, 0 };
00245   RgnHandle theDragRgn = ::NewRgn();
00246   ::RectRgn(theDragRgn, &frameRect);
00247 
00248   if ( mImageDraggingSupported ) {
00249     Point     imgOffsetPt;
00250     imgOffsetPt.v = imgOffsetPt.h = 0;
00251 
00252     PRBool canUseRect = BuildDragRegion ( aDragRgn, aDOMNode, theDragRgn );
00253     if ( canUseRect ) {
00254       // passing in null for image's PixMapHandle to SetDragImage() means use bits on screen
00255       ::SetDragImage (theDragRef, nsnull, theDragRgn, imgOffsetPt, kDragDarkerTranslucency);
00256     }
00257   }
00258   else
00259     BuildDragRegion ( aDragRgn, aDOMNode, theDragRgn );
00260 
00261   // add the flavors from the transferables. Cache this array for the send data proc
00262   mDataItems = aTransferableArray;
00263   RegisterDragItemsAndFlavors(aTransferableArray, theDragRgn);
00264 
00265   // we have to synthesize the native event because we may be called from JavaScript
00266   // through XPConnect. In that case, we only have a DOM event and no way to
00267   // get to the native event. As a consequence, we just always fake it.
00268   EventRecord theEvent;
00269   theEvent.what = mouseDown;
00270   theEvent.message = 0L;
00271   theEvent.when = TickCount();
00272   theEvent.modifiers = 0L;
00273 
00274   // since we don't have the original mouseDown location, just assume the drag
00275   // started in the middle of the frame. This prevents us from having the mouse
00276   // and the region we're dragging separated by a big gap (which could happen if
00277   // we used the current mouse position). I know this isn't exactly right, and you can
00278   // see it if you're paying attention, but who pays such close attention?
00279   Rect dragRect;
00280   ::GetRegionBounds(theDragRgn, &dragRect);
00281   theEvent.where.v = dragRect.top + ((dragRect.bottom - dragRect.top) / 2);
00282   theEvent.where.h = dragRect.left + ((dragRect.right - dragRect.left) / 2);
00283 
00284   // register drag send proc which will call us back when asked for the actual
00285   // flavor data (instead of placing it all into the drag manager)
00286   ::SetDragSendProc ( theDragRef, mDragSendDataUPP, this );
00287 
00288   // start the drag. Be careful, mDragRef will be invalid AFTER this call (it is
00289   // reset by the dragTrackingHandler).
00290   StartDragSession();
00291   ::TrackDrag ( theDragRef, &theEvent, theDragRgn );
00292 #ifndef MOZ_WIDGET_COCOA
00293   EndDragSession();
00294 #else  // MOZ_WIDGET_COCOA
00295   if (mDoingDrag) {
00296     // An action proc inside of TrackDrag may have already ended the drag.
00297     EndDragSession();
00298   }
00299 #endif  // MOZ_WIDGET_COCOA
00300   
00301   // clean up after ourselves 
00302   ::DisposeRgn ( theDragRgn );
00303   result = ::DisposeDrag ( theDragRef );
00304 #if DEBUG_DD
00305   printf("**** disposing drag ref %ld\n", theDragRef);
00306 #endif
00307   NS_ASSERTION ( result == noErr, "Error disposing drag" );
00308   mDragRef = 0L;
00309   mDataItems = nsnull;
00310 
00311   return NS_OK; 
00312 
00313 } // InvokeDragSession
00314 
00315 
00316 //
00317 // BuildDragRegion
00318 //
00319 // Given the XP region describing the drag rectangles, build up an appropriate drag region. If
00320 // the region we're given is null, try the given DOM node. If that doesn't work fake it as 
00321 // best we can.
00322 //
00323 // Returns PR_TRUE if the region is something that can be used with SetDragImage()
00324 //
00325 PRBool
00326 nsDragService::BuildDragRegion ( nsIScriptableRegion* inRegion, nsIDOMNode* inNode, RgnHandle ioDragRgn )
00327 {
00328   PRBool retVal = PR_TRUE;
00329   nsCOMPtr<nsIRegion> geckoRegion;
00330   if ( inRegion )
00331     inRegion->GetRegion(getter_AddRefs(geckoRegion));
00332     
00333 #ifdef MOZ_WIDGET_COCOA
00334   nsGraphicsUtils::SetPortToKnownGoodPort();
00335   GrafPtr port;
00336   GDHandle handle;
00337   ::GetGWorld(&port, &handle);
00338   if (!IsValidPort(port))
00339   return NS_ERROR_FAILURE;
00340 #endif
00341 
00342   // create the drag region. Pull out the native mac region from the nsIRegion we're
00343   // given, copy it, inset it one pixel, and subtract them so we're left with just an
00344   // outline. Too bad we can't do this with gfx api's.
00345   //
00346   // At the end, we are left with an outline of the region in global coordinates.
00347   if ( geckoRegion ) {
00348     RgnHandle dragRegion = nsnull;
00349     geckoRegion->GetNativeRegion((void*&)dragRegion);
00350     if ( dragRegion && ioDragRgn ) {
00351       ::CopyRgn ( dragRegion, ioDragRgn );
00352       ::InsetRgn ( ioDragRgn, 1, 1 );
00353       ::DiffRgn ( dragRegion, ioDragRgn, ioDragRgn ); 
00354       
00355       // now shift the region into global coordinates.
00356       Point offsetFromLocalToGlobal = { 0, 0 };
00357       ::LocalToGlobal ( &offsetFromLocalToGlobal );
00358       ::OffsetRgn ( ioDragRgn, offsetFromLocalToGlobal.h, offsetFromLocalToGlobal.v );
00359     }
00360   }
00361   else {
00362     PRBool useRectFromFrame = PR_FALSE;
00363     
00364     // no region provided, let's try to use the dom node to get the frame. Pick a 
00365     // silly default in case we can't get it.
00366     Point currMouse;
00367     ::GetMouse(&currMouse);
00368     Rect frameRect = { currMouse.v, currMouse.h, currMouse.v + 25, currMouse.h + 100 };
00369     if ( inNode )
00370       useRectFromFrame = ComputeGlobalRectFromFrame ( inNode, frameRect );
00371     else
00372       NS_WARNING ( "Can't find anything to get a drag rect from. I'm dyin' out here!" );
00373 
00374     if ( ioDragRgn ) {
00375       RgnHandle frameRgn = ::NewRgn();
00376       if ( frameRgn ) {
00377         ::RectRgn ( frameRgn, &frameRect );
00378         ::CopyRgn ( frameRgn, ioDragRgn );
00379         ::InsetRgn ( ioDragRgn, 1, 1 );
00380         ::DiffRgn ( frameRgn, ioDragRgn, ioDragRgn );
00381         
00382         ::DisposeRgn ( frameRgn );
00383       }
00384     }
00385     
00386     // if we couldn't find the exact frame coordinates, then we need to alert people that
00387     // they shouldn't use this as the basis of SetDragImage()
00388     retVal = useRectFromFrame;
00389   }
00390 
00391   return retVal;
00392   
00393 } // BuildDragRegion
00394 
00395 
00396 //
00397 // RegisterDragItemsAndFlavors
00398 //
00399 // Takes the multiple drag items from an array of transferables and registers them
00400 // and their flavors with the MacOS DragManager. Note that we don't actually place
00401 // any of the data there yet, but will rely on a sendDataProc to get the data as
00402 // requested.
00403 //
00404 void
00405 nsDragService::RegisterDragItemsAndFlavors(nsISupportsArray* inArray, RgnHandle inDragRgn)
00406 {
00407   const FlavorFlags flags = 0;
00408   
00409   Rect      dragRgnBounds = {0, 0, 0, 0};
00410   if (inDragRgn)
00411     GetRegionBounds(inDragRgn, &dragRgnBounds);
00412 
00413   PRUint32 numDragItems = 0;
00414   inArray->Count ( &numDragItems ) ;
00415   for ( PRUint32 itemIndex = 0; itemIndex < numDragItems; ++itemIndex ) {
00416     nsMimeMapperMac theMapper;
00417   
00418     nsCOMPtr<nsISupports> genericItem;
00419     inArray->GetElementAt ( itemIndex, getter_AddRefs(genericItem) );
00420     nsCOMPtr<nsITransferable> currItem ( do_QueryInterface(genericItem) );
00421     if ( currItem ) {   
00422       nsCOMPtr<nsISupportsArray> flavorList;
00423       if ( NS_SUCCEEDED(currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList))) ) {
00424         PRUint32 numFlavors;
00425         flavorList->Count ( &numFlavors );
00426         for ( PRUint32 flavorIndex = 0; flavorIndex < numFlavors; ++flavorIndex ) {
00427         
00428           nsCOMPtr<nsISupports> genericWrapper;
00429           flavorList->GetElementAt ( flavorIndex, getter_AddRefs(genericWrapper) );
00430           nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericWrapper) );
00431              if ( currentFlavor ) {
00432                nsCAutoString flavorStr;
00433                currentFlavor->GetData(flavorStr);
00434                FlavorType macOSFlavor = theMapper.MapMimeTypeToMacOSType(flavorStr.get());
00435               
00436             if (macOSFlavor == kDragFlavorTypePromiseHFS) {
00437               // we got kFilePromiseMime
00438               // kDragFlavorTypePromiseHFS is special. See http://developer.apple.com/technotes/tn/tn1085.html
00439               PromiseHFSFlavor promiseData;
00440               promiseData.fileType           = 0;     // let the file extension prevail!
00441               promiseData.fileCreator        = 0;
00442               promiseData.fdFlags            = 0;
00443               promiseData.promisedFlavor     = kDragPromisedFlavor;
00444 
00445               ::AddDragItemFlavor(mDragRef, itemIndex, kDragFlavorTypePromiseHFS, &promiseData, sizeof(promiseData), flavorNotSaved);
00446               ::AddDragItemFlavor(mDragRef, itemIndex, kDragPromisedFlavor, NULL, 0, flavorNotSaved);
00447             }
00448             else
00449               ::AddDragItemFlavor(mDragRef, itemIndex, macOSFlavor, NULL, 0, flags);
00450                
00451                // If we advertise text/unicode, then make sure we add 'TEXT' to the list
00452                // of flavors supported since we will do the conversion ourselves in GetDataForFlavor()
00453                if ( strcmp(flavorStr.get(), kUnicodeMime) == 0 ) {
00454                  theMapper.MapMimeTypeToMacOSType(kTextMime);
00455                  ::AddDragItemFlavor ( mDragRef, itemIndex, 'TEXT', NULL, 0, flags );              
00456                  ::AddDragItemFlavor ( mDragRef, itemIndex, 'styl', NULL, 0, flags );              
00457                }
00458              }
00459           
00460         } // foreach flavor in item              
00461       } // if valid flavor list
00462     } // if item is a transferable
00463     
00464     // put the mime mapping data for this item in a special flavor. Unlike the other data,
00465     // we have to put the data in now (rather than defer it) or the mappings will go out 
00466     // of scope by the time they are asked for. Remember that the |mappingLen|
00467     // includes the null, and we need to maintain that for when we parse it.
00468     short mappingLen;
00469     char* mapping = theMapper.ExportMapping(&mappingLen);
00470     if ( mapping && mappingLen ) {
00471       ::AddDragItemFlavor ( mDragRef, itemIndex, nsMimeMapperMac::MappingFlavor(), 
00472                                mapping, mappingLen, flags );
00473            nsMemory::Free ( mapping );
00474     
00475       ::SetDragItemBounds(mDragRef, itemIndex, &dragRgnBounds);
00476          }
00477     
00478   } // foreach drag item 
00479 
00480 } // RegisterDragItemsAndFlavors
00481 
00482 
00483 //
00484 // GetData
00485 //
00486 // Pull data out of the  OS drag item at the requested index and stash it into the 
00487 // given transferable. Only put in the data with the highest fidelity asked for and
00488 // stop as soon as we find a match.
00489 //
00490 NS_IMETHODIMP
00491 nsDragService::GetData ( nsITransferable * aTransferable, PRUint32 aItemIndex )
00492 {
00493   nsresult errCode = NS_ERROR_FAILURE;
00494 
00495   // make sure we have a good transferable
00496   if ( !aTransferable )
00497     return NS_ERROR_INVALID_ARG;
00498 
00499   // get flavor list that includes all acceptable flavors (including ones obtained through
00500   // conversion). Flavors are nsISupportsCStrings so that they can be seen from JS.
00501   nsCOMPtr<nsISupportsArray> flavorList;
00502   errCode = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
00503   if ( NS_FAILED(errCode) )
00504     return errCode;
00505 
00506   // get the data for the requested drag item. Remember that GetDragItemReferenceNumber()
00507   // is one-based NOT zero-based like |aItemIndex| is.   
00508   ItemReference itemRef;
00509   ::GetDragItemReferenceNumber ( mDragRef, aItemIndex + 1, &itemRef );
00510  
00511   // create a mime mapper to help us out based on data in a special flavor for this item
00512   char* mappings = LookupMimeMappingsForItem(mDragRef, itemRef);
00513   nsMimeMapperMac theMapper ( mappings );
00514   nsMemory::Free ( mappings );
00515   
00516   // Now walk down the list of flavors. When we find one that is actually present,
00517   // copy out the data into the transferable in that format. SetTransferData()
00518   // implicitly handles conversions.
00519   PRUint32 cnt;
00520   flavorList->Count ( &cnt );
00521   for (PRUint32 i = 0; i < cnt; ++i) {
00522     nsCOMPtr<nsISupports> genericWrapper;
00523     flavorList->GetElementAt ( i, getter_AddRefs(genericWrapper) );
00524     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericWrapper) );
00525     if ( currentFlavor ) {
00526       // find MacOS flavor (but don't add it if it's not there)
00527       nsCAutoString flavorStr;
00528       currentFlavor->GetData(flavorStr);
00529       FlavorType macOSFlavor = theMapper.MapMimeTypeToMacOSType(flavorStr.get(), PR_FALSE);
00530 #if DEBUG_DD
00531       printf("looking for data in type %s, mac flavor %ld\n", flavorStr.get(), macOSFlavor);
00532 #endif
00533 
00534       // check if it is present in the current drag item.
00535       FlavorFlags unused;
00536       PRBool dataFound = PR_FALSE;
00537       void* dataBuff = nsnull;
00538       PRUint32 dataSize = 0;
00539       if ( macOSFlavor && ::GetFlavorFlags(mDragRef, itemRef, macOSFlavor, &unused) == noErr ) {      
00540         nsresult loadResult = ExtractDataFromOS(mDragRef, itemRef, macOSFlavor, &dataBuff, &dataSize);
00541            if ( NS_SUCCEEDED(loadResult) && dataBuff )
00542              dataFound = PR_TRUE;
00543       }
00544       else {
00545              // if we are looking for text/unicode and we fail to find it on the clipboard first,
00546         // try again with text/plain. If that is present, convert it to unicode.
00547         if ( strcmp(flavorStr.get(), kUnicodeMime) == 0 ) {
00548           if ( ::GetFlavorFlags(mDragRef, itemRef, 'TEXT', &unused) == noErr ) {     
00549             
00550             // if 'styl' is available, we can get a script of the first run
00551             // and use it for converting 'TEXT'
00552             nsresult loadResult = ExtractDataFromOS(mDragRef, itemRef, 'styl', &dataBuff, &dataSize);
00553             if (NS_SUCCEEDED(loadResult) && 
00554                 dataBuff &&
00555                 (dataSize >= (sizeof(ScrpSTElement) + 2))) {
00556               StScrpRec *scrpRecP = (StScrpRec *) dataBuff;
00557               ScrpSTElement *styl = scrpRecP->scrpStyleTab;
00558               ScriptCode script = styl ? ::FontToScript(styl->scrpFont) : smCurrentScript;
00559               
00560               // free 'styl' and get 'TEXT'
00561               nsMemory::Free(dataBuff);
00562               loadResult = ExtractDataFromOS(mDragRef, itemRef, 'TEXT', &dataBuff, &dataSize);
00563               if ( NS_SUCCEEDED(loadResult) && dataBuff ) {
00564                 PRUnichar* convertedText = nsnull;
00565                 PRInt32 convertedTextLen = 0;
00566                 errCode = nsMacNativeUnicodeConverter::ConvertScripttoUnicode(script, 
00567                                                                               (const char *) dataBuff,
00568                                                                               dataSize,
00569                                                                               &convertedText,
00570                                                                               &convertedTextLen);
00571                 if (NS_SUCCEEDED(errCode) && convertedText) {
00572                   nsMemory::Free(dataBuff);
00573                   dataBuff = convertedText;
00574                   dataSize = convertedTextLen * sizeof(PRUnichar);
00575                   dataFound = PR_TRUE;
00576                 }
00577               }
00578             }          
00579           
00580             if (!dataFound) {
00581               loadResult = ExtractDataFromOS(mDragRef, itemRef, 'TEXT', &dataBuff, &dataSize);
00582               if ( NS_SUCCEEDED(loadResult) && dataBuff ) {
00583                 const char* castedText = NS_REINTERPRET_CAST(char*, dataBuff);          
00584                 PRUnichar* convertedText = nsnull;
00585                 PRInt32 convertedTextLen = 0;
00586                 nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, dataSize, 
00587                                                                           &convertedText, &convertedTextLen );
00588                 if ( convertedText ) {
00589                   // out with the old, in with the new 
00590                   nsMemory::Free(dataBuff);
00591                   dataBuff = convertedText;
00592                   dataSize = convertedTextLen * 2;
00593                   dataFound = PR_TRUE;
00594                 }
00595               } // if plain text data on clipboard
00596             }
00597           } // if plain text flavor present
00598         } // if looking for text/unicode   
00599       } // else we try one last ditch effort to find our data
00600 
00601          if ( dataFound ) {
00602       nsCOMPtr<nsISupports> genericDataWrapper;
00603 
00604            if ( strcmp(flavorStr.get(), kFileMime) == 0 ) {
00605              // we have a HFSFlavor struct in |dataBuff|. Create an nsLocalFileMac object.
00606              HFSFlavor* fileData = NS_REINTERPRET_CAST(HFSFlavor*, dataBuff);
00607              NS_ASSERTION ( sizeof(HFSFlavor) == dataSize, "Ooops, we really don't have a HFSFlavor" );
00608              nsCOMPtr<nsILocalFileMac> file;
00609              if ( NS_SUCCEEDED(NS_NewLocalFileWithFSSpec(&fileData->fileSpec, PR_TRUE, getter_AddRefs(file))) )
00610                genericDataWrapper = do_QueryInterface(file);
00611            }
00612       else if ((strcmp(flavorStr.get(), kURLDataMime) == 0) || (strcmp(flavorStr.get(), kURLDescriptionMime) == 0)) {
00613         // need to convert platform data to unicode
00614         const char* castedText        = NS_REINTERPRET_CAST(char*, dataBuff);          
00615         PRUnichar*  convertedText     = nsnull;
00616         PRInt32     convertedTextLen  = 0;
00617         nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode(castedText, dataSize, 
00618                                                                   &convertedText, &convertedTextLen);
00619         if (convertedText)
00620         {
00621           nsMemory::Free(dataBuff);
00622           dataBuff = convertedText;
00623           dataSize = convertedTextLen * 2;
00624           nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr.get(), (void *)dataBuff, dataSize, getter_AddRefs(genericDataWrapper));
00625         }
00626       }
00627            else {
00628           // we probably have some form of text. The DOM only wants LF, so convert k
00629           // from MacOS line endings to DOM line endings.
00630           nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr.get(), &dataBuff, NS_REINTERPRET_CAST(int*, &dataSize));
00631 
00632           unsigned char *dataPtr = (unsigned char *) dataBuff;
00633           // skip BOM (Byte Order Mark to distinguish little or big endian) in 'utxt'
00634           // 10.2 puts BOM for 'utxt', we need to remove it here
00635           // for little endian case, we also need to convert the data to big endian
00636           // but we do not do that currently (need this in case 'utxt' is really in little endian)
00637           if ( (macOSFlavor == 'utxt') &&
00638                (dataSize > 2) &&
00639                ((dataPtr[0] == 0xFE && dataPtr[1] == 0xFF) ||
00640                (dataPtr[0] == 0xFF && dataPtr[1] == 0xFE)) ) {
00641             dataSize -= sizeof(PRUnichar);
00642             dataPtr += sizeof(PRUnichar);
00643           }
00644           nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr.get(), (void *) dataPtr, dataSize, getter_AddRefs(genericDataWrapper));
00645         }
00646         
00647         // put it into the transferable.
00648         errCode = aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper, dataSize);
00649 #ifdef NS_DEBUG
00650          if ( errCode != NS_OK ) printf("nsDragService:: Error setting data into transferable\n");
00651 #endif
00652         nsMemory::Free ( dataBuff );
00653         errCode = NS_OK;
00654 
00655         // we found one, get out of this loop!
00656         break;
00657       } 
00658     }
00659   } // foreach flavor
00660   
00661   return errCode;
00662 }
00663 
00664 
00665 //
00666 // IsDataFlavorSupported
00667 //
00668 // Check the OS to see if the given drag flavor is in the list. Oddly returns
00669 // NS_OK for success and NS_ERROR_FAILURE if flavor is not present.
00670 //
00671 // Handle the case where we ask for unicode and it's not there, but plain text is. We 
00672 // say "yes" in that case, knowing that we will have to perform a conversion when we actually
00673 // pull the data out of the drag.
00674 //
00675 // ••• this is obviously useless with more than one drag item. Need to specify 
00676 // ••• and index to this API
00677 //
00678 NS_IMETHODIMP
00679 nsDragService::IsDataFlavorSupported(const char *aDataFlavor, PRBool *_retval)
00680 {
00681   if ( !_retval )
00682     return NS_ERROR_INVALID_ARG;
00683 
00684 #ifdef NS_DEBUG
00685       if ( strcmp(aDataFlavor, kTextMime) == 0 )
00686         NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
00687 #endif
00688 
00689   *_retval = PR_FALSE;
00690 
00691   // search through all drag items looking for something with this flavor. Recall
00692   // that drag item indices are 1-based.
00693   unsigned short numDragItems = 0;
00694   ::CountDragItems ( mDragRef, &numDragItems );
00695   for ( int i = 1; i <= numDragItems; ++i ) {
00696     ItemReference currItem;
00697     OSErr res = ::GetDragItemReferenceNumber ( mDragRef, i, &currItem );
00698     if ( res != noErr )
00699       return NS_ERROR_FAILURE;
00700 
00701     // convert to 4 character MacOS type for this item
00702     char* mappings = LookupMimeMappingsForItem(mDragRef, currItem) ;
00703     nsMimeMapperMac theMapper ( mappings );
00704     FlavorType macFlavor = theMapper.MapMimeTypeToMacOSType(aDataFlavor, PR_FALSE);
00705     nsMemory::Free(mappings);
00706 
00707     FlavorFlags ignored;
00708     if ( ::GetFlavorFlags(mDragRef, currItem, macFlavor, &ignored) == noErr )
00709       *_retval = PR_TRUE;
00710     else {
00711       // if the client asked for unicode and it wasn't present, check if we have TEXT.
00712       // We'll handle the actual data substitution in GetDataForFlavor().
00713       if ( strcmp(aDataFlavor, kUnicodeMime) == 0 ) {
00714         if ( ::GetFlavorFlags(mDragRef, currItem, 'TEXT', &ignored) == noErr )
00715           *_retval = PR_TRUE;
00716       }          
00717     }
00718   } // for each item in drag
00719 
00720   return NS_OK;
00721 
00722 } // IsDataFlavorSupported
00723 
00724 
00725 //
00726 // GetNumDropItems
00727 //
00728 // Returns the number of drop items present in the current drag.
00729 //
00730 NS_IMETHODIMP
00731 nsDragService::GetNumDropItems ( PRUint32 * aNumItems )
00732 {
00733   // we have to put it into a short first because that's what the MacOS API's expect.
00734   // After it's in a short, getting it into a long is no problem. Oh well.
00735   unsigned short numDragItems = 0;
00736   OSErr result = ::CountDragItems ( mDragRef, &numDragItems );
00737   *aNumItems = numDragItems;
00738   
00739   return ( result == noErr ? NS_OK : NS_ERROR_FAILURE );
00740 
00741 } // GetNumDropItems
00742 
00743 
00744 //
00745 // SetDragReference
00746 //
00747 // An API to allow the drag manager callback functions to tell the session about the
00748 // current dragRef w/out resorting to knowing the internals of the implementation
00749 //
00750 NS_IMETHODIMP
00751 nsDragService::SetDragReference ( DragReference aDragRef )
00752 {
00753   mDragRef = aDragRef;
00754   return NS_OK;
00755   
00756 } // SetDragReference
00757 
00758 
00759 //
00760 // DragSendDataProc
00761 //
00762 // Called when a drop occurs and the drop site asks for the data.
00763 //
00764 // This will only get called when Mozilla is the originator of the drag, so we can
00765 // make certain assumptions. One is that we've cached the list of transferables in the
00766 // drag session. The other is that the item ref is the index into this list so we
00767 // can easily pull out the item asked for.
00768 //
00769 // We cannot rely on |mDragRef| at this point so we have to use what was passed in
00770 // and pass that along.
00771 // 
00772 pascal OSErr
00773 nsDragService::DragSendDataProc(FlavorType inFlavor, void* inRefCon, ItemReference inItemRef,
00774                                                                DragReference inDragRef)
00775 {
00776   OSErr retVal = noErr;
00777   nsDragService* dragService = NS_STATIC_CAST(nsDragService*, inRefCon);
00778   NS_ASSERTION ( dragService, "Refcon not set correctly for DragSendDataProc" );
00779   if (dragService) {
00780     void* data = nsnull;
00781     PRUint32 dataSize = 0;
00782     retVal = dragService->GetDataForFlavor(dragService->mDataItems, inDragRef, inItemRef, inFlavor, &data, &dataSize);
00783     if ( retVal == noErr ) {      
00784         if ((inFlavor & kPrivateFlavorMask) == kPrivateFlavorTag) {
00785           // Byte-swap private flavors if running translated
00786           nsCOMPtr<nsIMacUtils> macUtils =
00787            do_GetService("@mozilla.org/xpcom/mac-utils;1");
00788           PRBool isTranslated;
00789           if (macUtils &&
00790               NS_SUCCEEDED(macUtils->GetIsTranslated(&isTranslated)) &&
00791               isTranslated) {
00792             char* swappedData = (char*) nsMemory::Alloc(dataSize);
00793             if (!swappedData) {
00794               nsMemory::Free(data);
00795               return notEnoughMemoryErr;
00796             }
00797             else {
00798               swab(data, swappedData, dataSize);
00799               nsMemory::Free(data);
00800               data = swappedData;
00801             }
00802           }
00803         }
00804         // make the data accessable to the DragManager
00805         retVal = ::SetDragItemFlavorData ( inDragRef, inItemRef, inFlavor, data, dataSize, 0 );
00806         NS_ASSERTION ( retVal == noErr, "SDIFD failed in DragSendDataProc" );
00807     }
00808     if ( data )
00809       nsMemory::Free ( data );
00810   } // if valid refcon
00811   
00812   return retVal;
00813   
00814 } // DragSendDataProc
00815 
00816 
00817 //
00818 // GetDataForFlavor
00819 //
00820 // Given a MacOS flavor and an index for which drag item to lookup, get the information from the
00821 // drag item corresponding to this flavor.
00822 // 
00823 // This routine also handles the conversions between text/plain and text/unicode to take platform
00824 // charset encodings into account. If someone asks for text/plain, we convert the unicode (we assume
00825 // it is present because that's how we knew to advertise text/plain in the first place) and give it
00826 // to them. If someone asks for text/unicode, and it isn't there, we need to convert text/plain and
00827 // hand them back the unicode. Again, we can assume that text/plain is there because otherwise we
00828 // wouldn't have been allowed to look for unicode.
00829 //
00830 OSErr
00831 nsDragService::GetDataForFlavor(nsISupportsArray* inDragItems, DragReference inDragRef, unsigned int inItemIndex, 
00832                                       FlavorType inFlavor, void** outData, unsigned int* outDataSize)
00833 {
00834   if ( !inDragItems || !inDragRef )
00835     return paramErr;
00836     
00837   *outData = nsnull;
00838   *outDataSize = 0;
00839 
00840   OSErr retVal = noErr;
00841   nsresult rv;
00842   
00843   // (assumes that the items were placed into the transferable as nsITranferable*'s, not nsISupports*'s.)
00844   nsCOMPtr<nsISupports> genericItem;
00845   inDragItems->GetElementAt(inItemIndex, getter_AddRefs(genericItem));
00846   nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
00847   if (!item) return cantGetFlavorErr;
00848     
00849     // create a mime mapper to help us out based on data in a special flavor for this item.
00850   char* mappings = LookupMimeMappingsForItem(inDragRef, inItemIndex);
00851   nsMimeMapperMac theMapper(mappings);
00852 
00853   nsCAutoString mimeFlavor;
00854   theMapper.MapMacOSTypeToMimeType(inFlavor, mimeFlavor);
00855   nsMemory::Free(mappings);
00856   
00857   // if someone was asking for text/plain, lookup unicode instead so we can convert it.
00858   PRBool needToDoConversionToPlainText = PR_FALSE;
00859   const char* actualFlavor = mimeFlavor.get();
00860   if ( strcmp(mimeFlavor.get(), kTextMime) == 0 || inFlavor == 'styl' )
00861   {
00862     actualFlavor = kUnicodeMime;
00863     needToDoConversionToPlainText = PR_TRUE;
00864   }
00865   else if (strcmp(actualFlavor, kURLDataMime) == 0)
00866   {
00867     needToDoConversionToPlainText = PR_TRUE;
00868   }
00869   else if (strcmp(actualFlavor, kURLDescriptionMime) == 0)
00870   {
00871     needToDoConversionToPlainText = PR_TRUE;
00872   }
00873   else if (strcmp(actualFlavor, kFilePromiseMime) == 0)
00874   {
00875     /*  Here's how file dragging works:
00876     
00877         Drag code adds a kFilePromiseMime flavor to the transferable, with an
00878         nsIFlavorDataProvider.
00879         It also adds a kFilePromiseURLMime flavor, with a string containing the source
00880         url.
00881     
00882         We map that to the OS drag flavor kDragFlavorTypePromiseHFS, which promises
00883         the flavor kDragPromisedFlavor. The OS asks for kDragPromisedFlavor, and we
00884         map that back to kFilePromiseMime.
00885         
00886         When asked for kFilePromiseMime data (here), we figure out the drop location from
00887         the OS, and set that as an nsILocalFile on the kFilePromiseDirectoryMime flavor. We then
00888         call GetTransferData() for the kFilePromiseMime flavor, which triggers
00889         the nsIFlavorDataProvider to do the save.
00890     */
00891 
00892     nsCOMPtr<nsILocalFile> dropDirectory;
00893     OSErr err = GetHFSPromiseDropDirectory(inDragRef, inItemIndex, inFlavor, getter_AddRefs(dropDirectory));
00894     if (err != noErr) return err;
00895     
00896     // now add a flavor to the transferable
00897     nsCOMPtr<nsISupports> localFileISupports = do_QueryInterface(dropDirectory);
00898     item->SetTransferData(kFilePromiseDirectoryMime, localFileISupports, sizeof(nsILocalFile*));
00899     
00900     // now request the kFilePromiseMime data, which will invoke the data provider
00901     // If successful, the returned data is a reference to the resulting file.
00902     nsCOMPtr<nsISupports> fileDataPrimitive;
00903     PRUint32 dataSize = 0;
00904     rv = item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
00905     if (NS_FAILED(rv)) return cantGetFlavorErr;
00906     
00907     // if we successfully saved, tell the Drag Manager
00908     nsCOMPtr<nsILocalFile> resultFile = do_QueryInterface(fileDataPrimitive);
00909     if (resultFile)
00910       return SetDropFileInDrag(inDragRef, inItemIndex, inFlavor, resultFile);
00911     
00912     // something went wrong.
00913     return cantGetFlavorErr;
00914   }
00915   else if (strcmp(actualFlavor, kNativeImageMime) == 0)
00916   {
00917     PRUint32 dataSize = 0;
00918     nsCOMPtr<nsISupports> transferSupports;
00919     rv = item->GetTransferData(actualFlavor, getter_AddRefs(transferSupports), &dataSize);
00920     if (NS_FAILED(rv)) return cantGetFlavorErr;
00921 
00922     nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(transferSupports));
00923     if (!ptrPrimitive) return cantGetFlavorErr; 
00924     
00925     nsCOMPtr<nsISupports> primitiveData;
00926     ptrPrimitive->GetData(getter_AddRefs(primitiveData));
00927     nsCOMPtr<nsIImageMac> image = do_QueryInterface(primitiveData);
00928     if (!image) return cantGetFlavorErr; 
00929       
00930     PicHandle picture = nsnull;
00931     image->ConvertToPICT(&picture);
00932     if (!picture) return cantGetFlavorErr; 
00933 
00934     PRInt32 pictSize = ::GetHandleSize((Handle)picture);
00935     char* pictData = nsnull;
00936     if (pictSize > 0)
00937       pictData = (char*)nsMemory::Alloc(pictSize);
00938     if (pictData) {
00939       ::BlockMoveData(*picture, pictData, pictSize);    // doesn't move memory
00940       *outData = (void*)pictData;
00941       *outDataSize = pictSize;
00942       retVal = noErr;
00943     }
00944     else
00945       retVal = cantGetFlavorErr;
00946 
00947     ::KillPicture(picture);
00948     return retVal;
00949   }
00950     
00951   nsCOMPtr<nsISupports> data;
00952   if (NS_SUCCEEDED(item->GetTransferData(actualFlavor, getter_AddRefs(data), outDataSize)))
00953   {
00954     nsPrimitiveHelpers::CreateDataFromPrimitive ( actualFlavor, data, outData, *outDataSize );
00955         
00956     // Convert unix to mac linebreaks, since mac linebreaks are required for clipboard compatibility.
00957     // I'm making the assumption here that the substitution will be entirely in-place, since both
00958     // types of line breaks are 1-byte.
00959     PRUnichar* castedUnicode = NS_REINTERPRET_CAST(PRUnichar*, *outData);
00960     nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(&castedUnicode,
00961                                                          nsLinebreakConverter::eLinebreakUnix,
00962                                                          nsLinebreakConverter::eLinebreakMac,
00963                                                          *outDataSize / sizeof(PRUnichar), nsnull);
00964 
00965     // if required, do the extra work to convert unicode to plain text and replace the output
00966     // values with the plain text.
00967     if ( needToDoConversionToPlainText ) {
00968       char* plainTextData = nsnull;
00969       PRInt32 plainTextLen = 0;
00970       nsresult rv =
00971       nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( castedUnicode, *outDataSize / 2, &plainTextData, &plainTextLen );
00972 
00973       ScriptCodeRun *scriptCodeRuns = nsnull;
00974       PRInt32 scriptRunOutLen;
00975 
00976       // if characters are not mapped from Unicode then try native API to convert to 
00977       // available script
00978       if (rv == NS_ERROR_UENC_NOMAPPING) {
00979         if (plainTextData) {
00980           nsMemory::Free(plainTextData);
00981           plainTextData = nsnull;
00982         }
00983         rv = nsMacNativeUnicodeConverter::ConvertUnicodetoScript(castedUnicode, 
00984                                                                  *outDataSize / sizeof(PRUnichar),
00985                                                                  &plainTextData, 
00986                                                                  &plainTextLen,
00987                                                                  &scriptCodeRuns,
00988                                                                  &scriptRunOutLen);
00989       }
00990       else if (NS_SUCCEEDED(rv)) {
00991         // create a single run with the default system script
00992         scriptCodeRuns = NS_REINTERPRET_CAST(ScriptCodeRun*,
00993                                              nsMemory::Alloc(sizeof(ScriptCodeRun)));
00994         if (scriptCodeRuns) {
00995           scriptCodeRuns[0].offset = 0;
00996           scriptCodeRuns[0].script = (ScriptCode) ::GetScriptManagerVariable(smSysScript);
00997           scriptRunOutLen = 1;
00998         }
00999       }
01000       
01001       if ( plainTextData && *outData ) {
01002         nsMemory::Free(*outData);
01003         *outData = nsnull;
01004         *outDataSize = 0;
01005         
01006         if (inFlavor != 'styl') {
01007           *outData = plainTextData;
01008           *outDataSize = plainTextLen;
01009         }
01010         else {
01011           nsMemory::Free(plainTextData);  // discard 'TEXT'
01012           
01013           char *stylData;
01014           PRInt32 stylLen;
01015           // create 'styl' from the script runs
01016           if (scriptCodeRuns) {
01017             rv = CreateStylFromScriptRuns(scriptCodeRuns,
01018                                                scriptRunOutLen,
01019                                                &stylData,
01020                                                &stylLen);
01021             if (NS_SUCCEEDED(rv)) {
01022               *outData = stylData;
01023               *outDataSize = stylLen;
01024             }
01025           }
01026         }
01027       }
01028       else
01029         retVal = cantGetFlavorErr;
01030       if (scriptCodeRuns)
01031         nsMemory::Free(scriptCodeRuns);
01032     }
01033   }
01034 
01035   return retVal;
01036 
01037 } // GetDataForFlavor
01038 
01039 
01040 //
01041 // LookupMimeMappingsForItem
01042 //
01043 // load up the flavor data for the mime mapper from the drag item and create a
01044 // mime mapper to help us go between mime types and macOS flavors. It may not be
01045 // there (such as when the drag originated from another app), but that's ok.
01046 //
01047 // Caller is responsible for deleting the memory.
01048 //
01049 char*
01050 nsDragService::LookupMimeMappingsForItem ( DragReference inDragRef, ItemReference inItemRef )
01051 {
01052   char* mapperData = nsnull;
01053   PRUint32 mapperSize = 0;
01054   ExtractDataFromOS(inDragRef, inItemRef, nsMimeMapperMac::MappingFlavor(),  (void**)&mapperData, &mapperSize);
01055 
01056   return mapperData;
01057   
01058 #if 0 
01059   OSErr err = ::GetFlavorDataSize ( inDragRef, itemRef, nsMimeMapperMac::MappingFlavor(), &mapperSize );
01060   if ( !err && mapperSize > 0 ) {
01061     mapperData = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(mapperSize + 1));
01062     if ( !mapperData )
01063       return nsnull;
01064                  
01065     err = ::GetFlavorData ( inDragRef, itemRef, nsMimeMapperMac::MappingFlavor(), mapperData, &mapperSize, 0 );
01066     if ( err ) {
01067       #ifdef NS_DEBUG
01068         printf("nsDragService: Error getting data out of drag manager for mime mapper, #%ld\n", err);
01069       #endif
01070       return nsnull;
01071     }
01072     else
01073       mapperData[mapperSize] = '\0';    // null terminate the data
01074   }
01075 
01076   return mapperData; 
01077 #endif
01078 }
01079 
01080 
01081 //
01082 // ExtractDataFromOS
01083 //
01084 // Handles pulling the data from the DragManager. Will return NS_ERROR_FAILURE if it can't get
01085 // the data for whatever reason.
01086 //
01087 nsresult
01088 nsDragService::ExtractDataFromOS ( DragReference inDragRef, ItemReference inItemRef, ResType inFlavor, 
01089                                        void** outBuffer, PRUint32* outBuffSize )
01090 {
01091   if ( !outBuffer || !outBuffSize || !inFlavor )
01092     return NS_ERROR_FAILURE;
01093 
01094   nsresult retval = NS_OK;
01095   char* buff = nsnull;
01096   Size buffSize = 0;
01097   OSErr err = ::GetFlavorDataSize ( inDragRef, inItemRef, inFlavor, &buffSize );
01098   if ( !err && buffSize > 0 ) {
01099     buff = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(buffSize + 1));
01100     if ( buff ) {         
01101       err = ::GetFlavorData ( inDragRef, inItemRef, inFlavor, buff, &buffSize, 0 );
01102       if (err == noErr) {
01103         if ((inFlavor & kPrivateFlavorMask) == kPrivateFlavorTag) {
01104           // Byte-swap private flavors if running translated
01105           nsCOMPtr<nsIMacUtils> macUtils =
01106            do_GetService("@mozilla.org/xpcom/mac-utils;1");
01107           PRBool isTranslated;
01108           if (macUtils &&
01109               NS_SUCCEEDED(macUtils->GetIsTranslated(&isTranslated)) &&
01110               isTranslated) {
01111             char* swappedData = (char*) nsMemory::Alloc(buffSize);
01112             if (!swappedData)
01113               retval = NS_ERROR_OUT_OF_MEMORY;
01114             else {
01115               swab(buff, swappedData, buffSize);
01116               nsMemory::Free(buff);
01117               buff = swappedData;
01118             }
01119           }
01120         }
01121       }
01122       else {
01123         #ifdef NS_DEBUG
01124           printf("nsDragService: Error getting data out of drag manager, #%ld\n", err);
01125         #endif
01126         retval = NS_ERROR_FAILURE;
01127       }
01128     }
01129     else
01130       retval = NS_ERROR_FAILURE;
01131   }
01132 
01133   if ( NS_FAILED(retval) ) {
01134     if ( buff )
01135       nsMemory::Free(buff);
01136   }
01137   else {
01138     *outBuffer = buff;
01139     *outBuffSize = buffSize;
01140   }
01141   return retval;
01142 
01143 } // ExtractDataFromOS
01144 
01145 
01146 //
01147 // SetDragAction
01148 //
01149 // Override to set the cursor to the appropriate feedback when the
01150 // drag action changes based on modifier keys
01151 //
01152 NS_IMETHODIMP
01153 nsDragService::SetDragAction ( PRUint32 anAction ) 
01154 {
01155   const PRInt32 kCopyCursorID = 144;
01156   const PRInt32 kLinkCursorID = 145;
01157   
01158   // don't do the work if it's the same.
01159   if ( anAction == mDragAction )
01160     return NS_OK;
01161     
01162   CursHandle newCursor = nsnull;  
01163   if ( anAction == DRAGDROP_ACTION_COPY )
01164     newCursor = ::GetCursor ( kCopyCursorID );
01165   else if ( anAction == DRAGDROP_ACTION_LINK )
01166     newCursor = ::GetCursor ( kLinkCursorID );
01167   if ( newCursor ) {
01168     ::HLock((Handle)newCursor);
01169     ::SetCursor ( *newCursor );
01170     ::HUnlock((Handle)newCursor);
01171   }
01172   else
01173     ::InitCursor();
01174  
01175   return nsBaseDragService::SetDragAction(anAction);
01176 }
01177 
01178 #pragma mark -
01179 // Utility routines for dragging files to the Finder
01180 
01181 static OSErr GetDropDirectory(DragReference dragRef, FSSpecPtr fssOut)
01182 {
01183   OSErr err;
01184 
01185   AEDesc dropLocAlias = { typeNull, nil };
01186   err = ::GetDropLocation(dragRef, &dropLocAlias);
01187   if (err != noErr) return err;
01188   
01189   if (dropLocAlias.descriptorType != typeAlias)
01190     return paramErr;
01191 
01192   AEDesc dropLocFSS = { typeNull, nil };
01193   if ((err = ::AECoerceDesc(&dropLocAlias, typeFSS, &dropLocFSS)) == noErr)
01194   {
01195     err = ::AEGetDescData(&dropLocFSS, fssOut, sizeof(FSSpec));
01196     (void)::AEDisposeDesc(&dropLocFSS);
01197   }
01198 
01199   if (dropLocAlias.dataHandle)
01200     (void)::AEDisposeDesc(&dropLocAlias);
01201 
01202   return err;
01203 }
01204 
01205 OSErr nsDragService::GetHFSPromiseDropDirectory(DragReference inDragRef, unsigned int inItemIndex,
01206                                 FlavorType inFlavor, nsILocalFile** outDir)
01207 {
01208   // get the promise data
01209   PromiseHFSFlavor promiseData;
01210   Size dataSize = sizeof(promiseData);
01211   OSErr err = ::GetFlavorData(inDragRef, inItemIndex, kDragFlavorTypePromiseHFS, &promiseData, &dataSize, 0);
01212   if (err != noErr) return err;
01213   
01214   FSSpec dropLocation;
01215   err = GetDropDirectory(inDragRef, &dropLocation);
01216   if (err != noErr) return err;
01217   
01218   nsCOMPtr<nsILocalFileMac> dropFolderSpec;
01219   nsresult rv = NS_NewLocalFileWithFSSpec(&dropLocation, PR_FALSE, getter_AddRefs(dropFolderSpec));
01220   if (NS_FAILED(rv)) return fnfErr;
01221 
01222   CallQueryInterface(dropFolderSpec, outDir);
01223   return noErr;
01224 }
01225 
01226 OSErr
01227 nsDragService::SetDropFileInDrag(DragReference inDragRef, unsigned int inItemIndex,
01228                                   FlavorType inFlavor, nsILocalFile* inFile)
01229 {
01230   nsCOMPtr<nsILocalFileMac> targetFileMac = do_QueryInterface(inFile);
01231   if (!targetFileMac) return paramErr;
01232   
01233   FSSpec targetFileSpec;
01234   nsresult rv = targetFileMac->GetFSSpec(&targetFileSpec);
01235   if (NS_FAILED(rv)) return paramErr;
01236   
01237   return ::SetDragItemFlavorData(inDragRef, inItemIndex, inFlavor, &targetFileSpec, sizeof(FSSpec), 0);
01238 }
01239