1 /*
2  * tkUtil.c --
3  *
4  *	This file contains miscellaneous utility functions that are used by
5  *	the rest of Tk, such as a function for drawing a focus highlight.
6  *
7  * Copyright (c) 1994 The Regents of the University of California.
8  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
9  *
10  * See the file "license.terms" for information on usage and redistribution of
11  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  */
13 
14 #include "tkInt.h"
15 
16 /*
17  * The structure below defines the implementation of the "statekey" Tcl
18  * object, used for quickly finding a mapping in a TkStateMap.
19  */
20 
21 Tcl_ObjType tkStateKeyObjType = {
22     "statekey",			/* name */
23     NULL,			/* freeIntRepProc */
24     NULL,			/* dupIntRepProc */
25     NULL,			/* updateStringProc */
26     NULL			/* setFromAnyProc */
27 };
28 
29 /*
30  *--------------------------------------------------------------
31  *
32  * TkStateParseProc --
33  *
34  *	This function is invoked during option processing to handle the
35  *	"-state" and "-default" options.
36  *
37  * Results:
38  *	A standard Tcl return value.
39  *
40  * Side effects:
41  *	The state for a given item gets replaced by the state indicated in the
42  *	value argument.
43  *
44  *--------------------------------------------------------------
45  */
46 
47 int
TkStateParseProc(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,const char * value,char * widgRec,int offset)48 TkStateParseProc(
49     ClientData clientData,	/* some flags.*/
50     Tcl_Interp *interp,		/* Used for reporting errors. */
51     Tk_Window tkwin,		/* Window containing canvas widget. */
52     const char *value,		/* Value of option. */
53     char *widgRec,		/* Pointer to record for item. */
54     int offset)			/* Offset into item. */
55 {
56     int c;
57     int flags = PTR2INT(clientData);
58     size_t length;
59 
60     register Tk_State *statePtr = (Tk_State *) (widgRec + offset);
61 
62     if(value == NULL || *value == 0) {
63 	*statePtr = TK_STATE_NULL;
64 	return TCL_OK;
65     }
66 
67     c = value[0];
68     length = strlen(value);
69 
70     if ((c == 'n') && (strncmp(value, "normal", length) == 0)) {
71 	*statePtr = TK_STATE_NORMAL;
72 	return TCL_OK;
73     }
74     if ((c == 'd') && (strncmp(value, "disabled", length) == 0)) {
75 	*statePtr = TK_STATE_DISABLED;
76 	return TCL_OK;
77     }
78     if ((c == 'a') && (flags&1) && (strncmp(value, "active", length) == 0)) {
79 	*statePtr = TK_STATE_ACTIVE;
80 	return TCL_OK;
81     }
82     if ((c == 'h') && (flags&2) && (strncmp(value, "hidden", length) == 0)) {
83 	*statePtr = TK_STATE_HIDDEN;
84 	return TCL_OK;
85     }
86 
87     Tcl_AppendResult(interp, "bad ", (flags&4)?"-default" : "state",
88 	    " value \"", value, "\": must be normal", NULL);
89     if (flags&1) {
90 	Tcl_AppendResult(interp, ", active", NULL);
91     }
92     if (flags&2) {
93 	Tcl_AppendResult(interp, ", hidden", NULL);
94     }
95     if (flags&3) {
96 	Tcl_AppendResult(interp, ",", NULL);
97     }
98     Tcl_AppendResult(interp, " or disabled", NULL);
99     *statePtr = TK_STATE_NORMAL;
100     return TCL_ERROR;
101 }
102 
103 /*
104  *--------------------------------------------------------------
105  *
106  * TkStatePrintProc --
107  *
108  *	This function is invoked by the Tk configuration code to produce a
109  *	printable string for the "-state" configuration option.
110  *
111  * Results:
112  *	The return value is a string describing the state for the item
113  *	referred to by "widgRec". In addition, *freeProcPtr is filled in with
114  *	the address of a function to call to free the result string when it's
115  *	no longer needed (or NULL to indicate that the string doesn't need to
116  *	be freed).
117  *
118  * Side effects:
119  *	None.
120  *
121  *--------------------------------------------------------------
122  */
123 
124 char *
TkStatePrintProc(ClientData clientData,Tk_Window tkwin,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)125 TkStatePrintProc(
126     ClientData clientData,	/* Ignored. */
127     Tk_Window tkwin,		/* Window containing canvas widget. */
128     char *widgRec,		/* Pointer to record for item. */
129     int offset,			/* Offset into item. */
130     Tcl_FreeProc **freeProcPtr)	/* Pointer to variable to fill in with
131 				 * information about how to reclaim storage
132 				 * for return string. */
133 {
134     register Tk_State *statePtr = (Tk_State *) (widgRec + offset);
135 
136     switch (*statePtr) {
137     case TK_STATE_NORMAL:
138 	return "normal";
139     case TK_STATE_DISABLED:
140 	return "disabled";
141     case TK_STATE_HIDDEN:
142 	return "hidden";
143     case TK_STATE_ACTIVE:
144 	return "active";
145     default:
146 	return "";
147     }
148 }
149 
150 /*
151  *--------------------------------------------------------------
152  *
153  * TkOrientParseProc --
154  *
155  *	This function is invoked during option processing to handle the
156  *	"-orient" option.
157  *
158  * Results:
159  *	A standard Tcl return value.
160  *
161  * Side effects:
162  *	The orientation for a given item gets replaced by the orientation
163  *	indicated in the value argument.
164  *
165  *--------------------------------------------------------------
166  */
167 
168 int
TkOrientParseProc(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,const char * value,char * widgRec,int offset)169 TkOrientParseProc(
170     ClientData clientData,	/* some flags.*/
171     Tcl_Interp *interp,		/* Used for reporting errors. */
172     Tk_Window tkwin,		/* Window containing canvas widget. */
173     const char *value,		/* Value of option. */
174     char *widgRec,		/* Pointer to record for item. */
175     int offset)			/* Offset into item. */
176 {
177     int c;
178     size_t length;
179 
180     register int *orientPtr = (int *) (widgRec + offset);
181 
182     if(value == NULL || *value == 0) {
183 	*orientPtr = 0;
184 	return TCL_OK;
185     }
186 
187     c = value[0];
188     length = strlen(value);
189 
190     if ((c == 'h') && (strncmp(value, "horizontal", length) == 0)) {
191 	*orientPtr = 0;
192 	return TCL_OK;
193     }
194     if ((c == 'v') && (strncmp(value, "vertical", length) == 0)) {
195 	*orientPtr = 1;
196 	return TCL_OK;
197     }
198     Tcl_AppendResult(interp, "bad orientation \"", value,
199 	    "\": must be vertical or horizontal", NULL);
200     *orientPtr = 0;
201     return TCL_ERROR;
202 }
203 
204 /*
205  *--------------------------------------------------------------
206  *
207  * TkOrientPrintProc --
208  *
209  *	This function is invoked by the Tk configuration code to produce a
210  *	printable string for the "-orient" configuration option.
211  *
212  * Results:
213  *	The return value is a string describing the orientation for the item
214  *	referred to by "widgRec". In addition, *freeProcPtr is filled in with
215  *	the address of a function to call to free the result string when it's
216  *	no longer needed (or NULL to indicate that the string doesn't need to
217  *	be freed).
218  *
219  * Side effects:
220  *	None.
221  *
222  *--------------------------------------------------------------
223  */
224 
225 char *
TkOrientPrintProc(ClientData clientData,Tk_Window tkwin,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)226 TkOrientPrintProc(
227     ClientData clientData,	/* Ignored. */
228     Tk_Window tkwin,		/* Window containing canvas widget. */
229     char *widgRec,		/* Pointer to record for item. */
230     int offset,			/* Offset into item. */
231     Tcl_FreeProc **freeProcPtr)	/* Pointer to variable to fill in with
232 				 * information about how to reclaim storage
233 				 * for return string. */
234 {
235     register int *statePtr = (int *) (widgRec + offset);
236 
237     if (*statePtr) {
238 	return "vertical";
239     } else {
240 	return "horizontal";
241     }
242 }
243 
244 /*
245  *----------------------------------------------------------------------
246  *
247  * TkOffsetParseProc --
248  *
249  *	Converts the offset of a stipple or tile into the Tk_TSOffset
250  *	structure.
251  *
252  *----------------------------------------------------------------------
253  */
254 
255 int
TkOffsetParseProc(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,const char * value,char * widgRec,int offset)256 TkOffsetParseProc(
257     ClientData clientData,	/* not used */
258     Tcl_Interp *interp,		/* Interpreter to send results back to */
259     Tk_Window tkwin,		/* Window on same display as tile */
260     const char *value,		/* Name of image */
261     char *widgRec,		/* Widget structure record */
262     int offset)			/* Offset of tile in record */
263 {
264     Tk_TSOffset *offsetPtr = (Tk_TSOffset *) (widgRec + offset);
265     Tk_TSOffset tsoffset;
266     const char *q, *p;
267     int result;
268 
269     if ((value == NULL) || (*value == 0)) {
270 	tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_MIDDLE;
271 	goto goodTSOffset;
272     }
273     tsoffset.flags = 0;
274     p = value;
275 
276     switch(value[0]) {
277     case '#':
278 	if (PTR2INT(clientData) & TK_OFFSET_RELATIVE) {
279 	    tsoffset.flags = TK_OFFSET_RELATIVE;
280 	    p++;
281 	    break;
282 	}
283 	goto badTSOffset;
284     case 'e':
285 	switch(value[1]) {
286 	case '\0':
287 	    tsoffset.flags = TK_OFFSET_RIGHT|TK_OFFSET_MIDDLE;
288 	    goto goodTSOffset;
289 	case 'n':
290 	    if (value[2]!='d' || value[3]!='\0') {
291 		goto badTSOffset;
292 	    }
293 	    tsoffset.flags = INT_MAX;
294 	    goto goodTSOffset;
295 	}
296     case 'w':
297 	if (value[1] != '\0') {goto badTSOffset;}
298 	tsoffset.flags = TK_OFFSET_LEFT|TK_OFFSET_MIDDLE;
299 	goto goodTSOffset;
300     case 'n':
301 	if ((value[1] != '\0') && (value[2] != '\0')) {
302 	    goto badTSOffset;
303 	}
304 	switch(value[1]) {
305 	case '\0':
306 	    tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_TOP;
307 	    goto goodTSOffset;
308 	case 'w':
309 	    tsoffset.flags = TK_OFFSET_LEFT|TK_OFFSET_TOP;
310 	    goto goodTSOffset;
311 	case 'e':
312 	    tsoffset.flags = TK_OFFSET_RIGHT|TK_OFFSET_TOP;
313 	    goto goodTSOffset;
314 	}
315 	goto badTSOffset;
316     case 's':
317 	if ((value[1] != '\0') && (value[2] != '\0')) {
318 	    goto badTSOffset;
319 	}
320 	switch(value[1]) {
321 	case '\0':
322 	    tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_BOTTOM;
323 	    goto goodTSOffset;
324 	case 'w':
325 	    tsoffset.flags = TK_OFFSET_LEFT|TK_OFFSET_BOTTOM;
326 	    goto goodTSOffset;
327 	case 'e':
328 	    tsoffset.flags = TK_OFFSET_RIGHT|TK_OFFSET_BOTTOM;
329 	    goto goodTSOffset;
330 	}
331 	goto badTSOffset;
332     case 'c':
333 	if (strncmp(value, "center", strlen(value)) != 0) {
334 	    goto badTSOffset;
335 	}
336 	tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_MIDDLE;
337 	goto goodTSOffset;
338     }
339     if ((q = strchr(p,',')) == NULL) {
340 	if (PTR2INT(clientData) & TK_OFFSET_INDEX) {
341 	    if (Tcl_GetInt(interp, (char *) p, &tsoffset.flags) != TCL_OK) {
342 		Tcl_ResetResult(interp);
343 		goto badTSOffset;
344 	    }
345 	    tsoffset.flags |= TK_OFFSET_INDEX;
346 	    goto goodTSOffset;
347 	}
348 	goto badTSOffset;
349     }
350     *((char *) q) = 0;
351     result = Tk_GetPixels(interp, tkwin, (char *) p, &tsoffset.xoffset);
352     *((char *) q) = ',';
353     if (result != TCL_OK) {
354 	return TCL_ERROR;
355     }
356     if (Tk_GetPixels(interp, tkwin, (char*)q+1, &tsoffset.yoffset) != TCL_OK) {
357 	return TCL_ERROR;
358     }
359 
360   goodTSOffset:
361     /*
362      * Below is a hack to allow the stipple/tile offset to be stored in the
363      * internal tile structure. Most of the times, offsetPtr is a pointer to
364      * an already existing tile structure. However if this structure is not
365      * already created, we must do it with Tk_GetTile()!!!!;
366      */
367 
368     memcpy(offsetPtr, &tsoffset, sizeof(Tk_TSOffset));
369     return TCL_OK;
370 
371   badTSOffset:
372     Tcl_AppendResult(interp, "bad offset \"", value,
373 	    "\": expected \"x,y\"", NULL);
374     if (PTR2INT(clientData) & TK_OFFSET_RELATIVE) {
375 	Tcl_AppendResult(interp, ", \"#x,y\"", NULL);
376     }
377     if (PTR2INT(clientData) & TK_OFFSET_INDEX) {
378 	Tcl_AppendResult(interp, ", <index>", NULL);
379     }
380     Tcl_AppendResult(interp, ", n, ne, e, se, s, sw, w, nw, or center", NULL);
381     return TCL_ERROR;
382 }
383 
384 /*
385  *----------------------------------------------------------------------
386  *
387  * TkOffsetPrintProc --
388  *
389  *	Returns the offset of the tile.
390  *
391  * Results:
392  *	The offset of the tile is returned.
393  *
394  *----------------------------------------------------------------------
395  */
396 
397 char *
TkOffsetPrintProc(ClientData clientData,Tk_Window tkwin,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)398 TkOffsetPrintProc(
399     ClientData clientData,	/* not used */
400     Tk_Window tkwin,		/* not used */
401     char *widgRec,		/* Widget structure record */
402     int offset,			/* Offset of tile in record */
403     Tcl_FreeProc **freeProcPtr)	/* not used */
404 {
405     Tk_TSOffset *offsetPtr = (Tk_TSOffset *) (widgRec + offset);
406     char *p, *q;
407 
408     if (offsetPtr->flags & TK_OFFSET_INDEX) {
409 	if (offsetPtr->flags >= INT_MAX) {
410 	    return "end";
411 	}
412 	p = (char *) ckalloc(32);
413 	sprintf(p, "%d", offsetPtr->flags & ~TK_OFFSET_INDEX);
414 	*freeProcPtr = TCL_DYNAMIC;
415 	return p;
416     }
417     if (offsetPtr->flags & TK_OFFSET_TOP) {
418 	if (offsetPtr->flags & TK_OFFSET_LEFT) {
419 	    return "nw";
420 	} else if (offsetPtr->flags & TK_OFFSET_CENTER) {
421 	    return "n";
422 	} else if (offsetPtr->flags & TK_OFFSET_RIGHT) {
423 	    return "ne";
424 	}
425     } else if (offsetPtr->flags & TK_OFFSET_MIDDLE) {
426 	if (offsetPtr->flags & TK_OFFSET_LEFT) {
427 	    return "w";
428 	} else if (offsetPtr->flags & TK_OFFSET_CENTER) {
429 	    return "center";
430 	} else if (offsetPtr->flags & TK_OFFSET_RIGHT) {
431 	    return "e";
432 	}
433     } else if (offsetPtr->flags & TK_OFFSET_BOTTOM) {
434 	if (offsetPtr->flags & TK_OFFSET_LEFT) {
435 	    return "sw";
436 	} else if (offsetPtr->flags & TK_OFFSET_CENTER) {
437 	    return "s";
438 	} else if (offsetPtr->flags & TK_OFFSET_RIGHT) {
439 	    return "se";
440 	}
441     }
442     q = p = (char *) ckalloc(32);
443     if (offsetPtr->flags & TK_OFFSET_RELATIVE) {
444 	*q++ = '#';
445     }
446     sprintf(q, "%d,%d", offsetPtr->xoffset, offsetPtr->yoffset);
447     *freeProcPtr = TCL_DYNAMIC;
448     return p;
449 }
450 
451 /*
452  *----------------------------------------------------------------------
453  *
454  * TkPixelParseProc --
455  *
456  *	Converts the name of an image into a tile.
457  *
458  *----------------------------------------------------------------------
459  */
460 
461 int
TkPixelParseProc(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,const char * value,char * widgRec,int offset)462 TkPixelParseProc(
463     ClientData clientData,	/* If non-NULL, negative values are allowed as
464 				 * well */
465     Tcl_Interp *interp,		/* Interpreter to send results back to */
466     Tk_Window tkwin,		/* Window on same display as tile */
467     const char *value,		/* Name of image */
468     char *widgRec,		/* Widget structure record */
469     int offset)			/* Offset of tile in record */
470 {
471     double *doublePtr = (double *) (widgRec + offset);
472     int result;
473 
474     result = TkGetDoublePixels(interp, tkwin, value, doublePtr);
475 
476     if ((result == TCL_OK) && (clientData == NULL) && (*doublePtr < 0.0)) {
477 	Tcl_AppendResult(interp, "bad screen distance \"", value, "\"", NULL);
478 	return TCL_ERROR;
479     }
480     return result;
481 }
482 
483 /*
484  *----------------------------------------------------------------------
485  *
486  * TkPixelPrintProc --
487  *
488  *	Returns the name of the tile.
489  *
490  * Results:
491  *	The name of the tile is returned.
492  *
493  *----------------------------------------------------------------------
494  */
495 
496 char *
TkPixelPrintProc(ClientData clientData,Tk_Window tkwin,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)497 TkPixelPrintProc(
498     ClientData clientData,	/* not used */
499     Tk_Window tkwin,		/* not used */
500     char *widgRec,		/* Widget structure record */
501     int offset,			/* Offset of tile in record */
502     Tcl_FreeProc **freeProcPtr)	/* not used */
503 {
504     double *doublePtr = (double *) (widgRec + offset);
505     char *p = (char *) ckalloc(24);
506 
507     Tcl_PrintDouble(NULL, *doublePtr, p);
508     *freeProcPtr = TCL_DYNAMIC;
509     return p;
510 }
511 
512 /*
513  *----------------------------------------------------------------------
514  *
515  * TkDrawInsetFocusHighlight --
516  *
517  *	This function draws a rectangular ring around the outside of a widget
518  *	to indicate that it has received the input focus. It takes an
519  *	additional padding argument that specifies how much padding is present
520  *	outside the widget.
521  *
522  * Results:
523  *	None.
524  *
525  * Side effects:
526  *	A rectangle "width" pixels wide is drawn in "drawable", corresponding
527  *	to the outer area of "tkwin".
528  *
529  *----------------------------------------------------------------------
530  */
531 
532 void
TkDrawInsetFocusHighlight(Tk_Window tkwin,GC gc,int width,Drawable drawable,int padding)533 TkDrawInsetFocusHighlight(
534     Tk_Window tkwin,		/* Window whose focus highlight ring is to be
535 				 * drawn. */
536     GC gc,			/* Graphics context to use for drawing the
537 				 * highlight ring. */
538     int width,			/* Width of the highlight ring, in pixels. */
539     Drawable drawable,		/* Where to draw the ring (typically a pixmap
540 				 * for double buffering). */
541     int padding)		/* Width of padding outside of widget. */
542 {
543     XRectangle rects[4];
544 
545     rects[0].x = padding;
546     rects[0].y = padding;
547     rects[0].width = Tk_Width(tkwin) - (2 * padding);
548     rects[0].height = width;
549     rects[1].x = padding;
550     rects[1].y = Tk_Height(tkwin) - width - padding;
551     rects[1].width = Tk_Width(tkwin) - (2 * padding);
552     rects[1].height = width;
553     rects[2].x = padding;
554     rects[2].y = width + padding;
555     rects[2].width = width;
556     rects[2].height = Tk_Height(tkwin) - 2*width - 2*padding;
557     rects[3].x = Tk_Width(tkwin) - width - padding;
558     rects[3].y = rects[2].y;
559     rects[3].width = width;
560     rects[3].height = rects[2].height;
561     XFillRectangles(Tk_Display(tkwin), drawable, gc, rects, 4);
562 }
563 
564 /*
565  *----------------------------------------------------------------------
566  *
567  * Tk_DrawFocusHighlight --
568  *
569  *	This function draws a rectangular ring around the outside of a widget
570  *	to indicate that it has received the input focus.
571  *
572  *	This function is now deprecated. Use TkpDrawHighlightBorder instead,
573  *	since this function does not handle drawing the Focus ring properly on
574  *	the Macintosh - you need to know the background GC as well as the
575  *	foreground since the Mac focus ring separated from the widget by a 1
576  *	pixel border.
577  *
578  * Results:
579  *	None.
580  *
581  * Side effects:
582  *	A rectangle "width" pixels wide is drawn in "drawable", corresponding
583  *	to the outer area of "tkwin".
584  *
585  *----------------------------------------------------------------------
586  */
587 
588 void
Tk_DrawFocusHighlight(Tk_Window tkwin,GC gc,int width,Drawable drawable)589 Tk_DrawFocusHighlight(
590     Tk_Window tkwin,		/* Window whose focus highlight ring is to be
591 				 * drawn. */
592     GC gc,			/* Graphics context to use for drawing the
593 				 * highlight ring. */
594     int width,			/* Width of the highlight ring, in pixels. */
595     Drawable drawable)		/* Where to draw the ring (typically a pixmap
596 				 * for double buffering). */
597 {
598     TkDrawInsetFocusHighlight(tkwin, gc, width, drawable, 0);
599 }
600 
601 /*
602  *----------------------------------------------------------------------
603  *
604  * Tk_GetScrollInfo --
605  *
606  *	This function is invoked to parse "xview" and "yview" scrolling
607  *	commands for widgets using the new scrolling command syntax ("moveto"
608  *	or "scroll" options).
609  *
610  * Results:
611  *	The return value is either TK_SCROLL_MOVETO, TK_SCROLL_PAGES,
612  *	TK_SCROLL_UNITS, or TK_SCROLL_ERROR. This indicates whether the
613  *	command was successfully parsed and what form the command took. If
614  *	TK_SCROLL_MOVETO, *dblPtr is filled in with the desired position; if
615  *	TK_SCROLL_PAGES or TK_SCROLL_UNITS, *intPtr is filled in with the
616  *	number of lines to move (may be negative); if TK_SCROLL_ERROR, the
617  *	interp's result contains an error message.
618  *
619  * Side effects:
620  *	None.
621  *
622  *----------------------------------------------------------------------
623  */
624 
625 int
Tk_GetScrollInfo(Tcl_Interp * interp,int argc,const char ** argv,double * dblPtr,int * intPtr)626 Tk_GetScrollInfo(
627     Tcl_Interp *interp,		/* Used for error reporting. */
628     int argc,			/* # arguments for command. */
629     const char **argv,		/* Arguments for command. */
630     double *dblPtr,		/* Filled in with argument "moveto" option, if
631 				 * any. */
632     int *intPtr)		/* Filled in with number of pages or lines to
633 				 * scroll, if any. */
634 {
635     int c = argv[2][0];
636     size_t length = strlen(argv[2]);
637 
638     if ((c == 'm') && (strncmp(argv[2], "moveto", length) == 0)) {
639 	if (argc != 4) {
640 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
641 		    " ", argv[1], " moveto fraction\"", NULL);
642 	    return TK_SCROLL_ERROR;
643 	}
644 	if (Tcl_GetDouble(interp, argv[3], dblPtr) != TCL_OK) {
645 	    return TK_SCROLL_ERROR;
646 	}
647 	return TK_SCROLL_MOVETO;
648     } else if ((c == 's')
649 	    && (strncmp(argv[2], "scroll", length) == 0)) {
650 	if (argc != 5) {
651 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
652 		    " ", argv[1], " scroll number units|pages\"", NULL);
653 	    return TK_SCROLL_ERROR;
654 	}
655 	if (Tcl_GetInt(interp, argv[3], intPtr) != TCL_OK) {
656 	    return TK_SCROLL_ERROR;
657 	}
658 	length = strlen(argv[4]);
659 	c = argv[4][0];
660 	if ((c == 'p') && (strncmp(argv[4], "pages", length) == 0)) {
661 	    return TK_SCROLL_PAGES;
662 	} else if ((c == 'u') && (strncmp(argv[4], "units", length) == 0)) {
663 	    return TK_SCROLL_UNITS;
664 	}
665 
666 	Tcl_AppendResult(interp, "bad argument \"", argv[4],
667 		"\": must be units or pages", NULL);
668 	return TK_SCROLL_ERROR;
669     }
670     Tcl_AppendResult(interp, "unknown option \"", argv[2],
671 	    "\": must be moveto or scroll", NULL);
672     return TK_SCROLL_ERROR;
673 }
674 
675 /*
676  *----------------------------------------------------------------------
677  *
678  * Tk_GetScrollInfoObj --
679  *
680  *	This function is invoked to parse "xview" and "yview" scrolling
681  *	commands for widgets using the new scrolling command syntax ("moveto"
682  *	or "scroll" options).
683  *
684  * Results:
685  *	The return value is either TK_SCROLL_MOVETO, TK_SCROLL_PAGES,
686  *	TK_SCROLL_UNITS, or TK_SCROLL_ERROR. This indicates whether the
687  *	command was successfully parsed and what form the command took. If
688  *	TK_SCROLL_MOVETO, *dblPtr is filled in with the desired position; if
689  *	TK_SCROLL_PAGES or TK_SCROLL_UNITS, *intPtr is filled in with the
690  *	number of lines to move (may be negative); if TK_SCROLL_ERROR, the
691  *	interp's result contains an error message.
692  *
693  * Side effects:
694  *	None.
695  *
696  *----------------------------------------------------------------------
697  */
698 
699 int
Tk_GetScrollInfoObj(Tcl_Interp * interp,int objc,Tcl_Obj * const objv[],double * dblPtr,int * intPtr)700 Tk_GetScrollInfoObj(
701     Tcl_Interp *interp,		/* Used for error reporting. */
702     int objc,			/* # arguments for command. */
703     Tcl_Obj *const objv[],	/* Arguments for command. */
704     double *dblPtr,		/* Filled in with argument "moveto" option, if
705 				 * any. */
706     int *intPtr)		/* Filled in with number of pages or lines to
707 				 * scroll, if any. */
708 {
709     int length;
710     const char *arg;
711 
712     arg = Tcl_GetStringFromObj(objv[2], &length);
713 
714 #define ArgPfxEq(str) ((arg[0]==str[0])&&!strncmp(arg,str,(unsigned)length))
715 
716     if (ArgPfxEq("moveto")) {
717 	if (objc != 4) {
718 	    Tcl_WrongNumArgs(interp, 2, objv, "moveto fraction");
719 	    return TK_SCROLL_ERROR;
720 	}
721 	if (Tcl_GetDoubleFromObj(interp, objv[3], dblPtr) != TCL_OK) {
722 	    return TK_SCROLL_ERROR;
723 	}
724 	return TK_SCROLL_MOVETO;
725     } else if (ArgPfxEq("scroll")) {
726 	if (objc != 5) {
727 	    Tcl_WrongNumArgs(interp, 2, objv, "scroll number units|pages");
728 	    return TK_SCROLL_ERROR;
729 	}
730 	if (Tcl_GetIntFromObj(interp, objv[3], intPtr) != TCL_OK) {
731 	    return TK_SCROLL_ERROR;
732 	}
733 
734 	arg = Tcl_GetStringFromObj(objv[4], &length);
735 	if (ArgPfxEq("pages")) {
736 	    return TK_SCROLL_PAGES;
737 	} else if (ArgPfxEq("units")) {
738 	    return TK_SCROLL_UNITS;
739 	}
740 
741 	Tcl_AppendResult(interp, "bad argument \"", arg,
742 		"\": must be units or pages", NULL);
743 	return TK_SCROLL_ERROR;
744     }
745     Tcl_AppendResult(interp, "unknown option \"", arg,
746 	    "\": must be moveto or scroll", NULL);
747     return TK_SCROLL_ERROR;
748 }
749 
750 /*
751  *---------------------------------------------------------------------------
752  *
753  * TkComputeAnchor --
754  *
755  *	Determine where to place a rectangle so that it will be properly
756  *	anchored with respect to the given window. Used by widgets to align a
757  *	box of text inside a window. When anchoring with respect to one of the
758  *	sides, the rectangle be placed inside of the internal border of the
759  *	window.
760  *
761  * Results:
762  *	*xPtr and *yPtr set to the upper-left corner of the rectangle anchored
763  *	in the window.
764  *
765  * Side effects:
766  *	None.
767  *
768  *---------------------------------------------------------------------------
769  */
770 
771 void
TkComputeAnchor(Tk_Anchor anchor,Tk_Window tkwin,int padX,int padY,int innerWidth,int innerHeight,int * xPtr,int * yPtr)772 TkComputeAnchor(
773     Tk_Anchor anchor,		/* Desired anchor. */
774     Tk_Window tkwin,		/* Anchored with respect to this window. */
775     int padX, int padY,		/* Use this extra padding inside window, in
776 				 * addition to the internal border. */
777     int innerWidth, int innerHeight,
778 				/* Size of rectangle to anchor in window. */
779     int *xPtr, int *yPtr)	/* Returns upper-left corner of anchored
780 				 * rectangle. */
781 {
782     /*
783      * Handle the horizontal parts.
784      */
785 
786     switch (anchor) {
787     case TK_ANCHOR_NW:
788     case TK_ANCHOR_W:
789     case TK_ANCHOR_SW:
790 	*xPtr = Tk_InternalBorderLeft(tkwin) + padX;
791 	break;
792 
793     case TK_ANCHOR_N:
794     case TK_ANCHOR_CENTER:
795     case TK_ANCHOR_S:
796 	*xPtr = (Tk_Width(tkwin) - innerWidth - Tk_InternalBorderLeft(tkwin) -
797 		Tk_InternalBorderRight(tkwin)) / 2 +
798 		Tk_InternalBorderLeft(tkwin);
799 	break;
800 
801     default:
802 	*xPtr = Tk_Width(tkwin) - Tk_InternalBorderRight(tkwin) - padX
803 		- innerWidth;
804 	break;
805     }
806 
807     /*
808      * Handle the vertical parts.
809      */
810 
811     switch (anchor) {
812     case TK_ANCHOR_NW:
813     case TK_ANCHOR_N:
814     case TK_ANCHOR_NE:
815 	*yPtr = Tk_InternalBorderTop(tkwin) + padY;
816 	break;
817 
818     case TK_ANCHOR_W:
819     case TK_ANCHOR_CENTER:
820     case TK_ANCHOR_E:
821 	*yPtr = (Tk_Height(tkwin) - innerHeight- Tk_InternalBorderTop(tkwin) -
822 		Tk_InternalBorderBottom(tkwin)) / 2 +
823 		Tk_InternalBorderTop(tkwin);
824 	break;
825 
826     default:
827 	*yPtr = Tk_Height(tkwin) - Tk_InternalBorderBottom(tkwin) - padY
828 		- innerHeight;
829 	break;
830     }
831 }
832 
833 /*
834  *---------------------------------------------------------------------------
835  *
836  * TkFindStateString --
837  *
838  *	Given a lookup table, map a number to a string in the table.
839  *
840  * Results:
841  *	If numKey was equal to the numeric key of one of the elements in the
842  *	table, returns the string key of that element. Returns NULL if numKey
843  *	was not equal to any of the numeric keys in the table.
844  *
845  * Side effects.
846  *	None.
847  *
848  *---------------------------------------------------------------------------
849  */
850 
851 char *
TkFindStateString(const TkStateMap * mapPtr,int numKey)852 TkFindStateString(
853     const TkStateMap *mapPtr,	/* The state table. */
854     int numKey)			/* The key to try to find in the table. */
855 {
856     for (; mapPtr->strKey!=NULL ; mapPtr++) {
857 	if (numKey == mapPtr->numKey) {
858 	    return (char *) mapPtr->strKey;
859 	}
860     }
861     return NULL;
862 }
863 
864 /*
865  *---------------------------------------------------------------------------
866  *
867  * TkFindStateNum, TkFindStateNumObj --
868  *
869  *	Given a lookup table, map a string to a number in the table.
870  *
871  * Results:
872  *	If strKey was equal to the string keys of one of the elements in the
873  *	table, returns the numeric key of that element. Returns the numKey
874  *	associated with the last element (the NULL string one) in the table if
875  *	strKey was not equal to any of the string keys in the table. In that
876  *	case, an error message is also left in the interp's result (if interp
877  *	is not NULL).
878  *
879  * Side effects.
880  *	None.
881  *
882  *---------------------------------------------------------------------------
883  */
884 
885 int
TkFindStateNum(Tcl_Interp * interp,const char * option,const TkStateMap * mapPtr,const char * strKey)886 TkFindStateNum(
887     Tcl_Interp *interp,		/* Interp for error reporting. */
888     const char *option,		/* String to use when constructing error. */
889     const TkStateMap *mapPtr,	/* Lookup table. */
890     const char *strKey)		/* String to try to find in lookup table. */
891 {
892     const TkStateMap *mPtr;
893 
894     /*
895      * See if the value is in the state map.
896      */
897 
898     for (mPtr = mapPtr; mPtr->strKey != NULL; mPtr++) {
899 	if (strcmp(strKey, mPtr->strKey) == 0) {
900 	    return mPtr->numKey;
901 	}
902     }
903 
904     /*
905      * Not there. Generate an error message (if we can) and return the
906      * default.
907      */
908 
909     if (interp != NULL) {
910 	mPtr = mapPtr;
911 	Tcl_AppendResult(interp, "bad ", option, " value \"", strKey,
912 		"\": must be ", mPtr->strKey, NULL);
913 	for (mPtr++; mPtr->strKey != NULL; mPtr++) {
914 	    Tcl_AppendResult(interp,
915 		    ((mPtr[1].strKey != NULL) ? ", " : ", or "),
916 		    mPtr->strKey, NULL);
917 	}
918     }
919     return mPtr->numKey;
920 }
921 
922 int
TkFindStateNumObj(Tcl_Interp * interp,Tcl_Obj * optionPtr,const TkStateMap * mapPtr,Tcl_Obj * keyPtr)923 TkFindStateNumObj(
924     Tcl_Interp *interp,		/* Interp for error reporting. */
925     Tcl_Obj *optionPtr,		/* String to use when constructing error. */
926     const TkStateMap *mapPtr,	/* Lookup table. */
927     Tcl_Obj *keyPtr)		/* String key to find in lookup table. */
928 {
929     const TkStateMap *mPtr;
930     const char *key;
931     const Tcl_ObjType *typePtr;
932 
933     /*
934      * See if the value is in the object cache.
935      */
936 
937     if ((keyPtr->typePtr == &tkStateKeyObjType)
938 	    && (keyPtr->internalRep.twoPtrValue.ptr1 == mapPtr)) {
939 	return PTR2INT(keyPtr->internalRep.twoPtrValue.ptr2);
940     }
941 
942     /*
943      * Not there. Look in the state map.
944      */
945 
946     key = Tcl_GetStringFromObj(keyPtr, NULL);
947     for (mPtr = mapPtr; mPtr->strKey != NULL; mPtr++) {
948 	if (strcmp(key, mPtr->strKey) == 0) {
949 	    typePtr = keyPtr->typePtr;
950 	    if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
951 		(*typePtr->freeIntRepProc)(keyPtr);
952 	    }
953 	    keyPtr->internalRep.twoPtrValue.ptr1 = (void *) mapPtr;
954 	    keyPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(mPtr->numKey);
955 	    keyPtr->typePtr = &tkStateKeyObjType;
956 	    return mPtr->numKey;
957 	}
958     }
959 
960     /*
961      * Not there either. Generate an error message (if we can) and return the
962      * default.
963      */
964 
965     if (interp != NULL) {
966 	mPtr = mapPtr;
967 	Tcl_AppendResult(interp, "bad ", Tcl_GetString(optionPtr),
968 		" value \"", key, "\": must be ", mPtr->strKey, NULL);
969 	for (mPtr++; mPtr->strKey != NULL; mPtr++) {
970 	    Tcl_AppendResult(interp,
971 		((mPtr[1].strKey != NULL) ? ", " : ", or "),
972 		mPtr->strKey, NULL);
973 	}
974     }
975     return mPtr->numKey;
976 }
977 
978 /*
979  * Local Variables:
980  * mode: c
981  * c-basic-offset: 4
982  * fill-column: 78
983  * End:
984  */
985