1 /* @(#)short.c	1.3	12/30/83
2  *
3  * Copyright -C- 1982 Barry S. Roitblat
4  *
5  *
6  *      This file contains routines to implement the short commands
7  * of the gremlin picture editor.
8  *
9  */
10 
11 #include "gremlin.h"
12 #include "grem2.h"
13 #include <signal.h>
14 #include <sgtty.h>
15 
16 /* imports from graphics files */
17 
18 extern GRVector(), GRPutText();
19 extern GRCurve();
20 extern GRClear();
21 extern int GRArc();
22 extern charxsize, charysize;
23 
24 /* imports from display.c */
25 
26 extern DISScreenAdd(), DISScreenErase();
27 extern DISDisplaySet(), DISEraseSet(), DISClearSetDisplay();
28 
29 /* imports from database files */
30 
31 extern ELT *DBInit(), *DBCreateElt();
32 extern DBDelete(), DBGravitate(), DBClearElt();
33 extern ELT *DBCopy();
34 extern DBXform(), DBBounded();
35 extern DBAddSet(), DBClearSet();
36 extern POINT *PTInit(), *PTMakePoint();
37 extern PTDeletePoint();
38 
39 /* imports from long.c */
40 
41 extern LGIncludeSet();
42 
43 /* imports from menu.c  */
44 
45 extern MNHighLt(), MNUnHighLt();
46 extern HiMen[], HiFont[], HiBrush[], HiMode[];
47 
48 int adj[4] = { 0, 0, 1, 2 };
49 
50 /* imports from textio.c */
51 
52 extern TxMsgOK(), TxPutString(), TxGetLine(), TxRedisplay(), TxPutMsg();
53 extern char TxGetChar();
54 extern TXFIELD TAlign, TAdjust, TBrush, TFont, TGravity, TCSize;
55 extern TXFIELD TEdit, TJustmode;
56 
57 /* imports from c */
58 
59 extern char *malloc();
60 extern char *sprintf();
61 
62 /* imports from main.c */
63 
64 extern globinit();                  /* global initialization routine */
65 extern ELT *PICTURE;                /* current PICTURE database      */
66 extern ELT *cset;                   /* current set database          */
67 extern CBRUSH, CSIZE, CFONT;        /* current brush, size, font     */
68 extern CJUST;                       /* text justification mode       */
69 extern Gridon;                      /* grid mode flag                */
70 extern Orientation;                 /* orientation of workspace      */
71 extern Alignment;                   /* point alignment indicator     */
72 extern float PX, PY;                /* cursor coordinates            */
73 extern float Lastx, Lasty;          /* previous cursor coordinates   */
74 extern SEQ;                         /* point sequence number         */
75 extern char *Editfile;              /* current edit file             */
76 extern POINT *POINTLIST;            /* accumulated point list        */
77 extern Adjustment;                  /* point adjustment mode         */
78 extern GravityOn;                   /* gravity mode flag             */
79 extern Consume;                     /* point clear flag              */
80 extern CHANGED;                     /* PICTURE changed flag          */
81 extern ELT *MEN[];                  /* pointers for user symbols     */
82 extern POINT MENPOINT[];            /* pointers used fo user symbols */
83 extern cmdbuf[];                    /* line buffer for commands      */
84 extern ELT arhead;                  /* arrow head template           */
85 extern char *textpos[], *dispmode[];/* text positioning modes        */
86 extern int textmode[];              /* text positioning              */
87 
88 
89 extern SHUpdate(), SHDrawArc(), SHDrawCurve(), SHCopy(), SHRedis(),
90        SHDefineSet(), SHErase(), SHSetArea(), SHGravity(),
91        SHGrid(), SHRotate(), SHScale(), SHTranslate(), SHShellEsc(),
92        SHDrawVector(), SHMAdjust(), SHBox(), SHArrow(),
93        SHSave1(), SHSave2(), SHSave3(), SHSave4();
94 
95 /* The following two arrays define the short commands and the routines
96  * that process them.
97  */
98 static char shcmds[] = { '\14', '!', '1', '2', '3', '4', 'a', 'b',
99                           'c', 'd', 'e', 'f', 'g', 'l', 'q', 'r',
100                           's', 't', 'v', 'w', 'x', 'z', '\0'};
101 static (*(shrtns[]))() = {
102     SHUpdate,			/* redraw screen */
103     SHShellEsc,                 /* shell escape */
104     SHSave1,                    /* save user symbol */
105     SHSave2,                    /* save user symbol */
106     SHSave3,                    /* save user symbol */
107     SHSave4,                    /* save user symbol */
108     SHDrawArc,			/* draw arc */
109     SHDrawCurve,		/* draw curve */
110     SHCopy,			/* copy current set */
111     SHDefineSet,		/* define current set */
112     SHErase,			/* erase elements */
113     SHSetArea,			/* select area for current set */
114     SHGravity,			/* gravity */
115     SHRedis,                    /* text screen redisplay */
116     SHGrid,			/* toggle grid display */
117     SHRotate,			/* rotate current set */
118     SHScale,			/* scale current set */
119     SHTranslate,		/* translate current set */
120     SHDrawVector,		/* draw vectors */
121     SHArrow,                    /* arrowhead */
122     SHBox,                      /* rectangle from two points */
123     SHMAdjust};			/* manhattan adjust */
124 
125 
126 int
127 SHLookup(c, table)
128 char c;				/* Pointer to a char to be looked up */
129 char table[];			/* Pointer to an array of characters
130 				 * which are the valid commands.  The array
131 				 * must be sorted by ascii value.
132 				 */
133 
134 /*---------------------------------------------------------
135  *	SHLookup searches a table of characters to find one that matches a
136  *	the given character.
137  *
138  *      Results: If c is a valid command character, its index is
139  * returned, otherwise, if c is the null command, -2 is returned
140  * else -1 is returned.
141  *
142  *	Side Effects:	None.
143  *
144  * (Modified from software written by John Ousterhout for the caesar
145  *  program)
146  *---------------------------------------------------------
147  */
148 
149 {
150     int index;
151 
152     if ((c == ' ') || (c == '\0')) return(-2);
153     for (index=0; table[index] != '\0'; index++)
154     {
155         if (table[index] == c) return(index);
156         if (table[index] > c) return(-1);
157     }
158     return(-1);
159 }  /* end SHLookup */
160 
161 
162 SHCommand(command)
163 char *command;
164 
165 /*---------------------------------------------------------
166  *	This routine reads in, looks up, and executes a long command.
167  *
168  *	Results:	None.
169  *
170  *	Side Effects:
171  *	Depends on the command that is invoked.
172  *---------------------------------------------------------
173  */
174 
175 {
176     int index;
177     index = SHLookup(*command, shcmds);
178     if (index == -2) return;
179     if (index >= 0)
180     {
181 
182 	(*(shrtns[index]))();
183     }
184     else
185     {
186 	error("no such command");
187     }
188 }  /* end SHCommand */
189 
190 static char nopnt[18] = "not enough points";
191 static char noset[15] = "no current set";
192 
193 SHDrawVector()
194 /*
195  *      This routine creates and displays a VECTOR element from the
196  * points previously specified.
197  */
198 
199 {
200 	ELT *e1;
201 	POINT *plist, *p1, *p2;
202 	char *txt;
203 
204 	if (SEQ < 2)        /* not enough points */
205 	{
206 		error(nopnt);
207 		return;
208 	}
209 	DBClearSet();         /* Clear current set in preparation */
210 	DISClearSetDisplay(); /* for making this the new one */
211 	GRsetwmask(linemask | setmask);
212 	plist = PTInit();
213 	p1 = POINTLIST;
214 	(void) PTMakePoint(p1->x, p1->y, &plist);
215 	p2 = PTNextPoint(p1);
216 	while ( !Nullpoint(p2) )
217 	{
218 		(void) PTMakePoint(p2->x, p2->y, &plist);
219 		GRVector(p1, p2, CBRUSH);
220 		p1 = p2;
221 		p2 = PTNextPoint(p1);
222 	}  /* end while */;
223 	txt = malloc(1);
224 	*txt = '\0';
225 	e1 = DBCreateElt(VECTOR, plist, CBRUSH, 0, txt, &PICTURE);
226 	DBAddSet(e1);
227 	CHANGED = TRUE;
228 }  /* end SHDrawVector */
229 
230 #define twoPi 6.2832
231 
232 SHDrawArc()
233 /*
234  *      This routine creates and displays an ARC element based on the
235  * points previously defined.  If 3 or more points are defined, the
236  * extent of the arc is calculated as the angle formed by the
237  * respective lines through the second and third points and the first
238  * point.  If only 2 points are specified, a full circle is drawn.
239  */
240 
241 {
242 	ELT *e1;
243 	POINT *plist, *p1, *p2;
244 	char *txt;
245 	double a1, a2, angle, radius;
246 	int stat;
247 
248 	if (SEQ < 2)         /* not enough points */
249 	{
250 		error(nopnt);
251 		return;
252 	}
253 	plist = PTInit();
254 	p1 = POINTLIST;
255 	p2 = PTNextPoint(p1);
256 	radius = sqrt( pow((p2->x - p1->x), 2.0)
257 	             + pow((p2->y - p1->y), 2.0));
258 	if (SEQ == 2)         /* draw full circle */
259 	{
260 		angle = 0;
261          /* Add extra positioning points  */
262 		(void) PTMakePoint(p1->x, p1->y, &plist);
263 		(void) PTMakePoint(p2->x, p2->y, &plist);
264 		(void) PTMakePoint(p1->x, p1->y + radius, &plist);
265 		(void) PTMakePoint(p1->x, p1->y - radius, &plist);
266 		(void) PTMakePoint(p1->x + radius, p1->y, &plist);
267 		(void) PTMakePoint(p1->x - radius, p1->y, &plist);
268 	}  /* end if */
269 	else
270 	{
271 		(void) PTMakePoint(POINTLIST->x, POINTLIST->y, &plist);
272 		p1 = PTNextPoint(POINTLIST);
273 		(void) PTMakePoint(p1->x, p1->y, &plist);
274 		p2 = PTNextPoint(p1);
275 		a1 =  atan2((p1->x - POINTLIST->x), (p1->y - POINTLIST->y));
276 		a2 =  atan2((p2->x - POINTLIST->x), (p2->y - POINTLIST->y));
277 		angle = a1 - a2;
278 		if (angle < 0.0) angle += twoPi;
279 
280 		       /* Set second point to lie on arc */
281 		(void) PTMakePoint((radius * sin(a2) + POINTLIST->x),
282 		                    (radius * cos(a2) + POINTLIST->y),
283 		                     &plist);
284 
285 		angle *= 360.0/twoPi;      /* convert to degrees */
286 	};
287 	p1 = PTNextPoint(POINTLIST);
288 	DBClearSet();         /* Clear current set, this element becomes */
289 	DISClearSetDisplay(); /* the new current set                     */
290 	GRsetwmask(linemask | setmask);
291 	stat = GRArc(POINTLIST, p1, angle, CBRUSH);
292 	if (stat > -1)
293 	{
294 		txt = malloc(1);
295 		*txt = '\0';
296 		e1 = DBCreateElt(ARC, plist, CBRUSH, (int) (angle + 0.5),
297 	                 	txt, &PICTURE);
298 		DBAddSet(e1);
299 		CHANGED = TRUE;
300 	}
301 }  /* end SHDrawARc */
302 
303 
304 SHDrawCurve()
305 /*
306  *      This routine creates and displays a curve using points previously
307  * defined.  This is dependent on DISCurve which is not yet implemented.
308  */
309 
310 {
311 	ELT *e1;
312 	char *txt;
313 	POINT *p1, *p2, *plist;
314 	int stat;
315 
316 	stat = 0;
317 	if (SEQ < 2)       /* not enough points */
318 	{
319 		error("need at least 2 points");
320 		return;
321 	}
322 	plist = PTInit();
323 	p1 = POINTLIST;
324 	(void) PTMakePoint(p1->x, p1->y, &plist);
325 	p2 = PTNextPoint(p1);
326 	while ( !Nullpoint(p2) )
327 	{
328 		(void) PTMakePoint(p2->x, p2->y, &plist);
329 		p1 = p2;
330 		p2 = PTNextPoint(p1);
331 	}  /* end while */;
332 	DBClearSet();         /* Clear current set, this element */
333 	DISClearSetDisplay(); /* the new current set             */
334 	GRsetwmask(linemask | setmask);
335 	stat = GRCurve(plist, CBRUSH);
336 	if (stat != 0)   /* bad knots */
337 	{
338 		error("too many consecutive knots at same place");
339 		return;
340 	}
341 	txt = malloc(1);
342 	*txt = '\0';
343 	e1 = DBCreateElt(CURVE, plist, CBRUSH, 0, txt, &PICTURE);
344 	DBAddSet(e1);
345 	CHANGED = TRUE;
346 }  /* end SHDrawCurve */
347 
348 SHErase()
349 /*
350  *     This routine erases selected elements from the screen and deletes
351  * them from the picture database.
352  */
353 
354 {
355 	ELT *e1;
356 
357 	while ( !DBNullelt(cset) )     /* delete elements in current set */
358 	{
359 		DISScreenErase(cset, (linemask | setmask));
360 		e1 = DBNextofSet(cset);
361 		DBDelete(cset, &PICTURE);
362 		cset = e1;
363 	};
364 	CHANGED = TRUE;
365 }  /* end SHErase */
366 
367 
368 SHGravity()
369 /*
370  *      This routine toggles the gravity mode.
371  */
372 
373 {
374 	if (GravityOn)
375 	{
376 		MNUnHighLt(HiMode[3]);
377 		GravityOn = FALSE;
378 		TxPutString(&TGravity, "OFF");
379 	}
380 	else
381 	{
382 		MNHighLt(HiMode[3], hicolor);
383 		GravityOn = TRUE;
384 		TxPutString(&TGravity, " ON");
385 	}
386 	Consume = FALSE;
387 }  /* End GravityOn */
388 
389 SHGrid()
390 /*
391  *      This routine toggles the display of the grid
392  */
393 
394 {
395 	if (Gridon)
396 	{
397 		Gridon = FALSE;
398 		GRBlankGrid();
399 	}
400 	else
401 	{
402 		Gridon = TRUE;
403 		GRDisplayGrid();
404 	}
405 	Consume = FALSE;
406 }  /* end SHGrid */
407 
408 
409 SHMAdjust()
410 /*
411  * Manhattan Adjust -
412  *      This routine toggles the adjustment mode.
413  */
414 
415 {
416 	if (Adjustment == MAN)
417 	{
418 		MNUnHighLt(HiMode[adj[MAN]]);
419 		Adjustment = NOADJ;
420 		TxPutString(&TAdjust, "NO ADJUSTMENT");
421 	}
422 	else
423 	{
424 		MNUnHighLt(HiMode[adj[Adjustment]]);
425 		MNHighLt(HiMode[adj[MAN]], hicolor);
426 		Adjustment = MAN;
427 		TxPutString(&TAdjust, "  MANHATTAN  ");
428 	}
429 	Consume = FALSE;
430 }
431 
432 
433 SHDefineSet()
434 /*
435  *      This routine defines the current set based upon previously
436  * defined points to select elements.  The action is performed by
437  * clearing the current set and calling LGIncludeSet.
438  */
439 
440 {
441 	DBClearSet();
442 	DISClearSetDisplay();
443 	LGIncludeSet("");
444 }  /* end SHDefineSet */
445 
446 SHSetArea()
447 /*
448  *      This routine defines the current set by selecting all elements
449  * bounded by a rectangle whose diagonal is defined by specifed points.
450  */
451 
452 {
453 	ELT *e1;
454 	POINT *p1;
455 	float x1, y1, x2, y2;
456 
457 	if (SEQ < 2)
458 	{
459 		error(nopnt);
460 		return;
461 	}
462 	DBClearSet();
463 	DISClearSetDisplay();
464 	x1 = POINTLIST->x;
465 	y1 = POINTLIST->y;
466 	p1 = PTNextPoint(POINTLIST);
467 	x2 = p1->x;
468 	y2 = p1->y;
469 	e1 = PICTURE;
470 	while ( !DBNullelt(e1) )
471 	{
472 		if ( DBBounded(e1, x1, y1, x2, y2) )
473 		{
474 			DBAddSet(e1);
475 			DISDisplaySet(e1);
476 		}  /* end if */
477 		e1 = DBNextElt(e1);
478 	}  /* end while */
479 }  /* end SHSetArea */
480 
481 
482 SHTranslate()
483 /*
484  *      This routine translates the elements in the current set as defined
485  * by points.  The translation is accomplished by defining a transformation
486  * matrix and calling DBXform.
487  */
488 
489 {
490 	ELT *e1;
491 	POINT *p1;
492 	float xmat[3][2];
493 
494 	if (SEQ < 2)        /* not enough points */
495 	{
496 		error(nopnt);
497 		return;
498 	}
499 	if (DBNullelt(cset))
500 	{
501 		error(noset);
502 		return;
503 	}
504 	p1 = PTNextPoint(POINTLIST);
505 	xmat[0][0] = xmat[1][1] = 1;    /* set up translation matrix */
506 	xmat[1][0] = xmat[0][1] = 0;
507 	xmat[2][0] = p1->x - POINTLIST->x;
508 	xmat[2][1] = p1->y - POINTLIST->y;
509 	e1 = cset;
510 	while ( !DBNullelt(e1) )
511 	{
512 		DISScreenErase(e1, (linemask | setmask));
513 		TxMsgOK();
514 		DBXform(e1, xmat, &PICTURE);
515 		DISScreenAdd(e1, (linemask | setmask));
516 		e1 = DBNextofSet(e1);
517 	}  /* end while */
518 	CHANGED = TRUE;
519 }  /* end SHTranslate */
520 
521 
522 SHCopy()
523 /*
524  *      This routine copies the elements in the current set as defined
525  * by points.  To copy, the current set pointer is cleared so that new
526  * elements as added by DBCopy can be used to comprise the new current
527  * set.  A pointer is maintained to the old current set which is traversed
528  * to determine the elements to be copied.  This process continues for all
529  * points specified.
530  *
531  * NOTE:  This assumes that the DBClearSet routine does not alter the
532  *        pointers between elements in the set (which is currently true),
533  *        and must be changed it this does not hold.
534  */
535 
536 {
537 	ELT *e1, *e2;
538 	POINT *p1, *p2;
539 	float xmat[3][2];
540 
541 	if (SEQ < 2)        /* not enough points */
542 	{
543 		error(nopnt);
544 		return;
545 	}
546 	if (DBNullelt(cset))
547 	{
548 		error(noset);
549 		return;
550 	}
551 	p1 = POINTLIST;
552 	p2 = PTNextPoint(POINTLIST);
553 	while ( !Nullpoint(p2) )
554 	{
555 		xmat[0][0] = xmat[1][1] = 1;    /* set up translation matrix */
556 		xmat[1][0] = xmat[0][1] = 0;
557 		xmat[2][0] = p2->x - p1->x;
558 		xmat[2][1] = p2->y - p1->y;
559 		DISClearSetDisplay();
560 		e1 = cset;
561 		DBClearSet();    /* Dependent on Clearset preserving pointers */
562 		while ( !DBNullelt(e1) )
563 		{
564 			e2 = DBCopy(e1, xmat, &PICTURE);
565 			DBAddSet(e2);
566 			DISScreenAdd(e2, (linemask | setmask));
567 			e1 = DBNextofSet(e1);
568 		}  /* end while ! null elt */
569 		p1 = p2;
570 		p2 = PTNextPoint(p2);
571 	}  /* end while ! null point */
572 	CHANGED = TRUE;
573 }  /* end SHCopy */
574 
575 
576 SHRotate()
577 /*
578  *      This routine rotates the elements in the current set as defined
579  * by points.  The rotation is accomplished by defining a transformation
580  * matrix and calling DBXform.
581  */
582 
583 {
584 	ELT *e1;
585 	POINT pos, *p1, *p2;
586 	float xmat[3][2], angle, s, c;
587 	int i, j;
588 
589 	if (SEQ < 3)        /* not enough points */
590 	{
591 		error(nopnt);
592 		return;
593 	}
594 	if (DBNullelt(cset))
595 	{
596 		error(noset);
597 		return;
598 	}
599 	p1 = PTNextPoint(POINTLIST);  /* calculate rotation angle */
600 	p2 = PTNextPoint(p1);
601 	angle = (float) atan2((p2->x - POINTLIST->x),(p2->y - POINTLIST->y))
602 	       -(float) atan2((p1->x - POINTLIST->x),(p1->y - POINTLIST->y));
603 	s = (float) sin(angle);
604 	c = (float) cos(angle);
605 
606    /* Define transformation matrix to translate set to origin, rotate,
607       and translate back.                                              */
608 
609 	xmat[0][0] = c;
610 	xmat[0][1] = -s;
611 	xmat[1][0] = s;
612 	xmat[1][1] = c;
613 	xmat[2][0] = (-c) * POINTLIST->x - s * POINTLIST->y + POINTLIST->x;
614 	xmat[2][1] = (-c) * POINTLIST->y + s * POINTLIST->x + POINTLIST->y;
615 	e1 = cset;
616 	while ( !DBNullelt(e1) )
617 	{
618 		DISScreenErase(e1, (linemask | setmask));
619 		TxMsgOK();
620 		DBXform(e1, xmat, &PICTURE);
621 		if (TEXT(e1->type))
622 		{
623 			GRsetwmask(textmask | setmask);
624 			p1 = e1->ptlist;
625 			GRPutText(e1->type, p1, e1->brushf,
626 				  e1->size,e1->textpt, &pos);
627 			i= strlen(e1->textpt);
628 			p2 = PTInit();
629 			(void) PTMakePoint(p1->x, p1->y, &p2);
630                    	    /* add extra positioning points */
631 			(void) PTMakePoint(pos.x, pos.y, &p2);
632 			(void) PTMakePoint(pos.x + i * charxsize / 2,
633 					   pos.y, &p2);
634 			(void) PTMakePoint(pos.x + i * charxsize,
635 					   pos.y, &p2);
636 			e1->ptlist = p2;
637 		}  /* end if TEXT */
638 		else
639 			DISScreenAdd(e1, (linemask | setmask));
640 		e1 = DBNextofSet(e1);
641 	}  /* end while */
642 	CHANGED = TRUE;
643 }  /* end SHRotate */
644 
645 
646 SHScale()
647 /*
648  *      This routine scales the elements in the current set as defined
649  * by points.  The scaling is accomplished by defining a transformation
650  * matrix and calling DBXform.
651  */
652 
653 {
654 	ELT *e1;
655 	POINT pos, *p1, *p2;
656 	float xmat[3][2], d1, d2, scalex, scaley;
657 	int i, j;
658 
659 	if (SEQ < 3)        /* not enough points */
660 	{
661 		error(nopnt);
662 		return;
663 	}
664 	if (DBNullelt(cset))
665 	{
666 		error(noset);
667 		return;
668 	}
669 	p1 = PTNextPoint(POINTLIST);
670 	p2 = PTNextPoint(p1);
671 	d1 = sqrt( pow((p1->x - POINTLIST->x), 2.0)
672 	         + pow((p1->y - POINTLIST->y), 2.0));
673 	d2 = sqrt( pow((p2->x - POINTLIST->x), 2.0)
674 	         + pow((p2->y - POINTLIST->y), 2.0));
675 	if (d1 == 0)
676 	{
677 		error("infinite scale");
678 		return;
679 	}
680 	scalex = scaley = d2 / d1;
681 
682     /* create transformation matrix to translate set to origin,
683        performaing the scaling and translating back               */
684 
685 	xmat[0][0] = scalex;
686 	xmat[1][1] = scaley;
687 	xmat[1][0] = xmat[0][1] = 0;
688 	xmat[2][0] = - POINTLIST->x * (scalex - 1.0);
689 	xmat[2][1] = - POINTLIST->y * (scaley - 1.0);
690 	e1 = cset;
691 	while ( !DBNullelt(e1) )
692 	{
693 		DISScreenErase(e1, (linemask | setmask));
694 		TxMsgOK();
695 		DBXform(e1, xmat, &PICTURE);
696 		if (TEXT(e1->type))
697 		{
698 			GRsetwmask(textmask | setmask);
699 			p1 = e1->ptlist;
700 			GRPutText(e1->type, p1, e1->brushf,
701 				  e1->size,e1->textpt, &pos);
702 			i= strlen(e1->textpt);
703 			p2 = PTInit();
704 			(void) PTMakePoint(p1->x, p1->y, &p2);
705                    	/* add extra positioning points */
706 			(void) PTMakePoint(pos.x, pos.y, &p2);
707 			(void) PTMakePoint(pos.x + i * charxsize / 2,
708 					   pos.y, &p2);
709 			(void) PTMakePoint(pos.x + i * charxsize,
710 					   pos.y, &p2);
711 			e1->ptlist = p2;
712 		}  /* end if TEXT */
713 		else
714 			DISScreenAdd(e1, (linemask | setmask));
715 		e1 = DBNextofSet(e1);
716 	}  /* end while */
717 	CHANGED = TRUE;
718 }  /* end SHScale */
719 
720 
721 SHUpdate()
722 /*
723  *      This routine redraws the graphics screen by clearing the screen ,
724  * redisplaying the menu and adding each element back to the display.
725  */
726 
727 {
728 	ELT *e1;
729 	int i;
730 
731 	GRClear(linemask | setmask | textmask);
732 	MNDisplayMenu();
733 	e1 = PICTURE;
734 	while ( !DBNullelt(e1) )
735 	{
736 		DISScreenAdd(e1, linemask);
737 		e1 = DBNextElt(e1);
738 	};
739 	e1 = cset;
740 	while ( !DBNullelt(e1) )
741 	{
742 		DISScreenAdd(e1, setmask);
743 		e1 = DBNextofSet(e1);
744 	};
745 	if ( Adjustment != NOADJ) MNHighLt(HiMode[adj[Adjustment]], hicolor);
746 	if (GravityOn) MNHighLt(HiMode[3], hicolor); /* re highlight  */
747 	MNHighLt(HiFont[CFONT-1], hicolor);          /* selected menu */
748 	MNHighLt(HiBrush[CBRUSH-1], hicolor);        /* menu attributes */
749 	for (i = 0; i < NUSER; ++i)
750 		if ( !DBNullelt(MEN[i]) ) MNHighLt(HiMen[i], hicolor);
751 	Consume = FALSE;
752 }  /* end SHUpdate */
753 
754 
755 SHRedis()
756 /*
757  *      This routine is used to redisplay the text screen.
758  * It clears the screen, then redisplays the text and each of the
759  * defined fields.
760  */
761 
762 {
763 	char string[4];
764 
765 	TxRedisplay();
766 	TxPutString(&TEdit, Editfile);
767 	(void) sprintf(string,"%1d",CBRUSH);
768 	TxPutString(&TBrush,string);
769 	(void) sprintf(string, "%1d",CFONT);
770 	TxPutString(&TFont,string);
771 	(void) sprintf(string, "%1d",CSIZE);
772 	TxPutString(&TCSize,string);
773 	TxPutString(&TJustmode,dispmode[CJUST]);
774 	(void) sprintf(string, "%3d",Alignment);
775 	TxPutString(&TAlign,string);
776 	(void) sprintf(string,"%1d",CBRUSH);
777 	TxPutString(&TBrush,string);
778 	if (GravityOn) TxPutString(&TGravity, " ON");
779 	else TxPutString(&TGravity, "OFF");
780 	switch(Adjustment)
781 	{
782 	  case NOADJ: TxPutString(&TAdjust, "NO ADJUSTMENT");
783 	              break;
784 	   case HORZ: TxPutString(&TAdjust, " HORIZONTAL  ");
785 	              break;
786 	   case VERT: TxPutString(&TAdjust, "  VERTICAL   ");
787 	              break;
788 	    case MAN: TxPutString(&TAdjust, "  MANHATTAN  ");
789 	              break;
790 	}  /* end switch */;
791         Consume = FALSE;
792 }  /* end SHRedis */
793 
794 
795 SHShellEsc()
796 /*
797  *      This routine performs a shell escape through the c 'system'
798  * function.  It first retrieves the command line through TxGetLine.
799  */
800 
801 {
802 	char line[80];
803 
804 	TxGetLine("!",line,80);
805 	TxClose();     /* Restore text terminal to 'normal' state */
806 	(void) system(line);  /* do command */
807           /* allow time for user to digest command */
808 	printf("Type <cr> to continue");
809 	(void) fflush(stdout);
810 	line[1] = TxGetChar();
811 	SHRedis();  /* reclaim terminal */
812 }  /* end ShellEsc */
813 
814 
815 static savemen(sym)
816 int sym;
817 /*
818  *      This local routine stores the current set in the specified
819  * user symbol.
820  */
821 
822 {
823 	ELT *elist;
824 	float xmat[3][2];
825 
826 	xmat[0][0] = xmat[1][1] = 1;  /* set up copy transformation */
827 	xmat[0][1] = xmat[1][0] = 0;  /* matrix for no              */
828 	xmat[2][0] = xmat[2][1] = 0;  /* transformation             */
829 	while ( !DBNullelt(MEN[sym]) )
830 	{                             /* clear out existing symbol */
831 		elist = DBNextElt(MEN[sym]);
832 		DBClearElt(MEN[sym]);
833 		MEN[sym] = elist;
834 	};
835 	elist = cset;          /* copy current set to symbol */
836 	while ( !DBNullelt(elist) )
837 	{
838 		(void) DBCopy(elist, xmat, &(MEN[sym]));
839 		elist = DBNextofSet(elist);
840 	}  /* end while */;
841 	if (SEQ == 0)     /* no positioning points */
842 	{
843 		MENPOINT[sym].x = 0;
844 		MENPOINT[sym].y = 0;
845 	}
846 	else
847 	{
848 		MENPOINT[sym].x = POINTLIST->x;
849 		MENPOINT[sym].y = POINTLIST->y;
850 	}
851 	if ( !DBNullelt(MEN[sym]) ) MNHighLt(HiMen[sym], hicolor);
852 	else     MNUnHighLt(HiMen[sym]);
853 	CHANGED = TRUE;
854 }  /* end savemen */
855 
856 SHSave1()
857 /*
858  *      This routine saves the current set in user symbol 1 by
859  * calling savemen.
860  */
861 
862 {
863 	savemen(0);
864 }
865 
866 SHSave2()
867 /*
868  *      This routine saves the current set in user symbol 2 by
869  * calling savemen.
870  */
871 
872 {
873 	savemen(1);
874 }
875 
876 SHSave3()
877 /*
878  *      This routine saves the current set in user symbol 3 by
879  * calling savemen.
880  */
881 
882 {
883 	savemen(2);
884 }
885 
886 SHSave4()
887 /*
888  *      This routine saves the current set in user symbol 4 by
889  * calling savemen.
890  */
891 
892 {
893 	savemen(3);
894 }
895 
896 
897 
898 SHBox()
899 /*
900  *      This routine creates and displays a rectangle whose diagonal is
901  * defined by two points.  The routine uses the coordinates of these
902  * points to define a VECTOR element with the appropriate vertices.
903  */
904 
905 {
906 	POINT *plist, *p1, *p2;
907 	char *txt;
908 	ELT *e1;
909 
910 	if (SEQ < 2)
911 	{
912 		error("not enough points");
913 		return;
914 	}
915 	p1 = POINTLIST;
916 	p2 = PTNextPoint(p1);
917 	plist = PTInit();   /* create points for vector element which defines
918 	                       the rectangle                                 */
919 	(void) PTMakePoint(p1->x, p1->y, &plist);
920 	(void) PTMakePoint(p1->x, p2->y, &plist);
921 	(void) PTMakePoint(p2->x, p2->y, &plist);
922 	(void) PTMakePoint(p2->x, p1->y, &plist);
923 	(void) PTMakePoint(p1->x, p1->y, &plist);   /* close rectangle */
924 	txt = malloc(1);
925 	*txt = '\0';
926 	DBClearSet();         /* clear old set in preparation to make */
927 	DISClearSetDisplay(); /* the new current set */
928 	e1 = DBCreateElt(VECTOR, plist, CBRUSH, 0, txt, &PICTURE);
929 	DBAddSet(e1);
930 	DISScreenAdd(e1, (linemask | setmask));
931 	CHANGED = TRUE;
932 }  /* end LGBox */
933 
934 SHArrow()
935 /*
936  *      This routine draws arrow heads by 'copying' the arrow head template
937  * into the picture appropriately transformed.
938  */
939 
940 {
941 	ELT *e1;
942 	POINT p1, *p2;
943 	float xmat[3][2], angle, s, c;
944 
945 	if (SEQ < 2)        /* not enough points */
946 	{
947 		error(nopnt);
948 		return;
949 	}
950 	p1.x = POINTLIST->x - 1;
951 	p1.y = POINTLIST->y;
952 	p2 = PTNextPoint(POINTLIST);
953 	angle = (float) atan2((p2->x - POINTLIST->x),(p2->y - POINTLIST->y))
954 	       -(float) atan2((p1.x - POINTLIST->x),(p1.y - POINTLIST->y));
955 	s = (float) sin(angle);
956 	c = (float) cos(angle);
957 
958    /* Define transformation matrix to translate element from origin
959       and rotate.                                                   */
960 
961 	xmat[0][0] = c;
962 	xmat[0][1] = -s;
963 	xmat[1][0] = s;
964 	xmat[1][1] = c;
965 	xmat[2][0] = POINTLIST->x;
966 	xmat[2][1] = POINTLIST->y;
967 
968 	DBClearSet();         /* clear old set in preparation to make */
969 	DISClearSetDisplay(); /* the new current set */
970 	arhead.brushf = CBRUSH;
971 	e1 = DBCopy(&arhead, xmat, &PICTURE);
972 	DBAddSet(e1);
973 	DISScreenAdd(e1, (linemask | setmask));
974 	CHANGED = TRUE;
975 }  /* end SHArrow */
976