1/* $Id$
2 *
3 * text, image, and label elements.
4 *
5 * The label element combines text and image elements,
6 * with layout determined by the "-compound" option.
7 *
8 */
9
10#include <tcl.h>
11#include <tk.h>
12#include "ttkTheme.h"
13
14/*----------------------------------------------------------------------
15 * +++ Text element.
16 *
17 * This element displays a textual label in the foreground color.
18 *
19 * Optionally underlines the mnemonic character if the -underline resource
20 * is present and >= 0.
21 */
22
23typedef struct {
24    /*
25     * Element options:
26     */
27    Tcl_Obj	*textObj;
28    Tcl_Obj	*fontObj;
29    Tcl_Obj	*foregroundObj;
30    Tcl_Obj	*underlineObj;
31    Tcl_Obj	*widthObj;
32    Tcl_Obj	*anchorObj;
33    Tcl_Obj	*justifyObj;
34    Tcl_Obj	*wrapLengthObj;
35    Tcl_Obj     *embossedObj;
36
37    /*
38     * Computed resources:
39     */
40    Tk_Font		tkfont;
41    Tk_TextLayout	textLayout;
42    int 		width;
43    int 		height;
44    int			embossed;
45
46} TextElement;
47
48/* Text element options table.
49 * NB: Keep in sync with label element option table.
50 */
51static Ttk_ElementOptionSpec TextElementOptions[] = {
52    { "-text", TK_OPTION_STRING,
53	Tk_Offset(TextElement,textObj), "" },
54    { "-font", TK_OPTION_FONT,
55	Tk_Offset(TextElement,fontObj), DEFAULT_FONT },
56    { "-foreground", TK_OPTION_COLOR,
57	Tk_Offset(TextElement,foregroundObj), "black" },
58    { "-underline", TK_OPTION_INT,
59	Tk_Offset(TextElement,underlineObj), "-1"},
60    { "-width", TK_OPTION_INT,
61	Tk_Offset(TextElement,widthObj), "-1"},
62    { "-anchor", TK_OPTION_ANCHOR,
63	Tk_Offset(TextElement,anchorObj), "w"},
64    { "-justify", TK_OPTION_JUSTIFY,
65	Tk_Offset(TextElement,justifyObj), "left" },
66    { "-wraplength", TK_OPTION_PIXELS,
67	Tk_Offset(TextElement,wrapLengthObj), "0" },
68    { "-embossed", TK_OPTION_INT,
69	Tk_Offset(TextElement,embossedObj), "0"},
70    { NULL, 0, 0, NULL }
71};
72
73static int TextSetup(TextElement *text, Tk_Window tkwin)
74{
75    const char *string = Tcl_GetString(text->textObj);
76    Tk_Justify justify = TK_JUSTIFY_LEFT;
77    int wrapLength = 0;
78
79    text->tkfont = Tk_GetFontFromObj(tkwin, text->fontObj);
80    Tk_GetJustifyFromObj(NULL, text->justifyObj, &justify);
81    Tk_GetPixelsFromObj(NULL, tkwin, text->wrapLengthObj, &wrapLength);
82    Tcl_GetBooleanFromObj(NULL, text->embossedObj, &text->embossed);
83
84    text->textLayout = Tk_ComputeTextLayout(
85	    text->tkfont, string, -1/*numChars*/, wrapLength, justify,
86	    0/*flags*/, &text->width, &text->height);
87
88    return 1;
89}
90
91/*
92 * TextReqWidth -- compute the requested width of a text element.
93 *
94 * If -width is positive, use that as the width
95 * If -width is negative, use that as the minimum width
96 * If not specified or empty, use the natural size of the text
97 */
98
99static int TextReqWidth(TextElement *text)
100{
101    int reqWidth;
102
103    if (   text->widthObj
104	&& Tcl_GetIntFromObj(NULL, text->widthObj, &reqWidth) == TCL_OK)
105    {
106	int avgWidth = Tk_TextWidth(text->tkfont, "0", 1);
107	if (reqWidth <= 0) {
108	    int specWidth = avgWidth * -reqWidth;
109	    if (specWidth > text->width)
110		return specWidth;
111	} else {
112	    return avgWidth * reqWidth;
113	}
114    }
115    return text->width;
116}
117
118static void TextCleanup(TextElement *text)
119{
120    Tk_FreeTextLayout(text->textLayout);
121}
122
123/*
124 * TextDraw --
125 * 	Draw a text element.
126 * 	Called by TextElementDraw() and LabelElementDraw().
127 */
128static void TextDraw(TextElement *text, Tk_Window tkwin, Drawable d, Ttk_Box b)
129{
130    XColor *color = Tk_GetColorFromObj(tkwin, text->foregroundObj);
131    int underline = -1;
132    int lastChar = -1;
133    XGCValues gcValues;
134    GC gc1, gc2;
135    Tk_Anchor anchor = TK_ANCHOR_CENTER;
136
137    gcValues.font = Tk_FontId(text->tkfont);
138    gcValues.foreground = color->pixel;
139    gc1 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);
140    gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
141    gc2 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);
142
143    /*
144     * Place text according to -anchor:
145     */
146    Tk_GetAnchorFromObj(NULL, text->anchorObj, &anchor);
147    b = Ttk_AnchorBox(b, text->width, text->height, anchor);
148
149    /*
150     * Clip text if it's too wide:
151     * @@@ BUG: This will overclip multi-line text.
152     */
153    if (b.width < text->width) {
154	lastChar = Tk_PointToChar(text->textLayout, b.width, 1) + 1;
155    }
156
157    if (text->embossed) {
158	Tk_DrawTextLayout(Tk_Display(tkwin), d, gc2,
159	    text->textLayout, b.x+1, b.y+1, 0/*firstChar*/, lastChar);
160    }
161    Tk_DrawTextLayout(Tk_Display(tkwin), d, gc1,
162	    text->textLayout, b.x, b.y, 0/*firstChar*/, lastChar);
163
164    Tcl_GetIntFromObj(NULL, text->underlineObj, &underline);
165    if (underline >= 0 && (lastChar == -1 || underline <= lastChar)) {
166	if (text->embossed) {
167	    Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc2,
168		text->textLayout, b.x+1, b.y+1, underline);
169	}
170	Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc1,
171	    text->textLayout, b.x, b.y, underline);
172    }
173
174    Tk_FreeGC(Tk_Display(tkwin), gc1);
175    Tk_FreeGC(Tk_Display(tkwin), gc2);
176}
177
178static void TextElementSize(
179    void *clientData, void *elementRecord, Tk_Window tkwin,
180    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
181{
182    TextElement *text = elementRecord;
183
184    if (!TextSetup(text, tkwin))
185	return;
186
187    *heightPtr = text->height;
188    *widthPtr = TextReqWidth(text);
189
190    TextCleanup(text);
191
192    return;
193}
194
195static void TextElementDraw(
196    void *clientData, void *elementRecord, Tk_Window tkwin,
197    Drawable d, Ttk_Box b, Ttk_State state)
198{
199    TextElement *text = elementRecord;
200    if (TextSetup(text, tkwin)) {
201	TextDraw(text, tkwin, d, b);
202	TextCleanup(text);
203    }
204}
205
206static Ttk_ElementSpec TextElementSpec = {
207    TK_STYLE_VERSION_2,
208    sizeof(TextElement),
209    TextElementOptions,
210    TextElementSize,
211    TextElementDraw
212};
213
214/*----------------------------------------------------------------------
215 * +++ Image element.
216 * Draws an image.
217 */
218
219typedef struct {
220    Tcl_Obj	*imageObj;
221    Tcl_Obj 	*stippleObj;	/* For TTK_STATE_DISABLED */
222    Tcl_Obj 	*backgroundObj;	/* " " */
223
224    Ttk_ImageSpec *imageSpec;
225    Tk_Image	tkimg;
226    int 	width;
227    int		height;
228} ImageElement;
229
230/* ===> NB: Keep in sync with label element option table.  <===
231 */
232static Ttk_ElementOptionSpec ImageElementOptions[] = {
233    { "-image", TK_OPTION_STRING,
234	Tk_Offset(ImageElement,imageObj), "" },
235    { "-stipple", TK_OPTION_STRING, 	/* Really: TK_OPTION_BITMAP */
236	Tk_Offset(ImageElement,stippleObj), "gray50" },
237    { "-background", TK_OPTION_COLOR,
238	Tk_Offset(ImageElement,backgroundObj), DEFAULT_BACKGROUND },
239    { NULL, 0, 0, NULL }
240};
241
242/*
243 * ImageSetup() --
244 * 	Look up the Tk_Image from the image element's imageObj resource.
245 * 	Caller must release the image with ImageCleanup().
246 *
247 * Returns:
248 * 	1 if successful, 0 if there was an error (unreported)
249 * 	or the image resource was not specified.
250 */
251
252static int ImageSetup(
253    ImageElement *image, Tk_Window tkwin, Ttk_State state)
254{
255
256    if (!image->imageObj) {
257	return 0;
258    }
259    image->imageSpec = TtkGetImageSpec(NULL, tkwin, image->imageObj);
260    if (!image->imageSpec) {
261	return 0;
262    }
263    image->tkimg = TtkSelectImage(image->imageSpec, state);
264    if (!image->tkimg) {
265	TtkFreeImageSpec(image->imageSpec);
266	return 0;
267    }
268    Tk_SizeOfImage(image->tkimg, &image->width, &image->height);
269
270    return 1;
271}
272
273static void ImageCleanup(ImageElement *image)
274{
275    TtkFreeImageSpec(image->imageSpec);
276}
277
278/*
279 * StippleOver --
280 * 	Draw a stipple over the image area, to make it look "grayed-out"
281 * 	when TTK_STATE_DISABLED is set.
282 */
283static void StippleOver(
284    ImageElement *image, Tk_Window tkwin, Drawable d, int x, int y)
285{
286    Pixmap stipple = Tk_AllocBitmapFromObj(NULL, tkwin, image->stippleObj);
287    XColor *color = Tk_GetColorFromObj(tkwin, image->backgroundObj);
288
289    if (stipple != None) {
290	unsigned long mask = GCFillStyle | GCStipple | GCForeground;
291	XGCValues gcvalues;
292	GC gc;
293	gcvalues.foreground = color->pixel;
294	gcvalues.fill_style = FillStippled;
295	gcvalues.stipple = stipple;
296	gc = Tk_GetGC(tkwin, mask, &gcvalues);
297	XFillRectangle(Tk_Display(tkwin),d,gc,x,y,image->width,image->height);
298	Tk_FreeGC(Tk_Display(tkwin), gc);
299	Tk_FreeBitmapFromObj(tkwin, image->stippleObj);
300    }
301}
302
303static void ImageDraw(
304    ImageElement *image, Tk_Window tkwin,Drawable d,Ttk_Box b,Ttk_State state)
305{
306    int width = image->width, height = image->height;
307
308    /* Clip width and height to remain within window bounds:
309     */
310    if (b.x + width > Tk_Width(tkwin)) {
311	width = Tk_Width(tkwin) - b.x;
312    }
313    if (b.y + height > Tk_Height(tkwin)) {
314	height = Tk_Height(tkwin) - b.y;
315    }
316
317    if (height <= 0 || width <= 0) {
318	/* Completely clipped - bail out.
319	 */
320	return;
321    }
322
323    Tk_RedrawImage(image->tkimg, 0,0, width, height, d, b.x, b.y);
324
325    /* If we're disabled there's no state-specific 'disabled' image,
326     * stipple the image.
327     * @@@ Possibly: Don't do disabled-stippling at all;
328     * @@@ it's ugly and out of fashion.
329     */
330    if (state & TTK_STATE_DISABLED) {
331	if (TtkSelectImage(image->imageSpec, 0ul) == image->tkimg) {
332	    StippleOver(image, tkwin, d, b.x,b.y);
333	}
334    }
335}
336
337static void ImageElementSize(
338    void *clientData, void *elementRecord, Tk_Window tkwin,
339    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
340{
341    ImageElement *image = elementRecord;
342
343    if (ImageSetup(image, tkwin, 0)) {
344	*widthPtr = image->width;
345	*heightPtr = image->height;
346	ImageCleanup(image);
347    }
348}
349
350static void ImageElementDraw(
351    void *clientData, void *elementRecord, Tk_Window tkwin,
352    Drawable d, Ttk_Box b, Ttk_State state)
353{
354    ImageElement *image = elementRecord;
355
356    if (ImageSetup(image, tkwin, state)) {
357	ImageDraw(image, tkwin, d, b, state);
358	ImageCleanup(image);
359    }
360}
361
362static Ttk_ElementSpec ImageElementSpec = {
363    TK_STYLE_VERSION_2,
364    sizeof(ImageElement),
365    ImageElementOptions,
366    ImageElementSize,
367    ImageElementDraw
368};
369
370/*------------------------------------------------------------------------
371 * +++ Label element.
372 *
373 * Displays an image and/or text, as determined by the -compound option.
374 *
375 * Differences from Tk 8.4 compound elements:
376 *
377 * This adds two new values for the -compound option, "text"
378 * and "image".  (This is useful for configuring toolbars to
379 * display icons, text and icons, or text only, as found in
380 * many browsers.)
381 *
382 * "-compound none" is supported, but I'd like to get rid of it;
383 * it makes the logic more complex, and the only benefit is
384 * backwards compatibility with Tk < 8.3.0 scripts.
385 *
386 * This adds a new resource, -space, for determining how much
387 * space to leave between the text and image; Tk 8.4 reuses the
388 * -padx or -pady option for this purpose.
389 *
390 * -width always specifies the length in characters of the text part;
391 *  in Tk 8.4 it's either characters or pixels, depending on the
392 *  value of -compound.
393 *
394 * Negative values of -width are interpreted as a minimum width
395 * on all platforms, not just on Windows.
396 *
397 * Tk 8.4 ignores -padx and -pady if -compound is set to "none".
398 * Here, padding is handled by a different element.
399 */
400
401typedef struct {
402    /*
403     * Element options:
404     */
405    Tcl_Obj		*compoundObj;
406    Tcl_Obj		*spaceObj;
407    TextElement 	text;
408    ImageElement	image;
409
410    /*
411     * Computed values (see LabelSetup)
412     */
413    Ttk_Compound	compound;
414    int  		space;
415    int 		totalWidth, totalHeight;
416} LabelElement;
417
418static Ttk_ElementOptionSpec LabelElementOptions[] = {
419    { "-compound", TK_OPTION_ANY,
420	Tk_Offset(LabelElement,compoundObj), "none" },
421    { "-space", TK_OPTION_PIXELS,
422	Tk_Offset(LabelElement,spaceObj), "4" },
423
424    /* Text element part:
425     * NB: Keep in sync with TextElementOptions.
426     */
427    { "-text", TK_OPTION_STRING,
428	Tk_Offset(LabelElement,text.textObj), "" },
429    { "-font", TK_OPTION_FONT,
430	Tk_Offset(LabelElement,text.fontObj), DEFAULT_FONT },
431    { "-foreground", TK_OPTION_COLOR,
432	Tk_Offset(LabelElement,text.foregroundObj), "black" },
433    { "-underline", TK_OPTION_INT,
434	Tk_Offset(LabelElement,text.underlineObj), "-1"},
435    { "-width", TK_OPTION_INT,
436	Tk_Offset(LabelElement,text.widthObj), ""},
437    { "-anchor", TK_OPTION_ANCHOR,
438	Tk_Offset(LabelElement,text.anchorObj), "w"},
439    { "-justify", TK_OPTION_JUSTIFY,
440	Tk_Offset(LabelElement,text.justifyObj), "left" },
441    { "-wraplength", TK_OPTION_PIXELS,
442	Tk_Offset(LabelElement,text.wrapLengthObj), "0" },
443    { "-embossed", TK_OPTION_INT,
444	Tk_Offset(LabelElement,text.embossedObj), "0"},
445
446    /* Image element part:
447     * NB: Keep in sync with ImageElementOptions.
448     */
449    { "-image", TK_OPTION_STRING,
450	Tk_Offset(LabelElement,image.imageObj), "" },
451    { "-stipple", TK_OPTION_STRING, 	/* Really: TK_OPTION_BITMAP */
452	Tk_Offset(LabelElement,image.stippleObj), "gray50" },
453    { "-background", TK_OPTION_COLOR,
454	Tk_Offset(LabelElement,image.backgroundObj), DEFAULT_BACKGROUND },
455    { NULL, 0, 0, NULL }
456};
457
458/*
459 * LabelSetup --
460 * 	Fills in computed fields of the label element.
461 *
462 * 	Calculate the text, image, and total width and height.
463 */
464
465#define MAX(a,b) ((a) > (b) ? a : b);
466static void LabelSetup(
467    LabelElement *c, Tk_Window tkwin, Ttk_State state)
468{
469    Ttk_Compound *compoundPtr = &c->compound;
470
471    Tk_GetPixelsFromObj(NULL,tkwin,c->spaceObj,&c->space);
472    Ttk_GetCompoundFromObj(NULL,c->compoundObj,(int*)compoundPtr);
473
474    /*
475     * Deal with TTK_COMPOUND_NONE.
476     */
477    if (c->compound == TTK_COMPOUND_NONE) {
478	if (ImageSetup(&c->image, tkwin, state)) {
479	    c->compound = TTK_COMPOUND_IMAGE;
480	} else {
481	    c->compound = TTK_COMPOUND_TEXT;
482	}
483    } else if (c->compound != TTK_COMPOUND_TEXT) {
484    	if (!ImageSetup(&c->image, tkwin, state)) {
485	    c->compound = TTK_COMPOUND_TEXT;
486	}
487    }
488    if (c->compound != TTK_COMPOUND_IMAGE)
489	TextSetup(&c->text, tkwin);
490
491    /*
492     * ASSERT:
493     * if c->compound != IMAGE, then TextSetup() has been called
494     * if c->compound != TEXT, then ImageSetup() has returned successfully
495     * c->compound != COMPOUND_NONE.
496     */
497
498    switch (c->compound)
499    {
500	case TTK_COMPOUND_NONE:
501	    /* Can't happen */
502	    break;
503	case TTK_COMPOUND_TEXT:
504	    c->totalWidth  = c->text.width;
505	    c->totalHeight = c->text.height;
506	    break;
507	case TTK_COMPOUND_IMAGE:
508	    c->totalWidth  = c->image.width;
509	    c->totalHeight = c->image.height;
510	    break;
511	case TTK_COMPOUND_CENTER:
512	    c->totalWidth  = MAX(c->image.width, c->text.width);
513	    c->totalHeight = MAX(c->image.height, c->text.height);
514	    break;
515	case TTK_COMPOUND_TOP:
516	case TTK_COMPOUND_BOTTOM:
517	    c->totalWidth  = MAX(c->image.width, c->text.width);
518	    c->totalHeight = c->image.height + c->text.height + c->space;
519	    break;
520
521	case TTK_COMPOUND_LEFT:
522	case TTK_COMPOUND_RIGHT:
523	    c->totalWidth  = c->image.width + c->text.width + c->space;
524	    c->totalHeight = MAX(c->image.height, c->text.height);
525	    break;
526    }
527}
528
529static void LabelCleanup(LabelElement *c)
530{
531    if (c->compound != TTK_COMPOUND_TEXT)
532	ImageCleanup(&c->image);
533    if (c->compound != TTK_COMPOUND_IMAGE)
534	TextCleanup(&c->text);
535}
536
537static void LabelElementSize(
538    void *clientData, void *elementRecord, Tk_Window tkwin,
539    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
540{
541    LabelElement *label = elementRecord;
542    int textReqWidth = 0;
543
544    LabelSetup(label, tkwin, 0);
545
546    *heightPtr = label->totalHeight;
547
548    /* Requested width based on -width option, not actual text width:
549     */
550    if (label->compound != TTK_COMPOUND_IMAGE)
551	textReqWidth = TextReqWidth(&label->text);
552
553    switch (label->compound)
554    {
555	case TTK_COMPOUND_TEXT:
556	    *widthPtr = textReqWidth;
557	    break;
558	case TTK_COMPOUND_IMAGE:
559	    *widthPtr = label->image.width;
560	    break;
561	case TTK_COMPOUND_TOP:
562	case TTK_COMPOUND_BOTTOM:
563	case TTK_COMPOUND_CENTER:
564	    *widthPtr = MAX(label->image.width, textReqWidth);
565	    break;
566	case TTK_COMPOUND_LEFT:
567	case TTK_COMPOUND_RIGHT:
568	    *widthPtr = label->image.width + textReqWidth + label->space;
569	    break;
570	case TTK_COMPOUND_NONE:
571	    break; /* Can't happen */
572    }
573
574    LabelCleanup(label);
575}
576
577/*
578 * DrawCompound --
579 * 	Helper routine for LabelElementDraw;
580 * 	Handles layout for -compound {left,right,top,bottom}
581 */
582static void DrawCompound(
583    LabelElement *l, Ttk_Box b, Tk_Window tkwin, Drawable d, Ttk_State state,
584    int imageSide, int textSide)
585{
586    Ttk_Box imageBox =
587	Ttk_PlaceBox(&b, l->image.width, l->image.height, imageSide, 0);
588    Ttk_Box textBox =
589	Ttk_PlaceBox(&b, l->text.width, l->text.height, textSide, 0);
590    ImageDraw(&l->image,tkwin,d,imageBox,state);
591    TextDraw(&l->text,tkwin,d,textBox);
592}
593
594static void LabelElementDraw(
595    void *clientData, void *elementRecord, Tk_Window tkwin,
596    Drawable d, Ttk_Box b, Ttk_State state)
597{
598    LabelElement *l = elementRecord;
599    Tk_Anchor anchor = TK_ANCHOR_CENTER;
600
601    LabelSetup(l, tkwin, state);
602
603    /*
604     * Adjust overall parcel based on -anchor:
605     */
606    Tk_GetAnchorFromObj(NULL, l->text.anchorObj, &anchor);
607    b = Ttk_AnchorBox(b, l->totalWidth, l->totalHeight, anchor);
608
609    /*
610     * Draw text and/or image parts based on -compound:
611     */
612    switch (l->compound)
613    {
614	case TTK_COMPOUND_NONE:
615	    /* Can't happen */
616	    break;
617	case TTK_COMPOUND_TEXT:
618	    TextDraw(&l->text,tkwin,d,b);
619	    break;
620	case TTK_COMPOUND_IMAGE:
621	    ImageDraw(&l->image,tkwin,d,b,state);
622	    break;
623	case TTK_COMPOUND_CENTER:
624	{
625	    Ttk_Box pb = Ttk_AnchorBox(
626		b, l->image.width, l->image.height, TK_ANCHOR_CENTER);
627	    ImageDraw(&l->image, tkwin, d, pb, state);
628	    pb = Ttk_AnchorBox(
629		b, l->text.width, l->text.height, TK_ANCHOR_CENTER);
630	    TextDraw(&l->text, tkwin, d, pb);
631	    break;
632	}
633	case TTK_COMPOUND_TOP:
634	    DrawCompound(l, b, tkwin, d, state, TTK_SIDE_TOP, TTK_SIDE_BOTTOM);
635	    break;
636	case TTK_COMPOUND_BOTTOM:
637	    DrawCompound(l, b, tkwin, d, state, TTK_SIDE_BOTTOM, TTK_SIDE_TOP);
638	    break;
639	case TTK_COMPOUND_LEFT:
640	    DrawCompound(l, b, tkwin, d, state, TTK_SIDE_LEFT, TTK_SIDE_RIGHT);
641	    break;
642	case TTK_COMPOUND_RIGHT:
643	    DrawCompound(l, b, tkwin, d, state, TTK_SIDE_RIGHT, TTK_SIDE_LEFT);
644	    break;
645    }
646
647    LabelCleanup(l);
648}
649
650static Ttk_ElementSpec LabelElementSpec = {
651    TK_STYLE_VERSION_2,
652    sizeof(LabelElement),
653    LabelElementOptions,
654    LabelElementSize,
655    LabelElementDraw
656};
657
658/*------------------------------------------------------------------------
659 * +++ Initialization.
660 */
661
662MODULE_SCOPE
663void TtkLabel_Init(Tcl_Interp *interp)
664{
665    Ttk_Theme theme =  Ttk_GetDefaultTheme(interp);
666
667    Ttk_RegisterElement(interp, theme, "text", &TextElementSpec, NULL);
668    Ttk_RegisterElement(interp, theme, "image", &ImageElementSpec, NULL);
669    Ttk_RegisterElement(interp, theme, "label", &LabelElementSpec, NULL);
670}
671
672