/*                               -*- Mode: C -*- 
 * rdd.c
 * 
 * Description     :  Roger's Drag 'n Drop Library.
 * 					  A drag-n-drop library for Xt programs.
 * 
 * Author          : Roger Reynolds	 - 	rogerr@netcom.com
 * Created On      : Sun Apr 26 14:14:01 1992
 * 
 * Configuration Management
 * 
 * @(#)rdd.c	1.10	10/5/92
 * 
 * VERSION   SPR   MODIFIER   DATE AND TIME
 * 1.10      0     rogerr     Mon Oct  5 14:34:36 1992
 *           more gcc stuff. 
 * 1.9       0     rogerr     Tue Sep 29 11:41:27 1992
 *           GCC2.2.2 clean up. Can now be cleanly compiled without 
 *           -traditional. 
 * 1.8       0     rogerr     Wed Sep 23 14:10:56 1992
 *           use debug.h which defines DEBUG TRACE and XtFree macros 
 * 1.7       0     rogerr     Thu Aug 27 15:06:39 1992
 *           added rddDragButton 
 * 1.6       0     rogerr     Mon Aug 17 15:51:33 1992
 *           additional example functionality 
 * 1.5       0     rogerr     Tue Jul 28 16:11:38 1992
 *           more ansi mumbo jumbo 
 * 1.4       0     rogerr     Wed Jul 15 14:49:27 1992
 *           send keymask in RddCallbackStruct so dropee knows what was 
 *           going on 
 * 1.3       0     rogerr     Fri Jun 26 16:16:58 1992
 *           deleteDropHandler as widget destroy callback 
 * 1.2       0     rogerr     Tue Jun 23 17:08:58 1992
 *           now dragging pixmaps around 
 * 
 */

#include <stdio.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/cursorfont.h>
#include <X11/StringDefs.h>

#include "rdd.h"

/*
 *  Private variables containing bits of current state 
 */
static Widget topLevel;
static XtAppContext appContext;
static Display *display;
static Atom rddProtocolMsg;
static Atom rddSelnAtom;
static Cursor dragCursor;
static Pixmap dragPixmap, savePixmap;
static int	pixmapwid, pixmaphgt, xcursoff, ycursoff;
static Window getPointerWindow();
static int rddDataType;
static GC  dragGC;
/*
 *  Is this event an rdd drop message? 
 */
#define rddIsRddMessageEvent(ev)\
	((ev).type == ClientMessage && (ev).xclient.message_type == rddProtocolMsg)


/*
 * Data structure describing a "drop event handler" 
 */
typedef struct 
{
	XtCallbackProc proc;
	char *data;
	Widget w;
	Boolean active;
} HandlerData;

/*
 * Internal list of all drop handlers 
 */
static HandlerData *handlerList;
static int handlerListCnt, handlerListAllocCnt;

/*
 * Action routines which changes the cursor when appropriate and begin
 * the drag-n-drop process.
 * They could be bound directly to btndown, btnmotion, and btnup events,
 * but to do anything useful, rddSetDropData must be called someplace.
 */
static int bx, by, dragflag;

void
rddStartAction (w, event, args, nargs)
	Widget w;
	XButtonEvent *event;
	String *args;
	int *nargs;
{
	dragflag = 0;
	bx = event->x;
	by = event->y;
/*	xcursoff = 0;
	ycursoff = 0;
 */
	DEBUG (fprintf(stderr, "rddStartAction at %d,%d\n", bx, by););
}

