🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.windows.x.apps
Article #764

BETA Version of XPaint 2.0 -- Part 3 / 5

#764
From: David Koblas
Date: Thu, 09 Jul 1992 21:07
2028 lines
59609 bytes
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 3 (of 5)."
# Contents:  BoxOp.c CircleOp.c Event.c FillOp.c Hash.c Menu.c
#   Pattern.c ReadWritePNM.c SelectAreaOp.c SelectBoxOp.c
# Wrapped by koblas@bananajr on Thu Jul  9 14:00:22 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'BoxOp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'BoxOp.c'\"
else
echo shar: Extracting \"'BoxOp.c'\" \(4320 characters\)
sed "s/^X//" >'BoxOp.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include "xpaint.h"
X
Xtypedef struct {
X	int	rx, ry;
X	int	startX, startY, endX, endY;
X	int	drawn;
X	Boolean	type;		/* Is is a rectangle or square? */
X	Boolean	fill;		/* Filled rectangle? */
X} LocalInfo;
X
X#define MKRECT(rect, sx, sy, ex, ey, typeFlag) do {			\
X	if (typeFlag) {							\
X		(rect)->width  = MIN(ABS(sx - ex),ABS(sy - ey));	\
X		(rect)->height = (rect)->width;				\
X		(rect)->x = (ex - sx < 0) ? sx - (rect)->width : sx;	\
X		(rect)->y = (ey - sy < 0) ? sy - (rect)->height : sy;	\
X	} else {							\
X		(rect)->x      = MIN(sx, ex);				\
X		(rect)->y      = MIN(sy, ey);				\
X		(rect)->width  = MAX(sx, ex) - (rect)->x;		\
X		(rect)->height = MAX(sy, ey) - (rect)->y;		\
X	}								\
X} while (0)
X
X
Xstatic void	press(Widget w, LocalInfo *l, 
X				XButtonEvent *event, OpInfo *info) 
X{
X	l->rx   = info->x;
X	l->ry   = info->y;
X	l->endX = l->startX = event->x;
X	l->endY = l->startY = event->y;
X
X	l->type = (event->state & ShiftMask);
X
X	l->drawn = False;
X}
X
Xstatic void	motion(Widget w, LocalInfo *l, 
X				XMotionEvent *event, OpInfo *info) 
X{
X	XRectangle	rect;
X
X	if (l->drawn) {
X		MKRECT(&rect, l->startX, l->startY, l->endX, l->endY, l->type);
X
X		XDrawRectangles(XtDisplay(w), XtWindow(w), Global.gcx, &rect,1);
X	}
X
X	l->endX = event->x;
X	l->endY = event->y;
X
X	/*
X	**  Really set this flag in the if statement
X	*/
X	if (l->drawn = (l->startX != l->endX && l->startY != l->endY)) {
X		MKRECT(&rect, l->startX, l->startY, l->endX, l->endY, l->type);
X
X		XDrawRectangles(XtDisplay(w), XtWindow(w), Global.gcx,&rect, 1);
X	}
X}
X
Xstatic void	release(Widget w, LocalInfo *l, 
X				XButtonEvent *event, OpInfo *info) 
X{
X	XRectangle	rect;
X
X	if (l->drawn && info->surface == opWindow) {
X		MKRECT(&rect, l->startX, l->startY, l->endX, l->endY, l->type);
X
X		XDrawRectangles(XtDisplay(w), XtWindow(w), Global.gcx,&rect, 1);
X	}
X
X	if (info->isFat && info->surface == opWindow)
X		return;
X
X	MKRECT(&rect, l->rx, l->ry, event->x, event->y, l->type);
X
X	UndoStart(w, info);
X
X	if (l->fill) {
X		XFillRectangles(XtDisplay(w), info->drawable, 
X					info->filled_gc, &rect, 1);
X	} else {
X		XDrawRectangles(XtDisplay(w), info->drawable, 
X					info->filled_gc, &rect, 1);
X	}
X
X	if (info->surface == opPixmap) {
X		rect.width++;
X		rect.height++;
X		UndoSetRectangle(w, &rect);
X		PwUpdate(w, &rect, False);
X	}
X}
X
X/*
X**  Public routines
X*/
X
Xvoid *BoxAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo *)XtMalloc(sizeof(LocalInfo));
X
X	l->fill = False;
X
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	SetCrossHairCursor(w);
X
X	return l;
X}
Xvoid BoxRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X
X	XtFree(l);
X}
X
Xvoid *FBoxAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo *)XtMalloc(sizeof(LocalInfo));
X
X	l->fill = True;
X
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	SetCrossHairCursor(w);
X	
X	return l;
X}
Xvoid FBoxRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X
X	XtFree(l);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'BoxOp.c'\"
if test 4321 -ne `wc -c <'BoxOp.c'`; then
    echo shar: \"'BoxOp.c'\" unpacked with wrong size!
fi
# end of 'BoxOp.c'
fi
if test -f 'CircleOp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'CircleOp.c'\"
else
echo shar: Extracting \"'CircleOp.c'\" \(5758 characters\)
sed "s/^X//" >'CircleOp.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include "xpaint.h"
X
X#define	FILL		0x02
X#define CIRCLE		0x01
X#define	ISFILL(x)	((x) & FILL)
X#define ISCIRCLE(x)	((x) & CIRCLE)
X
Xtypedef struct {
X	int	rx, ry, startX, startY, endX, endY, drawn, typeFlag, flag;
X} LocalInfo;
X
X/*
X**  Warning this define picks up "flag" from scope
X*/
X#define MKRECT(rect, sx, sy, ex, ey, flag) do {				\
X	(rect)->x      = MIN(sx, ex);					\
X	(rect)->y      = MIN(sy, ey);					\
X	(rect)->width  = MAX(sx, ex) - (rect)->x;			\
X	(rect)->height = MAX(sy, ey) - (rect)->y;			\
X	if (ISCIRCLE(flag)) {						\
X		(rect)->width = MIN((rect)->width, (rect)->height);	\
X		(rect)->height = (rect)->width; 			\
X	}								\
X	if ((rect)->x != sx) (rect)->x = sx - (rect)->width;		\
X 	if ((rect)->y != sy) (rect)->y = sy - (rect)->height;		\
X	(rect)->angle1 = 0;						\
X	(rect)->angle2 = 360*64;					\
X	} while (0)
X
Xstatic void	press(Widget w, LocalInfo *l, 
X			XButtonEvent *event, OpInfo *info) 
X{
X	l->rx   = info->x;
X	l->ry   = info->y;
X	l->endX = l->startX = event->x;
X	l->endY = l->startY = event->y;
X
X	l->typeFlag = (event->state & ShiftMask) ? CIRCLE : 0;
X
X	l->drawn = False;
X}
X
Xstatic void	motion(Widget w, LocalInfo *l, 
X			XMotionEvent *event, OpInfo *info) 
X{
X	XArc	rect;
X
X	if (l->drawn) {
X		MKRECT(&rect, l->startX,l->startY,l->endX,l->endY,l->typeFlag);
X		XDrawArcs(XtDisplay(w), info->drawable, Global.gcx, &rect, 1);
X	}
X
X	l->endX = event->x;
X	l->endY = event->y;
X
X	/*
X	**  Really set this flag in the if statement
X	*/
X	if (l->drawn = (l->startX != l->endX && l->startY != l->endY)) {
X		MKRECT(&rect, l->startX,l->startY,l->endX,l->endY,l->typeFlag);
X		XDrawArcs(XtDisplay(w), info->drawable, Global.gcx, &rect, 1);
X	}
X}
X
Xstatic void	release(Widget w, LocalInfo *l, 
X			XButtonEvent *event, OpInfo *info) 
X{
X	XArc		rect;
X	XRectangle	undo;
X
X	if (l->drawn && info->surface == opWindow) {
X		MKRECT(&rect, l->startX,l->startY,l->endX,l->endY,l->typeFlag);
X		XDrawArcs(XtDisplay(w), info->drawable, Global.gcx, &rect, 1);
X	}
X
X	if (info->surface == opWindow && info->isFat)
X		return;
X
X	MKRECT(&rect, l->rx, l->ry, event->x, event->y, l->typeFlag);
X
X	UndoStart(w, info);
X
X	if (ISFILL(l->flag)) {
X		XFillArcs(XtDisplay(w), info->drawable, 
X				info->filled_gc, &rect, 1);
X	} else {
X		XDrawArcs(XtDisplay(w), info->drawable, 
X				info->filled_gc, &rect, 1);
X	}
X
X	if (info->surface == opPixmap) {
X		XYtoRECT(l->rx, l->ry, event->x, event->y, &undo);
X		UndoSetRectangle(w, &undo);
X		PwUpdate(w, &undo, False);
X	}
X}
X
X/*
X**  Those public functions
X*/
Xvoid *CircleAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X	l->flag = CIRCLE;
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	SetCrossHairCursor(w);
X	return l;
X}
Xvoid CircleRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	XtFree(l);
X}
Xvoid *FCircleAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X	l->flag = CIRCLE|FILL;
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	SetCrossHairCursor(w);
X	return l;
X}
Xvoid FCircleRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	XtFree(l);
X}
Xvoid *OvalAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X	l->flag = 0;
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	SetCrossHairCursor(w);
X	return l;
X}
Xvoid OvalRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	XtFree(l);
X}
Xvoid *FOvalAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X	l->flag = FILL;
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	SetCrossHairCursor(w);
X	return l;
X}
Xvoid FOvalRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, release, l);
X	XtFree(l);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'CircleOp.c'\"
if test 5759 -ne `wc -c <'CircleOp.c'`; then
    echo shar: \"'CircleOp.c'\" unpacked with wrong size!
