1 /*
2  * Copyright (c) 2004, Joe English
3  *
4  * ttk::frame and ttk::labelframe widgets.
5  */
6 
7 #include "tkInt.h"
8 #include "ttkTheme.h"
9 #include "ttkWidget.h"
10 #include "ttkManager.h"
11 
12 /* ======================================================================
13  * +++ Frame widget:
14  */
15 
16 typedef struct {
17     Tcl_Obj	*borderWidthObj;
18     Tcl_Obj	*paddingObj;
19     Tcl_Obj	*reliefObj;
20     Tcl_Obj 	*widthObj;
21     Tcl_Obj 	*heightObj;
22 } FramePart;
23 
24 typedef struct {
25     WidgetCore	core;
26     FramePart	frame;
27 } Frame;
28 
29 static Tk_OptionSpec FrameOptionSpecs[] = {
30     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", NULL,
31 	Tk_Offset(Frame,frame.borderWidthObj), -1,
32 	TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
33     {TK_OPTION_STRING, "-padding", "padding", "Pad", NULL,
34 	Tk_Offset(Frame,frame.paddingObj), -1,
35 	TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
36     {TK_OPTION_RELIEF, "-relief", "relief", "Relief", NULL,
37 	Tk_Offset(Frame,frame.reliefObj), -1,
38 	TK_OPTION_NULL_OK,0,0 },
39     {TK_OPTION_PIXELS, "-width", "width", "Width", "0",
40 	Tk_Offset(Frame,frame.widthObj), -1,
41 	0,0,GEOMETRY_CHANGED },
42     {TK_OPTION_PIXELS, "-height", "height", "Height", "0",
43 	Tk_Offset(Frame,frame.heightObj), -1,
44 	0,0,GEOMETRY_CHANGED },
45 
46     WIDGET_TAKEFOCUS_FALSE,
47     WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
48 };
49 
50 static const Ttk_Ensemble FrameCommands[] = {
51     { "configure",	TtkWidgetConfigureCommand,0 },
52     { "cget",   	TtkWidgetCgetCommand,0 },
53     { "instate",	TtkWidgetInstateCommand,0 },
54     { "state",  	TtkWidgetStateCommand,0 },
55     { "identify",	TtkWidgetIdentifyCommand,0 },
56     { 0,0,0 }
57 };
58 
59 /*
60  * FrameMargins --
61  * 	Compute internal margins for a frame widget.
62  * 	This includes the -borderWidth, plus any additional -padding.
63  */
FrameMargins(Frame * framePtr)64 static Ttk_Padding FrameMargins(Frame *framePtr)
65 {
66     Ttk_Padding margins = Ttk_UniformPadding(0);
67 
68     /* Check -padding:
69      */
70     if (framePtr->frame.paddingObj) {
71 	Ttk_GetPaddingFromObj(NULL,
72 	    framePtr->core.tkwin, framePtr->frame.paddingObj, &margins);
73     }
74 
75     /* Add padding for border:
76      */
77     if (framePtr->frame.borderWidthObj) {
78 	int border = 0;
79 	Tk_GetPixelsFromObj(NULL,
80 	    framePtr->core.tkwin, framePtr->frame.borderWidthObj, &border);
81 	margins = Ttk_AddPadding(margins, Ttk_UniformPadding((short)border));
82     }
83 
84     return margins;
85 }
86 
87 /* FrameSize procedure --
88  * 	The frame doesn't request a size of its own by default,
89  * 	but it does have an internal border.  See also <<NOTE-SIZE>>
90  */
FrameSize(void * recordPtr,TCL_UNUSED (int *),TCL_UNUSED (int *))91 static int FrameSize(
92     void *recordPtr,
93     TCL_UNUSED(int *),
94     TCL_UNUSED(int *))
95 {
96     Frame *framePtr = (Frame *)recordPtr;
97     Ttk_SetMargins(framePtr->core.tkwin, FrameMargins(framePtr));
98     return 0;
99 }
100 
101 /*
102  * FrameConfigure -- configure hook.
103  *	<<NOTE-SIZE>> Usually the size of a frame is controlled by
104  *	a geometry manager (pack, grid); the -width and -height
105  *	options are only effective if geometry propagation is turned
106  *	off or if the [place] GM is used for child widgets.
107  *
108  *	To avoid geometry blinking, we issue a geometry request
109  *	in the Configure hook instead of the Size hook, and only
110  *	if -width and/or -height is nonzero and one of them
111  *	or the other size-related options (-borderwidth, -padding)
112  *	has been changed.
113  */
114 
FrameConfigure(Tcl_Interp * interp,void * recordPtr,int mask)115 static int FrameConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
116 {
117     Frame *framePtr = (Frame *)recordPtr;
118     int width, height;
119 
120     /*
121      * Make sure -padding resource, if present, is correct:
122      */
123     if (framePtr->frame.paddingObj) {
124 	Ttk_Padding unused;
125 	if (Ttk_GetPaddingFromObj(interp,
126 		    	framePtr->core.tkwin,
127 			framePtr->frame.paddingObj,
128 			&unused) != TCL_OK) {
129 	    return TCL_ERROR;
130 	}
131     }
132 
133     /* See <<NOTE-SIZE>>
134      */
135     if (  TCL_OK != Tk_GetPixelsFromObj(
136 	    interp,framePtr->core.tkwin,framePtr->frame.widthObj,&width)
137        || TCL_OK != Tk_GetPixelsFromObj(
138 	    interp,framePtr->core.tkwin,framePtr->frame.heightObj,&height)
139        )
140     {
141 	return TCL_ERROR;
142     }
143 
144     if ((width > 0 || height > 0) && (mask & GEOMETRY_CHANGED)) {
145 	Tk_GeometryRequest(framePtr->core.tkwin, width, height);
146     }
147 
148     return TtkCoreConfigure(interp, recordPtr, mask);
149 }
150 
151 static WidgetSpec FrameWidgetSpec = {
152     "TFrame",			/* className */
153     sizeof(Frame),		/* recordSize */
154     FrameOptionSpecs,		/* optionSpecs */
155     FrameCommands,		/* subcommands */
156     TtkNullInitialize,		/* initializeProc */
157     TtkNullCleanup,		/* cleanupProc */
158     FrameConfigure,		/* configureProc */
159     TtkNullPostConfigure,	/* postConfigureProc */
160     TtkWidgetGetLayout, 	/* getLayoutProc */
161     FrameSize,			/* sizeProc */
162     TtkWidgetDoLayout,		/* layoutProc */
163     TtkWidgetDisplay		/* displayProc */
164 };
165 
166 TTK_BEGIN_LAYOUT(FrameLayout)
167     TTK_NODE("Frame.border", TTK_FILL_BOTH)
168 TTK_END_LAYOUT
169 
170 /* ======================================================================
171  * +++ Labelframe widget:
172  */
173 
174 #define DEFAULT_LABELINSET	8
175 #define DEFAULT_BORDERWIDTH	2
176 
TtkGetLabelAnchorFromObj(Tcl_Interp * interp,Tcl_Obj * objPtr,Ttk_PositionSpec * anchorPtr)177 int TtkGetLabelAnchorFromObj(
178     Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_PositionSpec *anchorPtr)
179 {
180     const char *string = Tcl_GetString(objPtr);
181     char c = *string++;
182     Ttk_PositionSpec flags = 0;
183 
184     /* First character determines side:
185      */
186     switch (c) {
187 	case 'w' : flags = TTK_PACK_LEFT; 	break;
188 	case 'e' : flags = TTK_PACK_RIGHT; 	break;
189 	case 'n' : flags = TTK_PACK_TOP; 	break;
190 	case 's' : flags = TTK_PACK_BOTTOM; 	break;
191 	default  : goto error;
192     }
193 
194     /* Remaining characters are as per -sticky:
195      */
196     while ((c = *string++) != '\0') {
197 	switch (c) {
198 	    case 'w' : flags |= TTK_STICK_W; break;
199 	    case 'e' : flags |= TTK_STICK_E; break;
200 	    case 'n' : flags |= TTK_STICK_N; break;
201 	    case 's' : flags |= TTK_STICK_S; break;
202 	    default  : goto error;
203 	}
204     }
205 
206     *anchorPtr = flags;
207     return TCL_OK;
208 
209 error:
210     if (interp) {
211 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
212 		"Bad label anchor specification %s", Tcl_GetString(objPtr)));
213 	Tcl_SetErrorCode(interp, "TTK", "LABEL", "ANCHOR", NULL);
214     }
215     return TCL_ERROR;
216 }
217 
218 /* LabelAnchorSide --
219  * 	Returns the side corresponding to a LabelAnchor value.
220  */
LabelAnchorSide(Ttk_PositionSpec flags)221 static Ttk_Side LabelAnchorSide(Ttk_PositionSpec flags)
222 {
223     if (flags & TTK_PACK_LEFT) 		return TTK_SIDE_LEFT;
224     else if (flags & TTK_PACK_RIGHT)	return TTK_SIDE_RIGHT;
225     else if (flags & TTK_PACK_TOP)	return TTK_SIDE_TOP;
226     else if (flags & TTK_PACK_BOTTOM)	return TTK_SIDE_BOTTOM;
227     /*NOTREACHED*/
228     return TTK_SIDE_TOP;
229 }
230 
231 /*
232  * Labelframe widget record:
233  */
234 typedef struct {
235     Tcl_Obj 	*labelAnchorObj;
236     Tcl_Obj	*textObj;
237     Tcl_Obj 	*underlineObj;
238     Tk_Window	labelWidget;
239 
240     Ttk_Manager	*mgr;
241     Ttk_Layout	labelLayout;	/* Sublayout for label */
242     Ttk_Box	labelParcel;	/* Set in layoutProc */
243 } LabelframePart;
244 
245 typedef struct {
246     WidgetCore  	core;
247     FramePart   	frame;
248     LabelframePart	label;
249 } Labelframe;
250 
251 #define LABELWIDGET_CHANGED 0x100
252 
253 static Tk_OptionSpec LabelframeOptionSpecs[] = {
254     {TK_OPTION_STRING, "-labelanchor", "labelAnchor", "LabelAnchor",
255 	"nw", Tk_Offset(Labelframe, label.labelAnchorObj),-1,
256         0,0,GEOMETRY_CHANGED},
257     {TK_OPTION_STRING, "-text", "text", "Text", "",
258 	Tk_Offset(Labelframe,label.textObj), -1,
259 	0,0,GEOMETRY_CHANGED },
260     {TK_OPTION_INT, "-underline", "underline", "Underline",
261 	"-1", Tk_Offset(Labelframe,label.underlineObj), -1,
262 	0,0,0 },
263     {TK_OPTION_WINDOW, "-labelwidget", "labelWidget", "LabelWidget", NULL,
264 	-1, Tk_Offset(Labelframe,label.labelWidget),
265 	TK_OPTION_NULL_OK,0,LABELWIDGET_CHANGED|GEOMETRY_CHANGED },
266 
267     WIDGET_INHERIT_OPTIONS(FrameOptionSpecs)
268 };
269 
270 /*
271  * Labelframe style parameters:
272  */
273 typedef struct {
274     int 		borderWidth;	/* border width */
275     Ttk_Padding 	padding;	/* internal padding */
276     Ttk_PositionSpec	labelAnchor;	/* corner/side to place label */
277     Ttk_Padding		labelMargins;	/* extra space around label */
278     int 		labelOutside;	/* true=>place label outside border */
279 } LabelframeStyle;
280 
LabelframeStyleOptions(Labelframe * lf,LabelframeStyle * style)281 static void LabelframeStyleOptions(Labelframe *lf, LabelframeStyle *style)
282 {
283     Ttk_Layout layout = lf->core.layout;
284     Tcl_Obj *objPtr;
285 
286     style->borderWidth = DEFAULT_BORDERWIDTH;
287     style->padding = Ttk_UniformPadding(0);
288     style->labelAnchor = TTK_PACK_TOP | TTK_STICK_W;
289     style->labelOutside = 0;
290 
291     if ((objPtr = Ttk_QueryOption(layout, "-borderwidth", 0)) != NULL) {
292 	Tk_GetPixelsFromObj(NULL, lf->core.tkwin, objPtr, &style->borderWidth);
293     }
294     if ((objPtr = Ttk_QueryOption(layout, "-padding", 0)) != NULL) {
295 	Ttk_GetPaddingFromObj(NULL, lf->core.tkwin, objPtr, &style->padding);
296     }
297     if ((objPtr = Ttk_QueryOption(layout,"-labelanchor", 0)) != NULL) {
298 	TtkGetLabelAnchorFromObj(NULL, objPtr, &style->labelAnchor);
299     }
300     if ((objPtr = Ttk_QueryOption(layout,"-labelmargins", 0)) != NULL) {
301 	Ttk_GetBorderFromObj(NULL, objPtr, &style->labelMargins);
302     } else {
303 	if (style->labelAnchor & (TTK_PACK_TOP|TTK_PACK_BOTTOM)) {
304 	    style->labelMargins =
305 		Ttk_MakePadding(DEFAULT_LABELINSET,0,DEFAULT_LABELINSET,0);
306 	} else {
307 	    style->labelMargins =
308 		Ttk_MakePadding(0,DEFAULT_LABELINSET,0,DEFAULT_LABELINSET);
309 	}
310     }
311     if ((objPtr = Ttk_QueryOption(layout,"-labeloutside", 0)) != NULL) {
312 	Tcl_GetBooleanFromObj(NULL, objPtr, &style->labelOutside);
313     }
314 
315     return;
316 }
317 
318 /* LabelframeLabelSize --
319  * 	Extract the requested width and height of the labelframe's label:
320  * 	taken from the label widget if specified, otherwise the text label.
321  */
322 static void
LabelframeLabelSize(Labelframe * lframePtr,int * widthPtr,int * heightPtr)323 LabelframeLabelSize(Labelframe *lframePtr, int *widthPtr, int *heightPtr)
324 {
325     Tk_Window labelWidget = lframePtr->label.labelWidget;
326     Ttk_Layout labelLayout = lframePtr->label.labelLayout;
327 
328     if (labelWidget) {
329 	*widthPtr = Tk_ReqWidth(labelWidget);
330 	*heightPtr = Tk_ReqHeight(labelWidget);
331     } else if (labelLayout) {
332 	Ttk_LayoutSize(labelLayout, 0, widthPtr, heightPtr);
333     } else {
334 	*widthPtr = *heightPtr = 0;
335     }
336 }
337 
338 /*
339  * LabelframeSize --
340  * 	Like the frame, this doesn't request a size of its own
341  * 	but it does have internal padding and a minimum size.
342  */
LabelframeSize(void * recordPtr,TCL_UNUSED (int *),TCL_UNUSED (int *))343 static int LabelframeSize(
344     void *recordPtr,
345     TCL_UNUSED(int *),
346     TCL_UNUSED(int *))
347 {
348     Labelframe *lframePtr = (Labelframe *)recordPtr;
349     WidgetCore *corePtr = &lframePtr->core;
350     Ttk_Padding margins;
351     LabelframeStyle style;
352     int labelWidth, labelHeight;
353 
354     LabelframeStyleOptions(lframePtr, &style);
355 
356     /* Compute base margins (See also: FrameMargins)
357      */
358     margins = Ttk_AddPadding(
359 		style.padding, Ttk_UniformPadding((short)style.borderWidth));
360 
361     /* Adjust margins based on label size and position:
362      */
363     LabelframeLabelSize(lframePtr, &labelWidth, &labelHeight);
364     labelWidth += Ttk_PaddingWidth(style.labelMargins);
365     labelHeight += Ttk_PaddingHeight(style.labelMargins);
366 
367     switch (LabelAnchorSide(style.labelAnchor)) {
368 	case TTK_SIDE_LEFT:	margins.left   += labelWidth;	break;
369 	case TTK_SIDE_RIGHT:	margins.right  += labelWidth;	break;
370 	case TTK_SIDE_TOP:	margins.top    += labelHeight;	break;
371 	case TTK_SIDE_BOTTOM:	margins.bottom += labelHeight;	break;
372     }
373 
374     Ttk_SetMargins(corePtr->tkwin,margins);
375 
376     /* Request minimum size based on border width and label size:
377      */
378     Tk_SetMinimumRequestSize(corePtr->tkwin,
379 	    labelWidth + 2*style.borderWidth,
380 	    labelHeight + 2*style.borderWidth);
381 
382     return 0;
383 }
384 
385 /*
386  * LabelframeGetLayout --
387  * 	Getlayout widget hook.
388  */
389 
LabelframeGetLayout(Tcl_Interp * interp,Ttk_Theme theme,void * recordPtr)390 static Ttk_Layout LabelframeGetLayout(
391     Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
392 {
393     Labelframe *lf = (Labelframe *)recordPtr;
394     Ttk_Layout frameLayout = TtkWidgetGetLayout(interp, theme, recordPtr);
395     Ttk_Layout labelLayout;
396 
397     if (!frameLayout) {
398 	return NULL;
399     }
400 
401     labelLayout = Ttk_CreateSublayout(
402 	interp, theme, frameLayout, ".Label", lf->core.optionTable);
403 
404     if (labelLayout) {
405 	if (lf->label.labelLayout) {
406 	    Ttk_FreeLayout(lf->label.labelLayout);
407 	}
408 	Ttk_RebindSublayout(labelLayout, recordPtr);
409 	lf->label.labelLayout = labelLayout;
410     }
411 
412     return frameLayout;
413 }
414 
415 /*
416  * LabelframeDoLayout --
417  * 	Labelframe layout hook.
418  *
419  * Side effects: Computes labelParcel.
420  */
421 
LabelframeDoLayout(void * recordPtr)422 static void LabelframeDoLayout(void *recordPtr)
423 {
424     Labelframe *lframePtr = (Labelframe *)recordPtr;
425     WidgetCore *corePtr = &lframePtr->core;
426     int lw, lh;			/* Label width and height */
427     LabelframeStyle style;
428     Ttk_Box borderParcel = Ttk_WinBox(lframePtr->core.tkwin);
429     Ttk_Box labelParcel;
430 
431     /*
432      * Compute label parcel:
433      */
434     LabelframeStyleOptions(lframePtr, &style);
435     LabelframeLabelSize(lframePtr, &lw, &lh);
436     lw += Ttk_PaddingWidth(style.labelMargins);
437     lh += Ttk_PaddingHeight(style.labelMargins);
438 
439     labelParcel = Ttk_PadBox(
440 	Ttk_PositionBox(&borderParcel, lw, lh, style.labelAnchor),
441 	style.labelMargins);
442 
443     if (!style.labelOutside) {
444 	/* Move border edge so it's over label:
445 	*/
446 	switch (LabelAnchorSide(style.labelAnchor)) {
447 	    case TTK_SIDE_LEFT: 	borderParcel.x -= lw / 2;
448 	    /* FALLTHRU */
449 	    case TTK_SIDE_RIGHT:	borderParcel.width += lw/2; 	break;
450 	    case TTK_SIDE_TOP:  	borderParcel.y -= lh / 2;
451 	    /* FALLTHRU */
452 	    case TTK_SIDE_BOTTOM:	borderParcel.height += lh / 2;	break;
453 	}
454     }
455 
456     /*
457      * Place border and label:
458      */
459     Ttk_PlaceLayout(corePtr->layout, corePtr->state, borderParcel);
460     if (lframePtr->label.labelLayout) {
461 	Ttk_PlaceLayout(
462 	    lframePtr->label.labelLayout, corePtr->state, labelParcel);
463     }
464     /* labelWidget placed in LabelframePlaceContent GM hook */
465     lframePtr->label.labelParcel = labelParcel;
466 }
467 
LabelframeDisplay(void * recordPtr,Drawable d)468 static void LabelframeDisplay(void *recordPtr, Drawable d)
469 {
470     Labelframe *lframePtr = (Labelframe *)recordPtr;
471     Ttk_DrawLayout(lframePtr->core.layout, lframePtr->core.state, d);
472     if (lframePtr->label.labelLayout) {
473 	Ttk_DrawLayout(lframePtr->label.labelLayout, lframePtr->core.state, d);
474     }
475 }
476 
477 /* +++ Labelframe geometry manager hooks.
478  */
479 
480 /* LabelframePlaceContent --
481  * 	Sets the position and size of the labelwidget.
482  */
LabelframePlaceContent(void * recordPtr)483 static void LabelframePlaceContent(void *recordPtr)
484 {
485     Labelframe *lframe = (Labelframe *)recordPtr;
486 
487     if (Ttk_NumberContent(lframe->label.mgr) == 1) {
488 	Ttk_Box b;
489 	LabelframeDoLayout(recordPtr);
490 	b = lframe->label.labelParcel;
491 	/* ASSERT: content #0 is lframe->label.labelWidget */
492 	Ttk_PlaceContent(lframe->label.mgr, 0, b.x,b.y,b.width,b.height);
493     }
494 }
495 
LabelRequest(TCL_UNUSED (void *),TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int))496 static int LabelRequest(
497     TCL_UNUSED(void *),
498     TCL_UNUSED(int),
499     TCL_UNUSED(int),
500     TCL_UNUSED(int))
501 {
502     return 1;
503 }
504 
505 /* LabelRemoved --
506  * 	Unset the -labelwidget option.
507  *
508  * <<NOTE-LABELREMOVED>>:
509  * 	This routine is also called when the widget voluntarily forgets
510  * 	the window in LabelframeConfigure.
511  */
LabelRemoved(void * managerData,TCL_UNUSED (int))512 static void LabelRemoved(
513     void *managerData,
514     TCL_UNUSED(int))
515 {
516     Labelframe *lframe = (Labelframe *)managerData;
517 
518     lframe->label.labelWidget = 0;
519 }
520 
521 static Ttk_ManagerSpec LabelframeManagerSpec = {
522     { "labelframe", Ttk_GeometryRequestProc, Ttk_LostContentProc },
523     LabelframeSize,
524     LabelframePlaceContent,
525     LabelRequest,
526     LabelRemoved
527 };
528 
529 /* LabelframeInitialize --
530  * 	Initialization hook.
531  */
LabelframeInitialize(TCL_UNUSED (Tcl_Interp *),void * recordPtr)532 static void LabelframeInitialize(
533     TCL_UNUSED(Tcl_Interp *),
534     void *recordPtr)
535 {
536     Labelframe *lframe = (Labelframe *)recordPtr;
537 
538     lframe->label.mgr = Ttk_CreateManager(
539 	&LabelframeManagerSpec, lframe, lframe->core.tkwin);
540     lframe->label.labelWidget = 0;
541     lframe->label.labelLayout = 0;
542     lframe->label.labelParcel = Ttk_MakeBox(-1,-1,-1,-1);
543 }
544 
545 /* LabelframeCleanup --
546  * 	Cleanup hook.
547  */
LabelframeCleanup(void * recordPtr)548 static void LabelframeCleanup(void *recordPtr)
549 {
550     Labelframe *lframe = (Labelframe *)recordPtr;
551     Ttk_DeleteManager(lframe->label.mgr);
552     if (lframe->label.labelLayout) {
553 	Ttk_FreeLayout(lframe->label.labelLayout);
554     }
555 }
556 
557 /* RaiseLabelWidget --
558  * 	Raise the -labelwidget to ensure that the labelframe doesn't
559  * 	obscure it (if it's not a direct child), or bring it to
560  * 	the top of the stacking order (if it is).
561  */
RaiseLabelWidget(Labelframe * lframe)562 static void RaiseLabelWidget(Labelframe *lframe)
563 {
564     Tk_Window parent = Tk_Parent(lframe->label.labelWidget);
565     Tk_Window sibling = NULL;
566     Tk_Window w = lframe->core.tkwin;
567 
568     while (w && w != parent) {
569 	sibling = w;
570 	w = Tk_Parent(w);
571     }
572 
573     Tk_RestackWindow(lframe->label.labelWidget, Above, sibling);
574 }
575 
576 /* LabelframeConfigure --
577  * 	Configuration hook.
578  */
LabelframeConfigure(Tcl_Interp * interp,void * recordPtr,int mask)579 static int LabelframeConfigure(Tcl_Interp *interp,void *recordPtr,int mask)
580 {
581     Labelframe *lframePtr = (Labelframe *)recordPtr;
582     Tk_Window labelWidget = lframePtr->label.labelWidget;
583     Ttk_PositionSpec unused;
584 
585     /* Validate options:
586      */
587     if (mask & LABELWIDGET_CHANGED && labelWidget != NULL) {
588 	if (!Ttk_Maintainable(interp, labelWidget, lframePtr->core.tkwin)) {
589 	    return TCL_ERROR;
590 	}
591     }
592 
593     if (TtkGetLabelAnchorFromObj(
594     	interp, lframePtr->label.labelAnchorObj, &unused) != TCL_OK)
595     {
596 	return TCL_ERROR;
597     }
598 
599     /* Base class configuration:
600      */
601     if (FrameConfigure(interp, recordPtr, mask) != TCL_OK) {
602 	return TCL_ERROR;
603     }
604 
605     /* Update -labelwidget changes, if any:
606      */
607     if (mask & LABELWIDGET_CHANGED) {
608 	if (Ttk_NumberContent(lframePtr->label.mgr) == 1) {
609 	    Ttk_ForgetContent(lframePtr->label.mgr, 0);
610 	    /* Restore labelWidget field (see <<NOTE-LABELREMOVED>>)
611 	     */
612 	    lframePtr->label.labelWidget = labelWidget;
613 	}
614 
615 	if (labelWidget) {
616 		Ttk_InsertContent(lframePtr->label.mgr, 0, labelWidget, NULL);
617 	    RaiseLabelWidget(lframePtr);
618 	}
619     }
620 
621     if (mask & GEOMETRY_CHANGED) {
622 	Ttk_ManagerSizeChanged(lframePtr->label.mgr);
623 	Ttk_ManagerLayoutChanged(lframePtr->label.mgr);
624     }
625 
626     return TCL_OK;
627 }
628 
629 static WidgetSpec LabelframeWidgetSpec = {
630     "TLabelframe",		/* className */
631     sizeof(Labelframe),		/* recordSize */
632     LabelframeOptionSpecs, 	/* optionSpecs */
633     FrameCommands,		/* subcommands */
634     LabelframeInitialize,	/* initializeProc */
635     LabelframeCleanup,		/* cleanupProc */
636     LabelframeConfigure,	/* configureProc */
637     TtkNullPostConfigure,  	/* postConfigureProc */
638     LabelframeGetLayout, 	/* getLayoutProc */
639     LabelframeSize,		/* sizeProc */
640     LabelframeDoLayout,		/* layoutProc */
641     LabelframeDisplay		/* displayProc */
642 };
643 
644 TTK_BEGIN_LAYOUT(LabelframeLayout)
645     TTK_NODE("Labelframe.border", TTK_FILL_BOTH)
646 TTK_END_LAYOUT
647 
TTK_BEGIN_LAYOUT(LabelSublayout)648 TTK_BEGIN_LAYOUT(LabelSublayout)
649     TTK_GROUP("Label.fill", TTK_FILL_BOTH,
650 	TTK_NODE("Label.text", TTK_FILL_BOTH))
651 TTK_END_LAYOUT
652 
653 /* ======================================================================
654  * +++ Initialization.
655  */
656 
657 MODULE_SCOPE
658 void TtkFrame_Init(Tcl_Interp *interp)
659 {
660     Ttk_Theme theme =  Ttk_GetDefaultTheme(interp);
661 
662     Ttk_RegisterLayout(theme, "TFrame", FrameLayout);
663     Ttk_RegisterLayout(theme, "TLabelframe", LabelframeLayout);
664     Ttk_RegisterLayout(theme, "Label", LabelSublayout);
665 
666     RegisterWidget(interp, "ttk::frame", &FrameWidgetSpec);
667     RegisterWidget(interp, "ttk::labelframe", &LabelframeWidgetSpec);
668 }
669 
670