DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: B T

⟦9ed2d8a62⟧ TextFile

    Length: 27226 (0x6a5a)
    Types: TextFile
    Names: »BitmapEdit.c«

Derivation

└─⟦8648bda34⟧ Bits:30007244 EUUGD5_II: X11R5
    └─⟦2ca9b63e1⟧ »./contrib-1/contrib-1.00« 
        └─⟦a8392fb20⟧ 
            └─⟦this⟧ »contrib/examples/OReilly/Vol4/ch10/BitmapEdit.c« 

TextFile

/*
 * Copyright 1989 O'Reilly and Associates, Inc.
 * See ../Copyright for complete rights and liability information.
 */


/*
 * BitmapEdit.c - bitmap editor widget.
 */

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>

#include <stdio.h>

#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>

#include "BitmapEdiP.h"

#define INTERNAL_WIDTH	2
#define INTERNAL_HEIGHT 4

#define DEFAULT_PIXMAP_WIDTH	32	/* in cells */
#define DEFAULT_PIXMAP_HEIGHT	32	/* in cells */

#define DEFAULT_CELL_SIZE	30	/* in pixels */

/* values for instance variable is_drawn, and for char elements
 * in cell state array. */
#define DRAWN 'y'
#define UNDRAWN 'n'

/* modes for drawing */
#define DRAW 'y'
#define UNDRAW 'n'

#define MAXLINES 1000	/* max of horiz or vertical cells */
#define SCROLLBARWIDTH 15


#define offset(field) XtOffset(BitmapEditWidget, field)

static XtResource resources[] = {
	{XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
		offset(bitmapEdit.foreground), XtRString, XtDefaultForeground },
	{XtNselectionForeground, XtCSelectionForeground, XtRPixel, 
		sizeof(Pixel), offset(bitmapEdit.selectionForeground), 
		XtRString, "red" },
	{XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
		offset(bitmapEdit.callback), XtRCallback, NULL},
	{XtNcellSizeInPixels, XtCCellSizeInPixels, XtRInt, sizeof(int),
		offset(bitmapEdit.cell_size_in_pixels), XtRImmediate, 
		(XtPointer)DEFAULT_CELL_SIZE },
	{XtNpixmapWidthInCells, XtCPixmapWidthInCells, XtRDimension, 
		sizeof(Dimension), offset(bitmapEdit.pixmap_width_in_cells), 
		XtRImmediate, (XtPointer)DEFAULT_PIXMAP_WIDTH },
	{XtNpixmapHeightInCells, XtCPixmapHeightInCells, XtRDimension, 
		sizeof(Dimension), offset(bitmapEdit.pixmap_height_in_cells), 
		XtRImmediate, (XtPointer)DEFAULT_PIXMAP_HEIGHT },
	{XtNcurX, XtCCurX, XtRInt, sizeof(int),
		offset(bitmapEdit.cur_x), XtRImmediate, (XtPointer) 0},
	{XtNcurY, XtCCurY, XtRInt, sizeof(int),
		offset(bitmapEdit.cur_y), XtRImmediate, (XtPointer) 0},
};

/* Declaration of methods */

static void Initialize();
static void Redisplay();
static void Destroy();
static void Resize();
static Boolean SetValues();

/* these Core methods not needed by BitmapEdit:
 *
 * static void ClassInitialize();
 * static void Realize();
 * static void ClassInitialize();
 * static void Realize();
 */

/* the following are private functions unique to BitmapEdit */
static void DrawPixmaps(), HighlightCell(), draw_box(), DrawCell();

/* private functions for selections */
static void lose_ownership_proc(), transfer_done_proc(), 
		requestor_callback();
static Boolean convert_proc();

/* the following are actions of BitmapEdit */
static void DoCell(), UndoCell(), ToggleCell();
static void TopLeft(), BottomRight(), DragHighlight();
static void PasteSelection();

/* The following are public functions of BitmapEdit, declared extern
 * in the public include file: */
char *BitmapEditGetArrayString(); 

static char defaultTranslations[] =
	"Shift<Btn1Down>:	TopLeft()		  \n\
	 Shift<Btn1Motion>:  DragHighlight()	\n\
	 Shift<Btn1Up>:	  BottomRight()	  \n\
	 Shift<Btn2Down>:	PasteSelection()   \n\
	 ~Shift<Btn1Down>:	DoCell()		  \n\
	 ~Shift<Btn2Down>:	UndoCell()		\n\
	 ~Shift<Btn3Down>:	ToggleCell()	  \n\
	 ~Shift<Btn1Motion>:  DoCell()		  \n\
	 ~Shift<Btn2Motion>:  UndoCell()		\n\
	 ~Shift<Btn3Motion>:  ToggleCell()";

