1 /*
2  * tkMacOSXScale.c --
3  *
4  *	This file implements the Macintosh specific portion of the
5  *	scale widget.
6  *
7  * Copyright © 1996 Sun Microsystems, Inc.
8  * Copyright © 1998-2000 Scriptics Corporation.
9  * Copyright © 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
10  * Copyright © 2008-2009 Apple Inc.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  */
15 
16 #include "tkMacOSXPrivate.h"
17 #include "tkScale.h"
18 
19 #ifdef MAC_OSX_TK_TODO
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 
39 typedef 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  */
48 static ControlActionUPP scaleActionProc = NULL; /* Pointer to func. */
49 
50 /*
51  * Forward declarations for procedures defined later in this file:
52  */
53 
54 static void		MacScaleEventProc(ClientData clientData,
55 			    XEvent *eventPtr);
56 static pascal void	ScaleActionProc(ControlRef theControl,
57 			    ControlPartCode partCode);
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 
75 TkScale *
TkpCreateScale(Tk_Window tkwin)76 TkpCreateScale(
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, 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 
108 void
TkpDestroyScale(TkScale * scalePtr)109 TkpDestroyScale(
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 the contents
129  *	of a scale widget.
130  *
131  * Results:
132  *	None.
133  *
134  * Side effects:
135  *	The scale gets redisplayed.
136  *
137  *----------------------------------------------------------------------
138  */
139 
140 void
TkpDisplayScale(ClientData clientData)141 TkpDisplayScale(
142     ClientData clientData)	/* Widget record for scale. */
143 {
144     TkScale *scalePtr = clientData;
145     Tk_Window tkwin = scalePtr->tkwin;
146     Tcl_Interp *interp = scalePtr->interp;
147     int result;
148     char string[TCL_DOUBLE_SPACE];
149     MacScale *macScalePtr = clientData;
150     Rect r;
151     WindowRef windowRef;
152     MacDrawable *macDraw;
153     SInt32 initialValue, minValue, maxValue;
154     UInt16 numTicks;
155     Tcl_DString buf;
156 
157 #ifdef TK_MAC_DEBUG_SCALE
158     TkMacOSXDbgMsg("TkpDisplayScale");
159 #endif
160     scalePtr->flags &= ~REDRAW_PENDING;
161     if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(scalePtr->tkwin)) {
162 	goto done;
163     }
164 
165     /*
166      * Invoke the scale's command if needed.
167      */
168 
169     Tcl_Preserve(scalePtr);
170     if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
171 	Tcl_Preserve(interp);
172         if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->format,
173                 scalePtr->value) < 0) {
174             string[TCL_DOUBLE_SPACE - 1] = '\0';
175         }
176 	Tcl_DStringInit(&buf);
177 	Tcl_DStringAppend(&buf, scalePtr->command, -1);
178 	Tcl_DStringAppend(&buf, " ", -1);
179 	Tcl_DStringAppend(&buf, string, -1);
180 	result = Tcl_EvalEx(interp, Tcl_DStringValue(&buf), -1, TCL_EVAL_GLOBAL);
181 	Tcl_DStringFree(&buf);
182 	if (result != TCL_OK) {
183 	    Tcl_AddErrorInfo(interp, "\n    (command executed by scale)");
184 	    Tcl_BackgroundException(interp, result);
185 	}
186 	Tcl_Release(interp);
187     }
188     scalePtr->flags &= ~INVOKE_COMMAND;
189     if (scalePtr->flags & SCALE_DELETED) {
190 	Tcl_Release(scalePtr);
191 	return;
192     }
193     Tcl_Release(scalePtr);
194 
195     /*
196      * Now handle the part of redisplay that is the same for horizontal and
197      * vertical scales: border and traversal highlight.
198      */
199 
200     if (scalePtr->highlightWidth != 0) {
201 	GC gc = Tk_GCForColor(scalePtr->highlightColorPtr, Tk_WindowId(tkwin));
202 
203 	Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth,
204 		Tk_WindowId(tkwin));
205     }
206     Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), scalePtr->bgBorder,
207 	    scalePtr->highlightWidth, scalePtr->highlightWidth,
208 	    Tk_Width(tkwin) - 2*scalePtr->highlightWidth,
209 	    Tk_Height(tkwin) - 2*scalePtr->highlightWidth,
210 	    scalePtr->borderWidth, scalePtr->relief);
211 
212     /*
213      * Set up port for drawing Macintosh control.
214      */
215 
216     macDraw = (MacDrawable *)Tk_WindowId(tkwin);
217     windowRef = TkMacOSXGetNSWindowForDrawable(Tk_WindowId(tkwin));
218 
219     /*
220      * Create Macintosh control.
221      */
222 
223 #define MAC_OSX_SCROLL_WIDTH 10
224 
225     if (scalePtr->orient == ORIENT_HORIZONTAL) {
226 	int offset = (Tk_Height(tkwin) - MAC_OSX_SCROLL_WIDTH) / 2;
227 
228 	if (offset < 0) {
229 	    offset = 0;
230 	}
231 
232 	r.left = macDraw->xOff + scalePtr->inset;
233 	r.top = macDraw->yOff + offset;
234 	r.right = macDraw->xOff+Tk_Width(tkwin) - scalePtr->inset;
235 	r.bottom = macDraw->yOff + offset + MAC_OSX_SCROLL_WIDTH/2;
236     } else {
237 	int offset = (Tk_Width(tkwin) - MAC_OSX_SCROLL_WIDTH) / 2;
238 
239 	if (offset < 0) {
240 	    offset = 0;
241 	}
242 
243 	r.left = macDraw->xOff + offset;
244 	r.top = macDraw->yOff + scalePtr->inset;
245 	r.right = macDraw->xOff + offset + MAC_OSX_SCROLL_WIDTH/2;
246 	r.bottom = macDraw->yOff + Tk_Height(tkwin) - scalePtr->inset;
247     }
248 
249     if (macScalePtr->scaleHandle == NULL) {
250 #ifdef TK_MAC_DEBUG_SCALE
251 	TkMacOSXDbgMsg("Initialising scale");
252 #endif
253 	initialValue = scalePtr->value;
254 	if (scalePtr->orient == ORIENT_HORIZONTAL) {
255 	    minValue = scalePtr->fromValue;
256 	    maxValue = scalePtr->toValue;
257 	} else {
258 	    minValue = scalePtr->fromValue;
259 	    maxValue = scalePtr->toValue;
260 	}
261 
262 	if (scalePtr->tickInterval == 0) {
263 	    numTicks = 0;
264 	} else {
265 	    numTicks = (maxValue - minValue)/scalePtr->tickInterval;
266 	}
267 
268 	CreateSliderControl(windowRef, &r, initialValue, minValue, maxValue,
269 		kControlSliderPointsDownOrRight, numTicks, 1, scaleActionProc,
270 		&macScalePtr->scaleHandle);
271 	SetControlReference(macScalePtr->scaleHandle, (UInt32) scalePtr);
272 
273 	if (IsWindowActive(windowRef)) {
274 	    macScalePtr->flags |= ACTIVE;
275 	}
276     } else {
277 	SetControlBounds(macScalePtr->scaleHandle, &r);
278 	SetControl32BitValue(macScalePtr->scaleHandle, scalePtr->value);
279 	SetControl32BitMinimum(macScalePtr->scaleHandle, scalePtr->fromValue);
280 	SetControl32BitMaximum(macScalePtr->scaleHandle, scalePtr->toValue);
281     }
282 
283     /*
284      * Finally draw the control.
285      */
286 
287     SetControlVisibility(macScalePtr->scaleHandle, true, true);
288     HiliteControl(macScalePtr->scaleHandle, 0);
289     Draw1Control(macScalePtr->scaleHandle);
290 
291 done:
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 point.
301  *
302  * Results:
303  *	The return value is either TROUGH1, SLIDER, TROUGH2, or OTHER,
304  *	depending on which of the scale's active elements (if any) is under the
305  *	point at (x,y).
306  *
307  * Side effects:
308  *	None.
309  *
310  *----------------------------------------------------------------------
311  */
312 
313 int
TkpScaleElement(TkScale * scalePtr,int x,int y)314 TkpScaleElement(
315     TkScale *scalePtr,		/* Widget record for scale. */
316     int x, int y)		/* Coordinates within scalePtr's window. */
317 {
318     MacScale *macScalePtr = (MacScale *) scalePtr;
319     ControlPartCode part;
320     Point where;
321     Rect bounds;
322 
323 #ifdef TK_MAC_DEBUG_SCALE
324     TkMacOSXDbgMsg("TkpScaleElement");
325 #endif
326 
327     /*
328      * All of the calculations in this procedure mirror those in
329      * DisplayScrollbar. Be sure to keep the two consistent.
330      */
331 
332     TkMacOSXWinBounds((TkWindow *) scalePtr->tkwin, &bounds);
333     where.h = x + bounds.left;
334     where.v = y + bounds.top;
335     part = TestControl(macScalePtr->scaleHandle, where);
336 
337 #ifdef TK_MAC_DEBUG_SCALE
338     fprintf (stderr,"ScalePart %d, pos ( %d %d )\n", part, where.h, where.v );
339 #endif
340 
341     switch (part) {
342     case inSlider:
343 	return SLIDER;
344     case inInc:
345 	if (scalePtr->orient == ORIENT_VERTICAL) {
346 	    return TROUGH1;
347 	} else {
348 	    return TROUGH2;
349 	}
350     case inDecr:
351 	if (scalePtr->orient == ORIENT_VERTICAL) {
352 	    return TROUGH2;
353 	} else {
354 	    return TROUGH1;
355 	}
356     default:
357 	return OTHER;
358     }
359 }
360 
361 /*
362  *--------------------------------------------------------------
363  *
364  * MacScaleEventProc --
365  *
366  *	This procedure is invoked by the Tk dispatcher for ButtonPress events
367  *	on scales.
368  *
369  * Results:
370  *	None.
371  *
372  * Side effects:
373  *	When the window gets deleted, internal structures get cleaned up. When
374  *	it gets exposed, it is redisplayed.
375  *
376  *--------------------------------------------------------------
377  */
378 
379 static void
MacScaleEventProc(ClientData clientData,XEvent * eventPtr)380 MacScaleEventProc(
381     ClientData clientData,	/* Information about window. */
382     XEvent *eventPtr)		/* Information about event. */
383 {
384     MacScale *macScalePtr = (MacScale *) clientData;
385     Point where;
386     Rect bounds;
387     int part;
388 
389 #ifdef TK_MAC_DEBUG_SCALE
390     fprintf(stderr,"MacScaleEventProc\n" );
391 #endif
392 
393     /*
394      * To call Macintosh control routines we must have the port set to the
395      * window containing the control. We will then test which part of the
396      * control was hit and act accordingly.
397      */
398 
399     TkMacOSXDbgMsg("calling TestControl");
400 #endif
401     part = TestControl(macScalePtr->scaleHandle, where);
402     if (part == 0) {
403 	return;
404     }
405 
406     TkMacOSXTrackingLoop(1);
407     part = HandleControlClick(macScalePtr->scaleHandle, where,
408 	    TkMacOSXModifierState(), scaleActionProc);
409     TkMacOSXTrackingLoop(0);
410 
411     /*
412      * Update the value for the widget.
413      */
414 
415     macScalePtr->info.value = GetControlValue(macScalePtr->scaleHandle);
416     /* TkScaleSetValue(&macScalePtr->info, macScalePtr->info.value, 1, 0); */
417 
418     /*
419      * The HandleControlClick call will "eat" the ButtonUp event. We now
420      * generate a ButtonUp event so Tk will unset implicit grabs etc.
421      */
422 
423     TkGenerateButtonEventForXPointer(Tk_WindowId(macScalePtr->info.tkwin));
424 }
425 
426 /*
427  *--------------------------------------------------------------
428  *
429  * ScaleActionProc --
430  *
431  *	Callback procedure used by the Macintosh toolbox call
432  *	HandleControlClick. This call will update the display while the
433  *	scrollbar is being manipulated by the user.
434  *
435  * Results:
436  *	None.
437  *
438  * Side effects:
439  *	May change the display.
440  *
441  *--------------------------------------------------------------
442  */
443 
444 static pascal void
ScaleActionProc(ControlRef theControl,ControlPartCode partCode)445 ScaleActionProc(
446     ControlRef theControl,	/* Handle to scrollbat control */
447     ControlPartCode partCode)	/* Part of scrollbar that was "hit" */
448 {
449     int value;
450     TkScale *scalePtr = (TkScale *) GetControlReference(theControl);
451 
452 #ifdef TK_MAC_DEBUG_SCALE
453     TkMacOSXDbgMsg("ScaleActionProc");
454 #endif
455     value = GetControlValue(theControl);
456     TkScaleSetValue(scalePtr, value, 1, 1);
457     Tcl_Preserve(scalePtr);
458     TkMacOSXRunTclEventLoop();
459     Tcl_Release(scalePtr);
460 }
461 #endif
462 
463 /*
464  * Local Variables:
465  * mode: objc
466  * c-basic-offset: 4
467  * fill-column: 79
468  * coding: utf-8
469  * End:
470  */
471