1 /*
2  * @(#)long1.c	1.2	01/03/85
3  *
4  * Routines to implement "long" commands in the SUN Gremlin picture editor.
5  *
6  * Mark Opperman (opcode@monet.BERKELEY)
7  *
8  */
9 
10 /*
11  * This file contains routines to implement the long text commands
12  * of the gremlin PICTURE editor.
13  */
14 
15 #include <sunwindow/rect.h>
16 #include "gremlin.h"
17 #include <ctype.h>
18 
19 /* imports from graphics files */
20 
21 extern GRBlankPoints();
22 extern GRDisplayPoint();
23 extern GRErasePoint();
24 extern GRfontfound();
25 extern GROpenFont();
26 extern GRSetTextPos();
27 extern curve_set;		/* TRUE if spline points pre-computed */
28 
29 /* imports from point.c */
30 
31 extern POINT *PTMakeTextPoints();
32 
33 /* imports from display.c */
34 
35 extern DISClearSetDisplay();
36 extern DISScreenAdd();
37 extern DISScreenErase();
38 
39 /* imports from database files */
40 
41 extern ELT *DBCreateElt();
42 extern DBGravitate();
43 extern DBChangeBrush();
44 extern DBChangeFont();
45 extern DBChangeSize();
46 extern DBChangeStipple();
47 extern DBChangeText();
48 extern DBChangeJustify();
49 extern DBAddSet();
50 extern DBClearSet();
51 extern POINT *PTMakePoint();
52 extern PTDeletePoint();
53 
54 /* imports from undodb.c */
55 
56 extern UNRembMod();
57 
58 /* imports from short.c */
59 
60 extern SHUpdate();
61 
62 /* imports from menu.c  */
63 
64 extern HiArtMode;
65 extern HiLineStyle;
66 extern HiBrush[];
67 extern MNHighLt();
68 extern MNUnHighLt();
69 extern HiFont[];
70 extern HiSize[];
71 extern HiStipple[];
72 
73 /* imports from text.c */
74 
75 extern TxKillLine();
76 extern TxMsgOK();
77 extern text_getvalue();
78 
79 /* imports from C */
80 
81 extern char *malloc();
82 extern char *strcpy();
83 
84 /* imports from main.c */
85 
86 extern ELT *PICTURE;                /* current PICTURE database      */
87 extern ELT *cset;                   /* current set database          */
88 extern Artmode;			/* indication of point display size */
89 extern CBRUSH, CSIZE, CFONT;        /* current brush, size, font     */
90 extern CJUST;                       /* current text justification    */
91 extern CSTIPPLE;		    /* current stipple pattern	     */
92 extern Alignment;                   /* point alignment indicator     */
93 extern float PX, PY;                /* cursor coordinates            */
94 extern float Lastx, Lasty;          /* previous cursor coordinates   */
95 extern SEQ;                         /* point sequence number         */
96 extern POINT *POINTLIST, *BACKPOINT;/* accumulated point list        */
97 extern Adjustment;                  /* point adjustment mode         */
98 extern GravityOn;                   /* gravity mode flag             */
99 extern CHANGED;                     /* PICTURE changed flag          */
100 extern SymbolicLines;
101 extern Gridsize;
102 
103 extern SUN_XORIGIN;
104 extern SUN_YORIGIN;
105 extern struct rect pix_size;
106 
107 /* locals */
108 
109 int SHOWPOINTS;			/* TRUE if current set reference points on */
110 
111 static char badarg[] = "bad args";
112 static char noset[] = "no current set";
113 static char delmsg[] = "can't delete any more points";
114 
115 #define BADNUM 0x7fffffff	/* largest positive 32-bit integer */
116 
117 /*
118  * This routine trys to interpret the string starting at
119  * line+index as an integral numeric parameter.  The function
120  * returns the numeric equivalent or the largest possible
121  * integer if there is some error in interpreting the string.
122  */
123 GetNumParm(line, index)
124 register char *line;
125 register int *index;
126 {
127     char num[20];
128     register sign = 1;
129     register i;
130     int result;
131 
132     for (i=0; (*(line + *index) == ' '); ++i)	/* skip blanks */
133 	++(*index);
134 
135     if (*(line + *index) == '-') {		/* negative number */
136 	sign = -1;
137 	++(*index);
138     }
139 
140     for (i=0; !Delimiter(*(line + *index)); ++i) {
141 	num[i] = *(line + *index);
142 	if (!isdigit(num[i]))
143 	    return(BADNUM);
144 	++(*index);
145     }
146 
147     if (i == 0)
148 	return(BADNUM);
149 
150     num[i] = '\0';
151     (void) sscanf(num, "%d", &result);
152     return(result * sign);
153 }  /* end GetNumParm */
154 
155 
156 /*
157  * This routine accepts coordinates from the text terminal
158  * and creates and displays a point from them by passing them
159  * along to LGPoint.
160  */
161 LGOPoint()
162 {
163     int index, xcoord, ycoord;
164     char buf[TEXT_BUFMAX];
165 
166     text_getvalue(&buf[0]);
167     TxKillLine();
168     index = 0;
169     xcoord = GetNumParm(buf, &index);
170     if (xcoord == BADNUM) {
171 	error(badarg);
172 	return;
173     }
174 
175     ++index;
176     ycoord = GetNumParm(buf, &index);
177     if (ycoord == BADNUM) {
178 	error(badarg);
179 	return;
180     }
181 
182     PX = xcoord;
183     PY = ycoord;
184     LGPoint();
185 }  /* end LGOPoint */
186 
187 
188 /*
189  * This routine accepts cursor coordinates (global PX & PY) and then
190  * creates and displays points according to the current adjustment and
191  * alignment modes.  Note that alignment and gravity are mutually exclusive
192  * and adjustment takes precedence over either.
193  */
194 LGPoint()
195 {
196     ELT *temp;
197     POINT *p1;
198     float signx = 1.0;
199     float signy = 1.0;
200 
201     temp = DBInit();
202     if (GravityOn)
203 	DBGravitate (PX, PY, &PX, &PY, &p1, &temp, PICTURE, FALSE);
204 
205     if (DBNullelt(temp)) {   /* no gravity in effect */
206 	/* Round to nearest alignment boundary */
207 	if (PX < 0) {
208 	    signx = -1.0;
209 	    PX = -PX;
210 	}
211 	if (PY < 0) {
212 	    signy = -1.0;
213 	    PY = -PY;
214 	}
215 
216 	PX = (float) (((int) (PX / Alignment + 0.5)) * Alignment) * signx;
217 	PY = (float) (((int) (PY / Alignment + 0.5)) * Alignment) * signy;
218     }
219 
220     if (SEQ > 0) {    /* this isn't the first point */
221 	switch (Adjustment) {
222 	    case HORZ:
223 		PY = Lasty;
224 		break;
225 	    case VERT:
226 		PX = Lastx;
227 		break;
228 	    case MAN:
229 		if (fabs(PX - Lastx) > fabs(PY - Lasty))
230 		    PY = Lasty;
231 		else
232 		    PX = Lastx;
233 		break;
234 	}
235     }
236 
237     if (SEQ >= MAXPOINTS) {
238 	error("too many points");
239 	return;
240     }
241 
242     GRDisplayPoint(PX, PY, SEQ);
243     (void) PTMakePoint(PX, PY, &POINTLIST);
244     Lastx = PX;
245     Lasty = PY;
246 
247     ++SEQ;
248 }  /* end LGPoint */
249 
250 
251 /*
252  * Clear all points on from Showpoints command.
253  */
254 CSP()
255 {
256     if (SHOWPOINTS)
257 	LGShowPoints();
258 }
259 
260 
261 /*
262  * This routine deletes all points from the POINTLIST and
263  * clears them from the display also.
264  */
265 CP()
266 {
267     POINT *temp;
268 
269     while (!Nullpoint(BACKPOINT)) {
270 	temp = PTNextPoint(BACKPOINT);
271 	free ((char *) BACKPOINT);
272 	BACKPOINT = temp;
273     }
274 
275     GRBlankPoints(POINTLIST);
276     BACKPOINT = POINTLIST;
277     POINTLIST = PTInit();
278     SEQ = 0;
279 }  /* end CP */
280 
281 
282 /*
283  * Clear all displayed points.
284  */
285 LGClearPoints()
286 {
287     CP();
288     CSP();
289 }  /* end LGClearPoints */
290 
291 
292 /*
293  * This routine removes the last point from the POINTLIST
294  * and erases it from the screen.
295  */
296 LGDeletePoint()
297 {
298     POINT *pt1, *pt2, *pt3;
299 
300     if (SEQ == 0) {
301 	error("no point");
302 	return;
303     }
304 
305     pt2 = pt3 = POINTLIST;
306     while (!Nullpoint(pt3)) {	/* find last point and pointer to it */
307 	pt1 = pt2;
308 	pt2 = pt3;
309 	pt3 = PTNextPoint(pt3);
310     }
311 
312     SEQ--;
313     GRErasePoint(pt2->x, pt2->y, SEQ);
314     PTDeletePoint(pt2, &POINTLIST);
315     if (SEQ > 0) {	/* pt1 points to last one of them */
316 	Lastx = pt1->x;
317 	Lasty = pt1->y;
318     }
319 }  /* end LGDeletePoint */
320 
321 
322 /*
323  *  This routine causes the positioning points of the current set
324  *  to be displayed.
325  */
326 LGShowPoints()
327 {
328     register ELT *elt;
329     register POINT *p1;
330     register pno;
331 
332     if (DBNullelt(cset)) {
333 	error(noset);
334 	return;
335     }
336 
337     elt = cset;
338     while (!DBNullelt(elt)) {
339 	p1 = elt->ptlist;
340 	pno = 0;
341 
342 	while (!Nullpoint(p1)) {
343 	    GRDisplayPoint(p1->x, p1->y, pno);
344 	    p1 = PTNextPoint(p1);
345 	    pno++;
346 	}
347 
348 	elt = DBNextofSet(elt);
349     }
350     SHOWPOINTS = !SHOWPOINTS;
351 } /* end LGShowPoints */
352 
353 
354 /*
355  *  This routine handles the two forms of the TEXT command.
356  *  From the text subwindow, when a RETURN is pressed, the text
357  *  buffer is copied to the LAST point layed down, the text is
358  *  consumed, and that point is eaten.  This provides a convenient
359  *  method of entering several TEXT elements at many locations
360  *  in the picture.
361  *  From the menu subwindow, the traditional Gremlin TEXT command
362  *  is implemented.  One or two points may be specified, and all
363  *  points are consumed at the end of the command.
364  */
365 static
366 LGTextDisplay(oldway)
367 int oldway;
368 {
369     register ELT *elt;
370     char buf[TEXT_BUFMAX];
371     POINT pos, ppnt, *p1;
372     char *text;
373 
374     if (SEQ == 0) {
375 	error("not enough points");
376 	return;
377     }
378 
379     text_getvalue(&buf[0]);
380 
381     if (*buf == '\0') {		/* no text */
382 	error("empty string");
383 	return;
384     }
385 
386     GROpenFont(CFONT, CSIZE);
387     if (!GRfontfound(CFONT, CSIZE)) {
388 	error("can't open font file");
389 	return;
390     }
391 
392     UNForget();
393     text = malloc((unsigned) strlen(buf) + 1);
394     (void) strcpy(text, buf);
395     DISClearSetDisplay();
396     DBClearSet();
397 
398     if (oldway == TRUE) {	/* one or two points OK */
399 	ppnt.x = POINTLIST->x;
400 	ppnt.y = POINTLIST->y;
401 	if (SEQ > 1) {
402 	    p1 = PTNextPoint(POINTLIST);
403 	    ppnt.x = (ppnt.x + p1->x) / 2;
404 	    ppnt.y = (ppnt.y + p1->y) / 2;
405 	}
406     }
407     else {			/* find last point */
408 	p1 = POINTLIST;
409 	while (!Nullpoint(PTNextPoint(p1)))
410 	    p1 = PTNextPoint(p1);
411 	ppnt.x = p1->x;
412 	ppnt.y = p1->y;
413     }
414 
415     GRSetTextPos(text, CJUST, CFONT, CSIZE, &ppnt, &pos);
416     p1 = PTMakeTextPoints(text, CFONT, CSIZE, &ppnt, &pos);
417     elt = DBCreateElt(CJUST, p1, CFONT, CSIZE, text, &PICTURE);
418 
419     DISScreenAdd(elt, pixmask | csetmask);
420     DBAddSet(elt);
421 
422     if (oldway == TRUE)
423 	CP();
424     else
425 	LGDeletePoint();
426 
427     TxKillLine();
428     CHANGED = TRUE;
429 }  /* end LGTextDisplay */
430 
431 
432 /*
433  *  This routine implements the TEXT command from the menu subwindow.
434  */
435 LGText()
436 {
437     LGTextDisplay(TRUE);		/* the old way of doing text entry */
438 }  /* end LGText */
439 
440 
441 LGTextSW()
442 {
443     LGTextDisplay(FALSE);		/* the new way of doing text entry */
444 }  /* end LGTextSW */
445 
446 
447 /*
448  * This routine sets the current brush to that specified in the parameter.
449  */
450 LGBrush(brush)
451 {
452     MNUnHighLt(HiBrush[CBRUSH-1]);
453     CBRUSH = brush;
454     MNHighLt(HiBrush[CBRUSH-1]);
455 }  /* end LGBrush */
456 
457 
458 LGBrush1()
459 {
460     LGBrush(1);
461 }
462 
463 
464 LGBrush2()
465 {
466     LGBrush(2);
467 }
468 
469 
470 LGBrush3()
471 {
472     LGBrush(3);
473 }
474 
475 
476 LGBrush4()
477 {
478     LGBrush(4);
479 }
480 
481 
482 LGBrush5()
483 {
484     LGBrush(5);
485 }
486 
487 
488 LGBrush6()
489 {
490     LGBrush(6);
491 }
492 
493 
494 /*
495  * This routine causes the elements in the current set
496  * to be redrawn using the new brush.
497  */
498 LGMBrush(brush)
499 int brush;
500 {
501     register ELT *elt;
502 
503     if (DBNullelt(cset)) {
504 	error(noset);
505 	return;
506     }
507 
508     UNForget();
509     CSP();
510 
511     elt = cset;
512     while (!DBNullelt(elt)) {
513 	if (!TEXT(elt->type)) {
514 	    DISScreenErase(elt, pixmask | csetmask);
515 	    DBChangeBrush(elt, brush, &PICTURE);
516 	    curve_set = TRUE;	/* no need to re-compute spline points */
517 	    DISScreenAdd(elt, pixmask | csetmask);
518 	}
519 	elt = DBNextofSet(elt);
520     }
521 
522     CP();
523     CHANGED = TRUE;
524 } /* end LGMBrush */
525 
526 
527 LGMBrush1()
528 {
529     LGMBrush(1);
530 }
531 
532 
533 LGMBrush2()
534 {
535     LGMBrush(2);
536 }
537 
538 
539 LGMBrush3()
540 {
541     LGMBrush(3);
542 }
543 
544 
545 LGMBrush4()
546 {
547     LGMBrush(4);
548 }
549 
550 
551 LGMBrush5()
552 {
553     LGMBrush(5);
554 }
555 
556 
557 LGMBrush6()
558 {
559     LGMBrush(6);
560 }
561 
562 
563 /*
564  * This routine causes text elements in the current set
565  * to be redrawn using the new justification mode.
566  * mode is 1 - 9 for tl, tc, tr, cl, cc, cl, bl, bc, br
567  */
568 LGMJustify(just)
569 int just;
570 {
571     register ELT *elt;
572 
573     if (DBNullelt(cset)) {
574 	error(noset);
575 	return;
576     }
577 
578     UNForget();
579     CSP();
580 
581     elt = cset;
582     while (!DBNullelt(elt)) {
583 	if (TEXT(elt->type)) {
584 	    DISScreenErase(elt, pixmask | csetmask);
585 	    DBChangeJustify(elt, just, &PICTURE);
586 	    DISScreenAdd(elt, pixmask | csetmask);
587 	}
588 	elt = DBNextofSet(elt);
589     }
590 
591     CP();
592     CHANGED = TRUE;
593 } /* end LGMJustify */
594 
595 
596 /*
597  * This routine causes the text elements in the current set
598  * to be redrawn using the new font.
599  */
600 LGMFont(font)
601 int font;
602 {
603     register ELT *elt;
604 
605     if (DBNullelt(cset)) {
606 	error(noset);
607 	return;
608     }
609 
610     UNForget();
611     CSP();
612 
613     elt = cset;
614     while (!DBNullelt(elt)) {
615 	if (TEXT(elt->type)) {
616 	    GROpenFont(font, elt->size);
617 	    if (!GRfontfound(font, elt->size)) {
618 		error("can't open font file");
619 	    }
620 	    else {
621 		DISScreenErase(elt, pixmask | csetmask);
622 		TxMsgOK();
623 		DBChangeFont(elt, font, &PICTURE);
624 		DISScreenAdd(elt, pixmask | csetmask);
625 	    }
626 	}
627 	elt = DBNextofSet(elt);
628     }
629 
630     CP();
631     CHANGED = TRUE;
632 }  /* end LGMFont */
633 
634 
635 LGMFont1()
636 {
637     LGMFont(1);
638 }
639 
640 
641 LGMFont2()
642 {
643     LGMFont(2);
644 }
645 
646 
647 LGMFont3()
648 {
649     LGMFont(3);
650 }
651 
652 
653 LGMFont4()
654 {
655     LGMFont(4);
656 }
657 
658 
659 /*
660  * This routine causes the text elements in the current set
661  * to be redrawn using the new size.
662  */
663 LGMSize(size)
664 int size;
665 {
666     register ELT *elt;
667 
668     if (DBNullelt(cset)) {
669 	error(noset);
670 	return;
671     }
672 
673     UNForget();
674     CSP();
675 
676     elt = cset;
677     while (!DBNullelt(elt)) {
678 	if (TEXT(elt->type)) {
679 	    GROpenFont(elt->brushf, size);
680 	    if (!GRfontfound(elt->brushf, size)) {
681 		error("can't open font file");
682 	    }
683 	    else {
684 		DISScreenErase(elt, pixmask | csetmask);
685 		TxMsgOK();
686 		DBChangeSize(elt, size, &PICTURE);
687 		DISScreenAdd(elt, pixmask | csetmask);
688 	    }
689 	}
690 	elt = DBNextofSet(elt);
691     }
692 
693     CP();
694     CHANGED = TRUE;
695 }  /* end LGMFize */
696 
697 
698 LGMSize1()
699 {
700     LGMSize(1);
701 }
702 
703 
704 LGMSize2()
705 {
706     LGMSize(2);
707 }
708 
709 
710 LGMSize3()
711 {
712     LGMSize(3);
713 }
714 
715 
716 LGMSize4()
717 {
718     LGMSize(4);
719 }
720 
721 
722 /*
723  * This routine causes the polygon elements in the current set
724  * to be redrawn using the new stipple.
725  */
726 LGMStipple(stipple)
727 int stipple;
728 {
729     register ELT *elt;
730 
731     if (DBNullelt(cset)) {
732 	error(noset);
733 	return;
734     }
735 
736     UNForget();
737     CSP();
738 
739     elt = cset;
740     while (!DBNullelt(elt)) {
741 	if (elt->type == POLYGON) {
742 	    DISScreenErase(elt, pixmask | csetmask);
743 	    TxMsgOK();
744 	    DBChangeStipple(elt, stipple, &PICTURE);
745 	    DISScreenAdd(elt, pixmask | csetmask);
746 	}
747 	elt = DBNextofSet(elt);
748     }
749 
750     CP();
751     CHANGED = TRUE;
752 }  /* end LGMStipple */
753 
754 
755 LGMStipple1()
756 {
757     LGMStipple(1);
758 }
759 
760 
761 LGMStipple2()
762 {
763     LGMStipple(2);
764 }
765 
766 
767 LGMStipple3()
768 {
769     LGMStipple(3);
770 }
771 
772 
773 LGMStipple4()
774 {
775     LGMStipple(4);
776 }
777 
778 
779 LGMStipple5()
780 {
781     LGMStipple(5);
782 }
783 
784 
785 LGMStipple6()
786 {
787     LGMStipple(6);
788 }
789 
790 
791 LGMStipple7()
792 {
793     LGMStipple(7);
794 }
795 
796 
797 LGMStipple8()
798 {
799     LGMStipple(8);
800 }
801 
802 
803 /*
804  * This routine allows modification of text by replacing
805  * an existing string with a new one, appropriately repositioned
806  */
807 LGMText()
808 {
809     register ELT *elt;
810     char buf[TEXT_BUFMAX];
811 
812     if (DBNullelt(cset)) {
813 	error(noset);
814 	return;
815     }
816 
817     text_getvalue(&buf[0]);
818     if (*buf == '\0') {		/* no text */
819 	error("empty string");
820 	return;
821     }
822 
823     UNForget();
824     CSP();
825 
826     elt = cset;
827     while (!DBNullelt(elt)) {
828         if (TEXT(elt->type)) {
829             DISScreenErase(elt, pixmask | csetmask);
830             TxMsgOK();
831 	    DBChangeText(elt, buf, &PICTURE);
832             DISScreenAdd(elt, pixmask | csetmask);
833         }
834         elt = DBNextofSet(elt);
835     }
836 
837     CP();
838     TxKillLine();
839     CHANGED = TRUE;
840 }  /* end LGMText */
841 
842 
843 /*
844  * This routine modifies the element which contains the point
845  * closest to the first of two specified points so that that point
846  * coincides with the second of the points (if specified).
847  *
848  * Note: it implies knowledge of the database representation by modifying
849  *       the element directly.
850  */
851 LGMPoint()
852 {
853     ELT *elt;
854     POINT *p1, *p2, *p3, *p4;
855     float x1, y1;
856     int length;
857 
858     if (SEQ < 1) {
859 	error("no point specified");
860 	return;
861     }
862 
863     /* find point */
864     DBGravitate(POINTLIST->x, POINTLIST->y, &x1, &y1, &p1, &elt, cset, TRUE);
865 
866     if (DBNullelt(elt) || TEXT(elt->type)) {
867 	error("can't find a good element");
868 	return;
869     }
870 
871     if (SEQ == 1) {		/* wants to delete a point */
872 	length = PTListLength(elt);
873 	if (((elt->type == POLYGON) && (length == 3)) || (length == 2)) {
874 	    error(delmsg);
875 	    return;
876 	}
877     }
878 
879     /* now OK to do whatever */
880     UNForget();
881     CSP();
882 
883     DBClearSet();
884     GRClear(csetmask);
885     DISScreenErase(elt, pixmask);
886     UNRembMod(elt, &PICTURE);
887     if (SEQ > 1) {		/* move a point, not delete */
888 	p2 = PTNextPoint(POINTLIST);
889 	p1->x = p2->x;
890 	p1->y = p2->y;
891 	p2 = PTNextPoint(p2);
892 	if (!Nullpoint(p2)) {
893 	    p3 = PTInit();
894 	    while (!Nullpoint(p2)) {
895 		p4 = PTMakePoint(p2->x, p2->y, &p3);
896 		p2 = PTNextPoint(p2);
897 	    }
898 	    p4->nextpt = p1->nextpt;
899 	    p1->nextpt = p3;
900 	}
901     }
902     else {
903 	PTDeletePoint(p1, &(elt->ptlist));
904     }
905 
906     DISScreenAdd(elt, pixmask | csetmask);
907     DBAddSet(elt);
908 
909     CP();
910     CHANGED = TRUE;
911 }  /* end LGMPoint */
912 
913 
914 /*
915  * This routine allows users to leave gripe messages or report
916  * bugs to the maintainer.  Mail is invoked via the defined constant GRIPE.
917  */
918 LGGripe()
919 {
920     TxPutMsg("mail gripes to opcode@monet");
921 }  /* end LGGripe */
922 
923 
924 /*
925  * This routine controls the size of the point that is displayed.
926  * The sizes available are Artmode in which a small (3 x 3) point is displayed
927  * with no number and regular (non-Artmode).
928  */
929 LGLittlePoint()
930 {
931     register POINT *plist;
932     register sp;
933     register i = 0;
934 
935     GRBlankPoints(POINTLIST);
936     if ((sp = SHOWPOINTS) != 0)		/* turn off show points */
937 	CSP();
938     Artmode = !Artmode;
939 
940     plist = POINTLIST;
941     while (!Nullpoint(plist)) {
942 	GRDisplayPoint(plist->x, plist->y, i++);
943 	plist = PTNextPoint(plist);
944     }
945 
946     if (sp != 0)			/* turn on show points */
947 	LGShowPoints();
948 
949     if (Artmode)
950 	MNUnHighLt(HiArtMode);
951     else
952 	MNHighLt(HiArtMode);
953 }  /* end LGLittlePoint */
954 
955 
956 /*
957  * This routine looks at the command line for parameters to set
958  * the current Font.
959  */
960 LGFont(font)
961 int font;
962 {
963     MNUnHighLt(HiFont[CFONT-1]);
964     CFONT = font;
965     MNHighLt(HiFont[CFONT-1]);
966 }  /* end LGFont */
967 
968 
969 LGFont1()
970 {
971     LGFont(1);
972 }
973 
974 
975 LGFont2()
976 {
977     LGFont(2);
978 }
979 
980 
981 LGFont3()
982 {
983     LGFont(3);
984 }
985 
986 
987 LGFont4()
988 {
989     LGFont(4);
990 }
991 
992 
993 /*
994  * This routine changes the current character size.
995  */
996 LGSize(size)
997 int size;
998 {
999     MNUnHighLt(HiSize[CSIZE-1]);
1000     CSIZE = size;
1001     MNHighLt(HiSize[CSIZE-1]);
1002 }  /* end LGSize */
1003 
1004 
1005 LGSize1()
1006 {
1007     LGSize(1);
1008 }
1009 
1010 
1011 LGSize2()
1012 {
1013     LGSize(2);
1014 }
1015 
1016 
1017 LGSize3()
1018 {
1019     LGSize(3);
1020 }
1021 
1022 
1023 LGSize4()
1024 {
1025     LGSize(4);
1026 }
1027 
1028 
1029 /*
1030  * This routine changes the current stipple pattern.
1031  */
1032 LGStipple(stipple)
1033 int stipple;
1034 {
1035     MNUnHighLt(HiStipple[CSTIPPLE-1]);
1036     CSTIPPLE = stipple;
1037     MNHighLt(HiStipple[CSTIPPLE-1]);
1038 }  /* end LGStipple */
1039 
1040 
1041 LGStipple1()
1042 {
1043     LGStipple(1);
1044 }
1045 
1046 
1047 LGStipple2()
1048 {
1049     LGStipple(2);
1050 }
1051 
1052 
1053 LGStipple3()
1054 {
1055     LGStipple(3);
1056 }
1057 
1058 
1059 LGStipple4()
1060 {
1061     LGStipple(4);
1062 }
1063 
1064 
1065 LGStipple5()
1066 {
1067     LGStipple(5);
1068 }
1069 
1070 
1071 LGStipple6()
1072 {
1073     LGStipple(6);
1074 }
1075 
1076 
1077 LGStipple7()
1078 {
1079     LGStipple(7);
1080 }
1081 
1082 
1083 LGStipple8()
1084 {
1085     LGStipple(8);
1086 }
1087 
1088 
1089 /*
1090  * Toggle line style
1091  */
1092 LGLineStyle()
1093 {
1094     if (SymbolicLines = !SymbolicLines)
1095 	MNUnHighLt(HiLineStyle);
1096     else
1097 	MNHighLt(HiLineStyle);
1098 
1099     SHUpdate();
1100 }  /* end LGLineStyle */
1101 
1102 
1103 LGPan()
1104 {
1105     if (SEQ < 1) {
1106 	error("need one point");
1107 	return;
1108     }
1109 
1110     LGdopan(POINTLIST->x, POINTLIST->y);
1111 }
1112 
1113 
1114 /*
1115  * Make (wx, wy) center of Gremlin window.
1116  */
1117 LGdopan(wx, wy)
1118 float wx, wy;
1119 {
1120     float cx, cy;
1121     register tx, ty;
1122 
1123     CP();						/* eat points first */
1124 
1125     cx = SUN_XORIGIN + (pix_size.r_width >> 1);		/* window x center */
1126     cy = SUN_YORIGIN - (pix_size.r_height >> 1);	/* window y center */
1127 
1128     tx = (int) (wx - cx);				/* x translation */
1129     ty = (int) (wy - cy);				/* y translation */
1130 
1131     tx += (tx < 0) ? -1 : 1;				/* fudge factor */
1132     ty += (ty < 0) ? -1 : 1;
1133 
1134     SUN_XORIGIN += (tx / Gridsize) * Gridsize;
1135     SUN_YORIGIN += (ty / Gridsize) * Gridsize;
1136 
1137     SHUpdate();
1138 }  /* end LGPan */
1139 
1140 
1141 /*
1142  * Pan to absolute center of picture.
1143  * Invoked by the middle button on the PAN icon.
1144  */
1145 LGMPan()
1146 {
1147     register ELT *elt;
1148     register POINT *point;
1149     float minx, miny, maxx, maxy;
1150 
1151     if (DBNullelt(PICTURE)) {
1152 	error("empty picture");
1153 	return;
1154     }
1155 
1156     elt = PICTURE;
1157     minx = maxx = elt->ptlist->x;
1158     miny = maxy = elt->ptlist->y;
1159 
1160     while (!DBNullelt(elt)) {
1161 	point = elt->ptlist;
1162 
1163 	while (!Nullpoint(point)) {
1164 	    MINMAX(minx, maxx, point->x);
1165 	    MINMAX(miny, maxy, point->y);
1166 	    point = PTNextPoint(point);
1167 	}
1168 
1169 	elt = DBNextElt(elt);
1170     }
1171 
1172     LGdopan(maxx - ((maxx - minx) / 2.0), maxy - ((maxy - miny) / 2.0));
1173 }
1174