1/*
2 * tkMacOSXButton.c --
3 *
4 *	This file implements the Macintosh specific portion of the
5 *	button widgets.
6 *
7 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
8 * Copyright 2001, Apple Computer, Inc.
9 * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net>
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tkMacOSXButton.c,v 1.2.2.20 2007/11/09 07:36:45 das Exp $
15 */
16
17#include "tkMacOSXPrivate.h"
18#include "tkButton.h"
19#include "tkMacOSXFont.h"
20#include "tkMacOSXDebug.h"
21
22#define DEFAULT_USE_TK_TEXT 0
23
24#define CONTROL_INITIALIZED 1
25#define FIRST_DRAW	    2
26#define ACTIVE		    4
27
28#define MAX_VALUE	    1
29
30/*
31 * Default insets for controls
32 */
33
34#define DEF_INSET_LEFT 2
35#define DEF_INSET_RIGHT 2
36#define DEF_INSET_TOP 2
37#define DEF_INSET_BOTTOM 4
38
39/*
40 * Some defines used to control what type of control is drawn.
41 */
42
43#define DRAW_LABEL	0	/* Labels are treated genericly. */
44#define DRAW_CONTROL	1	/* Draw using the Native control. */
45#define DRAW_CUSTOM	2	/* Make our own button drawing. */
46#define DRAW_BEVEL	3
47
48/*
49 * Declaration of Mac specific button structure.
50 */
51
52typedef struct {
53    SInt16 initialValue;
54    SInt16 minValue;
55    SInt16 maxValue;
56    SInt16 procID;
57    int isBevel;
58} MacControlParams;
59
60typedef struct {
61    int drawType;
62    Tk_3DBorder border;
63    int relief;
64    int offset;			/* 0 means this is a normal widget. 1 means
65				 * it is an image button, so we offset the
66				 * image to make the button appear to move
67				 * up and down as the relief changes. */
68    GC gc;
69    int hasImageOrBitmap;
70} DrawParams;
71
72typedef struct {
73    TkButton info;		/* Generic button info */
74    int id;
75    int usingControl;
76    int useTkText;
77    int flags;			/* Initialisation status */
78    MacControlParams params;
79    WindowRef windowRef;
80    unsigned long userPaneBackground;
81    ControlRef userPane;	/* Carbon control */
82    ControlRef control;		/* Carbon control */
83    Str255 controlTitle;
84    ControlFontStyleRec fontStyle;
85    /*
86     * The following are used to store the image content for
87     * beveled buttons, i.e. buttons with images.
88     */
89    ControlButtonContentInfo bevelButtonContent;
90    OpenCPicParams picParams;
91} MacButton;
92
93/*
94 * Forward declarations for procedures defined later in this file:
95 */
96
97static OSStatus SetUserPaneDrawProc(ControlRef control,
98	ControlUserPaneDrawProcPtr upp);
99static OSStatus SetUserPaneSetUpSpecialBackgroundProc(ControlRef control,
100	ControlUserPaneBackgroundProcPtr upp);
101static void UserPaneDraw(ControlRef control, ControlPartCode cpc);
102static void UserPaneBackgroundProc(ControlHandle,
103	ControlBackgroundPtr info);
104
105static void ButtonEventProc(ClientData clientData, XEvent *eventPtr);
106static int UpdateControlColors(MacButton *mbPtr);
107static void TkMacOSXComputeControlParams(TkButton *butPtr,
108	MacControlParams *paramsPtr);
109static int TkMacOSXComputeDrawParams(TkButton *butPtr, DrawParams *dpPtr);
110static void TkMacOSXDrawControl(MacButton *butPtr, GWorldPtr destPort, GC gc,
111	Pixmap pixmap);
112static void SetupBevelButton(MacButton *butPtr, ControlRef controlHandle,
113	GWorldPtr destPort, GC gc, Pixmap pixmap);
114
115/*
116 * The class procedure table for the button widgets.
117 */
118
119Tk_ClassProcs tkpButtonProcs = {
120    sizeof(Tk_ClassProcs),	/* size */
121    TkButtonWorldChanged,	/* worldChangedProc */
122};
123
124static int bCount;
125
126
127/*
128 *----------------------------------------------------------------------
129 *
130 * TkpCreateButton --
131 *
132 *	Allocate a new TkButton structure.
133 *
134 * Results:
135 *	Returns a newly allocated TkButton structure.
136 *
137 * Side effects:
138 *	Registers an event handler for the widget.
139 *
140 *----------------------------------------------------------------------
141 */
142
143TkButton *
144TkpCreateButton(
145    Tk_Window tkwin)
146{
147    MacButton *macButtonPtr = (MacButton *) ckalloc(sizeof(MacButton));
148
149    Tk_CreateEventHandler(tkwin, ActivateMask,
150	    ButtonEventProc, (ClientData) macButtonPtr);
151    macButtonPtr->id = bCount++;
152    macButtonPtr->usingControl = 0;
153    macButtonPtr->flags = 0;
154    macButtonPtr->userPaneBackground = PIXEL_MAGIC << 24;
155    macButtonPtr->userPane = NULL;
156    macButtonPtr->control = NULL;
157    macButtonPtr->controlTitle[0] = 0;
158    macButtonPtr->controlTitle[1] = 0;
159    bzero(&macButtonPtr->params, sizeof(macButtonPtr->params));
160    bzero(&macButtonPtr->fontStyle, sizeof(macButtonPtr->fontStyle));
161
162    return (TkButton *)macButtonPtr;
163}
164
165/*
166 *----------------------------------------------------------------------
167 *
168 * TkpDisplayButton --
169 *
170 *	This procedure is invoked to display a button widget. It is
171 *	normally invoked as an idle handler.
172 *
173 * Results:
174 *	None.
175 *
176 * Side effects:
177 *	Commands are output to X to display the button in its
178 *	current mode. The REDRAW_PENDING flag is cleared.
179 *
180 *----------------------------------------------------------------------
181 */
182
183void
184TkpDisplayButton(
185    ClientData clientData)	/* Information about widget. */
186{
187    MacButton *macButtonPtr = (MacButton *) clientData;
188    TkButton *butPtr = (TkButton *) clientData;
189    Tk_Window tkwin = butPtr->tkwin;
190    CGrafPtr destPort, savePort;
191    Boolean portChanged;
192    Pixmap pixmap;
193    int width, height, fullWidth, fullHeight, textXOffset, textYOffset;
194    int borderWidth, wasUsingControl;
195    int haveImage = 0, haveText = 0, imageWidth = 0, imageHeight = 0;
196    int imageXOffset = 0, imageYOffset = 0; /* image information that will
197					     * be used to restrict disabled
198					     * pixmap as well */
199    DrawParams drawParams, *dpPtr = &drawParams;
200
201    butPtr->flags &= ~REDRAW_PENDING;
202    if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
203	return;
204    }
205    pixmap = (Pixmap) Tk_WindowId(tkwin);
206    wasUsingControl = macButtonPtr->usingControl;
207
208    if (TkMacOSXComputeDrawParams(butPtr, &drawParams) ) {
209	macButtonPtr->usingControl = 1;
210	if (butPtr->type == TYPE_BUTTON) {
211	    macButtonPtr->useTkText = 0;
212	} else {
213	    macButtonPtr->useTkText = 1;
214	}
215    } else {
216	macButtonPtr->usingControl = 0;
217	macButtonPtr->useTkText = 1;
218    }
219
220    /*
221     * See the comment in UpdateControlColors as to why we use the
222     * highlightbackground for the border of Macintosh buttons.
223     */
224
225    if (macButtonPtr->useTkText) {
226	if (butPtr->type == TYPE_BUTTON) {
227	    Tk_Fill3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0, 0,
228		    Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
229	} else {
230	    Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, 0, 0,
231		    Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
232	}
233    }
234
235    /*
236     * Set up clipping region. Make sure the we are using the port
237     * for this button, or we will set the wrong window's clip.
238     */
239
240    destPort = TkMacOSXGetDrawablePort(pixmap);
241    portChanged = QDSwapPort(destPort, &savePort);
242    TkMacOSXSetUpClippingRgn(pixmap);
243
244    /*
245     * Draw the native portion of the buttons. Start by creating the control
246     * if it doesn't already exist. Then configure the Macintosh control from
247     * the Tk info. Finally, we call Draw1Control to draw to the screen.
248     */
249
250    if (macButtonPtr->usingControl) {
251	borderWidth = 0;
252	TkMacOSXDrawControl(macButtonPtr, destPort, dpPtr->gc, pixmap);
253    } else if (wasUsingControl && macButtonPtr->userPane) {
254	DisposeControl(macButtonPtr->userPane);
255	macButtonPtr->userPane = NULL;
256	macButtonPtr->control = NULL;
257	macButtonPtr->flags = 0;
258    }
259
260    if ((dpPtr->drawType == DRAW_CUSTOM) || (dpPtr->drawType == DRAW_LABEL)) {
261	borderWidth = butPtr->borderWidth;
262    }
263
264    /*
265     * Display image or bitmap or text for button. This has
266     * already been done under Appearance with the Bevel
267     * button types.
268     */
269
270    if (dpPtr->drawType == DRAW_BEVEL) {
271	goto applyStipple;
272    }
273
274    if (butPtr->image != None) {
275	Tk_SizeOfImage(butPtr->image, &width, &height);
276	haveImage = 1;
277    } else if (butPtr->bitmap != None) {
278	Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
279	haveImage = 1;
280    }
281    imageWidth = width;
282    imageHeight = height;
283
284    haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0);
285    if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
286	int x, y;
287
288	textXOffset = 0;
289	textYOffset = 0;
290	fullWidth = 0;
291	fullHeight = 0;
292
293	switch ((enum compound) butPtr->compound) {
294	    case COMPOUND_TOP:
295	    case COMPOUND_BOTTOM:
296		/*
297		 * Image is above or below text.
298		 */
299		if (butPtr->compound == COMPOUND_TOP) {
300		    textYOffset = height + butPtr->padY;
301		} else {
302		    imageYOffset = butPtr->textHeight + butPtr->padY;
303		}
304		fullHeight = height + butPtr->textHeight + butPtr->padY;
305		fullWidth = (width > butPtr->textWidth ? width :
306			butPtr->textWidth);
307		textXOffset = (fullWidth - butPtr->textWidth)/2;
308		imageXOffset = (fullWidth - width)/2;
309		break;
310
311	    case COMPOUND_LEFT:
312	    case COMPOUND_RIGHT:
313		/*
314		 * Image is left or right of text.
315		 */
316
317		if (butPtr->compound == COMPOUND_LEFT) {
318		    textXOffset = width + butPtr->padX;
319		} else {
320		    imageXOffset = butPtr->textWidth + butPtr->padX;
321		}
322		fullWidth = butPtr->textWidth + butPtr->padX + width;
323		fullHeight = (height > butPtr->textHeight ? height :
324			butPtr->textHeight);
325		textYOffset = (fullHeight - butPtr->textHeight)/2;
326		imageYOffset = (fullHeight - height)/2;
327		break;
328
329	    case COMPOUND_CENTER:
330		/*
331		 * Image and text are superimposed.
332		 */
333
334		fullWidth = (width > butPtr->textWidth ? width :
335			butPtr->textWidth);
336		fullHeight = (height > butPtr->textHeight ? height :
337			butPtr->textHeight);
338		textXOffset = (fullWidth - butPtr->textWidth)/2;
339		imageXOffset = (fullWidth - width)/2;
340		textYOffset = (fullHeight - butPtr->textHeight)/2;
341		imageYOffset = (fullHeight - height)/2;
342		break;
343
344	    case COMPOUND_NONE:
345		break;
346	}
347
348	TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
349		butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y);
350
351	x += butPtr->indicatorSpace;
352
353	x += dpPtr->offset;
354	y += dpPtr->offset;
355	if (dpPtr->relief == TK_RELIEF_RAISED) {
356	    x -= dpPtr->offset;
357	    y -= dpPtr->offset;
358	} else if (dpPtr->relief == TK_RELIEF_SUNKEN) {
359	    x += dpPtr->offset;
360	    y += dpPtr->offset;
361	}
362	imageXOffset += x;
363	imageYOffset += y;
364	if (butPtr->image != NULL) {
365	    if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
366		Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
367			pixmap, imageXOffset, imageYOffset);
368#if 0
369	    } else if ((butPtr->tristateImage != NULL) &&
370		    (butPtr->flags & TRISTATED)) {
371		Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height,
372			pixmap, imageXOffset, imageYOffset);
373#endif
374	    } else {
375		Tk_RedrawImage(butPtr->image, 0, 0, width, height,
376			pixmap, imageXOffset, imageYOffset);
377	    }
378	} else {
379	    XSetClipOrigin(butPtr->display, dpPtr->gc, imageXOffset,
380		    imageYOffset);
381	    XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc,
382		    0, 0, width, height, imageXOffset, imageYOffset, 1);
383	    XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0);
384	}
385
386	if (macButtonPtr->useTkText) {
387	    Tk_DrawTextLayout(butPtr->display, pixmap, dpPtr->gc,
388		    butPtr->textLayout, x + textXOffset, y + textYOffset, 0,
389		    -1);
390	    Tk_UnderlineTextLayout(butPtr->display, pixmap, dpPtr->gc,
391		    butPtr->textLayout, x + textXOffset, y + textYOffset,
392		    butPtr->underline);
393	}
394	y += fullHeight/2;
395    } else if (haveImage) {
396	int x = 0, y;
397
398	TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
399		butPtr->indicatorSpace + width, height, &x, &y);
400	x += butPtr->indicatorSpace;
401
402	x += dpPtr->offset;
403	y += dpPtr->offset;
404	if (dpPtr->relief == TK_RELIEF_RAISED) {
405	    x -= dpPtr->offset;
406	    y -= dpPtr->offset;
407	} else if (dpPtr->relief == TK_RELIEF_SUNKEN) {
408	    x += dpPtr->offset;
409	    y += dpPtr->offset;
410	}
411	imageXOffset += x;
412	imageYOffset += y;
413	if (butPtr->image != NULL) {
414	    if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
415		Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
416			pixmap, imageXOffset, imageYOffset);
417#if 0
418	    } else if ((butPtr->tristateImage != NULL) &&
419		    (butPtr->flags & TRISTATED)) {
420		Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height,
421			pixmap, imageXOffset, imageYOffset);
422#endif
423	    } else {
424		Tk_RedrawImage(butPtr->image, 0, 0, width, height,
425			pixmap, imageXOffset, imageYOffset);
426	    }
427	} else {
428	    XSetClipOrigin(butPtr->display, dpPtr->gc, x, y);
429	    XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc,
430		    0, 0, width, height, x, y, 1);
431	    XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0);
432	}
433	y += height/2;
434    } else if (macButtonPtr->useTkText) {
435	int x = 0, y;
436
437	TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
438		butPtr->indicatorSpace + butPtr->textWidth,
439		butPtr->textHeight, &x, &y);
440	x += butPtr->indicatorSpace;
441	Tk_DrawTextLayout(butPtr->display, pixmap, dpPtr->gc,
442		butPtr->textLayout, x, y, 0, -1);
443    }
444
445    /*
446     * If the button is disabled with a stipple rather than a special
447     * foreground color, generate the stippled effect. If the widget
448     * is selected and we use a different background color when selected,
449     * must temporarily modify the GC so the stippling is the right color.
450     */
451
452  applyStipple:
453    if (macButtonPtr->useTkText) {
454	if ((butPtr->state == STATE_DISABLED)
455		&& ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
456	    if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
457		    && (butPtr->selectBorder != NULL)) {
458		XSetForeground(butPtr->display, butPtr->stippleGC,
459			Tk_3DBorderColor(butPtr->selectBorder)->pixel);
460	    }
461
462	    /*
463	     * Stipple the whole button if no disabledFg was specified,
464	     * otherwise restrict stippling only to displayed image
465	     */
466
467	    if (butPtr->disabledFg == NULL) {
468		XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
469			0, 0, (unsigned) Tk_Width(tkwin),
470			(unsigned) Tk_Height(tkwin));
471	    } else {
472		XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
473			imageXOffset, imageYOffset,
474			(unsigned) imageWidth, (unsigned) imageHeight);
475	    }
476	    if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
477		    && (butPtr->selectBorder != NULL)) {
478		XSetForeground(butPtr->display, butPtr->stippleGC,
479			Tk_3DBorderColor(butPtr->normalBorder)->pixel);
480	    }
481	}
482
483	/*
484	 * Draw the border and traversal highlight last. This way, if the
485	 * button's contents overflow they'll be covered up by the border.
486	 */
487
488	if (dpPtr->relief != TK_RELIEF_FLAT) {
489	    int inset = butPtr->highlightWidth;
490
491	    Tk_Draw3DRectangle(tkwin, pixmap, dpPtr->border, inset, inset,
492		    Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
493		    borderWidth, dpPtr->relief);
494	}
495    }
496    if (portChanged) {
497	QDSwapPort(savePort, NULL);
498    }
499}
500
501/*
502 *----------------------------------------------------------------------
503 *
504 * TkpComputeButtonGeometry --
505 *
506 *	After changes in a button's text or bitmap, this procedure
507 *	recomputes the button's geometry and passes this information
508 *	along to the geometry manager for the window.
509 *
510 * Results:
511 *	None.
512 *
513 * Side effects:
514 *	The button's window may change size.
515 *
516 *----------------------------------------------------------------------
517 */
518
519void
520TkpComputeButtonGeometry(
521    TkButton *butPtr)		/* Button whose geometry may have changed. */
522{
523    int width, height, avgWidth, haveImage = 0, haveText = 0;
524    int xInset, yInset, txtWidth, txtHeight;
525    Tk_FontMetrics fm;
526    DrawParams drawParams;
527
528    /*
529     * First figure out the size of the contents of the button.
530     */
531
532    width = 0;
533    height = 0;
534    txtWidth = 0;
535    txtHeight = 0;
536    avgWidth = 0;
537
538    butPtr->indicatorSpace = 0;
539    if (butPtr->image != NULL) {
540	Tk_SizeOfImage(butPtr->image, &width, &height);
541	haveImage = 1;
542    } else if (butPtr->bitmap != None) {
543	Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
544	haveImage = 1;
545    }
546
547    if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) {
548	Tk_FreeTextLayout(butPtr->textLayout);
549	butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
550		Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
551		butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
552
553	txtWidth = butPtr->textWidth;
554	txtHeight = butPtr->textHeight;
555	avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
556	Tk_GetFontMetrics(butPtr->tkfont, &fm);
557	haveText = (txtWidth != 0 && txtHeight != 0);
558    }
559
560    /*
561     * If the button is compound (ie, it shows both an image and text),
562     * the new geometry is a combination of the image and text geometry.
563     * We only honor the compound bit if the button has both text and an
564     * image, because otherwise it is not really a compound button.
565     */
566
567    if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
568	switch ((enum compound) butPtr->compound) {
569	    case COMPOUND_TOP:
570	    case COMPOUND_BOTTOM:
571		/*
572		 * Image is above or below text.
573		 */
574
575		height += txtHeight + butPtr->padY;
576		width = (width > txtWidth ? width : txtWidth);
577		break;
578	    case COMPOUND_LEFT:
579	    case COMPOUND_RIGHT:
580		/*
581		 * Image is left or right of text.
582		 */
583
584		width += txtWidth + butPtr->padX;
585		height = (height > txtHeight ? height : txtHeight);
586		break;
587	    case COMPOUND_CENTER:
588		/*
589		 * Image and text are superimposed.
590		 */
591
592		width = (width > txtWidth ? width : txtWidth);
593		height = (height > txtHeight ? height : txtHeight);
594		break;
595	    case COMPOUND_NONE:
596		break;
597	}
598	if (butPtr->width > 0) {
599	    width = butPtr->width;
600	}
601	if (butPtr->height > 0) {
602	    height = butPtr->height;
603	}
604
605	if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
606	    butPtr->indicatorSpace = height;
607	    if (butPtr->type == TYPE_CHECK_BUTTON) {
608		butPtr->indicatorDiameter = (65 * height)/100;
609	    } else {
610		butPtr->indicatorDiameter = (75 * height)/100;
611	    }
612	}
613
614	width += 2 * butPtr->padX;
615	height += 2 * butPtr->padY;
616    } else if (haveImage) {
617	if (butPtr->width > 0) {
618	    width = butPtr->width;
619	}
620	if (butPtr->height > 0) {
621	    height = butPtr->height;
622	}
623	if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
624	    butPtr->indicatorSpace = height;
625	    if (butPtr->type == TYPE_CHECK_BUTTON) {
626		butPtr->indicatorDiameter = (65 * height)/100;
627	    } else {
628		butPtr->indicatorDiameter = (75 * height)/100;
629	    }
630	}
631    } else {
632	width = txtWidth;
633	height = txtHeight;
634	if (butPtr->width > 0) {
635	    width = butPtr->width * avgWidth;
636	}
637	if (butPtr->height > 0) {
638	    height = butPtr->height * fm.linespace;
639	}
640	if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
641	    butPtr->indicatorDiameter = fm.linespace;
642	    if (butPtr->type == TYPE_CHECK_BUTTON) {
643		butPtr->indicatorDiameter =
644			(80 * butPtr->indicatorDiameter)/100;
645	    }
646	    butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
647	}
648    }
649
650    /*
651     * Now figure out the size of the border decorations for the button.
652     */
653
654    if (butPtr->highlightWidth < 0) {
655	butPtr->highlightWidth = 0;
656    }
657
658    /*
659     * The width and height calculation for Appearance buttons with images &
660     * non-Appearance buttons with images is different. In the latter case,
661     * we add the borderwidth to the inset, since we are going to stamp a
662     * 3-D border over the image. In the former, we add it to the height,
663     * directly, since Appearance will draw the border as part of our control.
664     *
665     * When issuing the geometry request, add extra space for the indicator,
666     * if any, and for the border and padding, plus if this is an image two
667     * extra pixels so the display can be offset by 1 pixel in either
668     * direction for the raised or lowered effect.
669     *
670     * The highlight width corresponds to the default ring on the Macintosh.
671     * As such, the highlight width is only added if the button is the default
672     * button. The actual width of the default ring is one less than the
673     * highlight width as there is also one pixel of spacing.
674     * Appearance buttons with images do not have a highlight ring, because the
675     * Bevel button type does not support one.
676     */
677
678    if ((butPtr->image == None) && (butPtr->bitmap == None)) {
679	width += 2*butPtr->padX;
680	height += 2*butPtr->padY;
681    }
682
683    if ((butPtr->type == TYPE_BUTTON)) {
684	if ((butPtr->image == None) && (butPtr->bitmap == None)) {
685	    butPtr->inset = 0;
686	    if (butPtr->defaultState != STATE_DISABLED) {
687		butPtr->inset += butPtr->highlightWidth;
688	    }
689	} else {
690	    butPtr->inset = 0;
691	    width += (2 * butPtr->borderWidth + 4);
692	    height += (2 * butPtr->borderWidth + 4);
693	}
694    } else if (butPtr->type == TYPE_LABEL) {
695	butPtr->inset = butPtr->borderWidth;
696    } else if (butPtr->indicatorOn) {
697	butPtr->inset = 0;
698    } else {
699	/*
700	 * Under Appearance, the Checkbutton or radiobutton with an image
701	 * is represented by a BevelButton with the Sticky defProc...
702	 * So we must set its height in the same way as the Button
703	 * with an image or bitmap.
704	 */
705
706	if (butPtr->image != None || butPtr->bitmap != None) {
707	    int border;
708
709	    butPtr->inset = 0;
710	    if (butPtr->borderWidth <= 2) {
711		border = 6;
712	    } else {
713		border = 2 * butPtr->borderWidth + 2;
714	    }
715	    width += border;
716	    height += border;
717	} else {
718	    butPtr->inset = butPtr->borderWidth;
719	}
720    }
721
722    if (TkMacOSXComputeDrawParams(butPtr, &drawParams)) {
723	xInset = butPtr->indicatorSpace + DEF_INSET_LEFT + DEF_INSET_RIGHT;
724	yInset = DEF_INSET_TOP + DEF_INSET_BOTTOM;
725    } else {
726	xInset = butPtr->indicatorSpace+butPtr->inset*2;
727	yInset = butPtr->inset*2;
728    }
729    Tk_GeometryRequest(butPtr->tkwin, width + xInset, height + yInset);
730    Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
731}
732
733/*
734 *----------------------------------------------------------------------
735 *
736 * TkpDestroyButton --
737 *
738 *	Free data structures associated with the button control.
739 *
740 * Results:
741 *	None.
742 *
743 * Side effects:
744 *	Restores the default control state.
745 *
746 *----------------------------------------------------------------------
747 */
748
749void
750TkpDestroyButton(
751    TkButton *butPtr)
752{
753    MacButton *mbPtr = (MacButton *) butPtr; /* Mac button. */
754
755    if (mbPtr->userPane) {
756	DisposeControl(mbPtr->userPane);
757	mbPtr->userPane = NULL;
758    }
759}
760
761/*
762 *----------------------------------------------------------------------
763 *
764 * TkMacOSXInitControl --
765 *
766 *	This procedure initialises a Carbon control.
767 *
768 * Results:
769 *	0 on success, 1 on failure.
770 *
771 * Side effects:
772 *	A background pane control and the control itself is created
773 *	The contol is embedded in the background control
774 *	The background control is embedded in the root control
775 *	of the containing window
776 *	The creation parameters for the control are also computed
777 *
778 *----------------------------------------------------------------------
779 */
780
781static int
782TkMacOSXInitControl(
783    MacButton *mbPtr,		/* Mac button. */
784    GWorldPtr destPort,
785    GC gc,
786    Pixmap pixmap,
787    Rect *paneRect,
788    Rect *cntrRect)
789{
790    TkButton *butPtr = (TkButton *) mbPtr;
791    ControlRef rootControl;
792    SInt16 procID, initialValue, minValue, maxValue;
793    Boolean initiallyVisible;
794    SInt32 controlReference;
795
796    rootControl = TkMacOSXGetRootControl(Tk_WindowId(butPtr->tkwin));
797    mbPtr->windowRef = TkMacOSXDrawableWindow(Tk_WindowId(butPtr->tkwin));
798
799    /*
800     * Set up the user pane.
801     */
802
803    initiallyVisible = false;
804    initialValue = kControlSupportsEmbedding|kControlHasSpecialBackground;
805    minValue = 0;
806    maxValue = 1;
807    procID = kControlUserPaneProc;
808    controlReference = (SInt32)mbPtr;
809    mbPtr->userPane = NewControl(mbPtr->windowRef, paneRect, "\p",
810	    initiallyVisible, initialValue, minValue, maxValue, procID,
811	    controlReference);
812
813    if (!mbPtr->userPane) {
814	TkMacOSXDbgMsg("Failed to create user pane control");
815	return 1;
816    }
817    if (ChkErr(EmbedControl, mbPtr->userPane,rootControl) != noErr) {
818	return 1;
819    }
820
821    SetUserPaneSetUpSpecialBackgroundProc(mbPtr->userPane,
822	    UserPaneBackgroundProc);
823    SetUserPaneDrawProc(mbPtr->userPane,UserPaneDraw);
824    initiallyVisible = false;
825    TkMacOSXComputeControlParams(butPtr,&mbPtr->params);
826    mbPtr->control = NewControl(mbPtr->windowRef, cntrRect, "\p",
827	    initiallyVisible, mbPtr->params.initialValue,
828	    mbPtr->params.minValue, mbPtr->params.maxValue,
829	    mbPtr->params.procID, controlReference);
830
831    if (!mbPtr->control) {
832	TkMacOSXDbgMsg("Failed to create control of type %d\n", procID);
833	return 1;
834    }
835    if (ChkErr(EmbedControl, mbPtr->control,mbPtr->userPane) != noErr ) {
836	return 1;
837    }
838
839    mbPtr->flags |= (CONTROL_INITIALIZED | FIRST_DRAW);
840    if (IsWindowActive(mbPtr->windowRef)) {
841	mbPtr->flags |= ACTIVE;
842    }
843    return 0;
844}
845
846/*
847 *--------------------------------------------------------------
848 *
849 * TkMacOSXDrawControl --
850 *
851 *	This function draws the tk button using Mac controls
852 *	In addition, this code may apply custom colors passed
853 *	in the TkButton.
854 *
855 * Results:
856 *	None.
857 *
858 * Side effects:
859 *	The control is created, or reinitialised as needed.
860 *
861 *--------------------------------------------------------------
862 */
863
864static void
865TkMacOSXDrawControl(
866    MacButton *mbPtr,		/* Mac button. */
867    GWorldPtr destPort,		/* Off screen GWorld. */
868    GC gc,			/* The GC we are drawing into - needed for the
869				 * bevel button */
870    Pixmap pixmap)		/* The pixmap we are drawing into - needed for
871				 * the bevel button */
872{
873    TkButton *butPtr = (TkButton *) mbPtr;
874    TkWindow *winPtr;
875    Rect paneRect, cntrRect;
876    int active, enabled;
877    int rebuild;
878
879    winPtr = (TkWindow *) butPtr->tkwin;
880
881    paneRect.left = winPtr->privatePtr->xOff;
882    paneRect.top = winPtr->privatePtr->yOff;
883    paneRect.right = paneRect.left + Tk_Width(butPtr->tkwin);
884    paneRect.bottom = paneRect.top + Tk_Height(butPtr->tkwin);
885
886    cntrRect = paneRect;
887
888/*
889    cntrRect.left += butPtr->inset;
890    cntrRect.top += butPtr->inset;
891    cntrRect.right -= butPtr->inset;
892    cntrRect.bottom -= butPtr->inset;
893*/
894    cntrRect.left += DEF_INSET_LEFT;
895    cntrRect.top += DEF_INSET_TOP;
896    cntrRect.right -= DEF_INSET_RIGHT;
897    cntrRect.bottom -= DEF_INSET_BOTTOM;
898
899    /*
900     * The control has been previously initialised.
901     * It may need to be re-initialised
902     */
903#if 0
904    rebuild = (winPtr->flags & TK_REBUILD_TOPLEVEL);
905    winPtr->flags &= ~TK_REBUILD_TOPLEVEL;
906#else
907    rebuild = 0;
908#endif
909    if (mbPtr->flags) {
910	MacControlParams params;
911
912	TkMacOSXComputeControlParams(butPtr, &params);
913	if (rebuild || bcmp(&params, &mbPtr->params, sizeof(params))) {
914	    /*
915	     * The type of control has changed.
916	     * Clean it up and clear the flag.
917	     */
918
919	    if (mbPtr->userPane) {
920		DisposeControl(mbPtr->userPane);
921		mbPtr->userPane = NULL;
922		mbPtr->control = NULL;
923	    }
924	    mbPtr->flags = 0;
925	}
926    }
927    if (!(mbPtr->flags & CONTROL_INITIALIZED)) {
928	if (TkMacOSXInitControl(mbPtr, destPort, gc, pixmap, &paneRect,
929		&cntrRect)) {
930	    return;
931	}
932    }
933    SetControlBounds(mbPtr->userPane, &paneRect);
934    SetControlBounds(mbPtr->control, &cntrRect);
935
936    if (!mbPtr->useTkText) {
937	Str255 controlTitle;
938	ControlFontStyleRec fontStyle;
939	Tk_Font font;
940	int len;
941
942	if (((mbPtr->info.image == NULL) && (mbPtr->info.bitmap == None))
943		|| (mbPtr->info.compound != COMPOUND_NONE)) {
944	    len = TkFontGetFirstTextLayout(butPtr->textLayout,
945		    &font, (char*) controlTitle);
946	    controlTitle[len] = 0;
947	} else {
948	    len = 0;
949	    controlTitle[0] = 0;
950	}
951	if (rebuild || bcmp(mbPtr->controlTitle, controlTitle, len+1)) {
952	    CFStringRef cf = CFStringCreateWithCString(NULL,
953		    (char*) controlTitle, kCFStringEncodingUTF8);
954
955	    if (cf != NULL) {
956		SetControlTitleWithCFString(mbPtr->control, cf);
957		CFRelease(cf);
958	    }
959	    bcopy(controlTitle, mbPtr->controlTitle, len+1);
960	}
961	if (len) {
962	    TkMacOSXInitControlFontStyle(font, &fontStyle);
963	    if (bcmp(&mbPtr->fontStyle, &fontStyle, sizeof(fontStyle)) ) {
964		ChkErr(SetControlFontStyle, mbPtr->control, &fontStyle);
965		bcopy(&fontStyle, &mbPtr->fontStyle, sizeof(fontStyle));
966	    }
967	}
968    }
969    if (mbPtr->params.isBevel) {
970	/*
971	 * Initialiase the image/button parameters.
972	 */
973
974	SetupBevelButton(mbPtr, mbPtr->control, destPort, gc, pixmap);
975    }
976
977    if (butPtr->flags & SELECTED) {
978	SetControlValue(mbPtr->control, 1);
979#if 0
980    } else if (butPtr->flags & TRISTATED) {
981	SetControlValue(mbPtr->control, 2);
982#endif
983    } else {
984	SetControlValue(mbPtr->control, 0);
985    }
986
987    active = ((mbPtr->flags & ACTIVE) != 0);
988    if (active != IsControlActive(mbPtr->control)) {
989	if (active) {
990	    ChkErr(ActivateControl, mbPtr->control);
991	} else {
992	    ChkErr(DeactivateControl, mbPtr->control);
993	}
994    }
995    enabled = !(butPtr->state == STATE_DISABLED);
996    if (enabled != IsControlEnabled(mbPtr->control)) {
997	if (enabled) {
998	    ChkErr(EnableControl, mbPtr->control);
999	} else {
1000	    ChkErr(DisableControl, mbPtr->control);
1001	}
1002    }
1003    if (active && enabled) {
1004	if (butPtr->state == STATE_ACTIVE) {
1005	    if (mbPtr->params.isBevel) {
1006		HiliteControl(mbPtr->control, kControlButtonPart);
1007	    } else {
1008		switch (butPtr->type) {
1009		    case TYPE_BUTTON:
1010			HiliteControl(mbPtr->control, kControlButtonPart);
1011			break;
1012		    case TYPE_RADIO_BUTTON:
1013			HiliteControl(mbPtr->control, kControlRadioButtonPart);
1014			break;
1015		    case TYPE_CHECK_BUTTON:
1016			HiliteControl(mbPtr->control, kControlCheckBoxPart);
1017			break;
1018		}
1019	    }
1020	} else {
1021	    HiliteControl(mbPtr->control, kControlNoPart);
1022	}
1023    }
1024    UpdateControlColors(mbPtr);
1025
1026    if (butPtr->type == TYPE_BUTTON && !mbPtr->params.isBevel) {
1027	Boolean isDefault;
1028
1029	if (butPtr->defaultState == STATE_ACTIVE) {
1030	    isDefault = true;
1031	} else {
1032	    isDefault = false;
1033	}
1034	ChkErr(SetControlData, mbPtr->control, kControlNoPart,
1035		kControlPushButtonDefaultTag, sizeof(isDefault), &isDefault);
1036    }
1037
1038    if (mbPtr->flags & FIRST_DRAW) {
1039	ShowControl(mbPtr->userPane);
1040	ShowControl(mbPtr->control);
1041	mbPtr->flags ^= FIRST_DRAW;
1042    } else {
1043	SetControlVisibility(mbPtr->control, true, true);
1044	Draw1Control(mbPtr->userPane);
1045    }
1046
1047    if (mbPtr->params.isBevel) {
1048	if (mbPtr->bevelButtonContent.contentType ==
1049		kControlContentPictHandle) {
1050	    KillPicture(mbPtr->bevelButtonContent.u.picture);
1051	}
1052    }
1053}
1054
1055/*
1056 *--------------------------------------------------------------
1057 *
1058 * SetupBevelButton --
1059 *
1060 *	Sets up the Bevel Button with image by copying the
1061 *	source image onto the PicHandle for the button.
1062 *
1063 * Results:
1064 *	None
1065 *
1066 * Side effects:
1067 *	The image or bitmap for the button is copied over to a picture.
1068 *
1069 *--------------------------------------------------------------
1070 */
1071
1072void
1073SetupBevelButton(
1074    MacButton *mbPtr,		/* Mac button. */
1075    ControlRef controlHandle,	/* The control to set this picture to. */
1076    GWorldPtr destPort,		/* Off screen GWorld. */
1077    GC gc,			/* The GC we are drawing into - needed for the
1078				 * bevel button. */
1079    Pixmap pixmap)		/* The pixmap we are drawing into - needed for
1080				 * the bevel button. */
1081{
1082    TkButton *butPtr = (TkButton *) mbPtr;
1083    int height, width;
1084    ControlButtonGraphicAlignment theAlignment;
1085    CGrafPtr savePort;
1086    Boolean portChanged = false;
1087
1088    if (butPtr->image != None) {
1089	Tk_SizeOfImage(butPtr->image, &width, &height);
1090    } else {
1091	Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
1092    }
1093
1094    if ((butPtr->width > 0) && (butPtr->width < width)) {
1095	width = butPtr->width;
1096    }
1097    if ((butPtr->height > 0) && (butPtr->height < height)) {
1098	height = butPtr->height;
1099    }
1100
1101    {
1102	portChanged = QDSwapPort(destPort, &savePort);
1103	mbPtr->picParams.version = -2;
1104	mbPtr->picParams.hRes = 0x00480000;
1105	mbPtr->picParams.vRes = 0x00480000;
1106	mbPtr->picParams.srcRect.top = 0;
1107	mbPtr->picParams.srcRect.left = 0;
1108	mbPtr->picParams.srcRect.bottom = height;
1109	mbPtr->picParams.srcRect.right = width;
1110	mbPtr->picParams.reserved1 = 0;
1111	mbPtr->picParams.reserved2 = 0;
1112	mbPtr->bevelButtonContent.contentType = kControlContentPictHandle;
1113	mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams);
1114	if (!mbPtr->bevelButtonContent.u.picture) {
1115	    TkMacOSXDbgMsg("OpenCPicture failed");
1116	}
1117	tkPictureIsOpen = 1;
1118
1119	/*
1120	 * TO DO - There is one case where XCopyPlane calls CopyDeepMask,
1121	 * which does not get recorded in the picture. So the bitmap code
1122	 * will fail in that case.
1123	 */
1124     }
1125
1126    if (butPtr->selectImage != NULL && (butPtr->flags & SELECTED)) {
1127	Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap, 0, 0);
1128#if 0
1129    } else if (butPtr->tristateImage != NULL && (butPtr->flags & TRISTATED)) {
1130	Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height, pixmap, 0,
1131		0);
1132#endif
1133    } else if (butPtr->image != NULL) {
1134	Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 0, 0);
1135    } else {
1136	XSetClipOrigin(butPtr->display, gc, 0, 0);
1137	XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, width,
1138		height, 0, 0, 1);
1139    }
1140
1141    {
1142	ClosePicture();
1143	tkPictureIsOpen = 0;
1144	if (portChanged) {
1145	    QDSwapPort(savePort, NULL);
1146	}
1147    }
1148    ChkErr(SetControlData, controlHandle, kControlButtonPart,
1149	    kControlBevelButtonContentTag,
1150	    sizeof(ControlButtonContentInfo),
1151	    (char *) &mbPtr->bevelButtonContent);
1152
1153    if (butPtr->anchor == TK_ANCHOR_N) {
1154	theAlignment = kControlBevelButtonAlignTop;
1155    } else if (butPtr->anchor == TK_ANCHOR_NE) {
1156	theAlignment = kControlBevelButtonAlignTopRight;
1157    } else if (butPtr->anchor == TK_ANCHOR_E) {
1158	theAlignment = kControlBevelButtonAlignRight;
1159    } else if (butPtr->anchor == TK_ANCHOR_SE) {
1160	theAlignment = kControlBevelButtonAlignBottomRight;
1161    } else if (butPtr->anchor == TK_ANCHOR_S) {
1162	theAlignment = kControlBevelButtonAlignBottom;
1163    } else if (butPtr->anchor == TK_ANCHOR_SW) {
1164	theAlignment = kControlBevelButtonAlignBottomLeft;
1165    } else if (butPtr->anchor == TK_ANCHOR_W) {
1166	theAlignment = kControlBevelButtonAlignLeft;
1167    } else if (butPtr->anchor == TK_ANCHOR_NW) {
1168	theAlignment = kControlBevelButtonAlignTopLeft;
1169    } else if (butPtr->anchor == TK_ANCHOR_CENTER) {
1170	theAlignment = kControlBevelButtonAlignCenter;
1171    }
1172    ChkErr(SetControlData, controlHandle, kControlButtonPart,
1173	    kControlBevelButtonGraphicAlignTag,
1174	    sizeof(ControlButtonGraphicAlignment), (char *) &theAlignment);
1175
1176    if (butPtr->compound != COMPOUND_NONE) {
1177	ControlButtonTextPlacement thePlacement =
1178		kControlBevelButtonPlaceNormally;
1179
1180	if (butPtr->compound == COMPOUND_TOP) {
1181	    thePlacement = kControlBevelButtonPlaceBelowGraphic;
1182	} else if (butPtr->compound == COMPOUND_BOTTOM) {
1183	    thePlacement = kControlBevelButtonPlaceAboveGraphic;
1184	} else if (butPtr->compound == COMPOUND_LEFT) {
1185	    thePlacement = kControlBevelButtonPlaceToRightOfGraphic;
1186	} else if (butPtr->compound == COMPOUND_RIGHT) {
1187	    thePlacement = kControlBevelButtonPlaceToLeftOfGraphic;
1188	}
1189	ChkErr(SetControlData, controlHandle, kControlButtonPart,
1190		kControlBevelButtonTextPlaceTag,
1191		sizeof(ControlButtonTextPlacement), (char *) &thePlacement);
1192    }
1193}
1194
1195/*
1196 *--------------------------------------------------------------
1197 *
1198 * SetUserPaneDrawProc --
1199 *
1200 *	Utility function to add a UserPaneDrawProc
1201 *	to a userPane control. From MoreControls code
1202 *	from Apple DTS.
1203 *
1204 * Results:
1205 *	MacOS system error.
1206 *
1207 * Side effects:
1208 *	The user pane gets a new UserPaneDrawProc.
1209 *
1210 *--------------------------------------------------------------
1211 */
1212
1213OSStatus
1214SetUserPaneDrawProc(
1215    ControlRef control,
1216    ControlUserPaneDrawProcPtr upp)
1217{
1218    ControlUserPaneDrawUPP myControlUserPaneDrawUPP;
1219
1220    myControlUserPaneDrawUPP = NewControlUserPaneDrawUPP(upp);
1221    return SetControlData(control, kControlNoPart,
1222	    kControlUserPaneDrawProcTag, sizeof(myControlUserPaneDrawUPP),
1223	    (Ptr) &myControlUserPaneDrawUPP);
1224}
1225
1226/*
1227 *--------------------------------------------------------------
1228 *
1229 * SetUserPaneSetUpSpecialBackgroundProc --
1230 *
1231 *	Utility function to add a UserPaneBackgroundProc
1232 *	to a userPane control
1233 *
1234 * Results:
1235 *	MacOS system error.
1236 *
1237 * Side effects:
1238 *	The user pane gets a new UserPaneBackgroundProc.
1239 *
1240 *--------------------------------------------------------------
1241 */
1242
1243OSStatus
1244SetUserPaneSetUpSpecialBackgroundProc(
1245    ControlRef control,
1246    ControlUserPaneBackgroundProcPtr upp)
1247{
1248    ControlUserPaneBackgroundUPP myControlUserPaneBackgroundUPP;
1249
1250    myControlUserPaneBackgroundUPP = NewControlUserPaneBackgroundUPP(upp);
1251    return SetControlData(control, kControlNoPart,
1252	    kControlUserPaneBackgroundProcTag,
1253	    sizeof(myControlUserPaneBackgroundUPP),
1254	    (Ptr) &myControlUserPaneBackgroundUPP);
1255}
1256
1257/*
1258 *--------------------------------------------------------------
1259 *
1260 * UserPaneDraw --
1261 *
1262 *	This function draws the background of the user pane that will
1263 *	lie under checkboxes and radiobuttons.
1264 *
1265 * Results:
1266 *	None.
1267 *
1268 * Side effects:
1269 *	The user pane gets updated to the current color.
1270 *
1271 *--------------------------------------------------------------
1272 */
1273
1274void
1275UserPaneDraw(
1276    ControlRef control,
1277    ControlPartCode cpc)
1278{
1279    MacButton *mbPtr = (MacButton *)(intptr_t)GetControlReference(control);
1280    Rect contrlRect;
1281    CGrafPtr port;
1282
1283    GetPort(&port);
1284    GetControlBounds(control,&contrlRect);
1285    TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port);
1286    EraseRect(&contrlRect);
1287}
1288
1289/*
1290 *--------------------------------------------------------------
1291 *
1292 * UserPaneBackgroundProc --
1293 *
1294 *	This function sets up the background of the user pane that will
1295 *	lie under checkboxes and radiobuttons.
1296 *
1297 * Results:
1298 *	None.
1299 *
1300 * Side effects:
1301 *	The user pane background gets set to the current color.
1302 *
1303 *--------------------------------------------------------------
1304 */
1305
1306void
1307UserPaneBackgroundProc(
1308    ControlHandle control,
1309    ControlBackgroundPtr info)
1310{
1311    MacButton * mbPtr = (MacButton *)(intptr_t)GetControlReference(control);
1312
1313    if (info->colorDevice) {
1314	CGrafPtr port;
1315
1316	GetPort(&port);
1317	TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port);
1318    }
1319}
1320
1321/*
1322 *--------------------------------------------------------------
1323 *
1324 * UpdateControlColors --
1325 *
1326 *	This function will review the colors used to display
1327 *	a Macintosh button. If any non-standard colors are
1328 *	used we create a custom palette for the button, populate
1329 *	with the colors for the button and install the palette.
1330 *
1331 *	Under Appearance, we just set the pointer that will be
1332 *	used by the UserPaneDrawProc.
1333 *
1334 * Results:
1335 *	None.
1336 *
1337 * Side effects:
1338 *	The Macintosh control may get a custom palette installed.
1339 *
1340 *--------------------------------------------------------------
1341 */
1342
1343static int
1344UpdateControlColors(
1345    MacButton *mbPtr)
1346{
1347    XColor *xcolor;
1348    TkButton *butPtr = (TkButton *) mbPtr;
1349
1350    /*
1351     * Under Appearance we cannot change the background of the
1352     * button itself. However, the color we are setting is the color
1353     * of the containing userPane. This will be the color that peeks
1354     * around the rounded corners of the button.
1355     * We make this the highlightbackground rather than the background,
1356     * because if you color the background of a frame containing a
1357     * button, you usually also color the highlightbackground as well,
1358     * or you will get a thin grey ring around the button.
1359     */
1360
1361    if (butPtr->type == TYPE_BUTTON) {
1362	xcolor = Tk_3DBorderColor(butPtr->highlightBorder);
1363    } else {
1364	xcolor = Tk_3DBorderColor(butPtr->normalBorder);
1365    }
1366    mbPtr->userPaneBackground = xcolor->pixel;
1367
1368    return false;
1369}
1370
1371/*
1372 *--------------------------------------------------------------
1373 *
1374 * ButtonEventProc --
1375 *
1376 *	This procedure is invoked by the Tk dispatcher for various
1377 *	events on buttons.
1378 *
1379 * Results:
1380 *	None.
1381 *
1382 * Side effects:
1383 *	When it gets exposed, it is redisplayed.
1384 *
1385 *--------------------------------------------------------------
1386 */
1387
1388static void
1389ButtonEventProc(
1390    ClientData clientData,	/* Information about window. */
1391    XEvent *eventPtr)		/* Information about event. */
1392{
1393    TkButton *buttonPtr = (TkButton *) clientData;
1394    MacButton *mbPtr = (MacButton *) clientData;
1395
1396    if (eventPtr->type == ActivateNotify
1397	    || eventPtr->type == DeactivateNotify) {
1398	if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) {
1399	    return;
1400	}
1401	if (eventPtr->type == ActivateNotify) {
1402	    mbPtr->flags |= ACTIVE;
1403	} else {
1404	    mbPtr->flags &= ~ACTIVE;
1405	}
1406	if ((buttonPtr->flags & REDRAW_PENDING) == 0) {
1407	    Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) buttonPtr);
1408	    buttonPtr->flags |= REDRAW_PENDING;
1409	}
1410    }
1411}
1412
1413/*
1414 *----------------------------------------------------------------------
1415 *
1416 * TkMacOSXComputeControlParams --
1417 *
1418 *	This procedure computes the various parameters used
1419 *	when creating a Carbon control (NewControl).
1420 *	These are determined by the various tk button parameters
1421 *
1422 * Results:
1423 *	None.
1424 *
1425 * Side effects:
1426 *	Sets the control initialisation parameters
1427 *
1428 *----------------------------------------------------------------------
1429 */
1430
1431static void
1432TkMacOSXComputeControlParams(
1433    TkButton *butPtr,
1434    MacControlParams *paramsPtr)
1435{
1436    paramsPtr->isBevel = 0;
1437
1438    /*
1439     * Determine ProcID based on button type and dimensions.
1440     */
1441
1442    switch (butPtr->type) {
1443	case TYPE_BUTTON:
1444	    if ((butPtr->image == None) && (butPtr->bitmap == None)) {
1445		paramsPtr->initialValue = 1;
1446		paramsPtr->minValue = 0;
1447		paramsPtr->maxValue = 1;
1448		paramsPtr->procID = kControlPushButtonProc;
1449	    } else {
1450		paramsPtr->initialValue = 0;
1451		paramsPtr->minValue = kControlBehaviorOffsetContents |
1452			kControlContentPictHandle;
1453		paramsPtr->maxValue = 1;
1454		if (butPtr->borderWidth <= 2) {
1455		    paramsPtr->procID = kControlBevelButtonSmallBevelProc;
1456		} else if (butPtr->borderWidth == 3) {
1457		    paramsPtr->procID = kControlBevelButtonNormalBevelProc;
1458		} else {
1459		    paramsPtr->procID = kControlBevelButtonLargeBevelProc;
1460		}
1461		paramsPtr->isBevel = 1;
1462	    }
1463	    break;
1464	case TYPE_RADIO_BUTTON:
1465	    if (((butPtr->image == None) && (butPtr->bitmap == None))
1466		|| (butPtr->indicatorOn)) {
1467		paramsPtr->initialValue = 1;
1468		paramsPtr->minValue = 0;
1469		paramsPtr->maxValue = MAX_VALUE;
1470		paramsPtr->procID = kControlRadioButtonProc;
1471	    } else {
1472		paramsPtr->initialValue = 0;
1473		paramsPtr->minValue = kControlBehaviorOffsetContents |
1474			kControlBehaviorSticky | kControlContentPictHandle;
1475		paramsPtr->maxValue = MAX_VALUE;
1476		if (butPtr->borderWidth <= 2) {
1477		    paramsPtr->procID = kControlBevelButtonSmallBevelProc;
1478		} else if (butPtr->borderWidth == 3) {
1479		    paramsPtr->procID = kControlBevelButtonNormalBevelProc;
1480		} else {
1481		    paramsPtr->procID = kControlBevelButtonLargeBevelProc;
1482		}
1483		paramsPtr->isBevel = 1;
1484	    }
1485	    break;
1486	case TYPE_CHECK_BUTTON:
1487	    if (((butPtr->image == None) && (butPtr->bitmap == None))
1488		    || (butPtr->indicatorOn)) {
1489		paramsPtr->initialValue = 1;
1490		paramsPtr->minValue = 0;
1491		paramsPtr->maxValue = MAX_VALUE;
1492		paramsPtr->procID = kControlCheckBoxProc;
1493	    } else {
1494		paramsPtr->initialValue = 0;
1495		paramsPtr->minValue = kControlBehaviorOffsetContents |
1496			kControlBehaviorSticky | kControlContentPictHandle;
1497		paramsPtr->maxValue = MAX_VALUE;
1498		if (butPtr->borderWidth <= 2) {
1499		    paramsPtr->procID = kControlBevelButtonSmallBevelProc;
1500		} else if (butPtr->borderWidth == 3) {
1501		    paramsPtr->procID = kControlBevelButtonNormalBevelProc;
1502		} else {
1503		    paramsPtr->procID = kControlBevelButtonLargeBevelProc;
1504		}
1505		paramsPtr->isBevel = 1;
1506	    }
1507	    break;
1508    }
1509}
1510
1511/*
1512 *----------------------------------------------------------------------
1513 *
1514 * TkMacOSXComputeDrawParams --
1515 *
1516 *	This procedure computes the various parameters used
1517 *	when drawing a button
1518 *	These are determined by the various tk button parameters
1519 *
1520 * Results:
1521 *	1 if control will be used, 0 otherwise.
1522 *
1523 * Side effects:
1524 *	Sets the button draw parameters
1525 *
1526 *----------------------------------------------------------------------
1527 */
1528
1529static int
1530TkMacOSXComputeDrawParams(
1531    TkButton *butPtr,
1532    DrawParams *dpPtr)
1533{
1534    dpPtr->hasImageOrBitmap = ((butPtr->image != NULL)
1535	    || (butPtr->bitmap != None));
1536    dpPtr->offset = (butPtr->type == TYPE_BUTTON)
1537	    && dpPtr->hasImageOrBitmap;
1538    dpPtr->border = butPtr->normalBorder;
1539    if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) {
1540	dpPtr->gc = butPtr->disabledGC;
1541    } else if (butPtr->type == TYPE_BUTTON && butPtr->state == STATE_ACTIVE) {
1542	dpPtr->gc = butPtr->activeTextGC;
1543	dpPtr->border = butPtr->activeBorder;
1544    } else {
1545	dpPtr->gc = butPtr->normalTextGC;
1546    }
1547
1548    if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE)
1549	    && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
1550	dpPtr->border = butPtr->selectBorder;
1551    }
1552
1553    /*
1554     * Override the relief specified for the button if this is a
1555     * checkbutton or radiobutton and there's no indicator.
1556     * However, don't do this in the presence of Appearance, since
1557     * then the bevel button will take care of the relief.
1558     */
1559
1560    dpPtr->relief = butPtr->relief;
1561
1562    if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
1563	if (!dpPtr->hasImageOrBitmap) {
1564	    dpPtr->relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
1565		    : TK_RELIEF_RAISED;
1566	}
1567    }
1568
1569    /*
1570     * Determine the draw type
1571     */
1572
1573    if (butPtr->type == TYPE_LABEL) {
1574	dpPtr->drawType = DRAW_LABEL;
1575    } else if (butPtr->type == TYPE_BUTTON) {
1576	if (!dpPtr->hasImageOrBitmap) {
1577	    dpPtr->drawType = DRAW_CONTROL;
1578	} else if (butPtr->image != None) {
1579	    dpPtr->drawType = DRAW_BEVEL;
1580	} else {
1581	    /*
1582	     * TO DO - The current way the we draw bitmaps (XCopyPlane)
1583	     * uses CopyDeepMask in this one case. The Picture recording
1584	     * does not record this call, and so we can't use the
1585	     * Appearance bevel button here. The only case that would
1586	     * exercise this is if you use a bitmap, with
1587	     * -data & -mask specified. We should probably draw the
1588	     * appearance button and overprint the image in this case.
1589	     * This just punts and draws the old-style, ugly, button.
1590	     */
1591
1592	    if (dpPtr->gc->clip_mask == 0) {
1593		dpPtr->drawType = DRAW_BEVEL;
1594	    } else {
1595		TkpClipMask *clipPtr = (TkpClipMask *) dpPtr->gc->clip_mask;
1596
1597		if ((clipPtr->type == TKP_CLIP_PIXMAP) &&
1598			(clipPtr->value.pixmap != butPtr->bitmap)) {
1599		    dpPtr->drawType = DRAW_CUSTOM;
1600		} else {
1601		    dpPtr->drawType = DRAW_BEVEL;
1602		}
1603	    }
1604	}
1605    } else if (butPtr->indicatorOn) {
1606	dpPtr->drawType = DRAW_CONTROL;
1607    } else if (dpPtr->hasImageOrBitmap) {
1608	if (dpPtr->gc->clip_mask == 0) {
1609	    dpPtr->drawType = DRAW_BEVEL;
1610	} else {
1611	    TkpClipMask *clipPtr = (TkpClipMask*) dpPtr->gc->clip_mask;
1612
1613	    if ((clipPtr->type == TKP_CLIP_PIXMAP) &&
1614		    (clipPtr->value.pixmap != butPtr->bitmap)) {
1615		dpPtr->drawType = DRAW_CUSTOM;
1616	    } else {
1617		dpPtr->drawType = DRAW_BEVEL;
1618	    }
1619	}
1620    } else {
1621	dpPtr->drawType = DRAW_CUSTOM;
1622    }
1623
1624    if ((dpPtr->drawType == DRAW_CONTROL) || (dpPtr->drawType == DRAW_BEVEL)) {
1625	return 1;
1626    } else {
1627	return 0;
1628    }
1629}
1630