void
rddDragAction (w, event, args, nargs)
	Widget w;
	XButtonEvent *event;
	String *args;
	int *nargs;
{
	DEBUG (fprintf(stderr, "rddDragAction\n"););
	if (dragflag == 0 && ((abs(bx - event->x)) > 10 ||
						  (abs(by - event->y)) > 10))
	{
		DEBUG (fprintf(stderr, "using drag cursor %d\n", dragCursor););
		dragflag = 1;
		if (dragCursor)
			XDefineCursor (XtDisplay(w), XtWindow(w), dragCursor);

		if (dragPixmap&&savePixmap)
			XCopyArea (display, RootWindow(display,0), savePixmap, dragGC,
					   bx=event->x_root-xcursoff, by=event->y_root-ycursoff,
					   pixmapwid, pixmaphgt,
					   0, 0);
	}

	if (0)
	{
		u_int state;
		Window child, root;
		int x, y;
		XQueryPointer (display, RootWindow(display,0), &root, &child,
					   &x, &y, &x, &y, &state);
		DEBUG (fprintf (stderr, "pointer root = 0x%x win 0x%x, state = 0x%x\n",
						root, child, state););
	}

	if (dragflag && dragPixmap && savePixmap)
	{
		XCopyArea (display, savePixmap, RootWindow(display,0), dragGC,
				   0, 0, pixmapwid, pixmaphgt, bx, by);

		XCopyArea (display, RootWindow(display,0), savePixmap, dragGC,
				   bx=event->x_root-xcursoff, by=event->y_root-ycursoff,
				   pixmapwid, pixmaphgt,
				   0, 0);

		XCopyArea (display, dragPixmap, RootWindow(display,0), dragGC,
				   0, 0, pixmapwid, pixmaphgt, bx, by);
	}
}
	
void
rddDropAction (w, event, args, nargs)
	Widget w;
	XButtonEvent *event;
	String *args;
	int *nargs;
{
	DEBUG (fprintf(stderr, "rddDropAction\n"););
	if (dragflag == 1)
	{
		XUndefineCursor (display, XtWindow(w));
		rddSendDropEvent(w, (XEvent*)event);

		if (savePixmap)
		{
			XCopyArea (display, savePixmap, RootWindow(display,0), dragGC,
					   0, 0, pixmapwid, pixmaphgt, bx, by);
		}
	}
}

#ifdef NEVER
/*
 * Install this as a callback on something like a push button, and
 * you get a button which starts a drag, with the next click sending
 * the drop event.  You might want to get in first and install a
 * drag pixmap...
 * NOTE: This is the only routine in rdd which assumes Motif.
 * If you aren't using motif, then you may need to remove or edit this
 * function...
 */
void
rddDragCallback(w, client_data, cbs)
	Widget w;
	XtPointer client_data;
	XmAnyCallbackStruct *cbs;
{
	XEvent event;
	XtAppContext app = XtWidgetToApplicationContext(w);
	int x, y;
    DEBUG (fprintf(stderr, "rddDragCallback: w = 0x%x\n",
				   cbs->event->xany.window););

	/* Make the pointer look like it moved some */
	x = cbs->event->xbutton.x;
	cbs->event->xbutton.x += 100;
	y = cbs->event->xbutton.y;
	cbs->event->xbutton.y += 100;
	rddStartAction(w, (XButtonEvent*)cbs->event, NULL, 0);

	/* Then back again */
	cbs->event->xbutton.x = x;
	cbs->event->xbutton.y = y;
	rddDragAction(w, (XButtonEvent*)cbs->event, NULL, 0);


	/* Grab the pointer and start an event loop */
	XGrabPointer (XtDisplay(w), cbs->event->xbutton.window, True,
				  PointerMotionMask | ButtonReleaseMask,
				  GrabModeAsync, GrabModeAsync, None, None, 
				  cbs->event->xbutton.time);
	for (;;)
	{
    	XtAppNextEvent(app, &event);

		XtDispatchEvent(&event);
		if (event.type == ButtonRelease)
		{
			/* Done, release pointer and send drop event */
			XUngrabPointer (XtDisplay(w), event.xbutton.time);
			rddDropAction (w, (XButtonEvent*)&event, NULL, 0);
			break;
		}
		else if (event.type == MotionNotify)
		{
			/* drag me */
			rddDragAction (w, (XButtonEvent*)&event, NULL, 0);
		}
	}
	DEBUG (fprintf (stderr, "out of rddDragCallback\n"););
}

