argv@island.uu.net (Dan Heller) (07/22/89)
Submitted-by: Mark Moraes <moraes@ai.toronto.edu> Posting-number: Volume 4, Issue 72 Archive-name: xpic/part07 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 7 (of 15)." # Contents: xpic/ask.c xpic/event.c xpic/handlers.c xpic/obj_block.c # xpic/obj_line.c # Wrapped by moraes@neat.ai on Thu Jul 13 22:36:08 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'xpic/ask.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xpic/ask.c'\" else echo shar: Extracting \"'xpic/ask.c'\" \(9084 characters\) sed "s/^X//" >'xpic/ask.c' <<'END_OF_FILE' X/* This file contains code from the JOVE screen editor */ X X/************************************************************************ X * JOVE is Copyright (C) 1986 by Jonathan Payne. JOVE is * X * provided to you without charge, and with no warranty. You may give * X * away copies of JOVE, including sources, provided that this notice is * X * included in all the files. * X ************************************************************************/ X X/* X * Modified by Mark Moraes for use in a widget for the X Windows System X * Version 11. This file is still independent of the X Windows System. X */ X X/* The routines in this file perform Tenex-style filename completion. X The routine to be called is X f_complete(buf, cursorposition, cols, c) X char *buf; X X where 'buf' contains the filename so far. 'cursorposition' is the X location of the cursor in 'buf' - it should be at the end of the 'buf'. X 'cols' is the width of the screen used for typeout, (the listing of files) X and 'c' is one of ' ', '\t', and '?'. For the former two, f_complete X attempts to complete the name in 'buf', and for the latter, it X lists out the files which match the name so far using the typeout X routines. X X It requires two sets of external routines to do this - insert_s(), X add_mess() and rbell() are used for completion, and TOstart(), X Typeout(), and TOstop() are used for typeout. These are described X below X */ X#include <ctype.h> X#include <signal.h> X#include <varargs.h> X#include <stdio.h> X#ifdef XWINDOWS X# include <X11/Xos.h> X#else X# include <strings.h> X# include <sys/types.h> X#endif X#include <sys/stat.h> X#ifdef DIRENT X# include <sys/param.h> X# include <dirent.h> X# ifndef DIRSIZE X# define DIRSIZE(entry) DIRSIZ X# endif X# ifndef direct X# define direct dirent X# endif X#else X# include <sys/dir.h> X# define DIRSIZE(entry) DIRSIZ(entry) X#endif DIRENT X X#define FILESIZE 128 X#define TRUE 1 X#define FALSE 0 X#define min(x, y) ((x) < (y) ? (x) : (y)) X#define max(x, y) ((x) > (y) ? (x) : (y)) X Xstatic char *linebuf; Xstatic int curchar; Xstatic int maxCols; X Xextern char *malloc(); Xextern char *realloc(); X X/**********************External functions **********************************/ X/* insert_s(at, s, len, curpos) char *at, *s; int len; int *curpos; X * deletes from 'at' to the end of the line, and inserts the first len X * characters of 's' there. It returns 'curpos' as the new end of the X * string being edited - the cursor should now be there X */ Xextern void insert_s(); X X/* add_mess(s) char *s; X * inserts 's' at the end of the buffer, then waits a respectable X * interval, deletes 's', and returns X */ Xextern void add_mess(); X X/* rbell() X * Rings a bell or attracts the user's attention in some other way X */ Xextern void rbell(); X X/* TOstart(s) char *s; X * Starts the typeout, and prints 's' as a title. Typeout is some X * sort of overlay 'window' or something, for temporary output, X * which can popup, and vanish after the user has read it. X */ Xextern TOstart(); X X/* Typeout(fmt, args) char *fmt; va_dcl args; X * Is like printf() - prints args according to format 'fmt'. X * Is a <varargs> routine X */ Xextern Typeout(); X X/* TOstop() X * End of typeout - this performs some sort of wait() X * - like for a keypress or a mouse click. It then cleans up X * the typeout and returns. X */ Xextern TOstop(); X Xchar *xmalloc(n) X{ X extern char *malloc(); X char *p = malloc((unsigned) n); X X if (!p) { X (void) fprintf(stderr, "out of memory in malloc\n"); X exit(-1); X } X return p; X} X Xchar *xrealloc(s, n) Xchar *s; X{ X extern char *realloc(); X char *p = realloc(s, (unsigned) n); X X if (!p) { X (void) fprintf(stderr, "out of memory in realloc\n"); X exit(-1); X } X return p; X} X X/* Scandir returns the number of entries or -1 if the directory cannoot X be opened or malloc fails. */ X Xint Xmyscandir(dir, nmptr, qualify, sorter) Xchar *dir; Xchar ***nmptr; Xint (*qualify)(); Xint (*sorter)(); X{ X DIR *dirp; X struct direct *entry; X char **ourarray; X int nalloc = 10; X int nentries = 0; X X if ((dirp = opendir(dir)) == 0) X return -1; X ourarray = (char **) xmalloc(nalloc * sizeof (char *)); X while ((entry = readdir(dirp)) != 0) { X if (qualify != 0 && (*qualify)(entry->d_name) == 0) X continue; X if (nentries == nalloc) { X ourarray = (char **) xrealloc((char *) ourarray, (nalloc += 10) * sizeof (char *)); X } X ourarray[nentries] = (char *) xmalloc((int) DIRSIZE(entry) + 1); X null_ncpy(ourarray[nentries], entry->d_name, (int) DIRSIZE(entry)); X nentries++; X } X closedir(dirp); X if ((nentries + 1) != nalloc) X ourarray = (char **) xrealloc((char *) ourarray, X ((nentries + 1) * sizeof (char *))); X if (sorter != 0) X qsort((char *) ourarray, nentries, sizeof (char **), sorter); X *nmptr = ourarray; X ourarray[nentries] = 0; /* guaranteed 0 pointer */ X X return nentries; X} X Xfreedir(nmptr, nentries) Xchar ***nmptr; X{ X char **ourarray = *nmptr; X X while (--nentries >= 0) X free(*ourarray++); X free((char *) *nmptr); X *nmptr = 0; X} X Xalphacomp(a, b) Xchar **a, X **b; X{ X return strcmp(*a, *b); X} X Xnumcomp(s1, s2) Xregister char *s1, X *s2; X{ X register int count = 0; X X while (*s1 != 0 && *s1++ == *s2++) X count++; X return count; X} X Xstatic char *fc_filebase; Xchar BadExtensions[128] = ".o"; X Xstatic Xbad_extension(name, bads) Xchar *name, X *bads; X{ X char *ip; X int namelen = strlen(name), X ext_len, X stop = 0; X X do { X if (ip = index(bads, ' ')) X *ip = 0; X else { X ip = bads + strlen(bads); X stop++; X } X if ((ext_len = ip - bads) == 0) X continue; X if ((ext_len < namelen) && X (strcmp(&name[namelen - ext_len], bads) == 0)) X return TRUE; X } while ((bads = ip + 1), !stop); X return FALSE; X} X Xf_match(file) Xchar *file; X{ X int len = strlen(fc_filebase); X X return ((len == 0) || X (strncmp(file, fc_filebase, strlen(fc_filebase)) == 0)); X} X Xstatic Xisdir(name) Xchar *name; X{ X struct stat stbuf; X char filebuf[FILESIZE]; X X PathParse(name, filebuf); X return ((stat(filebuf, &stbuf) != -1) && X (stbuf.st_mode & S_IFDIR) == S_IFDIR); X} X Xstatic Xfill_in(dir_vec, n) Xregister char **dir_vec; X{ X int minmatch = 0, X numfound = 0, X lastmatch = -1, X i, X the_same = TRUE, /* After filling in, are we the same X as when we were called? */ X is_ntdir; /* Is Newly Typed Directory name */ X char bads[128]; X X for (i = 0; i < n; i++) { X (void) strcpy(bads, BadExtensions); X /* bad_extension() is destructive */ X if (bad_extension(dir_vec[i], bads)) X continue; X if (numfound) X minmatch = min(minmatch, X numcomp(dir_vec[lastmatch], dir_vec[i])); X else X minmatch = strlen(dir_vec[i]); X lastmatch = i; X numfound++; X } X /* Ugh. Beware--this is hard to get right in a reasonable X manner. Please excuse this code--it's past my bedtime. */ X if (numfound == 0) { X rbell(); X return; X } X if (minmatch > strlen(fc_filebase)) { X the_same = FALSE; X insert_s(fc_filebase, dir_vec[lastmatch], minmatch, &curchar); X } X is_ntdir = ((numfound == 1) && X (curchar > 0) && X (linebuf[curchar - 1] != '/') && X (isdir(linebuf))); X if (the_same && !is_ntdir) { X add_mess((n == 1) ? " [Unique]" : " [Ambiguous]"); X } X if (is_ntdir) X insert_s(&linebuf[curchar], "/", 1, &curchar); X} X X/* X * called when one of "\t ?" is typed. Does the right thing, X * depending on which. X */ X Xf_complete(sbuf, curpos, cols, c) Xchar *sbuf; X{ X char dir[FILESIZE], X **dir_vec; X int nentries; X#ifdef TYPEOUT X int i; X#endif X X linebuf = sbuf; X curchar = curpos; X maxCols = cols; X X if (linebuf[curpos] != '\0') X linebuf[curpos] = '\0'; X X if ((fc_filebase = rindex(linebuf, '/')) != 0) { X char tmp[FILESIZE]; X X null_ncpy(tmp, linebuf, (++fc_filebase - linebuf)); X if (tmp[0] == '\0') X (void) strcpy(tmp, "/"); X PathParse(tmp, dir); X } else { X fc_filebase = linebuf; X (void) strcpy(dir, "."); X } X if ((nentries = myscandir(dir, &dir_vec, f_match, alphacomp)) == -1) { X char err[FILESIZE]; X X (void) sprintf(err, " [Unknown directory: %s]", dir); X add_mess(err); X return 1; X } X if (nentries == 0) { X add_mess(" [No match]"); X } else if (c == ' ' || c == '\t') X fill_in(dir_vec, nentries); X else { X /* we're a '?' */ X#ifdef TYPEOUT X int maxlen = 0, X ncols, X col, X lines, X linespercol; X X TOstart("Completion"); X Typeout("(! means file will not be chosen unless typed explicitly)"); X Typeout((char *) 0); X Typeout("Possible completions (in %s):", dir); X Typeout((char *) 0); X X for (i = 0; i < nentries; i++) X maxlen = max(strlen(dir_vec[i]), maxlen); X maxlen += 4; /* pad each column with at least 4 spaces */ X ncols = (maxCols - 2) / maxlen; X linespercol = 1 + (nentries / ncols); X X for (lines = 0; lines < linespercol; lines++) { X for (col = 0; col < ncols; col++) { X int isbad, X which; X char bads[128]; X X which = (col * linespercol) + lines; X if (which >= nentries) X break; X (void) strcpy(bads, BadExtensions); X isbad = bad_extension(dir_vec[which], bads); X Typeout("%s%-*s", isbad ? "!" : "", X maxlen - isbad, dir_vec[which]); X } X Typeout((char *) 0); X } X TOstop(); X#endif X } X freedir(&dir_vec, nentries); X return 1; X} END_OF_FILE if test 9084 -ne `wc -c <'xpic/ask.c'`; then echo shar: \"'xpic/ask.c'\" unpacked with wrong size! fi # end of 'xpic/ask.c' fi if test -f 'xpic/event.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xpic/event.c'\" else echo shar: Extracting \"'xpic/event.c'\" \(10071 characters\) sed "s/^X//" >'xpic/event.c' <<'END_OF_FILE' X/* $Header: event.c,v 1.4 89/04/21 03:30:34 xwindows Exp $ */ X/* X * The event handler determines which object's event processor to call, X * and passes it the event type, and object mode. Each object module X * must provide a procedure called xxx_event, where xxx is the object X * name, which takes the event code, and returns the new editing state. X * The event code consistes of the current drawingMode (START_MODE, END_MODE, X * DRAG_MODE, ASK_MODE), or'ed with the event type (REDRAW, MOTION, LEFT, X * MIDDLE, RIGHT). The object event routine will usually take some X * appropriate action, and return the new drawingMode. The object X * module must also provide a xxx_abort routine, which can be invoked X * by the CleanUpMode routine if the user hits a menu button whil estill X * editing something. X */ X X#include <values.h> X#include "xpic.h" X#include "windows.h" X#include "newfonts.h" X#include "gels.h" X#include "draw.h" X#include "spline.h" X Xstatic int mx, my; /* Mouse coordinates after snap */ X X/* X * processes events in the pic window X */ X/*ARGSUSED*/ Xvoid picEventHandle(w, data, event) XWidget w; Xcaddr_t data; XXEvent *event; X{ X int event_mode = drawingMode; X X switch (event->type) { X case Expose: X event_mode |= REDRAW; X /* X * mx, my not important in Expose - all the object event X * processor will do is redraw the last rubber banded object if X * any, at the last position. X */ X /* Can do selective redraw, but it is hardly necessary */ X PicRedraw(CurrentCell->gelList, &picBox); X break; X case MotionNotify: X event_mode |= MOTION; X /* X * We use the MotionNotify event as a hint, and ask for the X * mouse position. We use the mouse position to rubber badn - X * this is essentially "jump" rubber banding, and is possible X * because we have the compress_motion flag on the Window X * widget set to TRUE to make sure the MotionNotify events are X * hints, and not a stream of actual mouse movements. X */ X { X Window root_return, child_return; X int root_x_return, root_y_return, win_x_return, win_y_return; X unsigned int mask_return; X X (void) XQueryPointer(picDpy, picWin, &root_return, &child_return, X &root_x_return, &root_y_return, &win_x_return, &win_y_return, X &mask_return); X mx = snap(win_x_return, mouseResolution); X my = snap(win_y_return, mouseResolution); X } X /* Usually, if in start, then ignore, else rubber_band or drag */ X break; X case ButtonPress: X mx = snap(event->xbutton.x, mouseResolution); X my = snap(event->xbutton.y, mouseResolution); X switch (event->xbutton.button) { X case Button1: X event_mode |= LEFT; X /* Decision button */ X break; X case Button3: X event_mode |= RIGHT; X /* termination button for lines, splines, editing actions */ X break; X case Button2: X event_mode |= MIDDLE; X /* Abort current action */ X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "unknown button in picEventhandle - %d", X event->xbutton.button); X message(errstring); X#endif X break; X } X break; X case ConfigureNotify: X event_mode |= REDRAW; X if ((event->xconfigure.width != picWinWidth) X || (event->xconfigure.height != picWinHeight)) { X#ifdef DEBUG X (void) fprintf(stderr, "Window Size changed to %dx%d\n", X event->xconfigure.width, event->xconfigure.height); X#endif X picWinWidth = picBox.ur.x = event->xconfigure.width; X picWinHeight = picBox.ur.y = event->xconfigure.height; X pageWidth = picWinWidth / gridSpacing + 0.5; X pageHeight = picWinHeight / gridSpacing + 0.5; X } X /* Can do selective redraw, but it is hardly necessary */ X XClearWindow(picDpy, picWin); X PicRedraw(CurrentCell->gelList, &picBox); X break; X default: X /* Various other Notify types will end up here. */ X#ifdef DEBUG X (void) sprintf(errstring, X "picEventHandle: Unknown event %d", event->type); X message(errstring); X#endif X return; X } X X /* Now call the object event handler - wouldn't this be easy in C++*/ X switch (objectType) { X case LINE: X line_event(event_mode, mx, my); X break; X case SPLINE: X spline_event(event_mode, mx, my); X break; X case BOX: X box_event(event_mode, mx, my); X break; X case CIRCLE: X circle_event(event_mode, mx, my); X break; X case ELLIPSE: X ellipse_event(event_mode, mx, my); X break; X case TEXT: X text_event(event_mode, mx, my); X break; X case BLOCK: X block_event(event_mode, mx, my); X break; X case ELEMENT: X element_event(event_mode, mx, my); X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "Unknown object %d", objectType); X message(errstring); X#endif X break; X } X} X X X/* X * This gets called when the user presses a button to change a X * selection - it cleans up any rubber bands appropriately, by calling X * the object procedure xxx_abort, where xxx is the object name. X */ X/* !! remove the xxx_abort procedures by calling xxx_event() directly */ Xvoid CleanUpMode() X{ X switch(objectType) { X case LINE: X line_abort(); X break; X case SPLINE: X spline_abort(); X break; X case BOX: X box_abort(); X break; X case ELLIPSE: X ellipse_abort(); X break; X case CIRCLE: X circle_abort(); X break; X case TEXT: X text_abort(); X break; X case BLOCK: X block_abort(); X break; X case ELEMENT: X element_abort(); X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "Unknown object in cleanup? %d", X objectType); X message(errstring); X#endif X break; X } X} X X X/* X * The routine GelDraw to draw an element takes a pointer to a Gel and X * depending on the type, invokes different drawing methods X */ Xvoid GelDraw(g, func) XGel *g; Xint func; X{ X XFontStruct *font; X int pad, n; X PointList *pt; X Conic *conic; X TextString *text; X GC gc; X X switch (func) { X case DRAW: X gc = tmpGcNormal; X break; X case ERASE: X gc = tmpGcErase; X break; X case INVERT: X case HILITE: X gc = tmpGcInvert; X break; X } X X SETDASHES(gc, getlinestyle(g->attributes)) X if (func == HILITE) X setwidth(gc, g->linewidth + 2); X else X setwidth(gc, g->linewidth); X X#ifdef DRAWBBOX X /* Draw bounding boxes for all stuff except Boxes */ X if (g->type != BOX) X box(picWin, g->b_box.ll.x, g->b_box.ll.y, X g->b_box.ur.x, g->b_box.ur.y, gcBlock); X#endif X X switch (g->type) { X case LINE: X pt = (PointList *) g->data; X drawlines(picDpy, picWin, gc, pt->v, pt->nVerts, CoordModeOrigin); X if (pt->nVerts == 1) X break; X if (g->attributes & ST_ARROW) X Arrow(picDpy, picWin, pt->v[1].x, pt->v[1].y, X pt->v[0].x, pt->v[0].y, gc); X if (g->attributes & EN_ARROW) X Arrow(picDpy, picWin, X pt->v[pt->nVerts - 2].x, pt->v[pt->nVerts - 2].y, X pt->v[pt->nVerts - 1].x, pt->v[pt->nVerts - 1].y, gc); X break; X case SPLINE: X pt = (PointList *) g->data; X FlattenSpline(pt->v, pt->nVerts-1, &flatVerts2, &n, &flatSize2); X drawlines(picDpy, picWin, gc, flatVerts2, n, CoordModePrevious); X if (pt->nVerts == 1) X break; X if (g->attributes & ST_ARROW) X Arrow(picDpy, picWin, pt->v[2].x, pt->v[2].y, X pt->v[1].x, pt->v[1].y, gc); X if (g->attributes & EN_ARROW) X Arrow(picDpy, picWin, X pt->v[pt->nVerts - 3].x, pt->v[pt->nVerts - 3].y, X pt->v[pt->nVerts - 2].x, pt->v[pt->nVerts - 2].y, gc); X break; X case BOX: X box(picWin, g->b_box.ll.x, g->b_box.ll.y, g->b_box.ur.x, X g->b_box.ur.y, gc); X break; X case CIRCLE: X conic = (Conic *) g->data; X ellipse(picWin, conic->centre.x, conic->centre.y, X conic->xrad, conic->xrad, gc); X break; X case ELLIPSE: X conic = (Conic *) g->data; X ellipse(picWin, conic->centre.x, conic->centre.y, X conic->xrad, conic->yrad, gc); X break; X case TEXT: X text = (TextString *) g->data; X font = ChangeFont(&text->font->sizes[text->sizeindex], &pad); X setfont(gc, font->fid); X /* X * This weird location for drawing text is a result of the X * sloppy bounding box calculation, which is teh X10 heritage - X * using the bounding box lower left corner as the control X * point for text. Since text in X11 uses the baseline as the y X * coordinate, this code is crude. The text drawing needs X * cleaning up, especially for proper space padding X */ X drawtext(picWin, g->b_box.ll.x, g->b_box.ur.y, text->str, X text->length, gc, pad); X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "GelDraw: Unknown Gel type - %d", g->type); X message(errstring); X#endif X break; X } X} X X X/* These are rather sloppy - need to be cleaned up a bit */ Xvoid GelHilite(g) XGel *g; X{ X extern void SetWorkingCursor(), SetWaitCursor(); X X /* !! Should do something interesting for color */ X SetWaitCursor(); X for (; g != NULL; g = g->next) { X if (!(g->int_flags & HILITED)) { X g->int_flags |= HILITED; X if (g->type != TEXT) { X GelDraw(g, HILITE); X } else { X XFillRectangle(picDpy, picWin, gcGray, X g->b_box.ll.x, g->b_box.ll.y, X (unsigned) (g->b_box.ur.x - g->b_box.ll.x), X (unsigned) (g->b_box.ur.y - g->b_box.ll.y)); X GelDraw(g, INVERT); X } X } X } X SetWorkingCursor(); X} X X Xvoid GelUnHilite(g) XGel *g; X{ X extern void SetWorkingCursor(), SetWaitCursor(); X X /* !! Should do something interesting for color */ X SetWaitCursor(); X for (; g != NULL; g = g->next) { X if (g->int_flags & HILITED) { X g->int_flags &= ~HILITED; X if (g->type != TEXT) { X GelDraw(g, HILITE); X } else { X /* Make sure we have a correct bounding box */ X GelDraw(g, INVERT); X XFillRectangle(picDpy, picWin, gcGray, X g->b_box.ll.x, g->b_box.ll.y, X (unsigned) (g->b_box.ur.x - g->b_box.ll.x), X (unsigned) (g->b_box.ur.y - g->b_box.ll.y)); X } X } X } X SetWorkingCursor(); X} X X X/* X * The routine PicRedraw takes three parameters - a pointer to a Gel, X * and a Box which defines the Clip area needed to be drawn. */ X/* X * Ideally, any Gel whose bbox intersects the Box passed in is redrawn. X * This allows the efficient use of ExposeRegion events to redraw small X * sections quickly. For now, we just redraw the whole thing. X */ X/* !! proper clipped redraw - note that the rubber banded stuff X will pose problems */ X/*ARGSUSED*/ Xvoid PicRedraw(g, clip) XGel *g; XBox *clip; X{ X extern void SetWorkingCursor(), SetWaitCursor(); X X SetWaitCursor(); X CalcBBox(g, MAXINT); X for(; g != NULL; g = g->next) X GelDraw(g, DRAW); X SetWorkingCursor(); X} X X END_OF_FILE if test 10071 -ne `wc -c <'xpic/event.c'`; then echo shar: \"'xpic/event.c'\" unpacked with wrong size! fi # end of 'xpic/event.c' fi if test -f 'xpic/handlers.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xpic/handlers.c'\" else echo shar: Extracting \"'xpic/handlers.c'\" \(9120 characters\) sed "s/^X//" >'xpic/handlers.c' <<'END_OF_FILE' X/* $Header: handlers.c,v 1.4 89/04/21 03:30:43 xwindows Exp $ */ X#include "xpic.h" X#include "windows.h" X#include "newfonts.h" X#include "input.h" X#include "version.h" X#include "gels.h" X#include "patchlevel.h" X#include "draw.h" X Xextern void RedrawPicWin(); Xextern void CleanUpMode(); X X/* X * If a PushButton has been pressed in a mode other than START_MODE, X * there's probably rubber-banded stuff on teh screen that we'll have X * to clean up X */ X#define RESETMODE() if(drawingMode != START_MODE) CleanUpMode(); else X X/*ARGSUSED*/ Xvoid copy(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = COPY; X objectType = editMode; X} X X/*ARGSUSED*/ Xvoid cut(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = DELETE; X objectType = editMode; X} X X X/*ARGSUSED*/ Xvoid paste(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = PASTE; X objectType = editMode; X} X X X/*ARGSUSED*/ Xvoid move(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = MOVE; X objectType = editMode; X} X X X X/*ARGSUSED*/ Xvoid change(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = CHANGE_ATTRIBUTE; X objectType = editMode; X} X X X X/*ARGSUSED*/ Xvoid adjust(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = ADJUST; X objectType = editMode; X} X X X X/*ARGSUSED*/ Xvoid rotate(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X message("rotate: Not implemented yet. Sorry."); X} X X X/*ARGSUSED*/ Xvoid scale(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X message("scale: Not implemented yet. Sorry."); X} X X X X/*ARGSUSED*/ Xvoid getcell(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = GET; X objectType = editMode; X} X X X/*ARGSUSED*/ Xvoid putcell(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X editType = PUT; X objectType = editMode; X} X X X/*ARGSUSED*/ Xvoid element(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X /* Make sure everything is selectable - should be, but you never know! */ X ClearGelFlags(CurrentCell->gelList); X X if (STREQ(tag, "Line")) X objectType = LINE; X else if (STREQ(tag, "Box")) X objectType = BOX; X else if (STREQ(tag, "Ellipse")) X objectType = ELLIPSE; X else if (STREQ(tag, "Circle")) X objectType = CIRCLE; X else if (STREQ(tag, "Spline")) X objectType = SPLINE; X else if (STREQ(tag, "Text")) X objectType = TEXT; X else { X (void) sprintf("Unknown element - %s", errstring); X message(errstring); X } X} X X X/*ARGSUSED*/ Xvoid editattrib(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X if (STREQ(tag, "Block")) { X editMode = BLOCK; X if (objectType == ELEMENT) X objectType = BLOCK; X } else if (STREQ(tag, "Element")) { X editMode = ELEMENT; X if (objectType == BLOCK) X objectType = ELEMENT; X } else { X (void) sprintf(errstring, X "editattrib: Unknown editing mode - Tag = %s", tag); X message(errstring); X } X} X X X/*ARGSUSED*/ Xvoid linepattern(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X if (STREQ(tag, "Solid")) X line_type = SOLID; X else if (STREQ(tag, "Dotted")) X line_type = DOTTED; X else if (STREQ(tag, "Short-Dashed")) X line_type = SDASH; X else if (STREQ(tag, "Long-Dashed")) X line_type = LDASH; X else if (STREQ(tag, "Dot-Dashed")) X line_type = DDASH; X else { X (void) sprintf(errstring, X "linepattern: Unknown line pattern - Tag = %s",tag); X message(errstring); X } X SETDASHES(gcNormal, line_type) X SETDASHES(gcInvert, line_type) X} X X X/*ARGSUSED*/ Xvoid linearrow(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X if (STREQ(tag, "None")) X line_arrow = NO_ARROW; X else if (STREQ(tag, "Start")) X line_arrow = ST_ARROW; X else if (STREQ(tag, "End")) X line_arrow = EN_ARROW; X else if (STREQ(tag, "Both")) X line_arrow = ST_ARROW | EN_ARROW; X else { X#ifdef DEBUG X (void) sprintf(errstring, X "linearrow: Unknown line arrow - Tag = %s", tag); X message(errstring); X#endif X } X} X X X/*ARGSUSED*/ Xvoid linethickness(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X lineThickness = atoi(tag); X if (lineThickness< 0) { X message("Line Thickness must be positive"); X lineThickness = 0; X } X setwidth(gcNormal, lineThickness); X setwidth(gcInvert, lineThickness); X} X X X/*ARGSUSED*/ Xvoid textvalign(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X if (STREQ(tag, "Top")) X textVertAlign = TOPLINE; X else if (STREQ(tag, "Middle")) X textVertAlign = MIDLINE; X else if (STREQ(tag, "Bottom")) X textVertAlign = BOTLINE; X} X X X/*ARGSUSED*/ Xvoid texthalign(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X if (STREQ(tag, "Centred")) X textHorizAlign = CENTRE; X else if (STREQ(tag, "Left Just.")) X textHorizAlign = LJUST; X else if (STREQ(tag, "Right Just.")) X textHorizAlign = RJUST; X} X X X/*ARGSUSED*/ Xvoid setsnap(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X mouseResolution = atoi(tag); X if (mouseResolution < 0) X message("Mouse resolution must be positive"); X} X X X/* X * Status is printed on the message line - it prints certain global X * variables, like the program name, version number, buffername, the X * filename associated with the buffer, the * to show if the buffer was X * saved or not, and the present drawing mode X */ Xvoid DisplayStatus() X{ X X char *modified = " "; X X if (CurrentCell->saved & MODIFIED) X modified = "[Modified]"; X (void) sprintf(errstring, "XPIC %d.%d%s Buffer: %s File: %s %s", X progVersion, PATCHLEVEL, progStatus, CurrentCell->name, X CurrentCell->filename, modified); X message(errstring); X} X X X/* Callback entry point */ X/*ARGSUSED*/ Xvoid status(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X X DisplayStatus(); X} X X X X/*ARGSUSED*/ Xvoid readcell(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X Cell *cell; X X RESETMODE(); X X /* This will not be necessary when multiple buffers become available */ X if (CurrentCell->saved & MODIFIED) { X message("You must first save the current cell"); X return; X } X X if ((cell = ReadCell("Read file name ? ", (char *) NULL)) == NULL) X return; X X cell->next = MainCell; X MainCell = cell; X LastCell = CurrentCell; X CurrentCell = MainCell; X RedrawPicWin(); X DisplayStatus(); X} X X X/*ARGSUSED*/ Xvoid lprintcell(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X LPrintCell(CurrentCell); X} X X X/*ARGSUSED*/ Xvoid savecell(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X char *fname; X X RESETMODE(); X X if(STREQ(tag, "Save As")) { X fname = get_input("Save file name ? ", CurrentCell->filename, TRUE); X if (fname == NULL) X return; X if (CurrentCell->filename && !(STREQ(CurrentCell->filename, nullfile))) X free(CurrentCell->filename); X CurrentCell->filename = fname; X /* Force a save */ X CurrentCell->saved = MODIFIED | NEWFILE; X } X (void) WriteCell(CurrentCell, backupOnWrite); X} X X X/*ARGSUSED*/ Xvoid redisplay(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X RedrawPicWin(); X DisplayStatus(); X} X X X/*ARGSUSED*/ Xvoid setgrid(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X Arg args[1]; X X RESETMODE(); X gridOn = !gridOn; X if (gridOn) { X XtSetArg(args[0], XtNbackgroundPixmap, gridTile); X XtSetValues(picWidget, args, 1); X } else { X XtSetArg(args[0], XtNbackgroundPixmap, blankTile); X XtSetValues(picWidget, args, 1); X } X} X X X/*ARGSUSED*/ Xvoid undo(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X Gel *g; X register Gel *tmp; X int i; X X RESETMODE(); X X g = PopGel(&(CurrentCell->gelList), CurrentCell->undo); X for(tmp = g; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, ERASE); X for(tmp = CurrentCell->undoList; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X ClearGelFlags(CurrentCell->undoList); X i = PushGel(&(CurrentCell->gelList), CurrentCell->undoList); X CurrentCell->undo = i; X CurrentCell->undoList = g; X CurrentCell->saved |= MODIFIED; X X} X X X X/*ARGSUSED*/ Xvoid quit(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X Cell *cell; X X RESETMODE(); X /* Check all the buffers to make sure they're saved */ X for (cell = MainCell; cell != NULL; cell = cell->next) { X if (cell->saved & MODIFIED) { X (void) sprintf(errstring, "Buffer \"%s\" not saved. Save (y/n) ?", X cell->name); X switch ( confirm(errstring, "y")) { X case ABORT: X return; X case YES: X if (WriteCell(cell, backupOnWrite)) X break; X else X return; X case NO: X break; X } X } X } X exit(0); X} X X/*ARGSUSED*/ Xvoid change_buffer(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X message("change_buffer: Not implemented yet. Sorry."); X} X X X/*ARGSUSED*/ Xvoid kill_buffer(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X message("kill_buffer: Not implemented yet. Sorry."); X} X X X#ifdef DEBUG X/*ARGSUSED*/ Xvoid printcell(w, tag, calldata) XWidget w; Xcaddr_t tag; Xcaddr_t calldata; X{ X RESETMODE(); X PrintCell(CurrentCell); X} X#endif END_OF_FILE if test 9120 -ne `wc -c <'xpic/handlers.c'`; then echo shar: \"'xpic/handlers.c'\" unpacked with wrong size! fi # end of 'xpic/handlers.c' fi if test -f 'xpic/obj_block.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xpic/obj_block.c'\" else echo shar: Extracting \"'xpic/obj_block.c'\" \(10281 characters\) sed "s/^X//" >'xpic/obj_block.c' <<'END_OF_FILE' X/* $Header: obj_block.c,v 1.7 89/04/21 03:31:08 xwindows Exp $ */ X/* X * The block edit routines - treated as a separate pseudo-object X * because they are so similar. The code is somewhat intricate - the X * result of trying to cram as many operations into as little code as X * possible, so that the code to do something stayed in the one place. X * Sigh! macros might have been nicer. X */ X#include <values.h> X X#include "xpic.h" X#include "windows.h" X#include "spline.h" X#include "gels.h" X#include "draw.h" X#include "input.h" X#include "newfonts.h" X#include "assert.h" X Xstatic int x_1, y_1, x_2, y_2; /* Corners of box, ellipse, ends of line */ Xstatic int xmin, xmax, ymin, ymax; /* Bounding box */ Xstatic Gel *gel; Xstatic Gel *oldgel; Xstatic Cell *cell; Xstatic Box *bp; Xstatic Box adjbox; Xstatic int lastX, lastY; Xstatic int first_time = FALSE; X X/* X * For the editing constructs, we also may have a DRAG_MODE (where the X * object is dragged around, or an ASK_MODE where the user is asked to X * confirm the operation (usually a delete, with a click X */ Xblock_event(evtype, mx, my) X{ X char *err; X register Gel *tmp; X X switch(evtype) { X case MOTION | START_MODE: X case MOTION | ASK_MODE: X case RIGHT | START_MODE: X case MIDDLE | START_MODE: X case REDRAW | START_MODE: X case REDRAW | ASK_MODE: X break; X case MOTION | END_MODE: X /* rubber band the box corner */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X x_2 = mx; X y_2 = my; X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X break; X case MOTION | DRAG_MODE: X /* X * move the box around on the cursor - use the second corner as X * the mouse corner X */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X x_1 += mx - x_2; X y_1 += my - y_2; X x_2 = mx; X y_2 = my; X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X break; X case LEFT | START_MODE: X /* start the box */ X first_time = TRUE; X if (editType == PASTE) { X if (KillBuffer == NULL) { X message("Nothing to paste. Delete something first"); X break; X } X gel = CopyGel(KillBuffer, MAXINT); X MoveGel(gel, mx - KillX, my - KillY); X bp = GetBBox(gel); X x_1 = bp->ll.x; X y_1 = bp->ll.y; X lastX = x_2 = bp->ur.x; X lastY = y_2 = bp->ur.y; X drawingMode = DRAG_MODE; X } else if (editType == GET) { X if (cell) X FreeCell(cell); X cell = ReadCell("Get from file ? ", (char *) NULL); X if ( cell == NULL) X break; X else if (cell->gelList == NULL) { X message("Can't paste an empty cell!"); X FreeCell(cell); X cell = NULL; X break; X } X gel = cell->gelList; X cell->gelList = NULL; X bp = GetBBox(gel); X x_1 = bp->ll.x; X y_1 = bp->ll.y; X lastX = x_2 = bp->ur.x; X lastY = y_2 = bp->ur.y; X drawingMode = DRAG_MODE; X } else if (editType == SCALE || editType == ROTATE) { X message("SCALE/ROTATE not yet implemented"); X break; X } else { X x_1 = x_2 = mx; X y_1 = y_2 = my; X drawingMode = END_MODE; X } X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X break; X case LEFT | END_MODE: X /* Won't get here for GET, PASTE - they go straight to DRAG */ X /* End the box, find the contained gels, highlight them */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X lastX = x_2 = mx; X lastY = y_2 = my; X xmin = MIN(x_1, mx); X xmax = MAX(x_1, mx); X ymin = MIN(y_1, my); X ymax = MAX(y_1, my); X /* X * The very nature of ADJUST means that we want X * intersecting Gels - for the others, we want only X * those that are strictly contained within the box X */ X if (editType == ADJUST) { X oldgel = FindIntersectingGels(&(CurrentCell->gelList), X xmin, ymin, xmax, ymax); X gel = CopyGel(oldgel, MAXINT); X adjbox.ll.x = xmin; X adjbox.ll.y = ymin; X adjbox.ur.x = xmax; X adjbox.ur.y = ymax; X err = "No intersecting elements"; X } else { X oldgel = FindContainedGels(&(CurrentCell->gelList), X xmin, ymin, xmax, ymax); X gel = CopyGel(oldgel, MAXINT); X err = "No contained elements"; X } X if (oldgel == NULL) { X message(err); X drawingMode = START_MODE; X break; X } X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, ERASE); X GelHilite(gel); X if (editType == DELETE || editType == CHANGE_ATTRIBUTE) { X drawingMode = ASK_MODE; X (void) sprintf(errstring, "Click Left button to confirm %s", X (editType == DELETE) ? "Delete" : "Change"); X message(errstring); X } else if (editType == COPY || editType == MOVE || X editType == ADJUST) { X if (editType == COPY) { X /* 'gel' must be a copy of the gels selected */ X (void) PushUnderUndo(&(CurrentCell->gelList), oldgel, X CurrentCell->undo); X oldgel = NULL; X } X drawingMode = DRAG_MODE; X /* Draw the rubber banded box for drag */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X } else if (editType == PUT) { X if (cell) X FreeCell(cell); X (void) PushGel(&(CurrentCell->gelList), oldgel); X if ((cell = NewCell((char *) NULL, nullfile)) != NULL) { X cell->gelList = gel; X cell->saved |= MODIFIED; X /* X * If we try to write out a cell with a name of X * nullfile, it will ask for the name X */ X (void) WriteCell(cell, backupOnWrite); X cell->gelList = NULL; X } X GelUnHilite(gel); X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X FreeGel(gel); X gel = NULL; X drawingMode = START_MODE; X } else { /* shouldn't get here, since SCALE, ROTATE aren't working! */ X (void) sprintf(errstring, X "Unknown editType %d in block_event - LEFT", editType); X message(errstring); X (void) PushGel(&(CurrentCell->gelList), oldgel); X GelUnHilite(gel); X FreeGel(gel); X gel = NULL; X drawingMode = START_MODE; X } X break; X case LEFT | DRAG_MODE: X /* X * Every time LEFT is clicked in DRAG mode, we move all X * the objects in the gel list here, highlight them, X * and stay in this mode - gives the users multiple X * tries at adjusting the block since it isn't X * completely WYSIWYG. PUT doesn't get here since it X * ends in END_MODE, DELETE and CHANGE_ATTRIB go via X * ASK_MODE X */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X x_1 += mx - x_2; X y_1 += my - y_2; X x_2 = mx; X y_2 = my; X if (first_time) { X first_time = FALSE; X if (editType == COPY) { X GelUnHilite(gel); X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X } else if (editType == MOVE || editType == ADJUST) { X GelUnHilite(gel); X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, ERASE); X } else { /* GET, PASTE */ X /* Do nothing */ X } X } else { /* Not first time, so we zonk the last position */ X GelUnHilite(gel); X } X if (editType == ADJUST) { X AdjustGel(gel, &adjbox, mx - lastX, my - lastY); X adjbox.ll.x = MIN(x_1, x_2); X adjbox.ll.y = MIN(y_1, y_2); X adjbox.ur.x = MAX(x_1, x_2); X adjbox.ur.y = MAX(y_1, y_2); X } else X MoveGel(gel, mx - lastX, my - lastY); X lastX = mx; X lastY = my; X GelHilite(gel); X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X break; X case RIGHT | DRAG_MODE: X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X if ((!first_time) || editType == COPY || editType == MOVE || X editType == ADJUST) { X GelUnHilite(gel); X } X if (!(first_time && (editType == GET || editType == PASTE))) { X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X FreeGel(CurrentCell->undoList); X CurrentCell->undoList = NULL; X if (editType != GET && editType != PASTE) { X CurrentCell->undoList = oldgel; X oldgel = NULL; X } X FreeGel(KillBuffer); X KillBuffer = CopyGel(gel, MAXINT); X KillX = mx; X KillY = my; X CurrentCell->undo = PushGel(&(CurrentCell->gelList), X gel); X CurrentCell->saved |= MODIFIED; X } X gel = NULL; X drawingMode = START_MODE; X break; X case LEFT | ASK_MODE: X /* Delete, change the gels */ X GelUnHilite(gel); X FreeGel(CurrentCell->undoList); X CurrentCell->undoList = oldgel; X oldgel = NULL; X if (editType == DELETE) { X CurrentCell->undo = 0; X FreeGel(KillBuffer); X KillBuffer = gel; X KillX = mx; X KillY = my; X } else { /* CHANGE_ATTRIB */ X ChangeAttrib(gel, line_type, line_arrow, lineThickness, X fill_type, X textVertAlign | textHorizAlign, fontType, textSize); X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X CurrentCell->undo = PushGel(&(CurrentCell->gelList), gel); X } X gel = NULL; X CurrentCell->saved |= MODIFIED; X drawingMode = START_MODE; X break; X case RIGHT | ASK_MODE: X case MIDDLE | ASK_MODE: X /* Abort */ X GelUnHilite(gel); X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X (void) PushUnderUndo(&(CurrentCell->gelList), gel, CurrentCell->undo); X gel = NULL; X drawingMode = START_MODE; X break; X case MIDDLE | DRAG_MODE: X /* Abort, unhilite the gels - delete any copies */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X /* PUT only gets as far as END_MODE - won't get here */ X if ((!first_time) || editType == COPY || editType == MOVE || X editType == ADJUST) { X GelUnHilite(gel); X } X if (editType == COPY || editType == PASTE || editType == GET) { X FreeGel(gel); X if (cell) X FreeCell(cell); X cell = NULL; X } else if (editType == MOVE || editType == ADJUST) { X FreeGel(gel); X gel = oldgel; X for (tmp = gel; tmp != NULL; tmp = tmp->next) X GelDraw(tmp, DRAW); X (void) PushUnderUndo(&(CurrentCell->gelList), gel, X CurrentCell->undo); X } else { /* can't happen, since we don't have SCALE/ROTATE */ X (void) sprintf(errstring, X "Unknown editType %d in block_event - MIDDLE", editType); X message(errstring); X } X gel = NULL; X drawingMode = START_MODE; X break; X case RIGHT | END_MODE: X case MIDDLE | END_MODE: X /* Abort - stop rubber banding */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X drawingMode = START_MODE; X break; X case REDRAW | END_MODE: X /* redraw the rubber band */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X break; X case REDRAW | DRAG_MODE: X /* highlight the gels, redraw the drag box */ X box(picWin, x_1, y_1, x_2, y_2, gcBlock); X if (!first_time || (editType == COPY || editType == MOVE X || editType == ADJUST)) X GelHilite(gel); X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "Hey! Unknown BLOCK mode %d", drawingMode); X message(errstring); X#endif X break; X } X ASSERT(allock(), "block_event"); X} X Xblock_abort() X{ X /* Fudge up a RIGHT button pressed event - safer thing to do */ X block_event((RIGHT | drawingMode), 0, 0); X} X END_OF_FILE if test 10281 -ne `wc -c <'xpic/obj_block.c'`; then echo shar: \"'xpic/obj_block.c'\" unpacked with wrong size! fi # end of 'xpic/obj_block.c' fi if test -f 'xpic/obj_line.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xpic/obj_line.c'\" else echo shar: Extracting \"'xpic/obj_line.c'\" \(10261 characters\) sed "s/^X//" >'xpic/obj_line.c' <<'END_OF_FILE' X/* $Header: obj_line.c,v 1.5 89/04/21 03:31:18 xwindows Exp $ */ X/* Procedures for the line object */ X X#include <values.h> X#include <math.h> X X#include "xpic.h" X#include "windows.h" X#include "gels.h" X#include "draw.h" X#include "assert.h" X Xstatic int x_1, y_1, x_2, y_2; /* Corners of box, ellipse, ends of line */ Xstatic int xmin, xmax, ymin, ymax; /* Bounding box */ Xstatic PointList *ptList; X X/* Handles events for LINE mode */ Xline_event(evtype, mx, my) Xint evtype; /* One of MOUSE, LEFT, MIDDLE, RIGHT */ Xint mx, my; /* Snapped position of the mouse */ X{ X switch (evtype) { X case MOTION | START_MODE: X case RIGHT | START_MODE: X case MIDDLE | START_MODE: X case REDRAW | START_MODE: X break; X case MOTION | END_MODE: X if (((line_arrow & ST_ARROW) != 0) && (nVerts == 1)) X Arrow(picDpy, picWin, x_2, y_2, x_1, y_1, gcInvert); X if ((line_arrow & EN_ARROW) != 0) X Arrow(picDpy, picWin, x_1, y_1, x_2, y_2, gcInvert); X line(picWin, x_1, y_1, x_2, y_2, gcInvert); X x_2 = mx; X y_2 = my; X line(picWin, x_1, y_1, x_2, y_2, gcInvert); X if (((line_arrow & ST_ARROW) != 0) && (nVerts == 1)) X Arrow(picDpy, picWin, x_2, y_2, x_1, y_1, gcInvert); X if ((line_arrow & EN_ARROW) != 0) X Arrow(picDpy, picWin, x_1, y_1, x_2, y_2, gcInvert); X break; X case LEFT | START_MODE: X xmin = xmax = x_1 = x_2 = verts[0].x = mx; X ymin = ymax = y_1 = y_2 = verts[0].y = my; X nVerts = 1; X drawingMode = END_MODE; X break; X case LEFT | END_MODE: X if (nVerts + 1 >= maxVerts) { X maxVerts += INC_VERTS; X#ifdef DEBUG X (void) fprintf(stderr, "Reallocing verts to %d\n", maxVerts); X#endif X if ((verts = (XPoint *) realloc((char *) verts, X (unsigned) (maxVerts * sizeof(XPoint)))) == NULL) { X message("No more memory for vertices"); X break; X } X } X if (((line_arrow & ST_ARROW) != 0) && (nVerts == 1)) X Arrow(picDpy, picWin, x_2, y_2, x_1, y_1, gcInvert); X if ((line_arrow & EN_ARROW) != 0) X Arrow(picDpy, picWin, x_1, y_1, x_2, y_2, gcInvert); X line(picWin, x_1, y_1, x_2, y_2, gcInvert); X verts[nVerts].x = x_2 = mx; X verts[nVerts].y = y_2 = my; X xmin = MIN(xmin, mx); X xmax = MAX(xmax, mx); X ymin = MIN(ymin, my); X ymax = MAX(ymax, my); X line(picWin, x_1, y_1, x_2, y_2, gcInvert); X if (((line_arrow & ST_ARROW) != 0) && (nVerts == 1)) X Arrow(picDpy, picWin, x_2, y_2, x_1, y_1, gcNormal); X nVerts++; X x_1 = x_2; X y_1 = y_2; X break; X case RIGHT | END_MODE: X if (((line_arrow & ST_ARROW) != 0) && (nVerts == 1)) X Arrow(picDpy, picWin, x_2, y_2, x_1, y_1, gcInvert); X if ((line_arrow & EN_ARROW) != 0) X Arrow(picDpy, picWin, x_1, y_1, x_2, y_2, gcInvert); X line(picWin, x_1, y_1, x_2, y_2, gcInvert); X drawingMode = START_MODE; X if (nVerts <= 1) X break; X if ((ptList = NewPtList(verts, nVerts)) == NULL) X message("No more memory for line/spline"); X else { X int i; X /* X * erase the whole line - we must do it a segment at a time, X * because that's the way we drew it when rubber banding. If we X * draw the entire polyline at one go, then the dashes don't X * match. Then we redraw the polyline at one go. X */ X for (i = nVerts - 1; i > 0; i--) { X line(picWin, verts[i-1].x, verts[i-1].y, X verts[i].x, verts[i].y, gcInvert); X } X drawlines(picDpy,picWin, gcNormal, verts, nVerts, CoordModeOrigin); X if (((line_arrow & EN_ARROW) != 0) && (nVerts > 1)) X Arrow(picDpy, picWin, verts[nVerts - 2].x, verts[nVerts - 2].y, X x_1, y_1, gcNormal); X AddLineGel(&(CurrentCell->gelList), LINE, ptList, X line_type | line_arrow, xmin, ymin, xmax, ymax, lineThickness); X FreeGel(CurrentCell->undoList); X CurrentCell->undoList = NULL; X CurrentCell->undo = 1; X CurrentCell->saved |= MODIFIED; X } X break; X case MIDDLE | END_MODE: X if ((line_arrow & ST_ARROW) != 0) X Arrow(picDpy, picWin, verts[1].x, verts[1].y, verts[0].x, X verts[0].y, gcInvert); X if ((line_arrow & EN_ARROW) != 0) X Arrow(picDpy, picWin, x_1, y_1, x_2, y_2, gcInvert); X verts[nVerts].x = mx; X verts[nVerts].y = my; X /* X * erase the whole line - we must do it a segment at a time, because X * that's the way we drew it when rubber banding. If we draw the X * entire polyline at one go, then the dashes don't match X */ X for (; nVerts > 0; nVerts--) { X line(picWin, verts[nVerts-1].x, verts[nVerts-1].y, X verts[nVerts].x, verts[nVerts].y, gcInvert); X } X drawingMode = START_MODE; X break; X case REDRAW | END_MODE: X drawlines(picDpy, picWin, gcInvert, verts, nVerts, CoordModeOrigin); X line(picWin, x_1, y_1, x_2, y_2, gcInvert); X if (((line_arrow & ST_ARROW) != 0) && (nVerts == 1)) X Arrow(picDpy, picWin, x_2, y_2, x_1, y_1, gcInvert); X if ((line_arrow & EN_ARROW) != 0) X Arrow(picDpy, picWin, x_1, y_1, x_2, y_2, gcInvert); X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "Hey! Unknown LINE mode %d", drawingMode); X message(errstring); X#endif X break; X } X ASSERT(allock(), "line_event"); X} X X Xline_abort() X{ X line_event((RIGHT | drawingMode), 0, 0); X} X X Xline_adj(evtype, gel, mx, my) Xint evtype; XGel *gel; Xint mx, my; X{ X static XPoint *v; X static XPoint *adjusted; X static int arrowstyle, start, end, npts; X static Gel *linegel, *oldlinegel; X /* X * Will not need to process MOTION|START_MODE, RIGHT|START_MODE, X * REDRAW|START_MODE - these are taken care of in X * the adj_element routine. X */ X switch(evtype) { X case MOTION | END_MODE: X DrawLineSection(v, npts, tmpGcInvert, start, end); X adjusted->x = mx; X adjusted->y = my; X DrawLineSection(v, npts, tmpGcInvert, start, end); X break; X case LEFT | START_MODE: X linegel = CopyGel(gel, 1); X oldlinegel = gel; X gel = NULL; X GetClosestLinePoint(linegel, mx, my, &v, &npts, &adjusted, X &start, &end); X /* Line has been erased in element_adjust, so we redraw inverted */ X GelDraw(linegel, INVERT); X drawingMode = END_MODE; X setwidth(tmpGcNormal, linegel->linewidth); X setwidth(tmpGcInvert, linegel->linewidth); X SETDASHES(tmpGcNormal, getlinestyle(linegel->attributes)) X SETDASHES(tmpGcInvert, getlinestyle(linegel->attributes)) X arrowstyle = getlinearrow(linegel->attributes); X start = start && (arrowstyle & ST_ARROW); X end = end && (arrowstyle & EN_ARROW); X break; X case LEFT | END_MODE: X DrawLineSection(v, npts, tmpGcInvert, start, end); X adjusted->x = mx; X adjusted->y = my; X update_box(linegel->b_box, mx, my); X GelDraw(linegel, DRAW); X (void) PushGel(&(CurrentCell->gelList), linegel); X linegel = NULL; X FreeGel(CurrentCell->undoList); X CurrentCell->undoList = oldlinegel; X CurrentCell->undo = 1; X CurrentCell->saved |= MODIFIED; X drawingMode = START_MODE; X break; X case RIGHT | END_MODE: X case MIDDLE | END_MODE: X DrawLineSection(v, npts, tmpGcInvert, start, end); X GelDraw(oldlinegel, DRAW); X (void) PushUnderUndo(&(CurrentCell->gelList), oldlinegel, X CurrentCell->undo); X oldlinegel = NULL; X FreeGel(linegel); X linegel = NULL; X if (evtype == (MIDDLE | END_MODE)) X ClearGelFlags(CurrentCell->gelList); X drawingMode = START_MODE; X break; X case MIDDLE | START_MODE: X ClearGelFlags(CurrentCell->gelList); X break; X case REDRAW | END_MODE: X DrawLineSection(v, npts, tmpGcInvert, start, end); X break; X default: X#ifdef DEBUG X (void) sprintf(errstring, "Hey! Unknown mode %d in line_adj", X evtype); X message(errstring); X#endif X break; X } X ASSERT(allock(), "line_adj"); X} X X XDrawLineSection(v, npts, gc, start, end) XXPoint *v; XGC gc; Xint start, end, npts; X{ X drawlines(picDpy, picWin, gc, v, npts, CoordModeOrigin); X X if (start) X Arrow(picDpy, picWin, v[1].x, v[1].y, v[0].x, v[0].y, gc); X if (end) X Arrow(picDpy, picWin, v[0].x, v[0].y, v[1].x, v[1].y, gc); X} X X X/* X * Finds the closest point in the line gel 'g' to mx, my and X * puts the points in 'v'. Caller must allocate space for v. 'adjusted' X * will point to the closest point in the gel pointlist, and start and X * end will be set depending on whether the point is the start or end X * point. 'npts' is the number of points in v, usually 3, but 2 if one X * of the points is an endpoint. X */ XGetClosestLinePoint(g, mx, my, v, npts, adjusted, start, end) XGel *g; XXPoint **v; XXPoint **adjusted; Xint *start, *end, *npts; Xint mx, my; X{ X register int i; X int mindist = MAXINT; X int dist; X int closest; X int n = ((PointList *) g->data)->nVerts; X XPoint *vertices = ((PointList *) g->data)->v; X X *adjusted = vertices; X for (i = 0; i < n; i++, vertices++) { X dist = (vertices->x - mx)*(vertices->x - mx) + X (vertices->y - my)*(vertices->y - my); X if (dist < mindist) { X closest = i; X *adjusted = vertices; X mindist = dist; X } X } X *npts = 3; X if (closest == 0) { X *start = TRUE; X *v = *adjusted; X (*npts)--; X } else { X *start = FALSE; X *v = *adjusted - 1; X } X if (closest == n - 1) { X *end = TRUE; X (*npts)--; X } else { X *end = FALSE; X } X ASSERT((*npts != 1), "One point line"); X} X X X/* X * Finds distance of point from a line. This is the distance of the closest X * segment of the line from the point. The distance of a segment from the X * point is the perpendicular distance of the point from the segment, if the X * perpendicular intersects the segment, else it is the distance of the X * closest endpoint. All distances are integer distances, good enough for us. X */ Xint Xline_distance(gel, xp, yp) XGel *gel; Xint xp, yp; X{ X int n = ((PointList *) gel->data)->nVerts; X XPoint *v1 = ((PointList *) gel->data)->v; X XPoint *v2 = v1; X int dx; X int dy; X double t; X int distsqr; X int closest = MAXINT; X int xm, ym; X X for(v2++; --n > 0; v1++, v2++) { X /* Compute intersection of perpendicular with line segment */ X dx = v2->x - v1->x; X dy = v2->y - v1->y; X if (dx == 0) { X xm = v1->x; X ym = yp; X t = ym - v1->y; X t /= dy; X } else if (dy == 0) { X xm = xp; X ym = v1->y; X t = xm - v1->x; X t /= dx; X } else { X double slope = dy; X double c = (v2->x * v1->y - v1->x * v2->y); X X slope /= dx; X c /= dx; X X xm = (yp + xp / slope - c) / (slope + 1.0 / slope); X ym = slope * xm + c; X t = xm - v1->x; X t /= dx; X } X /* X * If perpendicular intersects an extension of the segment, then use X * the closer endpoint X */ X if (t < 0.0) { X xm = v1->x; X ym = v1->y; X } else if (t > 1.0) { X xm = v2->x; X ym = v2->y; X } X distsqr = (xp - xm) * (xp - xm) + (yp - ym) * (yp - ym); X closest = MIN(distsqr, closest); X } X return((int) sqrt((double) closest)); X} END_OF_FILE if test 10261 -ne `wc -c <'xpic/obj_line.c'`; then echo shar: \"'xpic/obj_line.c'\" unpacked with wrong size! fi # end of 'xpic/obj_line.c' fi echo shar: End of archive 7 \(of 15\). cp /dev/null ark7isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 15 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0