1 /* DBWelement.c -
2  *
3  *
4  *	This file provides a standard set of procedures for Magic
5  *	commands to use to handle drawing of various elements on
6  *	top of the layout.  Elements can be lines, rectangles,
7  *	and text (and hopefully will be expanded to include polygons
8  *	and arcs).  These operate very similarly to feedback regions,
9  *	in that they are persistant until destroyed, and do not
10  *	interact with the layout in any way.
11  *
12  * Copyright (C) 2003 Open Circuit Design, Inc., for MultiGiG, Ltd.
13  */
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <math.h>
18 
19 #include "tcltk/tclmagic.h"
20 #include "utils/magic.h"
21 #include "utils/geometry.h"
22 #include "tiles/tile.h"
23 #include "utils/hash.h"
24 #include "database/database.h"
25 #include "windows/windows.h"
26 #include "graphics/graphics.h"
27 #include "dbwind/dbwind.h"
28 #include "utils/utils.h"
29 #include "utils/styles.h"
30 #include "utils/malloc.h"
31 #include "utils/signals.h"
32 
33 /* Types of elements */
34 
35 #define ELEMENT_RECT	0
36 #define ELEMENT_LINE	1
37 #define ELEMENT_TEXT	2
38 
39 /* Linked list definition for styles */
40 
41 typedef struct _style *styleptr;
42 
43 typedef struct _style
44 {
45     int style;
46     styleptr next;
47 } stylestruct;
48 
49 /* Each element is stored in a record that looks like this: */
50 
51 typedef struct _element
52 {
53     int type;			/* Type of element (see list above) */
54     unsigned char flags;	/* Special flags (depends on element type) */
55     CellDef *rootDef;		/* Root definition of windows in which to
56 				 * display this element.
57 				 */
58     stylestruct *stylelist;	/* Linked list of display styles with which
59 				 * to paint this element.  There must be at
60 				 * least one style, so first one is hardwired.
61 				 */
62     Rect area;			/* The area of a rectangle element,
63 				 * or the two points of a line element,
64 				 * or the area box of a text element.
65 				 */
66     char *text;			/* The text of a text element, or NULL. */
67 
68 } DBWElement;
69 
70 /* The following stuff describes all the feedback information we know
71  * about.  The feedback is stored in a big array that grows whenever
72  * necessary.
73  */
74 
75 HashTable elementTable;			/* Hash table holding elements info */
76 
77 static CellDef *dbwelemRootDef;		/* To pass root cell definition from
78 					 * dbwelemGetTransform back up to
79 					 * DBWElementAdd.
80 					 */
81 
82 /*
83  * ----------------------------------------------------------------------------
84  *
85  * AppendString ---
86  *
87  *	Support function for DBWPrintElements().  Allocates memory for and
88  *	adds string "newstr" to the string pointed to by "oldstr".  If
89  *	"postfix" is non-NULL, this is also appended to "oldstr".
90  *
91  * Results:
92  *	None.
93  *
94  * Side effects:
95  *	Memory reallocation of *oldstr.
96  *
97  * ----------------------------------------------------------------------------
98  */
99 
100 void
AppendString(oldstr,newstr,postfix)101 AppendString(oldstr, newstr, postfix)
102     char **oldstr;
103     char *newstr;
104     char *postfix;
105 {
106     char *tmpstr;
107     int olen = 0;
108     int nlen = strlen(newstr);
109     int plen = 0;
110 
111     if (*oldstr != NULL) olen = strlen(*oldstr);
112     if (postfix != NULL) plen = strlen(postfix);
113     tmpstr = (char *)mallocMagic(olen + nlen + plen + 1);
114     if (*oldstr == NULL)
115 	strcpy(tmpstr, newstr);
116     else
117     {
118 	strcpy(tmpstr, *oldstr);
119 	strcat(tmpstr, newstr);
120 	freeMagic(*oldstr);
121     }
122     if (postfix != NULL) strcat(tmpstr, postfix);
123     *oldstr = tmpstr;
124 }
125 
126 /*
127  * ----------------------------------------------------------------------------
128  *
129  * AppendFlag --
130  *
131  *	Support function for DBWPrintElements to assist in generating
132  *	the string of comma-separated flags for each element printed
133  *	to the output.
134  *
135  * Results:
136  *	None.
137  *
138  * Side Effects:
139  *	Space is allocated and text added to "rstr".  The pointer to
140  *	boolean "flagset" is set to TRUE, indicating that at least one
141  *	flag has been written to the output.
142  *
143  * ----------------------------------------------------------------------------
144  */
145 
AppendFlag(char ** rstr,bool * flagset,char * fname)146 void AppendFlag(char **rstr, bool *flagset, char *fname)
147 {
148     AppendString(rstr, *flagset ? "," : " ", fname);
149     *flagset = TRUE;
150 }
151 
152 /*
153  * ----------------------------------------------------------------------------
154  *
155  * DBWPrintElements --
156  *
157  *	Generate a string containing the names of all elements which are
158  *	defined in CellDef "cellDef" and have flags matching "flagmask".
159  *	Format is appropriate for writing output to a .mag file.
160  *
161  * Results:
162  *	An allocated string, or NULL if no elements were found.
163  *
164  * Side Effects:
165  *	Memory is allocated for the string and must be freed by the calling
166  *	procedure.
167  * ----------------------------------------------------------------------------
168  */
169 
170 char *
DBWPrintElements(cellDef,flagmask,reducer)171 DBWPrintElements(cellDef, flagmask, reducer)
172     CellDef *cellDef;
173     unsigned char flagmask;
174     int reducer;
175 {
176     DBWElement *elem;
177     HashSearch hs;
178     HashEntry *he;
179     char *rstr = NULL;	/* allocated return string */
180     char istr[10];
181     styleptr sptr;
182     bool flagset;
183 
184     /* These must match the order of text sizes in graphics/graphics.h */
185     static char *textSizes[] = {"small", "medium", "large", "xlarge",
186 	"default", NULL};
187 
188     /* These must match the order of element type definitions above! */
189     char *etypes[] = {"rectangle", "line", "text"};
190 
191     HashStartSearch(&hs);
192     while (he = HashNext(&elementTable, &hs))
193     {
194 	if (elem = (DBWElement *)HashGetValue(he))
195 	{
196 	    if ((elem->rootDef == cellDef) && (elem->flags & flagmask))
197 	    {
198 		/* print element type */
199 		AppendString(&rstr, etypes[elem->type], " ");
200 		/* print element name */
201 		AppendString(&rstr, (char *)he->h_key.h_name, " ");
202 
203 		/* print dstyle(s) */
204 		for (sptr = elem->stylelist; sptr != NULL; sptr = sptr->next)
205 		    AppendString(&rstr, GrStyleTable[sptr->style].longname,
206 			((sptr->next == NULL) ? " " : ","));
207 
208 		/* print start point */
209 		sprintf(istr, "%d", elem->area.r_xbot / reducer);
210 		AppendString(&rstr, istr, " ");
211 		sprintf(istr, "%d", elem->area.r_ybot / reducer);
212 		AppendString(&rstr, istr, " ");
213 		switch (elem->type)
214 		{
215 		    case ELEMENT_RECT:
216 		        /* end point */
217 			sprintf(istr, "%d", elem->area.r_xtop / reducer);
218 			AppendString(&rstr, istr, " ");
219 			sprintf(istr, "%d", elem->area.r_ytop / reducer);
220 			AppendString(&rstr, istr, "\n");
221 			/* no flags to write.  Only applicable flag is  */
222 			/* temporary/persistent, and temporary elements */
223 			/* don't get written to the file.		*/
224 		        break;
225 		    case ELEMENT_LINE:
226 		        /* end point */
227 			sprintf(istr, "%d", elem->area.r_xtop / reducer);
228 			AppendString(&rstr, istr, " ");
229 			sprintf(istr, "%d", elem->area.r_ytop / reducer);
230 			AppendString(&rstr, istr, NULL);
231 			/* any non-default flags? */
232 			flagset = FALSE;
233 			if (elem->flags & DBW_ELEMENT_LINE_HALFX)
234 			    AppendFlag(&rstr, &flagset, "halfx");
235 			if (elem->flags & DBW_ELEMENT_LINE_HALFY)
236 			    AppendFlag(&rstr, &flagset, "halfy");
237 			if (elem->flags & DBW_ELEMENT_LINE_ARROWL)
238 			    AppendFlag(&rstr, &flagset, "arrowleft");
239 			if (elem->flags & DBW_ELEMENT_LINE_ARROWR)
240 			    AppendFlag(&rstr, &flagset, "arrowright");
241 			AppendString(&rstr, "\n", NULL);
242 		        break;
243 		    case ELEMENT_TEXT:
244 		        /* label text */
245 		        AppendString(&rstr, "\"", NULL);
246 		        AppendString(&rstr, elem->text, NULL);
247 		        AppendString(&rstr, "\"", NULL);
248 			/* any non-default flags? */
249 			flagset = FALSE;
250 			if (((elem->flags & DBW_ELEMENT_TEXT_POS) >> 4) !=
251 					GEO_CENTER)
252 			    AppendFlag(&rstr, &flagset, GeoPosToName((elem->flags &
253 					DBW_ELEMENT_TEXT_POS) >> 4));
254 			if (((elem->flags & DBW_ELEMENT_TEXT_SIZE) >> 1) !=
255 					GR_TEXT_MEDIUM)
256 			    AppendFlag(&rstr, &flagset, textSizes[(elem->flags &
257 					DBW_ELEMENT_TEXT_SIZE) >> 1]);
258 		        AppendString(&rstr, "\n", NULL);
259 		        break;
260 		}
261 	    }
262 	}
263     }
264     return rstr;
265 }
266 
267 /*
268  * ----------------------------------------------------------------------------
269  *
270  * DBWScaleElements --
271  *
272  *	Scale each element by the given integer numerator and denominator
273  *
274  * Results:
275  *	None.
276  *
277  * Side Effects:
278  *	Element values are modified.
279  * ----------------------------------------------------------------------------
280  */
281 
282 void
DBWScaleElements(n,d)283 DBWScaleElements(n, d)
284     int n, d;
285 {
286     DBWElement *elem;
287     HashSearch hs;
288     HashEntry *he;
289     extern bool DBScalePoint();	    /* Forward declaration */
290 
291     HashStartSearch(&hs);
292     while (he = HashNext(&elementTable, &hs))
293     {
294 	if (elem = (DBWElement *)HashGetValue(he))
295 	{
296 	    /* scale area rectangle */
297 	    DBScalePoint(&elem->area.r_ll, n, d);
298 	    DBScalePoint(&elem->area.r_ur, n, d);
299 	}
300     }
301 }
302 
303 
304 /*
305  * ----------------------------------------------------------------------------
306  *
307  * DBWElementRedraw --
308  *
309  * 	This procedure is called by the highlight manager to redisplay
310  *	feedback highlights.  The window is locked before entry.
311  *
312  * Results:
313  *	None.
314  *
315  * Side effects:
316  *	Any feedback information that overlaps a non-space tile in
317  *	plane is redrawn.
318  *
319  * Tricky stuff:
320  *	Redisplay is numerically difficult, particularly when feedbacks
321  *	have a large internal scale factor:  the tendency is to get
322  *	integer overflow and get everything goofed up.  Be careful
323  *	when making changes to the code below.
324  *
325  * ----------------------------------------------------------------------------
326  */
327 
328 void
DBWElementRedraw(window,plane)329 DBWElementRedraw(window, plane)
330     MagWindow *window;		/* Window in which to redraw. */
331     Plane *plane;		/* Non-space tiles on this plane mark what
332 				 * needs to be redrawn.
333 				 */
334 {
335     int curStyle, newStyle;
336     styleptr stylePtr;
337     CellDef *windowRoot;
338     Rect screenArea;
339     DBWElement *elem;
340     HashSearch hs;
341     HashEntry *entry;
342     Point p;
343 
344     windowRoot = ((CellUse *) (window->w_surfaceID))->cu_def;
345     curStyle = -1;
346 
347     HashStartSearch(&hs);
348     while ((entry = HashNext(&elementTable, &hs)) != NULL)
349     {
350 	elem = (DBWElement *) HashGetValue(entry);
351 	if (!elem) continue;
352 	else if (elem->rootDef != windowRoot) continue;
353 
354 	/* Transform the feedback area to screen coords.  This is
355 	 * very similar to the code in WindSurfaceToScreen, except
356 	 * that there's additional scaling involved.
357 	 */
358 
359 	WindSurfaceToScreenNoClip(window, &elem->area, &screenArea);
360 
361 	/* Deal with half-point-offset flags for the line element */
362 	if ((elem->type == ELEMENT_LINE) &&
363 		(elem->flags & (DBW_ELEMENT_LINE_HALFX | DBW_ELEMENT_LINE_HALFY)))
364 	{
365 	    static Rect unitArea = {{0, 0}, {1, 1}};
366 	    Rect transArea;
367 	    int offx, offy;
368 
369 	    WindSurfaceToScreenNoClip(window, &unitArea, &transArea);
370 	    offx = (transArea.r_xtop - transArea.r_xbot) >> 1;
371 	    offy = (transArea.r_ytop - transArea.r_ybot) >> 1;
372 
373 	    if (elem->flags & DBW_ELEMENT_LINE_HALFX)
374 	    {
375 		screenArea.r_xbot += offx;
376 		screenArea.r_xtop += offx;
377 	    }
378 	    if (elem->flags & DBW_ELEMENT_LINE_HALFY)
379 	    {
380 		screenArea.r_ybot += offy;
381 		screenArea.r_ytop += offy;
382 	    }
383 	}
384 
385 	if ((screenArea.r_xbot <= screenArea.r_xtop) &&
386 		(screenArea.r_ybot <= screenArea.r_ytop))
387 	{
388 	    for (stylePtr = elem->stylelist; stylePtr != NULL;
389 			stylePtr = stylePtr->next)
390 	    {
391 		newStyle = stylePtr->style;
392 		if (newStyle != curStyle)
393 		{
394 		    curStyle = newStyle;
395 		    GrSetStuff(curStyle);
396 		}
397 		switch (elem->type)
398 		{
399 		    case ELEMENT_RECT:
400 			GrFastBox(&screenArea);
401 			break;
402 		    case ELEMENT_TEXT:
403 			p.p_x = screenArea.r_xbot;
404 			p.p_y = screenArea.r_ybot;
405 			GrPutText(elem->text, curStyle, &p,
406 				((elem->flags & DBW_ELEMENT_TEXT_POS) >> 4),
407 				((elem->flags & DBW_ELEMENT_TEXT_SIZE) >> 1),
408 				FALSE, &(window->w_screenArea), (Rect *)NULL);
409 			break;
410 		    case ELEMENT_LINE:
411 			GrClipLine(screenArea.r_xbot, screenArea.r_ybot,
412 				screenArea.r_xtop, screenArea.r_ytop);
413 			/* Draw arrowheads on endpoints, if flags are set */
414 
415 			if (elem->flags & (DBW_ELEMENT_LINE_ARROWL |
416 					DBW_ELEMENT_LINE_ARROWR)) {
417 			    static Rect unitArea = {{0, 0}, {1, 1}};
418 			    Rect transArea;
419 			    Point polyp[4];
420 			    double theta, r;
421 			    int i, offx, offy;
422 
423 			    WindSurfaceToScreenNoClip(window, &unitArea, &transArea);
424 			    offx = (transArea.r_xtop - transArea.r_xbot) >> 1;
425 			    offy = (transArea.r_ytop - transArea.r_ybot) >> 1;
426 			    WindSurfaceToScreenNoClip(window, &elem->area, &screenArea);
427 			    if (elem->flags & DBW_ELEMENT_LINE_HALFX)
428 			    {
429                                 screenArea.r_xbot += offx;
430                                 screenArea.r_xtop += offx;
431 			    }
432 			    if (elem->flags & DBW_ELEMENT_LINE_HALFY)
433 			    {
434                                 screenArea.r_ybot += offy;
435                                 screenArea.r_ytop += offy;
436 			    }
437 			    theta = atan2((double)(screenArea.r_ytop
438 					- screenArea.r_ybot), (double)
439                                          (screenArea.r_xtop - screenArea.r_xbot));
440 			    r = (double)(transArea.r_xtop - transArea.r_xbot);
441 			    if (elem->flags & DBW_ELEMENT_LINE_ARROWL)
442 			    {
443 				for (i = 0; i < 4; i++)
444 				{
445 				    polyp[i].p_x = screenArea.r_xbot;
446 				    polyp[i].p_y = screenArea.r_ybot;
447 				}
448 				polyp[1].p_x += (int)(r * cos(theta + 0.2));
449 				polyp[1].p_y += (int)(r * sin(theta + 0.2));
450 				polyp[2].p_x += (int)(r * 0.9 * cos(theta));
451 				polyp[2].p_y += (int)(r * 0.9 * sin(theta));
452 				polyp[3].p_x += (int)(r * cos(theta - 0.2));
453 				polyp[3].p_y += (int)(r * sin(theta - 0.2));
454 				GrFillPolygon(polyp, 4);
455 			    }
456 
457 			    if (elem->flags & DBW_ELEMENT_LINE_ARROWR)
458 			    {
459 				for (i = 0; i < 4; i++)
460 				{
461 				    polyp[i].p_x = screenArea.r_xtop;
462 				    polyp[i].p_y = screenArea.r_ytop;
463 				}
464 				polyp[1].p_x -= (int)(r * cos(theta + 0.2));
465 				polyp[1].p_y -= (int)(r * sin(theta + 0.2));
466 				polyp[2].p_x -= (int)(r * 0.9 * cos(theta));
467 				polyp[2].p_y -= (int)(r * 0.9 * sin(theta));
468 				polyp[3].p_x -= (int)(r * cos(theta - 0.2));
469 				polyp[3].p_y -= (int)(r * sin(theta - 0.2));
470 				GrFillPolygon(polyp, 4);
471 			    }
472 			}
473 			break;
474 		}
475 	    }
476 	}
477     }
478 }
479 
480 /*
481  * ----------------------------------------------------------------------------
482  *
483  * DBWElementUndraw --
484  *
485  *	Paint the element in style ERASE_ALL to erase it.
486  *
487  * ----------------------------------------------------------------------------
488  */
489 
490 void
dbwElementUndraw(mw,elem)491 dbwElementUndraw(mw, elem)
492     MagWindow *mw;
493     DBWElement *elem;		/* The element to erase */
494 {
495     CellDef *windowRoot;
496     Rect screenArea, textArea;
497 
498     if (mw == NULL) return;	/* No window; can't undraw */
499     windowRoot = ((CellUse *) (mw->w_surfaceID))->cu_def;
500 
501     GrLock(mw, TRUE);
502 
503     /* Deal with half-point-offset flags for the line element	*/
504     /* by enlarging the area by 1 unit.				*/
505 
506     if ((elem->type == ELEMENT_LINE) &&
507 		(elem->flags & (DBW_ELEMENT_LINE_HALFX | DBW_ELEMENT_LINE_HALFY)))
508     {
509 	Rect newArea = elem->area;
510 
511 	if (elem->flags & DBW_ELEMENT_LINE_HALFX) newArea.r_xtop++;
512 	if (elem->flags & DBW_ELEMENT_LINE_HALFY) newArea.r_ytop++;
513 
514 	WindSurfaceToScreen(mw, &newArea, &screenArea);
515     }
516     else
517 	WindSurfaceToScreen(mw, &elem->area, &screenArea);
518 
519     /* For text, determine the size of the text and expand the	*/
520     /* screenArea rectangle accordingly.			*/
521 
522     if (elem->type == ELEMENT_TEXT)
523     {
524 	int tpos = (elem->flags & DBW_ELEMENT_TEXT_POS) >> 4;
525 	int tsize = (elem->flags & DBW_ELEMENT_TEXT_SIZE) >> 1;
526 
527 	GrLabelSize(elem->text, tpos, tsize, &textArea);
528 	screenArea.r_xbot += textArea.r_xbot;
529 	screenArea.r_ybot += textArea.r_ybot;
530 	screenArea.r_xtop += textArea.r_xtop;
531 	screenArea.r_ytop += textArea.r_ytop;
532     }
533 
534     if ((screenArea.r_xbot <= screenArea.r_xtop) &&
535 		(screenArea.r_ybot <= screenArea.r_ytop))
536     {
537 	GrSetStuff(STYLE_ERASEALL);
538 	GrFastBox(&screenArea);
539         WindAreaChanged(mw, &screenArea);
540     }
541     GrUnlock(mw, TRUE);
542 }
543 
544 
545 /*
546  * ----------------------------------------------------------------------------
547  *
548  * DBWElementDelete --
549  *
550  * 	This procedure deletes an element.
551  *
552  * Results:
553  *	None.
554  *
555  * Side effects:
556  *	An element is found in the hash table by name, and if it exists,
557  *	it is deleted.
558  *
559  * ----------------------------------------------------------------------------
560  */
561 
562 void
DBWElementDelete(MagWindow * w,char * name)563 DBWElementDelete(MagWindow *w, char *name)
564 {
565     DBWElement *elem;
566     CellDef *currentRoot;
567     HashEntry *entry;
568     styleptr stylePtr;
569 
570     entry = HashFind(&elementTable, name);
571 
572     if (entry == NULL) return;
573 
574     elem = (DBWElement *)HashGetValue(entry);
575 
576     if (elem == NULL) return;
577 
578     dbwElementUndraw(w, elem);
579 
580     /* mark element's cell as having been modified */
581     if (elem->flags & DBW_ELEMENT_PERSISTENT)
582 	elem->rootDef->cd_flags |= CDMODIFIED;
583 
584     for (stylePtr = elem->stylelist; stylePtr != NULL; stylePtr = stylePtr->next)
585     {
586 	freeMagic(stylePtr);
587     }
588     if (elem->type == ELEMENT_TEXT)
589 	freeMagic(elem->text);
590 
591     HashSetValue(entry, NULL);
592     freeMagic(elem);
593 
594     /* Area of elem->area is set to be updated by dbwElementUndraw().	*/
595     /* Can't do WindUpdate(), though, until the hash table entry is	*/
596     /* removed, or DBWdisplay will try to draw it again.		*/
597 
598     WindUpdate();
599 }
600 
601 /*
602  * Initialize the Element hash table.
603  */
604 
605 void
dbwElementInit()606 dbwElementInit()
607 {
608     HashInit(&elementTable, 10, HT_STRINGKEYS);
609     DBWHLAddClient(DBWElementRedraw);
610 }
611 
612 /*
613  * ----------------------------------------------------------------------------
614  *
615  * DBWElementNames --
616  *
617  *	Go through the hash table and print the names of all elements
618  *
619  * ----------------------------------------------------------------------------
620  */
621 
622 void
DBWElementNames()623 DBWElementNames()
624 {
625     DBWElement *elem;
626     HashSearch hs;
627     HashEntry *he;
628 
629 #ifndef MAGIC_WRAPPER
630     TxPrintf(stdout, "Known elements:");
631 #endif
632 
633     HashStartSearch(&hs);
634     while (he = HashNext(&elementTable, &hs))
635     {
636 	if (elem = (DBWElement *)HashGetValue(he))
637 	{
638 #ifdef MAGIC_WRAPPER
639 	    Tcl_AppendElement(magicinterp, he->h_key.h_name);
640 #else
641 	    TxPrintf(stdout, " %s", he->h_key.h_name);
642 #endif
643 	}
644     }
645 
646 #ifndef MAGIC_WRAPPER
647     TxPrintf(stdout, "/n");
648 #endif
649 
650 }
651 
652 /*
653  * ----------------------------------------------------------------------------
654  *
655  * DBWElementInbox --
656  *
657  *	Find the element that is nearest the box.
658  *
659  * ----------------------------------------------------------------------------
660  */
661 
662 void
DBWElementInbox(area)663 DBWElementInbox(area)
664     Rect *area;
665 {
666     DBWElement *elem;
667     HashSearch hs;
668     HashEntry *he;
669     int sqdist;
670 
671 #ifndef MAGIC_WRAPPER
672     TxPrintf(stdout, "Element(s) inside box: ");
673 #endif
674 
675     HashStartSearch(&hs);
676     while (he = HashNext(&elementTable, &hs))
677     {
678 	if (elem = (DBWElement *)HashGetValue(he))
679 	{
680 	    if (GEO_SURROUND(area, &elem->area))
681 	    {
682 #ifdef MAGIC_WRAPPER
683 		Tcl_AppendElement(magicinterp, he->h_key.h_name);
684 #else
685 		TxPrintf(stdout, " %s", he->h_key.h_name);
686 #endif
687 	    }
688 	}
689     }
690 
691 #ifndef MAGIC_WRAPPER
692     TxPrintf(stdout, "/n");
693 #endif
694 }
695 
696 
697 /*
698  * ----------------------------------------------------------------------------
699  *
700  * DBWElementAdd* --
701  *
702  * 	Adds a new element to the element hash table.
703  *
704  * Results:
705  *	None, except
706  *	DBWElementAdd(): returns Pointer to a DBWElement structure.
707  *
708  * Side effects:
709  *	CellDef's ancestors are searched until its first root definition
710  *	is found, and the coordinates of area are transformed into the
711  *	root.  Then the area is added to the element structure.
712  *	This stuff will be displayed on the screen at the end of the
713  *	current command.
714  * ----------------------------------------------------------------------------
715  */
716 
717 /* Set up everything is generic to all element types */
718 
719 DBWElement *
DBWElementAdd(w,name,area,cellDef,style)720 DBWElementAdd(w, name, area, cellDef, style)
721     MagWindow *w;
722     char *name;			/* Name of this element for the hash table */
723     Rect *area;			/* The area of the element */
724     CellDef *cellDef;		/* The cellDef in whose coordinates area
725 				 * is given.
726 				 */
727     int style;			/* An initial display style to use */
728 {
729     Transform transform;
730     DBWElement *elem;
731     HashEntry *entry;
732     extern int dbwelemGetTransform();	/* Forward declaration. */
733 
734     /* Find a transform from this cell to the root, and use it to
735      * transform the area.  If the root isn't an ancestor, just
736      * return.
737      */
738 
739     if (!DBSrRoots(cellDef, &GeoIdentityTransform,
740 		dbwelemGetTransform, (ClientData) &transform))
741 	if (w != NULL)
742 	    return NULL;
743 
744     /* SigInterruptPending screws up DBSrRoots */
745     if (SigInterruptPending)
746 	return NULL;
747 
748     /* If there is already an entry by this name, delete it */
749     DBWElementDelete(w, name);
750 
751     entry = HashFind(&elementTable, name);
752     elem = (DBWElement *)mallocMagic(sizeof(DBWElement));
753     HashSetValue(entry, elem);
754 
755     GeoCanonicalRect(area, &elem->area);
756     elem->stylelist = (styleptr)mallocMagic(sizeof(stylestruct));
757     elem->stylelist->style = style;
758     elem->stylelist->next = NULL;
759 
760     /* For .mag file loads, w will be NULL and cellDef will be	*/
761     /* the root.						*/
762     if (w == NULL)
763         elem->rootDef = cellDef;
764     else
765 	elem->rootDef = dbwelemRootDef;
766     elem->text = NULL;
767     elem->flags = 0;
768 
769     return elem;
770 }
771 
772 void
DBWElementAddRect(w,name,area,cellDef,style)773 DBWElementAddRect(w, name, area, cellDef, style)
774     MagWindow *w;
775     char *name;			/* Name of this element for the hash table */
776     Rect *area;			/* The area to be highlighted. */
777     CellDef *cellDef;		/* The cellDef in whose coordinates area
778 				 * is given.
779 				 */
780     int style;			/* An initial display style to use */
781 {
782     DBWElement *elem;
783 
784     elem = DBWElementAdd(w, name, area, cellDef, style);
785     if (elem == NULL) return;
786     elem->type = ELEMENT_RECT;
787 }
788 
789 void
DBWElementAddLine(w,name,area,cellDef,style)790 DBWElementAddLine(w, name, area, cellDef, style)
791     MagWindow *w;
792     char *name;			/* Name of this element for the hash table */
793     Rect *area;			/* The area to be highlighted. */
794     CellDef *cellDef;		/* The cellDef in whose coordinates area
795 				 * is given.
796 				 */
797     int style;			/* An initial display style to use */
798 {
799     DBWElement *elem;
800 
801     elem = DBWElementAdd(w, name, area, cellDef, style);
802     if (elem == NULL) return;
803     elem->type = ELEMENT_LINE;
804 }
805 
806 void
DBWElementAddText(w,name,x,y,text,cellDef,style)807 DBWElementAddText(w, name, x, y, text, cellDef, style)
808     MagWindow *w;
809     char *name;			/* Name of this element for the hash table */
810     int x, y;			/* Point of origin (x, y coordinates) */
811     char *text;			/* The text of the label */
812     CellDef *cellDef;		/* The cellDef in whose coordinates area
813 				 * is given.
814 				 */
815     int style;			/* An initial display style to use */
816 {
817     DBWElement *elem;
818     Rect area;
819 
820     area.r_xbot = x;
821     area.r_xtop = x;
822     area.r_ybot = y;
823     area.r_ytop = y;
824 
825     elem = DBWElementAdd(w, name, &area, cellDef, style);
826     if (elem == NULL) return;
827     elem->type = ELEMENT_TEXT;
828     elem->text = StrDup((char **)NULL, text);
829     elem->flags |= (GEO_CENTER << 4);		/* default centered */
830     elem->flags |= (GR_TEXT_MEDIUM << 1);	/* default medium size */
831 }
832 
833 /* This utility procedure is invoked by DBSrRoots.  Save the root definition
834  * in dbwRootDef, save the transform in the argument, and abort the search.
835  * Make sure that the root we pick is actually displayed in a window
836  * someplace (there could be root cells that are no longer displayed
837  * anywhere).
838  */
839 
840 int
dbwelemGetTransform(use,transform,cdarg)841 dbwelemGetTransform(use, transform, cdarg)
842     CellUse *use;			/* A root use that is an ancestor
843 					 * of cellDef in DBWElementAdd.
844 					 */
845     Transform *transform;		/* Transform up from cellDef to use. */
846     Transform *cdarg;			/* Place to store transform from
847 					 * cellDef to its root def.
848 					 */
849 {
850     extern int dbwElementAlways1();
851     if (use->cu_def->cd_flags & CDINTERNAL) return 0;
852     if (!WindSearch((ClientData) DBWclientID, (ClientData) use,
853 	    (Rect *) NULL, dbwElementAlways1, (ClientData) NULL)) return 0;
854     if (SigInterruptPending)
855 	return 0;
856     dbwelemRootDef = use->cu_def;
857     *cdarg = *transform;
858     return 1;
859 }
860 
861 int
dbwElementAlways1()862 dbwElementAlways1()
863 {
864     return 1;
865 }
866 
867 /*
868  * ----------------------------------------------------------------------------
869  *
870  * DBWElementText --
871  *
872  * 	Configures text of a text element
873  *
874  * Results:
875  *	None.
876  *
877  * Side effects:
878  *	Text element's text string is reallocated, or string is printed.
879  *	If altered, the element is erased and redrawn.
880  *
881  * ----------------------------------------------------------------------------
882  */
883 
884 void
DBWElementText(MagWindow * w,char * ename,char * text)885 DBWElementText(MagWindow *w, char *ename, char *text)
886 {
887     DBWElement *elem;
888     HashEntry *entry;
889 
890     entry = HashFind(&elementTable, ename);
891     if (entry == NULL)
892     {
893 	TxError("No such element %s\n", ename);
894 	return;
895     }
896 
897     elem = (DBWElement *)HashGetValue(entry);
898     if (elem == NULL) return;
899 
900     if (elem->type != ELEMENT_TEXT)
901     {
902 	TxError("Element %s is not a text element\n", ename);
903 	return;
904     }
905 
906     if (text == NULL)	/* get text */
907     {
908 #ifdef MAGIC_WRAPPER
909 	Tcl_SetResult(magicinterp, elem->text, NULL);
910 #else
911 	TxPrintf("%s\n", elem->text);
912 #endif
913     }
914     else  /* replace text */
915     {
916 	dbwElementUndraw(w, elem);
917 	freeMagic(elem->text);
918 	elem->text = StrDup((char **)NULL, text);
919     }
920 }
921 
922 /*
923  * ----------------------------------------------------------------------------
924  *
925  * DBWElementParseFlags --
926  *
927  * 	Configures flags of any element
928  *
929  * Results:
930  *	None.
931  *
932  * Side effects:
933  *	Element's flags are set.
934  *	If altered, the element is erased and redrawn.
935  *
936  * ----------------------------------------------------------------------------
937  */
938 
939 void
DBWElementParseFlags(MagWindow * w,char * ename,char * flagstr)940 DBWElementParseFlags(MagWindow *w, char *ename, char *flagstr)
941 {
942     DBWElement *elem;
943     HashEntry *entry;
944     int flidx, newflags;
945 
946     static char *lineOffset[] = {"halfx", "halfy", "exactx", "exacty",
947 		"arrowleft", "arrowbottom", "arrowright", "arrowtop",
948 		"plainleft", "plainbottom", "plainright", "plaintop", NULL};
949     static char *textSizes[] = {"small", "medium", "large", "xlarge",
950 	"default", NULL};
951     static char *genFlags[] = {"persistent", "temporary", NULL};
952 
953     entry = HashFind(&elementTable, ename);
954     if (entry == NULL)
955     {
956 	TxError("No such element %s\n", ename);
957 	return;
958     }
959 
960     elem = (DBWElement *)HashGetValue(entry);
961     if (elem == NULL) return;
962     newflags = elem->flags;
963 
964     /* Return list of known flags */
965     if (flagstr == NULL)
966     {
967 #ifdef MAGIC_WRAPPER
968 	Tcl_AppendElement(magicinterp, "(flags)");
969 #else
970 	TxPrintf("%s\n", "(flags)");
971 #endif
972     }
973     else
974     {
975 	/* Parse string for known flags */
976 	flidx = Lookup(flagstr, genFlags);
977 	switch (flidx)
978 	{
979 	    case 0:
980 		newflags |= DBW_ELEMENT_PERSISTENT;
981 		break;
982 	    case 1:
983 		newflags &= ~DBW_ELEMENT_PERSISTENT;
984 		break;
985 	    default:
986 		switch (elem->type)
987 		{
988 		    case ELEMENT_TEXT:
989 			flidx = Lookup(flagstr, textSizes);
990 			if (flidx >= 0)
991 			{
992 			    newflags &= ~DBW_ELEMENT_TEXT_SIZE;
993 			    newflags |= (flidx << 1) & DBW_ELEMENT_TEXT_SIZE;
994 			}
995 			else
996 			{
997 			    flidx = GeoNameToPos(flagstr, FALSE, FALSE);
998 			    if (flidx >= 0)
999 			    {
1000 				newflags &= ~DBW_ELEMENT_TEXT_POS;
1001 				newflags |= (flidx << 4) & DBW_ELEMENT_TEXT_POS;
1002 			    }
1003 			    else
1004 				TxError("No such text element flag \"%s\"\n",
1005 					flagstr);
1006 			}
1007 			break;
1008 		    case ELEMENT_LINE:
1009 			flidx = Lookup(flagstr, lineOffset);
1010 			switch(flidx)
1011 			{
1012 			    case 0:
1013 				newflags |= DBW_ELEMENT_LINE_HALFX;
1014 				break;
1015 			    case 1:
1016 				newflags |= DBW_ELEMENT_LINE_HALFY;
1017 				break;
1018 			    case 2:
1019 				newflags &= ~(DBW_ELEMENT_LINE_HALFX);
1020 				break;
1021 			    case 3:
1022 				newflags &= ~(DBW_ELEMENT_LINE_HALFY);
1023 				break;
1024 			    case 4:
1025 			    case 5:
1026 				newflags |= DBW_ELEMENT_LINE_ARROWL;
1027 				break;
1028 			    case 6:
1029 			    case 7:
1030 				newflags |= DBW_ELEMENT_LINE_ARROWR;
1031 				break;
1032 			    case 8:
1033 			    case 9:
1034 				newflags &= ~(DBW_ELEMENT_LINE_ARROWL);
1035 				break;
1036 			    case 10:
1037 			    case 11:
1038 				newflags &= ~(DBW_ELEMENT_LINE_ARROWR);
1039 				break;
1040 			    default:
1041 				TxError("No such line element flag \"%s\"\n",
1042 					flagstr);
1043 				break;
1044 			}
1045 			break;
1046 		    case ELEMENT_RECT:
1047 			TxError("No such rect element flag \"%s\"\n",
1048 					flagstr);
1049 			break;
1050 		}
1051 		break;
1052 	}
1053 
1054 	if (newflags != elem->flags)
1055 	{
1056 	    dbwElementUndraw(w, elem);
1057 
1058 	    /* Mark element's cell as having been modified either if	*/
1059 	    /* it is persistent or if its persistence has changed.	*/
1060 
1061 	    if (elem->flags & DBW_ELEMENT_PERSISTENT ||
1062 			newflags & DBW_ELEMENT_PERSISTENT)
1063 		elem->rootDef->cd_flags |= CDMODIFIED;
1064 
1065 	    elem->flags = newflags;
1066 	}
1067     }
1068 }
1069 
1070 /*
1071  * ----------------------------------------------------------------------------
1072  *
1073  * DBWElementStyle --
1074  *
1075  * 	Configures style of any element
1076  *
1077  * Results:
1078  *	None.
1079  *
1080  * Side effects:
1081  *	Element's style list is reallocated, or printed.
1082  *	If altered, the element is erased and redrawn.
1083  *
1084  * ----------------------------------------------------------------------------
1085  */
1086 
1087 void
DBWElementStyle(MagWindow * w,char * ename,int style,bool add)1088 DBWElementStyle(MagWindow *w, char *ename, int style, bool add)
1089 {
1090     DBWElement *elem;
1091     HashEntry *entry;
1092     styleptr sptr, newstyle;
1093 
1094     entry = HashFind(&elementTable, ename);
1095     if (entry == NULL)
1096     {
1097 	TxError("No such element %s\n", ename);
1098 	return;
1099     }
1100 
1101     elem = (DBWElement *)HashGetValue(entry);
1102     if (elem == NULL) return;
1103 
1104     if (style== -1)	/* get style(s) */
1105     {
1106 #ifdef MAGIC_WRAPPER
1107 	for (sptr = elem->stylelist; sptr != NULL; sptr = sptr->next)
1108 	{
1109 	    Tcl_AppendElement(magicinterp, GrStyleTable[sptr->style].longname);
1110 	}
1111 #else
1112 	for (sptr = elem->stylelist; sptr != NULL; sptr = sptr->next)
1113 	{
1114 	    TxPrintf("%s ", GrStyleTable[sptr->style].longname);
1115 	}
1116 	TxPrintf("\n");
1117 #endif
1118     }
1119     else
1120     {
1121 	dbwElementUndraw(w, elem);
1122 	if (add == TRUE)
1123 	{
1124 	    /* add style */
1125 	    for (sptr = elem->stylelist; sptr != NULL && sptr->next != NULL;
1126 			sptr = sptr->next);
1127 
1128 	    newstyle = (styleptr)mallocMagic(sizeof(stylestruct));
1129 	    newstyle->style = style;
1130 	    newstyle->next = NULL;
1131 	    if (sptr == NULL)
1132 		elem->stylelist = newstyle;
1133 	    else
1134 		sptr->next = newstyle;
1135 	}
1136 	else
1137 	{
1138 	    /* find style in list */
1139 	    for (sptr = elem->stylelist; sptr != NULL; sptr = sptr->next)
1140 	    {
1141 		if (sptr->next != NULL)
1142 		    if (sptr->next->style == style)
1143 			break;
1144 	    }
1145 
1146 	    /* delete style (if it is in the list) */
1147 
1148 	    if ((sptr == NULL) && (elem->stylelist != NULL) &&
1149 			(elem->stylelist->style == style))
1150 	    {
1151 		dbwElementUndraw(w, elem);
1152 		freeMagic(elem->stylelist);
1153 		elem->stylelist = elem->stylelist->next;
1154 		if (elem->stylelist == NULL)
1155 		    TxPrintf("Warning:  Element %s has no styles!\n", ename);
1156 	    }
1157 	    else if (sptr == NULL)
1158 	    {
1159 		TxError("Style %d is not in the style list for element %s\n",
1160 			style, ename);
1161 	    }
1162 	    else if (sptr->next != NULL)
1163 	    {
1164 		dbwElementUndraw(w, elem);
1165 		freeMagic(sptr->next);
1166 		sptr->next = sptr->next->next;
1167 	    }
1168 	}
1169 	/* mark element's cell as having been modified */
1170 	if (elem->flags & DBW_ELEMENT_PERSISTENT)
1171 	    elem->rootDef->cd_flags |= CDMODIFIED;
1172     }
1173 }
1174 
1175 /*
1176  * ----------------------------------------------------------------------------
1177  *
1178  * DBWElementPos --
1179  *
1180  * 	Configures position of a rect or line element
1181  *
1182  * Results:
1183  *	None.
1184  *
1185  * Side effects:
1186  *	Rect or line element's rect structure is altered, or else the
1187  *	position is printed.  If altered, the element is erased and redrawn.
1188  *
1189  * ----------------------------------------------------------------------------
1190  */
1191 
1192 void
DBWElementPos(MagWindow * w,char * ename,Rect * crect)1193 DBWElementPos(MagWindow *w, char *ename, Rect *crect)
1194 {
1195     DBWElement *elem;
1196     HashEntry *entry;
1197     Rect prect;
1198     char ptemp[22];
1199 
1200     entry = HashFind(&elementTable, ename);
1201     if (entry == NULL)
1202     {
1203 	TxError("No such element %s\n", ename);
1204 	return;
1205     }
1206 
1207     elem = (DBWElement *)HashGetValue(entry);
1208     if (elem == NULL) return;
1209 
1210     if (crect == NULL)	/* Get position */
1211     {
1212 #ifdef MAGIC_WRAPPER
1213 	snprintf(ptemp, 20, "%d", elem->area.r_xbot);
1214 	Tcl_AppendElement(magicinterp, ptemp);
1215 	snprintf(ptemp, 20, "%d", elem->area.r_ybot);
1216 	Tcl_AppendElement(magicinterp, ptemp);
1217 	if (elem->type == ELEMENT_RECT || elem->type == ELEMENT_LINE)
1218 	{
1219 	    snprintf(ptemp, 20, "%d", elem->area.r_xtop);
1220 	    Tcl_AppendElement(magicinterp, ptemp);
1221 	    snprintf(ptemp, 20, "%d", elem->area.r_ytop);
1222 	    Tcl_AppendElement(magicinterp, ptemp);
1223 	}
1224 #else
1225 	TxPrintf("(%d, %d)", elem->area.r_xbot, elem->area.r_ybot);
1226 
1227 	if (elem->type == ELEMENT_RECT || elem->type == ELEMENT_LINE)
1228 	    TxPrintf(" to (%d, %d)", elem->area.r_xtop, elem->area.r_ytop);
1229 
1230 	TxPrintf("\n");
1231 #endif
1232     }
1233     else		/* Change position */
1234     {
1235 	dbwElementUndraw(w, elem);
1236 	elem->area = *crect;
1237 	/* mark element's cell as having been modified */
1238 	if (elem->flags & DBW_ELEMENT_PERSISTENT)
1239 	    elem->rootDef->cd_flags |= CDMODIFIED;
1240     }
1241 }
1242 
1243 /*
1244  * ----------------------------------------------------------------------------
1245  *
1246  * DBWElementClearDef --
1247  *
1248  * 	Removes all elements associated with the given CellDef.
1249  *
1250  * Results:
1251  *	None.
1252  *
1253  * Side effects:
1254  *	Elements are removed from the element hash.
1255  *
1256  * ----------------------------------------------------------------------------
1257  */
1258 
1259 void
DBWElementClearDef(cellDef)1260 DBWElementClearDef(cellDef)
1261     CellDef *cellDef;
1262 {
1263     DBWElement *elem;
1264     HashEntry *entry;
1265     styleptr stylePtr;
1266     HashSearch hs;
1267 
1268     HashStartSearch(&hs);
1269     while ((entry = HashNext(&elementTable, &hs)) != NULL)
1270     {
1271 	elem = (DBWElement *) HashGetValue(entry);
1272 	if (!elem) continue;
1273 	if (elem->rootDef != cellDef) continue;
1274 
1275 	for (stylePtr = elem->stylelist; stylePtr != NULL; stylePtr = stylePtr->next)
1276 	    freeMagic(stylePtr);
1277 
1278 	if (elem->type == ELEMENT_TEXT)
1279 	    freeMagic(elem->text);
1280 
1281 	HashSetValue(entry, NULL);
1282 	freeMagic(elem);
1283     }
1284 }
1285