#endif

/*
 *  rddInit - 
 * 		Initialize the rdd package.	This routine must be called before
 * 		any other rdd routines.
 * 			Initialize static data,
 * 			Create rdd protocol atoms.
 * 			Create default cursor used for drag operations.
 * 			Register rdd actions.
 */
void
rddInit(shell, context)
	Widget shell;
	XtAppContext context;
{
	/* Should NOT need to CAST here!*/
	static XtActionsRec actions[] = {
			{"rddStartAction",   (XtActionProc)rddStartAction},
			{"rddDragAction",    (XtActionProc)rddDragAction},
			{"rddDropAction",    (XtActionProc)rddDropAction},
			{NULL,NULL}
	};

	DEBUG (fprintf(stderr, "rddInit entered\n"););

	topLevel		= shell;
	appContext		= context;
	display			= XtDisplay(topLevel);
	rddProtocolMsg	= XInternAtom (display, "RDD_MESSAGE", FALSE);
	rddSelnAtom		= XInternAtom (display, "RDD_SELECTION", FALSE);
	dragCursor 		= XCreateFontCursor (display, XC_cross);

	{
		XGCValues gcv;
		XtGCMask  mask;
		
		mask =GCSubwindowMode;
		gcv.subwindow_mode = IncludeInferiors;
		dragGC	= XCreateGC (display, RootWindow(display,0), mask, &gcv);
	}
	
	XtAppAddActions (context, actions, XtNumber(actions));

	DEBUG (fprintf (stderr,"rddInit: topLevel=%d display=%d\n",
					topLevel, display););
}

/*
 * rddSetDragCursor - 
 * 		Set the cursor that will be used for drag-n-drop operations.
 */
void
rddSetDragCursor (cursor)
	Cursor cursor;

{
	dragCursor = cursor;
}

/*
 * rddSetDragPixmap - 
 * 		Set the cursor that will be used for drag-n-drop operations.
 */
void
rddSetDragPixmap (pixmap, wid, hgt)
	Pixmap pixmap;
	int wid, hgt;
{
	if (savePixmap && (pixmapwid != wid || pixmaphgt != hgt))
	{
		XFreePixmap(display, savePixmap);
		savePixmap = 0;
	}
	if (!savePixmap)
	{
		savePixmap = XCreatePixmap (display, RootWindow(display,0), 
									pixmapwid = wid, pixmaphgt = hgt,
									XDefaultDepth(display,0));
	}
	DEBUG (fprintf (stderr, "savePixmap = %d wid=%d hgt=%d\n",
					savePixmap, pixmapwid, pixmaphgt););
	dragPixmap = pixmap;
}

void rddSetDragCursorOffset(x,y)
	int x,y;
{
	xcursoff = x;
	ycursoff = y;
}

/*
 *  rddInitCheck - 
 * 		Ensure that rdd has been initialized.
 */
static void
rddInitCheck()
{
	if (!topLevel)
	{
		fprintf (stderr, "rdd not initialized\n");
		exit(-1);
	}
}

/*
 *  These routines coordinate access to the rddSelection 
 */

static unsigned char *rddSelnData;
static unsigned long rddSelnLen;


static int 
rddSetSelection(data, nbytes)
	caddr_t data;
	int nbytes;
{
	int stat;
	Window window;

	rddInitCheck();

	window = XtWindow(topLevel);

	XSetSelectionOwner (display, rddSelnAtom, window, CurrentTime);

	if (XGetSelectionOwner (display, rddSelnAtom) != window)
	{
		fprintf (stderr, "XSetSelectionOwner failed\n");
	}

	if (rddSelnLen)
	{
		XtFree((char*)rddSelnData);
		rddSelnLen = 0;
	}

	rddSelnData = (u_char *)XtMalloc(rddSelnLen=nbytes);
	memcpy(rddSelnData, data, rddSelnLen);

	stat = XChangeProperty (display, RootWindow(display,0), rddSelnAtom,
							XA_STRING,
							8,
							PropModeReplace,
							rddSelnData, (int)rddSelnLen);
	return(stat);
}

