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