1/*
2 * tkMacOSXColor.c --
3 *
4 *	This file maintains a database of color values for the Tk
5 *	toolkit, in order to avoid round-trips to the server to
6 *	map color names to pixel values.
7 *
8 * Copyright (c) 1990-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1996 Sun Microsystems, Inc.
10 * Copyright 2001-2009, Apple Inc.
11 * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
12 *
13 * See the file "license.terms" for information on usage and redistribution
14 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 *
16 * RCS: @(#) $Id$
17 */
18
19#include "tkMacOSXPrivate.h"
20#include "tkColor.h"
21
22struct SystemColorMapEntry {
23    const char *name;
24    ThemeBrush brush;
25    ThemeTextColor textColor;
26    ThemeBackgroundKind background;
27};  /* unsigned char pixelCode; */
28
29/*
30 * Array of system color definitions: the array index is required to equal the
31 * color's (pixelCode - MIN_PIXELCODE), i.e. the array order needs to be kept
32 * in sync with the public pixel code values in tkMacOSXPort.h !
33 */
34
35#define MIN_PIXELCODE  30
36static const struct SystemColorMapEntry systemColorMap[] = {
37    { "Transparent",			    0, 0, 0 },							/*  30: TRANSPARENT_PIXEL */
38    { "Highlight",			    kThemeBrushPrimaryHighlightColor, 0, 0 },			/*  31: HIGHLIGHT_PIXEL */
39    { "HighlightSecondary",		    kThemeBrushSecondaryHighlightColor, 0, 0 },			/*  32: HIGHLIGHT_SECONDARY_PIXEL */
40    { "HighlightText",			    kThemeBrushBlack, 0, 0 },					/*  33: HIGHLIGHT_TEXT_PIXEL */
41    { "HighlightAlternate",		    kThemeBrushAlternatePrimaryHighlightColor, 0, 0 },		/*  34: HIGHLIGHT_ALTERNATE_PIXEL */
42    { "ButtonText",			    0, kThemeTextColorPushButtonActive, 0 },			/*  35: CONTROL_TEXT_PIXEL */
43    { "PrimaryHighlightColor",		    kThemeBrushPrimaryHighlightColor, 0, 0 },			/*  36 */
44    { "ButtonFace",			    kThemeBrushButtonFaceActive, 0, 0 },			/*  37: CONTROL_BODY_PIXEL */
45    { "SecondaryHighlightColor",	    kThemeBrushSecondaryHighlightColor, 0, 0 },			/*  38 */
46    { "ButtonFrame",			    kThemeBrushButtonFrameActive, 0, 0 },			/*  39: CONTROL_FRAME_PIXEL */
47    { "AlternatePrimaryHighlightColor",	    kThemeBrushAlternatePrimaryHighlightColor, 0, 0 },		/*  40 */
48    { "WindowBody",			    kThemeBrushDocumentWindowBackground, 0, 0 },		/*  41: WINDOW_BODY_PIXEL */
49    { "SheetBackground",		    kThemeBrushSheetBackground, 0, 0 },				/*  42 */
50    { "MenuActive",			    kThemeBrushMenuBackgroundSelected, 0, 0 },			/*  43: MENU_ACTIVE_PIXEL */
51    { "Black",				    kThemeBrushBlack, 0, 0 },					/*  44 */
52    { "MenuActiveText",			    0, kThemeTextColorMenuItemSelected, 0 },			/*  45: MENU_ACTIVE_TEXT_PIXEL */
53    { "White",				    kThemeBrushWhite, 0, 0 },					/*  46 */
54    { "Menu",				    kThemeBrushMenuBackground, 0, 0 },				/*  47: MENU_BACKGROUND_PIXEL */
55    { "DialogBackgroundActive",		    kThemeBrushDialogBackgroundActive, 0, 0 },			/*  48 */
56    { "MenuDisabled",			    0, kThemeTextColorMenuItemDisabled, 0 },			/*  49: MENU_DISABLED_PIXEL */
57    { "DialogBackgroundInactive",	    kThemeBrushDialogBackgroundInactive, 0, 0 },		/*  50 */
58    { "MenuText",			    0, kThemeTextColorMenuItemActive, 0 },			/*  51: MENU_TEXT_PIXEL */
59    { "AppearanceColor",		    0, 0, 0 },							/*  52: APPEARANCE_PIXEL */
60    { "AlertBackgroundActive",		    kThemeBrushAlertBackgroundActive, 0, 0 },			/*  53 */
61    { "AlertBackgroundInactive",	    kThemeBrushAlertBackgroundInactive, 0, 0 },			/*  54 */
62    { "ModelessDialogBackgroundActive",	    kThemeBrushModelessDialogBackgroundActive, 0, 0 },		/*  55 */
63    { "ModelessDialogBackgroundInactive",   kThemeBrushModelessDialogBackgroundInactive, 0, 0 },	/*  56 */
64    { "UtilityWindowBackgroundActive",	    kThemeBrushUtilityWindowBackgroundActive, 0, 0 },		/*  57 */
65    { "UtilityWindowBackgroundInactive",    kThemeBrushUtilityWindowBackgroundInactive, 0, 0 },		/*  58 */
66    { "ListViewSortColumnBackground",	    kThemeBrushListViewSortColumnBackground, 0, 0 },		/*  59 */
67    { "ListViewBackground",		    kThemeBrushListViewBackground, 0, 0 },			/*  60 */
68    { "IconLabelBackground",		    kThemeBrushIconLabelBackground, 0, 0 },			/*  61 */
69    { "ListViewSeparator",		    kThemeBrushListViewSeparator, 0, 0 },			/*  62 */
70    { "ChasingArrows",			    kThemeBrushChasingArrows, 0, 0 },				/*  63 */
71    { "DragHilite",			    kThemeBrushDragHilite, 0, 0 },				/*  64 */
72    { "DocumentWindowBackground",	    kThemeBrushDocumentWindowBackground, 0, 0 },		/*  65 */
73    { "FinderWindowBackground",		    kThemeBrushFinderWindowBackground, 0, 0 },			/*  66 */
74    { "ScrollBarDelimiterActive",	    kThemeBrushScrollBarDelimiterActive, 0, 0 },		/*  67 */
75    { "ScrollBarDelimiterInactive",	    kThemeBrushScrollBarDelimiterInactive, 0, 0 },		/*  68 */
76    { "FocusHighlight",			    kThemeBrushFocusHighlight, 0, 0 },				/*  69 */
77    { "PopupArrowActive",		    kThemeBrushPopupArrowActive, 0, 0 },			/*  70 */
78    { "PopupArrowPressed",		    kThemeBrushPopupArrowPressed, 0, 0 },			/*  71 */
79    { "PopupArrowInactive",		    kThemeBrushPopupArrowInactive, 0, 0 },			/*  72 */
80    { "AppleGuideCoachmark",		    kThemeBrushAppleGuideCoachmark, 0, 0 },			/*  73 */
81    { "IconLabelBackgroundSelected",	    kThemeBrushIconLabelBackgroundSelected, 0, 0 },		/*  74 */
82    { "StaticAreaFill",			    kThemeBrushStaticAreaFill, 0, 0 },				/*  75 */
83    { "ActiveAreaFill",			    kThemeBrushActiveAreaFill, 0, 0 },				/*  76 */
84    { "ButtonFrameActive",		    kThemeBrushButtonFrameActive, 0, 0 },			/*  77 */
85    { "ButtonFrameInactive",		    kThemeBrushButtonFrameInactive, 0, 0 },			/*  78 */
86    { "ButtonFaceActive",		    kThemeBrushButtonFaceActive, 0, 0 },			/*  79 */
87    { "ButtonFaceInactive",		    kThemeBrushButtonFaceInactive, 0, 0 },			/*  80 */
88    { "ButtonFacePressed",		    kThemeBrushButtonFacePressed, 0, 0 },			/*  81 */
89    { "ButtonActiveDarkShadow",		    kThemeBrushButtonActiveDarkShadow, 0, 0 },			/*  82 */
90    { "ButtonActiveDarkHighlight",	    kThemeBrushButtonActiveDarkHighlight, 0, 0 },		/*  83 */
91    { "ButtonActiveLightShadow",	    kThemeBrushButtonActiveLightShadow, 0, 0 },			/*  84 */
92    { "ButtonActiveLightHighlight",	    kThemeBrushButtonActiveLightHighlight, 0, 0 },		/*  85 */
93    { "ButtonInactiveDarkShadow",	    kThemeBrushButtonInactiveDarkShadow, 0, 0 },		/*  86 */
94    { "ButtonInactiveDarkHighlight",	    kThemeBrushButtonInactiveDarkHighlight, 0, 0 },		/*  87 */
95    { "ButtonInactiveLightShadow",	    kThemeBrushButtonInactiveLightShadow, 0, 0 },		/*  88 */
96    { "ButtonInactiveLightHighlight",	    kThemeBrushButtonInactiveLightHighlight, 0, 0 },		/*  89 */
97    { "ButtonPressedDarkShadow",	    kThemeBrushButtonPressedDarkShadow, 0, 0 },			/*  90 */
98    { "ButtonPressedDarkHighlight",	    kThemeBrushButtonPressedDarkHighlight, 0, 0 },		/*  91 */
99    { "ButtonPressedLightShadow",	    kThemeBrushButtonPressedLightShadow, 0, 0 },		/*  92 */
100    { "ButtonPressedLightHighlight",	    kThemeBrushButtonPressedLightHighlight, 0, 0 },		/*  93 */
101    { "BevelActiveLight",		    kThemeBrushBevelActiveLight, 0, 0 },			/*  94 */
102    { "BevelActiveDark",		    kThemeBrushBevelActiveDark, 0, 0 },				/*  95 */
103    { "BevelInactiveLight",		    kThemeBrushBevelInactiveLight, 0, 0 },			/*  96 */
104    { "BevelInactiveDark",		    kThemeBrushBevelInactiveDark, 0, 0 },			/*  97 */
105    { "NotificationWindowBackground",	    kThemeBrushNotificationWindowBackground, 0, 0 },		/*  98 */
106    { "MovableModalBackground",		    kThemeBrushMovableModalBackground, 0, 0 },			/*  99 */
107    { "SheetBackgroundOpaque",		    kThemeBrushSheetBackgroundOpaque, 0, 0 },			/* 100 */
108    { "DrawerBackground",		    kThemeBrushDrawerBackground, 0, 0 },			/* 101 */
109    { "ToolbarBackground",		    kThemeBrushToolbarBackground, 0, 0 },			/* 102 */
110    { "SheetBackgroundTransparent",	    kThemeBrushSheetBackgroundTransparent, 0, 0 },		/* 103 */
111    { "MenuBackground",			    kThemeBrushMenuBackground, 0, 0 },				/* 104 */
112    { "Pixel",				    0, 0, 0 },							/* 105: PIXEL_MAGIC */
113    { "MenuBackgroundSelected",		    kThemeBrushMenuBackgroundSelected, 0, 0 },			/* 106 */
114    { "ListViewOddRowBackground",	    kThemeBrushListViewOddRowBackground, 0, 0 },		/* 107 */
115    { "ListViewEvenRowBackground",	    kThemeBrushListViewEvenRowBackground, 0, 0 },		/* 108 */
116    { "ListViewColumnDivider",		    kThemeBrushListViewColumnDivider, 0, 0 },			/* 109 */
117    { "BlackText",			    0, kThemeTextColorBlack, 0 },				/* 110 */
118    { "DialogActiveText",		    0, kThemeTextColorDialogActive, 0 },			/* 111 */
119    { "DialogInactiveText",		    0, kThemeTextColorDialogInactive, 0 },			/* 112 */
120    { "AlertActiveText",		    0, kThemeTextColorAlertActive, 0 },				/* 113 */
121    { "AlertInactiveText",		    0, kThemeTextColorAlertInactive, 0 },			/* 114 */
122    { "ModelessDialogActiveText",	    0, kThemeTextColorModelessDialogActive, 0 },		/* 115 */
123    { "ModelessDialogInactiveText",	    0, kThemeTextColorModelessDialogInactive, 0 },		/* 116 */
124    { "WindowHeaderActiveText",		    0, kThemeTextColorWindowHeaderActive, 0 },			/* 117 */
125    { "WindowHeaderInactiveText",	    0, kThemeTextColorWindowHeaderInactive, 0 },		/* 118 */
126    { "PlacardActiveText",		    0, kThemeTextColorPlacardActive, 0 },			/* 119 */
127    { "PlacardInactiveText",		    0, kThemeTextColorPlacardInactive, 0 },			/* 120 */
128    { "PlacardPressedText",		    0, kThemeTextColorPlacardPressed, 0 },			/* 121 */
129    { "PushButtonActiveText",		    0, kThemeTextColorPushButtonActive, 0 },			/* 122 */
130    { "PushButtonInactiveText",		    0, kThemeTextColorPushButtonInactive, 0 },			/* 123 */
131    { "PushButtonPressedText",		    0, kThemeTextColorPushButtonPressed, 0 },			/* 124 */
132    { "BevelButtonActiveText",		    0, kThemeTextColorBevelButtonActive, 0 },			/* 125 */
133    { "BevelButtonInactiveText",	    0, kThemeTextColorBevelButtonInactive, 0 },			/* 126 */
134    { "BevelButtonPressedText",		    0, kThemeTextColorBevelButtonPressed, 0 },			/* 127 */
135    { "PopupButtonActiveText",		    0, kThemeTextColorPopupButtonActive, 0 },			/* 128 */
136    { "PopupButtonInactiveText",	    0, kThemeTextColorPopupButtonInactive, 0 },			/* 129 */
137    { "PopupButtonPressedText",		    0, kThemeTextColorPopupButtonPressed, 0 },			/* 130 */
138    { "IconLabelText",			    0, kThemeTextColorIconLabel, 0 },				/* 131 */
139    { "ListViewText",			    0, kThemeTextColorListView, 0 },				/* 132 */
140    { "DocumentWindowTitleActiveText",	    0, kThemeTextColorDocumentWindowTitleActive, 0 },		/* 133 */
141    { "DocumentWindowTitleInactiveText",    0, kThemeTextColorDocumentWindowTitleInactive, 0 },		/* 134 */
142    { "MovableModalWindowTitleActiveText",  0, kThemeTextColorMovableModalWindowTitleActive, 0 },	/* 135 */
143    { "MovableModalWindowTitleInactiveText",0, kThemeTextColorMovableModalWindowTitleInactive, 0 },	/* 136 */
144    { "UtilityWindowTitleActiveText",	    0, kThemeTextColorUtilityWindowTitleActive, 0 },		/* 137 */
145    { "UtilityWindowTitleInactiveText",	    0, kThemeTextColorUtilityWindowTitleInactive, 0 },		/* 138 */
146    { "PopupWindowTitleActiveText",	    0, kThemeTextColorPopupWindowTitleActive, 0 },		/* 139 */
147    { "PopupWindowTitleInactiveText",	    0, kThemeTextColorPopupWindowTitleInactive, 0 },		/* 140 */
148    { "RootMenuActiveText",		    0, kThemeTextColorRootMenuActive, 0 },			/* 141 */
149    { "RootMenuSelectedText",		    0, kThemeTextColorRootMenuSelected, 0 },			/* 142 */
150    { "RootMenuDisabledText",		    0, kThemeTextColorRootMenuDisabled, 0 },			/* 143 */
151    { "MenuItemActiveText",		    0, kThemeTextColorMenuItemActive, 0 },			/* 144 */
152    { "MenuItemSelectedText",		    0, kThemeTextColorMenuItemSelected, 0 },			/* 145 */
153    { "MenuItemDisabledText",		    0, kThemeTextColorMenuItemDisabled, 0 },			/* 146 */
154    { "PopupLabelActiveText",		    0, kThemeTextColorPopupLabelActive, 0 },			/* 147 */
155    { "PopupLabelInactiveText",		    0, kThemeTextColorPopupLabelInactive, 0 },			/* 148 */
156    { "TabFrontActiveText",		    0, kThemeTextColorTabFrontActive, 0 },			/* 149 */
157    { "TabNonFrontActiveText",		    0, kThemeTextColorTabNonFrontActive, 0 },			/* 150 */
158    { "TabNonFrontPressedText",		    0, kThemeTextColorTabNonFrontPressed, 0 },			/* 151 */
159    { "TabFrontInactiveText",		    0, kThemeTextColorTabFrontInactive, 0 },			/* 152 */
160    { "TabNonFrontInactiveText",	    0, kThemeTextColorTabNonFrontInactive, 0 },			/* 153 */
161    { "IconLabelSelectedText",		    0, kThemeTextColorIconLabelSelected, 0 },			/* 154 */
162    { "BevelButtonStickyActiveText",	    0, kThemeTextColorBevelButtonStickyActive, 0 },		/* 155 */
163    { "BevelButtonStickyInactiveText",	    0, kThemeTextColorBevelButtonStickyInactive, 0 },		/* 156 */
164    { "NotificationText",		    0, kThemeTextColorNotification, 0 },			/* 157 */
165    { "SystemDetailText",		    0, kThemeTextColorSystemDetail, 0 },			/* 158 */
166    { "WhiteText",			    0, kThemeTextColorWhite, 0 },				/* 159 */
167    { "TabPaneBackground",		    0, 0, kThemeBackgroundTabPane },				/* 160 */
168    { "PlacardBackground",		    0, 0, kThemeBackgroundPlacard },				/* 161 */
169    { "WindowHeaderBackground",		    0, 0, kThemeBackgroundWindowHeader },			/* 162 */
170    { "ListViewWindowHeaderBackground",	    0, 0, kThemeBackgroundListViewWindowHeader },		/* 163 */
171    { "SecondaryGroupBoxBackground",	    0, 0, kThemeBackgroundSecondaryGroupBox },			/* 164 */
172    { "MetalBackground",		    0, 0, kThemeBackgroundMetal },				/* 165 */
173    { NULL,				    0, 0, 0 }
174};
175#define MAX_PIXELCODE 165
176
177/*
178 *----------------------------------------------------------------------
179 *
180 * GetThemeFromPixelCode --
181 *
182 *	When given a pixel code corresponding to a theme system color,
183 *	set one of brush, textColor or background to the corresponding
184 *	Appearance Mgr theme constant.
185 *
186 * Results:
187 *	Returns false if not a real pixel, true otherwise.
188 *
189 * Side effects:
190 *	None.
191 *
192 *----------------------------------------------------------------------
193 */
194
195static int
196GetThemeFromPixelCode(
197    unsigned char code,
198    ThemeBrush *brush,
199    ThemeTextColor *textColor,
200    ThemeBackgroundKind *background)
201{
202    if (code >= MIN_PIXELCODE && code <= MAX_PIXELCODE) {
203	*brush = systemColorMap[code - MIN_PIXELCODE].brush;
204	*textColor = systemColorMap[code - MIN_PIXELCODE].textColor;
205	*background = systemColorMap[code - MIN_PIXELCODE].background;
206    } else {
207	*brush = 0;
208	*textColor = 0;
209	*background = 0;
210    }
211    if (!*brush && !*textColor && !*background && code != PIXEL_MAGIC &&
212	    code != TRANSPARENT_PIXEL) {
213	return false;
214    } else {
215	return true;
216    }
217}
218
219/*
220 *----------------------------------------------------------------------
221 *
222 * GetThemeColor --
223 *
224 *	Get RGB color for a given system color or pixel value.
225 *
226 * Results:
227 *	OSStatus
228 *
229 * Side effects:
230 *	None.
231 *
232 *----------------------------------------------------------------------
233 */
234
235static OSStatus
236GetThemeColor(
237    unsigned long pixel,
238    ThemeBrush brush,
239    ThemeTextColor textColor,
240    ThemeBackgroundKind background,
241    CGColorRef *c)
242{
243    OSStatus err = noErr;
244
245    if (brush) {
246	err = ChkErr(HIThemeBrushCreateCGColor, brush, c);
247    /*} else if (textColor) {
248	err = ChkErr(GetThemeTextColor, textColor, 32, true, c);*/
249    } else {
250	CGFloat rgba[4] = {0, 0, 0, 1};
251
252	switch ((pixel >> 24) & 0xff) {
253	case PIXEL_MAGIC: {
254	    unsigned short red, green, blue;
255	    red		= (pixel >> 16) & 0xff;
256	    green	= (pixel >>  8) & 0xff;
257	    blue	= (pixel      ) & 0xff;
258	    red		|= red   << 8;
259	    green	|= green << 8;
260	    blue	|= blue  << 8;
261	    rgba[0]	= red	/ 65535.0;
262	    rgba[1]	= green / 65535.0;
263	    rgba[2]	= blue  / 65535.0;
264	    break;
265	    }
266	case TRANSPARENT_PIXEL:
267	    rgba[3]	= 0.0;
268	    break;
269	}
270
271	*c = CGColorCreateGenericRGB(rgba[0], rgba[1], rgba[2], rgba[3]);
272
273	/*static CGColorSpaceRef deviceRGBSpace = NULL;
274	if (!deviceRGBSpace) {
275	    deviceRGBSpace = CGDisplayCopyColorSpace(CGMainDisplayID());
276	}
277	*c = CGColorCreate(deviceRGBSpace, rgba );*/
278    }
279    return err;
280}
281
282/*
283 *----------------------------------------------------------------------
284 *
285 * TkSetMacColor --
286 *
287 *	Creates a CGColorRef from a X style pixel value.
288 *
289 * Results:
290 *	Returns false if not a real pixel, true otherwise.
291 *
292 * Side effects:
293 *	The variable macColor is set to a new CGColorRef, the caller is
294 *	responsible for releasing it!
295 *
296 *----------------------------------------------------------------------
297 */
298
299int
300TkSetMacColor(
301    unsigned long pixel,		/* Pixel value to convert. */
302    void *macColor)			/* CGColorRef to modify. */
303{
304    CGColorRef *color = (CGColorRef*)macColor;
305    OSStatus err = -1;
306    ThemeBrush brush;
307    ThemeTextColor textColor;
308    ThemeBackgroundKind background;
309
310    if (GetThemeFromPixelCode((pixel >> 24) & 0xff, &brush, &textColor,
311	    &background)) {
312	err = ChkErr(GetThemeColor, pixel, brush, textColor, background,
313		color);
314    }
315    return (err == noErr);
316}
317
318/*
319 *----------------------------------------------------------------------
320 *
321 * TkpInitGCCache, TkpFreeGCCache, CopyCachedColor, SetCachedColor --
322 *
323 *	Maintain a per-GC cache of previously converted CGColorRefs
324 *
325 * Results:
326 *	None resp. retained CGColorRef for CopyCachedColor()
327 *
328 * Side effects:
329 *	None.
330 *
331 *----------------------------------------------------------------------
332 */
333
334void
335TkpInitGCCache(
336    GC gc)
337{
338    bzero(TkpGetGCCache(gc), sizeof(TkpGCCache));
339}
340
341void
342TkpFreeGCCache(
343    GC gc)
344{
345    TkpGCCache *gcCache = TkpGetGCCache(gc);
346
347    if (gcCache->cachedForegroundColor) {
348	CFRelease(gcCache->cachedForegroundColor);
349    }
350    if (gcCache->cachedBackgroundColor) {
351	CFRelease(gcCache->cachedBackgroundColor);
352    }
353}
354
355static CGColorRef
356CopyCachedColor(
357    GC gc,
358    unsigned long pixel)
359{
360    TkpGCCache *gcCache = TkpGetGCCache(gc);
361    CGColorRef cgColor = NULL;
362
363    if (gcCache) {
364	if (gcCache->cachedForeground == pixel) {
365	    cgColor = gcCache->cachedForegroundColor;
366	} else if (gcCache->cachedBackground == pixel) {
367	    cgColor = gcCache->cachedBackgroundColor;
368	}
369	if (cgColor) {
370	    CFRetain(cgColor);
371	}
372    }
373    return cgColor;
374}
375
376static void
377SetCachedColor(
378    GC gc,
379    unsigned long pixel,
380    CGColorRef cgColor)
381{
382    TkpGCCache *gcCache = TkpGetGCCache(gc);
383
384    if (gcCache && cgColor) {
385	if (gc->foreground == pixel) {
386	    if (gcCache->cachedForegroundColor) {
387		CFRelease(gcCache->cachedForegroundColor);
388	    }
389	    gcCache->cachedForegroundColor = (CGColorRef) CFRetain(cgColor);
390	    gcCache->cachedForeground = pixel;
391	} else if (gc->background == pixel) {
392	    if (gcCache->cachedBackgroundColor) {
393		CFRelease(gcCache->cachedBackgroundColor);
394	    }
395	    gcCache->cachedBackgroundColor = (CGColorRef) CFRetain(cgColor);
396	    gcCache->cachedBackground = pixel;
397	}
398    }
399}
400
401/*
402 *----------------------------------------------------------------------
403 *
404 * TkMacOSXCreateCGColor --
405 *
406 *	Creates a CGColorRef from a X style pixel value.
407 *
408 * Results:
409 *	Returns NULL if not a real pixel, CGColorRef otherwise.
410 *
411 * Side effects:
412 *	None
413 *
414 *----------------------------------------------------------------------
415 */
416
417CGColorRef
418TkMacOSXCreateCGColor(
419    GC gc,
420    unsigned long pixel)		/* Pixel value to convert. */
421{
422    CGColorRef cgColor = CopyCachedColor(gc, pixel);
423
424    if (!cgColor && TkSetMacColor(pixel, &cgColor)) {
425	SetCachedColor(gc, pixel, cgColor);
426    }
427    return cgColor;
428}
429
430/*
431 *----------------------------------------------------------------------
432 *
433 * TkMacOSXGetNSColor --
434 *
435 *	Creates an autoreleased NSColor from a X style pixel value.
436 *
437 * Results:
438 *	Returns nil if not a real pixel, NSColor* otherwise.
439 *
440 * Side effects:
441 *	None
442 *
443 *----------------------------------------------------------------------
444 */
445
446NSColor*
447TkMacOSXGetNSColor(
448    GC gc,
449    unsigned long pixel)		/* Pixel value to convert. */
450{
451    CGColorRef cgColor = TkMacOSXCreateCGColor(gc, pixel);
452    NSColor *nsColor = nil;
453
454    if (cgColor) {
455	NSColorSpace *colorSpace = [[NSColorSpace alloc]
456		initWithCGColorSpace:CGColorGetColorSpace(cgColor)];
457	nsColor = [NSColor colorWithColorSpace:colorSpace
458		components:CGColorGetComponents(cgColor)
459		count:CGColorGetNumberOfComponents(cgColor)];
460	[colorSpace release];
461	CFRelease(cgColor);
462    }
463    return nsColor;
464}
465
466/*
467 *----------------------------------------------------------------------
468 *
469 * TkMacOSXSetColorInContext --
470 *
471 *	Sets fill and stroke color in the given CG context from an X
472 *	pixel value, or if the pixel code indicates a system color,
473 *	sets the corresponding brush, textColor or background via
474 *	HITheme APIs if available or Appearance mgr APIs.
475 *
476 * Results:
477 *	None.
478 *
479 * Side effects:
480 *	None.
481 *
482 *----------------------------------------------------------------------
483 */
484
485void
486TkMacOSXSetColorInContext(
487    GC gc,
488    unsigned long pixel,
489    CGContextRef context)
490{
491    OSStatus err = -1;
492    CGColorRef cgColor = CopyCachedColor(gc, pixel);
493    ThemeBrush brush;
494    ThemeTextColor textColor;
495    ThemeBackgroundKind background;
496
497    if (!cgColor && GetThemeFromPixelCode((pixel >> 24) & 0xff, &brush,
498	    &textColor, &background)) {
499	if (brush) {
500	    err = ChkErr(HIThemeSetFill, brush, NULL, context,
501		    kHIThemeOrientationNormal);
502	    if (err == noErr) {
503		err = ChkErr(HIThemeSetStroke, brush, NULL, context,
504			kHIThemeOrientationNormal);
505	    }
506	} else if (textColor) {
507	    err = ChkErr(HIThemeSetTextFill, textColor, NULL, context,
508		    kHIThemeOrientationNormal);
509	} else if (background) {
510	    CGRect rect = CGContextGetClipBoundingBox(context);
511	    HIThemeBackgroundDrawInfo info = { 0, kThemeStateActive,
512		    background };
513
514	    err = ChkErr(HIThemeApplyBackground, &rect, &info,
515		    context, kHIThemeOrientationNormal);
516	}
517	if (err == noErr) {
518	    return;
519	}
520	err = ChkErr(GetThemeColor, pixel, brush, textColor, background,
521		&cgColor);
522	if (err == noErr) {
523	    SetCachedColor(gc, pixel, cgColor);
524	}
525    } else if (!cgColor) {
526	TkMacOSXDbgMsg("Ignored unknown pixel value 0x%lx", pixel);
527    }
528    if (cgColor) {
529	CGContextSetFillColorWithColor(context, cgColor);
530	CGContextSetStrokeColorWithColor(context, cgColor);
531	CGColorRelease(cgColor);
532    }
533}
534
535/*
536 *----------------------------------------------------------------------
537 *
538 * TkpGetColor --
539 *
540 *	Allocate a new TkColor for the color with the given name.
541 *
542 * Results:
543 *	Returns a newly allocated TkColor, or NULL on failure.
544 *
545 * Side effects:
546 *	May invalidate the colormap cache associated with tkwin upon
547 *	allocating a new colormap entry. Allocates a new TkColor
548 *	structure.
549 *
550 *----------------------------------------------------------------------
551 */
552
553TkColor *
554TkpGetColor(
555    Tk_Window tkwin,		/* Window in which color will be used. */
556    Tk_Uid name)		/* Name of color to allocated (in form
557				 * suitable for passing to XParseColor). */
558{
559    Display *display = tkwin != None ? Tk_Display(tkwin) : NULL;
560    Colormap colormap = tkwin!= None ? Tk_Colormap(tkwin) : None;
561    TkColor *tkColPtr;
562    XColor color;
563
564    /*
565     * Check to see if this is a system color. Otherwise, XParseColor
566     * will do all the work.
567     */
568    if (strncasecmp(name, "system", 6) == 0) {
569	Tcl_Obj *strPtr = Tcl_NewStringObj(name+6, -1);
570	int idx, result;
571
572	result = Tcl_GetIndexFromObjStruct(NULL, strPtr, systemColorMap,
573		    sizeof(struct SystemColorMapEntry), NULL, TCL_EXACT, &idx);
574	Tcl_DecrRefCount(strPtr);
575	if (result == TCL_OK) {
576	    OSStatus err;
577	    CGColorRef c;
578	    unsigned char pixelCode = idx + MIN_PIXELCODE;
579	    ThemeBrush brush = systemColorMap[idx].brush;
580	    ThemeTextColor textColor = systemColorMap[idx].textColor;
581	    ThemeBackgroundKind background = systemColorMap[idx].background;
582
583	    err = ChkErr(GetThemeColor, 0, brush, textColor, background, &c);
584	    if (err == noErr) {
585		const size_t n = CGColorGetNumberOfComponents(c);
586		const CGFloat *rgba = CGColorGetComponents(c);
587
588		switch (n) {
589		case 4:
590		    color.red   = rgba[0] * 65535.0;
591		    color.green = rgba[1] * 65535.0;
592		    color.blue  = rgba[2] * 65535.0;
593		    break;
594		case 2:
595		    color.red = color.green = color.blue = rgba[0] * 65535.0;
596		    break;
597		default:
598		    Tcl_Panic("CGColor with %d components", n);
599		}
600		color.pixel = ((((((pixelCode << 8)
601		    | ((color.red   >> 8) & 0xff)) << 8)
602		    | ((color.green >> 8) & 0xff)) << 8)
603		    | ((color.blue  >> 8) & 0xff));
604		CGColorRelease(c);
605		goto validXColor;
606	    }
607	    CGColorRelease(c);
608	}
609    }
610
611    if (XParseColor(display, colormap, name, &color) == 0) {
612	return (TkColor *) NULL;
613    }
614
615validXColor:
616    tkColPtr = (TkColor *) ckalloc(sizeof(TkColor));
617    tkColPtr->color = color;
618
619    return tkColPtr;
620}
621
622/*
623 *----------------------------------------------------------------------
624 *
625 * TkpGetColorByValue --
626 *
627 *	Given a desired set of red-green-blue intensities for a color,
628 *	locate a pixel value to use to draw that color in a given
629 *	window.
630 *
631 * Results:
632 *	The return value is a pointer to an TkColor structure that
633 *	indicates the closest red, blue, and green intensities available
634 *	to those specified in colorPtr, and also specifies a pixel
635 *	value to use to draw in that color.
636 *
637 * Side effects:
638 *	May invalidate the colormap cache for the specified window.
639 *	Allocates a new TkColor structure.
640 *
641 *----------------------------------------------------------------------
642 */
643
644TkColor *
645TkpGetColorByValue(
646    Tk_Window tkwin,		/* Window in which color will be used. */
647    XColor *colorPtr)		/* Red, green, and blue fields indicate
648				 * desired color. */
649{
650    TkColor *tkColPtr = (TkColor *) ckalloc(sizeof(TkColor));
651
652    tkColPtr->color.red = colorPtr->red;
653    tkColPtr->color.green = colorPtr->green;
654    tkColPtr->color.blue = colorPtr->blue;
655    tkColPtr->color.pixel = TkpGetPixel(&tkColPtr->color);
656    return tkColPtr;
657}
658
659/*
660 *----------------------------------------------------------------------
661 *
662 * Stub functions --
663 *
664 *	These functions are just stubs for functions that either
665 *	don't make sense on the Mac or have yet to be implemented.
666 *
667 * Results:
668 *	None.
669 *
670 * Side effects:
671 *	These calls do nothing - which may not be expected.
672 *
673 *----------------------------------------------------------------------
674 */
675
676Status
677XAllocColor(
678    Display *display,		/* Display. */
679    Colormap map,		/* Not used. */
680    XColor *colorPtr)		/* XColor struct to modify. */
681{
682    display->request++;
683    colorPtr->pixel = TkpGetPixel(colorPtr);
684    return 1;
685}
686
687Colormap
688XCreateColormap(
689    Display *display,		/* Display. */
690    Window window,		/* X window. */
691    Visual *visual,		/* Not used. */
692    int alloc)			/* Not used. */
693{
694    static Colormap index = 1;
695
696    /*
697     * Just return a new value each time.
698     */
699    return index++;
700}
701
702void
703XFreeColormap(
704    Display* display,		/* Display. */
705    Colormap colormap)		/* Colormap. */
706{
707}
708
709void
710XFreeColors(
711    Display* display,		/* Display. */
712    Colormap colormap,		/* Colormap. */
713    unsigned long* pixels,	/* Array of pixels. */
714    int npixels,		/* Number of pixels. */
715    unsigned long planes)	/* Number of pixel planes. */
716{
717    /*
718     * The Macintosh version of Tk uses TrueColor. Nothing
719     * needs to be done to release colors as there really is
720     * no colormap in the Tk sense.
721     */
722}
723
724/*
725 * Local Variables:
726 * mode: c
727 * c-basic-offset: 4
728 * fill-column: 79
729 * coding: utf-8
730 * End:
731 */
732