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