1 /* @(#)long1.c	1.2	04/18/83
2  *
3  * Copyright -C- 1982 Barry S. Roitblat
4  *
5  *
6  *      This file contains routines to implement the long text commands
7  * of the gremlin PICTURE editor.
8  *
9  */
10 
11 #include "gremlin.h"
12 #include "grem2.h"
13 #include <ctype.h>
14 
15 /* imports from config.c */
16 
17 extern char GMailCommand[];
18 
19 /* imports from graphics files */
20 
21 extern GRVector(), GRArc(), GRPutText();
22 extern GRDisplayPoint(), GRDeletePoint(), GRBlankPoints();
23 extern charxsize, charysize;
24 extern artmode;   /* indication of point display size */
25 
26 /* imports from display.c */
27 
28 extern DISScreenAdd(), DISScreenErase();
29 extern DISDisplaySet(), DISEraseSet(), DISClearSetDisplay();
30 
31 /* imports from database files */
32 
33 extern ELT *DBInit(), *DBCreateElt(), *DBRead();
34 extern DBDelete(), DBSetGravitate(), DBGravitate(), DBClearElt();
35 extern ELT *DBCopy();
36 extern DBXform(), DBChangeBrush();
37 extern DBAddSet(), DBClearSet();
38 extern POINT *PTInit(), *PTMakePoint();
39 extern PTDeletePoint();
40 
41 /* imports from undodb.c */
42 
43 extern UNELT *unlist, *unback;
44 extern UNRembMod();
45 
46 /* imports from short.c */
47 
48 extern SHUpdate();
49 
50 /* imports from menu.c  */
51 
52 extern MNHighLt(), MNUnHighLt();
53 extern MNInitMenu();
54 extern HiMen[], HiFont[], HiBrush[], HiMode[];
55 
56 /* imports from textio.c */
57 
58 extern TxMsgOK(), TxPutString();
59 extern TXFIELD TAlign, TAdjust, TBrush, TFont, TGravity, TCSize;
60 extern TXFIELD TEdit, TJustmode;
61 
62 /* imports from c */
63 
64 extern char *malloc();
65 extern char *strcpy(), *sprintf();
66 
67 /* imports from main.c */
68 
69 extern ELT *PICTURE;                /* current PICTURE database      */
70 extern ELT *cset;                   /* current set database          */
71 extern CBRUSH, CSIZE, CFONT;        /* current brush, size, font     */
72 extern CJUST;                       /* current text justification    */
73 extern Gridon;                      /* grid mode flag                */
74 extern Orientation;                 /* orientation of workspace      */
75 extern Alignment;                   /* point alignment indicator     */
76 extern float PX, PY;                /* cursor coordinates            */
77 extern float Lastx, Lasty;          /* previous cursor coordinates   */
78 extern SEQ;                         /* point sequence number         */
79 extern char *Editfile;              /* current edit file             */
80 extern POINT *POINTLIST, *BACKPOINT;/* accumulated point list        */
81 extern Adjustment;                  /* point adjustment mode         */
82 extern GravityOn;                   /* gravity mode flag             */
83 extern Consume;                     /* point clear flag              */
84 extern CHANGED;                     /* PICTURE changed flag          */
85 extern ELT *MEN[];                  /* pointers for user symbols     */
86 extern POINT MENPOINT[];            /* pointers used fo user symbols */
87 extern cmdbuf[];                    /* line buffer for commands      */
88 extern char *lines[], *fonts[];     /* line and character styles     */
89 extern int lnum[], fnum[];
90 
91 /*      The following is available to the outside world */
92 
93 int bang;
94 
95 
96 /*      The following are defined to allow creation of the command
97  * lookup table.
98  */
99 
100 extern LGAlign(), LGBrush(), LGClearPoints(), LGGripe(), LGLittlePoint(),
101        LGDeletePoint(), LGEdit(), LGFont(), LGIncludeSet(), LGSize(), LGSave(),
102        LGJust(), LGMenu(), LGPoint(), LGPath(), LGQuit(), LGRead(), LGHAdjust(),
103        LGMBrush(), LGMFont(), LGMSize(), LGMText(), LGMPoint(), LGMirror(),
104        LGOrient(), LGVAdjust(), LGText(), LGUndo(), LGWrite(), LGOpoint(),
105        LGShowPoints();
106 
107 /* The following two arrays define the long commands and the routines
108  * that process them.
109  */
110 static char *lcmds[] = {
111     "align",
112     "brush",
113     "buffer",
114     "clearpoints",
115     "deletepoint",
116     "edit",
117     "font",
118     "gripe",
119     "hadjust",
120     "includeset",
121     "justificaion",
122     "littlepoint",
123     "mbrush",
124     "mfont",
125     "mirror",
126     "mpoint",
127     "msize",
128     "mtext",
129     "orient",
130     "path",
131     "point",
132     "quit",
133     "read",
134     "size",
135     "saveset",
136     "showpoints",
137     "text",
138     "undo",
139     "vadjust",
140     "write",
141     "zpoint",
142     NULL};
143 static (*(lrtns[]))() = {
144     LGAlign,			/* align */
145     LGBrush,			/* set brush */
146     LGMenu,			/* select user set buffer */
147     LGClearPoints,		/* clear points */
148     LGDeletePoint,		/* delete a point */
149     LGEdit,			/* edit new file */
150     LGFont,			/* set font */
151     LGGripe,			/* leave a gripe or bug */
152     LGHAdjust,                  /* horizontal adjust */
153     LGIncludeSet,		/* add to set */
154     LGJust,			/* text justification */
155     LGLittlePoint,		/* point size */
156     LGMBrush,			/* modify brush */
157     LGMFont,			/* modify font */
158     LGMirror,			/* mirror current set */
159     LGMPoint,			/* move point */
160     LGMSize,			/* modify size */
161     LGMText,			/* modify text string */
162     LGOrient,			/* change picture orientation */
163     LGPath,			/* set path or toggle search mode */
164     LGOpoint,			/* obtain point from terminal */
165     LGQuit,			/* quit */
166     LGRead,			/* read user sysmbol */
167     LGSize,                     /* character size */
168     LGSave,			/* save current set in file */
169     LGShowPoints,		/* display reference points in set */
170     LGText,			/* input text */
171     LGUndo,			/* undo last command */
172     LGVAdjust,                  /* vertical adjust */
173     LGWrite,			/* write file */
174     LGPoint};			/* create point from cursor */
175 
176 
177 int
178 LGLookup(str, table, next)
179 char str[];			/* Pointer to a string to be looked up */
180 char *(table[]);		/* Pointer to an array of string pointers
181 				 * which are the valid commands.  The strings
182 				 * must be ordered monotonically (i.e. all
183 				 * strings whose first characters are identical
184 				 * must be adjacent in the table).
185 				 */
186 int *next;
187 
188 /*---------------------------------------------------------
189  *	LGLookup searches a table of strings to find one that matches a
190  *	given string.
191  *
192  *	Results:
193  *	If str is an unambiguous abbreviation for one of the entries
194  *	in table, then the index of the matching entry is returned.
195  *	If str is an abbreviation for more than one entry in table,
196  *	then -1 is returned.  If str doesn't match any entry, then
197  *	-2 is returned.
198  *
199  *	Side Effects:	None.
200  *
201  * (Modified from software written by John Ousterhout for the caesar
202  *  program)
203  *---------------------------------------------------------
204  */
205 
206 {
207     /* The search is carried out by using two pointers, one which moves
208      * forward through table from its start, and one which moves backward
209      * through table from its end.  The two pointers mark the range of
210      * strings that match the portion of str that we have scanned.  When
211      * all of the characters of str have been scanned, then the two
212      * pointers better be identical.
213      */
214     char **bot, **top;
215     int match, index;
216 
217     bang = FALSE;
218     match = 0;
219     bot = table;
220     for (top = table; *top != NULL; top++);
221     if (top == bot) return(-2);
222     top--;
223 
224     for (index=0; ; index++)
225     {
226         *next = index;
227 
228 	/* Check for the end of string */
229 
230 	if ( (str[index] == '\0')  ||
231              (str[index] == ',')   ||
232              (str[index] == ' ')       )
233 	{
234 	    if (bot == top) return(match);
235 	    else return(-1);
236 	}
237 	if (str[index] == '!') bang = TRUE;
238 	else
239 	{
240 
241 	/* Move bot up until the string it points to matches str in the
242 	 * index'th position.  Make match refer to the index of bot in table.
243 	 */
244 	    while ((*bot)[index] != str[index])
245 	    {
246 	        if (bot == top) return(-2);
247 	        bot++;
248 	        match++;
249 	    }
250 
251 	/* Move top down until it matches */
252 	    while ((*top)[index] != str[index])
253 	    {
254 	        if (bot == top) return(-2);
255 	        top--;
256 	    }
257         }
258     }
259 }
260 
261 
262 LGCommand(command)
263 char *command;
264 
265 /*---------------------------------------------------------
266  *	This routine reads in, looks up, and executes a long command.
267  *
268  *	Results:	None.
269  *
270  *	Side Effects:
271  *	Depends on the command that is invoked.
272  *---------------------------------------------------------
273  */
274 
275 {
276     int index, next;
277 
278     if (*command == '\0')
279     {
280         Consume = FALSE;
281         return;
282     }
283     index = LGLookup(command, lcmds, &next);
284     if (index >= 0)
285     {
286 
287 	(*(lrtns[index]))(command + next);
288     }
289     else
290     {
291 	if (index == -1) error("command is ambiguous.");
292 	if (index == -2) error("not a command.");
293     }
294 }
295 
296 static char badarg[10] = "bad args";
297 static char noset[15] = "no current set";
298 
299 #define BADNUM -1
300 #define NONUM -2
301 #define Delimiter(c) ((c == '\0') || (c == ' ') || (c == ','))
302 
303 GetNumParm(line, index)
304 char *line;
305 int *index;
306 /*
307  *      This routine trys to interpret the string starting at
308  * line+index as a positive integeral numeric parameter.  The function
309  * returns the numeric equivalent or a negative number it there is some
310  * error in interpreting the string.
311  */
312 
313 {
314 	char num[20];
315 	int i, result;
316 
317 	for (i=0; !Delimiter(*(line + *index)); ++i)
318 	{
319 		num[i] = *(line + *index);
320 		if ( !isdigit(num[i]) ) return (BADNUM);
321 		++(*index);
322 	}  /* end for */
323 	if ( i == 0 ) return(NONUM);
324 	num[i] = '\0';
325 	(void) sscanf(num,"%d",&result);
326 	return(result);
327 }  /* end GetNumParm */
328 
329 
330 LGOpoint(line)
331 char *line;
332 /*
333  *      This routine accepts coordinates from the text terminal
334  * and creates and displays a point from them by passing them
335  * along to LGPoint.
336  *
337  * NOTE: coordinates from the terminal must be non-negative
338  * integers.
339  */
340 
341 {
342 	int index, xcoord, ycoord;
343 
344 	index = 1;
345 	xcoord = GetNumParm(line, &index);
346 	if (xcoord < 0)
347 	{
348 		error(badarg);
349 		return;
350 	}
351 	++index;
352 	ycoord = GetNumParm(line, &index);
353 	if (ycoord < 0)
354 	{
355 		error(badarg);
356 		return;
357 	}
358 	PX = xcoord;
359 	PY = ycoord;
360 	LGPoint(line);
361 }  /* end LGOpoint */
362 
363 
364 LGPoint(line)
365 char *line;
366 /*
367  *      This routine accepts cursor coordinates and creates and
368  * displays points according to the current adjustment and alignment
369  * modes.  Note that alignment and gravity are mutually exclusive
370  * and adjustment takes precedence over either.
371  */
372 
373 {
374 	ELT *temp;
375 	POINT *p1;
376 
377 	temp = DBInit();
378 	if (GravityOn) DBGravitate (PX, PY, &PX, &PY, &p1, &temp, PICTURE);
379 	if (DBNullelt(temp))     /* no gravity in effect */
380 	{
381 		/* Round to nearest alignment boundary */
382 
383 		PX = (float) (((int) (PX / Alignment + 0.5)) * Alignment);
384 		PY = (float) (((int) (PY / Alignment + 0.5)) * Alignment);
385 	}
386 
387 
388 	if (SEQ > 0)      /* this isn't the first point */
389 	{
390 		if (Adjustment == HORZ) PY = Lasty;
391 		if (Adjustment == VERT) PX = Lastx;
392 		if (Adjustment == MAN)
393 			if (fabs(PX - Lastx) > fabs(PY - Lasty)) PY = Lasty;
394 			else   PX = Lastx;
395 	}  /* end if SEQ */;
396 	GRDisplayPoint((int) PX, (int) PY, SEQ, pointstyle);
397 	(void) PTMakePoint(PX, PY, &POINTLIST);
398 	Lastx = PX;
399 	Lasty = PY;
400 	Consume = FALSE;
401 	++SEQ;
402 } /* end Point */
403 
404 CP()
405 /*
406  *      This routine deletes all points from the POINTLIST and
407  * clears them from the display also.
408  */
409 
410 {
411 	POINT *temp;
412 
413 	while ( !Nullpoint(BACKPOINT) )
414 	{
415 		temp = PTNextPoint(BACKPOINT);
416 		free ((char *) BACKPOINT);
417 		BACKPOINT = temp;
418 	}
419        	BACKPOINT = POINTLIST;
420         POINTLIST = PTInit();
421 	SEQ = 0;
422 	GRBlankPoints();
423 }  /* end CP */
424 
425 
426 LGClearPoints(line)
427 char *line;
428 /*
429  *      This routine is a no-op since all points are cleared by default
430  * each time through the interpreter loop unless Consume is false.
431  */
432 {
433 }
434 
435 LGDeletePoint(line)
436 char *line;
437 /*
438  *      This routine removes the last point from the POINTLIST
439  * and erases it from the screen.
440  */
441 
442 {
443 	POINT *pt1, *pt2, *pt3;
444 	float savex, savey;
445 	int i;
446 
447 	if (SEQ == 0)
448 	{
449 		error("no point");
450 		return;
451 	}
452 	pt2 = pt3 = POINTLIST;
453 	while ( !Nullpoint(pt3) )     /* find last point and pointer to it */
454 	{
455 		pt1 = pt2;
456 		pt2 = pt3;
457 		pt3 = PTNextPoint(pt3);
458 	};
459 	SEQ--;
460 	savex = pt2->x;
461 	savey = pt2->y;
462 	GRErasePoint( (int) pt2->x, (int) pt2->y, SEQ);
463 	PTDeletePoint(pt2);
464 	if (SEQ > 0)  /* any points left after deleting */
465 	{             /* then pt1 points to last of them */
466 		Lastx = pt1->x;
467 		Lasty = pt1->y;
468 		pt3 = POINTLIST;   /* redisplay any nearby points */
469 		for (i=0; i<SEQ; ++i)   /* which may have been clobbered by */
470 		{                      /* the erase */
471 			if (fabs(pt3->x - savex) < (2*halfpoint))
472 				if (fabs(pt3->y - savey) < (2*halfpoint))
473 					GRDisplayPoint((int) pt3->x,
474 					               (int) pt3->y,
475 					               i, pointstyle);
476 			pt3 = PTNextPoint(pt3);
477 		}  /* end for */
478 	}
479 	Consume = FALSE;
480 }  /* end DeletePoint */
481 
482 
483 LGShowPoints(line)
484 char *line;
485 /*
486  *      This routine causes the text elements in the current set
487  * to be redrawn using the new font.
488  */
489 
490 {
491 	ELT *elist;
492 	POINT *p1;
493 	int pno;
494 
495 	if (DBNullelt(cset))
496 	{
497 		error(noset);
498 		return;
499 	}
500 	elist = cset;
501 	while ( !DBNullelt(elist) )
502 	{
503 		p1 = elist->ptlist;
504 		pno = 0;
505 		while (!Nullpoint(p1))
506 		{
507 			GRDisplayPoint((int)p1->x, (int)p1->y, pno, pointstyle);
508 			p1 = PTNextPoint(p1);
509 			pno++;
510 		}
511 		elist = DBNextofSet(elist);
512 	}  /* end while */
513 	Consume = FALSE;
514 } /* end ShowPoints */
515 
516 
517 LGText(line)
518 char *line;
519 /*      This routine implements the text commands.  It first looks
520  * for a justification specification (if not specified, default is
521  * centering), and then the text string from the input line.  Text
522  * justification requires a point upon which the text is positioned.
523  */
524 
525 {
526 	ELT *e1;
527 	POINT pos, ppnt, *p1;
528 	int i, j;
529 	char *text;
530 
531 	if (SEQ == 0)
532 	{
533 		error("not enough points");
534 		return;
535 	}
536 	if (*line == '\0') return;   /* no text */
537 	for (; *line == ' '; ++line) /* delete leading blanks */
538 	i = strlen(line);
539 	text = malloc((unsigned) i + 1);
540 	(void) strcpy(text, line);
541 	DBClearSet();         /* clear old current set so this can form */
542 	DISClearSetDisplay();  /* the new one  */
543 	GRsetwmask(textmask | setmask);
544 	ppnt.x = POINTLIST->x;
545 	ppnt.y = POINTLIST->y;
546 	if (SEQ > 1)
547 	{
548 		p1 = PTNextPoint(POINTLIST);
549 		ppnt.x = (POINTLIST->x + p1->x)/2;
550 		ppnt.y = (POINTLIST->y + p1->y)/2;
551 	}
552 
553 	GRPutText(CJUST, &ppnt, CFONT, CSIZE, text, &pos);
554 	p1 = PTInit();
555 	(void) PTMakePoint(ppnt.x, ppnt.y, &p1);
556                   /* end extra positioning points */
557 	(void) PTMakePoint(pos.x, pos.y, &p1);
558 	(void) PTMakePoint(pos.x + i * charxsize / 2, pos.y, &p1);
559 	(void) PTMakePoint(pos.x + i * charxsize, pos.y, &p1);
560 	e1 = DBCreateElt(CJUST, p1, CFONT, CSIZE, text, &PICTURE);
561 	DBAddSet(e1);
562 	CHANGED = TRUE;
563 }  /* end LGText */
564 
565 
566 
567 LGBrush(line)
568 char *line;
569 /*
570  *      This routine sets the current brush to that specified in the
571  * parameter.
572  */
573 
574 {
575 	int newbrush, index;
576 	char string[2];
577 
578 	index = 0;
579 	if (isalpha(*(++line)))
580         {
581               newbrush = LGLookup(line, lines, &index);
582               if (newbrush >= 0) newbrush = lnum[newbrush];
583               else  newbrush = BADNUM;
584         }
585 	else newbrush = GetNumParm(line, &index);
586 	if ( (newbrush == BADNUM) || (newbrush > NBRUSHES) )
587 	{
588 		error(badarg);
589 		return;
590 	}
591 	MNUnHighLt(HiBrush[CBRUSH-1]);
592 	MNHighLt(HiBrush[newbrush-1], hicolor);
593 	CBRUSH = newbrush;
594 	(void) sprintf(string,"%1d",newbrush);
595 	TxPutString(&TBrush,string);
596 	Consume = FALSE;
597 }  /* end LGBrush */
598 
599 
600 
601 LGMBrush(line)
602 char *line;
603 /*
604  *      This routine causes the elements in the current set
605  * to be redrawn using the new brush.
606  */
607 
608 {
609 	ELT *elist;
610 	int new, index;
611 
612 	index = 0;
613 	if (isalpha(*(++line)))
614         {
615               new = LGLookup(line, lines, &index);
616               if (new >= 0) new = lnum[new];
617               else  new = BADNUM;
618         }
619 	else new = GetNumParm(line, &index);
620 	if ( (new < 0) || (new > NBRUSHES) )
621 	{
622 		error(badarg);
623 		return;
624 	}
625 	if (DBNullelt(cset))
626 	{
627 		error(noset);
628 		return;
629 	}
630 	elist = cset;
631 	while ( !DBNullelt(elist) )
632 	{
633 		if (!TEXT(elist->type))
634 		{
635 			DISScreenErase(elist, (linemask | setmask));
636 			DBChangeBrush(elist, new, &PICTURE);
637 			DISScreenAdd(elist, (linemask | setmask));
638 		}  /* end if */
639 		elist = DBNextofSet(elist);
640 	}  /* end while */
641 	CHANGED = TRUE;
642 } /* end MBrush */
643 
644 
645 LGMFont(line)
646 char *line;
647 /*
648  *      This routine causes the text elements in the current set
649  * to be redrawn using the new font.
650  */
651 
652 {
653 	ELT *elist;
654 	int new, index;
655 
656 	index = 0;
657 	if (isalpha(*(++line)))
658         {
659               new = LGLookup(line, fonts, &index);
660               if (new >= 0) new = fnum[new];
661               else  new = BADNUM;
662         }
663 	else new = GetNumParm(line, &index);
664 	if ( (new < 0) || (new > NFONTS) )
665 	{
666 		error(badarg);
667 		return;
668 	}
669 	if (DBNullelt(cset))
670 	{
671 		error(noset);
672 		return;
673 	}
674 	elist = cset;
675 	while ( !DBNullelt(elist) )
676 	{
677 		if (TEXT(elist->type))
678 		{
679 			DISScreenErase(elist, (linemask | setmask));
680 			TxMsgOK();
681 			DBChangeFont(elist, new, elist->size, &PICTURE);
682 			DISScreenAdd(elist, (linemask | setmask));
683 		}  /* end if */
684 		elist = DBNextofSet(elist);
685 	}  /* end while */
686 	CHANGED = TRUE;
687 } /* end MFont */
688 
689 
690 LGMSize(line)
691 char *line;
692 /*
693  *      This routine causes the text elements in the current set
694  * to be redrawn using the new size.
695  *
696  */
697 
698 {
699 	POINT *p1, *p2, pos;
700 	ELT *elist;
701 	int new, index, i, j;
702 
703 	index = 1;
704 	new = GetNumParm(line, &index);
705 	if ( (new < 0) || (new > NSIZES) )
706 	{
707 		error(badarg);
708 		return;
709 	}
710 	if (DBNullelt(cset))
711 	{
712 		error(noset);
713 		return;
714 	}
715 	elist = cset;
716 	while ( !DBNullelt(elist) )
717 	{
718 		if (TEXT(elist->type))
719 		{
720 			DISScreenErase(elist, (linemask | setmask));
721 			TxMsgOK();
722 			UNRembMod(elist,&PICTURE);
723 			elist->size = new;
724 			GRsetwmask(textmask | setmask);
725 			p1 = elist->ptlist;
726 			GRPutText(elist->type, p1, elist->brushf,
727 				  elist->size,elist->textpt, &pos);
728 			i= strlen(elist->textpt);
729 			p2 = PTInit();
730 			(void) PTMakePoint(p1->x, p1->y, &p2);
731                    	   /* end extra positioning points */
732 			(void) PTMakePoint(pos.x, pos.y, &p2);
733 			(void) PTMakePoint(pos.x + i * charxsize / 2,
734 					   pos.y, &p2);
735 			(void) PTMakePoint(pos.x + i * charxsize, pos.y, &p2);
736 			elist->ptlist = p2;
737 		}  /* end if */
738 		elist = DBNextofSet(elist);
739 	}  /* end while */
740 	CHANGED = TRUE;
741 } /* end MSize */
742 
743 
744 LGMText(line)
745 char *line;
746 /*      This routine allows modification of text by replacing
747  * an existing string with a new one, appropriately repositioned
748  */
749 
750 {
751     ELT *e1;
752     POINT pos, ppnt, *p1;
753     int i, j;
754     char *text;
755 
756     i = strlen(++line);
757     text = malloc((unsigned) i + 1);
758     (void) strcpy(text, line);
759     GRsetwmask(textmask | setmask);
760     e1 = cset;
761     while ( !DBNullelt(e1) )
762     {
763         if (TEXT(e1->type))
764         {
765             DISScreenErase(e1, (textmask | setmask));
766             TxMsgOK();
767             UNRembMod(e1, &PICTURE);
768             ppnt.x = e1->ptlist->x;
769             ppnt.y = e1->ptlist->y;
770 
771             GRPutText(e1->type, &ppnt, e1->brushf, e1->size, text, &pos);
772             p1 = PTInit();
773             (void) PTMakePoint(ppnt.x, ppnt.y, &p1);
774                        /* end extra positioning points */
775             (void) PTMakePoint(pos.x, pos.y, &p1);
776             (void) PTMakePoint(pos.x + i * charxsize / 2, pos.y, &p1);
777             (void) PTMakePoint(pos.x + i * charxsize, pos.y, &p1);
778             e1->textpt = text;
779         }  /* end if text */
780         e1 = DBNextofSet(e1);
781     }  /* end while */
782     CHANGED = TRUE;
783 }  /* end LGMText */
784 
785 LGMPoint(line)
786 char *line;
787 /*
788  *      This routine modifies the element which contains the point
789  * closest to the first of two specified points so that that point
790  * coincides with the second of the points (if specified).
791  *
792  *       Note: it implies knowlege of the database representation by modifying
793  *           the element directly.
794  */
795 
796 {
797 	ELT *e1;
798 	POINT *p1, *p2, *p3, *p4;
799 	float x1, y1;
800 
801 	if (SEQ < 1)     /* no points */
802 	{
803 		error("no point specified");
804 		return;
805 	}
806                       /* find point */
807 	DBSetGravitate(POINTLIST->x, POINTLIST->y, &x1, &y1, &p1, &e1, cset);
808 	if ( !DBNullelt(e1) )
809 	{
810 		DBClearSet();
811 		DISClearSetDisplay();
812 		DISScreenErase(e1, (linemask | setmask));
813 		UNRembMod(e1,&PICTURE);
814 		if (SEQ > 1)     /* move a point, not delete */
815 		{
816 			p2 = PTNextPoint(POINTLIST);
817 			p1->x = p2->x;
818 			p1->y = p2->y;
819 			p2 = PTNextPoint(p2);
820 			if (!Nullpoint(p2))
821 			{
822 				p3 = PTInit();
823 				while (!Nullpoint(p2))
824 				{
825 					p4 = PTMakePoint(p2->x, p2->y, &p3);
826 					p2 = PTNextPoint(p2);
827 				}
828 				p4->nextpt = p1->nextpt;
829 				p1->nextpt = p3;
830 			}
831 		}
832 		else
833 		{
834 			PTDeletePoint(p1);
835 		}
836 		DISScreenAdd(e1, (linemask | setmask));
837 		DBAddSet(e1);
838 	}  /* end if !DBNullelt */
839 }  /* end MPOINT */
840 
841 
842 LGGripe(line)
843 char *line;
844 /*
845  *      This routine allows users to leave gripe messages or report
846  * bugs to the maintainer.  Mail is invoked via the defined constant GRIPE.
847  */
848 
849 {
850 	TxClose();     /* Restore text terminal to 'normal' state */
851 	(void) system(GMailCommand);  /* mail complaint */
852 	SHRedis();  /* reclaim terminal */
853 }  /* end Gripe */
854 
855 LGLittlePoint(line)
856 char *line;
857 /*
858  *      This routine controls the size of the point that is displayed
859  * The sizes available are artmode in which a small (3 x 3) point is displayed
860  * with no number and regular (non- artmode).
861  */
862 
863 {
864 
865 	if (artmode) artmode = FALSE;
866 	else artmode = TRUE;
867 	Consume = FALSE;
868 }  /* end Little Point */
869 
870 
871 SetOrient(orient)
872 int orient;
873 /*
874  *      This routine sets up the orientation of the drawing area.
875  */
876 
877 {
878     int x1, x2, y1, y2;      /* used to set grid parameters */
879 
880     if (orient == 0)    /* initialize grid */
881     {
882         x1 = y1 = 0;
883         x2 = 511;
884         y2 = 395;
885     }
886     else
887     {
888         x1 = 116;
889         y1 = 0;
890         x2 = 511;
891         y2 = 483;
892     };
893     MNInitMenu(orient);
894     GRSetGrid(x1, y1, x2, y2, 16);
895 }
896 
897 LGOrient(line)
898 char *line;
899 /*
900  *      This routine toggles the orientation of the drawing area.
901  */
902 
903 {
904 
905     if (Orientation == 1)
906     {
907         Orientation = 0;
908     }
909     else
910     {
911         Orientation = 1;
912     };
913     SetOrient(Orientation);
914     SHUpdate();
915 }  /* end Orient */
916 
917 
918 LGSave(line)
919 char *line;
920 /*
921  *      This routine writes the current set into the specified filename
922  * or to the current Editfile
923  */
924 
925 {
926     FILE *fp, *fopen();
927     char tname[50], filename[100], string[100], *tn, *fn, *wfile;
928     ELT *elist;
929     POINT *plist, pos;
930     int i, space, stat;
931 
932     space = 100;
933     ++line;
934     tn = tname;  fn = filename;
935     (void) sscanf(line, "%s", tname);
936     i = strlen(tname);
937     if (i == 0)       /* no filename */
938     {
939         error("write to where?");
940         return;
941     }
942     stat = PConvertTilde(&tn, &fn, &space);
943     *fn = '\0';
944     if (stat == FALSE)
945     {
946         sprintf(string, "unknown path %s", tname);
947         error(string);
948         return;
949     }
950     if ( !bang )  /* user doesn't insist */
951     {
952         fp = fopen(filename, "r");
953         if ( fp != NULL )
954         {
955              error("file already exists");
956              return;
957         }
958     }
959     fp = fopen(filename, "w");
960     wfile = filename;
961     if (fp == NULL)      /* file error */
962     {
963         (void) sprintf(string,"can't open %s", wfile);
964         error(string);
965         return;
966     };
967     TxPutMsg("writing file...");
968     CHANGED = FALSE;
969     if (SEQ > 0)       /* specified a positioning point */
970     {
971         pos.x = POINTLIST->x;
972         pos.y = POINTLIST->y;
973     }
974     else
975     {
976         if ( !DBNullelt(PICTURE) )
977         {
978             pos.x = PICTURE->ptlist->x;
979             pos.y = PICTURE->ptlist->y;
980         }
981         else
982         {
983             pos.x = pos.y = 0;
984         };
985     }
986     fprintf(fp,"gremlinfile\n");    /* write header */
987     fprintf(fp, "%d %1.2f %1.2f\n", Orientation, pos.x, pos.y);
988     elist = cset;
989     while ( !DBNullelt(elist) )    /* write each element */
990     {
991         fprintf(fp, "%d\n", elist->type);
992         plist = elist->ptlist;
993         while ( !Nullpoint(plist) )  /* write each point */
994         {
995             fprintf(fp, "%1.2f %1.2f\n",plist->x, plist->y);
996             plist = PTNextPoint(plist);
997         }  /* end while plist */
998         fprintf(fp, "%1.2f %1.2f\n", -1.0, -1.0);  /* end pointlist */
999         fprintf(fp, "%d %d\n",elist->brushf, elist->size);
1000         fprintf(fp,"%d %s\n ", strlen(elist->textpt), elist->textpt);
1001         elist = DBNextofSet(elist);
1002     }  /* end while */
1003     fprintf(fp,"%d\n",-1);   /* end of element list */
1004     TxMsgOK();
1005     (void) fclose(fp);
1006 }  /* end LGSave */;
1007