static int
rddGetSelection (data)
	u_char **data;
{
	int n;
	Atom type;
	int format;
	u_long leftover;

	DEBUG (fprintf (stderr, "rddGetSelection entered\n"););

	rddInitCheck();

	n =XGetWindowProperty (display, RootWindow(display,0), rddSelnAtom,
						   0L, 100000L,
						   FALSE,
						   AnyPropertyType,
						   &type, &format,
						   &rddSelnLen,
						   &leftover,
						   &rddSelnData);

	*data = rddSelnData;
	return(rddSelnLen);	
}

/*
 *  getPointerWindow - 
 * 		return the first subwindow of top which the pointer is in that
 * 		has a drop handler installed on it.
 */
static Window
getPointerWindow ()
{
	int i;
	for (i=0; i < handlerListCnt; i++)
	{
		if (handlerList[i].active)
		{
			DEBUG (fprintf(stderr, "returning handler %d %s window = 0x%x\n",
						   i, XtName(handlerList[i].w),
						   XtWindow(handlerList[i].w)););
			return (XtWindow(handlerList[i].w));
		}
	}
	DEBUG (fprintf (stderr, "getPointerWindow returned 0\n"););
	return (0);
}

/*
 *  rddSendDropEvent - 
 * 		Send an rdd drop message to the window which contains the pointer.
 * 		The window parameter is filled in with the window id  of the widget
 * 		passed in, or with the window id of the topLevel shell if w is NULL
 * 		This is to facilitate a possible conversation between the
 * 		dropping window and its target.  Such a conversation could be 
 * 		needed to negotiate the details of a complex drop operation,
 * 		none is required to send simple messages.
 */
void 
rddSendDropEvent (w, event)
	Widget w;
	XEvent *event;
{
	static XClientMessageEvent ev;
	XButtonEvent *bv = (XButtonEvent*)event;

	if (!ev.type)
	{
		rddInitCheck();
		ev.type			= ClientMessage;
		ev.display		= display;
		ev.message_type = rddProtocolMsg;
		ev.format		= 32;
		ev.data.l[0]	= rddDataType;
	}


	ev.window	= XtWindow(topLevel);
	ev.data.l[1]	= w ? XtWindow(w) : XtWindow(topLevel);

	if (bv && (bv->type == ButtonPress || bv->type == ButtonRelease))
		ev.data.l[2]	= bv->state;
	else
		ev.data.l[2]	= 0;

	++ev.serial;

	DEBUG (fprintf(stderr, "rddSendDropEvent to win 0x%x\n", ev.window););

	XSendEvent (display, PointerWindow, TRUE, NoEventMask, (XEvent*)&ev);
}

/*
 *  rddDropEventHandler - 
 * 		This procedure is installed to handel rdd drop events on all windows.
 * 		It dispatches to the user specified procedure after retrieving data
 * 		from the rdd selection buffer.
 */
static void
rddDropEventHandler(w, hd, ev)
	Widget w;
	HandlerData *hd;
	XClientMessageEvent *ev;
{
	int nbytes;
	u_char *sdata;
	RddCallbackStruct cbs;

	if (ev->type != ClientMessage || ev->message_type != rddProtocolMsg)
		return;
	
	nbytes = rddGetSelection (&sdata);

	DEBUG (fprintf (stderr,"GOT DROP MESSAGE %d bytes\n", nbytes););

	if (hd && hd->proc)
	{
		cbs.data	= (caddr_t)sdata;
		cbs.len		= nbytes;
		cbs.type	= ev->data.l[0];
		cbs.from	= ev->data.l[1];
		cbs.keymask	= ev->data.l[2];
		cbs.event	= ev;
		(hd->proc)(hd->w, hd->data, &cbs);
	}
}

