1/*
2 * tkMacOSXMenubutton.c --
3 *
4 *	This file implements the Macintosh specific portion of the
5 *	menubutton widget.
6 *
7 * Copyright (c) 1996 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: tkMacOSXMenubutton.c,v 1.2.2.12 2007/11/09 06:26:56 das Exp $
15 */
16
17#include "tkMacOSXPrivate.h"
18#include "tkMenu.h"
19#include "tkMenubutton.h"
20#include "tkMacOSXFont.h"
21#include "tkMacOSXDebug.h"
22
23#define kShadowOffset	(3)	/* amount to offset shadow from frame */
24#define kTriangleWidth	(11)	/* width of the triangle */
25#define kTriangleHeight (6)	/* height of the triangle */
26#define kTriangleMargin (5)	/* margin around triangle */
27
28#define TK_POPUP_OFFSET 32	/* size of popup marker */
29
30#define FIRST_DRAW	    2
31#define ACTIVE		    4
32
33MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr,
34	int cascade, short *menuIDPtr);
35MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID);
36
37typedef struct {
38    SInt16 initialValue;
39    SInt16 minValue;
40    SInt16 maxValue;
41    SInt16 procID;
42    int isBevel;
43} MenuButtonControlParams;
44
45typedef struct {
46    int len;
47    Str255 title;
48    ControlFontStyleRec style;
49} ControlTitleParams;
50
51/*
52 * Declaration of Mac specific button structure.
53 */
54
55typedef struct MacMenuButton {
56    TkMenuButton info;		/* Generic button info. */
57    WindowRef windowRef;
58    ControlRef userPane;
59    ControlRef control;
60    MenuRef menuRef;
61    unsigned long userPaneBackground;
62    int flags;
63    MenuButtonControlParams params;
64    ControlTitleParams titleParams;
65    ControlButtonContentInfo bevelButtonContent;
66    OpenCPicParams picParams;
67} MacMenuButton;
68
69/*
70 * Forward declarations for procedures defined later in this file:
71 */
72
73static OSStatus SetUserPaneDrawProc(ControlRef control,
74	ControlUserPaneDrawProcPtr upp);
75static OSStatus SetUserPaneSetUpSpecialBackgroundProc(ControlRef control,
76	ControlUserPaneBackgroundProcPtr upp);
77static void UserPaneDraw(ControlRef control, ControlPartCode cpc);
78static void UserPaneBackgroundProc(ControlHandle,
79	ControlBackgroundPtr info);
80static int MenuButtonInitControl (MacMenuButton *mbPtr, Rect *paneRect,
81	Rect *cntrRect );
82static void MenuButtonEventProc(ClientData clientData, XEvent *eventPtr);
83static int UpdateControlColors(MacMenuButton *mbPtr);
84static void ComputeMenuButtonControlParams(TkMenuButton *mbPtr,
85	MenuButtonControlParams * paramsPtr);
86static void ComputeControlTitleParams(TkMenuButton *mbPtr,
87	ControlTitleParams *paramsPtr);
88static void CompareControlTitleParams(ControlTitleParams *p1Ptr,
89	ControlTitleParams *p2Ptr, int *titleChanged, int *styleChanged);
90
91/*
92 * The structure below defines menubutton class behavior by means of
93 * procedures that can be invoked from generic window code.
94 */
95
96Tk_ClassProcs tkpMenubuttonClass = {
97    sizeof(Tk_ClassProcs),	/* size */
98    TkMenuButtonWorldChanged,	/* worldChangedProc */
99};
100
101
102/*
103 *----------------------------------------------------------------------
104 *
105 * TkpCreateMenuButton --
106 *
107 *	Allocate a new TkMenuButton structure.
108 *
109 * Results:
110 *	Returns a newly allocated TkMenuButton structure.
111 *
112 * Side effects:
113 *	Registers an event handler for the widget.
114 *
115 *----------------------------------------------------------------------
116 */
117
118TkMenuButton *
119TkpCreateMenuButton(
120    Tk_Window tkwin)
121{
122    MacMenuButton *mbPtr = (MacMenuButton *) ckalloc(sizeof(MacMenuButton));
123
124    Tk_CreateEventHandler(tkwin, ActivateMask,
125	    MenuButtonEventProc, (ClientData) mbPtr);
126    mbPtr->flags = 0;
127    mbPtr->userPaneBackground = PIXEL_MAGIC << 24;
128    mbPtr->userPane = NULL;
129    mbPtr->control = NULL;
130    mbPtr->menuRef = NULL;
131    bzero(&mbPtr->params, sizeof(mbPtr->params));
132    bzero(&mbPtr->titleParams, sizeof(mbPtr->titleParams));
133
134    return (TkMenuButton *) mbPtr;
135}
136
137/*
138 *----------------------------------------------------------------------
139 *
140 * TkpDisplayMenuButton --
141 *
142 *	This procedure is invoked to display a menubutton widget.
143 *
144 * Results:
145 *	None.
146 *
147 * Side effects:
148 *	Commands are output to X to display the menubutton in its
149 *	current mode.
150 *
151 *----------------------------------------------------------------------
152 */
153
154void
155TkpDisplayMenuButton(
156    ClientData clientData)	/* Information about widget. */
157{
158    TkMenuButton *butPtr = (TkMenuButton *) clientData;
159    Tk_Window tkwin = butPtr->tkwin;
160    TkWindow *winPtr;
161    Pixmap pixmap;
162    MacMenuButton *mbPtr = (MacMenuButton *) butPtr;
163    CGrafPtr destPort, savePort;
164    Boolean portChanged = false;
165    int hasImageOrBitmap = 0, width, height;
166    OSStatus err;
167    ControlButtonGraphicAlignment theAlignment;
168    Rect paneRect, cntrRect;
169    int active, enabled;
170
171    butPtr->flags &= ~REDRAW_PENDING;
172    if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
173	return;
174    }
175    pixmap = (Pixmap) Tk_WindowId(tkwin);
176    TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin));
177
178    winPtr = (TkWindow *)butPtr->tkwin;
179    paneRect.left = winPtr->privatePtr->xOff;
180    paneRect.top = winPtr->privatePtr->yOff;
181    paneRect.right = paneRect.left+Tk_Width(butPtr->tkwin);
182    paneRect.bottom = paneRect.top+Tk_Height(butPtr->tkwin);
183
184    cntrRect = paneRect;
185
186    cntrRect.left += butPtr->inset;
187    cntrRect.top += butPtr->inset;
188    cntrRect.right -= butPtr->inset;
189    cntrRect.bottom -= butPtr->inset;
190
191    if (mbPtr->userPane) {
192	MenuButtonControlParams params;
193	bzero(&params, sizeof(params));
194	ComputeMenuButtonControlParams(butPtr, &params);
195	if (
196#if 0
197	    (winPtr->flags & TK_REBUILD_TOPLEVEL) ||
198#endif
199	    bcmp(&params,&mbPtr->params,sizeof(params))) {
200	    if (mbPtr->userPane) {
201		DisposeControl(mbPtr->userPane);
202		mbPtr->userPane = NULL;
203		mbPtr->control = NULL;
204	    }
205	}
206    }
207    if (!mbPtr->userPane) {
208	if (MenuButtonInitControl(mbPtr, &paneRect, &cntrRect)) {
209	    TkMacOSXDbgMsg("Init Control failed");
210	    return;
211	}
212    }
213    SetControlBounds(mbPtr->userPane, &paneRect);
214    SetControlBounds(mbPtr->control, &cntrRect);
215
216    if (butPtr->image != None) {
217	Tk_SizeOfImage(butPtr->image, &width, &height);
218	hasImageOrBitmap = 1;
219    } else if (butPtr->bitmap != None) {
220	Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
221	hasImageOrBitmap = 1;
222    }
223
224    /*
225     * We need to cache the title and its style
226     */
227
228    if (!(mbPtr->flags & FIRST_DRAW)) {
229	ControlTitleParams titleParams;
230	int titleChanged;
231	int styleChanged;
232
233	ComputeControlTitleParams(butPtr, &titleParams);
234	CompareControlTitleParams(&titleParams, &mbPtr->titleParams,
235		&titleChanged, &styleChanged);
236	if (titleChanged) {
237	    CFStringRef cf = CFStringCreateWithCString(NULL,
238		  (char*) titleParams.title, kCFStringEncodingUTF8);
239
240	    if (hasImageOrBitmap) {
241		SetControlTitleWithCFString(mbPtr->control, cf);
242	    } else {
243		SetMenuItemTextWithCFString(mbPtr->menuRef, 1, cf);
244	    }
245	    CFRelease(cf);
246	    bcopy(titleParams.title, mbPtr->titleParams.title,
247		    titleParams.len + 1);
248	    mbPtr->titleParams.len = titleParams.len;
249	}
250	if ((titleChanged||styleChanged) && titleParams .len) {
251	    if (hasImageOrBitmap) {
252		err = ChkErr(SetControlFontStyle, mbPtr->control,
253			&titleParams.style);
254		if (err != noErr) {
255		    return;
256		}
257	    }
258	    bcopy(&titleParams.style, &mbPtr->titleParams.style,
259		    sizeof(titleParams.style));
260	}
261    }
262    if (hasImageOrBitmap) {
263	{
264	    destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin));
265	    portChanged = QDSwapPort(destPort, &savePort);
266	    mbPtr->picParams.version = -2;
267	    mbPtr->picParams.hRes = 0x00480000;
268	    mbPtr->picParams.vRes = 0x00480000;
269	    mbPtr->picParams.srcRect.top = 0;
270	    mbPtr->picParams.srcRect.left = 0;
271	    mbPtr->picParams.srcRect.bottom = height;
272	    mbPtr->picParams.srcRect.right = width;
273	    mbPtr->picParams.reserved1 = 0;
274	    mbPtr->picParams.reserved2 = 0;
275	    mbPtr->bevelButtonContent.contentType = kControlContentPictHandle;
276	    mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams);
277	    if (!mbPtr->bevelButtonContent.u.picture) {
278		TkMacOSXDbgMsg("OpenCPicture failed");
279	    }
280	    tkPictureIsOpen = 1;
281
282	    /*
283	     * TO DO - There is one case where XCopyPlane calls CopyDeepMask,
284	     * which does not get recorded in the picture. So the bitmap code
285	     * will fail in that case.
286	     */
287	}
288	if (butPtr->image != NULL) {
289	    Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 0, 0);
290	} else {
291	    GC gc;
292
293	    if (butPtr->state == STATE_DISABLED) {
294		gc = butPtr->disabledGC;
295	    } else if (butPtr->state == STATE_ACTIVE) {
296		gc = butPtr->activeTextGC;
297	    } else {
298		gc = butPtr->normalTextGC;
299	    }
300	    XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
301		    width, height, 0, 0, 1);
302	}
303	{
304	    ClosePicture();
305	    tkPictureIsOpen = 0;
306	    if (portChanged) {
307		QDSwapPort(savePort, NULL);
308	    }
309	}
310	ChkErr(SetControlData, mbPtr->control, kControlButtonPart,
311		kControlBevelButtonContentTag,
312		sizeof(ControlButtonContentInfo),
313		(char *) &mbPtr->bevelButtonContent);
314	switch (butPtr->anchor) {
315	    case TK_ANCHOR_N:
316		theAlignment = kControlBevelButtonAlignTop;
317		break;
318	    case TK_ANCHOR_NE:
319		theAlignment = kControlBevelButtonAlignTopRight;
320		break;
321	    case TK_ANCHOR_E:
322		theAlignment = kControlBevelButtonAlignRight;
323		break;
324	    case TK_ANCHOR_SE:
325		theAlignment = kControlBevelButtonAlignBottomRight;
326		break;
327	    case TK_ANCHOR_S:
328		theAlignment = kControlBevelButtonAlignBottom;
329		break;
330	    case TK_ANCHOR_SW:
331		theAlignment = kControlBevelButtonAlignBottomLeft;
332		break;
333	    case TK_ANCHOR_W:
334		theAlignment = kControlBevelButtonAlignLeft;
335		break;
336	    case TK_ANCHOR_NW:
337		theAlignment = kControlBevelButtonAlignTopLeft;
338		break;
339	    case TK_ANCHOR_CENTER:
340		theAlignment = kControlBevelButtonAlignCenter;
341		break;
342	}
343
344	ChkErr(SetControlData, mbPtr->control, kControlButtonPart,
345		kControlBevelButtonGraphicAlignTag,
346		sizeof(ControlButtonGraphicAlignment), (char *) &theAlignment);
347    }
348    active = ((mbPtr->flags & ACTIVE) != 0);
349    if (active != IsControlActive(mbPtr->control)) {
350	if (active) {
351	    ChkErr(ActivateControl, mbPtr->control);
352	} else {
353	    ChkErr(DeactivateControl, mbPtr->control);
354	}
355    }
356    enabled = !(butPtr->state == STATE_DISABLED);
357    if (enabled != IsControlEnabled(mbPtr->control)) {
358	if (enabled) {
359	    ChkErr(EnableControl, mbPtr->control);
360	} else {
361	    ChkErr(DisableControl, mbPtr->control);
362	}
363    }
364    if (active && enabled) {
365	if (butPtr->state == STATE_ACTIVE) {
366	    if (hasImageOrBitmap) {
367		HiliteControl(mbPtr->control, kControlButtonPart);
368	    } else {
369		HiliteControl(mbPtr->control, kControlLabelPart);
370	    }
371	} else {
372	    HiliteControl(mbPtr->control, kControlNoPart);
373	}
374    }
375    UpdateControlColors(mbPtr);
376    if (mbPtr->flags & FIRST_DRAW) {
377	ShowControl(mbPtr->control);
378	ShowControl(mbPtr->userPane);
379	mbPtr->flags ^= FIRST_DRAW;
380    } else {
381	SetControlVisibility(mbPtr->control, true, true);
382	Draw1Control(mbPtr->userPane);
383    }
384    if (hasImageOrBitmap) {
385	if (mbPtr->bevelButtonContent.contentType ==
386		kControlContentPictHandle) {
387	    KillPicture(mbPtr->bevelButtonContent.u.picture);
388	}
389    }
390}
391
392/*
393 *----------------------------------------------------------------------
394 *
395 * TkpDestroyMenuButton --
396 *
397 *	Free data structures associated with the menubutton control.
398 *
399 * Results:
400 *	None.
401 *
402 * Side effects:
403 *	Restores the default control state.
404 *
405 *----------------------------------------------------------------------
406 */
407
408void
409TkpDestroyMenuButton(
410    TkMenuButton *mbPtr)
411{
412    MacMenuButton *macMbPtr = (MacMenuButton *) mbPtr;
413
414    if (macMbPtr->userPane) {
415	DisposeControl(macMbPtr->userPane);
416	macMbPtr->userPane = NULL;
417    }
418    if (macMbPtr->menuRef) {
419	short menuID = GetMenuID(macMbPtr->menuRef);
420
421	TkMacOSXFreeMenuID(menuID);
422	DisposeMenu(macMbPtr->menuRef);
423	macMbPtr->menuRef = NULL;
424    }
425}
426
427/*
428 *----------------------------------------------------------------------
429 *
430 * TkpComputeMenuButtonGeometry --
431 *
432 *	After changes in a menu button's text or bitmap, this procedure
433 *	recomputes the menu button's geometry and passes this information
434 *	along to the geometry manager for the window.
435 *
436 * Results:
437 *	None.
438 *
439 * Side effects:
440 *	The menu button's window may change size.
441 *
442 *----------------------------------------------------------------------
443 */
444
445void
446TkpComputeMenuButtonGeometry(mbPtr)
447    register TkMenuButton *mbPtr;	/* Widget record for menu button. */
448{
449    int width, height, mm, pixels;
450    int hasImageOrBitmap = 0;
451
452    mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth;
453    if (mbPtr->image != None) {
454	Tk_SizeOfImage(mbPtr->image, &width, &height);
455	hasImageOrBitmap = 1;
456    } else if (mbPtr->bitmap != None) {
457	Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
458	hasImageOrBitmap = 1;
459    } else {
460	hasImageOrBitmap = 0;
461	Tk_FreeTextLayout(mbPtr->textLayout);
462	mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text,
463		-1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth,
464		&mbPtr->textHeight);
465	width = mbPtr->textWidth;
466	height = mbPtr->textHeight;
467	if (mbPtr->width > 0) {
468	    width = mbPtr->width * Tk_TextWidth(mbPtr->tkfont, "0", 1);
469	}
470	if (mbPtr->height > 0) {
471	    Tk_FontMetrics fm;
472
473	    Tk_GetFontMetrics(mbPtr->tkfont, &fm);
474	    height = mbPtr->height * fm.linespace;
475	}
476	width += 2*mbPtr->padX;
477	height += 2*mbPtr->padY;
478    }
479    if (hasImageOrBitmap) {
480	if (mbPtr->width > 0) {
481	    width = mbPtr->width;
482	}
483	if (mbPtr->height > 0) {
484	    height = mbPtr->height;
485	}
486	mbPtr->inset = mbPtr->highlightWidth + 2;
487	width += (2 * mbPtr->borderWidth + 4);
488	height += (2 * mbPtr->borderWidth + 4);
489    } else {
490	width += TK_POPUP_OFFSET;
491    }
492    if (mbPtr->indicatorOn) {
493	mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin));
494	pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin));
495	mbPtr->indicatorHeight = kTriangleHeight;
496	mbPtr->indicatorWidth = kTriangleWidth + kTriangleMargin;
497	width += mbPtr->indicatorWidth;
498    } else {
499	mbPtr->indicatorHeight = 0;
500	mbPtr->indicatorWidth = 0;
501    }
502
503    Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset),
504	    (int) (height + 2*mbPtr->inset));
505    Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);
506}
507
508/*
509 *----------------------------------------------------------------------
510 *
511 * ComputeMenuButtonControlParams --
512 *
513 *	This procedure computes the various parameters used
514 *	when creating a Carbon control (NewControl)
515 *	These are determined by the various tk menu button parameters
516 *
517 * Results:
518 *	None.
519 *
520 * Side effects:
521 *	Sets the control initialisation parameters
522 *
523 *----------------------------------------------------------------------
524 */
525
526static void
527ComputeMenuButtonControlParams(
528    TkMenuButton *mbPtr,
529    MenuButtonControlParams *paramsPtr)
530{
531    int fakeMenuID = 256;
532
533    /*
534     * Determine ProcID based on button type and dimensions
535     *
536     * We need to set minValue to some non-zero value,
537     * Otherwise, the markers do not show up
538     */
539
540    paramsPtr->minValue = kControlBehaviorMultiValueMenu;
541    paramsPtr->maxValue = 0;
542    if (mbPtr->image || mbPtr->bitmap) {
543	paramsPtr->isBevel = 1;
544	if (mbPtr->borderWidth <= 2) {
545	    paramsPtr->procID = kControlBevelButtonSmallBevelProc;
546	} else if (mbPtr->borderWidth == 3) {
547	    paramsPtr->procID = kControlBevelButtonNormalBevelProc;
548	} else {
549	    paramsPtr->procID = kControlBevelButtonLargeBevelProc;
550	}
551	if (mbPtr->indicatorOn) {
552	    paramsPtr->initialValue = fakeMenuID;
553	} else {
554	    paramsPtr->initialValue = 0;
555	}
556    } else {
557	paramsPtr->isBevel = 0;
558	paramsPtr->procID = kControlPopupButtonProc
559		+ kControlPopupVariableWidthVariant;
560	paramsPtr->minValue = -12345;
561	paramsPtr->maxValue = -1;
562	paramsPtr->initialValue = 0;
563    }
564}
565
566/*
567 *----------------------------------------------------------------------
568 *
569 * returns 0 if same, 1 otherwise
570 *
571 *----------------------------------------------------------------------
572 */
573
574static void
575CompareControlTitleParams(
576    ControlTitleParams *p1Ptr,
577    ControlTitleParams *p2Ptr,
578    int *titleChanged,
579    int *styleChanged)
580{
581    if (p1Ptr->len != p2Ptr->len) {
582	*titleChanged = 1;
583    } else if (bcmp(p1Ptr->title,p2Ptr->title,p1Ptr->len)) {
584	*titleChanged = 1;
585    } else {
586	*titleChanged = 0;
587    }
588
589    if (p1Ptr->len && p2Ptr->len) {
590	*styleChanged = bcmp(&p1Ptr->style, &p2Ptr->style,
591		sizeof(p2Ptr->style));
592    } else {
593	*styleChanged = p1Ptr->len||p2Ptr->len;
594    }
595}
596
597static void
598ComputeControlTitleParams(
599    TkMenuButton *butPtr,
600    ControlTitleParams *paramsPtr)
601{
602    Tk_Font font;
603
604    paramsPtr->len = TkFontGetFirstTextLayout(butPtr->textLayout, &font,
605	    (char*) paramsPtr->title);
606    paramsPtr->title[paramsPtr->len] = 0;
607    if (paramsPtr->len) {
608	TkMacOSXInitControlFontStyle(font,&paramsPtr->style);
609    }
610}
611
612/*
613 *----------------------------------------------------------------------
614 *
615 * MenuButtonInitControl --
616 *
617 *	This procedure initialises a Carbon control
618 *
619 * Results:
620 *	0 on success, 1 on failure.
621 *
622 * Side effects:
623 *	A background pane control and the control itself is created
624 *	The contol is embedded in the background control
625 *	The background control is embedded in the root control
626 *	of the containing window
627 *	The creation parameters for the control are also computed
628 *
629 *----------------------------------------------------------------------
630 */
631int
632MenuButtonInitControl(
633    MacMenuButton *mbPtr,	/* Mac button. */
634    Rect *paneRect,
635    Rect *cntrRect)
636{
637    OSStatus err;
638    TkMenuButton *butPtr = (TkMenuButton *) mbPtr;
639    SInt16 procID, initialValue, minValue, maxValue;
640    Boolean initiallyVisible;
641    SInt32 controlReference;
642    short menuID;
643    ControlRef rootControl =
644	    TkMacOSXGetRootControl(Tk_WindowId(butPtr->tkwin));
645
646    mbPtr->windowRef = TkMacOSXDrawableWindow(Tk_WindowId(butPtr->tkwin));
647
648    /*
649     * Set up the user pane
650     */
651
652    initiallyVisible = false;
653    initialValue = kControlSupportsEmbedding | kControlHasSpecialBackground;
654    minValue = 0;
655    maxValue = 1;
656    procID = kControlUserPaneProc;
657    controlReference = (SInt32)mbPtr;
658    mbPtr->userPane = NewControl(mbPtr->windowRef, paneRect, "\p",
659	    initiallyVisible, initialValue, minValue, maxValue, procID,
660	    controlReference);
661    if (!mbPtr->userPane) {
662	TkMacOSXDbgMsg("Failed to create user pane control");
663	return 1;
664    }
665    err = ChkErr(EmbedControl, mbPtr->userPane, rootControl);
666    if (err != noErr) {
667	return 1;
668    }
669    SetUserPaneSetUpSpecialBackgroundProc(mbPtr->userPane,
670	    UserPaneBackgroundProc);
671    SetUserPaneDrawProc(mbPtr->userPane,UserPaneDraw);
672    initiallyVisible = false;
673    ComputeMenuButtonControlParams(butPtr,&mbPtr->params);
674
675    /*
676     * Do this only if we are using bevel buttons.
677     */
678
679    ComputeControlTitleParams(butPtr, &mbPtr->titleParams);
680    mbPtr->control = NewControl(mbPtr->windowRef,
681	    cntrRect, "\p" /* mbPtr->titleParams.title */,
682	    initiallyVisible, mbPtr->params.initialValue,
683	    mbPtr->params.minValue, mbPtr->params.maxValue,
684	    mbPtr->params.procID, controlReference);
685    if (!mbPtr->control) {
686	TkMacOSXDbgMsg("Failed to create control of type %d",
687		mbPtr->params.procID);
688	return 1;
689    }
690    err = ChkErr(EmbedControl, mbPtr->control, mbPtr->userPane);
691    if (err != noErr ) {
692	return 1;
693    }
694    if (mbPtr->params.isBevel) {
695	CFStringRef cf = CFStringCreateWithCString(NULL,
696		(char*) mbPtr->titleParams.title, kCFStringEncodingUTF8);
697
698	SetControlTitleWithCFString(mbPtr->control, cf);
699	CFRelease(cf);
700	if (mbPtr->titleParams.len) {
701	    err = ChkErr(SetControlFontStyle, mbPtr->control,
702		    &mbPtr->titleParams.style);
703	    if (err != noErr) {
704		return 1;
705	    }
706	}
707    } else {
708	CFStringRef cfStr;
709
710	err = TkMacOSXGetNewMenuID(mbPtr->info.interp, (TkMenu *) mbPtr, 0,
711		&menuID);
712	if (err != TCL_OK) {
713	    return 1;
714	}
715	err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys,
716		&(mbPtr->menuRef));
717	if (err != noErr) {
718	    return 1;
719	}
720	cfStr = CFStringCreateWithCString(NULL, Tk_PathName(mbPtr->info.tkwin),
721		kCFStringEncodingUTF8);
722	if (!cfStr) {
723	    TkMacOSXDbgMsg("CFStringCreateWithCString failed");
724	    return 1;
725	}
726	err = ChkErr(SetMenuTitleWithCFString, mbPtr->menuRef, cfStr);
727	CFRelease(cfStr);
728	if (err != noErr) {
729	    return 1;
730	}
731	cfStr = CFStringCreateWithCString(NULL,
732		(char*) mbPtr->titleParams.title, kCFStringEncodingUTF8);
733	AppendMenuItemText(mbPtr->menuRef, "\px");
734	if (cfStr) {
735	    SetMenuItemTextWithCFString(mbPtr->menuRef, 1, cfStr);
736	    CFRelease(cfStr);
737	}
738	ChkErr(SetControlData, mbPtr->control, kControlNoPart,
739		kControlPopupButtonMenuRefTag, sizeof(mbPtr->menuRef),
740		&mbPtr->menuRef);
741	SetControlMinimum(mbPtr->control, 1);
742	SetControlMaximum(mbPtr->control, 1);
743	SetControlValue(mbPtr->control, 1);
744    }
745    mbPtr->flags |= FIRST_DRAW;
746    if (IsWindowActive(mbPtr->windowRef)) {
747	mbPtr->flags |= ACTIVE;
748    }
749    return 0;
750}
751
752/*
753 *--------------------------------------------------------------
754 *
755 * SetUserPane
756 *
757 *	Utility function to add a UserPaneDrawProc
758 *	to a userPane control. From MoreControls code
759 *	from Apple DTS.
760 *
761 * Results:
762 *	MacOS system error.
763 *
764 * Side effects:
765 *	The user pane gets a new UserPaneDrawProc.
766 *
767 *--------------------------------------------------------------
768 */
769OSStatus
770SetUserPaneDrawProc(
771    ControlRef control,
772    ControlUserPaneDrawProcPtr upp)
773{
774    ControlUserPaneDrawUPP myControlUserPaneDrawUPP =
775	    NewControlUserPaneDrawUPP(upp);
776
777    return SetControlData(control, kControlNoPart,kControlUserPaneDrawProcTag,
778	    sizeof(myControlUserPaneDrawUPP), (Ptr)&myControlUserPaneDrawUPP);
779}
780
781/*
782 *--------------------------------------------------------------
783 *
784 * SetUserPaneSetUpSpecialBackgroundProc --
785 *
786 *	Utility function to add a UserPaneBackgroundProc
787 *	to a userPane control
788 *
789 * Results:
790 *	MacOS system error.
791 *
792 * Side effects:
793 *	The user pane gets a new UserPaneBackgroundProc.
794 *
795 *--------------------------------------------------------------
796 */
797
798OSStatus
799SetUserPaneSetUpSpecialBackgroundProc(
800    ControlRef control,
801    ControlUserPaneBackgroundProcPtr upp)
802{
803    ControlUserPaneBackgroundUPP myControlUserPaneBackgroundUPP =
804	    NewControlUserPaneBackgroundUPP(upp);
805
806    return SetControlData(control, kControlNoPart,
807	kControlUserPaneBackgroundProcTag,
808	sizeof(myControlUserPaneBackgroundUPP),
809	(Ptr) &myControlUserPaneBackgroundUPP);
810}
811
812/*
813 *--------------------------------------------------------------
814 *
815 * UserPaneDraw --
816 *
817 *	This function draws the background of the user pane that will
818 *	lie under checkboxes and radiobuttons.
819 *
820 * Results:
821 *	None.
822 *
823 * Side effects:
824 *	The user pane gets updated to the current color.
825 *
826 *--------------------------------------------------------------
827 */
828
829void
830UserPaneDraw(
831    ControlRef control,
832    ControlPartCode cpc)
833{
834    Rect contrlRect;
835    MacMenuButton * mbPtr =
836	    (MacMenuButton *)(intptr_t)GetControlReference(control);
837    CGrafPtr port;
838
839    GetPort(&port);
840    GetControlBounds(control,&contrlRect);
841    TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port);
842    EraseRect (&contrlRect);
843}
844
845/*
846 *--------------------------------------------------------------
847 *
848 * UserPaneBackgroundProc --
849 *
850 *	This function sets up the background of the user pane that will
851 *	lie under checkboxes and radiobuttons.
852 *
853 * Results:
854 *	None.
855 *
856 * Side effects:
857 *	The user pane background gets set to the current color.
858 *
859 *--------------------------------------------------------------
860 */
861
862void
863UserPaneBackgroundProc(
864    ControlHandle control,
865    ControlBackgroundPtr info)
866{
867    MacMenuButton *mbPtr =
868	    (MacMenuButton *)(intptr_t)GetControlReference(control);
869
870    if (info->colorDevice) {
871	CGrafPtr port;
872
873	GetPort(&port);
874	TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port);
875    }
876}
877
878/*
879 *--------------------------------------------------------------
880 *
881 * UpdateControlColors --
882 *
883 *	This function will review the colors used to display
884 *	a Macintosh button. If any non-standard colors are
885 *	used we create a custom palette for the button, populate
886 *	with the colors for the button and install the palette.
887 *
888 *	Under Appearance, we just set the pointer that will be
889 *	used by the UserPaneDrawProc.
890 *
891 * Results:
892 *	None.
893 *
894 * Side effects:
895 *	The Macintosh control may get a custom palette installed.
896 *
897 *--------------------------------------------------------------
898 */
899
900static int
901UpdateControlColors(
902    MacMenuButton *mbPtr)
903{
904    XColor *xcolor;
905    TkMenuButton * butPtr = (TkMenuButton *) mbPtr;
906
907    /*
908     * Under Appearance we cannot change the background of the
909     * button itself. However, the color we are setting is the color
910     * of the containing userPane. This will be the color that peeks
911     * around the rounded corners of the button.
912     * We make this the highlightbackground rather than the background,
913     * because if you color the background of a frame containing a
914     * button, you usually also color the highlightbackground as well,
915     * or you will get a thin grey ring around the button.
916     */
917
918    xcolor = Tk_3DBorderColor(butPtr->normalBorder);
919    mbPtr->userPaneBackground = xcolor->pixel;
920
921    return false;
922}
923
924/*
925 *--------------------------------------------------------------
926 *
927 * MenuButtonEventProc --
928 *
929 *	This procedure is invoked by the Tk dispatcher for various
930 *	events on buttons.
931 *
932 * Results:
933 *	None.
934 *
935 * Side effects:
936 *	When it gets exposed, it is redisplayed.
937 *
938 *--------------------------------------------------------------
939 */
940
941static void
942MenuButtonEventProc(
943    ClientData clientData,	/* Information about window. */
944    XEvent *eventPtr)		/* Information about event. */
945{
946    TkMenuButton *buttonPtr = (TkMenuButton *) clientData;
947    MacMenuButton *mbPtr = (MacMenuButton *) clientData;
948
949    if (eventPtr->type == ActivateNotify
950	    || eventPtr->type == DeactivateNotify) {
951	if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) {
952	    return;
953	}
954	if (eventPtr->type == ActivateNotify) {
955	    mbPtr->flags |= ACTIVE;
956	} else {
957	    mbPtr->flags &= ~ACTIVE;
958	}
959	if ((buttonPtr->flags & REDRAW_PENDING) == 0) {
960	    Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) buttonPtr);
961	    buttonPtr->flags |= REDRAW_PENDING;
962	}
963    }
964}
965