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