static XtActionsRec actions[] = {
	{"DoCell", DoCell},
	{"UndoCell", UndoCell},
	{"ToggleCell", ToggleCell},
	{"TopLeft", TopLeft},
	{"DragHighlight", DragHighlight},
	{"BottomRight", BottomRight},
	{"PasteSelection", PasteSelection},
};

/* definition in BitmapEdit.h */
static BitmapEditPointInfo info;

BitmapEditClassRec bitmapEditClassRec = {
	{
	/* core_class fields */
	/* superclass	  	 */ (WidgetClass) &widgetClassRec,
	/* class_name	  	 */ "BitmapEdit",
	/* widget_size	  	 */ sizeof(BitmapEditRec),
	/* class_initialize   	 */ NULL,
	/* class_part_initialize	 */ NULL,
	/* class_inited	   	 */ FALSE,
	/* initialize	  	 */ Initialize,
	/* initialize_hook		 */ NULL,
	/* realize		  	 */ XtInheritRealize,
	/* actions		  	 */ actions,
	/* num_actions	  	 */ XtNumber(actions),
	/* resources	  	 */ resources,
	/* num_resources	  	 */ XtNumber(resources),
	/* xrm_class	  	 */ NULLQUARK,
	/* compress_motion	  	 */ TRUE,
	/* compress_exposure  	 */ TRUE,
	/* compress_enterleave	 */ TRUE,
	/* visible_interest	  	 */ FALSE,
	/* destroy		  	 */ Destroy,
	/* resize		  	 */ Resize,
	/* expose		  	 */ Redisplay,
	/* set_values	  	 */ SetValues,
	/* set_values_hook		 */ NULL,
	/* set_values_almost	 */ XtInheritSetValuesAlmost,
	/* get_values_hook		 */ NULL,
	/* accept_focus	 	 */ NULL,
	/* version			 */ XtVersion,
	/* callback_private   	 */ NULL,
	/* tm_table		   	 */ defaultTranslations,
	/* query_geometry		 */ NULL,
	},
	{
	/* dummy_field			   */ 0,
	},
};

WidgetClass bitmapEditWidgetClass = (WidgetClass) & bitmapEditClassRec;

static void
GetDrawGC(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	XGCValues values;
	XtGCMask mask = GCForeground | GCBackground | GCDashOffset 
			| GCDashList | GCLineStyle;

	/* 
	 * Setting foreground and background to 1 and 0 looks like a 
	 * kludge but isn't.  This GC is used for drawing
	 * into a pixmap of depth one.  Real colors are applied with a
	 * separate GC when the pixmap is copied into the window.
	 */
	values.foreground = 1;
	values.background = 0;
	values.dashes = 1;
	values.dash_offset = 0;
	values.line_style = LineOnOffDash;

	cw->bitmapEdit.draw_gc = XCreateGC(XtDisplay(cw), 
			cw->bitmapEdit.big_picture, mask, &values);
}

static void
GetUndrawGC(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	XGCValues values;
	XtGCMask mask = GCForeground | GCBackground;

	/* this looks like a kludge but isn't.  This GC is used for drawing
	 * into a pixmap of depth one.  Real colors are applied as the 
	 * pixmap is copied into the window.
	 */
	values.foreground = 0;
	values.background = 1;

	cw->bitmapEdit.undraw_gc = XCreateGC(XtDisplay(cw), 
			cw->bitmapEdit.big_picture, mask, &values);
}

static void
GetDeepUndrawGC(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	XGCValues values;
	XtGCMask mask = GCForeground | GCBackground;

	values.foreground = cw->core.background_pixel;
	values.background = cw->bitmapEdit.foreground;

	cw->bitmapEdit.deep_undraw_gc = XtGetGC(cw, mask, &values);
}

static void
GetDeepDrawGC(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	XGCValues values;
	XtGCMask mask = GCForeground | GCBackground;

	values.foreground = cw->bitmapEdit.foreground;
	values.background = cw->core.background_pixel;

	cw->bitmapEdit.deep_draw_gc = XtGetGC(cw, mask, &values);
}

