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