fi
# end of 'CircleOp.c'
fi
if test -f 'Event.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Event.c'\"
else
echo shar: Extracting \"'Event.c'\" \(5686 characters\)
sed "s/^X//" >'Event.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/IntrinsicP.h>
X#include "PaintP.h"
X#include "xpaint.h"
X
Xtypedef struct data_s {
X	Widget		w;
X        void            (*func)(Widget, void *, XEvent *, OpInfo *);
X        void            *data;
X        int             mask, flag;
X	int		surfMask;
X        struct data_s   *next;
X	OpInfo		*info;
X} data_t;
X
Xstatic data_t	*list = NULL;
X
Xstatic data_t	**widgetEvent(Widget w, data_t *look)
X{
X	static data_t	*d[10];
X	data_t		*cur = list;
X	int		n = 0;
X
X	while (cur != NULL) {
X		if (cur->mask == look->mask &&
X		    cur->flag == look->flag &&
X		    cur->func == look->func &&
X		    cur->w    == w) 
X			d[n++] = cur;
X		cur = cur->next;
X	}
X
X	d[n] = NULL;
X
X	return n == 0 ? NULL : d;
X}
X
Xstatic void opHandleEvent(Widget w, data_t *data, XEvent *event)
X{
X	int		grid;
X	PaintWidget	paint = (PaintWidget)w;
X	OpInfo		*info = data->info;
X	static Window	lastWindow  = None;
X	static Display	*lastDisplay = None;
X	static int	lastType;
X	static int	lastX, lastY;
X	Boolean		same;
X	int		zoom;
X
X	/*
X	**  Figure out if there is a grid, either by
X	**   choice or by zooming
X	*/
X	if (info->isFat = ((zoom = paint->paint.zoom) > 1))
X		grid = paint->paint.zoom;
X	else if (paint->paint.gridOn)
X		grid = paint->paint.grid;
X	else
X		grid = 1;
X
X	same = (event->xany.window == lastWindow) && 
X	       (event->xany.display == lastDisplay) && 
X               (event->xany.type == lastType);
X	lastWindow  = event->xany.window;
X	lastDisplay = event->xany.display;
X	lastType    = event->xany.type;
X
X	/*
X	**  Snap events to the grid
X	*/
X	if (((event->type == ButtonPress) ||
X	     (event->type == ButtonRelease) ||
X	     (event->type == MotionNotify)) && grid > 1) {
X		switch (event->type) {
X		case ButtonPress:
X		case ButtonRelease:
X			event->xbutton.x = event->xbutton.x - 
X						(event->xbutton.x % grid);
X			event->xbutton.y = event->xbutton.y - 
X						(event->xbutton.y % grid);
X			if (same && lastX == event->xbutton.x && lastY == event->xbutton.y)
X				return;
X			break;
X		case MotionNotify:
X			event->xmotion.x = event->xmotion.x - 
X						(event->xmotion.x % grid);
X			event->xmotion.y = event->xmotion.y - 
X						(event->xmotion.y % grid);
X			if (same && lastX == event->xmotion.x && lastY == event->xmotion.y)
X				return;
X			break;
X		}
X		lastX = event->xmotion.x;
X		lastY = event->xmotion.y;
X	} else {
X		lastX = lastY = -1;
X	}
X
X	if (event->type == MotionNotify) {
X		info->x = (event->xmotion.x / zoom) + paint->paint.zoomX;
X		info->y = (event->xmotion.y / zoom) + paint->paint.zoomY;
X	} else if (event->type == ButtonPress || event->type == ButtonRelease) {
X		info->x = (event->xbutton.x / zoom) + paint->paint.zoomX;
X		info->y = (event->xbutton.y / zoom) + paint->paint.zoomY;
X	}
X
X	if (data->surfMask & opWindow) {
X		info->surface  = opWindow;
X		info->drawable = event->xany.window;
X		data->func(w, data->data, event, info);
X	}
X	if (event->type == MotionNotify) {
X		event->xmotion.x = info->x;
X		event->xmotion.y = info->y;
X	} else if (event->type == ButtonPress || event->type == ButtonRelease) {
X		event->xbutton.x = info->x;
X		event->xbutton.y = info->y;
X	}
X
X	if (data->surfMask & opPixmap) {
X		info->surface = opPixmap;
X		info->drawable = paint->paint.undo ? paint->paint.undo->pixmap : None;
X		data->func(w, data->data, event, info);
X	}
X}
X
Xvoid OpAddEventHandler(Widget w, int surfMask, int mask, Boolean flag, 
X		void (*func)(Widget,void *, XEvent*, OpInfo *), void *data)
X{
X	PaintWidget	paint = (PaintWidget)w;
X	data_t		*new = (data_t *)XtMalloc(sizeof(data_t));
X	OpInfo		*info;
X	data_t		*cur;
X
X	if (new == NULL)
X		return;
X
X	new->w        = w;
X	new->func     = func;
X	new->data     = data;
X	new->mask     = mask;
X	new->flag     = flag;
X	new->surfMask = surfMask;
X
X	info = new->info = (OpInfo*)XtMalloc(sizeof(OpInfo));
X
X	/*
X	**  Now build the approprate info structure
X	*/
X	if (paint->paint.paint == None) {
X		/*
X		** If this is a paint widget, this is easy
X		*/
X		info->filled_gc = paint->paint.fgc;
X		info->solid_gc = paint->paint.sgc;
X		info->base_gc = paint->paint.igc;
X	} else {
X		/*
X		** For a fatbits paint widget get paint information
X		*/
X		info->filled_gc = ((PaintWidget)paint->paint.paint)->paint.fgc;
X		info->solid_gc  = ((PaintWidget)paint->paint.paint)->paint.sgc;
X		info->base_gc   = ((PaintWidget)paint->paint.paint)->paint.igc;
X	}
X
X	new->next = list;
X	list      = new;
X
X        XtAddEventHandler(w, mask, flag, opHandleEvent, new);
X}
X
Xvoid OpRemoveEventHandler(Widget w, int surfMask, int mask, Boolean flag, 
X		void (*func)(Widget, void *, XEvent*, OpInfo *), void *data)
X{
X	data_t	*cur   = list;
X	data_t	**prev = &list;
X
X	while (cur != NULL) {
X		if (cur->w        == w    &&
X		    cur->data     == data &&
X		    cur->mask     == mask &&
X		    cur->flag     == flag &&
X		    cur->surfMask == surfMask &&
X		    cur->func     == func)
X			break;
X		prev = &cur->next;
X		cur = cur->next;
X	}
X
X	if (cur == NULL)
X		return;
X
X        XtRemoveEventHandler(w, mask, flag, opHandleEvent, cur);
X
X	*prev = cur->next;
X
X	cur->info->refCount--;
X	if (cur->info->refCount == 0)
X		XtFree(cur->info);
X
X	XtFree(cur);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'Event.c'\"
if test 5687 -ne `wc -c <'Event.c'`; then
    echo shar: \"'Event.c'\" unpacked with wrong size!
fi
# end of 'Event.c'
fi
if test -f 'FillOp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'FillOp.c'\"
else
echo shar: Extracting \"'FillOp.c'\" \(5656 characters\)
sed "s/^X//" >'FillOp.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/cursorfont.h>
X#include "Paint.h"
X#include "xpaint.h"
X
Xtypedef struct {
X	Boolean		done;
X	Pixmap		pix;
X} LocalInfo;
X
Xstatic XImage	*source, *mask;
Xstatic int	xMin, yMin, xMax, yMax;
X
X/*
X * A Seed Fill Algorithm
X * by Paul Heckbert
X * from "Graphics Gems", Academic Press, 1990
X */
X
X/*
X * fill.c : simple seed fill program
X * Calls pixelread() to read pixels, pixelwrite() to write pixels.
X *
X * Paul Heckbert        13 Sept 1982, 28 Jan 1987
X */
X
X#define pixelwrite(x,y)	{ 			\
X		XPutPixel(mask, x, y, True); 	\
X		xMin = MIN(xMin, x);		\
X		yMin = MIN(yMin, y);		\
X		xMax = MAX(xMax, x);		\
X		yMax = MAX(yMax, y);		\
X	}
X#define pixelread(x,y)	(XGetPixel(mask, x, y) ? ~ov : XGetPixel(source, x, y))
X
Xtypedef struct {int y, xl, xr, dy;} Segment;
X/*
X * Filled horizontal segment of scanline y for xl<=x<=xr.
X * Parent segment was on line y-dy.  dy=1 or -1
X */
X
X#define STACKSIZE 10000              /* max depth of stack */
X
X#define PUSH(Y, XL, XR, DY)  /* push new segment on stack */ \
X    if (sp<stack+STACKSIZE && Y+(DY)>=0 && Y+(DY)<=height) \
X    {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
X
X#define POP(Y, XL, XR, DY)    /* pop segment off stack */ \
X    {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
X
X/*
X * fill: set the pixel at (x,y) and all of its 4-connected neighbors
X * with the same pixel value to the new pixel value nv.
X * A 4-connected neighbor is a pixel above, below, left, or right of a pixel.
X */
X
Xstatic void fill(int x, int y, int width, int height)
X{
X    int start, x1, x2, dy = 0;
X    Pixel ov;      /* old pixel value */
X    Segment stack[STACKSIZE], *sp = stack;   /* stack of filled segments */
X
X    ov = pixelread(x, y);              /* read pv at seed point */
X    PUSH(y, x, x, 1);                      /* needed in some cases */
X    PUSH(y+1, x, x, -1);                /* seed segment (popped 1st) */
X
X    while (sp>stack) {
X        /* pop segment off stack and fill a neighboring scan line */
X        POP(y, x1, x2, dy);
X        /*
X         * segment of scan line y-dy for x1<=x<=x2 was previously filled,
X         */
X        for (x = x1; x >= 0 && pixelread(x, y) == ov; x--)
X            pixelwrite(x, y);
X        if (x >= x1) {
X          for (x++; x<=x2 && pixelread(x, y)!=ov; x++);
X          start = x;
X	  if (x > x2)
X	    continue;
X	} else {
X          start = x+1;
X          if (start<x1) PUSH(y, start, x1-1, -dy);       /* leak on left? */
X          x = x1+1;
X	}
X        do {
X            for (; x<=width && pixelread(x, y)==ov; x++)
X                pixelwrite(x, y);
X            PUSH(y, start, x-1, dy);
X            if (x>x2+1) PUSH(y, x2+1, x-1, -dy);       /* leak on right? */
X            for (x++; x<=x2 && pixelread(x, y)!=ov; x++);
X            start = x;
X        } while (x<=x2);
X    }
X}
X
Xstatic void     press(Widget w, LocalInfo *l, XButtonEvent *event, OpInfo *info)
X{
X	XRectangle	undo;
X	char		*mData;
X	int		width, height;
X	GC		gc;
X	int		fillWidth, fillHeight;
X	XGCValues	values;
X
X	UndoStart(w, info);
X
X	SetWatchCursor(w);
X
X	XtVaGetValues(w, XtNdrawWidth, &width, XtNdrawHeight, &height, NULL);
X
X	if ((mData = XtMalloc(width * height * sizeof(char))) == NULL)
X		return;
X
X	bzero(mData, width * height * sizeof(char));
X
X	if ((mask   = XCreateImage(XtDisplay(w), NULL,
X				1, XYBitmap, 0, mData, 
X				width, height, 8, 0)) == NULL) {
X		XtFree(mData);
X		return;
X	}
X
X	if ((source = XGetImage(XtDisplay(w), info->drawable, 0, 0,
X				width, height, ~0, ZPixmap)) == NULL) {
X		XDestroyImage(mask);
X		return;
X	}
X
X	xMin = xMax = event->x;
X	yMin = yMax = event->y;
X
X	fill(event->x, event->y, width, height);
X
X	XDestroyImage(source);
X
X	fillWidth  = (xMax - xMin) + 1;
X	fillHeight = (yMax - yMin) + 1;
X
X	l->pix = XCreatePixmap(XtDisplay(w), XtWindow(w), fillWidth,fillHeight,1);
X
X	values.foreground = True;
X	values.background = False;
X	gc = XCreateGC(XtDisplay(w), l->pix, GCForeground|GCBackground, &values);
X	XPutImage(XtDisplay(w), l->pix, gc, mask, xMin, yMin,
X				0, 0, fillWidth, fillHeight);
X	XFreeGC(XtDisplay(w),gc);
X
X	XDestroyImage(mask);
X
X	XSetClipOrigin(XtDisplay(w), info->filled_gc, xMin, yMin);
X	XSetClipMask(XtDisplay(w), info->filled_gc, l->pix);
X
X	XFillRectangle(XtDisplay(w), info->drawable, info->filled_gc, 
X			xMin, yMin, fillWidth, fillHeight);
X	if (!info->isFat)
X		XFillRectangle(XtDisplay(w), XtWindow(w), info->filled_gc, 
X			xMin, yMin, fillWidth, fillHeight);
X
X	XSetClipMask(XtDisplay(w), info->filled_gc, None);
X	XFreePixmap(XtDisplay(w), l->pix);
X
X	XYtoRECT(xMin, yMin, xMax + 1, yMax + 1, &undo);
X	UndoSetRectangle(w, &undo);
X	PwUpdate(w, &undo, False);
X
X	SetCrossHairCursor(w);
X}
X
Xvoid *FillAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X	l->done = False;
X	OpAddEventHandler(w, opPixmap, ButtonPressMask, FALSE, press, l);
X	SetCrossHairCursor(w);
X	return l;
X}
Xvoid FillRemove(Widget w, LocalInfo *l)
X{
X	OpRemoveEventHandler(w, opPixmap, ButtonPressMask, FALSE, press, l);
X	XtFree(l);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'FillOp.c'\"
if test 5657 -ne `wc -c <'FillOp.c'`; then
    echo shar: \"'FillOp.c'\" unpacked with wrong size!
fi
# end of 'FillOp.c'
fi
if test -f 'Hash.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Hash.c'\"
else
echo shar: Extracting \"'Hash.c'\" \(5404 characters\)
sed "s/^X//" >'Hash.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X/*
X**  A Simple useful set of hash routines:
X**
X**      void	*HashCreate(int (*cmp)(void *, void*), void (*free)(void *), int nelem)
X**	  -- Create a hash table with cmp() function, and optional free() function.
X**	void	 HashDestroy(HashTable *tbl)
X**	  -- Destroy the whole hash table
X**	void	*HashFind(HashTable *tbl, int value, void *val)
X** 	  -- Find an element in the hash table, returns NULL if not found
X**	int	 HashAdd(HashTable *tbl, int value, void *val)
X**	  -- Add an element to the table, returns non-zero on failure
X**	int	 HashRemove(HashTable *tbl, int value, void *elem)
X**	  -- Remove a particular hash entry from the table, pointer compairson
X**	int	 HashAll(HashTable *tbl, int (*func)(void *))
X**	  -- Apply function to all elements of the table, until func() returns non-zero
X*/
X
X
X#include <stdio.h>
X#if 0
X#include <malloc.h>
X#else
Xextern void free(void *);
Xextern void *malloc(unsigned int);
X#endif
X
Xtypedef struct hash_s {
X	int		*val;
X	struct hash_s	*left, *right, **parentp;
X} HashElement;
X
Xtypedef struct {
X	int		(*cmp)(void *, void *);
X	void		(*free)(void *);
X	HashElement	**table;
X	int		nelem;
X} HashTable;
X
X/*
X**  Create a hashtable, with cmp() compare function, and free() free function
X**       with a hash table width of nelem, free can be null in which case the
X**       system free function will be used.
X*/
Xvoid	*HashCreate(int (*cmp)(void *, void *), void (*free)(void *), int nelem)
X{
X	HashTable	*new;
X	int		i;
X
X	new = (HashTable*)malloc(sizeof(HashTable));
X	new->nelem = nelem;
X	new->cmp   = cmp;
X	new->free  = free;
X	new->table = (HashElement **)malloc(nelem * sizeof(HashElement*));
X
X	for (i = 0; i < nelem; i++)
X		new->table[i] = NULL;
X
X	return (void*)new;
X}
X
Xstatic void hashDestory(void (*func)(void *), HashElement *entry)
X{
X	if (entry->left != NULL) {
X		hashDestory(func, entry->left);
X		free(entry->left);
X	}
X	if (entry->right != NULL) {
X		hashDestory(func, entry->right);
X		free(entry->right);
X	}
X
X	if (entry->val)
X		func(entry->val);
X}
X
X/*
X**  Free the entire hash table, and all elements
X*/
Xvoid	HashDestroy(HashTable *tbl)
X{
X	int		i;
X	HashElement	*cur;
X
X	for (i = 0; i < tbl->nelem; i++) {
X		if (tbl->table[i] != NULL) {
X			hashDestory(tbl->free ? tbl->free : free, tbl->table[i]);
X			free(tbl->table[i]);
X		}
X	}
X	free(tbl);
X}
X
X/*
X**  Find a particular element in the hash table, returns NULL if it isn't found
X*/
Xvoid	*HashFind(HashTable *tbl, int value, void *val)
X{
X	HashElement	*cur = tbl->table[value];
X	int		v;
X
X	while (cur != NULL) {
X		if ((v = tbl->cmp(cur->val, val)) == 0)
X			return cur->val;
X
X		if (v < 0)
X			cur = cur->left;
X		else
X			cur = cur->right;
X	}
X
X	return NULL;
X}
X
X/*
X**  And a new element to the has table, returns non-zero on failure
X*/
Xint	HashAdd(HashTable *tbl, int value, void *val)
X{
X	HashElement	*new = (HashElement*)malloc(sizeof(HashElement));
X	HashElement	*cur = tbl->table[value];
X	HashElement	**prev;
X	
X	if (new == NULL)
X		return 1;
X
X	new->left    = NULL; 
X	new->right   = NULL; 
X	new->val     = val;
X	new->parentp = NULL;
X
X	prev = &tbl->table[value];
X	while (cur != NULL) {
X		if (tbl->cmp(cur->val, val) < 0) {
X			prev = &cur->left;
X			cur  = cur->left;
X		} else {
X			prev = &cur->right;
X			cur  = cur->right;
X		}
X	}
X	*prev = new;
X	new->parentp = prev;
X
X	return 0;
X}
X
Xstatic HashElement 	*find(HashElement *pos, void *elem)
X{
X	HashElement	*v;
X
X	if (pos->val == elem)
X		return pos;
X	if (pos->left != NULL && (v = find(pos->left, elem)) != NULL)
X		return v;
X	if (pos->right != NULL && (v = find(pos->right, elem)) != NULL)
X		return v;
X	return NULL;
X}
X
X/*
X**  Remove a particular element from the hash table, done using pointer compares
X*/
Xint	HashRemove(HashTable *tbl, int value, void *elem)
X{
X	HashElement	*v;
X	
X	if (elem == NULL)
X		return 1;
X
X	if ((v = find(tbl->table[value], elem)) == NULL) 
X		return 0;
X
X	if (v->left != NULL) {
X		*v->parentp = v->left;
X		v->left->parentp = v->parentp;
X		if (v->right != NULL) {
X			HashElement	*cur = v->left, **prev;
X			while (cur != NULL) {
X				if (tbl->cmp(cur->val, v->right->val) < 0) {
X					prev = &cur->left;
X					cur  =  cur->left;
X				} else {
X					prev = &cur->right;
X					cur  =  cur->right;
X				}
X			}
X			*prev = v->right;
X			v->right->parentp = prev;
X		}
X	} else {
X		/* no left side, just attach the right */
X		*v->parentp = v->right;
X		if (v->right != NULL)
X			v->right->parentp = v->parentp;
X	}
X	free(v);
X
X	return 1;
X}
X
Xstatic int overAll(HashElement *elem, int (*func)(void *)) 
X{
X	if (elem == NULL)
X		return 0;
X	if (elem->val != NULL)
X		func(elem->val);
X
X	return overAll(elem->left, func) || overAll(elem->right, func);
X}
X
Xint	HashAll(HashTable *tbl, int (*func)(void *))
X{
X	int	i;
X
X	for (i = 0; i < tbl->nelem; i++)
X		if (overAll(tbl->table[i], func))
X			return 1;
X	return 0;
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'Hash.c'\"
if test 5405 -ne `wc -c <'Hash.c'`; then
    echo shar: \"'Hash.c'\" unpacked with wrong size!
fi
# end of 'Hash.c'
fi
if test -f 'Menu.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Menu.c'\"
else
echo shar: Extracting \"'Menu.c'\" \(2539 characters\)
sed "s/^X//" >'Menu.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/bitmaps/xlogo16>
X
X#include <X11/Xaw/MenuButton.h>
X#include <X11/Xaw/SimpleMenu.h>
X#include <X11/Xaw/SmeBSB.h>
X#include <X11/Xaw/SmeLine.h>
X#include <X11/Xaw/Form.h>
X
X#include "xpaint.h"
X
XWidget MenuBarCreate(Widget parent, int nbar, PaintMenuBar bar[])
X{
X	static int	checkWidth = 0;
X	int	list, item;
X	Widget	button, menu, entry;
X	Widget	prevButton = NULL;
X	char 	menuPopupName[80];
X	Widget	ret;
X
X	/*
X	**  If there is more than one entry in this bar
X	**    reparent it.
X	*/
X	if (nbar > 1) 
X		parent = XtVaCreateManagedWidget("menu", 
X				formWidgetClass, parent,
X				XtNborderWidth, 0,
X				NULL);
X
X	if (checkWidth == 0) {
X		checkWidth = 16;	/* XXX */
X	}
X	
X	for (list = 0; list < nbar; list++) {
X		strcpy(menuPopupName, bar[list].name);
X		strcat(menuPopupName, "Menu");
X
X		button = XtVaCreateManagedWidget(bar[list].name, 
X				menuButtonWidgetClass, parent,
X				XtNmenuName,  XtNewString(menuPopupName),
X				XtNfromHoriz, prevButton,
X				NULL);
X		prevButton = button;
X
X		menu = XtCreatePopupShell(menuPopupName,
X				simpleMenuWidgetClass, button,
X				NULL, 0);
X			
X		for (item = 0; item < bar[list].nitems; item++) {
X			if (bar[list].items[item].name[0] == '\0')
X				entry = XtVaCreateManagedWidget("seperator",
X					smeLineObjectClass, menu,
X					NULL);
X			else if (bar[list].items[item].flags & MF_CHECK)
X				entry = XtVaCreateManagedWidget(
X					bar[list].items[item].name,
X					smeBSBObjectClass, menu,
X					XtNleftMargin, checkWidth + 4,
X					NULL);
X			else
X				entry = XtVaCreateManagedWidget(
X					bar[list].items[item].name,
X					smeBSBObjectClass, menu,
X					NULL);
X			
X			if (bar[list].items[item].callback != NULL)
X				XtAddCallback(entry, XtNcallback, 
X						bar[list].items[item].callback, 
X						bar[list].items[item].data);
X		}
X	}
X
X	return (nbar > 1) ? parent : button;
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'Menu.c'\"
if test 2540 -ne `wc -c <'Menu.c'`; then
    echo shar: \"'Menu.c'\" unpacked with wrong size!
fi
# end of 'Menu.c'
fi
if test -f 'Pattern.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Pattern.c'\"
else
echo shar: Extracting \"'Pattern.c'\" \(5333 characters\)
sed "s/^X//" >'Pattern.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/IntrinsicP.h>
X#include <X11/Shell.h>
X#include <X11/StringDefs.h>
X#include <X11/Xaw/Form.h>
X#include <X11/Xaw/Scrollbar.h>
X#include <X11/Xaw/Command.h>
X#include <X11/CoreP.h>
X#include <X11/Xaw/ToggleP.h>
X#include "Colormap.h"
X#include "Paint.h"
X#include "xpaint.h"
X
X#define	RED	1
X#define	GREEN	2
X#define	BLUE	3
X
Xtypedef struct {
X	int	add;
X	Pixmap	pix;
X	Widget	fat, widget, paint;
X} LocalInfo;
X
Xstatic void scrollCallback(Widget w, Widget cwidget, int val, int col)
X{
X	XColor	color;
X
X	CwGetColor(cwidget, &color);
X
X	switch (col) {
X	case RED:	color.red = val << 8; break;
X	case GREEN:	color.green = val << 8; break;
X	case BLUE:	color.blue = val << 8; break;
X	}
X
X	CwSetColor(cwidget, &color);
X}
X
Xstatic void redCallback(Widget w, Widget color, float *percent)
X{
X	int	val = *percent * 255;
X	scrollCallback(w, color, val, RED);
X}
Xstatic void greenCallback(Widget w, Widget color, float *percent)
X{
X	int	val = *percent * 255;
X	scrollCallback(w, color, val, GREEN);
X}
Xstatic void blueCallback(Widget w, Widget color, float *percent)
X{
X	int	val = *percent * 255;
X	scrollCallback(w, color, val, BLUE);
X}
Xstatic void colorCallback(Widget w, Widget* bars, XColor *color)
X{
X	float	r = (float)color->red   / 65536.0;
X	float	g = (float)color->green / 65536.0;
X	float	b = (float)color->blue  / 65536.0;
X
X	XtVaSetValues(bars[0], XtNtopOfThumb, &r, NULL);
X	XtVaSetValues(bars[1], XtNtopOfThumb, &g, NULL);
X	XtVaSetValues(bars[2], XtNtopOfThumb, &b, NULL);
X	XtVaSetValues(bars[3], XtNforeground, color->pixel, NULL);
X#if 0
X	XawScrollbarSetThumb(bars[0], r, -1.0);
X	XawScrollbarSetThumb(bars[1], g, -1.0);
X	XawScrollbarSetThumb(bars[2], b, -1.0);
X#endif
X}
X
Xstatic void buttonCallback(Widget w, LocalInfo *info, void *junk2)
X{
X	Pixmap	pix = None;
X	int	width, height;
X	Widget	form, icon;
X
X	if (info) {
X		PwGetPixmap(info->fat, &pix, &width, &height);
X		if (info->add) {
X			form = XtParent(info->widget);
X
X			XawFormDoLayout(form, True);
X			icon = XtVaCreateManagedWidget(NULL, 
X				toggleWidgetClass, form,
X				XtNbitmap, pix,
X				XtNradioGroup, info->widget,
X				NULL);
X			XtAddCallback(icon, XtNcallback, ToggleSetPattern, info->paint);
X			XtVaSetValues(icon, XtNradioData, icon, NULL);
X		} else {
X			Widget	data;
X
X			data = (Widget)XawToggleGetCurrent(info->widget);
X			XtVaSetValues(data, XtNbitmap, pix, NULL);
X		}
X		XtVaSetValues(info->paint, XtNpattern, pix, NULL);
X	}
X	XtDestroyWidget(GetShell(w));
X}
X
XPatternEdit(Widget w, Pixmap pix, Widget button)
X{
X	Widget		shell, form, topf;
X	Widget		color, fat;
X	Widget		red, green, blue;
X	Widget		okButton, cancelButton;
X	Colormap	cmap;
X	Widget		*bars = (Widget*)XtMalloc(sizeof(Widget)*4);
X	LocalInfo	*info = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X
X	info->paint = w;
X	info->add   = (pix == None);
X
X	XtVaGetValues(w, XtNcolormap, &cmap, NULL);
X
X	shell = XtVaCreatePopupShell("pattern", transientShellWidgetClass, w,
X				XtNcolormap, cmap,
X				NULL);
X
X	topf  = XtVaCreateManagedWidget("form", formWidgetClass, shell, 
X			NULL);
X
X	form  = XtVaCreateManagedWidget("form", formWidgetClass, topf, 
X			NULL);
X
X	if (pix == None)
X		fat = XtVaCreateManagedWidget("bits", paintWidgetClass, form, 
X				XtNpixmap, pix,
X				XtNzoom, 8,
X				XtNdrawWidth, 32,
X				XtNdrawHeight, 32,
X				NULL);
X	else
X		fat = XtVaCreateManagedWidget("bits", paintWidgetClass, form, 
X				XtNpixmap, pix,
X				XtNzoom, 8,
X				NULL);
X	color = XtVaCreateManagedWidget("cedit", colormapWidgetClass, form, 
X				XtNwidth, 256,
X				XtNheight, 256,
X				XtNcolormap, cmap,
X				XtNfromHoriz, fat,
X				NULL);
X
X	red = XtVaCreateManagedWidget("red", scrollbarWidgetClass, form,
X				XtNfromHoriz, color,
X				XtNlength, 255,
X				NULL);
X	green = XtVaCreateManagedWidget("green", scrollbarWidgetClass, form,
X				XtNfromHoriz, red,
X				XtNlength, 255,
X				NULL);
X	blue = XtVaCreateManagedWidget("blue", scrollbarWidgetClass, form,
X				XtNfromHoriz, green,
X				XtNlength, 255,
X				NULL);
X
X	XtAddCallback(red,   XtNjumpProc, redCallback, color);
X	XtAddCallback(green, XtNjumpProc, greenCallback, color);
X	XtAddCallback(blue,  XtNjumpProc, blueCallback, color);
X	bars[0] = red;
X	bars[1] = green;
X	bars[2] = blue;
X	bars[3] = fat;
X	XtAddCallback(color, XtNcallback, colorCallback, bars);
X
X	info->widget = button;
X	info->fat    = fat;
X
X	okButton = XtVaCreateManagedWidget("ok", 
X				commandWidgetClass, topf,
X				XtNfromVert, form,
X				NULL);
X
X	cancelButton = XtVaCreateManagedWidget("cancel", 
X				commandWidgetClass, topf,
X				XtNfromVert, form,
X				XtNfromHoriz, okButton,
X				NULL);
X	XtAddCallback(cancelButton, XtNcallback, buttonCallback, NULL);
X	XtAddCallback(okButton, XtNcallback, buttonCallback, info);
X
X	XtPopup(shell, XtGrabNone);
X
X	GraphicAdd(fat);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'Pattern.c'\"
if test 5334 -ne `wc -c <'Pattern.c'`; then
    echo shar: \"'Pattern.c'\" unpacked with wrong size!
fi
# end of 'Pattern.c'
fi
if test -f 'ReadWritePNM.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ReadWritePNM.c'\"
else
echo shar: Extracting \"'ReadWritePNM.c'\" \(4487 characters\)
sed "s/^X//" >'ReadWritePNM.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <stdio.h>
X#include <X11/StringDefs.h>
X#include <X11/Intrinsic.h>
X#include "libpnmrw.h"
X#include "Hash.h"
X#include "Cmap.h"
X
X#define HASH_SIZE	128
X#define	HASH(r,g,b)	((r + g + b) % HASH_SIZE)
X
Xtypedef struct he {
X	int		r,g,b;
X	int		pixel;
X	struct he	*next;
X} hashEntry;
X
Xstatic void	*htable = NULL;
Xstatic int	format;
X
Xstatic int	getPixel(Display *dpy, Cmap *cmap, int r, int g, int b)
X{
X	XColor		c;
X
X	c.red   = r * 256;
X	c.green = g * 256;
X	c.blue  = b * 256;
X
X	CmapAlloc(cmap, &c);
X
X	return c.pixel;
X}
X
Xint	ReadPNM(Widget w, char *file, unsigned int *width, unsigned int *height, Pixmap *pix, Colormap *cmap)
X{
X	static int	inited = FALSE;
X	int		y, x;
X	XImage		*xim = NULL;
X	xel		**image = NULL;
X	xel		*curel;
X	char		*data = NULL;
X	int		depth = DefaultDepthOfScreen(XtScreen(w));
X	int		status = TRUE;
X	GC		gc;
X	xelval		maxval;
X	int		format;
X	int		isPPM;
X	FILE		*fd;
X	int		wth, hth;
X	Display		*dpy = XtDisplay(w);
X	Cmap		*map;
X
X	*pix = None;
X
X	pnm_init2(file);
X	if ((fd = fopen(file, "r")) == NULL)
X		return TRUE;
X	if ((image=pnm_readpnm(fd, &wth, &hth, &maxval, &format))==NULL)
X		return TRUE;
X	isPPM = PPM_FORMAT_TYPE(format) == PPM_TYPE;
X	*width = wth;
X	*height = hth;
X	fclose(fd);
X
X	map = CreateCmap(w);
X	*cmap = map->cmap;
X
X	if ((*pix = XCreatePixmap(dpy, XtWindow(w),
X				  *width, *height, depth)) == None)
X		goto out;
X
X	if ((data = XtMalloc(*width * *height * sizeof(char) * 4)) == NULL)
X		goto out;
X	if ((xim = XCreateImage(dpy, NULL, depth,
X			ZPixmap, 0, data, *width, *height, 8, 0)) == NULL)
X		goto out;
X
X	for (curel = image[y = 0]; y < *height; curel = image[++y]) {
X		for (x = 0; x < *width; x++, curel++) {
X			int		pixel;
X			XColor		c;
X			int		val;
X
X			if (isPPM) {
X				pixel = getPixel(dpy, map, 
X						255 * PPM_GETR(*curel) / maxval,
X						255 * PPM_GETG(*curel) / maxval,
X						255 * PPM_GETB(*curel) / maxval);
X			} else {
X				val = 255 * PNM_GET1(*curel) / maxval;
X				pixel = getPixel(dpy, map, val, val, val);
X			}
X
X			XPutPixel(xim, x, y, pixel);
X		}
X	}
X
X	gc = XtGetGC(w, 0, NULL);
X	XPutImage(dpy, *pix, gc, xim, 0, 0, 0, 0, *width, *height);
X	XtReleaseGC(w, gc);
X
X	status = FALSE;
Xout:
X	if (image != NULL)
X		pnm_freearray(image, *height);
X	if (xim != NULL)
X		XDestroyImage(xim);
X	if (status && *pix != None)
X		XFreePixmap(dpy, *pix);
X	return status;
X}
X
Xint  writeCMP(XColor *a, XColor *b)
X{
X	return a->pixel - b->pixel;
X}
X
Xvoid find(Display *dpy, Colormap cmap, long val, xel *pv)
X{
X	XColor	c, *col;
X
X	c.pixel = val;
X
X	if ((col = HashFind(htable, val % HASH_SIZE, &c)) == NULL) {
X		col = (XColor *)malloc(sizeof(XColor));
X		col->pixel = val;
X		col->flags = DoRed|DoGreen|DoBlue;
X		XQueryColor(dpy, cmap, col);
X		HashAdd(htable, val % HASH_SIZE, col);
X	}
X
X	PPM_ASSIGN(*pv, col->red >> 8, col->green >> 8, col->blue >> 8);
X
X	if (format != PPM_FORMAT) 
X		if (col->red != col->green || col->green != col->blue)
X			format = PPM_FORMAT;
X	if (format == PBM_FORMAT)
X		if (col->red != 0 && (col->red & 0xff00) != 0xff00) 
X			format = PGM_FORMAT;
X}
X
Xint WritePNM(Widget w, char *file, int width, int height, Pixmap pix)
X{
X	xel		**xels;
X	XImage		*xim = XGetImage(XtDisplay(w), pix, 0, 0, width, height, ~0, ZPixmap);
X	Colormap	cmap;
X	Display		*dpy = XtDisplay(w);
X	int		x, y;
X	FILE		*fd;
X
X	if (xim == NULL)
X		return TRUE;
X	if ((fd = fopen(file, "w")) == NULL) {
X		XDestroyImage(xim);
X		return TRUE;
X	}
X
X	XtVaGetValues(w, XtNcolormap, &cmap, NULL);
X
X	format = PBM_FORMAT;
X	htable = HashCreate(writeCMP, NULL, HASH_SIZE);
X
X	pnm_init2(file);
X
X	xels = pnm_allocarray(width, height);
X
X	for (y = 0; y < height; y++)
X		for (x = 0; x < width; x++) 
X			find(dpy, cmap, XGetPixel(xim, x, y), &xels[y][x]);
X
X	pnm_writepnm(fd, xels, width, height, 255, format, FALSE);
X
X	pnm_freearray(xels, height);
X	XDestroyImage(xim);
X	fclose(fd);
X	HashDestroy(htable);
X	htable = NULL;
X
X	return FALSE;
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'ReadWritePNM.c'\"
if test 4488 -ne `wc -c <'ReadWritePNM.c'`; then
    echo shar: \"'ReadWritePNM.c'\" unpacked with wrong size!
fi
# end of 'ReadWritePNM.c'
fi
if test -f 'SelectAreaOp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'SelectAreaOp.c'\"
else
echo shar: Extracting \"'SelectAreaOp.c'\" \(5743 characters\)
sed "s/^X//" >'SelectAreaOp.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/cursorfont.h>
X#include "xpaint.h"
X
Xtypedef struct {
X	int		startX, startY, endX, endY;
X	int		id, pos;
X	GC		gc;
X	Boolean		drawn;
X
X	XPoint		pbuf[1024];
X	int		npoints;
X} LocalInfo;
X
X#if 0
X
X
X#define	DELAY		100
X
Xstatic void	freeFunc(void *region)
X{
X	XPoint	*pnts = ((RegionArea_t*)region)->points;
X
X	XtFree(pnts);
X}
X
Xstatic Pixmap	generateMask(Widget wid)
X{
X	PaintWidget	w = (PaintWidget)wid;
X	GC		gc;
X	XGCValues	values;
X	Pixmap		pix;
X	RegionArea_t	*area = (RegionArea_t*)w->paint.region;
X	int		x, y;
X
X	pix = XCreatePixmap(XtDisplay(w), XtWindow(w),
X				area->bbox.width,
X				area->bbox.height, 1);
X
X	values.foreground = False;
X	values.background = False;
X	gc = XCreateGC(XtDisplay(w), pix, GCForeground|GCBackground, &values);
X	XFillRectangle(XtDisplay(w), pix, gc, 0, 0, 
X				area->bbox.width, area->bbox.height);
X	XSetForeground(XtDisplay(w), gc, True);
X
X	x = area->points[0].x;
X	y = area->points[0].y;
X
X	area->points[0].x = area->startX;
X	area->points[0].y = area->startY;
X
X	XFillPolygon(XtDisplay(w), pix, gc, area->points, area->npoints - 1,
X				Complex, CoordModePrevious);
X
X	area->points[0].x = x;
X	area->points[0].y = y;
X
X	XFreeGC(XtDisplay(w), gc);
X
X	return pix;
X}
X
Xstatic void	flash(PaintWidget w)
X{
X	XGCValues	values;
X
X	XDrawLines(XtDisplay(w), XtWindow(w), gc, 
X				w->paint.region->area.points, 
X				w->paint.region->area.npoints, 
X				CoordModePrevious);
X
X	values.dash_offset = (++pos) % 8;
X	XChangeGC(XtDisplay(w), gc, GCDashOffset, &values);
X	XDrawLines(XtDisplay(w), XtWindow(w), gc, 
X				w->paint.region->area.points, 
X				w->paint.region->area.npoints, 
X				CoordModePrevious);
X
X	id = XtAddTimeOut(DELAY, flash, w);
X}
X
Xstatic void	press(PaintWidget w, void *junk, XButtonEvent *event) 
X{
X	drawn = False;
X
X	if (id != NULL) {
X		XDrawLines(XtDisplay(w), XtWindow(w), gc, 
X				w->paint.region->area.points, 
X				w->paint.region->area.npoints, 
X				CoordModePrevious);
X		XtRemoveTimeOut(id);
X
X		id = NULL;
X	}
X
X	pbuf[0].x = event->x;
X	pbuf[0].y = event->y;
X	npoints = 1;
X}
X
Xstatic void	motion(PaintWidget w, void *junk, XMotionEvent *event) 
X{
X	pbuf[npoints].x = event->x;
X	pbuf[npoints].y = event->y;
X
X	XDrawLine(XtDisplay(w), XtWindow(w), w->paint.gcx,
X		pbuf[npoints-1].x, pbuf[npoints-1].y,
X		pbuf[npoints].x,   pbuf[npoints].y);
X
X	npoints++;
X}
X
Xstatic void	release(PaintWidget w, void *junk, XButtonEvent *event) 
X{
X	RegionArea_t	*area;
X	int		i;
X	int		xmin, xmax, ymin, ymax;
X	XRectangle	rect;
X
X	XDrawLines(XtDisplay(w), XtWindow(w), w->paint.gcx,
X			pbuf, npoints, CoordModeOrigin);
X	if (npoints > 2) {
X		XDrawPoints(XtDisplay(w), XtWindow(w), w->paint.gcx,
X				pbuf+1, npoints-2, CoordModeOrigin);
X	}
X
X	pbuf[npoints].x = pbuf[0].x;
X	pbuf[npoints].y = pbuf[0].y;
X	npoints++;
X
X	area = (RegionArea_t*)XtMalloc(sizeof(RegionArea_t));
X	area->mask    = generateMask;
X	area->free    = freeFunc;
X	area->points  = (XPoint*)XtMalloc(sizeof(XPoint) * npoints);
X	area->npoints = npoints;
X
X	xmin = xmax = pbuf[0].x;
X	ymin = ymax = pbuf[0].y;
X	area->points[0].x = pbuf[0].x;
X	area->points[0].y = pbuf[0].y;
X	for (i = 1; i < npoints; i++) {
X		xmin = MIN(xmin, pbuf[i].x);
X		ymin = MIN(ymin, pbuf[i].y);
X		xmax = MAX(xmax, pbuf[i].x);
X		ymax = MAX(ymax, pbuf[i].y);
X
X		area->points[i].x = pbuf[i].x - pbuf[i - 1].x;
X		area->points[i].y = pbuf[i].y - pbuf[i - 1].y;
X	}
X
X	area->startX      = pbuf[0].x - xmin;
X	area->startY      = pbuf[0].y - ymin;
X
X	area->bbox.x      = xmin;
X	area->bbox.y      = ymin;
X	area->bbox.width  = xmax - xmin;
X	area->bbox.height = ymax - ymin;
X
X	SETREGION(w->paint.region, area);
X
X	XDrawLines(XtDisplay(w), XtWindow(w), gc, 
X				w->paint.region->area.points, 
X				w->paint.region->area.npoints, 
X				CoordModePrevious);
X
X	id = XtAddTimeOut(DELAY, flash, w);
X}
X#endif
X
X/*
X**  Those public functions
X*/
Xvoid *SelectAreaAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X#if 0
X	PaintWidget	pw = (PaintWidget)w;
X	static char	dashList[] = { 4, 4 };
X	Cursor		c;
X
X	OpAddEventHandler(w, ButtonPressMask, FALSE, press, NULL);
X	OpAddEventHandler(w, ButtonMotionMask, FALSE, motion, NULL);
X	OpAddEventHandler(w, ButtonReleaseMask, FALSE, release, NULL);
X
X	gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, NULL);
X	XCopyGC(XtDisplay(w), pw->paint.gcx, ~0, gc);
X	XSetLineAttributes(XtDisplay(w), gc, 0, 
X				LineOnOffDash, CapButt, JoinMiter);
X	XSetDashes(XtDisplay(w), gc, 0, dashList, XtNumber(dashList));
X
X	c = XCreateFontCursor(XtDisplay(w), XC_pencil);
X	XDefineCursor(XtDisplay(w), XtWindow(w), c);
X#endif
X	return l;
X}
Xvoid SelectAreaRemove(Widget w, LocalInfo *l)
X{
X#if 0
X	PaintWidget	pw = (PaintWidget)w;
X
X	if (id != NULL) {
X		XDrawLines(XtDisplay(w), XtWindow(w), gc, 
X				pw->paint.region->area.points, 
X				pw->paint.region->area.npoints, 
X				CoordModePrevious);
X		XtRemoveTimeOut(id);
X
X		id = NULL;
X	}
X
X	XFreeGC(XtDisplay(w), gc);
X
X	OpRemoveEventHandler(w, ButtonPressMask, FALSE, press, NULL);
X	OpRemoveEventHandler(w, ButtonMotionMask, FALSE, motion, NULL);
X	OpRemoveEventHandler(w, ButtonReleaseMask, FALSE, release, NULL);
X#endif
X	XtFree(l);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'SelectAreaOp.c'\"
if test 5744 -ne `wc -c <'SelectAreaOp.c'`; then
    echo shar: \"'SelectAreaOp.c'\" unpacked with wrong size!
fi
# end of 'SelectAreaOp.c'
fi
if test -f 'SelectBoxOp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'SelectBoxOp.c'\"
else
echo shar: Extracting \"'SelectBoxOp.c'\" \(5106 characters\)
sed "s/^X//" >'SelectBoxOp.c' <<'END_OF_FILE'
X/* +-------------------------------------------------------------------+ */
X/* | Copyright 1992, David Koblas.                                     | */
X/* |   Permission to use, copy, modify, and distribute this software   | */
X/* |   and its documentation for any purpose and without fee is hereby | */
X/* |   granted, provided that the above copyright notice appear in all | */
X/* |   copies and that both that copyright notice and this permission  | */
X/* |   notice appear in supporting documentation.  This software is    | */
X/* |   provided "as is" without express or implied warranty.           | */
X/* +-------------------------------------------------------------------+ */
X
X#include <X11/IntrinsicP.h>
X#include <X11/StringDefs.h>
X#include <X11/cursorfont.h>
X#include "xpaint.h"
X#include "PaintP.h"
X
Xtypedef struct {
X	int		rx, ry;
X	int		startX, startY, endX, endY;
X	GC		gc;
X	int		id, pos;
X	Boolean		drawn;
X} LocalInfo;
X
X#define MKRECT(rect, sx, sy, ex, ey) do {                               \
X                        (rect)->x      = MIN(sx, ex);                   \
X                        (rect)->y      = MIN(sy, ey);                   \
X                        (rect)->width  = MAX(sx, ex) - (rect)->x;       \
X                        (rect)->height = MAX(sy, ey) - (rect)->y;       \
X                } while (0)
X
X#define	DELAY		50
X
Xstatic Pixmap	generateMask(Widget wid)
X{
X	PaintWidget	w = (PaintWidget)wid;
X	GC		gc;
X	XGCValues	values;
X	Pixmap		pix;
X	RegionBox_t	*box = (RegionBox_t*)w->paint.region;
X
X	pix = XCreatePixmap(XtDisplay(w), XtWindow(w),
X				box->bbox.width,
X				box->bbox.height, 1);
X
X	values.foreground = True;
X	values.background = False;
X	gc = XCreateGC(XtDisplay(w), pix, GCForeground|GCBackground, &values);
X
X	XFillRectangles(XtDisplay(w), pix, gc, &box->bbox, 1);
X
X	XFreeGC(XtDisplay(w), gc);
X
X	return pix;
X}
X
X#if 0
Xstatic void	flash(PaintWidget w)
X{
X	XGCValues	values;
X
X	XDrawRectangles(XtDisplay(w), XtWindow(w), gc, 
X				&w->paint.region->box.bbox, 1);
X
X	values.dash_offset = (++pos) % 8;
X	XChangeGC(XtDisplay(w), gc, GCDashOffset, &values);
X	XDrawRectangles(XtDisplay(w), XtWindow(w), gc, 
X				&w->paint.region->box.bbox, 1);
X
X	id = XtAddTimeOut(DELAY, flash, w);
X}
X#endif
X
Xstatic void	press(PaintWidget w, LocalInfo *l, XButtonEvent *event, OpInfo *info) 
X{
X	if (info->surface != opWindow)
X		return;
X
X	l->rx     = info->x;
X	l->ry     = info->y;
X	l->startX = l->endX = event->x;
X	l->startY = l->endY = event->y;
X
X	l->drawn = False;
X
X#if 0
X	if (id != NULL) {
X		XDrawRectangles(XtDisplay(w), XtWindow(w), gc, 
X				&w->paint.region->area.bbox, 1);
X		XtRemoveTimeOut(id);
X
X		id = NULL;
X	}
X#endif
X}
X
Xstatic void	motion(PaintWidget w, LocalInfo *l, XMotionEvent *event, OpInfo *info) 
X{
X	XRectangle	rect;
X
X	if (info->surface != opWindow)
X		return;
X
X	if (l->drawn) {
X		MKRECT(&rect, l->startX, l->startY, l->endX, l->endY);
X		XDrawRectangles(XtDisplay(w), XtWindow(w), 
X					Global.gcx, &rect, 1);
X	}
X
X	l->endX = event->x;
X	l->endY = event->y;
X
X	if (l->drawn = (l->startX != l->endX && l->startY != l->endY)) {
X		MKRECT(&rect, l->startX, l->startY, 
X				l->endX, l->endY);
X
X		XDrawRectangles(XtDisplay(w), XtWindow(w), 
X					Global.gcx, &rect, 1);
X	}
X}
X
Xstatic void	release(PaintWidget w, LocalInfo *l, XButtonEvent *event, OpInfo *info) 
X{
X	RegionBox_t	*box;
X	XRectangle	rect;
X
X	if (info->surface != opWindow)
X		return;
X
X	if (l->drawn) {
X		MKRECT(&rect, l->startX, l->startY, l->endX, l->endY);
X
X		XDrawRectangles(XtDisplay(w), XtWindow(w), 
X					Global.gcx, &rect, 1);
X	}
X
X	box = (RegionBox_t*)XtMalloc(sizeof(RegionBox_t));
X	MKRECT(&box->bbox, l->rx, l->ry, info->x, info->y);
X	box->mask = generateMask;
X	box->free = NULL;
X
X	SETREGION(w->paint.region, box);
X
X#if 0
X	XDrawRectangles(XtDisplay(w), XtWindow(w), gc, 
X				&w->paint.region->box.bbox, 1);
X
X	l->id = XtAddTimeOut(DELAY, flash, w);
X#endif
X}
X
X/*
X**  Those public functions
X*/
Xvoid *SelectBoxAdd(Widget w)
X{
X	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
X	PaintWidget	pw = (PaintWidget)w;
X	Cursor		c;
X	static char     dashList[] = { 4, 4 };
X
X	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpAddEventHandler(w, opWindow, ButtonReleaseMask, FALSE, release, l);
X
X#if 0
X	gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, NULL);
X	XCopyGC(XtDisplay(w), Global.gcx, ~0, gc);
X	XSetLineAttributes(XtDisplay(w), gc, 0,
X				LineOnOffDash, CapButt, JoinMiter);
X	XSetDashes(XtDisplay(w), gc, 0, dashList, XtNumber(dashList));
X#endif
X
X	c = XCreateFontCursor(XtDisplay(w), XC_pencil);
X	XDefineCursor(XtDisplay(w), XtWindow(w), c);
X
X	return l;
X}
X
Xvoid SelectBoxRemove(Widget w, LocalInfo *l)
X{
X	PaintWidget	pw = (PaintWidget)w;
X
X#if 0
X	if (id != NULL) {
X		XDrawRectangles(XtDisplay(w), XtWindow(w), gc, 
X				&pw->paint.region->box.bbox, 1);
X		XtRemoveTimeOut(id);
X
X		id = NULL;
X	}
X
X	XFreeGC(XtDisplay(w), gc);
X#endif
X
X	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, press, l);
X	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, motion, l);
X	OpRemoveEventHandler(w, opWindow, ButtonReleaseMask, FALSE, release, l);
X}

END_OF_FILE
echo shar: NEWLINE appended to \"'SelectBoxOp.c'\"
if test 5107 -ne `wc -c <'SelectBoxOp.c'`; then
    echo shar: \"'SelectBoxOp.c'\" unpacked with wrong size!
fi
# end of 'SelectBoxOp.c'
fi
echo shar: End of archive 3 \(of 5\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
name : David Koblas                        domain: koblas@mips.com
quote: If you have sex in your car, does that make it a recreational vehicle?
Technical Details
Message-ID: <l5pakqINNmi2@spim.mips.com>
Path: arc1-ca.pugleaf.net!wupost!zaphod.mps.ohio-state.edu!mips!koblas

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!wupost!zaphod.mps.ohio-state.edu!mips!koblas
From: koblas@mips.com (David Koblas)
Newsgroups: comp.windows.x.apps,alt.sources
Subject: BETA Version of XPaint 2.0  -- Part 3 / 5
Date: 9 Jul 1992 21:07:38 GMT
Organization: MIPS Computer Systems, Sunnyvale, California
Lines: 2028
Distribution: world
Message-ID: <l5pakqINNmi2@spim.mips.com>
NNTP-Posting-Host: lloyd.mips.com
Originator: koblas@lloyd.mips.com