From 2b51d635a7c60d6f9a2817e55480a172088324b8 Mon Sep 17 00:00:00 2001 From: CharlesJS Date: Wed, 4 Jul 2012 22:59:06 -0500 Subject: [PATCH] International text clipboard patch for Basilisk II and SheepShaver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for international text to Basilisk II / SheepShaver's clipboard support. Text copied on the host side is converted from Unicode to the format that the classic Mac OS Script Manager expects, with localized font variants used if they are available on the emulated system (unfortunately, if a localized font is not available, the text will render incorrectly due to the nature of the Script Manager). When text is copied on the emulated system, the script of the current font is used to determine the encoding of the text, and it is converted to Unicode to be pasted on the host side. This patch supports copying and pasting text containing multiple scripts; for example, "EnglishČeský日本語", where ranges (0, 7) and (8, 3) are Roman, (7, 1) and (11, 1) are Central European, and (12, 3) is Japanese, can be freely copied and pasted back and forth between the host and emulated platforms, provided that the emulated platform has localized Central European and Japanese fonts installed. In order to get this to work, I rewrote pretty much all of clip_macosx64.mm. The code now completely uses the Cocoa framework rather than CoreFoundation and Pasteboard Manager. Because this API now uses the Mac OS X 10.6+ version of the pasteboard API, the minimum version of OS X supported by clip_macosx64.mm is now 10.6. I think this shouldn't be a problem, since the 32-bit version still exists, but if this version needs to support older releases, let me know and I can add version-check code to do so. One of the benefits of using the modern API, however, is that our rich-text format is no longer hard-coded to the RTF format, which means we have automatic support for any other format used by the OS X pasteboard system, which as of Lion seems to include RTF, UTF-16 text, UTF-8 text, 'ut16'/'ustl', and others, and which may be supplemented by other formats in future releases of OS X. I hope you find this patch useful. Charles --- BasiliskII/src/MacOSX/autorelease.h | 34 + BasiliskII/src/MacOSX/clip_macosx64.mm | 1009 +++++++++++++++--------- SheepShaver/src/MacOSX/autorelease.h | 1 + 3 files changed, 662 insertions(+), 382 deletions(-) create mode 100644 BasiliskII/src/MacOSX/autorelease.h create mode 120000 SheepShaver/src/MacOSX/autorelease.h diff --git a/BasiliskII/src/MacOSX/autorelease.h b/BasiliskII/src/MacOSX/autorelease.h new file mode 100644 index 00000000..49c34e62 --- /dev/null +++ b/BasiliskII/src/MacOSX/autorelease.h @@ -0,0 +1,34 @@ +/* + * autorelease.h - a macro wrapping autorelease pools for use with Objective-C++ source files. + * + * Expands to @autoreleasepool on clang, uses a little hack to emulate @autoreleasepool on gcc. + * + * (C) 2012 Charles Srstka + * + * Feel free to put this under whatever license you wish. + */ + +#import + +#ifndef __Autorelease_H__ +#define __Autorelease_H__ + +// just a little forward compatibility in case we ever support LLVM/clang +#if __clang__ +#define AUTORELEASE_POOL @autoreleasepool +#else +class Autorelease_Pool_Wrapper { +public: + Autorelease_Pool_Wrapper() { m_pool = [[NSAutoreleasePool alloc] init]; } + ~Autorelease_Pool_Wrapper() { [m_pool drain]; } + operator bool() const { return true; } +private: + NSAutoreleasePool *m_pool; +}; + +#define POOL_NAME(x, y) x##_##y +#define POOL_NAME2(x, y) POOL_NAME(x, y) +#define AUTORELEASE_POOL if(Autorelease_Pool_Wrapper POOL_NAME2(pool, __LINE__) = Autorelease_Pool_Wrapper()) +#endif // !__clang__ + +#endif // __Autorelease_H__ diff --git a/BasiliskII/src/MacOSX/clip_macosx64.mm b/BasiliskII/src/MacOSX/clip_macosx64.mm index 84ba0673..6ea5511f 100644 --- a/BasiliskII/src/MacOSX/clip_macosx64.mm +++ b/BasiliskII/src/MacOSX/clip_macosx64.mm @@ -29,6 +29,7 @@ #include "main.h" #include "cpu_emulation.h" #include "emul_op.h" +#include "autorelease.h" #define DEBUG 0 #include "debug.h" @@ -41,16 +42,22 @@ #define TYPE_TEXT FOURCC('T','E','X','T') #define TYPE_STYL FOURCC('s','t','y','l') -static PasteboardRef g_pbref; +static NSPasteboard *g_pboard; // Flag for PutScrap(): the data was put by GetScrap(), don't bounce it back to the MacOS X side static bool we_put_this_data = false; static bool should_clear = false; -static CFStringRef const UTF16_TEXT_FLAVOR_NAME = CFSTR("public.utf16-plain-text"); -static CFStringRef const TEXT_FLAVOR_NAME = CFSTR("com.apple.traditional-mac-plain-text"); -static CFStringRef const STYL_FLAVOR_NAME = CFSTR("net.cebix.basilisk.styl-data"); +static NSMutableDictionary *macScrap; + +// flavor UTIs + +static NSString * const UTF16_TEXT_FLAVOR_NAME = @"public.utf16-plain-text"; +static NSString * const TEXT_FLAVOR_NAME = @"com.apple.traditional-mac-plain-text"; +static NSString * const STYL_FLAVOR_NAME = @"net.cebix.basilisk.styl-data"; + +// font face types enum { FONT_FACE_PLAIN = 0, @@ -63,20 +70,26 @@ enum { FONT_FACE_EXTENDED = 64 }; -static CFStringRef GetUTIFromFlavor(uint32 type) +// Script Manager constants + +#define smRoman 0 +#define smMacSysScript 18 +#define smMacRegionCode 40 + +static NSString *GetUTIFromFlavor(uint32_t type) { switch (type) { - case TYPE_PICT: return kUTTypePICT; + case TYPE_PICT: return (NSString *)kUTTypePICT; case TYPE_TEXT: return TEXT_FLAVOR_NAME; - //case TYPE_TEXT: return UTF16_TEXT_FLAVOR_NAME; + //case TYPE_TEXT: return UTF16_TEXT_FLAVOR_NAME; case TYPE_STYL: return STYL_FLAVOR_NAME; - case FOURCC('m','o','o','v'): return kUTTypeQuickTimeMovie; - case FOURCC('s','n','d',' '): return kUTTypeAudio; - //case FOURCC('u','t','x','t'): return UTF16_TEXT_FLAVOR_NAME; + case FOURCC('m','o','o','v'): return (NSString *)kUTTypeQuickTimeMovie; + case FOURCC('s','n','d',' '): return (NSString *)kUTTypeAudio; + //case FOURCC('u','t','x','t'): return UTF16_TEXT_FLAVOR_NAME; case FOURCC('u','t','1','6'): return UTF16_TEXT_FLAVOR_NAME; - //case FOURCC('u','s','t','l'): return CFSTR("????"); - case FOURCC('i','c','n','s'): return kUTTypeAppleICNS; - default: return NULL; + //case FOURCC('u','s','t','l'): return @"????"; + case FOURCC('i','c','n','s'): return (NSString *)kUTTypeAppleICNS; + default: return nil; } } @@ -84,16 +97,13 @@ static CFStringRef GetUTIFromFlavor(uint32 type) * Get current system script encoding on Mac */ -#define smMacSysScript 18 -#define smMacRegionCode 40 - -static int GetMacScriptManagerVariable(uint16 id) +static int GetMacScriptManagerVariable(uint16_t varID) { int ret = -1; M68kRegisters r; - static uint8 proc[] = { + static uint8_t proc[] = { 0x59, 0x4f, // subq.w #4,sp - 0x3f, 0x3c, 0x00, 0x00, // move.w #id,-(sp) + 0x3f, 0x3c, 0x00, 0x00, // move.w #varID,-(sp) 0x2f, 0x3c, 0x84, 0x02, 0x00, 0x08, // move.l #-2080243704,-(sp) 0xa8, 0xb5, // ScriptUtil() 0x20, 0x1f, // move.l (a7)+,d0 @@ -101,10 +111,36 @@ static int GetMacScriptManagerVariable(uint16 id) }; r.d[0] = sizeof(proc); Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 proc_area = r.a[0]; + uint32_t proc_area = r.a[0]; if (proc_area) { Host2Mac_memcpy(proc_area, proc, sizeof(proc)); - WriteMacInt16(proc_area + 4, id); + WriteMacInt16(proc_area + 4, varID); + Execute68k(proc_area, &r); + ret = r.d[0]; + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr + } + return ret; +} + +static ScriptCode ScriptNumberForFontID(int16_t fontID) +{ + ScriptCode ret = -1; + M68kRegisters r; + static uint8_t proc[] = { + 0x55, 0x4f, // subq.w #2,sp + 0x3f, 0x3c, 0x00, 0x00, // move.w #fontID,-(sp) + 0x2f, 0x3c, 0x82, 0x02, 0x00, 0x06, // move.l #-2113798138,-(sp) + 0xa8, 0xb5, // ScriptUtil() + 0x30, 0x1f, // move.w (sp)+,d0 + M68K_RTS >> 8, M68K_RTS & 0xff + }; + r.d[0] = sizeof(proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + uint32_t proc_area = r.a[0]; + if (proc_area) { + Host2Mac_memcpy(proc_area, proc, sizeof(proc)); + WriteMacInt16(proc_area + 4, fontID); Execute68k(proc_area, &r); ret = r.d[0]; r.a[0] = proc_area; @@ -114,63 +150,264 @@ static int GetMacScriptManagerVariable(uint16 id) } /* - * Convert utf-16 from/to system script encoding on Mac + * Get Mac's default text encoding */ -static CFDataRef ConvertMacTextEncoding(CFDataRef pbData, int from_host) +static TextEncoding MacDefaultTextEncoding() { - static TextEncoding g_textEncodingHint = kTextEncodingUnknown; - static UnicodeMapping uMapping; - static UnicodeToTextInfo utInfo; - static TextToUnicodeInfo tuInfo; - static int ready; + int script = GetMacScriptManagerVariable(smMacSysScript); + int region = GetMacScriptManagerVariable(smMacRegionCode); + TextEncoding encoding; - // should we check this only once ? - if (g_textEncodingHint == kTextEncodingUnknown) { - int script = GetMacScriptManagerVariable(smMacSysScript); - int region = GetMacScriptManagerVariable(smMacRegionCode); - if (UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, NULL, &g_textEncodingHint)) - g_textEncodingHint = kTextEncodingMacRoman; - uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, kTextEncodingDefaultVariant, kUnicode16BitFormat); - uMapping.otherEncoding = GetTextEncodingBase(g_textEncodingHint); - uMapping.mappingVersion = kUnicodeUseLatestMapping; - ready = !CreateUnicodeToTextInfo(&uMapping, &utInfo) && !CreateTextToUnicodeInfo(&uMapping, &tuInfo); - } - if (!ready) - return pbData; + if (UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, NULL, &encoding)) + encoding = kTextEncodingMacRoman; - ByteCount byteCount = CFDataGetLength(pbData); - ByteCount outBytesLength = byteCount * 2; - LogicalAddress outBytesPtr = malloc(byteCount * 2); - if (!outBytesPtr) - return pbData; + return encoding; +} - ByteCount outBytesConverted; - OSStatus err; - if (from_host) { - err = ConvertFromUnicodeToText(utInfo, byteCount, (UniChar *)CFDataGetBytePtr(pbData), - kUnicodeLooseMappingsMask, - 0, NULL, 0, NULL, - outBytesLength, - &outBytesConverted, &outBytesLength, outBytesPtr); - } else { - err = ConvertFromTextToUnicode(tuInfo, byteCount, CFDataGetBytePtr(pbData), - kUnicodeLooseMappingsMask, - 0, NULL, 0, NULL, - outBytesLength, - &outBytesConverted, &outBytesLength, (UniChar *)outBytesPtr); +static NSData *ConvertToMacTextEncoding(NSAttributedString *aStr, NSArray **styleAndScriptRuns) +{ + NSUInteger length = [aStr length]; + + NSMutableArray *styleRuns = [NSMutableArray array]; + + for (NSUInteger index = 0; index < length;) { + NSRange attrRange; + NSDictionary *attrs = [aStr attributesAtIndex:index effectiveRange:&attrRange]; + + [styleRuns addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInteger:index], @"offset", + attrs, @"attributes", nil]]; + + index = NSMaxRange(attrRange); } - if (err == noErr && outBytesConverted == byteCount) { - CFDataRef pbDataConverted = CFDataCreate(kCFAllocatorDefault, (UInt8*)outBytesPtr, outBytesLength); - if (pbDataConverted) { - CFRelease(pbData); - pbData = pbDataConverted; + UnicodeToTextRunInfo info; + + OSStatus err = CreateUnicodeToTextRunInfoByScriptCode(0, NULL, &info); + + if (err != noErr) { + if (styleAndScriptRuns) + *styleAndScriptRuns = styleRuns; + + return [[aStr string] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(MacDefaultTextEncoding())]; + } + + unichar chars[length]; + + [[aStr string] getCharacters:chars range:NSMakeRange(0, length)]; + + NSUInteger unicodeLength = length * sizeof(unichar); + NSUInteger bufLen = unicodeLength * 2; + uint8_t buf[bufLen]; + ByteCount bytesRead; + + ItemCount scriptRunCount = 1601; // max number of allowed style changes + ScriptCodeRun scriptRuns[scriptRunCount]; + + ItemCount inOffsetCount = [styleRuns count]; + ByteOffset inOffsets[inOffsetCount]; + + if (inOffsetCount) { + for (NSUInteger i = 0; i < inOffsetCount; i++) { + NSDictionary *eachRun = [styleRuns objectAtIndex:i]; + + inOffsets[i] = [[eachRun objectForKey:@"offset"] unsignedLongValue] * 2; } } - free(outBytesPtr); - return pbData; + ItemCount offsetCount; + ByteOffset offsets[inOffsetCount]; + + err = ConvertFromUnicodeToScriptCodeRun(info, unicodeLength, chars, + kUnicodeTextRunMask | kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask, + inOffsetCount, inOffsets, &offsetCount, offsets, + bufLen, &bytesRead, &bufLen, buf, + scriptRunCount, &scriptRunCount, scriptRuns); + + if (err != noErr) { + if (styleAndScriptRuns) + *styleAndScriptRuns = styleRuns; + + return [[aStr string] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(MacDefaultTextEncoding())]; + } + + if (styleAndScriptRuns) { + NSMutableArray *runs = [NSMutableArray array]; + NSUInteger currentStyleRun = 0; + NSUInteger currentScriptRun = 0; + + for (NSUInteger currentOffset = 0; currentOffset < bufLen;) { + ScriptCodeRun scriptRun = scriptRuns[currentScriptRun]; + NSDictionary *attrs = [[styleRuns objectAtIndex:currentStyleRun] objectForKey:@"attributes"]; + + NSUInteger nextStyleOffset = (currentStyleRun < offsetCount - 1) ? offsets[currentStyleRun + 1] : bufLen; + NSUInteger nextScriptOffset = (currentScriptRun < scriptRunCount - 1) ? scriptRuns[currentScriptRun + 1].offset : bufLen; + + [runs addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInteger:currentOffset], @"offset", + [NSNumber numberWithShort:scriptRun.script], @"script", + attrs, @"attributes", nil]]; + + if (nextStyleOffset == nextScriptOffset) { + currentStyleRun++; + currentScriptRun++; + currentOffset = nextStyleOffset; + } else if (nextStyleOffset < nextScriptOffset) { + currentStyleRun++; + currentOffset = nextStyleOffset; + } else { + currentScriptRun++; + currentOffset = nextScriptOffset; + } + } + + *styleAndScriptRuns = runs; + } + + return [NSData dataWithBytes:buf length:bufLen]; +} + +/* + * Count all Mac font IDs on the system + */ + +static NSUInteger CountMacFonts() +{ + M68kRegisters r; + static uint8_t proc[] = { + 0x55, 0x4f, // subq.w #2,sp + 0x2f, 0x3c, 'F', 'O', 'N', 'D', // move.l #'FOND',-(sp) + 0xa9, 0x9c, // CountResources() + 0x30, 0x1f, // move.w (sp)+,D0 + M68K_RTS >> 8, M68K_RTS & 0xff + }; + r.d[0] = sizeof(proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + uint32_t proc_area = r.a[0]; + int16_t fontCount = 0; + + if (proc_area) { + Host2Mac_memcpy(proc_area, proc, sizeof(proc)); + Execute68k(proc_area, &r); + + fontCount = r.d[0]; + + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr + } + + if (fontCount < 0) { + fontCount = 0; + } + + return fontCount; +} + +/* + * Get Mac font ID at index + */ + +static int16_t MacFontIDAtIndex(NSUInteger index) +{ + M68kRegisters r; + static uint8_t get_res_handle_proc[] = { + 0x42, 0x27, // clr.b -(sp) + 0xa9, 0x9b, // SetResLoad() + 0x59, 0x4f, // subq.w #4,sp + 0x2f, 0x3c, 'F', 'O', 'N', 'D', // move.l #'FOND',-(sp) + 0x3f, 0x3c, 0, 0, // move.w #index,-(sp) + 0xa9, 0x9d, // GetIndResource() + 0x26, 0x5f, // movea.l (sp)+,A3 + 0x1f, 0x3c, 0x00, 0x01, // move.b #1,-(sp) + 0xa9, 0x9b, // SetResLoad() + M68K_RTS >> 8, M68K_RTS & 0xff + }; + r.d[0] = sizeof(get_res_handle_proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + uint32_t proc_area = r.a[0]; + + uint32_t res_handle = 0; + int16_t fontID = 0; + + if (proc_area) { + Host2Mac_memcpy(proc_area, get_res_handle_proc, sizeof(get_res_handle_proc)); + WriteMacInt16(proc_area + 14, (uint16_t)(index + 1)); + + Execute68k(proc_area, &r); + + res_handle = r.a[3]; + + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr() + } + + if (res_handle) { + static uint8_t get_info_proc[] = { + 0x2f, 0x0a, // move.l A2,-(sp) + 0x2f, 0x0b, // move.l A3,-(sp) + 0x42, 0xa7, // clr.l -(sp) + 0x42, 0xa7, // clr.l -(sp) + 0xa9, 0xa8, // GetResInfo() + 0x2f, 0x0a, // move.l A2,-(sp) + 0xa9, 0xa3, // ReleaseResource() + M68K_RTS >> 8, M68K_RTS & 0xff, + 0, 0 + }; + + r.d[0] = sizeof(get_info_proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + proc_area = r.a[0]; + + if (proc_area) { + Host2Mac_memcpy(proc_area, get_info_proc, sizeof(get_info_proc)); + r.a[2] = res_handle; + r.a[3] = proc_area + 16; + + Execute68k(proc_area, &r); + + fontID = ReadMacInt16(proc_area + 16); + + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr() + } + } + + return fontID; +} + +/* + * List all font IDs on the system + */ + +static NSArray *ListMacFonts() +{ + NSUInteger fontCount = CountMacFonts(); + NSMutableArray *fontIDs = [NSMutableArray array]; + + for (NSUInteger i = 0; i < fontCount; i++) { + int16_t eachFontID = MacFontIDAtIndex(i); + + [fontIDs addObject:[NSNumber numberWithShort:eachFontID]]; + } + + return fontIDs; +} + +/* + * List all font IDs having a certain script + */ + +static NSArray *ListMacFontsForScript(ScriptCode script) +{ + NSMutableArray *fontIDs = [NSMutableArray array]; + + for (NSNumber *eachFontIDNum in ListMacFonts()) { + if (ScriptNumberForFontID([eachFontIDNum shortValue]) == script) + [fontIDs addObject:eachFontIDNum]; + } + + return fontIDs; } /* @@ -210,12 +447,12 @@ static NSString *FontNameFromFontID(int16_t fontID) uint8_t * const namePtr = Mac2HostAddr(name_area); - CFStringRef name = CFStringCreateWithPascalString(kCFAllocatorDefault, namePtr, kCFStringEncodingMacRoman); + NSString *name = (NSString *)CFStringCreateWithPascalString(kCFAllocatorDefault, namePtr, kCFStringEncodingMacRoman); r.a[0] = name_area; Execute68kTrap(0xa01f, &r); // DisposePtr - return [(NSString *)name autorelease]; + return [name autorelease]; } /* @@ -269,12 +506,42 @@ static int16_t FontIDFromFontName(NSString *fontName) } /* - * Convert Mac styl to attributed string + * Get font ID in desired script if possible; otherwise, try to get some font in the desired script. */ -static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *stylData) +static int16_t FontIDFromFontNameAndScript(NSString *fontName, ScriptCode script) { - NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] initWithString:string] autorelease]; + int16_t fontID = FontIDFromFontName(fontName); + + if (ScriptNumberForFontID(fontID) == script) + return fontID; + + NSArray *fontIDs = ListMacFontsForScript(script); + + if ([fontIDs count] == 0) + return fontID; // no fonts are going to work; might as well return the original one + + if (fontName) { + // look for a localized version of our font; e.g. "Helvetica CE" if our font is Helvetica + for (NSNumber *eachFontIDNum in fontIDs) { + int16_t eachFontID = [eachFontIDNum shortValue]; + + if ([FontNameFromFontID(eachFontID) hasPrefix:fontName]) + return eachFontID; + } + } + + // Give up and just return a font that will work + return [[fontIDs objectAtIndex:0] shortValue]; +} + +/* + * Convert Mac TEXT/styl to attributed string + */ + +static NSAttributedString *AttributedStringFromMacTEXTAndStyl(NSData *textData, NSData *stylData) +{ + NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] init] autorelease]; if (aStr == nil) return nil; @@ -291,12 +558,12 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s if (length < elements * elementSize) return nil; - CFIndex pointer = 2; + NSUInteger pointer = 2; - for (NSUInteger i = 0; i < elements; i++) { + for (NSUInteger i = 0; i < elements; i++) AUTORELEASE_POOL { int32_t startChar = CFSwapInt32BigToHost(*(int32_t *)(bytes + pointer)); pointer += 4; - int16_t height = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; - int16_t ascent = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; + int16_t height __attribute__((unused)) = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; + int16_t ascent __attribute__((unused)) = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; int16_t fontID = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; uint8_t face = bytes[pointer]; pointer += 2; int16_t size = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; @@ -307,13 +574,14 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s int32_t nextChar; if (i + 1 == elements) - nextChar = [aStr length]; + nextChar = [textData length]; else nextChar = CFSwapInt32BigToHost(*(int32_t *)(bytes + pointer)); NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; NSColor *color = [NSColor colorWithDeviceRed:(CGFloat)red / 65535.0 green:(CGFloat)green / 65535.0 blue:(CGFloat)blue / 65535.0 alpha:1.0]; NSFont *font; + TextEncoding encoding; if (fontID == 0) { // System font CGFloat fontSize = (size == 0) ? [NSFont systemFontSize] : (CGFloat)size; @@ -323,11 +591,25 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s } else { NSString *fontName = FontNameFromFontID(fontID); font = [NSFont fontWithName:fontName size:(CGFloat)size]; + + if (font == nil) { + // Convert localized variants of fonts; e.g. "Helvetica CE" to "Helvetica" + + NSRange wsRange = [fontName rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet] options:NSBackwardsSearch]; + + if (wsRange.length) { + fontName = [fontName substringToIndex:wsRange.location]; + font = [NSFont fontWithName:fontName size:(CGFloat)size]; + } + } } if (font == nil) font = [NSFont userFontOfSize:(CGFloat)size]; + if (UpgradeScriptInfoToTextEncoding(ScriptNumberForFontID(fontID), kTextLanguageDontCare, kTextRegionDontCare, NULL, &encoding)) + encoding = MacDefaultTextEncoding(); + NSFontManager *fm = [NSFontManager sharedFontManager]; if (face & FONT_FACE_BOLD) @@ -366,198 +648,204 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s [attrs setObject:color forKey:NSForegroundColorAttributeName]; - [aStr setAttributes:attrs range:NSMakeRange(startChar, nextChar - startChar)]; + NSData *partialData = [textData subdataWithRange:NSMakeRange(startChar, nextChar - startChar)]; + NSString *partialString = [[NSString alloc] initWithData:partialData encoding:CFStringConvertEncodingToNSStringEncoding(encoding)]; + if (partialString) { + NSAttributedString *partialAttribString = [[NSAttributedString alloc] initWithString:partialString attributes:attrs]; + + [aStr appendAttributedString:partialAttribString]; + + [partialAttribString release]; + } + + [partialString release]; [attrs release]; } return aStr; } +/* + * Append styl data for one text run + */ + +static void AppendStylRunData(NSMutableData *stylData, NSDictionary *attrs, ScriptCode script) +{ + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + + NSFont *font = [attrs objectForKey:NSFontAttributeName]; + NSColor *color = [[attrs objectForKey:NSForegroundColorAttributeName] colorUsingColorSpaceName:NSDeviceRGBColorSpace device:nil]; + NSFontTraitMask traits = [fontManager traitsOfFont:font]; + NSNumber *underlineStyle = [attrs objectForKey:NSUnderlineStyleAttributeName]; + NSNumber *strokeWidth = [attrs objectForKey:NSStrokeWidthAttributeName]; + NSShadow *shadow = [attrs objectForKey:NSShadowAttributeName]; + + int16_t hostFontID = FontIDFromFontNameAndScript([font familyName], script); + + if (hostFontID == 0) { + hostFontID = [font isFixedPitch] ? 4 /* Monaco */ : 1 /* Application font */; + } + + int16_t height = CFSwapInt16HostToBig((int16_t)rint([layoutManager defaultLineHeightForFont:font])); + int16_t ascent = CFSwapInt16HostToBig((int16_t)rint([font ascender])); + int16_t fontID = CFSwapInt16HostToBig(hostFontID); + uint8_t face = 0; + int16_t size = CFSwapInt16HostToBig((int16_t)rint([font pointSize])); + uint16_t red = CFSwapInt16HostToBig((int16_t)rint([color redComponent] * 65535.0)); + uint16_t green = CFSwapInt16HostToBig((int16_t)rint([color greenComponent] * 65535.0)); + uint16_t blue = CFSwapInt16HostToBig((int16_t)rint([color blueComponent] * 65535.0)); + + if (traits & NSBoldFontMask) { + face |= FONT_FACE_BOLD; + } + + if (traits & NSItalicFontMask) { + face |= FONT_FACE_ITALIC; + } + + if (traits & NSCondensedFontMask) { + face |= FONT_FACE_CONDENSED; + } + + if (traits & NSExpandedFontMask) { + face |= FONT_FACE_EXTENDED; + } + + if (underlineStyle && [underlineStyle integerValue] != NSUnderlineStyleNone) { + face |= FONT_FACE_UNDERLINE; + } + + if (strokeWidth && [strokeWidth doubleValue] > 0.0) { + face |= FONT_FACE_OUTLINE; + } + + if (shadow) { + face |= FONT_FACE_SHADOW; + } + + [stylData appendBytes:&height length:2]; + [stylData appendBytes:&ascent length:2]; + [stylData appendBytes:&fontID length:2]; + [stylData appendBytes:&face length:1]; + [stylData increaseLengthBy:1]; + [stylData appendBytes:&size length:2]; + [stylData appendBytes:&red length:2]; + [stylData appendBytes:&green length:2]; + [stylData appendBytes:&blue length:2]; + + [layoutManager release]; +} + /* * Convert attributed string to TEXT/styl */ -static NSData *ConvertToMacTEXTAndStyl(NSAttributedString *aStr, NSData **outStylData) { +static NSData *ConvertToMacTEXTAndStyl(NSAttributedString *aStr, NSData **outStylData) +{ // Limitations imposed by the Mac TextEdit system. - // Something to test would be whether using UTF16 causes TextEdit to choke at 16K characters - // instead of 32K characters, depending on whether this is a byte limit or a true character limit. - // If the former, UTF8 might be a better choice for encoding here. const NSUInteger charLimit = 32 * 1024; const NSUInteger elementLimit = 1601; - - if ([aStr length] > charLimit) { + + NSUInteger length = [aStr length]; + + if (length > charLimit) { aStr = [aStr attributedSubstringFromRange:NSMakeRange(0, charLimit)]; } - - // See comment in CreateRTFDataFromMacTEXTAndStyl regarding encodings; I hope I've interpreted - // the existing code correctly in this regard -#if __LITTLE_ENDIAN__ - NSStringEncoding encoding = NSUTF16LittleEndianStringEncoding; -#else - NSStringEncoding encoding = NSUTF16BigEndianStringEncoding; -#endif - - NSData *textData = [[aStr string] dataUsingEncoding:encoding]; + + NSArray *runs = nil; + + NSData *textData = ConvertToMacTextEncoding(aStr, &runs); + NSMutableData *stylData = [NSMutableData dataWithLength:2]; // number of styles to be filled in at the end - - NSUInteger length = [aStr length]; + NSUInteger elements = 0; - - NSFontManager *fontManager = [NSFontManager sharedFontManager]; - NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease]; - - for (NSUInteger index = 0; index < length && elements < elementLimit;) { - NSRange attrRange; - NSDictionary *attrs = [aStr attributesAtIndex:index effectiveRange:&attrRange]; - - NSFont *font = [attrs objectForKey:NSFontAttributeName]; - NSColor *color = [[attrs objectForKey:NSForegroundColorAttributeName] colorUsingColorSpaceName:NSDeviceRGBColorSpace device:nil]; - NSFontTraitMask traits = [fontManager traitsOfFont:font]; - NSNumber *underlineStyle = [attrs objectForKey:NSUnderlineStyleAttributeName]; - NSNumber *strokeWidth = [attrs objectForKey:NSStrokeWidthAttributeName]; - NSShadow *shadow = [attrs objectForKey:NSShadowAttributeName]; - - int16_t hostFontID = FontIDFromFontName([font familyName]); - - if (hostFontID == 0) { - hostFontID = [font isFixedPitch] ? 4 /* Monaco */ : 1 /* Application font */; - } - - int32_t startChar = CFSwapInt32HostToBig((int32_t)index); - int16_t height = CFSwapInt16HostToBig((int16_t)rint([layoutManager defaultLineHeightForFont:font])); - int16_t ascent = CFSwapInt16HostToBig((int16_t)rint([font ascender])); - int16_t fontID = CFSwapInt16HostToBig(hostFontID); - uint8_t face = 0; - int16_t size = CFSwapInt16HostToBig((int16_t)rint([font pointSize])); - uint16_t red = CFSwapInt16HostToBig((int16_t)rint([color redComponent] * 65535.0)); - uint16_t green = CFSwapInt16HostToBig((int16_t)rint([color greenComponent] * 65535.0)); - uint16_t blue = CFSwapInt16HostToBig((int16_t)rint([color blueComponent] * 65535.0)); - - if (traits & NSBoldFontMask) { - face |= FONT_FACE_BOLD; - } - - if (traits & NSItalicFontMask) { - face |= FONT_FACE_ITALIC; - } - - if (traits & NSCondensedFontMask) { - face |= FONT_FACE_CONDENSED; - } - - if (traits & NSExpandedFontMask) { - face |= FONT_FACE_EXTENDED; - } - - if (underlineStyle && [underlineStyle integerValue] != NSUnderlineStyleNone) { - face |= FONT_FACE_UNDERLINE; - } - - if (strokeWidth && [strokeWidth doubleValue] > 0.0) { - face |= FONT_FACE_OUTLINE; - } - - if (shadow) { - face |= FONT_FACE_SHADOW; - } - + + for (NSDictionary *eachRun in runs) { + if (elements >= elementLimit) + break; + + NSUInteger offset = [[eachRun objectForKey:@"offset"] unsignedIntegerValue]; + ScriptCode script = [[eachRun objectForKey:@"script"] shortValue]; + NSDictionary *attrs = [eachRun objectForKey:@"attributes"]; + + int32_t startChar = CFSwapInt32HostToBig((int32_t)offset); [stylData appendBytes:&startChar length:4]; - [stylData appendBytes:&height length:2]; - [stylData appendBytes:&ascent length:2]; - [stylData appendBytes:&fontID length:2]; - [stylData appendBytes:&face length:1]; - [stylData increaseLengthBy:1]; - [stylData appendBytes:&size length:2]; - [stylData appendBytes:&red length:2]; - [stylData appendBytes:&green length:2]; - [stylData appendBytes:&blue length:2]; - - index += attrRange.length; + + AppendStylRunData(stylData, attrs, script); + elements++; } - + uint16_t bigEndianElements = CFSwapInt16HostToBig((uint16_t)elements); - + [stylData replaceBytesInRange:NSMakeRange(0, 2) withBytes:&bigEndianElements length:2]; - + if (outStylData) *outStylData = stylData; - - textData = (NSData *)ConvertMacTextEncoding((CFDataRef)[textData retain], YES); - - return [textData autorelease]; + + return textData; } /* * Convert Mac TEXT/styl to RTF */ -static CFDataRef CreateRTFDataFromMacTEXTAndStyl(CFDataRef textData, CFDataRef stylData) +static void WriteMacTEXTAndStylToPasteboard(NSData *textData, NSData *stylData) { - // Unfortunately, CF does not seem to have any RTF conversion routines, so do this in Cocoa instead. - // If we are willing to require OS X 10.6 minimum, we should use the NSPasteboardWriting methods - // instead of putting the RTF data up ourselves. - - NSData *rtfData = nil; - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - // I think this is what's going on? ConvertMacTextEncoding() converts to Unicode in host endian? - // Maybe UTF8 would be a less ambiguous form to store the pasteboard data in? - -#if __LITTLE_ENDIAN__ - NSStringEncoding encoding = NSUTF16LittleEndianStringEncoding; -#else - NSStringEncoding encoding = NSUTF16BigEndianStringEncoding; -#endif - - NSMutableString *string = [[[NSMutableString alloc] initWithData:(NSData *)textData encoding:encoding] autorelease]; - - // fix line endings - [string replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [string length])]; - - if (string != nil) { - NSAttributedString *aStr = ConvertToAttributedString(string, (NSData *)stylData); - - rtfData = [[aStr RTFFromRange:NSMakeRange(0, [aStr length]) documentAttributes:nil] retain]; + NSMutableAttributedString *aStr = [AttributedStringFromMacTEXTAndStyl(textData, stylData) mutableCopy]; + + if (!aStr) { + NSString *string = [[NSString alloc] initWithData:textData encoding:CFStringConvertEncodingToNSStringEncoding(MacDefaultTextEncoding())]; + + if (!string) + return; + + aStr = [[NSMutableAttributedString alloc] initWithString:string attributes:nil]; + + [string release]; } - - [pool drain]; - - return (CFDataRef)rtfData; + + // fix line endings + [[aStr mutableString] replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [aStr length])]; + + [g_pboard writeObjects:[NSArray arrayWithObject:aStr]]; } /* * Convert RTF to Mac TEXT/styl */ -static CFDataRef CreateMacTEXTAndStylFromRTFData(CFDataRef rtfData, CFDataRef *outStylData) +static NSData *GetMacTEXTAndStylDataFromPasteboard(NSData **outStylData) { - // No easy way to do this at the CF layer, so use Cocoa. - // Reading RTF should be backward compatible to the early releases of OS X; - // if we are willing to require OS X 10.6 or better, we should use NSPasteboardReading - // to read an NSAttributedString off of the pasteboard, which will give us the ability - // to potentially read more rich-text formats than RTF. - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] initWithRTF:(NSData *)rtfData documentAttributes:nil] autorelease]; - + NSMutableAttributedString *aStr; + + NSArray *objs = [g_pboard readObjectsForClasses:[NSArray arrayWithObject:[NSAttributedString class]] options:nil]; + + if ([objs count]) { + aStr = [[objs objectAtIndex:0] mutableCopy]; + } else { + objs = [g_pboard readObjectsForClasses:[NSArray arrayWithObject:[NSString class]] options:nil]; + + if (![objs count]) + return nil; + + aStr = [[NSMutableAttributedString alloc] initWithString:[objs objectAtIndex:0]]; + } + // fix line endings [[aStr mutableString] replaceOccurrencesOfString:@"\n" withString:@"\r" options:NSLiteralSearch range:NSMakeRange(0, [[aStr mutableString] length])]; - + NSData *stylData = nil; NSData *textData = ConvertToMacTEXTAndStyl(aStr, &stylData); - - [textData retain]; - + + [aStr release]; + if (outStylData) - *outStylData = (CFDataRef)[stylData retain]; - - [pool drain]; - - return (CFDataRef)textData; + *outStylData = stylData; + + return textData; } /* @@ -566,11 +854,12 @@ static CFDataRef CreateMacTEXTAndStylFromRTFData(CFDataRef rtfData, CFDataRef *o void ClipInit(void) { - OSStatus err = PasteboardCreate(kPasteboardClipboard, &g_pbref); - if (err) { + g_pboard = [[NSPasteboard generalPasteboard] retain]; + if (!g_pboard) { D(bug("could not create Pasteboard\n")); - g_pbref = NULL; } + + macScrap = [[NSMutableDictionary alloc] init]; } @@ -580,36 +869,11 @@ void ClipInit(void) void ClipExit(void) { - if (g_pbref) { - CFRelease(g_pbref); - g_pbref = NULL; - } -} + [g_pboard release]; + g_pboard = nil; -/* - * Copy data from the host pasteboard - */ - -static CFDataRef CopyPasteboardDataWithFlavor(CFStringRef flavor) -{ - ItemCount itemCount; - - if (PasteboardGetItemCount(g_pbref, &itemCount)) - return NULL; - - for (UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex++) { - PasteboardItemID itemID; - CFDataRef pbData; - - if (PasteboardGetItemIdentifier(g_pbref, itemIndex, &itemID)) - break; - - if (!PasteboardCopyItemFlavorData(g_pbref, itemID, flavor, &pbData)) { - return pbData; - } - } - - return NULL; + [macScrap release]; + macScrap = nil; } /* @@ -618,9 +882,9 @@ static CFDataRef CopyPasteboardDataWithFlavor(CFStringRef flavor) static void ZeroMacClipboard() { - D(bug(stderr, "Zeroing Mac clipboard\n")); + D(bug("Zeroing Mac clipboard\n")); M68kRegisters r; - static uint8 proc[] = { + static uint8_t proc[] = { 0x59, 0x8f, // subq.l #4,sp 0xa9, 0xfc, // ZeroScrap() 0x58, 0x8f, // addq.l #4,sp @@ -628,12 +892,12 @@ static void ZeroMacClipboard() }; r.d[0] = sizeof(proc); Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 proc_area = r.a[0]; - + uint32_t proc_area = r.a[0]; + if (proc_area) { Host2Mac_memcpy(proc_area, proc, sizeof(proc)); Execute68k(proc_area, &r); - + r.a[0] = proc_area; Execute68kTrap(0xa01f, &r); // DisposePtr } @@ -643,25 +907,25 @@ static void ZeroMacClipboard() * Write data to Mac clipboard */ -static void WriteDataToMacClipboard(CFDataRef pbData, uint32 type) +static void WriteDataToMacClipboard(NSData *pbData, uint32_t type) { - D(bug(stderr, "Writing data %s to Mac clipboard with type '%c%c%c%c'\n", [[(NSData *)pbData description] UTF8String], - (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, type & 0xff)); - + D(bug("Writing data %s to Mac clipboard with type '%c%c%c%c'\n", [[pbData description] UTF8String], + (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, type & 0xff)); + // Allocate space for new scrap in MacOS side M68kRegisters r; - r.d[0] = CFDataGetLength(pbData); + r.d[0] = [pbData length]; Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 scrap_area = r.a[0]; - + uint32_t scrap_area = r.a[0]; + // Get the native clipboard data if (scrap_area) { - uint8 * const data = Mac2HostAddr(scrap_area); - - memcpy(data, CFDataGetBytePtr(pbData), CFDataGetLength(pbData)); - + uint8_t * const data = Mac2HostAddr(scrap_area); + + memcpy(data, [pbData bytes], [pbData length]); + // Add new data to clipboard - static uint8 proc[] = { + static uint8_t proc[] = { 0x59, 0x8f, // subq.l #4,sp 0x2f, 0x3c, 0, 0, 0, 0, // move.l #length,-(sp) 0x2f, 0x3c, 0, 0, 0, 0, // move.l #type,-(sp) @@ -672,20 +936,20 @@ static void WriteDataToMacClipboard(CFDataRef pbData, uint32 type) }; r.d[0] = sizeof(proc); Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 proc_area = r.a[0]; - + uint32_t proc_area = r.a[0]; + if (proc_area) { Host2Mac_memcpy(proc_area, proc, sizeof(proc)); - WriteMacInt32(proc_area + 4, CFDataGetLength(pbData)); + WriteMacInt32(proc_area + 4, [pbData length]); WriteMacInt32(proc_area + 10, type); WriteMacInt32(proc_area + 16, scrap_area); we_put_this_data = true; Execute68k(proc_area, &r); - + r.a[0] = proc_area; Execute68kTrap(0xa01f, &r); // DisposePtr } - + r.a[0] = scrap_area; Execute68kTrap(0xa01f, &r); // DisposePtr } @@ -695,55 +959,52 @@ static void WriteDataToMacClipboard(CFDataRef pbData, uint32 type) * Mac application reads clipboard */ -void GetScrap(void **handle, uint32 type, int32 offset) +void GetScrap(void **handle, uint32_t type, int32_t offset) { D(bug("GetScrap handle %p, type %4.4s, offset %d\n", handle, (char *)&type, offset)); - - CFStringRef typeStr; - PasteboardSyncFlags syncFlags; - - if (!g_pbref) - return; - - syncFlags = PasteboardSynchronize(g_pbref); - if (syncFlags & kPasteboardModified) - return; - - if (!(typeStr = GetUTIFromFlavor(type))) - return; - - if (type == TYPE_TEXT || type == TYPE_STYL) { - CFDataRef rtfData = CopyPasteboardDataWithFlavor(kUTTypeRTF); - - if (rtfData != NULL) { - CFDataRef stylData = NULL; - CFDataRef textData = CreateMacTEXTAndStylFromRTFData(rtfData, &stylData); - - ZeroMacClipboard(); - - if (stylData) - WriteDataToMacClipboard(stylData, TYPE_STYL); - - WriteDataToMacClipboard(textData, TYPE_TEXT); - - CFRelease(textData); - CFRelease(stylData); - CFRelease(rtfData); - + + AUTORELEASE_POOL { + NSString *typeStr; + + if (!g_pboard) return; + + if (!(typeStr = GetUTIFromFlavor(type))) + return; + + if (type == TYPE_TEXT || type == TYPE_STYL) { + NSData *stylData = nil; + NSData *textData = GetMacTEXTAndStylDataFromPasteboard(&stylData); + + if (textData) { + ZeroMacClipboard(); + + if (stylData) + WriteDataToMacClipboard(stylData, TYPE_STYL); + + WriteDataToMacClipboard(textData, TYPE_TEXT); + + return; + } + } + + NSData *pbData = nil; + + NSArray *objs = [g_pboard readObjectsForClasses:[NSArray arrayWithObject:[NSPasteboardItem class]] options:nil]; + + for (NSPasteboardItem *eachItem in objs) { + NSData *data = [eachItem dataForType:typeStr]; + + if ([data length]) { + pbData = data; + break; + } + } + + if (pbData) { + ZeroMacClipboard(); + WriteDataToMacClipboard(pbData, type); } - } - - CFDataRef pbData = CopyPasteboardDataWithFlavor(typeStr); - - if (pbData) { - if (type == TYPE_TEXT) - pbData = ConvertMacTextEncoding(pbData, TRUE); - - ZeroMacClipboard(); - WriteDataToMacClipboard(pbData, type); - - CFRelease(pbData); } } @@ -754,9 +1015,9 @@ void GetScrap(void **handle, uint32 type, int32 offset) void ZeroScrap() { D(bug("ZeroScrap\n")); - + we_put_this_data = false; - + // Defer clearing the host pasteboard until the Mac tries to put something on it. // This prevents us from clearing the pasteboard when ZeroScrap() is called during startup. should_clear = true; @@ -766,71 +1027,55 @@ void ZeroScrap() * Mac application wrote to clipboard */ -void PutScrap(uint32 type, void *scrap, int32 length) +void PutScrap(uint32_t type, void *scrap, int32_t length) { D(bug("PutScrap type %4.4s, data %p, length %ld\n", (char *)&type, scrap, (long)length)); - - PasteboardSyncFlags syncFlags; - CFStringRef typeStr; - - if (!g_pbref) - return; - - if (!(typeStr = GetUTIFromFlavor(type))) - return; - - if (we_put_this_data) { - we_put_this_data = false; - return; - } - if (length <= 0) - return; - - if (should_clear) { - PasteboardClear(g_pbref); - should_clear = false; - } - - syncFlags = PasteboardSynchronize(g_pbref); - if ((syncFlags & kPasteboardModified) || !(syncFlags & kPasteboardClientIsOwner)) - return; - - CFDataRef pbData = CFDataCreate(kCFAllocatorDefault, (UInt8*)scrap, length); - if (!pbData) - return; - - - if (type == TYPE_TEXT) - pbData = ConvertMacTextEncoding(pbData, FALSE); - - if (pbData) { - PasteboardPutItemFlavor(g_pbref, (PasteboardItemID)1, typeStr, pbData, 0); - CFRelease(pbData); - } - - if (type == TYPE_TEXT || type == TYPE_STYL) { - CFDataRef textData; - CFDataRef stylData; - if (PasteboardCopyItemFlavorData(g_pbref, (PasteboardItemID)1, TEXT_FLAVOR_NAME, &textData) != noErr) - textData = NULL; + AUTORELEASE_POOL { + NSString *typeStr; - if (PasteboardCopyItemFlavorData(g_pbref, (PasteboardItemID)1, STYL_FLAVOR_NAME, &stylData) != noErr) - stylData = NULL; + if (!g_pboard) + return; - if (textData != NULL && stylData != NULL) { - CFDataRef rtfData = CreateRTFDataFromMacTEXTAndStyl(textData, stylData); + if (!(typeStr = GetUTIFromFlavor(type))) + return; - if (rtfData) { - PasteboardPutItemFlavor(g_pbref, (PasteboardItemID)1, kUTTypeRTF, rtfData, 0); - CFRelease(rtfData); - } + if (we_put_this_data) { + we_put_this_data = false; + return; + } + if (length <= 0) + return; + + if (should_clear) { + [g_pboard clearContents]; + + [macScrap removeAllObjects]; + should_clear = false; } - if (textData) - CFRelease(textData); + NSData *pbData = [NSData dataWithBytes:scrap length:length]; + if (!pbData) + return; - if (stylData) - CFRelease(stylData); + [macScrap setObject:pbData forKey:[NSNumber numberWithInteger:type]]; + + if (type == TYPE_TEXT || type == TYPE_STYL) { + NSData *textData; + NSData *stylData; + + textData = [macScrap objectForKey:[NSNumber numberWithInteger:TYPE_TEXT]]; + stylData = [macScrap objectForKey:[NSNumber numberWithInteger:TYPE_STYL]]; + + if (textData) { + WriteMacTEXTAndStylToPasteboard(textData, stylData); + } + } else if (pbData) { + NSPasteboardItem *pbItem = [[[NSPasteboardItem alloc] init] autorelease]; + + [pbItem setData:pbData forType:typeStr]; + + [g_pboard writeObjects:[NSArray arrayWithObject:pbItem]]; + } } } diff --git a/SheepShaver/src/MacOSX/autorelease.h b/SheepShaver/src/MacOSX/autorelease.h new file mode 120000 index 00000000..154cdead --- /dev/null +++ b/SheepShaver/src/MacOSX/autorelease.h @@ -0,0 +1 @@ +../../../BasiliskII/src/MacOSX/autorelease.h \ No newline at end of file