1 /*
2  * Copyright (c) 2003, Joe English
3  *
4  * Core widget utilities.
5  */
6 
7 #include "tkInt.h"
8 #include "ttkTheme.h"
9 #include "ttkWidget.h"
10 
11 /*------------------------------------------------------------------------
12  * +++ Internal helper routines.
13  */
14 
15 /* UpdateLayout --
16  * 	Call the widget's get-layout hook to recompute corePtr->layout.
17  * 	Returns TCL_OK if successful, returns TCL_ERROR and leaves
18  * 	the layout unchanged otherwise.
19  */
UpdateLayout(Tcl_Interp * interp,WidgetCore * corePtr)20 static int UpdateLayout(Tcl_Interp *interp, WidgetCore *corePtr)
21 {
22     Ttk_Theme themePtr = Ttk_GetCurrentTheme(interp);
23     Ttk_Layout newLayout =
24     	corePtr->widgetSpec->getLayoutProc(interp, themePtr,corePtr);
25 
26     if (newLayout) {
27 	if (corePtr->layout) {
28 	    Ttk_FreeLayout(corePtr->layout);
29 	}
30 	corePtr->layout = newLayout;
31 	return TCL_OK;
32     }
33     return TCL_ERROR;
34 }
35 
36 /* SizeChanged --
37  * 	Call the widget's sizeProc to compute new requested size
38  * 	and pass it to the geometry manager.
39  */
SizeChanged(WidgetCore * corePtr)40 static void SizeChanged(WidgetCore *corePtr)
41 {
42     int reqWidth = 1, reqHeight = 1;
43 
44     if (corePtr->widgetSpec->sizeProc(corePtr,&reqWidth,&reqHeight)) {
45 	Tk_GeometryRequest(corePtr->tkwin, reqWidth, reqHeight);
46     }
47 }
48 
49 #ifndef TK_NO_DOUBLE_BUFFERING
50 
51 /* BeginDrawing --
52  * 	Returns a Drawable for drawing the widget contents.
53  *	This is normally an off-screen Pixmap, copied to
54  *	the window by EndDrawing().
55  */
BeginDrawing(Tk_Window tkwin)56 static Drawable BeginDrawing(Tk_Window tkwin)
57 {
58     return Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
59 	    Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
60 }
61 
62 /* EndDrawing --
63  *	Copy the drawable contents to the screen and release resources.
64  */
EndDrawing(Tk_Window tkwin,Drawable d)65 static void EndDrawing(Tk_Window tkwin, Drawable d)
66 {
67     XGCValues gcValues;
68     GC gc;
69 
70     gcValues.function = GXcopy;
71     gcValues.graphics_exposures = False;
72     gc = Tk_GetGC(tkwin, GCFunction|GCGraphicsExposures, &gcValues);
73 
74     XCopyArea(Tk_Display(tkwin), d, Tk_WindowId(tkwin), gc,
75 	    0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
76 	    0, 0);
77 
78     Tk_FreePixmap(Tk_Display(tkwin), d);
79     Tk_FreeGC(Tk_Display(tkwin), gc);
80 }
81 #else
82 /* No double-buffering: draw directly into the window. */
BeginDrawing(Tk_Window tkwin)83 static Drawable BeginDrawing(Tk_Window tkwin) { return Tk_WindowId(tkwin); }
EndDrawing(Tk_Window tkwin,Drawable d)84 static void EndDrawing(Tk_Window tkwin, Drawable d) { }
85 #endif
86 
87 /* DrawWidget --
88  *	Redraw a widget.  Called as an idle handler.
89  */
DrawWidget(ClientData recordPtr)90 static void DrawWidget(ClientData recordPtr)
91 {
92     WidgetCore *corePtr = recordPtr;
93 
94     corePtr->flags &= ~REDISPLAY_PENDING;
95     if (Tk_IsMapped(corePtr->tkwin)) {
96 	Drawable d = BeginDrawing(corePtr->tkwin);
97 	corePtr->widgetSpec->layoutProc(recordPtr);
98 	corePtr->widgetSpec->displayProc(recordPtr, d);
99 	EndDrawing(corePtr->tkwin, d);
100     }
101 }
102 
103 /* TtkRedisplayWidget --
104  * 	Schedule redisplay as an idle handler.
105  */
TtkRedisplayWidget(WidgetCore * corePtr)106 void TtkRedisplayWidget(WidgetCore *corePtr)
107 {
108     if (corePtr->flags & WIDGET_DESTROYED) {
109 	return;
110     }
111 
112     if (!(corePtr->flags & REDISPLAY_PENDING)) {
113 	Tcl_DoWhenIdle(DrawWidget, corePtr);
114 	corePtr->flags |= REDISPLAY_PENDING;
115     }
116 }
117 
118 /* TtkResizeWidget --
119  * 	Recompute widget size, schedule geometry propagation and redisplay.
120  */
TtkResizeWidget(WidgetCore * corePtr)121 void TtkResizeWidget(WidgetCore *corePtr)
122 {
123     if (corePtr->flags & WIDGET_DESTROYED) {
124 	return;
125     }
126 
127     SizeChanged(corePtr);
128     TtkRedisplayWidget(corePtr);
129 }
130 
131 /* TtkWidgetChangeState --
132  * 	Set / clear the specified bits in the 'state' flag,
133  */
TtkWidgetChangeState(WidgetCore * corePtr,unsigned int setBits,unsigned int clearBits)134 void TtkWidgetChangeState(WidgetCore *corePtr,
135     unsigned int setBits, unsigned int clearBits)
136 {
137     Ttk_State oldState = corePtr->state;
138     corePtr->state = (oldState & ~clearBits) | setBits;
139     if (corePtr->state ^ oldState) {
140 	TtkRedisplayWidget(corePtr);
141     }
142 }
143 
144 /* WidgetInstanceObjCmd --
145  *	Widget instance command implementation.
146  */
147 static int
WidgetInstanceObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])148 WidgetInstanceObjCmd(
149     ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
150 {
151     WidgetCore *corePtr = clientData;
152     const Ttk_Ensemble *commands = corePtr->widgetSpec->commands;
153     int status;
154 
155     Tcl_Preserve(clientData);
156     status = Ttk_InvokeEnsemble(commands,1, clientData,interp,objc,objv);
157     Tcl_Release(clientData);
158 
159     return status;
160 }
161 
162 /*------------------------------------------------------------------------
163  * +++ Widget destruction.
164  *
165  * A widget can be destroyed when the application explicitly
166  * destroys the window or one of its ancestors via [destroy]
167  * or Tk_DestroyWindow(); when the application deletes the widget
168  * instance command; when there is an error in the widget constructor;
169  * or when another application calls XDestroyWindow on the window ID.
170  *
171  * The window receives a <DestroyNotify> event in all cases,
172  * so we do the bulk of the cleanup there.  See [#2207435] for
173  * further notes (esp. re: Tk_FreeConfigOptions).
174  *
175  * Widget code that reenters the interp should only do so
176  * when the widtget is Tcl_Preserve()d, and should check
177  * the WIDGET_DESTROYED flag bit upon return.
178  */
179 
180 /* WidgetInstanceObjCmdDeleted --
181  * 	Widget instance command	deletion callback.
182  */
183 static void
WidgetInstanceObjCmdDeleted(ClientData clientData)184 WidgetInstanceObjCmdDeleted(ClientData clientData)
185 {
186     WidgetCore *corePtr = clientData;
187     corePtr->widgetCmd = NULL;
188     if (corePtr->tkwin != NULL)
189 	Tk_DestroyWindow(corePtr->tkwin);
190 }
191 
192 /* FreeWidget --
193  *	 Final cleanup for widget; called via Tcl_EventuallyFree().
194  */
195 static void
FreeWidget(void * memPtr)196 FreeWidget(void *memPtr)
197 {
198     ckfree(memPtr);
199 }
200 
201 /* DestroyWidget --
202  * 	Main widget destructor; called from <DestroyNotify> event handler.
203  */
204 static void
DestroyWidget(WidgetCore * corePtr)205 DestroyWidget(WidgetCore *corePtr)
206 {
207     corePtr->flags |= WIDGET_DESTROYED;
208 
209     corePtr->widgetSpec->cleanupProc(corePtr);
210 
211     Tk_FreeConfigOptions(
212 	(ClientData)corePtr, corePtr->optionTable, corePtr->tkwin);
213 
214     if (corePtr->layout) {
215 	Ttk_FreeLayout(corePtr->layout);
216     }
217 
218     if (corePtr->flags & REDISPLAY_PENDING) {
219 	Tcl_CancelIdleCall(DrawWidget, corePtr);
220     }
221 
222     corePtr->tkwin = NULL;
223     if (corePtr->widgetCmd) {
224 	Tcl_Command cmd = corePtr->widgetCmd;
225 	corePtr->widgetCmd = 0;
226 	/* NB: this can reenter the interpreter via a command traces */
227 	Tcl_DeleteCommandFromToken(corePtr->interp, cmd);
228     }
229     Tcl_EventuallyFree(corePtr, (Tcl_FreeProc *) FreeWidget);
230 }
231 
232 /*
233  * CoreEventProc --
234  *	Event handler for basic events.
235  *	Processes Expose, Configure, FocusIn/Out, and Destroy events.
236  *	Also handles <<ThemeChanged>> virtual events.
237  *
238  *	For Expose and Configure, simply schedule the widget for redisplay.
239  *	For Destroy events, handle the cleanup process.
240  *
241  *	For Focus events, set/clear the focus bit in the state field.
242  *	It turns out this is impossible to do correctly in a binding script,
243  *	because Tk filters out focus events with detail == NotifyInferior.
244  *
245  *	For Deactivate/Activate pseudo-events, set/clear the background state
246  *	flag.
247  */
248 
249 static const unsigned CoreEventMask
250     = ExposureMask
251     | StructureNotifyMask
252     | FocusChangeMask
253     | VirtualEventMask
254     | ActivateMask
255     | EnterWindowMask
256     | LeaveWindowMask
257     ;
258 
CoreEventProc(ClientData clientData,XEvent * eventPtr)259 static void CoreEventProc(ClientData clientData, XEvent *eventPtr)
260 {
261     WidgetCore *corePtr = (WidgetCore *)clientData;
262 
263     switch (eventPtr->type)
264     {
265 	case ConfigureNotify :
266 	    TtkRedisplayWidget(corePtr);
267 	    break;
268 	case Expose :
269 	    if (eventPtr->xexpose.count == 0) {
270 		TtkRedisplayWidget(corePtr);
271 	    }
272 	    break;
273 	case DestroyNotify :
274 	    Tk_DeleteEventHandler(
275 		corePtr->tkwin, CoreEventMask,CoreEventProc,clientData);
276 	    DestroyWidget(corePtr);
277 	    break;
278 	case FocusIn:
279 	case FocusOut:
280 	    /* Don't process "virtual crossing" events */
281 	    if (   eventPtr->xfocus.detail == NotifyInferior
282 		|| eventPtr->xfocus.detail == NotifyAncestor
283 		|| eventPtr->xfocus.detail == NotifyNonlinear)
284 	    {
285 		if (eventPtr->type == FocusIn)
286 		    corePtr->state |= TTK_STATE_FOCUS;
287 		else
288 		    corePtr->state &= ~TTK_STATE_FOCUS;
289 		TtkRedisplayWidget(corePtr);
290 	    }
291 	    break;
292 	case ActivateNotify:
293 	    corePtr->state &= ~TTK_STATE_BACKGROUND;
294 	    TtkRedisplayWidget(corePtr);
295 	    break;
296 	case DeactivateNotify:
297 	    corePtr->state |= TTK_STATE_BACKGROUND;
298 	    TtkRedisplayWidget(corePtr);
299 	    break;
300 	case LeaveNotify:
301 	    corePtr->state &= ~TTK_STATE_HOVER;
302 	    TtkRedisplayWidget(corePtr);
303 	    break;
304 	case EnterNotify:
305 	    corePtr->state |= TTK_STATE_HOVER;
306 	    TtkRedisplayWidget(corePtr);
307 	    break;
308 	case VirtualEvent: {
309 	    const char *name = ((XVirtualEvent *)eventPtr)->name;
310 	    if ((name != NULL) && !strcmp("ThemeChanged", name)) {
311 		(void)UpdateLayout(corePtr->interp, corePtr);
312 		SizeChanged(corePtr);
313 		TtkRedisplayWidget(corePtr);
314 	    }
315 	    break;
316 	}
317 	default:
318 	    /* can't happen... */
319 	    break;
320     }
321 }
322 
323 /*
324  * WidgetWorldChanged --
325  * 	Default Tk_ClassWorldChangedProc() for widgets.
326  * 	Invoked whenever fonts or other system resources are changed;
327  * 	recomputes geometry.
328  */
WidgetWorldChanged(ClientData clientData)329 static void WidgetWorldChanged(ClientData clientData)
330 {
331     WidgetCore *corePtr = clientData;
332     SizeChanged(corePtr);
333     TtkRedisplayWidget(corePtr);
334 }
335 
336 static Tk_ClassProcs widgetClassProcs = {
337     sizeof(Tk_ClassProcs),	/* size */
338     WidgetWorldChanged,	/* worldChangedProc */
339     NULL,					/* createProc */
340     NULL					/* modalProc */
341 };
342 
343 /*
344  * TtkWidgetConstructorObjCmd --
345  *	General-purpose widget constructor command implementation.
346  *	ClientData is a WidgetSpec *.
347  */
TtkWidgetConstructorObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])348 int TtkWidgetConstructorObjCmd(
349     ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
350 {
351     WidgetSpec *widgetSpec = clientData;
352     const char *className = widgetSpec->className;
353     Tk_OptionTable optionTable =
354 	Tk_CreateOptionTable(interp, widgetSpec->optionSpecs);
355     Tk_Window tkwin;
356     void *recordPtr;
357     WidgetCore *corePtr;
358     Tk_SavedOptions savedOptions;
359     int i;
360 
361     if (objc < 2 || objc % 2 == 1) {
362 	Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?");
363 	return TCL_ERROR;
364     }
365 
366     /* Check if a -class option has been specified.
367      * We have to do this before the InitOptions() call,
368      * since InitOptions() is affected by the widget class.
369      */
370     for (i = 2; i < objc; i += 2) {
371 	if (!strcmp(Tcl_GetString(objv[i]), "-class")) {
372 	    className = Tcl_GetString(objv[i+1]);
373 	    break;
374 	}
375     }
376 
377     tkwin = Tk_CreateWindowFromPath(
378 	interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), NULL);
379     if (tkwin == NULL)
380 	return TCL_ERROR;
381 
382     /*
383      * Allocate and initialize the widget record.
384      */
385     recordPtr = ckalloc(widgetSpec->recordSize);
386     memset(recordPtr, 0, widgetSpec->recordSize);
387     corePtr = recordPtr;
388 
389     corePtr->tkwin	= tkwin;
390     corePtr->interp 	= interp;
391     corePtr->widgetSpec	= widgetSpec;
392     corePtr->widgetCmd	= Tcl_CreateObjCommand(interp, Tk_PathName(tkwin),
393 	WidgetInstanceObjCmd, recordPtr, WidgetInstanceObjCmdDeleted);
394     corePtr->optionTable = optionTable;
395     corePtr->layout	= NULL;
396     corePtr->flags 	= 0;
397     corePtr->state 	= 0;
398 
399     Tk_SetClass(tkwin, className);
400     Tk_SetClassProcs(tkwin, &widgetClassProcs, recordPtr);
401     Tk_SetWindowBackgroundPixmap(tkwin, ParentRelative);
402 
403     widgetSpec->initializeProc(interp, recordPtr);
404 
405     Tk_CreateEventHandler(tkwin, CoreEventMask, CoreEventProc, recordPtr);
406 
407     /*
408      * Initial configuration.
409      */
410 
411     Tcl_Preserve(corePtr);
412     if (Tk_InitOptions(interp, recordPtr, optionTable, tkwin) != TCL_OK) {
413 	goto error;
414     }
415 
416     if (Tk_SetOptions(interp, recordPtr, optionTable,
417 	    objc - 2, objv + 2, tkwin, &savedOptions, NULL) != TCL_OK) {
418 	Tk_RestoreSavedOptions(&savedOptions);
419 	goto error;
420     } else {
421 	Tk_FreeSavedOptions(&savedOptions);
422     }
423     if (widgetSpec->configureProc(interp, recordPtr, ~0) != TCL_OK)
424 	goto error;
425     if (widgetSpec->postConfigureProc(interp, recordPtr, ~0) != TCL_OK)
426 	goto error;
427 
428     if (WidgetDestroyed(corePtr))
429 	goto error;
430 
431     Tcl_Release(corePtr);
432 
433     SizeChanged(corePtr);
434     Tk_MakeWindowExist(tkwin);
435 
436     Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tkwin), -1));
437     return TCL_OK;
438 
439 error:
440     if (WidgetDestroyed(corePtr)) {
441 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
442 		"widget has been destroyed", -1));
443     } else {
444 	Tk_DestroyWindow(tkwin);
445     }
446     Tcl_Release(corePtr);
447     return TCL_ERROR;
448 }
449 
450 /*------------------------------------------------------------------------
451  * +++ Default implementations for widget hook procedures.
452  */
453 
454 /* TtkWidgetGetLayout --
455  * 	Default getLayoutProc.
456  *	Looks up the layout based on the -style resource (if specified),
457  *	otherwise use the widget class.
458  */
TtkWidgetGetLayout(Tcl_Interp * interp,Ttk_Theme themePtr,void * recordPtr)459 Ttk_Layout TtkWidgetGetLayout(
460     Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr)
461 {
462     WidgetCore *corePtr = recordPtr;
463     const char *styleName = 0;
464 
465     if (corePtr->styleObj)
466     	styleName = Tcl_GetString(corePtr->styleObj);
467 
468     if (!styleName || *styleName == '\0')
469     	styleName = corePtr->widgetSpec->className;
470 
471     return Ttk_CreateLayout(interp, themePtr, styleName,
472 	recordPtr, corePtr->optionTable, corePtr->tkwin);
473 }
474 
475 /*
476  * TtkWidgetGetOrientedLayout --
477  * 	Helper routine.  Same as TtkWidgetGetLayout, but prefixes
478  * 	"Horizontal." or "Vertical." to the style name, depending
479  * 	on the value of the 'orient' option.
480  */
TtkWidgetGetOrientedLayout(Tcl_Interp * interp,Ttk_Theme themePtr,void * recordPtr,Tcl_Obj * orientObj)481 Ttk_Layout TtkWidgetGetOrientedLayout(
482     Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr, Tcl_Obj *orientObj)
483 {
484     WidgetCore *corePtr = recordPtr;
485     const char *baseStyleName = 0;
486     Tcl_DString styleName;
487     int orient = TTK_ORIENT_HORIZONTAL;
488     Ttk_Layout layout;
489 
490     Tcl_DStringInit(&styleName);
491 
492     /* Prefix:
493      */
494     Ttk_GetOrientFromObj(NULL, orientObj, &orient);
495     if (orient == TTK_ORIENT_HORIZONTAL)
496 	Tcl_DStringAppend(&styleName, "Horizontal.", -1);
497     else
498 	Tcl_DStringAppend(&styleName, "Vertical.", -1);
499 
500     /* Add base style name:
501      */
502     if (corePtr->styleObj)
503     	baseStyleName = Tcl_GetString(corePtr->styleObj);
504     if (!baseStyleName || *baseStyleName == '\0')
505     	baseStyleName = corePtr->widgetSpec->className;
506 
507     Tcl_DStringAppend(&styleName, baseStyleName, -1);
508 
509     /* Create layout:
510      */
511     layout= Ttk_CreateLayout(interp, themePtr, Tcl_DStringValue(&styleName),
512 	recordPtr, corePtr->optionTable, corePtr->tkwin);
513 
514     Tcl_DStringFree(&styleName);
515 
516     return layout;
517 }
518 
519 /* TtkNullInitialize --
520  * 	Default widget initializeProc (no-op)
521  */
TtkNullInitialize(Tcl_Interp * interp,void * recordPtr)522 void TtkNullInitialize(Tcl_Interp *interp, void *recordPtr)
523 {
524 }
525 
526 /* TtkNullPostConfigure --
527  * 	Default widget postConfigureProc (no-op)
528  */
TtkNullPostConfigure(Tcl_Interp * interp,void * clientData,int mask)529 int TtkNullPostConfigure(Tcl_Interp *interp, void *clientData, int mask)
530 {
531     return TCL_OK;
532 }
533 
534 /* TtkCoreConfigure --
535  * 	Default widget configureProc.
536  * 	Handles -style option.
537  */
TtkCoreConfigure(Tcl_Interp * interp,void * clientData,int mask)538 int TtkCoreConfigure(Tcl_Interp *interp, void *clientData, int mask)
539 {
540     WidgetCore *corePtr = clientData;
541     int status = TCL_OK;
542 
543     if (mask & STYLE_CHANGED) {
544 	status = UpdateLayout(interp, corePtr);
545     }
546 
547     return status;
548 }
549 
550 /* TtkNullCleanup --
551  * 	Default widget cleanupProc (no-op)
552  */
TtkNullCleanup(void * recordPtr)553 void TtkNullCleanup(void *recordPtr)
554 {
555     return;
556 }
557 
558 /* TtkWidgetDoLayout --
559  * 	Default widget layoutProc.
560  */
TtkWidgetDoLayout(void * clientData)561 void TtkWidgetDoLayout(void *clientData)
562 {
563     WidgetCore *corePtr = clientData;
564     Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
565 }
566 
567 /* TtkWidgetDisplay --
568  * 	Default widget displayProc.
569  */
TtkWidgetDisplay(void * recordPtr,Drawable d)570 void TtkWidgetDisplay(void *recordPtr, Drawable d)
571 {
572     WidgetCore *corePtr = recordPtr;
573     Ttk_DrawLayout(corePtr->layout, corePtr->state, d);
574 }
575 
576 /* TtkWidgetSize --
577  * 	Default widget sizeProc()
578  */
TtkWidgetSize(void * recordPtr,int * widthPtr,int * heightPtr)579 int TtkWidgetSize(void *recordPtr, int *widthPtr, int *heightPtr)
580 {
581     WidgetCore *corePtr = recordPtr;
582     Ttk_LayoutSize(corePtr->layout, corePtr->state, widthPtr, heightPtr);
583     return 1;
584 }
585 
586 /*------------------------------------------------------------------------
587  * +++ Default implementations for widget subcommands.
588  */
589 
590 /* $w cget -option
591  */
TtkWidgetCgetCommand(void * recordPtr,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])592 int TtkWidgetCgetCommand(
593     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
594 {
595     WidgetCore *corePtr = recordPtr;
596     Tcl_Obj *result;
597 
598     if (objc != 3) {
599 	Tcl_WrongNumArgs(interp, 2, objv, "option");
600 	return TCL_ERROR;
601     }
602     result = Tk_GetOptionValue(interp, recordPtr,
603 		corePtr->optionTable, objv[2], corePtr->tkwin);
604     if (result == NULL)
605 	return TCL_ERROR;
606     Tcl_SetObjResult(interp, result);
607     return TCL_OK;
608 }
609 
610 /* $w configure ?-option ?value ....??
611  */
TtkWidgetConfigureCommand(void * recordPtr,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])612 int TtkWidgetConfigureCommand(
613     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
614 {
615     WidgetCore *corePtr = recordPtr;
616     Tcl_Obj *result;
617 
618     if (objc == 2) {
619 	result = Tk_GetOptionInfo(interp, recordPtr,
620 		corePtr->optionTable, NULL, corePtr->tkwin);
621     } else if (objc == 3) {
622 	result = Tk_GetOptionInfo(interp, recordPtr,
623 		corePtr->optionTable, objv[2], corePtr->tkwin);
624     } else {
625 	Tk_SavedOptions savedOptions;
626 	int status;
627 	int mask = 0;
628 
629 	status = Tk_SetOptions(interp, recordPtr,
630 		corePtr->optionTable, objc - 2, objv + 2,
631 		corePtr->tkwin, &savedOptions, &mask);
632 	if (status != TCL_OK)
633 	    return status;
634 
635 	if (mask & READONLY_OPTION) {
636 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
637 		    "attempt to change read-only option", -1));
638 	    Tk_RestoreSavedOptions(&savedOptions);
639 	    return TCL_ERROR;
640 	}
641 
642 	status = corePtr->widgetSpec->configureProc(interp, recordPtr, mask);
643 	if (status != TCL_OK) {
644 	    Tk_RestoreSavedOptions(&savedOptions);
645 	    return status;
646 	}
647 	Tk_FreeSavedOptions(&savedOptions);
648 
649 	status = corePtr->widgetSpec->postConfigureProc(interp,recordPtr,mask);
650 	if (WidgetDestroyed(corePtr)) {
651 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
652 		    "widget has been destroyed", -1));
653 	    status = TCL_ERROR;
654 	}
655 	if (status != TCL_OK) {
656 	    return status;
657 	}
658 
659 	if (mask & (STYLE_CHANGED | GEOMETRY_CHANGED)) {
660 	    SizeChanged(corePtr);
661 	}
662 
663 	TtkRedisplayWidget(corePtr);
664 	result = Tcl_NewObj();
665     }
666 
667     if (result == 0) {
668 	return TCL_ERROR;
669     }
670     Tcl_SetObjResult(interp, result);
671     return TCL_OK;
672 }
673 
674 /* $w state ? $stateSpec ?
675  *
676  * 	If $stateSpec is specified, modify the widget state accordingly,
677  * 	return a new stateSpec representing the changed bits.
678  *
679  * 	Otherwise, return a statespec matching all the currently-set bits.
680  */
681 
TtkWidgetStateCommand(void * recordPtr,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])682 int TtkWidgetStateCommand(
683     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
684 {
685     WidgetCore *corePtr = recordPtr;
686     Ttk_StateSpec spec;
687     int status;
688     Ttk_State oldState, changed;
689 
690     if (objc == 2) {
691 	Tcl_SetObjResult(interp,
692 	    Ttk_NewStateSpecObj(corePtr->state, 0ul));
693 	return TCL_OK;
694     }
695 
696     if (objc != 3) {
697 	Tcl_WrongNumArgs(interp, 2, objv, "state-spec");
698 	return TCL_ERROR;
699     }
700     status = Ttk_GetStateSpecFromObj(interp, objv[2], &spec);
701     if (status != TCL_OK)
702 	return status;
703 
704     oldState = corePtr->state;
705     corePtr->state = Ttk_ModifyState(corePtr->state, &spec);
706     changed = corePtr->state ^ oldState;
707 
708     TtkRedisplayWidget(corePtr);
709 
710     Tcl_SetObjResult(interp,
711 	Ttk_NewStateSpecObj(oldState & changed, ~oldState & changed));
712     return status;
713 }
714 
715 /* $w instate $stateSpec ?$script?
716  *
717  * 	Tests if widget state matches $stateSpec.
718  *	If $script is specified, execute script if state matches.
719  *	Otherwise, return true/false
720  */
721 
TtkWidgetInstateCommand(void * recordPtr,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])722 int TtkWidgetInstateCommand(
723     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
724 {
725     WidgetCore *corePtr = recordPtr;
726     Ttk_State state = corePtr->state;
727     Ttk_StateSpec spec;
728     int status = TCL_OK;
729 
730     if (objc < 3 || objc > 4) {
731 	Tcl_WrongNumArgs(interp, 2, objv, "state-spec ?script?");
732 	return TCL_ERROR;
733     }
734     status = Ttk_GetStateSpecFromObj(interp, objv[2], &spec);
735     if (status != TCL_OK)
736 	return status;
737 
738     if (objc == 3) {
739 	Tcl_SetObjResult(interp,
740 	    Tcl_NewBooleanObj(Ttk_StateMatches(state,&spec)));
741     } else if (objc == 4) {
742 	if (Ttk_StateMatches(state,&spec)) {
743 	    status = Tcl_EvalObjEx(interp, objv[3], 0);
744 	}
745     }
746     return status;
747 }
748 
749 /* $w identify $x $y
750  * $w identify element $x $y
751  * 	Returns: name of element at $x, $y
752  */
TtkWidgetIdentifyCommand(void * recordPtr,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])753 int TtkWidgetIdentifyCommand(
754     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
755 {
756     WidgetCore *corePtr = recordPtr;
757     Ttk_Element element;
758     static const char *whatTable[] = { "element", NULL };
759     int x, y, what;
760 
761     if (objc < 4 || objc > 5) {
762 	Tcl_WrongNumArgs(interp, 2, objv, "?what? x y");
763 	return TCL_ERROR;
764     }
765     if (objc == 5) {
766 	/* $w identify element $x $y */
767 	if (Tcl_GetIndexFromObjStruct(interp, objv[2], whatTable,
768 		sizeof(char *), "option", 0, &what) != TCL_OK)
769 	{
770 	    return TCL_ERROR;
771 	}
772     }
773 
774     if (   Tcl_GetIntFromObj(interp, objv[objc-2], &x) != TCL_OK
775 	|| Tcl_GetIntFromObj(interp, objv[objc-1], &y) != TCL_OK
776     ) {
777 	return TCL_ERROR;
778     }
779 
780     element = Ttk_IdentifyElement(corePtr->layout, x, y);
781     if (element) {
782 	const char *elementName = Ttk_ElementName(element);
783 	Tcl_SetObjResult(interp,Tcl_NewStringObj(elementName,-1));
784     }
785 
786     return TCL_OK;
787 }
788 
789 /*EOF*/
790