1/*
2 * tkMacOSXScale.c --
3 *
4 *	This file implements the Macintosh specific portion of the
5 *	scale widget.
6 *
7 * Copyright (c) 1996 by Sun Microsystems, Inc.
8 * Copyright (c) 1998-2000 by Scriptics Corporation.
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: tkMacOSXScale.c,v 1.2.2.9 2007/06/29 03:22:02 das Exp $
15 */
16
17#include "tkMacOSXPrivate.h"
18#include "tkScale.h"
19
20/*
21#ifdef TK_MAC_DEBUG
22#define TK_MAC_DEBUG_SCALE
23#endif
24*/
25
26/*
27 * Defines used in this file.
28 */
29
30#define slider		1110
31#define inSlider	1
32#define inInc		2
33#define inDecr		3
34
35/*
36 * Declaration of Macintosh specific scale structure.
37 */
38
39typedef struct MacScale {
40    TkScale info;		/* Generic scale info. */
41    int flags;			/* Flags. */
42    ControlRef scaleHandle;	/* Handle to the Scale control struct. */
43} MacScale;
44
45/*
46 * Globals uses locally in this file.
47 */
48static ControlActionUPP scaleActionProc = NULL; /* Pointer to func. */
49
50/*
51 * Forward declarations for procedures defined later in this file:
52 */
53
54static void MacScaleEventProc(ClientData clientData, XEvent *eventPtr);
55static pascal void ScaleActionProc(ControlRef theControl,
56	ControlPartCode partCode);
57
58
59/*
60 *----------------------------------------------------------------------
61 *
62 * TkpCreateScale --
63 *
64 *	Allocate a new TkScale structure.
65 *
66 * Results:
67 *	Returns a newly allocated TkScale structure.
68 *
69 * Side effects:
70 *	None.
71 *
72 *----------------------------------------------------------------------
73 */
74
75TkScale *
76TkpCreateScale(
77    Tk_Window tkwin)
78{
79    MacScale *macScalePtr = (MacScale *) ckalloc(sizeof(MacScale));
80
81    macScalePtr->scaleHandle = NULL;
82    if (scaleActionProc == NULL) {
83	scaleActionProc = NewControlActionUPP(ScaleActionProc);
84    }
85
86    Tk_CreateEventHandler(tkwin, ButtonPressMask,
87	    MacScaleEventProc, (ClientData) macScalePtr);
88
89    return (TkScale *) macScalePtr;
90}
91
92/*
93 *----------------------------------------------------------------------
94 *
95 * TkpDestroyScale --
96 *
97 *	Free Macintosh specific resources.
98 *
99 * Results:
100 *	None
101 *
102 * Side effects:
103 *	The slider control is destroyed.
104 *
105 *----------------------------------------------------------------------
106 */
107
108void
109TkpDestroyScale(
110    TkScale *scalePtr)
111{
112    MacScale *macScalePtr = (MacScale *) scalePtr;
113
114    /*
115     * Free Macintosh control.
116     */
117
118    if (macScalePtr->scaleHandle != NULL) {
119	DisposeControl(macScalePtr->scaleHandle);
120    }
121}
122
123/*
124 *----------------------------------------------------------------------
125 *
126 * TkpDisplayScale --
127 *
128 *	This procedure is invoked as an idle handler to redisplay
129 *	the contents of a scale widget.
130 *
131 * Results:
132 *	None.
133 *
134 * Side effects:
135 *	The scale gets redisplayed.
136 *
137 *----------------------------------------------------------------------
138 */
139
140void
141TkpDisplayScale(
142    ClientData clientData)	/* Widget record for scale. */
143{
144    TkScale *scalePtr = (TkScale *) clientData;
145    Tk_Window tkwin = scalePtr->tkwin;
146    Tcl_Interp *interp = scalePtr->interp;
147    int result;
148    char string[PRINT_CHARS];
149    MacScale *macScalePtr = (MacScale *) clientData;
150    Rect r;
151    WindowRef windowRef;
152    CGrafPtr destPort, savePort;
153    Boolean portChanged;
154    MacDrawable *macDraw;
155    SInt32 initialValue, minValue, maxValue;
156    UInt16 numTicks;
157
158#ifdef TK_MAC_DEBUG_SCALE
159    TkMacOSXDbgMsg("TkpDisplayScale");
160#endif
161    scalePtr->flags &= ~REDRAW_PENDING;
162    if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(scalePtr->tkwin)) {
163	goto done;
164    }
165
166    /*
167     * Invoke the scale's command if needed.
168     */
169
170    Tcl_Preserve((ClientData) scalePtr);
171    if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
172	Tcl_Preserve((ClientData) interp);
173	sprintf(string, scalePtr->format, scalePtr->value);
174	result = Tcl_VarEval(interp, scalePtr->command, " ", string, NULL);
175	if (result != TCL_OK) {
176	    Tcl_AddErrorInfo(interp, "\n    (command executed by scale)");
177	    Tcl_BackgroundError(interp);
178	}
179	Tcl_Release((ClientData) interp);
180    }
181    scalePtr->flags &= ~INVOKE_COMMAND;
182    if (scalePtr->flags & SCALE_DELETED) {
183	Tcl_Release((ClientData) scalePtr);
184	return;
185    }
186    Tcl_Release((ClientData) scalePtr);
187
188    /*
189     * Now handle the part of redisplay that is the same for
190     * horizontal and vertical scales: border and traversal
191     * highlight.
192     */
193
194    if (scalePtr->highlightWidth != 0) {
195	GC gc = Tk_GCForColor(scalePtr->highlightColorPtr, Tk_WindowId(tkwin));
196
197	Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth,
198		Tk_WindowId(tkwin));
199    }
200    Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), scalePtr->bgBorder,
201	    scalePtr->highlightWidth, scalePtr->highlightWidth,
202	    Tk_Width(tkwin) - 2*scalePtr->highlightWidth,
203	    Tk_Height(tkwin) - 2*scalePtr->highlightWidth,
204	    scalePtr->borderWidth, scalePtr->relief);
205
206    /*
207     * Set up port for drawing Macintosh control.
208     */
209
210    macDraw = (MacDrawable *) Tk_WindowId(tkwin);
211    destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin));
212    windowRef = TkMacOSXDrawableWindow(Tk_WindowId(tkwin));
213    portChanged = QDSwapPort(destPort, &savePort);
214    TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin));
215
216    /*
217     * Create Macintosh control.
218     */
219
220#define MAC_OSX_SCROLL_WIDTH 10
221
222    if (scalePtr->orient == ORIENT_HORIZONTAL) {
223	int offset = (Tk_Height(tkwin) - MAC_OSX_SCROLL_WIDTH)/2;
224
225	if (offset < 0) {
226	    offset = 0;
227	}
228
229	r.left = macDraw->xOff + scalePtr->inset;
230	r.top = macDraw->yOff + offset;
231	r.right = macDraw->xOff+Tk_Width(tkwin) - scalePtr->inset;
232	r.bottom = macDraw->yOff + offset + MAC_OSX_SCROLL_WIDTH/2;
233    } else {
234	int offset = (Tk_Width(tkwin) - MAC_OSX_SCROLL_WIDTH)/2;
235
236	if (offset < 0) {
237	    offset = 0;
238	}
239
240	r.left = macDraw->xOff + offset;
241	r.top = macDraw->yOff + scalePtr->inset;
242	r.right = macDraw->xOff + offset + MAC_OSX_SCROLL_WIDTH/2;
243	r.bottom = macDraw->yOff+Tk_Height(tkwin) - scalePtr->inset;
244    }
245
246    if (macScalePtr->scaleHandle == NULL) {
247#ifdef TK_MAC_DEBUG_SCALE
248	TkMacOSXDbgMsg("Initialising scale");
249#endif
250	initialValue = scalePtr->value;
251	if (scalePtr->orient == ORIENT_HORIZONTAL) {
252	    minValue = scalePtr->fromValue;
253	    maxValue = scalePtr->toValue;
254	} else {
255	    minValue = scalePtr->fromValue;
256	    maxValue = scalePtr->toValue;
257	}
258
259	if (scalePtr->tickInterval == 0) {
260	    numTicks = 0;
261	} else {
262	    numTicks = (maxValue - minValue)/scalePtr->tickInterval;
263	}
264
265	CreateSliderControl(windowRef, &r, initialValue, minValue, maxValue,
266		kControlSliderPointsDownOrRight, numTicks, 1, scaleActionProc,
267		&(macScalePtr->scaleHandle));
268	SetControlReference(macScalePtr->scaleHandle, (UInt32) scalePtr);
269
270	if (IsWindowActive(windowRef)) {
271	    macScalePtr->flags |= ACTIVE;
272	}
273    } else {
274	SetControlBounds(macScalePtr->scaleHandle, &r);
275	SetControl32BitValue(macScalePtr->scaleHandle, scalePtr->value);
276	SetControl32BitMinimum(macScalePtr->scaleHandle, scalePtr->fromValue);
277	SetControl32BitMaximum(macScalePtr->scaleHandle, scalePtr->toValue);
278    }
279
280    /*
281     * Finally draw the control.
282     */
283
284    SetControlVisibility(macScalePtr->scaleHandle,true,true);
285    HiliteControl(macScalePtr->scaleHandle,0);
286    Draw1Control(macScalePtr->scaleHandle);
287
288    if (portChanged) {
289	QDSwapPort(savePort, NULL);
290    }
291done:
292    scalePtr->flags &= ~REDRAW_ALL;
293}
294
295/*
296 *----------------------------------------------------------------------
297 *
298 * TkpScaleElement --
299 *
300 *	Determine which part of a scale widget lies under a given
301 *	point.
302 *
303 * Results:
304 *	The return value is either TROUGH1, SLIDER, TROUGH2, or
305 *	OTHER, depending on which of the scale's active elements
306 *	(if any) is under the point at (x,y).
307 *
308 * Side effects:
309 *	None.
310 *
311 *----------------------------------------------------------------------
312 */
313
314int
315TkpScaleElement(
316    TkScale *scalePtr,		/* Widget record for scale. */
317    int x, int y)		/* Coordinates within scalePtr's window. */
318{
319    MacScale *macScalePtr = (MacScale *) scalePtr;
320    ControlPartCode part;
321    Point where;
322    Rect bounds;
323    CGrafPtr destPort, savePort;
324    Boolean portChanged;
325
326#ifdef TK_MAC_DEBUG_SCALE
327    TkMacOSXDbgMsg("TkpScaleElement");
328#endif
329    destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scalePtr->tkwin));
330    portChanged = QDSwapPort(destPort, &savePort);
331
332    /*
333     * All of the calculations in this procedure mirror those in
334     * DisplayScrollbar. Be sure to keep the two consistent.
335     */
336
337    TkMacOSXWinBounds((TkWindow *) scalePtr->tkwin, &bounds);
338    where.h = x + bounds.left;
339    where.v = y + bounds.top;
340    part = TestControl(macScalePtr->scaleHandle, where);
341
342    if (portChanged) {
343	QDSwapPort(savePort, NULL);
344    }
345
346#ifdef TK_MAC_DEBUG_SCALE
347    fprintf (stderr,"ScalePart %d, pos ( %d %d )\n", part, where.h, where.v );
348#endif
349
350    switch (part) {
351	case inSlider:
352	    return SLIDER;
353	case inInc:
354	    if (scalePtr->orient == ORIENT_VERTICAL) {
355		return TROUGH1;
356	    } else {
357		return TROUGH2;
358	    }
359	case inDecr:
360	    if (scalePtr->orient == ORIENT_VERTICAL) {
361		return TROUGH2;
362	    } else {
363		return TROUGH1;
364	    }
365	default:
366	    return OTHER;
367    }
368}
369
370/*
371 *--------------------------------------------------------------
372 *
373 * MacScaleEventProc --
374 *
375 *	This procedure is invoked by the Tk dispatcher for
376 *	ButtonPress events on scales.
377 *
378 * Results:
379 *	None.
380 *
381 * Side effects:
382 *	When the window gets deleted, internal structures get
383 *	cleaned up. When it gets exposed, it is redisplayed.
384 *
385 *--------------------------------------------------------------
386 */
387
388static void
389MacScaleEventProc(
390    ClientData clientData,	/* Information about window. */
391    XEvent *eventPtr)		/* Information about event. */
392{
393    MacScale *macScalePtr = (MacScale *) clientData;
394    Point where;
395    Rect bounds;
396    int part;
397    CGrafPtr destPort, savePort;
398    Boolean portChanged;
399
400#ifdef TK_MAC_DEBUG_SCALE
401    fprintf(stderr,"MacScaleEventProc\n" );
402#endif
403
404    /*
405     * To call Macintosh control routines we must have the port
406     * set to the window containing the control. We will then test
407     * which part of the control was hit and act accordingly.
408     */
409
410    destPort = TkMacOSXGetDrawablePort(Tk_WindowId(macScalePtr->info.tkwin));
411    portChanged = QDSwapPort(destPort, &savePort);
412    TkMacOSXSetUpClippingRgn(Tk_WindowId(macScalePtr->info.tkwin));
413
414    TkMacOSXWinBounds((TkWindow *) macScalePtr->info.tkwin, &bounds);
415    where.h = eventPtr->xbutton.x + bounds.left;
416    where.v = eventPtr->xbutton.y + bounds.top;
417#ifdef TK_MAC_DEBUG_SCALE
418    TkMacOSXDbgMsg("calling TestControl");
419#endif
420    part = TestControl(macScalePtr->scaleHandle, where);
421    if (part == 0) {
422	return;
423    }
424
425    TkMacOSXTrackingLoop(1);
426    part = HandleControlClick(macScalePtr->scaleHandle, where,
427	    TkMacOSXModifierState(), scaleActionProc);
428    TkMacOSXTrackingLoop(0);
429
430    /*
431     * Update the value for the widget.
432     */
433
434    macScalePtr->info.value = GetControlValue(macScalePtr->scaleHandle);
435    /* TkScaleSetValue(&macScalePtr->info, macScalePtr->info.value, 1, 0); */
436
437    /*
438     * The HandleControlClick call will "eat" the ButtonUp event. We now
439     * generate a ButtonUp event so Tk will unset implicit grabs etc.
440     */
441
442    TkGenerateButtonEventForXPointer(Tk_WindowId(macScalePtr->info.tkwin));
443
444    if (portChanged) {
445	QDSwapPort(savePort, NULL);
446    }
447}
448
449/*
450 *--------------------------------------------------------------
451 *
452 * ScaleActionProc --
453 *
454 *	Callback procedure used by the Macintosh toolbox call
455 *	HandleControlClick. This call will update the display
456 *	while the scrollbar is being manipulated by the user.
457 *
458 * Results:
459 *	None.
460 *
461 * Side effects:
462 *	May change the display.
463 *
464 *--------------------------------------------------------------
465 */
466
467static pascal void
468ScaleActionProc(
469    ControlRef theControl,	/* Handle to scrollbat control */
470    ControlPartCode partCode)	/* Part of scrollbar that was "hit" */
471{
472    int value;
473    TkScale *scalePtr = (TkScale *) GetControlReference(theControl);
474
475#ifdef TK_MAC_DEBUG_SCALE
476    TkMacOSXDbgMsg("ScaleActionProc");
477#endif
478    value = GetControlValue(theControl);
479    TkScaleSetValue(scalePtr, value, 1, 1);
480    Tcl_Preserve((ClientData) scalePtr);
481    TkMacOSXRunTclEventLoop();
482    Tcl_Release((ClientData) scalePtr);
483}
484
485