/* ARGSUSED */
static void
Initialize(treq, tnew, args, num_args)
Widget treq, tnew;
ArgList args;
Cardinal *num_args;
{
	BitmapEditWidget new = (BitmapEditWidget) tnew;
	new->bitmapEdit.cur_x = 0;
	new->bitmapEdit.cur_y = 0;

	/* 
	 *  Check instance values set by resources that may be invalid. 
	 */

	if ((new->bitmapEdit.pixmap_width_in_cells < 1) ||
		(new->bitmapEdit.pixmap_height_in_cells < 1))  {
	XtWarning("BitmapEdit: pixmapWidth and/or pixmapHeight is too small\
			(using 10 x 10)."); 
		new->bitmapEdit.pixmap_width_in_cells = 10;
		new->bitmapEdit.pixmap_height_in_cells = 10;
	}

	if (new->bitmapEdit.cell_size_in_pixels < 5) {
	XtWarning("BitmapEdit: cellSize is too small (using 5)."); 
		new->bitmapEdit.cell_size_in_pixels = 5;
	}

	if ((new->bitmapEdit.cur_x < 0) ||  (new->bitmapEdit.cur_y < 0)) {
	XtWarning("BitmapEdit: cur_x and cur_y must be non-negative\
			(using 0, 0)."); 
		new->bitmapEdit.cur_x = 0;
		new->bitmapEdit.cur_y = 0;
	}

	new->bitmapEdit.cell = XtCalloc(new->bitmapEdit.pixmap_width_in_cells 
			* new->bitmapEdit.pixmap_height_in_cells, sizeof(char));

	new->bitmapEdit.pixmap_width_in_pixels = new->bitmapEdit.pixmap_width_in_cells 
			* new->bitmapEdit.cell_size_in_pixels;

	new->bitmapEdit.pixmap_height_in_pixels = new->bitmapEdit.pixmap_height_in_cells 
			* new->bitmapEdit.cell_size_in_pixels;

	if (new->core.width == 0)
	new->core.width = (new->bitmapEdit.pixmap_width_in_pixels > 300) 
			? 300 : (new->bitmapEdit.pixmap_width_in_pixels);
	if (new->core.height == 0)
	new->core.height = (new->bitmapEdit.pixmap_height_in_pixels > 300) 
			? 300 : (new->bitmapEdit.pixmap_height_in_pixels);

	CreateBigPixmap(new);

	GetDrawGC(new);
	GetUndrawGC(new);
	GetDeepDrawGC(new);
	GetDeepUndrawGC(new);

	DrawIntoBigPixmap(new);

	new->bitmapEdit.target_atom = XInternAtom(XtDisplay(new), 
			"CELL_ARRAY", False);

	/* This makes Xmu initialize the necessary atoms.
	 * Wierd, but true. */
	(void) XmuInternAtom( XtDisplay(new), XmuMakeAtom("NULL") );

	new->bitmapEdit.data = XtCalloc(
		new->bitmapEdit.pixmap_width_in_cells * 
		new->bitmapEdit.pixmap_height_in_cells, sizeof(char));
}

/* ARGSUSED */
static void
Redisplay(w, event)
Widget w;
XExposeEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	register int x, y;
	unsigned int width, height;

	if (!XtIsRealized(cw))
		return;

	if (event) {  /* called from btn-event */
		x = event->x;
		y = event->y; 
		width = event->width;
		height =  event->height;
	} 
	else {		/* called because of expose */
		x = 0;
		y = 0; 
		width = cw->bitmapEdit.pixmap_width_in_pixels;
		height = cw->bitmapEdit.pixmap_height_in_pixels;
	}

	if (DefaultDepthOfScreen(XtScreen(cw)) == 1)
		XCopyArea(XtDisplay(cw), cw->bitmapEdit.big_picture, XtWindow(cw),
				cw->bitmapEdit.deep_draw_gc, x + cw->bitmapEdit.cur_x, y + 
				cw->bitmapEdit.cur_y, width, height, x, y);
	else
		XCopyPlane(XtDisplay(cw), cw->bitmapEdit.big_picture, XtWindow(cw),
				cw->bitmapEdit.deep_draw_gc, x + cw->bitmapEdit.cur_x, y + 
				cw->bitmapEdit.cur_y, width, height, x, y, 1);

	if (cw->bitmapEdit.first_box)
		draw_box(cw, cw->bitmapEdit.select_end_x, cw->bitmapEdit.select_end_y, True);
}

