1/* 2 * tkMacOSXBitmap.c -- 3 * 4 * This file handles the implementation of native bitmaps. 5 * 6 * Copyright (c) 1996-1997 Sun Microsystems, Inc. 7 * Copyright 2001-2009, Apple Inc. 8 * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution 11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id$ 14 */ 15 16#include "tkMacOSXPrivate.h" 17 18/* 19 * This structure holds information about native bitmaps. 20 */ 21 22typedef struct { 23 const char *name; /* Name of icon. */ 24 OSType iconType; /* OSType of icon. */ 25} BuiltInIcon; 26 27/* 28 * This array mapps a string name to the supported builtin icons 29 * on the Macintosh. 30 */ 31 32static BuiltInIcon builtInIcons[] = { 33 {"document", kGenericDocumentIcon}, 34 {"stationery", kGenericStationeryIcon}, 35 {"edition", kGenericEditionFileIcon}, 36 {"application", kGenericApplicationIcon}, 37 {"accessory", kGenericDeskAccessoryIcon}, 38 {"folder", kGenericFolderIcon}, 39 {"pfolder", kPrivateFolderIcon}, 40 {"trash", kTrashIcon}, 41 {"floppy", kGenericFloppyIcon}, 42 {"ramdisk", kGenericRAMDiskIcon}, 43 {"cdrom", kGenericCDROMIcon}, 44 {"preferences", kGenericPreferencesIcon}, 45 {"querydoc", kGenericQueryDocumentIcon}, 46 {"stop", kAlertStopIcon}, 47 {"note", kAlertNoteIcon}, 48 {"caution", kAlertCautionIcon}, 49 {NULL} 50}; 51 52#define builtInIconSize 32 53 54static Tcl_HashTable iconBitmapTable = {}; 55typedef struct { 56 int kind, width, height; 57 char *value; 58} IconBitmap; 59 60static const char *iconBitmapOptionStrings[] = { 61 "-file", "-fileType", "-osType", "-systemType", "-namedImage", 62 "-imageFile", NULL 63}; 64enum iconBitmapOptions { 65 ICON_FILE, ICON_FILETYPE, ICON_OSTYPE, ICON_SYSTEMTYPE, ICON_NAMEDIMAGE, 66 ICON_IMAGEFILE, 67}; 68 69 70/* 71 *---------------------------------------------------------------------- 72 * 73 * TkpDefineNativeBitmaps -- 74 * 75 * Add native bitmaps. 76 * 77 * Results: 78 * A standard Tcl result. If an error occurs then TCL_ERROR is 79 * returned and a message is left in the interp's result. 80 * 81 * Side effects: 82 * "Name" is entered into the bitmap table and may be used from 83 * here on to refer to the given bitmap. 84 * 85 *---------------------------------------------------------------------- 86 */ 87 88void 89TkpDefineNativeBitmaps(void) 90{ 91 Tcl_HashTable *tablePtr = TkGetBitmapPredefTable(); 92 BuiltInIcon *builtInPtr; 93 94 for (builtInPtr = builtInIcons; builtInPtr->name != NULL; builtInPtr++) { 95 Tcl_HashEntry *predefHashPtr; 96 Tk_Uid name; 97 int isNew; 98 99 name = Tk_GetUid(builtInPtr->name); 100 predefHashPtr = Tcl_CreateHashEntry(tablePtr, name, &isNew); 101 if (isNew) { 102 TkPredefBitmap *predefPtr = (TkPredefBitmap *) 103 ckalloc(sizeof(TkPredefBitmap)); 104 predefPtr->source = UINT2PTR(builtInPtr->iconType); 105 predefPtr->width = builtInIconSize; 106 predefPtr->height = builtInIconSize; 107 predefPtr->native = 1; 108 Tcl_SetHashValue(predefHashPtr, predefPtr); 109 } 110 } 111} 112 113/* 114 *---------------------------------------------------------------------- 115 * 116 * GetBitmapForIcon -- 117 * 118 * Results: 119 * Bitmap for the given IconRef. 120 * 121 * Side effects: 122 * None. 123 * 124 *---------------------------------------------------------------------- 125 */ 126 127static Pixmap 128GetBitmapForIcon( 129 Display *display, 130 IconRef icon, 131 CGSize size) 132{ 133 TkMacOSXDrawingContext dc; 134 Pixmap pixmap; 135 136 pixmap = Tk_GetPixmap(display, None, size.width, size.height, 0); 137 if (TkMacOSXSetupDrawingContext(pixmap, NULL, 1, &dc)) { 138 if (dc.context) { 139 const CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, 140 .tx = 0, .ty = size.height }; 141 const CGRect r = { .origin = { .x = 0, .y = 0 }, .size = size }; 142 143 CGContextConcatCTM(dc.context, t); 144 PlotIconRefInContext(dc.context, &r, kAlignAbsoluteCenter, 145 kTransformNone, NULL, kPlotIconRefNormalFlags, icon); 146 } 147 TkMacOSXRestoreDrawingContext(&dc); 148 } 149 return pixmap; 150} 151 152/* 153 *---------------------------------------------------------------------- 154 * 155 * TkpCreateNativeBitmap -- 156 * 157 * Create native bitmap. 158 * 159 * Results: 160 * Native bitmap. 161 * 162 * Side effects: 163 * None. 164 * 165 *---------------------------------------------------------------------- 166 */ 167 168Pixmap 169TkpCreateNativeBitmap( 170 Display *display, 171 const char *source) /* Info about the icon to build. */ 172{ 173 Pixmap pixmap; 174 IconRef icon; 175 OSErr err; 176 177 err = ChkErr(GetIconRef, kOnSystemDisk, kSystemIconsCreator, 178 PTR2UINT(source), &icon); 179 if (err == noErr) { 180 pixmap = GetBitmapForIcon(display, icon, CGSizeMake(builtInIconSize, 181 builtInIconSize)); 182 ReleaseIconRef(icon); 183 } else { 184 pixmap = Tk_GetPixmap(display, None, builtInIconSize, 185 builtInIconSize, 0); 186 } 187 return pixmap; 188} 189 190/* 191 *---------------------------------------------------------------------- 192 * 193 * OSTypeFromString -- 194 * 195 * Helper to convert string to OSType. 196 * 197 * Results: 198 * A standard Tcl result. 199 * 200 * Side effects: 201 * t is set to OSType if conversion successful. 202 * 203 *---------------------------------------------------------------------- 204 */ 205 206static int 207OSTypeFromString(const char *s, OSType *t) { 208 int result = TCL_ERROR; 209 Tcl_DString ds; 210 Tcl_Encoding encoding = Tcl_GetEncoding(NULL, "macRoman"); 211 212 Tcl_UtfToExternalDString(encoding, s, -1, &ds); 213 if (Tcl_DStringLength(&ds) <= 4) { 214 char string[4] = {}; 215 memcpy(string, Tcl_DStringValue(&ds), (size_t) Tcl_DStringLength(&ds)); 216 *t = (OSType) string[0] << 24 | (OSType) string[1] << 16 | 217 (OSType) string[2] << 8 | (OSType) string[3]; 218 result = TCL_OK; 219 } 220 Tcl_DStringFree(&ds); 221 Tcl_FreeEncoding(encoding); 222 return result; 223} 224 225/* 226 *---------------------------------------------------------------------- 227 * 228 * TkpGetNativeAppBitmap -- 229 * 230 * Get a named native bitmap. 231 * 232 * Attemps to interpret the given name in order as: 233 * - name defined by ::tk::mac::iconBitmap 234 * - NSImage named image name 235 * - NSImage url string 236 * - 4-char OSType of IconServices icon 237 * 238 * Results: 239 * Native bitmap or None. 240 * 241 * Side effects: 242 * None. 243 * 244 *---------------------------------------------------------------------- 245 */ 246 247Pixmap 248TkpGetNativeAppBitmap( 249 Display *display, /* The display. */ 250 const char *name, /* The name of the bitmap. */ 251 int *width, /* The width & height of the bitmap. */ 252 int *height) 253{ 254 Tcl_HashEntry *hPtr; 255 Pixmap pixmap = None; 256 NSString *string; 257 NSImage *image = nil; 258 NSSize size = { .width = builtInIconSize, .height = builtInIconSize }; 259 260 if (iconBitmapTable.buckets && 261 (hPtr = Tcl_FindHashEntry(&iconBitmapTable, name))) { 262 OSType type; 263 IconBitmap *iconBitmap = Tcl_GetHashValue(hPtr); 264 name = NULL; 265 size = NSMakeSize(iconBitmap->width, iconBitmap->height); 266 switch (iconBitmap->kind) { 267 case ICON_FILE: 268 string = [[NSString stringWithUTF8String:iconBitmap->value] 269 stringByExpandingTildeInPath]; 270 image = [[NSWorkspace sharedWorkspace] iconForFile:string]; 271 break; 272 case ICON_FILETYPE: 273 string = [NSString stringWithUTF8String:iconBitmap->value]; 274 image = [[NSWorkspace sharedWorkspace] iconForFileType:string]; 275 break; 276 case ICON_OSTYPE: 277 if (OSTypeFromString(iconBitmap->value, &type) == TCL_OK) { 278 string = NSFileTypeForHFSTypeCode(type); 279 image = [[NSWorkspace sharedWorkspace] iconForFileType:string]; 280 } 281 break; 282 case ICON_SYSTEMTYPE: 283 name = iconBitmap->value; 284 break; 285 case ICON_NAMEDIMAGE: 286 string = [NSString stringWithUTF8String:iconBitmap->value]; 287 image = [NSImage imageNamed:string]; 288 break; 289 case ICON_IMAGEFILE: 290 string = [[NSString stringWithUTF8String:iconBitmap->value] 291 stringByExpandingTildeInPath]; 292 image = [[[NSImage alloc] initWithContentsOfFile:string] 293 autorelease]; 294 break; 295 } 296 if (image) { 297 [image setSize:size]; 298 } 299 } else { 300 string = [NSString stringWithUTF8String:name]; 301 image = [NSImage imageNamed:string]; 302 if (!image) { 303 NSURL *url = [NSURL URLWithString:string]; 304 if (url) { 305 image = [[[NSImage alloc] initWithContentsOfURL:url] 306 autorelease]; 307 } 308 } 309 if (image) { 310 size = [image size]; 311 } 312 } 313 if (image) { 314 TkMacOSXDrawingContext dc; 315 int depth = 0; 316 317#ifdef MAC_OSX_TK_TODO 318 for (NSImageRep *r in [image representations]) { 319 NSInteger bitsPerSample = [r bitsPerSample]; 320 if (bitsPerSample && bitsPerSample > depth) { 321 depth = bitsPerSample; 322 }; 323 } 324 if (depth == 1) { 325 /* TODO: convert BW NSImage to CGImageMask */ 326 } 327#endif 328 pixmap = Tk_GetPixmap(display, None, size.width, size.height, depth); 329 *width = size.width; 330 *height = size.height; 331 if (TkMacOSXSetupDrawingContext(pixmap, NULL, 1, &dc)) { 332 if (dc.context) { 333 CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, 334 .tx = 0, .ty = size.height}; 335 336 CGContextConcatCTM(dc.context, t); 337 [NSGraphicsContext saveGraphicsState]; 338 [NSGraphicsContext setCurrentContext:[NSGraphicsContext 339 graphicsContextWithGraphicsPort:dc.context flipped:NO]]; 340 [image drawAtPoint:NSZeroPoint fromRect:NSZeroRect 341 operation:NSCompositeCopy fraction:1.0]; 342 [NSGraphicsContext restoreGraphicsState]; 343 } 344 TkMacOSXRestoreDrawingContext(&dc); 345 } 346 } else if (name) { 347 OSType iconType; 348 if (OSTypeFromString(name, &iconType) == TCL_OK) { 349 IconRef icon; 350 OSErr err = ChkErr(GetIconRef, kOnSystemDisk, kSystemIconsCreator, 351 iconType, &icon); 352 if (err == noErr) { 353 pixmap = GetBitmapForIcon(display, icon, NSSizeToCGSize(size)); 354 *width = size.width; 355 *height = size.height; 356 ReleaseIconRef(icon); 357 } 358 } 359 } 360 return pixmap; 361} 362 363/* 364 *---------------------------------------------------------------------- 365 * 366 * TkMacOSXIconBitmapObjCmd -- 367 * 368 * Implements the ::tk::mac::iconBitmap command. 369 * 370 * Results: 371 * A standard Tcl result. 372 * 373 * Side effects: 374 * none 375 * 376 *---------------------------------------------------------------------- 377 */ 378 379int 380TkMacOSXIconBitmapObjCmd( 381 ClientData clientData, /* Unused. */ 382 Tcl_Interp *interp, /* Current interpreter. */ 383 int objc, /* Number of arguments. */ 384 Tcl_Obj *const objv[]) /* Argument objects. */ 385{ 386 Tcl_HashEntry *hPtr; 387 int i = 1, len, isNew, result = TCL_ERROR; 388 const char *name, *value; 389 IconBitmap ib, *iconBitmap; 390 391 if (objc != 6) { 392 Tcl_WrongNumArgs(interp, 1, objv, "name width height " 393 "-file|-fileType|-osType|-systemType|-namedImage|-imageFile " 394 "value"); 395 goto end; 396 } 397 name = Tcl_GetStringFromObj(objv[i++], &len); 398 if (!len) { 399 Tcl_AppendResult(interp, "empty bitmap name", NULL); 400 goto end; 401 } 402 if (Tcl_GetIntFromObj(interp, objv[i++], &ib.width) != TCL_OK) { 403 goto end; 404 } 405 if (Tcl_GetIntFromObj(interp, objv[i++], &ib.height) != TCL_OK) { 406 goto end; 407 } 408 if (Tcl_GetIndexFromObj(interp, objv[i++], iconBitmapOptionStrings, 409 "kind", TCL_EXACT, &ib.kind) != TCL_OK) { 410 goto end; 411 } 412 value = Tcl_GetStringFromObj(objv[i++], &len); 413 if (!len) { 414 Tcl_AppendResult(interp, "empty bitmap value", NULL); 415 goto end; 416 } 417#if 0 418 if ((kind == ICON_TYPE || kind == ICON_SYSTEM)) { 419 Tcl_DString ds; 420 Tcl_Encoding encoding = Tcl_GetEncoding(NULL, "macRoman"); 421 Tcl_UtfToExternalDString(encoding, value, -1, &ds); 422 len = Tcl_DStringLength(&ds); 423 Tcl_DStringFree(&ds); 424 Tcl_FreeEncoding(encoding); 425 if (len > 4) { 426 Tcl_AppendResult(interp, "invalid bitmap value", NULL); 427 goto end; 428 } 429 } 430#endif 431 ib.value = ckalloc(len + 1); 432 strcpy(ib.value, value); 433 if (!iconBitmapTable.buckets) { 434 Tcl_InitHashTable(&iconBitmapTable, TCL_STRING_KEYS); 435 } 436 hPtr = Tcl_CreateHashEntry(&iconBitmapTable, name, &isNew); 437 if (!isNew) { 438 iconBitmap = Tcl_GetHashValue(hPtr); 439 ckfree(iconBitmap->value); 440 } else { 441 iconBitmap = (IconBitmap *) ckalloc(sizeof(IconBitmap)); 442 Tcl_SetHashValue(hPtr, iconBitmap); 443 } 444 *iconBitmap = ib; 445 result = TCL_OK; 446end: 447 return result; 448} 449 450/* 451 * Local Variables: 452 * mode: c 453 * c-basic-offset: 4 454 * fill-column: 79 455 * coding: utf-8 456 * End: 457 */ 458