static void
enterLeaveProc(w, hd, ev)
	Widget w;
	HandlerData *hd;
	XCrossingEvent *ev;
{
	hd->active	= (ev->type == EnterNotify);
	DEBUG (fprintf(stderr, "window %s active = %d\n",
				   XtName(hd->w), hd->active););
}

static void
deleteDropHandler (w, h, cbs)
	Widget w;
	HandlerData *h;
	XtPointer *cbs;
{
	int i;

	DEBUG (fprintf (stderr, "deleted handler for widget 0x%x\n", w););
	for (i=0; i < handlerListCnt; i++)
	{
		if (handlerList[i].w == w)	
		{
			bzero(&handlerList[i], sizeof(handlerList[i]));
			return;
		}
	}
}

/*
 *  rddAddDropHandler - 
 * 		Add a callback routine to a widget for rdd drop events.
 * 		Call scemantics for proc:
 * 			void proc (Widget w; XtPointer data; RddCallbackStruct *cbs)
 * 		where w     is the widget for which the handler is being called
 * 			  data  is user specified data passed through to proc
 * 			  cbs	is an RddCallbackStruct filled in.
 */
void
rddAddDropHandler(w, proc, data)
	Widget w;
	XtCallbackProc proc;
	caddr_t data;
{
	int i;
	HandlerData *newHandler;

	rddInitCheck();

	if (handlerListCnt >= handlerListAllocCnt)
	{

		for (i=0; i < handlerListCnt; i++)
		{
			if (handlerList[i].w == 0)	/* was deleted */
				continue;
			 XtRemoveEventHandler(handlerList[i].w, 0, TRUE, 
								  rddDropEventHandler, 
								  handlerList+i);
			XtRemoveEventHandler (handlerList[i].w,
								  EnterWindowMask|LeaveWindowMask, FALSE, 
								  enterLeaveProc, 
								  handlerList+i);
		}

		handlerListAllocCnt += 10;
		handlerList =
			(HandlerData*) XtRealloc((char*)handlerList,
								 sizeof(HandlerData) * handlerListAllocCnt);
		DEBUG(fprintf(stderr,"handlerList = 0x%x\n", handlerList););

		for (i=0; i < handlerListCnt; i++)
		{
			if (handlerList[i].w == 0)	/* was deleted */
				continue;
			XtAddEventHandler (handlerList[i].w, 0, TRUE, 
							   rddDropEventHandler, 
							   handlerList+i);
			XtAddEventHandler (handlerList[i].w,
							   EnterWindowMask|LeaveWindowMask,
							   FALSE, enterLeaveProc, 
							   handlerList+i);
		}
	}

	newHandler = handlerList + handlerListCnt++;
	
	DEBUG (fprintf(stderr, "rddAddDropHandler: for widget %s window=0x%x\n",
				  XtName(w), XtWindow(w)););

	newHandler->proc	=	proc;
	newHandler->data	=	data;
	newHandler->w		=   w;
	newHandler->active	=   FALSE;
	XtAddEventHandler (w, 0, TRUE, 
					   rddDropEventHandler, newHandler);
	XtAddEventHandler (w, EnterWindowMask|LeaveWindowMask, FALSE, 
					   enterLeaveProc, newHandler);
	XtAddCallback (w, XtNdestroyCallback, deleteDropHandler, newHandler);
}

/*
 *  rddSetDropData - 
 * 		Public function to set data into the rdd selection buffer.
 */
void
rddSetDropData (data, len)
	caddr_t data;
	int len;
{
	rddDataType = RDD_NO_DATA_TYPE;
	rddSetSelection (data, len);
}

void
rddSetDropDataType (data, len, type)
	caddr_t data;
	int len, type;
{
	rddDataType = type;
	rddSetSelection (data, len);
}