/* ARGSUSED */
static Boolean
SetValues(current, request, new, args, num_args)
Widget current, request, new;
ArgList args;
Cardinal *num_args;
{
	BitmapEditWidget curcw = (BitmapEditWidget) current;
	BitmapEditWidget newcw = (BitmapEditWidget) new;
	Boolean do_redisplay = False;

	if (curcw->bitmapEdit.foreground != newcw->bitmapEdit.foreground) {
		XtReleaseGC(curcw, curcw->bitmapEdit.deep_draw_gc);
		XtReleaseGC(curcw, curcw->bitmapEdit.deep_undraw_gc);
		GetDeepDrawGC(newcw);
		GetDeepUndrawGC(newcw);
		do_redisplay = True;
	}

	if ((curcw->bitmapEdit.cur_x != newcw->bitmapEdit.cur_x) || 
			(curcw->bitmapEdit.cur_y != newcw->bitmapEdit.cur_y)) {
		do_redisplay = True;
	}

	if ((curcw->bitmapEdit.pixmap_width_in_cells != 
			newcw->bitmapEdit.pixmap_width_in_cells) || 
			(curcw->bitmapEdit.pixmap_height_in_cells != 
			newcw->bitmapEdit.pixmap_height_in_cells) || 
			(curcw->bitmapEdit.cell_size_in_pixels != 
			newcw->bitmapEdit.cell_size_in_pixels))
		XtWarning("BitmapEdit widget: pixmap_width_in_cells,\
				 pixmap_heigt, and cell_size_in_pixels resources\
				 can only be set before widget is created\n");

	return do_redisplay;
}


static void
Destroy(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	if (cw->bitmapEdit.big_picture)
		XFreePixmap(XtDisplay(cw), cw->bitmapEdit.big_picture);

	if (cw->bitmapEdit.draw_gc)
		XFreeGC(XtDisplay(cw), cw->bitmapEdit.draw_gc);

	if (cw->bitmapEdit.undraw_gc)
		XFreeGC(XtDisplay(cw), cw->bitmapEdit.undraw_gc);

	XtFree(cw->bitmapEdit.cell);
}

static void
DoCell(w, event)
Widget w;
XEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	DrawPixmaps(cw->bitmapEdit.draw_gc, DRAW, cw, event);
}

static void
UndoCell(w, event)
Widget w;
XEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	DrawPixmaps(cw->bitmapEdit.undraw_gc, UNDRAW, cw, event);
}

static void
ToggleCell(w, event)
Widget w;
XEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	static int oldx = -1, oldy = -1;
	int newx, newy;

	/* This is strictly correct, but doesn't
	 * seem to be necessary */
	if (event->type == ButtonPress) {
		newx = (cw->bitmapEdit.cur_x + ((XButtonEvent *)event)->x) 
				/ cw->bitmapEdit.cell_size_in_pixels;
		newy = (cw->bitmapEdit.cur_y + ((XButtonEvent *)event)->y) 
				/ cw->bitmapEdit.cell_size_in_pixels;
	}
	else  {
		newx = (cw->bitmapEdit.cur_x + ((XMotionEvent *)event)->x) 
				/ cw->bitmapEdit.cell_size_in_pixels;
		newy = (cw->bitmapEdit.cur_y + ((XMotionEvent *)event)->y) 
				/ cw->bitmapEdit.cell_size_in_pixels;

		if (oldx == newx && oldy == newy)
			return;
	}

	if (cw->bitmapEdit.cell[newx + newy * cw->bitmapEdit.pixmap_width_in_cells] == DRAWN)
		DrawPixmaps(cw->bitmapEdit.undraw_gc, UNDRAW, cw, event);
	else
		DrawPixmaps(cw->bitmapEdit.draw_gc, DRAW, cw, event);

	if (event->type == MotionNotify) {
	oldx = newx;
	oldy = newy;
	}
}

