1/*
2 * tkUnixScale.c --
3 *
4 *	This file implements the X specific portion of the scrollbar
5 *	widget.
6 *
7 * Copyright (c) 1996 by Sun Microsystems, Inc.
8 * Copyright (c) 1998-2000 by Scriptics Corporation.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tkUnixScale.c,v 1.8.4.2 2007/04/29 02:24:01 das Exp $
14 */
15
16#include "tkScale.h"
17#include "tkInt.h"
18
19/*
20 * Forward declarations for procedures defined later in this file:
21 */
22
23static void		DisplayHorizontalScale _ANSI_ARGS_((TkScale *scalePtr,
24			    Drawable drawable, XRectangle *drawnAreaPtr));
25static void		DisplayHorizontalValue _ANSI_ARGS_((TkScale *scalePtr,
26			    Drawable drawable, double value, int top));
27static void		DisplayVerticalScale _ANSI_ARGS_((TkScale *scalePtr,
28			    Drawable drawable, XRectangle *drawnAreaPtr));
29static void		DisplayVerticalValue _ANSI_ARGS_((TkScale *scalePtr,
30			    Drawable drawable, double value, int rightEdge));
31
32/*
33 *----------------------------------------------------------------------
34 *
35 * TkpCreateScale --
36 *
37 *	Allocate a new TkScale structure.
38 *
39 * Results:
40 *	Returns a newly allocated TkScale structure.
41 *
42 * Side effects:
43 *	None.
44 *
45 *----------------------------------------------------------------------
46 */
47
48TkScale *
49TkpCreateScale(tkwin)
50    Tk_Window tkwin;
51{
52    return (TkScale *) ckalloc(sizeof(TkScale));
53}
54
55/*
56 *----------------------------------------------------------------------
57 *
58 * TkpDestroyScale --
59 *
60 *	Destroy a TkScale structure.  It's necessary to do this with
61 *	Tcl_EventuallyFree to allow the Tcl_Preserve(scalePtr) to work
62 *	as expected in TkpDisplayScale. (hobbs)
63 *
64 * Results:
65 *	None
66 *
67 * Side effects:
68 *	Memory is freed.
69 *
70 *----------------------------------------------------------------------
71 */
72
73void
74TkpDestroyScale(scalePtr)
75    TkScale *scalePtr;
76{
77    Tcl_EventuallyFree((ClientData) scalePtr, TCL_DYNAMIC);
78}
79
80/*
81 *--------------------------------------------------------------
82 *
83 * DisplayVerticalScale --
84 *
85 *	This procedure redraws the contents of a vertical scale
86 *	window.  It is invoked as a do-when-idle handler, so it only
87 *	runs when there's nothing else for the application to do.
88 *
89 * Results:
90 *	There is no return value.  If only a part of the scale needs
91 *	to be redrawn, then drawnAreaPtr is modified to reflect the
92 *	area that was actually modified.
93 *
94 * Side effects:
95 *	Information appears on the screen.
96 *
97 *--------------------------------------------------------------
98 */
99
100static void
101DisplayVerticalScale(scalePtr, drawable, drawnAreaPtr)
102    TkScale *scalePtr;			/* Widget record for scale. */
103    Drawable drawable;			/* Where to display scale (window
104					 * or pixmap). */
105    XRectangle *drawnAreaPtr;		/* Initally contains area of window;
106					 * if only a part of the scale is
107					 * redrawn, gets modified to reflect
108					 * the part of the window that was
109					 * redrawn. */
110{
111    Tk_Window tkwin = scalePtr->tkwin;
112    int x, y, width, height, shadowWidth;
113    double tickValue, tickInterval = scalePtr->tickInterval;
114    Tk_3DBorder sliderBorder;
115
116    /*
117     * Display the information from left to right across the window.
118     */
119
120    if (!(scalePtr->flags & REDRAW_OTHER)) {
121	drawnAreaPtr->x = scalePtr->vertTickRightX;
122	drawnAreaPtr->y = scalePtr->inset;
123	drawnAreaPtr->width = scalePtr->vertTroughX + scalePtr->width
124		+ 2*scalePtr->borderWidth - scalePtr->vertTickRightX;
125	drawnAreaPtr->height -= 2*scalePtr->inset;
126    }
127    Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder,
128	    drawnAreaPtr->x, drawnAreaPtr->y, drawnAreaPtr->width,
129	    drawnAreaPtr->height, 0, TK_RELIEF_FLAT);
130    if (scalePtr->flags & REDRAW_OTHER) {
131	/*
132	 * Display the tick marks.
133	 */
134
135	if (tickInterval != 0) {
136	    double ticks, maxTicks;
137
138	    /*
139	     * Ensure that we will only draw enough of the tick values
140	     * such that they don't overlap
141	     */
142	    ticks = fabs((scalePtr->toValue - scalePtr->fromValue)
143		    / tickInterval);
144	    maxTicks = (double) Tk_Height(tkwin)
145		/ (double) scalePtr->fontHeight;
146	    if (ticks > maxTicks) {
147		tickInterval *= (ticks / maxTicks);
148	    }
149	    for (tickValue = scalePtr->fromValue; ;
150		 tickValue += tickInterval) {
151		/*
152		 * The TkRoundToResolution call gets rid of accumulated
153		 * round-off errors, if any.
154		 */
155
156		tickValue = TkRoundToResolution(scalePtr, tickValue);
157		if (scalePtr->toValue >= scalePtr->fromValue) {
158		    if (tickValue > scalePtr->toValue) {
159			break;
160		    }
161		} else {
162		    if (tickValue < scalePtr->toValue) {
163			break;
164		    }
165		}
166		DisplayVerticalValue(scalePtr, drawable, tickValue,
167			scalePtr->vertTickRightX);
168	    }
169	}
170    }
171
172    /*
173     * Display the value, if it is desired.
174     */
175
176    if (scalePtr->showValue) {
177	DisplayVerticalValue(scalePtr, drawable, scalePtr->value,
178		scalePtr->vertValueRightX);
179    }
180
181    /*
182     * Display the trough and the slider.
183     */
184
185    Tk_Draw3DRectangle(tkwin, drawable,
186	    scalePtr->bgBorder, scalePtr->vertTroughX, scalePtr->inset,
187	    scalePtr->width + 2*scalePtr->borderWidth,
188	    Tk_Height(tkwin) - 2*scalePtr->inset, scalePtr->borderWidth,
189	    TK_RELIEF_SUNKEN);
190    XFillRectangle(scalePtr->display, drawable, scalePtr->troughGC,
191	    scalePtr->vertTroughX + scalePtr->borderWidth,
192	    scalePtr->inset + scalePtr->borderWidth,
193	    (unsigned) scalePtr->width,
194	    (unsigned) (Tk_Height(tkwin) - 2*scalePtr->inset
195		- 2*scalePtr->borderWidth));
196    if (scalePtr->state == STATE_ACTIVE) {
197	sliderBorder = scalePtr->activeBorder;
198    } else {
199	sliderBorder = scalePtr->bgBorder;
200    }
201    width = scalePtr->width;
202    height = scalePtr->sliderLength/2;
203    x = scalePtr->vertTroughX + scalePtr->borderWidth;
204    y = TkScaleValueToPixel(scalePtr, scalePtr->value) - height;
205    shadowWidth = scalePtr->borderWidth/2;
206    if (shadowWidth == 0) {
207	shadowWidth = 1;
208    }
209    Tk_Draw3DRectangle(tkwin, drawable, sliderBorder, x, y, width,
210	    2*height, shadowWidth, scalePtr->sliderRelief);
211    x += shadowWidth;
212    y += shadowWidth;
213    width -= 2*shadowWidth;
214    height -= shadowWidth;
215    Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x, y, width,
216	    height, shadowWidth, scalePtr->sliderRelief);
217    Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x, y+height,
218	    width, height, shadowWidth, scalePtr->sliderRelief);
219
220    /*
221     * Draw the label to the right of the scale.
222     */
223
224    if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelLength != 0)) {
225	Tk_FontMetrics fm;
226
227	Tk_GetFontMetrics(scalePtr->tkfont, &fm);
228	Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
229		scalePtr->tkfont, scalePtr->label,
230                scalePtr->labelLength, scalePtr->vertLabelX,
231                scalePtr->inset + (3*fm.ascent)/2);
232    }
233}
234
235/*
236 *----------------------------------------------------------------------
237 *
238 * DisplayVerticalValue --
239 *
240 *	This procedure is called to display values (scale readings)
241 *	for vertically-oriented scales.
242 *
243 * Results:
244 *	None.
245 *
246 * Side effects:
247 *	The numerical value corresponding to value is displayed with
248 *	its right edge at "rightEdge", and at a vertical position in
249 *	the scale that corresponds to "value".
250 *
251 *----------------------------------------------------------------------
252 */
253
254static void
255DisplayVerticalValue(scalePtr, drawable, value, rightEdge)
256    register TkScale *scalePtr;	/* Information about widget in which to
257				 * display value. */
258    Drawable drawable;		/* Pixmap or window in which to draw
259				 * the value. */
260    double value;		/* Y-coordinate of number to display,
261				 * specified in application coords, not
262				 * in pixels (we'll compute pixels). */
263    int rightEdge;		/* X-coordinate of right edge of text,
264				 * specified in pixels. */
265{
266    register Tk_Window tkwin = scalePtr->tkwin;
267    int y, width, length;
268    char valueString[PRINT_CHARS];
269    Tk_FontMetrics fm;
270
271    Tk_GetFontMetrics(scalePtr->tkfont, &fm);
272    y = TkScaleValueToPixel(scalePtr, value) + fm.ascent/2;
273    sprintf(valueString, scalePtr->format, value);
274    length = (int) strlen(valueString);
275    width = Tk_TextWidth(scalePtr->tkfont, valueString, length);
276
277    /*
278     * Adjust the y-coordinate if necessary to keep the text entirely
279     * inside the window.
280     */
281
282    if ((y - fm.ascent) < (scalePtr->inset + SPACING)) {
283	y = scalePtr->inset + SPACING + fm.ascent;
284    }
285    if ((y + fm.descent) > (Tk_Height(tkwin) - scalePtr->inset - SPACING)) {
286	y = Tk_Height(tkwin) - scalePtr->inset - SPACING - fm.descent;
287    }
288    Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
289	    scalePtr->tkfont, valueString, length, rightEdge - width, y);
290}
291
292/*
293 *--------------------------------------------------------------
294 *
295 * DisplayHorizontalScale --
296 *
297 *	This procedure redraws the contents of a horizontal scale
298 *	window.  It is invoked as a do-when-idle handler, so it only
299 *	runs when there's nothing else for the application to do.
300 *
301 * Results:
302 *	There is no return value.  If only a part of the scale needs
303 *	to be redrawn, then drawnAreaPtr is modified to reflect the
304 *	area that was actually modified.
305 *
306 * Side effects:
307 *	Information appears on the screen.
308 *
309 *--------------------------------------------------------------
310 */
311
312static void
313DisplayHorizontalScale(scalePtr, drawable, drawnAreaPtr)
314    TkScale *scalePtr;			/* Widget record for scale. */
315    Drawable drawable;			/* Where to display scale (window
316					 * or pixmap). */
317    XRectangle *drawnAreaPtr;		/* Initally contains area of window;
318					 * if only a part of the scale is
319					 * redrawn, gets modified to reflect
320					 * the part of the window that was
321					 * redrawn. */
322{
323    register Tk_Window tkwin = scalePtr->tkwin;
324    int x, y, width, height, shadowWidth;
325    double tickValue, tickInterval = scalePtr->tickInterval;
326    Tk_3DBorder sliderBorder;
327
328    /*
329     * Display the information from bottom to top across the window.
330     */
331
332    if (!(scalePtr->flags & REDRAW_OTHER)) {
333	drawnAreaPtr->x = scalePtr->inset;
334	drawnAreaPtr->y = scalePtr->horizValueY;
335	drawnAreaPtr->width -= 2*scalePtr->inset;
336	drawnAreaPtr->height = scalePtr->horizTroughY + scalePtr->width
337		+ 2*scalePtr->borderWidth - scalePtr->horizValueY;
338    }
339    Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder,
340	    drawnAreaPtr->x, drawnAreaPtr->y, drawnAreaPtr->width,
341	    drawnAreaPtr->height, 0, TK_RELIEF_FLAT);
342    if (scalePtr->flags & REDRAW_OTHER) {
343	/*
344	 * Display the tick marks.
345	 */
346
347	if (tickInterval != 0) {
348	    char valueString[PRINT_CHARS];
349	    double ticks, maxTicks;
350
351	    /*
352	     * Ensure that we will only draw enough of the tick values
353	     * such that they don't overlap.  We base this off the width that
354	     * fromValue would take.  Not exact, but better than no constraint.
355	     */
356	    ticks = fabs((scalePtr->toValue - scalePtr->fromValue)
357		    / tickInterval);
358	    sprintf(valueString, scalePtr->format, scalePtr->fromValue);
359	    maxTicks = (double) Tk_Width(tkwin)
360		/ (double) Tk_TextWidth(scalePtr->tkfont, valueString, -1);
361	    if (ticks > maxTicks) {
362		tickInterval *= (ticks / maxTicks);
363	    }
364	    for (tickValue = scalePtr->fromValue; ;
365		 tickValue += tickInterval) {
366		/*
367		 * The TkRoundToResolution call gets rid of accumulated
368		 * round-off errors, if any.
369		 */
370
371		tickValue = TkRoundToResolution(scalePtr, tickValue);
372		if (scalePtr->toValue >= scalePtr->fromValue) {
373		    if (tickValue > scalePtr->toValue) {
374			break;
375		    }
376		} else {
377		    if (tickValue < scalePtr->toValue) {
378			break;
379		    }
380		}
381		DisplayHorizontalValue(scalePtr, drawable, tickValue,
382			scalePtr->horizTickY);
383	    }
384	}
385    }
386
387    /*
388     * Display the value, if it is desired.
389     */
390
391    if (scalePtr->showValue) {
392	DisplayHorizontalValue(scalePtr, drawable, scalePtr->value,
393		scalePtr->horizValueY);
394    }
395
396    /*
397     * Display the trough and the slider.
398     */
399
400    y = scalePtr->horizTroughY;
401    Tk_Draw3DRectangle(tkwin, drawable,
402	    scalePtr->bgBorder, scalePtr->inset, y,
403	    Tk_Width(tkwin) - 2*scalePtr->inset,
404	    scalePtr->width + 2*scalePtr->borderWidth,
405	    scalePtr->borderWidth, TK_RELIEF_SUNKEN);
406    XFillRectangle(scalePtr->display, drawable, scalePtr->troughGC,
407	    scalePtr->inset + scalePtr->borderWidth,
408	    y + scalePtr->borderWidth,
409	    (unsigned) (Tk_Width(tkwin) - 2*scalePtr->inset
410		- 2*scalePtr->borderWidth),
411	    (unsigned) scalePtr->width);
412    if (scalePtr->state == STATE_ACTIVE) {
413	sliderBorder = scalePtr->activeBorder;
414    } else {
415	sliderBorder = scalePtr->bgBorder;
416    }
417    width = scalePtr->sliderLength/2;
418    height = scalePtr->width;
419    x = TkScaleValueToPixel(scalePtr, scalePtr->value) - width;
420    y += scalePtr->borderWidth;
421    shadowWidth = scalePtr->borderWidth/2;
422    if (shadowWidth == 0) {
423	shadowWidth = 1;
424    }
425    Tk_Draw3DRectangle(tkwin, drawable, sliderBorder,
426	    x, y, 2*width, height, shadowWidth, scalePtr->sliderRelief);
427    x += shadowWidth;
428    y += shadowWidth;
429    width -= shadowWidth;
430    height -= 2*shadowWidth;
431    Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x, y, width, height,
432	    shadowWidth, scalePtr->sliderRelief);
433    Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x+width, y,
434	    width, height, shadowWidth, scalePtr->sliderRelief);
435
436    /*
437     * Draw the label at the top of the scale.
438     */
439
440    if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelLength != 0)) {
441	Tk_FontMetrics fm;
442
443	Tk_GetFontMetrics(scalePtr->tkfont, &fm);
444	Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
445		scalePtr->tkfont, scalePtr->label,
446                scalePtr->labelLength, scalePtr->inset + fm.ascent/2,
447                scalePtr->horizLabelY + fm.ascent);
448    }
449}
450
451/*
452 *----------------------------------------------------------------------
453 *
454 * DisplayHorizontalValue --
455 *
456 *	This procedure is called to display values (scale readings)
457 *	for horizontally-oriented scales.
458 *
459 * Results:
460 *	None.
461 *
462 * Side effects:
463 *	The numerical value corresponding to value is displayed with
464 *	its bottom edge at "bottom", and at a horizontal position in
465 *	the scale that corresponds to "value".
466 *
467 *----------------------------------------------------------------------
468 */
469
470static void
471DisplayHorizontalValue(scalePtr, drawable, value, top)
472    register TkScale *scalePtr;	/* Information about widget in which to
473				 * display value. */
474    Drawable drawable;		/* Pixmap or window in which to draw
475				 * the value. */
476    double value;		/* X-coordinate of number to display,
477				 * specified in application coords, not
478				 * in pixels (we'll compute pixels). */
479    int top;			/* Y-coordinate of top edge of text,
480				 * specified in pixels. */
481{
482    register Tk_Window tkwin = scalePtr->tkwin;
483    int x, y, length, width;
484    char valueString[PRINT_CHARS];
485    Tk_FontMetrics fm;
486
487    x = TkScaleValueToPixel(scalePtr, value);
488    Tk_GetFontMetrics(scalePtr->tkfont, &fm);
489    y = top + fm.ascent;
490    sprintf(valueString, scalePtr->format, value);
491    length = (int) strlen(valueString);
492    width = Tk_TextWidth(scalePtr->tkfont, valueString, length);
493
494    /*
495     * Adjust the x-coordinate if necessary to keep the text entirely
496     * inside the window.
497     */
498
499    x -= (width)/2;
500    if (x < (scalePtr->inset + SPACING)) {
501	x = scalePtr->inset + SPACING;
502    }
503    /*
504     * Check the right border so use starting point +text width
505     * for the check.
506     */
507    if (x + width >= (Tk_Width(tkwin) - scalePtr->inset)) {
508	x = Tk_Width(tkwin) - scalePtr->inset - SPACING - width;
509    }
510    Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
511	    scalePtr->tkfont, valueString, length, x, y);
512}
513
514/*
515 *----------------------------------------------------------------------
516 *
517 * TkpDisplayScale --
518 *
519 *	This procedure is invoked as an idle handler to redisplay
520 *	the contents of a scale widget.
521 *
522 * Results:
523 *	None.
524 *
525 * Side effects:
526 *	The scale gets redisplayed.
527 *
528 *----------------------------------------------------------------------
529 */
530
531void
532TkpDisplayScale(clientData)
533    ClientData clientData;	/* Widget record for scale. */
534{
535    TkScale *scalePtr = (TkScale *) clientData;
536    Tk_Window tkwin = scalePtr->tkwin;
537    Tcl_Interp *interp = scalePtr->interp;
538    Pixmap pixmap;
539    int result;
540    char string[PRINT_CHARS];
541    XRectangle drawnArea;
542
543    scalePtr->flags &= ~REDRAW_PENDING;
544    if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(scalePtr->tkwin)) {
545	goto done;
546    }
547
548    /*
549     * Invoke the scale's command if needed.
550     */
551    Tcl_Preserve((ClientData) scalePtr);
552    if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
553	Tcl_Preserve((ClientData) interp);
554	sprintf(string, scalePtr->format, scalePtr->value);
555	result = Tcl_VarEval(interp, scalePtr->command, " ", string,
556		(char *) NULL);
557	if (result != TCL_OK) {
558	    Tcl_AddErrorInfo(interp, "\n    (command executed by scale)");
559	    Tcl_BackgroundError(interp);
560	}
561	Tcl_Release((ClientData) interp);
562    }
563    scalePtr->flags &= ~INVOKE_COMMAND;
564    if (scalePtr->flags & SCALE_DELETED) {
565	Tcl_Release((ClientData) scalePtr);
566	return;
567    }
568    Tcl_Release((ClientData) scalePtr);
569
570#ifndef TK_NO_DOUBLE_BUFFERING
571    /*
572     * In order to avoid screen flashes, this procedure redraws
573     * the scale in a pixmap, then copies the pixmap to the
574     * screen in a single operation.  This means that there's no
575     * point in time where the on-sreen image has been cleared.
576     */
577
578    pixmap = Tk_GetPixmap(scalePtr->display, Tk_WindowId(tkwin),
579	    Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
580#else
581    pixmap = Tk_WindowId(tkwin);
582#endif /* TK_NO_DOUBLE_BUFFERING */
583    drawnArea.x = 0;
584    drawnArea.y = 0;
585    drawnArea.width = Tk_Width(tkwin);
586    drawnArea.height = Tk_Height(tkwin);
587
588    /*
589     * Much of the redisplay is done totally differently for
590     * horizontal and vertical scales.  Handle the part that's
591     * different.
592     */
593
594    if (scalePtr->orient == ORIENT_VERTICAL) {
595	DisplayVerticalScale(scalePtr, pixmap, &drawnArea);
596    } else {
597	DisplayHorizontalScale(scalePtr, pixmap, &drawnArea);
598    }
599
600    /*
601     * Now handle the part of redisplay that is the same for
602     * horizontal and vertical scales:  border and traversal
603     * highlight.
604     */
605
606    if (scalePtr->flags & REDRAW_OTHER) {
607	if (scalePtr->relief != TK_RELIEF_FLAT) {
608	    Tk_Draw3DRectangle(tkwin, pixmap, scalePtr->bgBorder,
609		    scalePtr->highlightWidth, scalePtr->highlightWidth,
610		    Tk_Width(tkwin) - 2*scalePtr->highlightWidth,
611		    Tk_Height(tkwin) - 2*scalePtr->highlightWidth,
612		    scalePtr->borderWidth, scalePtr->relief);
613	}
614	if (scalePtr->highlightWidth != 0) {
615	    GC gc;
616
617	    if (scalePtr->flags & GOT_FOCUS) {
618		gc = Tk_GCForColor(scalePtr->highlightColorPtr, pixmap);
619	    } else {
620		gc = Tk_GCForColor(
621                        Tk_3DBorderColor(scalePtr->highlightBorder), pixmap);
622	    }
623	    Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth, pixmap);
624	}
625    }
626
627#ifndef TK_NO_DOUBLE_BUFFERING
628    /*
629     * Copy the information from the off-screen pixmap onto the screen,
630     * then delete the pixmap.
631     */
632
633    XCopyArea(scalePtr->display, pixmap, Tk_WindowId(tkwin),
634	    scalePtr->copyGC, drawnArea.x, drawnArea.y, drawnArea.width,
635	    drawnArea.height, drawnArea.x, drawnArea.y);
636    Tk_FreePixmap(scalePtr->display, pixmap);
637#endif /* TK_NO_DOUBLE_BUFFERING */
638
639    done:
640    scalePtr->flags &= ~REDRAW_ALL;
641}
642
643/*
644 *----------------------------------------------------------------------
645 *
646 * TkpScaleElement --
647 *
648 *	Determine which part of a scale widget lies under a given
649 *	point.
650 *
651 * Results:
652 *	The return value is either TROUGH1, SLIDER, TROUGH2, or
653 *	OTHER, depending on which of the scale's active elements
654 *	(if any) is under the point at (x,y).
655 *
656 * Side effects:
657 *	None.
658 *
659 *----------------------------------------------------------------------
660 */
661
662int
663TkpScaleElement(scalePtr, x, y)
664    TkScale *scalePtr;		/* Widget record for scale. */
665    int x, y;			/* Coordinates within scalePtr's window. */
666{
667    int sliderFirst;
668
669    if (scalePtr->orient == ORIENT_VERTICAL) {
670	if ((x < scalePtr->vertTroughX)
671		|| (x >= (scalePtr->vertTroughX + 2*scalePtr->borderWidth +
672		scalePtr->width))) {
673	    return OTHER;
674	}
675	if ((y < scalePtr->inset)
676		|| (y >= (Tk_Height(scalePtr->tkwin) - scalePtr->inset))) {
677	    return OTHER;
678	}
679	sliderFirst = TkScaleValueToPixel(scalePtr, scalePtr->value)
680		- scalePtr->sliderLength/2;
681	if (y < sliderFirst) {
682	    return TROUGH1;
683	}
684	if (y < (sliderFirst+scalePtr->sliderLength)) {
685	    return SLIDER;
686	}
687	return TROUGH2;
688    }
689
690    if ((y < scalePtr->horizTroughY)
691	    || (y >= (scalePtr->horizTroughY + 2*scalePtr->borderWidth +
692	    scalePtr->width))) {
693	return OTHER;
694    }
695    if ((x < scalePtr->inset)
696	    || (x >= (Tk_Width(scalePtr->tkwin) - scalePtr->inset))) {
697	return OTHER;
698    }
699    sliderFirst = TkScaleValueToPixel(scalePtr, scalePtr->value)
700	    - scalePtr->sliderLength/2;
701    if (x < sliderFirst) {
702	return TROUGH1;
703    }
704    if (x < (sliderFirst+scalePtr->sliderLength)) {
705	return SLIDER;
706    }
707    return TROUGH2;
708}
709