/*
 *  rddAppMainLoop - 
 * 		Replacement for XtAppMainLoop. This routine knows more than it
 * 		should, but seems to be needed to make rdd work for "all" cases.
 */
void rddAppMainLoop(app)
	XtAppContext app;
{
    XEvent event;

    for (;;)
	{
    	XtAppNextEvent(app, &event);

		/*
		 * If it is an rdd message, try to dispatch it to the
		 * currently active drop window. 
		 */
		if (rddIsRddMessageEvent(event))
		{
			DEBUG (fprintf(stderr, "clientMessage received window 0x%x\n",
						   event.xclient.window););
			if (!(event.xclient.window = getPointerWindow()))
			{
				DEBUG (fprintf (stderr, "no window to dispatch to\n"););
				continue;
			}
		}
		if (!XtDispatchEvent(&event))
		{
			/*DEBUG (fprintf (stderr, "rddAppMainLoop dispatch failed type %d\n",
							event.type););*/
		}
	}
}


Boolean
rddDeliverMessageEvent(event)
    XEvent event;
{
  if (rddIsRddMessageEvent(event))
    return (event.xclient.window = getPointerWindow());
  else 
    return False;
}

/*
 * When called, initiate and dispatch an RDD operation.
 * This can be used to, for example, to start an RDD thing by pressing 
 * a button.  The next click will be reported to this window, and
 * the user supplied function will be called, presumably to do
 * rddSetDataType and rddDropActions.
 */
void 
rddDragButton (w, ev, fptr)
	Widget w;
	XButtonEvent *ev;
	void (*fptr)();
{
	XEvent event;
	Window win = XtWindow(w);
	XtAppContext app = XtWidgetToApplicationContext(w);

    DEBUG (fprintf(stderr, "rddDragButton: w = 0x%x app = 0x%x\n",
				   win, app););

	rddStartAction(w, ev, NULL, 0);

	ev->x += 100;
	ev->y += 100;

	rddDragAction(w, ev, NULL, 0);

	XGrabPointer (XtDisplay(w), win, FALSE,
				  PointerMotionMask | ButtonReleaseMask,
				  GrabModeAsync, GrabModeAsync, None, None, 
				  ev->time);
	for (;;)
	{
    	XtAppNextEvent(app, &event);

		XtDispatchEvent(&event);
		if (event.type == ButtonRelease)
		{
			XUngrabPointer (XtDisplay(w), event.xbutton.time);
			(*fptr)(w, &event);
			break;
		}
		else if (event.type == MotionNotify)
		{
			rddDragAction (w, (XButtonEvent*)&event, NULL, 0);
		}
	}
	DEBUG (fprintf (stderr, "out of rddDragButton\n"););
}

#ifdef MAIN

/* As an example of how to add drag-n-drop to an existimg program,
 * here is the hello program from chapter 2 of Dan Heller's book,
 * modified so that the button can both initiate a drag-n-drop
 * operation, and respond to something being dropped on it.
 * 
 * RDD comments denote rdd additions and changes.
 */


/* Written by Dan Heller.  Copyright 1991, O'Reilly && Associates.
 * This program is freely distributable without licensing fees and
 * is provided without guarantee or warrantee expressed or implied.
 * This program is -not- in the public domain.
 */

/* hello.c --
 * Initialize the toolkit using an application context and a toplevel
 * shell widget, then create a pushbutton that says Hello using
 * the R4 varargs interface.
 */
#include <Xm/Xm.h>
#include <Xm/PushB.h>


/*
 * RDD
 * The normal translations for a button, plus those needed to do a 
 * drag-n-drop procedure.
 */
String myTranslations =
		"<Btn1Down>:	Arm() myStartAction()		\n\
		<Btn1Up>:		Activate() Disarm() myDropAction() \n\
		<Btn1Motion>:	rddDragAction() \n\
";

XtAppContext  app;