static void
DrawPixmaps(gc, mode, w, event)
GC gc;
char mode;
Widget w;
XButtonEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	int newx = (cw->bitmapEdit.cur_x + event->x) 
			/ cw->bitmapEdit.cell_size_in_pixels;
	int newy = (cw->bitmapEdit.cur_y + event->y) 
			/ cw->bitmapEdit.cell_size_in_pixels;
	XExposeEvent fake_event;

	/* if already done, return */
	if (cw->bitmapEdit.cell[newx + newy * 
			cw->bitmapEdit.pixmap_width_in_cells] == mode)
		return;

	DrawCell(cw, newx, newy, gc); 

	cw->bitmapEdit.cell[newx + newy * cw->bitmapEdit.pixmap_width_in_cells] = mode;

	info.mode = mode;
	info.newx = newx;
	info.newy = newy;

	fake_event.x = cw->bitmapEdit.cell_size_in_pixels * newx - cw->bitmapEdit.cur_x;
	fake_event.y = cw->bitmapEdit.cell_size_in_pixels * newy - cw->bitmapEdit.cur_y;
	fake_event.width = cw->bitmapEdit.cell_size_in_pixels;
	fake_event.height = cw->bitmapEdit.cell_size_in_pixels;

	Redisplay(cw, &fake_event);
	XtCallCallbacks(cw, XtNcallback, &info);
}

static void
DrawCell(w, x, y, gc)
Widget w;
int x, y;
GC gc;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;

	/* draw or undraw */
	XFillRectangle(XtDisplay(cw), cw->bitmapEdit.big_picture, gc,
			cw->bitmapEdit.cell_size_in_pixels * x + 2, 
			cw->bitmapEdit.cell_size_in_pixels * y + 2, 
			(unsigned int)cw->bitmapEdit.cell_size_in_pixels - 3, 
			(unsigned int)cw->bitmapEdit.cell_size_in_pixels - 3);
}

CreateBigPixmap(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	/* always a 1 bit deep pixmap, regardless of screen depth */
	cw->bitmapEdit.big_picture = XCreatePixmap(XtDisplay(cw),
			RootWindow(XtDisplay(cw), DefaultScreen(XtDisplay(cw))),
			cw->bitmapEdit.pixmap_width_in_pixels, cw->bitmapEdit.pixmap_height_in_pixels, 1);
}

DrawIntoBigPixmap(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	int n_horiz_segments, n_vert_segments;
	XSegment segment[MAXLINES];
	register int x, y;

	XFillRectangle(XtDisplay(cw), cw->bitmapEdit.big_picture,
			cw->bitmapEdit.undraw_gc, 0, 0, 
			cw->bitmapEdit.pixmap_width_in_pixels
			+ 2, cw->bitmapEdit.pixmap_height_in_pixels + 2);

	n_horiz_segments = cw->bitmapEdit.pixmap_height_in_cells + 1;
	n_vert_segments = cw->bitmapEdit.pixmap_width_in_cells + 1;

	for (x = 0; x < n_horiz_segments; x += 1) {
		segment[x].x1 = 0;
		segment[x].x2 = cw->bitmapEdit.pixmap_width_in_pixels;
		segment[x].y1 = cw->bitmapEdit.cell_size_in_pixels * x;
		segment[x].y2 = cw->bitmapEdit.cell_size_in_pixels * x;
	}

	XDrawSegments(XtDisplay(cw), cw->bitmapEdit.big_picture, 
			cw->bitmapEdit.draw_gc, segment, n_horiz_segments);

	for (y = 0; y < n_vert_segments; y += 1) {
		segment[y].x1 = y * cw->bitmapEdit.cell_size_in_pixels;
		segment[y].x2 = y * cw->bitmapEdit.cell_size_in_pixels;
		segment[y].y1 = 0;
		segment[y].y2 = cw->bitmapEdit.pixmap_height_in_pixels;
	}

	XDrawSegments(XtDisplay(cw), cw->bitmapEdit.big_picture, 
			cw->bitmapEdit.draw_gc, segment, n_vert_segments);
}

/* A Public function, not static */
char *
BitmapEditGetArrayString(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	return (cw->bitmapEdit.cell);
}

/* ARGSUSED */
static void
Resize(w)
Widget w;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	/*
	 * This takes care of the case where the widget is resized larger
	 * but there is no more pixmap to display in the bottom right corner.
	 * in that case, we need to move cur_x and cur_y to make sure that
	 * the pixmap fills as much of the window as possible, and fill 
	 * the remainder of the window with gray, or draw a line around it.
	 */
	if (cw->bitmapEdit.cur_x + cw->core.width > 
			cw->bitmapEdit.pixmap_width_in_pixels)
		cw->bitmapEdit.cur_x = cw->bitmapEdit.pixmap_width_in_pixels 
				- cw->core.width;

	if (cw->bitmapEdit.cur_y + cw->core.height > 
			cw->bitmapEdit.pixmap_height_in_pixels)
		cw->bitmapEdit.cur_y = cw->bitmapEdit.pixmap_height_in_pixels 
				- cw->core.height;

	/* Can't clear window is it hasn't been realized yet,
	 * as would happen if -geometry option is specified. */
	if (XtIsRealized(cw))
		/* now force redraw by clearing window */
		XClearArea(XtDisplay(cw), XtWindow(cw), 0, 0, 
				cw->bitmapEdit.pixmap_width_in_pixels, 
				cw->bitmapEdit.pixmap_height_in_pixels, True);
}

