1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/dataobj.cpp
3 // Purpose: implementation of wxDataObject class
4 // Author: Stefan Csomor
8 // Copyright: (c) 1999 Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
17 #include "wx/dataobj.h"
22 #include "wx/dcmemory.h"
26 #include "wx/mstream.h"
27 #include "wx/metafile.h"
28 #include "wx/tokenzr.h"
29 #include "wx/filename.h"
31 #include "wx/osx/private.h"
33 #if wxOSX_USE_COCOA_OR_CARBON
34 #include <QuickTime/QuickTime.h>
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 wxDataFormat::wxDataFormat()
43 m_type
= wxDF_INVALID
;
47 wxDataFormat::wxDataFormat( wxDataFormatId vType
)
50 m_type
= wxDF_INVALID
;
54 wxDataFormat::wxDataFormat( const wxChar
*zId
)
57 m_type
= wxDF_INVALID
;
61 wxDataFormat::wxDataFormat( const wxString
& rId
)
64 m_type
= wxDF_INVALID
;
68 wxDataFormat::wxDataFormat(const wxDataFormat
& rFormat
)
70 if ( rFormat
.m_format
)
71 m_format
= (NativeFormat
) CFStringCreateCopy(NULL
, (CFStringRef
)rFormat
.m_format
);
74 m_type
= rFormat
.m_type
;
78 wxDataFormat::wxDataFormat( NativeFormat vFormat
)
81 m_type
= wxDF_INVALID
;
85 wxDataFormat::~wxDataFormat()
89 CFRelease( (CFStringRef
) m_format
);
94 // in order to be correct for 10.3 we restrict to the available types there
95 // http://developer.apple.com/qa/qa2005/qa1406.html
96 // TODO : Use UTCoreTypes.h constants once we support 10.4+ only
98 wxDataFormat
& wxDataFormat::operator=(const wxDataFormat
& rFormat
)
102 CFRelease( (CFStringRef
) m_format
);
105 if ( rFormat
.m_format
)
106 m_format
= (NativeFormat
) CFStringCreateCopy(NULL
, (CFStringRef
)rFormat
.m_format
);
107 m_type
= rFormat
.m_type
;
112 void wxDataFormat::SetType( wxDataFormatId dataType
)
117 CFRelease( (CFStringRef
) m_format
);
124 m_format
= (long) CFStringCreateCopy( NULL
, kUTTypePlainText
);
127 case wxDF_UNICODETEXT
:
128 m_format
= (long) CFStringCreateCopy( NULL
, kUTTypeUTF16PlainText
);
132 m_format
= (long) CFStringCreateCopy( NULL
, kUTTypeHTML
);
136 m_format
= (long) CFStringCreateCopy( NULL
, kUTTypeTIFF
);
139 m_format
= (long) CFStringCreateCopy( NULL
, kUTTypePDF
);
143 m_format
= (long) CFStringCreateCopy( NULL
, kUTTypeFileURL
);
147 wxFAIL_MSG( wxT("invalid data format") );
152 wxString
wxDataFormat::GetId() const
154 return wxCFStringRef(wxCFRetain((CFStringRef
)m_format
)).AsString();
157 void wxDataFormat::SetId( NativeFormat format
)
161 CFRelease( (CFStringRef
) m_format
);
164 m_format
= (NativeFormat
) CFStringCreateCopy(NULL
, (CFStringRef
)format
);
165 if ( UTTypeConformsTo( (CFStringRef
)format
, kUTTypeHTML
) )
169 if ( UTTypeConformsTo( (CFStringRef
)format
, kUTTypeUTF16PlainText
) )
171 m_type
= wxDF_UNICODETEXT
;
173 else if ( UTTypeConformsTo( (CFStringRef
)format
,kUTTypeUTF16ExternalPlainText
) )
175 m_type
= wxDF_UNICODETEXT
;
177 else if ( UTTypeConformsTo( (CFStringRef
)format
,kUTTypeUTF8PlainText
) )
179 m_type
= wxDF_UNICODETEXT
;
181 else if ( UTTypeConformsTo( (CFStringRef
)format
, kUTTypePlainText
) )
185 else if ( UTTypeConformsTo( (CFStringRef
)format
, kUTTypeImage
) )
187 m_type
= wxDF_BITMAP
;
189 else if ( UTTypeConformsTo( (CFStringRef
)format
, kUTTypePDF
) )
191 m_type
= wxDF_METAFILE
;
193 else if ( UTTypeConformsTo( (CFStringRef
)format
, kUTTypeFileURL
) ||
194 UTTypeConformsTo( (CFStringRef
)format
, kPasteboardTypeFileURLPromise
))
196 m_type
= wxDF_FILENAME
;
200 m_type
= wxDF_PRIVATE
;
201 m_id
= wxCFStringRef( (CFStringRef
) CFRetain((CFStringRef
) format
)).AsString();
205 void wxDataFormat::SetId( const wxString
& zId
)
207 m_type
= wxDF_PRIVATE
;
211 CFRelease( (CFStringRef
) m_format
);
214 // since it is private, no need to conform to anything ...
215 m_format
= (long) wxCFRetain( (CFStringRef
) wxCFStringRef(m_id
) );
218 bool wxDataFormat::operator==(const wxDataFormat
& format
) const
220 if (IsStandard() || format
.IsStandard())
221 return (format
.m_type
== m_type
);
223 return ( UTTypeConformsTo( (CFStringRef
) m_format
, (CFStringRef
) format
.m_format
) );
226 //-------------------------------------------------------------------------
228 //-------------------------------------------------------------------------
230 wxDataObject::wxDataObject()
234 bool wxDataObject::IsSupportedFormat( const wxDataFormat
& rFormat
, Direction vDir
) const
236 size_t nFormatCount
= GetFormatCount( vDir
);
239 if (nFormatCount
== 1)
241 found
= (rFormat
== GetPreferredFormat());
245 wxDataFormat
*pFormats
= new wxDataFormat
[nFormatCount
];
246 GetAllFormats( pFormats
, vDir
);
248 for (size_t n
= 0; n
< nFormatCount
; n
++)
250 if (pFormats
[n
] == rFormat
)
263 void wxDataObject::AddToPasteboard( void * pb
, int itemID
)
265 PasteboardRef pasteboard
= (PasteboardRef
) pb
;
266 // get formats from wxDataObjects
267 wxDataFormat
*array
= new wxDataFormat
[ GetFormatCount() ];
268 GetAllFormats( array
);
270 for (size_t i
= 0; i
< GetFormatCount(); i
++)
272 wxDataFormat thisFormat
= array
[ i
];
274 // add four bytes at the end for data objs like text that
275 // have a datasize = strlen but still need a buffer for the
276 // string including trailing zero
278 size_t datasize
= GetDataSize( thisFormat
);
279 if ( datasize
== wxCONV_FAILED
&& thisFormat
.GetType() == wxDF_TEXT
)
281 // conversion to local text failed, so we must use unicode
282 // if wxDF_UNICODETEXT is already on the 'todo' list, skip this iteration
283 // otherwise force it
285 for (j
= 0; j
< GetFormatCount(); j
++)
287 if ( array
[j
].GetType() == wxDF_UNICODETEXT
)
290 if ( j
< GetFormatCount() )
293 thisFormat
.SetType(wxDF_UNICODETEXT
);
294 datasize
= GetDataSize( thisFormat
);
297 size_t sz
= datasize
+ 4;
298 void* buf
= malloc( sz
);
301 // empty the buffer because in some case GetDataHere does not fill buf
302 memset( buf
, 0, sz
);
303 if ( GetDataHere( thisFormat
, buf
) )
306 if ( thisFormat
.GetType() == wxDF_FILENAME
)
308 // the data is D-normalized UTF8 strings of filenames delimited with \n
309 char *fname
= strtok((char*) buf
,"\n");
310 while (fname
!= NULL
)
312 // translate the filepath into a fileurl and put that into the pasteobard
313 CFStringRef path
= CFStringCreateWithBytes(NULL
,(UInt8
*)fname
,strlen(fname
),kCFStringEncodingUTF8
,false);
314 CFURLRef url
= CFURLCreateWithFileSystemPath(NULL
, path
, kCFURLPOSIXPathStyle
, false);
316 CFDataRef data
= CFURLCreateData(NULL
,url
,kCFStringEncodingUTF8
,true);
318 PasteboardPutItemFlavor( pasteboard
, (PasteboardItemID
) counter
,
319 (CFStringRef
) thisFormat
.GetFormatId() , data
, kPasteboardFlavorNoFlags
);
322 fname
= strtok (NULL
,"\n");
328 CFDataRef data
= CFDataCreate( kCFAllocatorDefault
, (UInt8
*)buf
, datasize
);
329 if ( thisFormat
.GetType() == wxDF_TEXT
)
330 PasteboardPutItemFlavor( pasteboard
, (PasteboardItemID
) itemID
,
331 CFSTR("com.apple.traditional-mac-plain-text") , data
, kPasteboardFlavorNoFlags
);
333 PasteboardPutItemFlavor( pasteboard
, (PasteboardItemID
) itemID
,
334 (CFStringRef
) thisFormat
.GetFormatId() , data
, kPasteboardFlavorNoFlags
);
345 bool wxDataObject::IsFormatInPasteboard( void * pb
, const wxDataFormat
&dataFormat
)
347 PasteboardRef pasteboard
= (PasteboardRef
) pb
;
348 bool hasData
= false;
349 OSStatus err
= noErr
;
352 // we synchronize here once again, so we don't mind which flags get returned
353 PasteboardSynchronize( pasteboard
);
355 err
= PasteboardGetItemCount( pasteboard
, &itemCount
);
358 for( UInt32 itemIndex
= 1; itemIndex
<= itemCount
&& hasData
== false ; itemIndex
++ )
360 PasteboardItemID itemID
;
361 CFArrayRef flavorTypeArray
;
364 err
= PasteboardGetItemIdentifier( pasteboard
, itemIndex
, &itemID
);
368 err
= PasteboardCopyItemFlavors( pasteboard
, itemID
, &flavorTypeArray
);
372 flavorCount
= CFArrayGetCount( flavorTypeArray
);
374 for( CFIndex flavorIndex
= 0; flavorIndex
< flavorCount
&& hasData
== false ; flavorIndex
++ )
376 CFStringRef flavorType
;
378 flavorType
= (CFStringRef
)CFArrayGetValueAtIndex( flavorTypeArray
,
381 wxDataFormat
flavorFormat( (wxDataFormat::NativeFormat
) flavorType
);
382 if ( dataFormat
== flavorFormat
)
384 else if ( dataFormat
.GetType() == wxDF_UNICODETEXT
&& flavorFormat
.GetType() == wxDF_TEXT
)
387 CFRelease (flavorTypeArray
);
394 bool wxDataObject::GetFromPasteboard( void * pb
)
396 PasteboardRef pasteboard
= (PasteboardRef
) pb
;
397 size_t formatcount
= GetFormatCount() + 1;
398 wxDataFormat
*array
= new wxDataFormat
[ formatcount
];
399 array
[0] = GetPreferredFormat();
400 GetAllFormats( &array
[1] );
401 ItemCount itemCount
= 0;
402 wxString filenamesPassed
;
403 bool transferred
= false;
405 // we synchronize here once again, so we don't mind which flags get returned
406 PasteboardSynchronize( pasteboard
);
408 OSStatus err
= PasteboardGetItemCount( pasteboard
, &itemCount
);
411 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
413 // go through the data in our order of preference
414 wxDataFormat dataFormat
= array
[ i
];
416 for( UInt32 itemIndex
= 1; itemIndex
<= itemCount
&& transferred
== false ; itemIndex
++ )
418 PasteboardItemID itemID
= 0;
419 CFArrayRef flavorTypeArray
= NULL
;
420 CFIndex flavorCount
= 0;
422 err
= PasteboardGetItemIdentifier( pasteboard
, itemIndex
, &itemID
);
426 err
= PasteboardCopyItemFlavors( pasteboard
, itemID
, &flavorTypeArray
);
430 flavorCount
= CFArrayGetCount( flavorTypeArray
);
432 for( CFIndex flavorIndex
= 0; !transferred
&& flavorIndex
< flavorCount
; flavorIndex
++ )
434 CFStringRef flavorType
;
435 CFDataRef flavorData
;
436 CFIndex flavorDataSize
;
438 flavorType
= (CFStringRef
)CFArrayGetValueAtIndex( flavorTypeArray
,
441 wxDataFormat
flavorFormat( (wxDataFormat::NativeFormat
) flavorType
);
443 if ( dataFormat
== flavorFormat
)
445 if ( UTTypeConformsTo( (CFStringRef
)flavorType
, kPasteboardTypeFileURLPromise
) )
447 wxString tempdir
= wxFileName::GetTempDir() + wxFILE_SEP_PATH
+ "wxtemp.XXXXXX";
448 char* result
= mkdtemp((char*)tempdir
.fn_str().data());
453 wxCFRef
<CFURLRef
> dest(CFURLCreateFromFileSystemRepresentation(NULL
,(const UInt8
*)result
,strlen(result
),true));
454 PasteboardSetPasteLocation(pasteboard
, dest
);
456 else if ( flavorFormat
.GetType() != wxDF_PRIVATE
)
458 // indicate the expected format for the type, benefiting from native conversions eg utf8 -> utf16
459 flavorType
= (CFStringRef
) wxDataFormat( flavorFormat
.GetType()).GetFormatId();
462 err
= PasteboardCopyItemFlavorData( pasteboard
, itemID
, flavorType
, &flavorData
);
465 flavorDataSize
= CFDataGetLength( flavorData
);
466 if (dataFormat
.GetType() == wxDF_FILENAME
)
468 // revert the translation and decomposition to arrive at a proper utf8 string again
469 CFURLRef url
= CFURLCreateWithBytes( kCFAllocatorDefault
, CFDataGetBytePtr( flavorData
), flavorDataSize
, kCFStringEncodingUTF8
, NULL
);
470 CFStringRef cfString
= CFURLCopyFileSystemPath( url
, kCFURLPOSIXPathStyle
);
472 CFMutableStringRef cfMutableString
= CFStringCreateMutableCopy(NULL
, 0, cfString
);
473 CFRelease( cfString
);
474 CFStringNormalize(cfMutableString
,kCFStringNormalizationFormC
);
475 wxString path
= wxCFStringRef(cfMutableString
).AsString();
477 filenamesPassed
+= path
+ wxT("\n");
481 // because some data implementation expect trailing a trailing NUL, we add some headroom
482 void *buf
= malloc( flavorDataSize
+ 4 );
485 memset( buf
, 0, flavorDataSize
+ 4 );
486 memcpy( buf
, CFDataGetBytePtr( flavorData
), flavorDataSize
);
488 if (dataFormat
.GetType() == wxDF_TEXT
)
489 wxMacConvertNewlines10To13( (char*) buf
);
490 SetData( flavorFormat
, flavorDataSize
, buf
);
495 CFRelease (flavorData
);
498 else if ( dataFormat
.GetType() == wxDF_UNICODETEXT
&& flavorFormat
.GetType() == wxDF_TEXT
)
500 err
= PasteboardCopyItemFlavorData( pasteboard
, itemID
, flavorType
, &flavorData
);
503 flavorDataSize
= CFDataGetLength( flavorData
);
504 void *asciibuf
= malloc( flavorDataSize
+ 1 );
507 memset( asciibuf
, 0, flavorDataSize
+ 1 );
508 memcpy( asciibuf
, CFDataGetBytePtr( flavorData
), flavorDataSize
);
509 CFRelease (flavorData
);
511 SetData( wxDF_TEXT
, flavorDataSize
, asciibuf
);
516 CFRelease (flavorData
);
520 CFRelease( flavorTypeArray
);
522 if ( !filenamesPassed
.empty() )
524 wxCharBuffer buf
= filenamesPassed
.fn_str();
525 SetData( wxDF_FILENAME
, strlen( buf
), (const char*)buf
);
533 bool wxDataObject::HasDataInPasteboard( void * pb
)
535 PasteboardRef pasteboard
= (PasteboardRef
) pb
;
536 size_t formatcount
= GetFormatCount() + 1;
537 wxDataFormat
*array
= new wxDataFormat
[ formatcount
];
538 array
[0] = GetPreferredFormat();
539 GetAllFormats( &array
[1] );
540 ItemCount itemCount
= 0;
541 bool hasData
= false;
543 // we synchronize here once again, so we don't mind which flags get returned
544 PasteboardSynchronize( pasteboard
);
546 OSStatus err
= PasteboardGetItemCount( pasteboard
, &itemCount
);
549 for (size_t i
= 0; !hasData
&& i
< formatcount
; i
++)
551 // go through the data in our order of preference
552 wxDataFormat dataFormat
= array
[ i
];
554 for( UInt32 itemIndex
= 1; itemIndex
<= itemCount
&& hasData
== false ; itemIndex
++ )
556 PasteboardItemID itemID
= 0;
557 CFArrayRef flavorTypeArray
= NULL
;
558 CFIndex flavorCount
= 0;
560 err
= PasteboardGetItemIdentifier( pasteboard
, itemIndex
, &itemID
);
564 err
= PasteboardCopyItemFlavors( pasteboard
, itemID
, &flavorTypeArray
);
568 flavorCount
= CFArrayGetCount( flavorTypeArray
);
570 for( CFIndex flavorIndex
= 0; !hasData
&& flavorIndex
< flavorCount
; flavorIndex
++ )
572 CFStringRef flavorType
;
574 flavorType
= (CFStringRef
)CFArrayGetValueAtIndex( flavorTypeArray
,
577 wxDataFormat
flavorFormat( (wxDataFormat::NativeFormat
) flavorType
);
579 if ( dataFormat
== flavorFormat
||
580 (dataFormat
.GetType() == wxDF_UNICODETEXT
&& flavorFormat
.GetType() == wxDF_TEXT
) )
585 CFRelease( flavorTypeArray
);
592 // ----------------------------------------------------------------------------
594 // ----------------------------------------------------------------------------
597 void wxTextDataObject::GetAllFormats(wxDataFormat
*formats
,
598 wxDataObjectBase::Direction
WXUNUSED(dir
)) const
600 *formats
++ = wxDataFormat( wxDF_TEXT
);
601 *formats
= wxDataFormat( wxDF_UNICODETEXT
);
605 // ----------------------------------------------------------------------------
607 // ----------------------------------------------------------------------------
609 void wxFileDataObject::GetFileNames( wxCharBuffer
&buf
) const
613 for (size_t i
= 0; i
< m_filenames
.GetCount(); i
++)
615 filenames
+= m_filenames
[i
];
616 filenames
+= wxT('\n');
619 buf
= filenames
.fn_str();
622 bool wxFileDataObject::GetDataHere( void *pBuf
) const
631 buffLength
= strlen( buf
);
632 memcpy( pBuf
, (const char*)buf
, buffLength
+ 1 );
637 size_t wxFileDataObject::GetDataSize() const
643 buffLength
= strlen( buf
);
645 return buffLength
+ 1;
648 bool wxFileDataObject::SetData( size_t WXUNUSED(nSize
), const void *pBuf
)
653 filenames
= wxString( (const char*)pBuf
, *wxConvFileName
);
655 filenames
= wxString (wxConvLocal
.cWC2WX(wxConvFileName
->cMB2WC( (const char*)pBuf
)));
658 m_filenames
= wxStringTokenize( filenames
, wxT("\n"), wxTOKEN_STRTOK
);
663 void wxFileDataObject::AddFile( const wxString
& rFilename
)
665 m_filenames
.Add( rFilename
);
668 // ----------------------------------------------------------------------------
669 // wxBitmapDataObject
670 // ----------------------------------------------------------------------------
672 wxBitmapDataObject::wxBitmapDataObject()
677 wxBitmapDataObject::wxBitmapDataObject( const wxBitmap
& rBitmap
)
678 : wxBitmapDataObjectBase( rBitmap
)
684 SetBitmap( rBitmap
);
688 wxBitmapDataObject::~wxBitmapDataObject()
693 void wxBitmapDataObject::SetBitmap( const wxBitmap
& rBitmap
)
696 wxBitmapDataObjectBase::SetBitmap( rBitmap
);
699 CGImageRef cgImageRef
= (CGImageRef
) m_bitmap
.CreateCGImage();
701 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
702 CGImageDestinationRef destination
= CGImageDestinationCreateWithData( data
, kUTTypeTIFF
, 1 , NULL
);
705 CGImageDestinationAddImage( destination
, cgImageRef
, NULL
);
706 CGImageDestinationFinalize( destination
);
707 CFRelease( destination
);
709 m_pictHandle
= NewHandle(CFDataGetLength(data
));
712 memcpy( *(Handle
)m_pictHandle
, (const char *)CFDataGetBytePtr(data
), CFDataGetLength(data
) );
716 CGImageRelease(cgImageRef
);
720 void wxBitmapDataObject::Init()
723 m_pictCreated
= false;
726 void wxBitmapDataObject::Clear()
728 if (m_pictHandle
!= NULL
)
730 DisposeHandle( (Handle
) m_pictHandle
);
733 m_pictCreated
= false;
736 bool wxBitmapDataObject::GetDataHere( void *pBuf
) const
738 if (m_pictHandle
== NULL
)
740 wxFAIL_MSG( wxT("attempt to copy empty bitmap failed") );
747 memcpy( pBuf
, *(Handle
)m_pictHandle
, GetHandleSize( (Handle
)m_pictHandle
) );
752 size_t wxBitmapDataObject::GetDataSize() const
754 if (m_pictHandle
!= NULL
)
755 return GetHandleSize( (Handle
)m_pictHandle
);
760 Handle
MacCreateDataReferenceHandle(Handle theDataHandle
)
762 Handle dataRef
= NULL
;
765 // Create a data reference handle for our data.
766 err
= PtrToHand( &theDataHandle
, &dataRef
, sizeof(Handle
));
771 bool wxBitmapDataObject::SetData( size_t nSize
, const void *pBuf
)
775 if ((pBuf
== NULL
) || (nSize
== 0))
778 Handle picHandle
= NewHandle( nSize
);
779 memcpy( *picHandle
, pBuf
, nSize
);
780 m_pictHandle
= picHandle
;
781 CGImageRef cgImageRef
= 0;
783 CFDataRef data
= CFDataCreateWithBytesNoCopy( kCFAllocatorDefault
, (const UInt8
*) pBuf
, nSize
, kCFAllocatorNull
);
784 CGImageSourceRef source
= CGImageSourceCreateWithData( data
, NULL
);
787 cgImageRef
= CGImageSourceCreateImageAtIndex(source
, 0, NULL
);
794 m_bitmap
.Create( CGImageGetWidth(cgImageRef
) , CGImageGetHeight(cgImageRef
) );
795 CGRect r
= CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef
) , CGImageGetHeight(cgImageRef
) );
796 // since our context is upside down we dont use CGContextDrawImage
797 wxMacDrawCGImage( (CGContextRef
) m_bitmap
.GetHBITMAP() , &r
, cgImageRef
) ;
798 CGImageRelease(cgImageRef
);
802 return m_bitmap
.IsOk();