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