static void
TopLeft(w, event)
Widget w;
XButtonEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	cw->bitmapEdit.first_box = False;

	cw->bitmapEdit.select_start_x = (cw->bitmapEdit.cur_x + event->x) 
			/ cw->bitmapEdit.cell_size_in_pixels;
	cw->bitmapEdit.select_start_y = (cw->bitmapEdit.cur_y + event->y) 
			/ cw->bitmapEdit.cell_size_in_pixels;

	/* clear old selection */
	Redisplay(cw, NULL);
}

/* ARGSUSED */
static void
transfer_done_proc(w, selection, target)
Widget w;
Atom *selection;
Atom *target;
{
	/* 
	 * This widget keeps the selection ready for pasting additional
	 * times.  Having a transfer_done_proc indicates that the
	 * selection owner owns the storage allocated for
	 * converting the selection.  However, this widget allocates
	 * this storage only once in initialize, and keeps it until
	 * quitting.  Therefore, no \f(CWXtFree\fP call necessary here.
	 */
}

static void
BottomRight(w, event)
Widget w;
XButtonEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	int temp;

	cw->bitmapEdit.select_end_x = (cw->bitmapEdit.cur_x + event->x) 
			/ cw->bitmapEdit.cell_size_in_pixels;
	cw->bitmapEdit.select_end_y = (cw->bitmapEdit.cur_y + event->y) 
			/ cw->bitmapEdit.cell_size_in_pixels;

	if ((cw->bitmapEdit.select_end_x == cw->bitmapEdit.select_start_x) && 
			(cw->bitmapEdit.select_end_y == cw->bitmapEdit.select_start_y))  {
		Redisplay(cw, NULL);
		return;	/* no selection */
	}

	/* swap start and end if end is greater than start */
	if (cw->bitmapEdit.select_end_x < cw->bitmapEdit.select_start_x) {
		temp = cw->bitmapEdit.select_end_x;
		cw->bitmapEdit.select_end_x = cw->bitmapEdit.select_start_x;
		cw->bitmapEdit.select_start_x = temp;
	}

	if (cw->bitmapEdit.select_end_y < cw->bitmapEdit.select_start_y) {
		temp = cw->bitmapEdit.select_end_y;
		cw->bitmapEdit.select_end_y = cw->bitmapEdit.select_start_y;
		cw->bitmapEdit.select_start_y = temp;
	}

	if (XtOwnSelection(cw, XA_PRIMARY, event->time, convert_proc, 
			lose_ownership_proc, transfer_done_proc) == False) {
		XtWarning("bitmapEdit: failed attempting to become selection owner; make a new selection.\n");
		/* Clear old selection, because lose_ownership_proc 
		 * isn't registered. */
		Redisplay(cw, NULL); 
	}
}

