/*
* tkMacOSXBitmap.c --
*
* This file handles the implementation of native bitmaps.
*
* Copyright © 1996-1997 Sun Microsystems, Inc.
* Copyright © 2001-2009 Apple Inc.
* Copyright © 2006-2009 Daniel A. Steffen <[email protected]>
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tkMacOSXPrivate.h"
#include "tkMacOSXConstants.h"
/*
* This structure holds information about native bitmaps.
*/
typedef struct {
const char *name; /* Name of icon. */
OSType iconType; /* OSType of icon. */
} BuiltInIcon;
/*
* This array mapps a string name to the supported builtin icons
* on the Macintosh.
*/
static BuiltInIcon builtInIcons[] = {
{"document", kGenericDocumentIcon},
{"stationery", kGenericStationeryIcon},
{"edition", kGenericEditionFileIcon},
{"application", kGenericApplicationIcon},
{"accessory", kGenericDeskAccessoryIcon},
{"folder", kGenericFolderIcon},
{"pfolder", kPrivateFolderIcon},
{"trash", kTrashIcon},
{"floppy", kGenericFloppyIcon},
{"ramdisk", kGenericRAMDiskIcon},
{"cdrom", kGenericCDROMIcon},
{"preferences", kGenericPreferencesIcon},
{"querydoc", kGenericQueryDocumentIcon},
{"stop", kAlertStopIcon},
{"note", kAlertNoteIcon},
{"caution", kAlertCautionIcon},
{NULL, 0}
};
#define builtInIconSize 32
#define OSTYPE_TO_UTI(x) ((NSString *)UTTypeCreatePreferredIdentifierForTag( \
kUTTagClassOSType, UTCreateStringForOSType(x), nil))
static Tcl_HashTable iconBitmapTable = {};
typedef struct {
int kind, width, height;
char *value;
} IconBitmap;
static const char *const iconBitmapOptionStrings[] = {
"-file", "-fileType", "-imageFile", "-namedImage", "-osType",
"-systemType", NULL
};
enum iconBitmapOptions {
ICON_FILE, ICON_FILETYPE, ICON_IMAGEFILE, ICON_NAMEDIMAGE, ICON_OSTYPE,
ICON_SYSTEMTYPE
};
/*
*----------------------------------------------------------------------
*
* TkpDefineNativeBitmaps --
*
* Add native bitmaps.
*
* Results:
* A standard Tcl result. If an error occurs then TCL_ERROR is
* returned and a message is left in the interp's result.
*
* Side effects:
* "Name" is entered into the bitmap table and may be used from
* here on to refer to the given bitmap.
*
*----------------------------------------------------------------------
*/
void
TkpDefineNativeBitmaps(void)
{
Tcl_HashTable *tablePtr = TkGetBitmapPredefTable();
BuiltInIcon *builtInPtr;
for (builtInPtr = builtInIcons; builtInPtr->name != NULL; builtInPtr++) {
Tcl_HashEntry *predefHashPtr;
Tk_Uid name;
int isNew;
name = Tk_GetUid(builtInPtr->name);
predefHashPtr = Tcl_CreateHashEntry(tablePtr, name, &isNew);
if (isNew) {
TkPredefBitmap *predefPtr = (TkPredefBitmap *)ckalloc(sizeof(TkPredefBitmap));
predefPtr->source = UINT2PTR(builtInPtr->iconType);
predefPtr->width = builtInIconSize;
predefPtr->height = builtInIconSize;
predefPtr->native = 1;
Tcl_SetHashValue(predefHashPtr, predefPtr);
}
}
}
/*
*----------------------------------------------------------------------
*
* PixmapFromImage --
*
* Results:
* Returns a Pixmap with an NSImage drawn into it.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Pixmap
PixmapFromImage(
Display *display,
NSImage* image,
CGSize size)
{
TkMacOSXDrawingContext dc;
Pixmap pixmap;
pixmap = Tk_GetPixmap(display, None, (int)size.width, (int)size.height, 0);
if (TkMacOSXSetupDrawingContext(pixmap, NULL, &dc)) {
if (dc.context) {
CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1,
.tx = 0, .ty = size.height};
CGContextConcatCTM(dc.context, t);
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:GET_NSCONTEXT(dc.context, NO)];
[image drawAtPoint:NSZeroPoint fromRect:NSZeroRect
operation:NSCompositeCopy fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
}
TkMacOSXRestoreDrawingContext(&dc);
}
return pixmap;
}
/*
*----------------------------------------------------------------------
*
* TkpCreateNativeBitmap --
*
* Create native bitmap.
*
* Results:
* Native bitmap.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Pixmap
TkpCreateNativeBitmap(
Display *display,
const void *source) /* Info about the icon to build. */
{
NSString *filetype = [NSString stringWithUTF8String:(char *)source];
NSImage *iconImage = TkMacOSXIconForFileType(filetype);
CGSize size = CGSizeMake(builtInIconSize, builtInIconSize);
Pixmap pixmap = PixmapFromImage(display, iconImage, size);
return pixmap;
}
/*
*----------------------------------------------------------------------
*
* OSTypeFromString --
*
* Helper to convert string to OSType.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* t is set to OSType if conversion successful.
*
*----------------------------------------------------------------------
*/
static int
OSTypeFromString(const char *s, OSType *t) {
int result = TCL_ERROR;
Tcl_DString ds;
Tcl_Encoding encoding = Tcl_GetEncoding(NULL, "macRoman");
(void)Tcl_UtfToExternalDString(encoding, s, TCL_INDEX_NONE, &ds);
if (Tcl_DStringLength(&ds) <= 4) {
char string[4] = {};
memcpy(string, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
*t = (OSType) string[0] << 24 | (OSType) string[1] << 16 |
(OSType) string[2] << 8 | (OSType) string[3];
result = TCL_OK;
}
Tcl_DStringFree(&ds);
Tcl_FreeEncoding(encoding);
return result;
}
/*
*----------------------------------------------------------------------
*
* TkpGetNativeAppBitmap --
*
* Get a named native bitmap.
*
* Attemps to interpret the given name in order as:
* - name defined by ::tk::mac::iconBitmap
* - NSImage named image name
* - NSImage url string
* - 4-char OSType of IconServices icon
*
* Results:
* Native bitmap or None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Pixmap
TkpGetNativeAppBitmap(
Display *display, /* The display. */
const char *name, /* The name of the bitmap. */
int *width, /* The width & height of the bitmap. */
int *height)
{
Tcl_HashEntry *hPtr;
Pixmap pixmap = None;
NSString *string;
NSImage *image = nil;
NSSize size = { .width = builtInIconSize, .height = builtInIconSize };
if (iconBitmapTable.buckets &&
(hPtr = Tcl_FindHashEntry(&iconBitmapTable, name))) {
OSType type;
IconBitmap *iconBitmap = (IconBitmap *)Tcl_GetHashValue(hPtr);
name = NULL;
size = NSMakeSize(iconBitmap->width, iconBitmap->height);
switch (iconBitmap->kind) {
case ICON_FILE:
string = [[NSString stringWithUTF8String:iconBitmap->value]
stringByExpandingTildeInPath];
image = [[NSWorkspace sharedWorkspace] iconForFile:string];
break;
case ICON_FILETYPE:
string = [NSString stringWithUTF8String:iconBitmap->value];
image = TkMacOSXIconForFileType(string);
break;
case ICON_OSTYPE:
if (OSTypeFromString(iconBitmap->value, &type) == TCL_OK) {
string = [NSString stringWithUTF8String:iconBitmap->value];
image = TkMacOSXIconForFileType(string);
}
break;
case ICON_SYSTEMTYPE:
name = iconBitmap->value;
break;
case ICON_NAMEDIMAGE:
string = [NSString stringWithUTF8String:iconBitmap->value];
image = [NSImage imageNamed:string];
break;
case ICON_IMAGEFILE:
string = [[NSString stringWithUTF8String:iconBitmap->value]
stringByExpandingTildeInPath];
image = [[[NSImage alloc] initWithContentsOfFile:string]
autorelease];
break;
}
if (image) {
[image setSize:size];
}
} else {
string = [NSString stringWithUTF8String:name];
image = [NSImage imageNamed:string];
if (!image) {
NSURL *url = [NSURL fileURLWithPath:string];
if (url) {
image = [[[NSImage alloc] initWithContentsOfURL:url]
autorelease];
}
}
if (image) {
size = [image size];
}
}
if (image) {
pixmap = PixmapFromImage(display, image, NSSizeToCGSize(size));
} else if (name) {
/*
* As a last resort, try to interpret the name as an OSType.
* It would probably be better to just return None at this
* point.
*/
OSType iconType;
if (OSTypeFromString(name, &iconType) == TCL_OK) {
NSString *iconUTI = TkMacOSXOSTypeToUTI(iconType);
NSImage *iconImage = TkMacOSXIconForFileType(iconUTI);
pixmap = PixmapFromImage(display, iconImage, NSSizeToCGSize(size));
}
}
*width = (int)size.width;
*height = (int)size.height;
return pixmap;
}
/*
*----------------------------------------------------------------------
*
* TkMacOSXIconBitmapObjCmd --
*
* Implements the ::tk::mac::iconBitmap command.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* none
*
*----------------------------------------------------------------------
*/
int
TkMacOSXIconBitmapObjCmd(
TCL_UNUSED(void *),
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
Tcl_HashEntry *hPtr;
int isNew, result = TCL_ERROR;
Tcl_Size i = 1, len;
const char *name, *value;
IconBitmap ib, *iconBitmap;
if (objc != 6) {
Tcl_WrongNumArgs(interp, 1, objv, "name width height "
"-file|-fileType|-osType|-systemType|-namedImage|-imageFile "
"value");
goto end;
}
name = Tcl_GetStringFromObj(objv[i++], &len);
if (!len) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("empty bitmap name", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "MACBITMAP", "BAD", NULL);
goto end;
}
if (Tcl_GetIntFromObj(interp, objv[i++], &ib.width) != TCL_OK) {
goto end;
}
if (Tcl_GetIntFromObj(interp, objv[i++], &ib.height) != TCL_OK) {
goto end;
}
if (Tcl_GetIndexFromObjStruct(interp, objv[i++], iconBitmapOptionStrings,
sizeof(char *), "kind", TCL_EXACT, &ib.kind) != TCL_OK) {
goto end;
}
value = Tcl_GetStringFromObj(objv[i++], &len);
if (!len) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("empty bitmap value", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "MACBITMAP", "EMPTY", NULL);
goto end;
}
#if 0
if ((kind == ICON_TYPE || kind == ICON_SYSTEM)) {
Tcl_DString ds;
Tcl_Encoding encoding = Tcl_GetEncoding(NULL, "macRoman");
(void)Tcl_UtfToExternalDString(encoding, value, TCL_INDEX_NONE, &ds);
len = Tcl_DStringLength(&ds);
Tcl_DStringFree(&ds);
Tcl_FreeEncoding(encoding);
if (len > 4) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
"invalid bitmap value", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "MACBITMAP", "INVALID", NULL);
goto end;
}
}
#endif
ib.value = (char *)ckalloc(len + 1);
strcpy(ib.value, value);
if (!iconBitmapTable.buckets) {
Tcl_InitHashTable(&iconBitmapTable, TCL_STRING_KEYS);
}
hPtr = Tcl_CreateHashEntry(&iconBitmapTable, name, &isNew);
if (!isNew) {
iconBitmap = (IconBitmap *)Tcl_GetHashValue(hPtr);
ckfree(iconBitmap->value);
} else {
iconBitmap = (IconBitmap *)ckalloc(sizeof(IconBitmap));
Tcl_SetHashValue(hPtr, iconBitmap);
}
*iconBitmap = ib;
result = TCL_OK;
end:
return result;
}
/*
* Local Variables:
* mode: objc
* c-basic-offset: 4
* fill-column: 79
* coding: utf-8
* End:
*/