wine/dlls/winemac.drv/cocoa_clipboard.m
Ken Thomases 4ae5b106f8 winemac: Enable pasteboard functions to operate on arbitrary pasteboards.
... not just the general pasteboard (although the general pasteboard is
still the default).
2013-03-14 12:02:36 +01:00

215 lines
7.4 KiB
Objective-C

/*
* MACDRV Cocoa clipboard code
*
* Copyright 2012, 2013 Ken Thomases for CodeWeavers Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "macdrv_cocoa.h"
#import "cocoa_app.h"
#import "cocoa_event.h"
static int owned_change_count = -1;
static NSArray* BitmapOutputTypes;
static NSDictionary* BitmapOutputTypeMap;
static dispatch_once_t BitmapOutputTypesInitOnce;
/***********************************************************************
* macdrv_is_pasteboard_owner
*/
int macdrv_is_pasteboard_owner(void)
{
__block int ret;
OnMainThread(^{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
ret = ([pb changeCount] == owned_change_count);
});
return ret;
}
/***********************************************************************
* macdrv_copy_pasteboard_types
*
* Returns an array of UTI strings for the types of data available on
* the pasteboard, or NULL on error. The caller is responsible for
* releasing the returned array with CFRelease().
*/
CFArrayRef macdrv_copy_pasteboard_types(CFTypeRef pasteboard)
{
NSPasteboard* pb = (NSPasteboard*)pasteboard;
__block CFArrayRef ret = NULL;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
dispatch_once(&BitmapOutputTypesInitOnce, ^{
NSArray* bitmapFileTypes = [NSArray arrayWithObjects:
[NSNumber numberWithUnsignedInteger:NSTIFFFileType],
[NSNumber numberWithUnsignedInteger:NSPNGFileType],
[NSNumber numberWithUnsignedInteger:NSBMPFileType],
[NSNumber numberWithUnsignedInteger:NSGIFFileType],
[NSNumber numberWithUnsignedInteger:NSJPEGFileType],
[NSNumber numberWithUnsignedInteger:NSJPEG2000FileType],
nil];
BitmapOutputTypes = [[NSArray alloc] initWithObjects:@"public.tiff", @"public.png",
@"com.microsoft.bmp", @"com.compuserve.gif", @"public.jpeg",
@"public.jpeg-2000", nil];
BitmapOutputTypeMap = [[NSDictionary alloc] initWithObjects:bitmapFileTypes
forKeys:BitmapOutputTypes];
});
OnMainThread(^{
@try
{
NSPasteboard* local_pb = pb;
NSArray* types;
if (!local_pb) local_pb = [NSPasteboard generalPasteboard];
types = [local_pb types];
// If there are any types understood by NSBitmapImageRep, then we
// can offer all of the types that it can output, too. For example,
// if TIFF is on the pasteboard, we can offer PNG, BMP, etc. to the
// Windows program. We'll convert on demand.
if ([types firstObjectCommonWithArray:[NSBitmapImageRep imageTypes]] ||
[types firstObjectCommonWithArray:[NSBitmapImageRep imagePasteboardTypes]])
{
NSMutableArray* newTypes = [BitmapOutputTypes mutableCopy];
[newTypes removeObjectsInArray:types];
types = [types arrayByAddingObjectsFromArray:newTypes];
[newTypes release];
}
ret = (CFArrayRef)[types copy];
}
@catch (id e)
{
ERR(@"Exception discarded while copying pasteboard types: %@\n", e);
}
});
[pool release];
return ret;
}
/***********************************************************************
* macdrv_copy_pasteboard_data
*
* Returns the pasteboard data for a specified type, or NULL on error or
* if there's no such type on the pasteboard. The caller is responsible
* for releasing the returned data object with CFRelease().
*/
CFDataRef macdrv_copy_pasteboard_data(CFTypeRef pasteboard, CFStringRef type)
{
NSPasteboard* pb = (NSPasteboard*)pasteboard;
__block NSData* ret = nil;
OnMainThread(^{
@try
{
NSPasteboard* local_pb = pb;
if (!local_pb) local_pb = [NSPasteboard generalPasteboard];
if ([local_pb availableTypeFromArray:[NSArray arrayWithObject:(NSString*)type]])
ret = [[local_pb dataForType:(NSString*)type] copy];
else
{
NSNumber* bitmapType = [BitmapOutputTypeMap objectForKey:(NSString*)type];
if (bitmapType)
{
NSArray* reps = [NSBitmapImageRep imageRepsWithPasteboard:local_pb];
ret = [NSBitmapImageRep representationOfImageRepsInArray:reps
usingType:[bitmapType unsignedIntegerValue]
properties:nil];
ret = [ret copy];
}
}
}
@catch (id e)
{
ERR(@"Exception discarded while copying pasteboard types: %@\n", e);
}
});
return (CFDataRef)ret;
}
/***********************************************************************
* macdrv_clear_pasteboard
*
* Takes ownership of the Mac pasteboard and clears it of all data types.
*/
void macdrv_clear_pasteboard(void)
{
OnMainThreadAsync(^{
@try
{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
owned_change_count = [pb declareTypes:[NSArray array] owner:nil];
}
@catch (id e)
{
ERR(@"Exception discarded while clearing pasteboard: %@\n", e);
}
});
}
/***********************************************************************
* macdrv_set_pasteboard_data
*
* Sets the pasteboard data for a specified type. Replaces any data of
* that type already on the pasteboard. If data is NULL, promises the
* type.
*
* Returns 0 on error, non-zero on success.
*/
int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data, macdrv_window w)
{
__block int ret = 0;
WineWindow* window = (WineWindow*)w;
OnMainThread(^{
@try
{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
NSInteger change_count = [pb addTypes:[NSArray arrayWithObject:(NSString*)type]
owner:window];
if (change_count)
{
owned_change_count = change_count;
if (data)
ret = [pb setData:(NSData*)data forType:(NSString*)type];
else
ret = 1;
}
}
@catch (id e)
{
ERR(@"Exception discarded while copying pasteboard types: %@\n", e);
}
});
return ret;
}