static Boolean
convert_proc(w, selection, target, type_return, value_return, length_return, format_return)
Widget w;
Atom *selection;
Atom *target;
Atom *type_return;
XtPointer *value_return;
unsigned long *length_return;
int *format_return;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	int x, y;
	int width, height;
	XSelectionRequestEvent* req = XtGetSelectionRequest(w, 
			*selection, (XtRequestId) NULL);

	/* handle all required atoms, and the one that we use */
	if (*target == XA_TARGETS(XtDisplay(cw))) {
		/* TARGETS handling copied from xclipboard.c */
		Atom* targetP;
		Atom* std_targets;
		unsigned long std_length;
		XmuConvertStandardSelection(cw, req->time, selection, 
				target, type_return,
   				(XtPointer*)&std_targets, 
				&std_length, format_return);
		*value_return = XtMalloc(sizeof(Atom)*(std_length + 1));
		targetP = *(Atom**)value_return;
		*length_return = std_length + 1;
		*targetP++ = cw->bitmapEdit.target_atom;
		bcopy((char*)std_targets, (char*)targetP, sizeof(Atom)*std_length);
		XtFree((char*)std_targets);
		*type_return = XA_ATOM;
		*format_return = sizeof(Atom) * 8;
		return(True);
	}
	/* Xt already handles MULTIPLE, no branch necessary */ 
	else if (*target == cw->bitmapEdit.target_atom) {
		char *data;

		width = cw->bitmapEdit.select_end_x - cw->bitmapEdit.select_start_x;
		height = cw->bitmapEdit.select_end_y - cw->bitmapEdit.select_start_y;

		/* 8 chars is enough for two 3-digit numbers and two delimiters */
		*length_return = ((width * height) + 8) * sizeof(char);
			
		data = XtMalloc(*length_return);

		sprintf(data, "%d@%d~", width, height);

		for (x = 0; x < width; x++) {
			for (y = 0; y < height; y++) {
				data[8 + x + (y * width)] = cw->bitmapEdit.cell[(x + cw->bitmapEdit.select_start_x) + ((y + cw->bitmapEdit.select_start_y) * cw->bitmapEdit.pixmap_width_in_cells)];
			}
		}

		*value_return = data;

		*type_return = cw->bitmapEdit.target_atom;

		*format_return = 8;  /* number of bits in char */
		return(True);
	}
	else { 
 		if (XmuConvertStandardSelection(cw, CurrentTime, selection, 
				target, type_return, value_return, 
				length_return, format_return))
				return True;
		else {
			XtWarning("bitmapEdit: requestor is requesting\
					 unsupported selection target type.\n");
			return(False);
		}
	}
}

/* ARGSUSED */
static void
lose_ownership_proc(w, selection)
Widget w;
Atom *selection;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	/* clear old selection */
	cw->bitmapEdit.first_box = False;
	cw->bitmapEdit.select_start_x = 0;
	cw->bitmapEdit.select_start_y = 0;
	cw->bitmapEdit.select_end_x = 0;
	cw->bitmapEdit.select_end_y = 0;
	Redisplay(cw, NULL);
}

/* Note: these global variables are necessary
 * because the x and y position in the buttonpress event
 * last only as long as the PasteSelection Action, yet
 * these values are needed when interpreting the selection
 * in requestor_callback. */

static int xPos, yPos;

static void
PasteSelection(w, event)
Widget w;
XButtonEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	xPos = event->x;
	yPos = event->y;

	/* 
	 * Note: the actual pasting takes place in requestor_callback.
	 * This is a callback because the owner and requestor roles 
	 * may not be in the same widget.
	 */
	XtGetSelectionValue(cw, XA_PRIMARY, cw->bitmapEdit.target_atom, 
			requestor_callback, (XtPointer) event /* client_data */, 
			XtLastTimestampProcessed(XtDisplay(w)));
}

/* ARGSUSED */
static void
requestor_callback(w, client_data, selection, type, value, length, format)
Widget w;
XtPointer client_data;
Atom *selection;
Atom *type;
XtPointer value;
unsigned long *length;
int *format;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	char *data = (char *)value;

	if (*type == 0 /*XT_CONVERT_FAIL*/ || *length == 0 || value == NULL) {
		XBell(XtDisplay(cw), 100);
		XtWarning("bitmapEdit: no selection or selection timed out; try again\n");
	}
	else {
		XButtonEvent *event = (XButtonEvent *) client_data;
		int width, height;
		int x, y;
		int dst_offset_x, dst_offset_y;
		char *ptr;

		dst_offset_x = (cw->bitmapEdit.cur_x + xPos) / cw->bitmapEdit.cell_size_in_pixels;
		dst_offset_y = (cw->bitmapEdit.cur_y + yPos) / cw->bitmapEdit.cell_size_in_pixels;

		ptr = (char *) value;
		width = atoi(ptr);
		ptr = index(ptr, '@');
		ptr++;
		height = atoi(ptr);
		ptr = &data[8];

		for (x = 0; x < width; x++) {
			for (y = 0; y < height; y++) {
				/* range checking */
				if (((dst_offset_x + x) > 
					   cw->bitmapEdit.pixmap_width_in_cells) 
					   || ((dst_offset_x + x) < 0))
					break;
				if (((dst_offset_y + y) > 
					   cw->bitmapEdit.pixmap_height_in_cells) 
					   || ((dst_offset_y + y) < 0))
					break;
				cw->bitmapEdit.cell[(dst_offset_x + x) 
					   + ((dst_offset_y + y) * 
					   cw->bitmapEdit.pixmap_width_in_cells)] 
					   = ptr[x + (y * width)];
				if (cw->bitmapEdit.cell[(dst_offset_x + x) 
					   + ((dst_offset_y + y) * 
					   cw->bitmapEdit.pixmap_width_in_cells)] 
					   == DRAWN)
					DrawCell(cw, dst_offset_x + x, 
							dst_offset_y + y, 
							cw->bitmapEdit.draw_gc);
				else
					DrawCell(cw, dst_offset_x + x, 
							dst_offset_y + y, 
							cw->bitmapEdit.undraw_gc);
			}
		}
		/* Regardless of the presence of a 
		 * \f(CWtransfer_done_proc\fP in the owner, 
		 * the requestor must free the data passed by 
		 * Xt after using it. */
		XtFree(value);
		Redisplay(cw, NULL);
	}
}

