1 /*
2  * @(#)long2.c	1.2	01/03/85
3  *
4  * More routines to implement "long" commands for the SUN Gremlin
5  * picture editor.
6  *
7  * Mark Opperman (opcode@monet.BERKELEY)
8  *
9  */
10 
11 #include <suntool/tool_hs.h>
12 #include <suntool/menu.h>
13 #include <sys/file.h>
14 #include "gremlin.h"
15 #include <ctype.h>
16 
17 /* imports from graphics files */
18 
19 extern GRBlankPoints();
20 extern GRDisplayPoint();
21 extern GRSetTextPos();
22 
23 /* imports from path.c */
24 
25 extern PSetPath();
26 extern PConvertTilde();
27 extern char *PGetPath();
28 
29 /* imports from display.c */
30 
31 extern DISClearSetDisplay();
32 extern DISScreenAdd();
33 extern DISScreenErase();
34 
35 /* imports from database files */
36 
37 extern ELT *DBCopy();
38 extern ELT *DBRead();
39 extern ELT *DBCreateElt();
40 
41 extern POINT *PTMakeTextPoints();
42 extern POINT *PTMakePoint();
43 
44 extern DBAddSet();
45 extern DBChangeBrush();
46 extern DBChangeType();
47 extern DBChangeTypeStipple();
48 extern DBClearElt();
49 extern DBClearSet();
50 extern DBDelete();
51 extern DBGravitate();
52 extern DBXform();
53 
54 /* imports from undodb.c */
55 
56 extern UNELT *unlist;
57 extern UNELT *unback;
58 extern UNForget();
59 
60 /* imports from short.c */
61 
62 extern SHUpdate();
63 extern int adj[];
64 
65 /* imports from text.c */
66 
67 extern TxKillLine();
68 extern TxMsgOK();
69 extern TxPutMsg();
70 
71 /* imports from menu.c  */
72 
73 extern MNHighLt();
74 extern MNUnHighLt();
75 extern HiMode[];
76 
77 /* imports from C */
78 
79 extern char *strcpy();
80 extern char *sprintf();
81 extern char *malloc();
82 extern FILE *fopen();
83 
84 /* imports from sun.c */
85 
86 extern flush_window_input();
87 extern prompt_ok();
88 
89 /* imports from main.c */
90 
91 extern char namestripe[];
92 extern char version[];
93 extern struct tool *tool;
94 extern struct pixfont *text_pf;
95 
96 extern tool_fd;
97 extern menu_fd;
98 
99 extern ELT *PICTURE;                /* current PICTURE database      */
100 extern ELT *cset;                   /* current set database          */
101 extern Orientation;                 /* orientation of workspace      */
102 extern SEARCH;                      /* flag for path search          */
103 extern Alignment;                   /* point alignment indicator     */
104 extern CBRUSH;			    /* current brush                 */
105 extern CSTIPPLE;		    /* current stipple               */
106 extern float PX, PY;                /* cursor coordinates            */
107 extern float Lastx, Lasty;          /* previous cursor coordinates   */
108 extern SEQ;                         /* point sequence number         */
109 extern POINT *POINTLIST, *BACKPOINT;/* accumulated point list        */
110 extern Gridsize;                    /* grid spacing                  */
111 extern Adjustment;                  /* point adjustment mode         */
112 extern CHANGED;                     /* PICTURE changed flag          */
113 extern ELT *MEN[];                  /* pointers for user symbols     */
114 extern POINT MENPOINT[];            /* pointers used fo user symbols */
115 extern newfileformat;		    /* TRUE if using SUN file format */
116 
117 /*  imports from long1.c         */
118 
119 extern CSP();
120 extern CP();
121 
122 char *Editfile;
123 char *eltnames[] = {
124     "BOTLEFT", "BOTRIGHT", "CENTCENT", "VECTOR", "ARC", "CURVE", "POLYGON",
125     "", "", "",
126     "TOPLEFT", "TOPCENT", "TOPRIGHT", "CENTLEFT", "CENTRIGHT", "BOTCENT"
127 };
128 
129 char nowrite_msg[] = "NO WRITE SINCE LAST CHANGE!       Press left button to \
130 edit new file, middle or right button to cancel.";
131 char filexists_msg[] = "FILE EXISTS!  Press left button to overwrite, middle \
132 or right button to cancel.";
133 static char quit_msg[] = "NO WRITE SINCE LAST CHANGE!       Press left button \
134 to confirm quit, middle or right button to cancel.";
135 static char quit2_msg[] = "Press left button to confirm quit, middle or right \
136 button to cancel.";
137 
138 #define BADNUM -1
139 #define NONUM -2
140 
141 static char badarg[] = "bad args";
142 
143 
144 /*
145  * This routine creates and displays a POLYGON element from the
146  * points previously specified.
147  */
148 static
149 LGDrawPolygon(bordered)
150 int bordered;
151 {
152     register POINT *p1, *p2;
153     POINT *p0, *plist;
154     ELT *e1;
155     char *txt;
156 
157     if (SEQ < 3) {      /* not enough points */
158 	error("need at least 3 points");
159 	return;
160     }
161 
162     UNForget();
163 
164     DISClearSetDisplay();
165     DBClearSet();
166 
167     plist = PTInit();
168     p0 = p1 = POINTLIST;
169     (void) PTMakePoint(p1->x, p1->y, &plist);
170     p2 = PTNextPoint(p1);
171 
172     while (!Nullpoint(p2)) {
173 	(void) PTMakePoint(p2->x, p2->y, &plist);
174 	p1 = p2;
175 	p2 = PTNextPoint(p1);
176     }
177 
178     txt = malloc(1);
179     *txt = '\0';
180     e1 = DBCreateElt(POLYGON, plist, bordered ? CBRUSH : 0, CSTIPPLE,
181 								txt, &PICTURE);
182     DISScreenAdd(e1, pixmask | csetmask);
183     DBAddSet(e1);
184 
185     CP();
186     CHANGED = TRUE;
187 }  /* end LGDrawPolygon */
188 
189 
190 LGBPolygon()
191 {
192     LGDrawPolygon(TRUE);
193 }
194 
195 
196 LGPolygon()
197 {
198     LGDrawPolygon(FALSE);
199 }
200 
201 
202 /*
203  * Modify elements in current set to POLYGON type with the indicated
204  * brush.  POLYGONs, CURVEs and VECTORs can be modified to either
205  * bordered or unbordered POLYGONs.
206  */
207 LGModifyPolygon(brush)
208 int brush;		/* zero for unbordered */
209 {
210     register ELT *elt;
211 
212     if (DBNullelt(cset)) {
213 	error("no current set");
214 	return;
215     }
216 
217     UNForget();
218     CSP();
219 
220     elt = cset;
221     while (!DBNullelt(elt)) {
222 	if (elt->type == POLYGON) {
223 	    DISScreenErase(elt, pixmask | csetmask);
224 	    DBChangeBrush(elt, brush, &PICTURE);
225 	    DISScreenAdd(elt, pixmask | csetmask);
226 	}
227 	else if ((elt->type == VECTOR) || (elt->type == CURVE)) {
228 	    DISScreenErase(elt, pixmask | csetmask);
229 	    if (brush != 0)			/* bordered polygon */
230 		DBChangeTypeStipple(elt, POLYGON, CSTIPPLE, &PICTURE);
231 	    else				/* unbordered polygon */
232 		DBChangeTypeBrushStipple(elt, POLYGON, 0, CSTIPPLE, &PICTURE);
233 	    DISScreenAdd(elt, pixmask | csetmask);
234 	}
235 
236 	elt = DBNextofSet(elt);
237     }
238 
239     CP();
240     CHANGED = TRUE;
241 }  /* end LGModifyPolygon */
242 
243 
244 /*
245  * Modify curves, vectors and polygons in the current set
246  * to bordered polygons.
247  */
248 LGMBPolygon()
249 {
250     LGModifyPolygon(CBRUSH);
251 }
252 
253 
254 /*
255  * Modify curves, vectors and polygons in the current set
256  * to unbordered polygons.
257  */
258 LGMPolygon()
259 {
260     LGModifyPolygon(0);
261 }
262 
263 
264 /*
265  * Modify curves and polygons in the current set to vectors.
266  */
267 LGMVector()
268 {
269     register ELT *elt;
270 
271     if (DBNullelt(cset)) {
272 	error("no current set");
273 	return;
274     }
275 
276     UNForget();
277     CSP();
278 
279     elt = cset;
280     while (!DBNullelt(elt)) {
281 	if (elt->type == POLYGON) {
282 	    DISScreenErase(elt, pixmask | csetmask);
283 	    if (elt->brushf != 0)
284 		DBChangeTypeStipple(elt, VECTOR, 0, &PICTURE);
285 	    else
286 		DBChangeTypeBrushStipple(elt, VECTOR, CBRUSH, 0, &PICTURE);
287 	    DISScreenAdd(elt, pixmask | csetmask);
288 	}
289 	else if (elt->type == CURVE) {
290 	    DISScreenErase(elt, pixmask | csetmask);
291 	    DBChangeType(elt, VECTOR, &PICTURE);
292 	    DISScreenAdd(elt, pixmask | csetmask);
293 	}
294 	elt = DBNextofSet(elt);
295     }
296 
297     CP();
298     CHANGED = TRUE;
299 }
300 
301 
302 /*
303  * Modify vectors and polygons in the current set to curves.
304  */
305 LGMCurve()
306 {
307     register ELT *elt;
308 
309     if (DBNullelt(cset)) {
310 	error("no current set");
311 	return;
312     }
313 
314     UNForget();
315     CSP();
316 
317     elt = cset;
318     while (!DBNullelt(elt)) {
319 	if (elt->type == VECTOR) {
320 	    DISScreenErase(elt, pixmask | csetmask);
321 	    DBChangeType(elt, CURVE, &PICTURE);
322 	    DISScreenAdd(elt, pixmask | csetmask);
323 	}
324 	else if (elt->type == POLYGON) {
325 	    DISScreenErase(elt, pixmask | csetmask);
326 	    if (elt->brushf != 0)		/* bordered polygon */
327 		DBChangeTypeStipple(elt, CURVE, 0, &PICTURE);
328 	    else				/* unbordered polygon */
329 		DBChangeTypeBrushStipple(elt, CURVE, CBRUSH, 0, &PICTURE);
330 	    DISScreenAdd(elt, pixmask | csetmask);
331 	}
332 	elt = DBNextofSet(elt);
333     }
334 
335     CP();
336     CHANGED = TRUE;
337 }
338 
339 
340 LGIncludeSet()
341 /*
342  * This routine adds all elements selected by points in POINTLIST
343  * to the current set.  It does not remove previously selected elements.
344  */
345 {
346     POINT *p1, *p2;
347     ELT *e1;
348     float n1, n2;
349 
350     if (DBNullelt(PICTURE))
351 	return;
352 
353     if (SEQ == 0) {	/* no points: entire picture becomes current set */
354         e1 = PICTURE;
355         while (!DBNullelt(e1)) {
356 	    if (!DBInCset(e1)) {		/* not now in current set */
357 		DBAddSet(e1);			/* add it to current set */
358 		DISScreenAdd(e1, csetmask);	/* and display it */
359 	    }
360             e1 = DBNextElt(e1);
361         }
362     }
363     else {
364         p1 = POINTLIST;
365 
366 	/* for each user point */
367         while (!Nullpoint(p1)) {
368 
369 	    /* find closest element */
370             DBGravitate(p1->x, p1->y, &n1, &n2, &p2, &e1, PICTURE, FALSE);
371 
372 	    /* if something's close and its not already in the current set */
373             if (!DBNullelt(e1) && !DBInCset(e1)) {
374                 DBAddSet(e1);				/* add it */
375                 DISScreenAdd(e1, csetmask);		/* and display it */
376             }
377             p1 = PTNextPoint(p1);
378         }
379     }
380 
381     CP();
382 } /* end LGIncludeSet */
383 
384 
385 /*
386  * This routine implements the menu command.  The contents of
387  * the specified user menu item is copied into the PICTURE transformed
388  * to the positioning point.
389  */
390 LGGet(buffer)
391 int buffer;
392 {
393 
394     ELT *elist, *e1;
395     POINT *plist;
396     int symbol, index;
397     float xmat[3][2];
398 
399     if (SEQ < 1) {
400         error("no positioning point");
401         return;
402     }
403 
404     UNForget();
405     buffer--;     /* users inputs number between 1 and N, actual
406                      buffer number is between 0 and N-1 */
407 
408     xmat[0][0] = xmat[1][1] = 1;    /* create transformation matrix */
409     xmat[0][1] = xmat[1][0] = 0;    /* for copy into PICTURE        */
410     plist = POINTLIST;
411 
412     while (!Nullpoint(plist)) {
413 	DISClearSetDisplay();
414         DBClearSet();
415         xmat[2][0] = plist->x - (MENPOINT[buffer]).x;
416         xmat[2][1] = plist->y - (MENPOINT[buffer]).y;
417         elist = MEN[buffer];
418 
419         while (!DBNullelt(elist)) { /* copy buffer to picture */
420             e1 = DBCopy(elist, xmat, &PICTURE);
421             DISScreenAdd(e1, pixmask | csetmask);
422             DBAddSet(e1);
423             elist = DBNextElt(elist);
424         }
425 
426         plist = PTNextPoint(plist);
427     }
428 
429     CP();
430     CHANGED = TRUE;
431 }  /* end LGGet */
432 
433 
434 LGGet1()
435 {
436     LGGet(1);
437 }
438 
439 
440 LGGet2()
441 {
442     LGGet(2);
443 }
444 
445 
446 LGGet3()
447 {
448     LGGet(3);
449 }
450 
451 
452 LGGet4()
453 {
454     LGGet(4);
455 }
456 
457 
458 /*
459  * This routine reads in the specified filename (command line) to the
460  * selected user symbol or current set if no user symbol is selected.  If
461  * no filename is specified, the current set is copied to the user symbol;
462  */
463 LGRead()
464 {
465     POINT pos, ppos;
466     ELT *elist, *e1;
467     char tname[TEXT_BUFMAX];
468     float xmat[3][2];
469     int orient;
470 
471     text_getvalue(&tname[0]);
472     if (*tname == '\0') {
473         error("read from where?");
474         return;
475     }
476 
477     elist = DBRead(tname, &orient, &pos);	/* read file */
478     if (elist == (ELT *) NULL)
479 	return;
480 
481     UNForget();					/* forget changes registered */
482 						/* by DBRead */
483     if (SEQ < 1) {				/* no positioning point */
484         ppos.x = pos.x;
485         ppos.y = pos.y;
486     }
487     else {
488         ppos.x = POINTLIST->x;
489         ppos.y = POINTLIST->y;
490     }
491 
492     xmat[0][0] = xmat[1][1] = 1;		/* set up matrix to copy to */
493     xmat[0][1] = xmat[1][0] = 0;		/* appropriate place in */
494     xmat[2][0] = ppos.x - pos.x;		/* picture as current set */
495     xmat[2][1] = ppos.y - pos.y;
496     DISClearSetDisplay();
497     DBClearSet();
498 
499     while (!DBNullelt(elist)) {
500         e1 = DBCopy(elist, xmat, &PICTURE);
501         DISScreenAdd(e1, pixmask | csetmask);
502         DBAddSet(e1);
503         e1 = DBNextElt(elist);
504         DBClearElt(elist);
505         elist = e1;
506     }
507 
508     CHANGED = TRUE;
509     TxKillLine();
510     CP();
511 }  /* end LGRead */
512 
513 
514 /*
515  * This routine reads in a new PICTURE for editing
516  */
517 LGEdit()
518 {
519     FILE *fp, *POpen();
520     POINT pos;
521     ELT *e1;
522     char *prealname, *tn, tname[TEXT_BUFMAX];
523     int fd;
524 
525     text_getvalue(&tname[0]);
526 
527     if (CHANGED) {
528 	if (!prompt_ok(menu_fd, nowrite_msg)) {
529 	    return;
530 	}
531     }
532 
533     DISClearSetDisplay();
534     DBClearSet();
535 
536     while (!DBNullelt(PICTURE)) {	/* clear current PICTURE */
537         e1 = DBNextElt(PICTURE);
538         DBClearElt(PICTURE);
539         PICTURE = e1;
540     }
541 
542     tn = tname;
543 
544     POINTLIST = PTInit();		/* initialize globals */
545     SEQ = 0;
546     CHANGED = FALSE;
547     (void) strcpy(namestripe, version);
548 
549     if (*tname != '\0') {		/* filename present */
550         fp = POpen(tname, &prealname, SEARCH);
551 
552         if (fp == NULL) {
553             PICTURE = DBInit();
554 	    strcpy(Editfile, tname);
555 	    strcat(namestripe, tname);
556 	    error("creating new file");
557         }
558         else {
559 	    fclose(fp);
560 	    strcpy(Editfile, prealname);
561 	    strcat(namestripe, prealname);
562             PICTURE = DBRead(tname, &Orientation, &pos);
563 	    if ((fd = open(prealname, O_WRONLY | O_APPEND)) < 0)
564 		strcat(namestripe, " (read only)");
565 	    else
566 		close(fd);
567         }
568     }
569     else {				/* create new file */
570 	PICTURE = DBInit();
571 	(void) strcat(namestripe, "new file");
572 	(void) strcpy(Editfile, "");
573     }
574 
575     tool_display(tool);
576 
577     unlist = unback = NULL;
578     CP();
579     SHUpdate();      /* display new picture */
580     TxKillLine();
581 }  /* end LGEdit */
582 
583 
584 /*
585  * This routine (re) displays the points in the back-up pointlist
586  */
587 static
588 restorepoints()
589 {
590 
591     register POINT *plist, *pl1;
592     register i;
593 
594     GRBlankPoints(POINTLIST);
595     plist = BACKPOINT;
596 
597     for (i=0; !Nullpoint(plist); ++i) {
598         Lastx = plist->x;
599         Lasty = plist->y;
600         GRDisplayPoint(plist->x, plist->y, i);
601         plist = PTNextPoint(plist);
602     }
603 
604     pl1 = POINTLIST;
605     POINTLIST = BACKPOINT;
606     SEQ = i;
607     BACKPOINT = pl1;
608 }  /* end restorepoints */
609 
610 
611 /*
612  * This routine uses the information in the undo database to reconstruct
613  * the PICTURE as it was before the last command.  The undo database is set
614  * so that the next undo would nullify this one.
615  * An undo of an Add is to delete the new element.
616  * Add the old element back to undo a delete.
617  * Modified elements are undone by copying the old element into the database
618  * in place of the modified element.
619  */
620 LGUndo()
621 {
622     UNELT *fix, *temp;
623     ELT *(*e1);
624 
625     fix = unlist;	/* initialize unlist so that undo-ing can */
626     unlist = NULL;	/* add items to properly undo the undo */
627 
628     if (fix == NULL) {
629         fix = unback;
630         unback = NULL;
631     }
632 
633     DISClearSetDisplay();
634     DBClearSet();
635 
636     while (fix != NULL) {
637         switch (fix->action) {
638 	    case ADD:
639 		DISScreenErase(fix->newelt, pixmask);
640                 TxMsgOK();
641                 restorepoints();
642                 DBDelete(fix->newelt, fix->dbase);
643                 temp = fix->nextun;
644                 free((char *) fix);
645                 fix = temp;
646                 break;
647 	    case DELETE:
648 		fix->action = ADD;   /* create undo unelt */
649                 fix->newelt = fix->oldelt;
650                 fix->oldelt = NULL;
651                 fix->newelt->nextelt = PICTURE;
652                 restorepoints();
653                 DISScreenAdd(fix->newelt, pixmask | csetmask);
654                 DBAddSet(fix->newelt);
655                 PICTURE = fix->newelt;    /* put in database */
656                 temp = fix->nextun;
657                 fix->nextun = unlist;     /* link into unlist */
658                 unlist = fix;
659                 fix = temp;
660                 break;
661 	    case MOD:
662 		DISScreenErase(fix->newelt, pixmask);
663                 TxMsgOK();
664                 restorepoints();
665                 DISScreenAdd(fix->oldelt, pixmask | csetmask);
666                 DBAddSet(fix->oldelt);
667                 e1 = fix->dbase;
668 
669                 while (*e1 != fix->newelt) { /* find elt to replace */
670                     e1 = &(DBNextElt((*e1)));
671                 }
672 
673                 fix->oldelt->nextelt = DBNextElt((*e1));
674                 *e1 = fix->oldelt;
675                 fix->oldelt = fix->newelt;
676                 fix->newelt = *e1;     /* create undo unelt */
677                 temp = fix->nextun;
678                 fix->nextun = unlist;
679                 unlist = fix;     /* link into unlist */
680                 fix = temp;
681                 break;
682         }
683     }
684 }  /* end LGUndo */
685 
686 
687 /*
688  * Write elements from elist to filename.
689  * If setonly is true, elements are taken from the "setnext"
690  * pointer; otherwise, elements are taken from "nextelt".
691  * Ie., the current set is written with setonly = TRUE and
692  * the complete picture is written with setonly = FALSE.
693  */
694 static
695 LGWriteSet(elist, filename, setonly)
696 ELT *elist;
697 char *filename;
698 int setonly;
699 {
700     FILE *fp;
701     POINT *plist, pos;
702     char string[256];
703 
704     fp = fopen(filename, "w");
705     if (fp == NULL) {
706         (void) sprintf(string, "can't open %s", filename);
707         error(string);
708         return;
709     }
710 
711     TxPutMsg("writing file...");
712     UNForget();
713     CHANGED = FALSE;
714 
715     if (SEQ > 0) {			/* specified a positioning point */
716         pos.x = POINTLIST->x;
717         pos.y = POINTLIST->y;
718     }
719     else {
720         if (!DBNullelt(elist)) {
721             pos.x = elist->ptlist->x;
722             pos.y = elist->ptlist->y;
723         }
724         else {
725             pos.x = pos.y = 0.0;
726         }
727     }
728 
729     if (newfileformat)
730 	fprintf(fp, "sungremlinfile\n");		/* write header */
731     else
732 	fprintf(fp, "gremlinfile\n");			/* write header */
733     fprintf(fp, "%d %1.2f %1.2f\n", Orientation, pos.x, pos.y);
734 
735     while (!DBNullelt(elist)) {			/* write each element */
736 	if (newfileformat)
737 	    fprintf(fp, "%s\n", eltnames[elist->type]);
738 	else
739 	    fprintf(fp, "%d\n", elist->type);
740 
741         plist = elist->ptlist;
742 
743         while (!Nullpoint(plist)) {		/* write each point */
744             fprintf(fp, "%1.2f %1.2f\n", plist->x, plist->y);
745             plist = PTNextPoint(plist);
746         }
747 
748 	if (newfileformat)
749 	    fprintf(fp, "*\n");			/* end pointlist */
750 	else
751 	    fprintf(fp, "-1.00 -1.00\n");	/* end pointlist */
752 
753         fprintf(fp, "%d %d\n", elist->brushf, elist->size);
754         fprintf(fp,"%d %s\n", strlen(elist->textpt), elist->textpt);
755         elist = setonly ? DBNextofSet(elist) : DBNextElt(elist);
756     }
757     fprintf(fp, "-1\n");
758 
759     (void) fclose(fp);
760     TxMsgOK();
761     TxKillLine();
762     CP();
763 }  /* end LGWriteSet */
764 
765 
766 /*
767  * This routine writes the current set into the specified filename
768  */
769 LGSave()
770 {
771     FILE *fp;
772     char tname[TEXT_BUFMAX], filename[TEXT_BUFMAX], *tn, *fn;
773     int space, stat;
774 
775     space = TEXT_BUFMAX;
776     text_getvalue(&tname[0]);
777     tn = tname;
778     fn = filename;
779 
780     if (*tname == '\0') {
781         error("write to where?");
782         return;
783     }
784 
785     stat = PConvertTilde(&tn, &fn, &space);
786     *fn = '\0';
787 
788     if (stat == FALSE) {
789         sprintf(filename, "unknown path %s", tname);
790         error(filename);
791         return;
792     }
793 
794     fp = fopen(filename, "r");
795     if (fp != NULL) {
796 	 if (!prompt_ok(menu_fd, filexists_msg)) {
797 	    fclose(fp);
798 	    return;
799 	}
800 	else
801 	    fclose(fp);
802     }
803 
804     LGWriteSet(cset, filename, TRUE);
805 }  /* end LGSave */;
806 
807 
808 /*
809  * This routine writes the current PICTURE into the specified filename
810  * or to the current Editfile
811  */
812 LGWrite()
813 {
814     FILE *fp;
815     char tname[TEXT_BUFMAX], filename[TEXT_BUFMAX], *tn, *fn;
816     int space, stat;
817 
818     space = TEXT_BUFMAX;
819     text_getvalue(&tname[0]);
820     tn = tname;
821     fn = filename;
822 
823     if (tname[0] == '\0') {
824         if (Editfile[0] == '\0') {
825             error("write to where?");
826             return;
827         }
828 	strcpy(filename, Editfile);
829     }
830     else {
831 	stat = PConvertTilde(&tn, &fn, &space);
832 	*fn = '\0';
833 	if (stat == FALSE) {
834 	    sprintf(filename, "unknown path %s", tname);
835 	    error(filename);
836 	    return;
837 	}
838 	fp = fopen(filename, "r");
839 	if (fp != NULL) {
840 	    if (!prompt_ok(menu_fd, filexists_msg)) {
841 		fclose(fp);
842 		return;
843 	    }
844 	    else
845 		fclose(fp);
846 	}
847     }
848 
849     LGWriteSet(PICTURE, filename, FALSE);
850 }  /* end LGWrite */;
851 
852 
853 /*
854  * This routine terminates the editor.  The terminal states for the text
855  * terminal and the graphics display are restored and an EXIT is performed.
856  */
857 LGQuit()
858 {
859     if (prompt_ok(tool_fd, CHANGED ? quit_msg : quit2_msg))
860 	exit(0);
861 }  /* end LGQuit */
862 
863 
864 /*
865  * Horizontal Adjust -
866  * This routine toggles the adjustment mode.
867  */
868 LGHAdjust()
869 {
870     if (Adjustment == HORZ) {
871         MNUnHighLt(HiMode[adj[HORZ]]);
872         Adjustment = NOADJ;
873     }
874     else {
875 	if (Adjustment != NOADJ)
876 	    MNUnHighLt(HiMode[adj[Adjustment]]);
877         MNHighLt(HiMode[adj[HORZ]]);
878         Adjustment = HORZ;
879     }
880 }  /* end LGHAdjust */
881 
882 
883 /*
884  * Vertical Adjust -
885  * This routine toggles the adjustment mode.
886  */
887 LGVAdjust()
888 {
889     if (Adjustment == VERT) {
890         MNUnHighLt(HiMode[adj[VERT]]);
891         Adjustment = NOADJ;
892     }
893     else {
894 	if (Adjustment != NOADJ)
895 	    MNUnHighLt(HiMode[adj[Adjustment]]);
896         MNHighLt(HiMode[adj[VERT]]);
897         Adjustment = VERT;
898     }
899 }  /* end LGVAdjust */
900 
901 
902 /*
903  * This local routine returns 1 if x >= 0
904  * otherwise returns -1
905  */
906 static
907 sign(x)
908 float x;
909 {
910     return((x >= 0) ? 1 : -1);
911 }
912 
913 
914 /*
915  * This routine is called by all mirroring routines to effect the
916  * transformation specified by xmat.
917  */
918 static
919 mirror(xmat)
920 float xmat[3][2];
921 {
922     register ELT *elt;
923     POINT pt, pos, *p1, *p2;
924     int i, j;
925 
926     UNForget();
927     elt = cset;
928     CSP();
929 
930     while (!DBNullelt(elt)) {
931         DISScreenErase(elt, pixmask | csetmask);
932         TxMsgOK();
933         DBXform(elt, xmat, &PICTURE);
934         if (TEXT(elt->type)) {
935 	    GRSetTextPos(elt->textpt, elt->type, elt->brushf, elt->size,
936 						    elt->ptlist, &pos);
937             elt->ptlist = PTMakeTextPoints(elt->textpt, elt->brushf, elt->size,
938 						    elt->ptlist, &pos);
939 	    DISScreenAdd(elt, pixmask | csetmask);
940         }
941         else {
942             if ((elt->type == ARC) && (elt->size > 0) &&
943 					(xmat[0][0] * xmat[1][1] < 0)) {
944 		/* arcs require special handling */
945                 /* but, circles OK and mirror in both directions OK */
946 	        /* otherwise, swap starting and ending points of arc */
947                 p1 = PTNextPoint(elt->ptlist);
948                 p2 = PTNextPoint(p1);
949                 pt.x = p1->x;
950                 pt.y = p1->y;
951                 p1->x = p2->x;
952                 p1->y = p2->y;
953                 p2->x = pt.x;
954                 p2->y = pt.y;
955 	    }
956             DISScreenAdd(elt, pixmask | csetmask);
957         }
958         elt = DBNextofSet(elt);
959     }
960     CP();
961 }  /* end mirror */
962 
963 
964 /*
965  * This routine mirrors the elements in the current set VERTICALLY
966  * The mirroring is accomplished by defining a transformation
967  * matrix and calling DBXform.
968  */
969 LGVMirror()
970 {
971     float xmat[3][2];
972 
973     if (SEQ < 1) {      /* not enough points */
974         error("not enough points");
975         return;
976     }
977 
978     if (DBNullelt(cset)) {
979         error("no current set");
980         return;
981     }
982 
983     /* create transformation matrix to translate set to origin,
984        perform the mirroring and translate back */
985 
986     xmat[0][0] = -1.0;
987     xmat[1][1] = 1.0;
988     xmat[1][0] = xmat[0][1] = xmat[2][1] = 0.0;
989     xmat[2][0] = 2.0 * POINTLIST->x;
990 
991     mirror(xmat);
992     CHANGED = TRUE;
993 }  /* end LGVMirror */
994 
995 
996 /*
997  * This routine mirrors the elements in the current set HORIZONTALLY
998  * The mirroring is accomplished by defining a transformation
999  * matrix and calling DBXform.
1000  */
1001 LGHMirror()
1002 {
1003     float xmat[3][2];
1004 
1005     if (SEQ < 1) {      /* not enough points */
1006         error("not enough points");
1007         return;
1008     }
1009 
1010     if (DBNullelt(cset)) {
1011         error("no current set");
1012         return;
1013     }
1014 
1015     /* create transformation matrix to translate set to origin,
1016        perform the mirroring and translate back */
1017 
1018     xmat[0][0] = 1.0;
1019     xmat[1][1] = -1.0;
1020     xmat[1][0] = xmat[0][1] = xmat[2][0] = 0.0;
1021     xmat[2][1] = 2.0 * POINTLIST->y;
1022 
1023     mirror(xmat);
1024     CHANGED = TRUE;
1025 }  /* end LGHMirror */
1026 
1027 
1028 /*
1029  * This routine looks at the command line for parameters to set
1030  * the current search path.
1031  */
1032 LGPath()
1033 {
1034     char buf[TEXT_BUFMAX];
1035     char buf2[TEXT_BUFMAX];
1036     register i, i2;
1037 
1038     i = i2 = -1;
1039     text_getvalue(&buf[0]);
1040     while (buf[++i]) {
1041 	if (buf[i] != ' ')
1042 	    buf2[++i2] = buf[i];
1043     }
1044     buf2[++i2] = '\0';
1045 
1046     if (*buf2 == '\0')
1047 	TxPutMsg(PGetPath());     /* no arguments */
1048     else {
1049         SEARCH = TRUE;
1050         PSetPath(buf2);
1051     }
1052 
1053     TxKillLine();
1054 }  /* end LGPath */
1055 
1056 
1057 /*
1058  * Sometimes it's important to do nothing.
1059  */
1060 nop()
1061 {
1062 }
1063