/* RDD myStartAction */
void myStartAction (w, event, args, nargs)
	Widget w;
	XButtonEvent *event;
	String *args;
	int *nargs;
{
	static Pixmap pixmap;
	Display *dpy = XtDisplay(w);
	int wid=40, hgt=40;
	static GC gc;

	if (!pixmap)
	{
		XGCValues gcv;

		gcv.subwindow_mode = IncludeInferiors;
		gc = XCreateGC (dpy, XtWindow(w), GCSubwindowMode, &gcv);
		
		pixmap = XCreatePixmap (dpy, RootWindow(dpy,0), 
								wid, hgt, XDefaultDepth(dpy,0));
	}

	/* Create a pixmap of the screen near the cursor */
	XCopyArea (dpy, RootWindow(dpy,0), pixmap, gc,
			   event->x_root-wid/2, event->y_root-hgt/2,
			   wid, hgt, 0, 0);
	rddSetDragPixmap (pixmap, wid, hgt);
	rddSetDragCursorOffset(wid/2, hgt/2);

	rddStartAction (w, event, args, nargs);		/* then use default action */
}


/* RDD myDropAction */
void myDropAction (w, event, args, nargs)
	Widget w;
	XButtonEvent *event;
	String *args;
	int *nargs;
{
	static char *data = "my drop data";

	/* copy data to rdd, use filename as data type  */
	rddSetDropDataType (data, strlen(data), RDD_FILENAME_TYPE);

	/* then use default action */
	rddDropAction (w, event, args, nargs);	
}

/* RDD dropTraceProc */
void
dropTraceProc(w, call, cbs)
	Widget w;
	char *call;
	RddCallbackStruct *cbs;
{
	fprintf (stderr,
			 "dropTraceProc entered w = %s call=%s len=%d data=<%s> type=%d keymask=%d\n",
			 XtName(w), call, cbs->len, cbs->data, cbs->type, cbs->keymask);
}


main(argc, argv)
	int argc;
	char *argv[];
{
    Widget        toplevel, button;
    void i_was_pushed();
    XmString label;

    toplevel = XtVaAppInitialize(&app, "Hello", (XrmOptionDescList)NULL, 0,
        (Cardinal*)&argc, argv, (String*)NULL, (String*)NULL);

	/* RDD add action myAction */
	{
		static XtActionsRec actions[] = 
		{
			"myDropAction", myDropAction,
			"myStartAction", myNewStartAction,
			NULL, NULL,
		};
		XtAppAddActions (app, actions, XtNumber(actions));
	}

	/* RDD  initialize the rdd package */
	rddInit (toplevel, app);

    label = XmStringCreateSimple("Push here to say hello"); 
    button = XtVaCreateManagedWidget("pushme",
        xmPushButtonWidgetClass, toplevel,
        XmNlabelString, label,
        NULL);
    XmStringFree(label);

#if 1
	/* RDD  set new translations on button. 
	 * This causes click and drag on button to initiate drag action.
	 */
    XtAddCallback(button, XmNactivateCallback, i_was_pushed, NULL);

	XtVaSetValues (button,
				   XmNtranslations, XtParseTranslationTable(myTranslations),
				   NULL);
#else
	/* RDD Callback when button pushed to grap pointer and start drag action */
	XtAddCallback(button, XmNactivateCallback, rddDragCallback, NULL);
#endif

    XtRealizeWidget(toplevel);


	/* RDD  add drop callback to button */
	rddAddDropHandler (button, dropTraceProc, "no client data");

	/* RDD  use rddAppMainLoop instead of XtAppMainLoop */
    rddAppMainLoop(app);
}

void
i_was_pushed(w, client_data, cbs)
Widget w;
XtPointer client_data;
XmPushButtonCallbackStruct *cbs;
{
	XEvent event;
    fprintf(stderr, "i_was_pushed: w = 0x%x\n", cbs->event->xany.window);
}


#endif /*MAIN*/