static void
DragHighlight(w, event)
Widget w;
XMotionEvent *event;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	static int last_drawn_x, last_drawn_y;
	int event_cell_x, event_cell_y;

	event_cell_x = cw->bitmapEdit.cur_x + (event->x / cw->bitmapEdit.cell_size_in_pixels);
	event_cell_y = cw->bitmapEdit.cur_y + (event->y / cw->bitmapEdit.cell_size_in_pixels);

	if ((event_cell_x == last_drawn_x) && (event_cell_y == last_drawn_y))
		return;

	if (cw->bitmapEdit.first_box) {
		draw_box(cw, last_drawn_x, last_drawn_y, False);
		draw_box(cw, event_cell_x, event_cell_y, True);
	}
	else {
		draw_box(cw, event_cell_x, event_cell_y, True);
		cw->bitmapEdit.first_box = True;
	}

	last_drawn_x = event_cell_x;
	last_drawn_y = event_cell_y;
}

static void
draw_box(w, x, y, draw)
Widget w;
Position x, y;
Bool draw;
{
	BitmapEditWidget cw = (BitmapEditWidget) w;
	Position start_pos_x, start_pos_y;
	Dimension width, height;
	int i, j;

	start_pos_x = cw->bitmapEdit.cur_x + cw->bitmapEdit.select_start_x;
	start_pos_y = cw->bitmapEdit.cur_x + cw->bitmapEdit.select_start_y;
	
	/* swap start and end if end is greater than start */
	if (x < start_pos_x) {
		width = start_pos_x - x;
		start_pos_x = x;
	}
	else {
		width = x - start_pos_x;
	}

	if (y < start_pos_y) {
		height = start_pos_y - y;
		start_pos_y = y;
	}
	else {
		height = y - start_pos_y;
	}

/*	XDrawRectangle(XtDisplay(cw), XtWindow(cw), gc,
			(start_pos_x * cw->bitmapEdit.cell_size_in_pixels) - 1,
			(start_pos_y * cw->bitmapEdit.cell_size_in_pixels) - 1, 
			(unsigned int) width * cw->bitmapEdit.cell_size_in_pixels + 2,
			(unsigned int) height * cw->bitmapEdit.cell_size_in_pixels + 2);
*/
	for (i=start_pos_x;i < start_pos_x + width;i++)
		for (j=start_pos_y;j < start_pos_y + height;j++)
				DrawX(cw, i, j, draw);
}

DrawX(cw, x, y, draw)
BitmapEditWidget cw;
Position x, y;
Bool draw;
{
	GC gc;
   	if (cw->bitmapEdit.cell[x + y * cw->bitmapEdit.pixmap_width_in_cells] == DRAWN)
		if (draw)
			gc = cw->bitmapEdit.deep_undraw_gc;
		else
			gc = cw->bitmapEdit.deep_draw_gc;
	else
		if (draw)
			gc = cw->bitmapEdit.deep_draw_gc;
		else
			gc = cw->bitmapEdit.deep_undraw_gc;

	XDrawLine(XtDisplay(cw), XtWindow(cw), gc,
			x * cw->bitmapEdit.cell_size_in_pixels,
			y * cw->bitmapEdit.cell_size_in_pixels,
			(x + 1) * cw->bitmapEdit.cell_size_in_pixels,
			(y + 1) * cw->bitmapEdit.cell_size_in_pixels);

	XDrawLine(XtDisplay(cw), XtWindow(cw), gc,
			x * cw->bitmapEdit.cell_size_in_pixels,
			(y + 1) * cw->bitmapEdit.cell_size_in_pixels,
			(x + 1) * cw->bitmapEdit.cell_size_in_pixels,
			y * cw->bitmapEdit.cell_size_in_pixels);
}