1 /*
2  * tixDiStyle.c --
3  *
4  *	This file implements the "Display Item Styles" in the Tix library.
5  *
6  * Copyright (c) 1993-1999 Ioi Kim Lam.
7  * Copyright (c) 2000-2001 Tix Project Group.
8  *
9  * See the file "license.terms" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * $Id: tixDiStyle.c,v 1.6 2004/03/28 02:44:56 hobbs Exp $
13  */
14 
15 #include <tixPort.h>
16 #include <tixInt.h>
17 
18 typedef struct StyleLink {
19     Tix_DItemInfo * diTypePtr;
20     Tix_DItemStyle* stylePtr;
21     struct StyleLink * next;
22 } StyleLink;
23 
24 typedef struct StyleInfo {
25     Tix_StyleTemplate * tmplPtr;
26     Tix_StyleTemplate tmpl;
27     StyleLink * linkHead;
28 } StyleInfo;
29 
30 
31 static int   		DItemStyleParseProc _ANSI_ARGS_((ClientData clientData,
32 			    Tcl_Interp *interp, Tk_Window tkwin,
33 			    CONST84 char *value,char *widRec, int offset));
34 static char *		DItemStylePrintProc _ANSI_ARGS_((
35 			    ClientData clientData, Tk_Window tkwin,
36 			    char *widRec, int offset,
37 			    Tcl_FreeProc **freeProcPtr));
38 static Tix_DItemStyle*	FindDefaultStyle _ANSI_ARGS_((
39 			    Tix_DItemInfo * diTypePtr, Tk_Window tkwin));
40 static Tix_DItemStyle*	FindStyle _ANSI_ARGS_((
41 			    CONST84 char *styleName, Tcl_Interp *interp));
42 static Tix_DItemStyle* 	GetDItemStyle  _ANSI_ARGS_((
43 			    Tix_DispData * ddPtr, Tix_DItemInfo * diTypePtr,
44 			    CONST84 char *styleName, int *isNew_ret));
45 static void		ListAdd _ANSI_ARGS_((Tix_DItemStyle * stylePtr,
46 			    Tix_DItem *iPtr));
47 static void		ListDelete _ANSI_ARGS_((Tix_DItemStyle * stylePtr,
48 			    Tix_DItem *iPtr));
49 static void		ListDeleteAll _ANSI_ARGS_((Tix_DItemStyle * stylePtr));
50 static void		StyleCmdDeletedProc _ANSI_ARGS_((
51 			    ClientData clientData));
52 static int		StyleCmd _ANSI_ARGS_((ClientData clientData,
53 			    Tcl_Interp *interp, int argc, CONST84 char **argv));
54 static int		StyleConfigure _ANSI_ARGS_((Tcl_Interp *interp,
55 			    Tix_DItemStyle* stylePtr, int argc,
56 			    CONST84 char **argv, int flags));
57 static void		StyleDestroy _ANSI_ARGS_((ClientData clientData));
58 static void		DeleteStyle _ANSI_ARGS_((Tix_DItemStyle * stylePtr));
59 static void		DefWindowStructureProc _ANSI_ARGS_((
60 			    ClientData clientData, XEvent *eventPtr));
61 static void		RefWindowStructureProc _ANSI_ARGS_((
62 			    ClientData clientData, XEvent *eventPtr));
63 static void		SetDefaultStyle _ANSI_ARGS_((Tix_DItemInfo *diTypePtr,
64 			    Tk_Window tkwin, Tix_DItemStyle * stylePtr));
65 
66 static TIX_DECLARE_SUBCMD(StyleConfigCmd);
67 static TIX_DECLARE_SUBCMD(StyleCGetCmd);
68 static TIX_DECLARE_SUBCMD(StyleDeleteCmd);
69 
70 static Tcl_HashTable defaultTable;
71 
72 /*
73  * This macro returns a hashtable to convert from style names (such as
74  * "tixStyle0") to Tix_DItemStyle structures.
75  */
76 
77 #define GetStyleTable(x) \
78     (TixGetHashTable((x), "tixStyleTab", NULL, TCL_STRING_KEYS))
79 
80 
81 /*
82  *--------------------------------------------------------------
83  *
84  * TixInitializeDisplayItems --
85  *
86  *	Initializes the Display Item subsystem.
87  *
88  * Results:
89  *	Nothing
90  *
91  * Side effects:
92  *	Built-in Display Types styles are created.
93  *
94  *--------------------------------------------------------------
95  */
96 
97 void
TixInitializeDisplayItems()98 TixInitializeDisplayItems()
99 {
100     static int inited = 0;
101 
102     if (!inited) {
103         inited = 1;
104         Tcl_InitHashTable(&defaultTable, TCL_ONE_WORD_KEYS);
105 
106 	Tix_AddDItemType(&tix_ImageTextItemType);
107 	Tix_AddDItemType(&tix_TextItemType);
108 	Tix_AddDItemType(&tix_WindowItemType);
109 	Tix_AddDItemType(&tix_ImageItemType);
110     }
111 }
112 
113 /*
114  *--------------------------------------------------------------
115  *
116  * TixDItemStyleFree --
117  *
118  *	When an item does not need a style anymore (when the item
119  *	is destroyed, e.g.), it must call this procedute to free the
120  *	style).
121  *
122  * Results:
123  *	Nothing
124  *
125  * Side effects:
126  *	The item is freed from the list of attached items in the style.
127  *	Also, the style will be freed if it was already destroyed and
128  *	it has no more items attached to it.
129  *
130  *--------------------------------------------------------------
131  */
132 
133 void
TixDItemStyleFree(iPtr,stylePtr)134 TixDItemStyleFree(iPtr, stylePtr)
135     Tix_DItem *iPtr;
136     Tix_DItemStyle * stylePtr;
137 {
138     ListDelete(stylePtr, iPtr);
139 }
140 
141 /*
142  *--------------------------------------------------------------
143  *
144  * Tix_ItemStyleCmd --
145  *
146  *	This procedure is invoked to process the "tixItemStyle" Tcl
147  *	command.
148  *
149  * Results:
150  *	A standard Tcl result.
151  *
152  * Side effects:
153  *	A new widget is created and configured.
154  *
155  *--------------------------------------------------------------
156  */
157 
158 int
Tix_ItemStyleCmd(clientData,interp,argc,argv)159 Tix_ItemStyleCmd(clientData, interp, argc, argv)
160     ClientData clientData;
161     Tcl_Interp *interp;		/* Current interpreter. */
162     int argc;			/* Number of arguments. */
163     CONST84 char **argv;	/* Argument strings. */
164 {
165     Tix_DItemInfo * diTypePtr;
166     Tk_Window tkwin = (Tk_Window)clientData;
167     CONST84 char * styleName = NULL;
168     Tix_DispData dispData;
169     char buff[16 + TCL_INTEGER_SPACE];
170     int i, n;
171     static int counter = 0;
172     Tix_DItemStyle * stylePtr;
173 
174     if (argc < 2) {
175 	return Tix_ArgcError(interp, argc, argv, 1,
176 	    "itemtype ?option value ...");
177     }
178 
179     if ((diTypePtr=Tix_GetDItemType(interp, argv[1])) == NULL) {
180 	return TCL_ERROR;
181     }
182 
183     /*
184      * Parse the -refwindow option: this tells the style to use this
185      * window to query the default values for background, foreground
186      * etc. Usually, you should set the -refwindow to the window that
187      * holds the display items which are controlled by this style.
188      */
189 
190     if (argc > 2) {
191 	size_t len;
192 	if (argc %2 != 0) {
193 	    Tcl_AppendResult(interp, "value for \"", argv[argc-1],
194 		"\" missing", NULL);
195 	    return TCL_ERROR;
196 	}
197 	for (n=i=2; i<argc; i+=2) {
198 	    len = strlen(argv[i]);
199 	    if (strncmp(argv[i], "-refwindow", len) == 0) {
200 		if ((tkwin=Tk_NameToWindow(interp,argv[i+1],tkwin)) == NULL) {
201 		    return TCL_ERROR;
202 		}
203 		continue;
204 	    }
205 	    if (strncmp(argv[i], "-stylename", len) == 0) {
206 		styleName = argv[i+1];
207 		if (FindStyle(styleName, interp) != NULL) {
208 		    Tcl_AppendResult(interp, "style \"", argv[i+1],
209 			"\" already exists", NULL);
210 		    return TCL_ERROR;
211 		}
212 		continue;
213 	    }
214 
215 	    if (n!=i) {
216 		argv[n]   = argv[i];
217 		argv[n+1] = argv[i+1];
218 	    }
219 	    n+=2;
220 	}
221 	argc = n;
222     }
223 
224     if (styleName == NULL) {
225 	/*
226 	 * No name is given, we'll make a unique name by default. The
227          * following loop hopefully will not run forever (unless
228          * the user has created more than 4 billion styles with default
229          * names ...:-)
230 	 */
231 
232         while (1) {
233             sprintf(buff, "tixStyle%d", counter++);
234             if (Tcl_FindHashEntry(GetStyleTable(interp), buff) == NULL) {
235                 styleName = buff;
236                 break;
237             }
238         }
239     }
240 
241     dispData.interp  = interp;
242     dispData.display = Tk_Display(tkwin);
243     dispData.tkwin   = tkwin;
244 
245     if ((stylePtr = GetDItemStyle(&dispData, diTypePtr,
246 	 styleName, NULL)) == NULL) {
247 	return TCL_ERROR;
248     }
249     if (StyleConfigure(interp, stylePtr, argc-2, argv+2, 0) != TCL_OK) {
250 	DeleteStyle(stylePtr);
251 	return TCL_ERROR;
252     }
253     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
254 	    RefWindowStructureProc, (ClientData)stylePtr);
255 
256     Tcl_ResetResult(interp);
257     Tcl_AppendResult(interp, styleName, NULL);
258     return TCL_OK;
259 }
260 
261 static int
StyleCmd(clientData,interp,argc,argv)262 StyleCmd(clientData, interp, argc, argv)
263     ClientData clientData;
264     Tcl_Interp *interp;
265     int argc;
266     CONST84 char **argv;
267 {
268     int code;
269 
270     static Tix_SubCmdInfo subCmdInfo[] = {
271 	{TIX_DEFAULT_LEN, "cget", 1, 1, StyleCGetCmd,
272 	   "option"},
273 	{TIX_DEFAULT_LEN, "configure", 0, TIX_VAR_ARGS, StyleConfigCmd,
274 	   "?option? ?value? ?option value ... ?"},
275 	{TIX_DEFAULT_LEN, "delete", 0, 0, StyleDeleteCmd,
276 	   ""},
277     };
278 
279     static Tix_CmdInfo cmdInfo = {
280 	Tix_ArraySize(subCmdInfo), 1, TIX_VAR_ARGS, "?option? arg ?arg ...?",
281     };
282 
283     Tk_Preserve(clientData);
284     code = Tix_HandleSubCmds(&cmdInfo, subCmdInfo, clientData,
285 	interp, argc, argv);
286     Tk_Release(clientData);
287 
288     return code;
289 }
290 
291 /*----------------------------------------------------------------------
292  * "cget" sub command
293  *----------------------------------------------------------------------
294  */
295 static int
StyleCGetCmd(clientData,interp,argc,argv)296 StyleCGetCmd(clientData, interp, argc, argv)
297     ClientData clientData;
298     Tcl_Interp *interp;		/* Current interpreter. */
299     int argc;			/* Number of arguments. */
300     CONST84 char **argv;	/* Argument strings. */
301 {
302     Tix_DItemStyle* stylePtr= (Tix_DItemStyle*) clientData;
303 
304     return Tk_ConfigureValue(interp, stylePtr->base.tkwin,
305 	stylePtr->base.diTypePtr->styleConfigSpecs,
306 	(char *)stylePtr, argv[0], 0);
307 }
308 
309 /*----------------------------------------------------------------------
310  * "configure" sub command
311  *----------------------------------------------------------------------
312  */
313 static int
StyleConfigCmd(clientData,interp,argc,argv)314 StyleConfigCmd(clientData, interp, argc, argv)
315     ClientData clientData;
316     Tcl_Interp *interp;		/* Current interpreter. */
317     int argc;			/* Number of arguments. */
318     CONST84 char **argv;	/* Argument strings. */
319 {
320     Tix_DItemStyle* stylePtr= (Tix_DItemStyle*) clientData;
321 
322     if (argc == 0) {
323 	return Tk_ConfigureInfo(interp, stylePtr->base.tkwin,
324 	    stylePtr->base.diTypePtr->styleConfigSpecs,
325 	    (char *)stylePtr, (char *) NULL, 0);
326     } else if (argc == 1) {
327 	return Tk_ConfigureInfo(interp, stylePtr->base.tkwin,
328 	    stylePtr->base.diTypePtr->styleConfigSpecs,
329 	    (char *)stylePtr, argv[0], 0);
330     } else {
331 	return StyleConfigure(interp, stylePtr, argc, argv,
332 	    TK_CONFIG_ARGV_ONLY);
333     }
334 }
335 
336 /*----------------------------------------------------------------------
337  * "delete" sub command
338  *----------------------------------------------------------------------
339  */
340 static int
StyleDeleteCmd(clientData,interp,argc,argv)341 StyleDeleteCmd(clientData, interp, argc, argv)
342     ClientData clientData;
343     Tcl_Interp *interp;		/* Current interpreter. */
344     int argc;			/* Number of arguments. */
345     CONST84 char **argv;	/* Argument strings. */
346 {
347     Tix_DItemStyle* stylePtr= (Tix_DItemStyle*) clientData;
348 
349     if (stylePtr->base.flags & TIX_STYLE_DEFAULT) {
350 	Tcl_AppendResult(interp, "Cannot delete default item style",
351 	    NULL);
352 	return TCL_ERROR;
353     }
354 
355     DeleteStyle(stylePtr);
356     return TCL_OK;
357 }
358 
359 static int
StyleConfigure(interp,stylePtr,argc,argv,flags)360 StyleConfigure(interp, stylePtr, argc, argv, flags)
361     Tcl_Interp *interp;		/* Used for error reporting. */
362     Tix_DItemStyle* stylePtr;	/* Information about the style;  may or may
363 				 * not already have values for some fields. */
364     int argc;			/* Number of valid entries in argv. */
365     CONST84 char **argv;	/* Arguments. */
366     int flags;			/* Flags to pass to Tk_ConfigureWidget. */
367 {
368     Tix_DItemInfo * diTypePtr = stylePtr->base.diTypePtr;
369 
370     if (diTypePtr->styleConfigureProc(stylePtr, argc, argv, flags) != TCL_OK) {
371 	return TCL_ERROR;
372     }
373     return TCL_OK;
374 }
375 
376 /*----------------------------------------------------------------------
377  * StyleDestroy --
378  *
379  *	Destroy a display style.
380  *----------------------------------------------------------------------
381  */
382 static void
StyleDestroy(clientData)383 StyleDestroy(clientData)
384     ClientData clientData;
385 {
386     Tix_DItemStyle* stylePtr= (Tix_DItemStyle*) clientData;
387     int i;
388 
389     if ((stylePtr->base.flags & TIX_STYLE_DEFAULT)) {
390 	/*
391 	 * If this is the default style for the display items, we
392 	 * can't tell the display items that it has lost its style,
393 	 * otherwise the ditem will just attempt to create the default
394 	 * style again, and we will go into an infinite loop
395 	 */
396 	if (stylePtr->base.refCount != 0) {
397 	    /*
398 	     * If the refcount is not zero, this style will NOT be
399 	     * destroyed.  The real destroy will be triggered if all
400 	     * DItems associated with this style is destroyed (in the
401 	     * ListDelete() function).
402 	     *
403 	     * If a widget is destroyed, it is the responsibility of the
404 	     * widget writer to delete all DItems associated with this
405 	     * widget. We can discover memory leak if the widget is
406 	     * destroyed but some default styles associated with it still
407 	     * exist
408 	     */
409 	    return;
410 	}
411     } else {
412 	stylePtr->base.refCount = 0;
413     }
414 
415     Tcl_DeleteHashTable(&stylePtr->base.items);
416     ckfree((char*)stylePtr->base.name);
417 
418     for (i=0; i<4; i++) {
419 	if (stylePtr->base.colors[i].backGC != None) {
420 	    Tk_FreeGC(Tk_Display(stylePtr->base.tkwin),
421                     stylePtr->base.colors[i].backGC);
422 	}
423 	if (stylePtr->base.colors[i].foreGC != None) {
424 	    Tk_FreeGC(Tk_Display(stylePtr->base.tkwin),
425                     stylePtr->base.colors[i].foreGC);
426 	}
427 	if (stylePtr->base.colors[i].anchorGC != None) {
428 	    Tk_FreeGC(Tk_Display(stylePtr->base.tkwin),
429                       stylePtr->base.colors[i].anchorGC);
430 	}
431     }
432 
433     stylePtr->base.diTypePtr->styleFreeProc(stylePtr);
434 }
435 
436 static void
StyleCmdDeletedProc(clientData)437 StyleCmdDeletedProc(clientData)
438     ClientData clientData;
439 {
440     Tix_DItemStyle * stylePtr = (Tix_DItemStyle *)clientData;
441 
442     stylePtr->base.styleCmd = NULL;
443     if (stylePtr->base.flags & TIX_STYLE_DEFAULT) {
444 	/*
445 	 * Don't do anything
446 	 * ToDo: maybe should give a background warning:
447 	 */
448     } else {
449 	DeleteStyle(stylePtr);
450     }
451 }
452 
453 static void
DeleteStyle(stylePtr)454 DeleteStyle(stylePtr)
455     Tix_DItemStyle * stylePtr;
456 {
457     Tcl_HashEntry * hashPtr;
458 
459     if (!(stylePtr->base.flags & TIX_STYLE_DELETED)) {
460 	stylePtr->base.flags |= TIX_STYLE_DELETED;
461 
462         /*
463          * Delete the Tcl command of this style
464          */
465 
466 	if (stylePtr->base.styleCmd != NULL) {
467 	    Tcl_DeleteCommand(stylePtr->base.interp,
468 	            Tcl_GetCommandName(stylePtr->base.interp,
469 	            stylePtr->base.styleCmd));
470 	}
471 
472         /*
473          * Remove info about this style from the hash table.
474          */
475 
476 	hashPtr = Tcl_FindHashEntry(GetStyleTable(stylePtr->base.interp),
477                 stylePtr->base.name);
478 	if (hashPtr != NULL) {
479 	    Tcl_DeleteHashEntry(hashPtr);
480 	}
481 	ListDeleteAll(stylePtr);
482 
483         /*
484          * Make sure the event handler is removed. Otherwise we'd crash
485          * when the interpreter exits.
486          */
487 
488         Tk_DeleteEventHandler(stylePtr->base.tkwin, StructureNotifyMask,
489 	        RefWindowStructureProc, (ClientData)stylePtr);
490 
491         /*
492          * Schedule it to be free'd
493          */
494 
495 	Tk_EventuallyFree((ClientData)stylePtr, (Tix_FreeProc *)StyleDestroy);
496     }
497 }
498 
499 /*
500  *----------------------------------------------------------------------
501  * FindDefaultStyle --
502  *
503  *	Return the default style of the given type of ditem for the
504  *	given tkwin, if such a default style exists.
505  *
506  * Results:
507  *	Pointer to the default style or NULL.
508  *
509  * Side effects:
510  *	None.
511  *----------------------------------------------------------------------
512  */
513 
514 static Tix_DItemStyle*
FindDefaultStyle(diTypePtr,tkwin)515 FindDefaultStyle(diTypePtr, tkwin)
516     Tix_DItemInfo * diTypePtr;
517     Tk_Window tkwin;
518 {
519     Tcl_HashEntry *hashPtr;
520     StyleInfo * infoPtr;
521     StyleLink * linkPtr;
522 
523     if ((hashPtr=Tcl_FindHashEntry(&defaultTable, (char*)tkwin)) == NULL) {
524 	return NULL;
525     }
526     infoPtr = (StyleInfo *)Tcl_GetHashValue(hashPtr);
527     for (linkPtr = infoPtr->linkHead; linkPtr; linkPtr=linkPtr->next) {
528 	if (linkPtr->diTypePtr == diTypePtr) {
529 	    return linkPtr->stylePtr;
530 	}
531     }
532     return NULL;
533 }
534 
SetDefaultStyle(diTypePtr,tkwin,stylePtr)535 static void SetDefaultStyle(diTypePtr, tkwin, stylePtr)
536     Tix_DItemInfo * diTypePtr;
537     Tk_Window tkwin;
538     Tix_DItemStyle * stylePtr;
539 {
540     Tcl_HashEntry *hashPtr;
541     StyleInfo * infoPtr;
542     StyleLink * newPtr;
543     int isNew;
544 
545     newPtr = (StyleLink *)ckalloc(sizeof(StyleLink));
546     newPtr->diTypePtr = diTypePtr;
547     newPtr->stylePtr  = stylePtr;
548 
549     hashPtr = Tcl_CreateHashEntry(&defaultTable, (char*)tkwin, &isNew);
550 
551     if (!isNew) {
552 	infoPtr = (StyleInfo *)Tcl_GetHashValue(hashPtr);
553 	if (infoPtr->tmplPtr) {
554 	    if (diTypePtr->styleSetTemplateProc != NULL) {
555 		diTypePtr->styleSetTemplateProc(stylePtr,
556 		    infoPtr->tmplPtr);
557 	    }
558 	}
559     } else {
560 	infoPtr = (StyleInfo *)ckalloc(sizeof(StyleInfo));
561 	infoPtr->linkHead = NULL;
562 	infoPtr->tmplPtr  = NULL;
563 
564 	Tk_CreateEventHandler(tkwin, StructureNotifyMask,
565 	    DefWindowStructureProc, (ClientData)tkwin);
566 	Tcl_SetHashValue(hashPtr, (char*)infoPtr);
567     }
568     newPtr->next = infoPtr->linkHead;
569     infoPtr->linkHead = newPtr;
570 }
571 
572 /*
573  *----------------------------------------------------------------------
574  * TixGetDefaultDItemStyle --
575  *
576  *	Gets the default style for an item if the application doesn't
577  *	explicitly give it an style with the -style switch.
578  *
579  * Results:
580  *	The default style.
581  *
582  * Side effects:
583  *
584  *----------------------------------------------------------------------
585  */
586 
587 Tix_DItemStyle*
TixGetDefaultDItemStyle(ddPtr,diTypePtr,iPtr,oldStylePtr)588 TixGetDefaultDItemStyle(ddPtr, diTypePtr, iPtr, oldStylePtr)
589     Tix_DispData * ddPtr;		/* Info about the display. */
590     Tix_DItemInfo * diTypePtr;		/* Info about the DItem type. */
591     Tix_DItem *iPtr;			/* Get default style for this DItem. */
592     Tix_DItemStyle* oldStylePtr;	/* ?? */
593 {
594     Tcl_DString dString;
595     Tix_DItemStyle* stylePtr;
596     int isNew;
597 
598     stylePtr = FindDefaultStyle(diTypePtr, ddPtr->tkwin);
599     if (stylePtr == NULL) {
600 	/*
601 	 * Format default name for this style+window
602 	 */
603 	Tcl_DStringInit(&dString);
604 	Tcl_DStringAppend(&dString, "style", 5);
605 	Tcl_DStringAppend(&dString, Tk_PathName(ddPtr->tkwin),
606 		(int) strlen(Tk_PathName(ddPtr->tkwin)));
607 	Tcl_DStringAppend(&dString, ":", 1);
608 	Tcl_DStringAppend(&dString, diTypePtr->name,
609 		(int) strlen(diTypePtr->name));
610 
611 	/*
612 	 * Create the new style
613 	 */
614 	stylePtr = GetDItemStyle(ddPtr, diTypePtr, dString.string, &isNew);
615 	if (isNew) {
616 	    diTypePtr->styleConfigureProc(stylePtr, 0, NULL, 0);
617 	    stylePtr->base.flags |= TIX_STYLE_DEFAULT;
618 	}
619 
620 	SetDefaultStyle(diTypePtr, ddPtr->tkwin, stylePtr);
621 	Tcl_DStringFree(&dString);
622     }
623 
624     if (oldStylePtr) {
625 	ListDelete(oldStylePtr, iPtr);
626     }
627     ListAdd(stylePtr, iPtr);
628 
629     return stylePtr;
630 }
631 
Tix_SetDefaultStyleTemplate(tkwin,tmplPtr)632 void Tix_SetDefaultStyleTemplate(tkwin, tmplPtr)
633     Tk_Window tkwin;
634     Tix_StyleTemplate * tmplPtr;
635 {
636     Tcl_HashEntry * hashPtr;
637     StyleInfo * infoPtr;
638     StyleLink * linkPtr;
639     int isNew;
640 
641     hashPtr=Tcl_CreateHashEntry(&defaultTable, (char*)tkwin, &isNew);
642     if (!isNew) {
643 	infoPtr = (StyleInfo *)Tcl_GetHashValue(hashPtr);
644 	infoPtr->tmplPtr = &infoPtr->tmpl;
645 	infoPtr->tmpl = *tmplPtr;
646 
647 	for (linkPtr = infoPtr->linkHead; linkPtr; linkPtr=linkPtr->next) {
648 	    if (linkPtr->diTypePtr->styleSetTemplateProc != NULL) {
649 		linkPtr->diTypePtr->styleSetTemplateProc(linkPtr->stylePtr,
650 		    tmplPtr);
651 	    }
652 	}
653     } else {
654 	infoPtr = (StyleInfo *)ckalloc(sizeof(StyleInfo));
655 	infoPtr->linkHead = NULL;
656 	infoPtr->tmplPtr = &infoPtr->tmpl;
657 	infoPtr->tmpl = *tmplPtr;
658 
659 	Tk_CreateEventHandler(tkwin, StructureNotifyMask,
660 	    DefWindowStructureProc, (ClientData)tkwin);
661 	Tcl_SetHashValue(hashPtr, (char*)infoPtr);
662     }
663 }
664 
665 /*
666  *----------------------------------------------------------------------
667  * GetDItemStyle --
668  *
669  *	Returns an ItemStyle with the given name.
670  *
671  * Results:
672  *	Pointer to the given Tix_DItsmStyle.
673  *
674  * Side effects:
675  *	If the style doesn't already exist, it is allocated.
676  *----------------------------------------------------------------------
677  */
678 
679 static Tix_DItemStyle*
GetDItemStyle(ddPtr,diTypePtr,styleName,isNew_ret)680 GetDItemStyle(ddPtr, diTypePtr, styleName, isNew_ret)
681     Tix_DispData * ddPtr;
682     Tix_DItemInfo * diTypePtr;
683     CONST84 char * styleName;
684     int * isNew_ret;
685 {
686     Tcl_HashEntry *hashPtr;
687     int isNew, i;
688     Tix_DItemStyle * stylePtr;
689 
690     hashPtr = Tcl_CreateHashEntry(GetStyleTable(ddPtr->interp),
691             styleName, &isNew);
692     if (!isNew) {
693 	stylePtr = (Tix_DItemStyle *)Tcl_GetHashValue(hashPtr);
694     } else {
695 	stylePtr = diTypePtr->styleCreateProc(ddPtr->interp,
696 	        ddPtr->tkwin, diTypePtr, (char *) styleName);
697 	stylePtr->base.styleCmd = Tcl_CreateCommand(ddPtr->interp,
698 	        styleName, StyleCmd, (ClientData)stylePtr,
699                 StyleCmdDeletedProc);
700 	stylePtr->base.interp 	 = ddPtr->interp;
701 	stylePtr->base.tkwin  	 = ddPtr->tkwin;
702 	stylePtr->base.diTypePtr = diTypePtr;
703 	stylePtr->base.name      = tixStrDup(styleName);
704 	stylePtr->base.pad[0] 	 = 0;
705 	stylePtr->base.pad[1] 	 = 0;
706 	stylePtr->base.anchor 	 = TK_ANCHOR_CENTER;
707 	stylePtr->base.refCount  = 0;
708 	stylePtr->base.flags     = 0;
709         for (i=0; i<4; i++) {
710             stylePtr->base.colors[i].bg = NULL;
711             stylePtr->base.colors[i].fg = NULL;
712             stylePtr->base.colors[i].backGC= None;
713             stylePtr->base.colors[i].foreGC = None;
714             stylePtr->base.colors[i].anchorGC = None;
715         }
716 
717 	Tcl_InitHashTable(&stylePtr->base.items, TCL_ONE_WORD_KEYS);
718 
719 	Tcl_SetHashValue(hashPtr, (char*)stylePtr);
720     }
721 
722     if (isNew_ret != NULL) {
723 	* isNew_ret = isNew;
724     }
725 
726     return stylePtr;
727 }
728 
729 static Tix_DItemStyle *
FindStyle(styleName,interp)730 FindStyle(styleName, interp)
731     CONST84 char *styleName;
732     Tcl_Interp *interp;		/* Current interpreter. */
733 {
734     Tix_DItemStyle* retval = NULL;
735     Tcl_HashEntry *hashPtr;
736 
737     if ((hashPtr=Tcl_FindHashEntry(GetStyleTable(interp), styleName))!= NULL) {
738         retval = (Tix_DItemStyle *)Tcl_GetHashValue(hashPtr);
739     }
740 
741     return retval;
742 }
743 
744 /*----------------------------------------------------------------------
745  * TixDItemStyleChanged --
746  *
747  *	Tell each Ditem that are affected by this style that the style
748  *	has changed. The Ditems will respond by updating their
749  *	attributes according to the new values of the style.
750  *----------------------------------------------------------------------
751  */
752 
753 void
TixDItemStyleChanged(diTypePtr,stylePtr)754 TixDItemStyleChanged(diTypePtr, stylePtr)
755     Tix_DItemInfo * diTypePtr;
756     Tix_DItemStyle * stylePtr;
757 {
758     Tcl_HashSearch hashSearch;
759     Tcl_HashEntry *hashPtr;
760     Tix_DItem * iPtr;
761 
762     for (hashPtr = Tcl_FirstHashEntry(&stylePtr->base.items, &hashSearch);
763 	 hashPtr;
764 	 hashPtr = Tcl_NextHashEntry(&hashSearch)) {
765 
766 	iPtr = (Tix_DItem *)Tcl_GetHashValue(hashPtr);
767 	diTypePtr->styleChangedProc(iPtr);
768     }
769 }
770 
771 /*----------------------------------------------------------------------
772  * ListAdd --
773  *
774  *	Add an item to the list of items affected by a style.
775  *----------------------------------------------------------------------
776  */
777 
778 static void
ListAdd(stylePtr,iPtr)779 ListAdd(stylePtr, iPtr)
780     Tix_DItemStyle * stylePtr;
781     Tix_DItem *iPtr;
782 {
783     Tcl_HashEntry *hashPtr;
784     int isNew;
785 
786     hashPtr = Tcl_CreateHashEntry(&stylePtr->base.items, (char*)iPtr, &isNew);
787     if (!isNew) {
788 	panic("DItem is already associated with style");
789     } else {
790 	Tcl_SetHashValue(hashPtr, (char*)iPtr);
791     }
792     ++ stylePtr->base.refCount;
793 }
794 
795 static void
ListDelete(stylePtr,iPtr)796 ListDelete(stylePtr, iPtr)
797     Tix_DItemStyle * stylePtr;
798     Tix_DItem *iPtr;
799 {
800     Tcl_HashEntry *hashPtr;
801 
802     hashPtr = Tcl_FindHashEntry(&stylePtr->base.items, (char*)iPtr);
803     if (hashPtr == NULL) {
804 	panic("DItem is not associated with style");
805     }
806     Tcl_DeleteHashEntry(hashPtr);
807     stylePtr->base.refCount--;
808 
809     if ((stylePtr->base.refCount == 0) &&
810             (stylePtr->base.flags & TIX_STYLE_DELETED) &&
811 	    (stylePtr->base.flags & TIX_STYLE_DEFAULT)) {
812 	Tk_EventuallyFree((ClientData)stylePtr, (Tix_FreeProc *)StyleDestroy);
813     }
814 }
815 
816 static void
ListDeleteAll(stylePtr)817 ListDeleteAll(stylePtr)
818     Tix_DItemStyle * stylePtr;
819 {
820     Tcl_HashSearch hashSearch;
821     Tcl_HashEntry *hashPtr;
822     Tix_DItem * iPtr;
823 
824     for (hashPtr = Tcl_FirstHashEntry(&stylePtr->base.items, &hashSearch);
825 	 hashPtr;
826 	 hashPtr = Tcl_NextHashEntry(&hashSearch)) {
827 
828 	iPtr = (Tix_DItem *)Tcl_GetHashValue(hashPtr);
829 	if (stylePtr->base.diTypePtr->lostStyleProc != NULL) {
830 	    stylePtr->base.diTypePtr->lostStyleProc(iPtr);
831 	}
832 	Tcl_DeleteHashEntry(hashPtr);
833     }
834 }
835 
836 /*
837  *--------------------------------------------------------------
838  *
839  * DefWindowStructureProc --
840  *
841  *	This procedure is invoked whenever StructureNotify events
842  *	occur for a window that has some default style(s) associated with it
843  *
844  * Results:
845  *	None.
846  *
847  * Side effects:
848  *	The style(s) associated with this window will all be deleted.
849  *
850  *--------------------------------------------------------------
851  */
852 
853 static void
DefWindowStructureProc(clientData,eventPtr)854 DefWindowStructureProc(clientData, eventPtr)
855     ClientData clientData;	/* Pointer to record describing window item. */
856     XEvent *eventPtr;		/* Describes what just happened. */
857 {
858     Tk_Window tkwin = (Tk_Window)clientData;
859     Tcl_HashEntry *hashPtr;
860     StyleInfo * infoPtr;
861     StyleLink * linkPtr, *toFree;
862 
863     if (eventPtr->type != DestroyNotify) {
864 	return;
865     }
866 
867     if ((hashPtr=Tcl_FindHashEntry(&defaultTable, (char*)tkwin)) == NULL) {
868 	return;
869     }
870 
871     infoPtr = (StyleInfo *)Tcl_GetHashValue(hashPtr);
872     for (linkPtr = infoPtr->linkHead; linkPtr; ) {
873 	toFree = linkPtr;
874 	linkPtr=linkPtr->next;
875 
876 	DeleteStyle(toFree->stylePtr);
877 	ckfree((char*)toFree);
878     }
879 
880     ckfree((char*)infoPtr);
881     Tcl_DeleteHashEntry(hashPtr);
882 }
883 
884 /*
885  *--------------------------------------------------------------
886  *
887  * RefWindowStructureProc --
888  *
889  *	This procedure is invoked when the refwindow of a non-default
890  *	style is deleted.
891  *
892  * Results:
893  *	None.
894  *
895  * Side effects:
896  *	The style is deleted.
897  *
898  *--------------------------------------------------------------
899  */
900 
901 static void
RefWindowStructureProc(clientData,eventPtr)902 RefWindowStructureProc(clientData, eventPtr)
903     ClientData clientData;	/* Pointer to record describing window item. */
904     XEvent *eventPtr;		/* Describes what just happened. */
905 {
906     Tix_DItemStyle * stylePtr = (Tix_DItemStyle *)clientData;
907 
908     if (eventPtr->type == DestroyNotify) {
909 	/*
910 	 * If some DItems are still associated with this window, they
911 	 * will receive a "LostStyle" notification.
912 	 */
913 	DeleteStyle(stylePtr);
914     }
915 }
916 
917 /*----------------------------------------------------------------------
918  *
919  *		 The Tix Customed Config Options
920  *
921  *----------------------------------------------------------------------
922  */
923 
924 /*
925  * The global data structures to use in widget configSpecs arrays
926  *
927  * These are declared in <tix.h>
928  */
929 
930 Tk_CustomOption tixConfigItemStyle = {
931     DItemStyleParseProc, DItemStylePrintProc, 0,
932 };
933 
934 /*----------------------------------------------------------------------
935  *  DItemStyleParseProc --
936  *
937  *	Parse the text string and store the Tix_DItemStyleType information
938  *	inside the widget record.
939  *----------------------------------------------------------------------
940  */
DItemStyleParseProc(clientData,interp,tkwin,value,widRec,offset)941 static int DItemStyleParseProc(clientData, interp, tkwin, value, widRec,offset)
942     ClientData clientData;
943     Tcl_Interp *interp;
944     Tk_Window tkwin;
945     CONST84 char *value;
946     char *widRec;		/* Must point to a valid Tix_DItem struct */
947     int offset;
948 {
949     Tix_DItem       * iPtr = (Tix_DItem *)widRec;
950     Tix_DItemStyle ** ptr = (Tix_DItemStyle **)(widRec + offset);
951     Tix_DItemStyle  * oldPtr = *ptr;
952     Tix_DItemStyle  * newPtr;
953 
954     if (value == NULL || strlen(value) == 0) {
955 	/*
956 	 * User gives a NULL string -- meaning he wants the default
957 	 * style
958 	 */
959 	if (oldPtr && oldPtr->base.flags & TIX_STYLE_DEFAULT) {
960 	    /*
961 	     * This ditem is already associated with a default style. Let's
962 	     * keep it.
963 	     */
964 	    newPtr = oldPtr;
965 	} else {
966 	    if (oldPtr) {
967 		ListDelete(oldPtr, iPtr);
968 	    }
969 	    newPtr = NULL;
970 	}
971     } else {
972 	if ((newPtr = FindStyle(value, interp)) == NULL) {
973 	    goto not_found;
974 	}
975 	if (newPtr->base.flags & TIX_STYLE_DELETED) {
976 	    goto not_found;
977 	}
978 	if (newPtr->base.diTypePtr != iPtr->base.diTypePtr) {
979 	    Tcl_AppendResult(interp, "Style type mismatch ",
980 	        "Needed ", iPtr->base.diTypePtr->name, " style but got ",
981 	        newPtr->base.diTypePtr->name, " style", NULL);
982 	    return TCL_ERROR;
983 	}
984 	if (oldPtr != newPtr) {
985 	    if (oldPtr != NULL) {
986 		ListDelete(oldPtr, iPtr);
987 	    }
988 	    ListAdd(newPtr, iPtr);
989 	}
990     }
991 
992     *ptr = newPtr;
993     return TCL_OK;
994 
995 not_found:
996     Tcl_AppendResult(interp, "Display style \"", value,
997 	"\" not found", NULL);
998     return TCL_ERROR;
999 }
1000 
DItemStylePrintProc(clientData,tkwin,widRec,offset,freeProcPtr)1001 static char *DItemStylePrintProc(clientData, tkwin, widRec,offset, freeProcPtr)
1002     ClientData clientData;
1003     Tk_Window tkwin;
1004     char *widRec;
1005     int offset;
1006     Tcl_FreeProc **freeProcPtr;
1007 {
1008     Tix_DItemStyle *stylePtr = *((Tix_DItemStyle**)(widRec+offset));
1009 
1010     if (stylePtr != NULL) {
1011 	return stylePtr->base.name;
1012     } else {
1013 	return 0;
1014     }
1015 }
1016 
1017 void
TixDItemStyleConfigureGCs(style)1018 TixDItemStyleConfigureGCs(style)
1019     Tix_DItemStyle *style;
1020 {
1021     TixColorStyle * stylePtr = (TixColorStyle *)style;
1022     XGCValues gcValues;
1023     GC newGC;
1024     int i;
1025 
1026     gcValues.graphics_exposures = False;
1027     for (i=0; i<4; i++) {
1028 	/*
1029          * Foreground GC
1030          */
1031 
1032 	gcValues.background = stylePtr->colors[i].bg->pixel;
1033 	gcValues.foreground = stylePtr->colors[i].fg->pixel;
1034 	newGC = Tk_GetGC(stylePtr->tkwin,
1035 	    GCForeground|GCBackground|GCGraphicsExposures, &gcValues);
1036 
1037 	if (stylePtr->colors[i].foreGC != None) {
1038 	    Tk_FreeGC(Tk_Display(stylePtr->tkwin),
1039 		stylePtr->colors[i].foreGC);
1040 	}
1041 	stylePtr->colors[i].foreGC = newGC;
1042 
1043 	/*
1044          * Background GC
1045          */
1046 
1047 	gcValues.foreground = stylePtr->colors[i].bg->pixel;
1048 	newGC = Tk_GetGC(stylePtr->tkwin,
1049 	    GCForeground|GCGraphicsExposures, &gcValues);
1050 
1051 	if (stylePtr->colors[i].backGC != None) {
1052 	    Tk_FreeGC(Tk_Display(stylePtr->tkwin),
1053 		stylePtr->colors[i].backGC);
1054 	}
1055 	stylePtr->colors[i].backGC = newGC;
1056 
1057         /*
1058          * Anchor GC
1059          */
1060 
1061         newGC = Tix_GetAnchorGC(stylePtr->tkwin,
1062                 stylePtr->colors[i].bg);
1063 
1064 	if (stylePtr->colors[i].anchorGC != None) {
1065 	    Tk_FreeGC(Tk_Display(stylePtr->tkwin),
1066 		stylePtr->colors[i].anchorGC);
1067 	}
1068 	stylePtr->colors[i].anchorGC = newGC;
1069     }
1070 }
1071