/* 
 * dest.c - Example of how to register interest in receiving drag and drop
 *          events and how to complete a drag and drop operation.
 *	
 */
#include <stdio.h>
#include <xview/xview.h>
#include <xview/frame.h>
#include <xview/canvas.h>
#include <xview/panel.h>
#include <xview/font.h>
#include <xview/dragdrop.h>
#include <xview/xv_xrect.h>
#include <X11/Xlib.h>

#define  DROP_WIDTH	65
#define  DROP_HEIGHT	65

#define  BULLSEYE_SITE	1

Frame 		frame;
Canvas  	canvas;
Panel  		panel;
Xv_drop_site 	drop_site;
Server_image	drop_image;
Server_image	drop_image_inv;
Panel_item	p_string,
		p_length,
		p_host;

Selection_requestor  sel;

int		inverted;

main(argc, argv)
    int argc;
    char **argv;
{
    void	EventProc(),
		PaintCanvas(),
		ResizeCanvas();
    Xv_Font	font;

    xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, 0);

    frame = xv_create((Window)NULL, FRAME,
				XV_X, 			330,
				XV_Y, 			10,
				XV_WIDTH, 		10,
				XV_LABEL, 	      "Drag & Drop Destination",
				0);
    font = (Xv_Font)xv_find(frame, FONT, FONT_NAME, "lucida-12", NULL);
    if (!font) {
	fprintf(stderr, "Cannot use font: lucida-12.\n");
	font = (Xv_Font)xv_get(frame, XV_FONT);
    }


    panel = xv_create(frame, PANEL,
				PANEL_LAYOUT,		PANEL_VERTICAL,
				XV_FONT,		font,
			 	0);

    p_string = xv_create(panel, PANEL_TEXT,
				PANEL_LABEL_STRING, 	"Dropped Text:",
				PANEL_VALUE_DISPLAY_LENGTH, 50,
				0);
    p_host = xv_create(panel, PANEL_TEXT,
				PANEL_LABEL_STRING, 	"From host:",
				PANEL_VALUE_DISPLAY_LENGTH, 15,
				0);
    p_length = xv_create(panel, PANEL_TEXT,
				PANEL_LABEL_STRING, 	"Length:",
				PANEL_VALUE_DISPLAY_LENGTH, 6,
				0);

    window_fit(panel);

    canvas = xv_create(frame, CANVAS,
				XV_HEIGHT, 		100,
				XV_WIDTH, 		WIN_EXTEND_TO_EDGE,
				XV_X,			0,
				WIN_BELOW, 		panel,
				CANVAS_REPAINT_PROC, 	PaintCanvas,
				CANVAS_RESIZE_PROC, 	ResizeCanvas,
				CANVAS_X_PAINT_WINDOW, 	TRUE,
			        0);

    xv_set(canvas_paint_window(canvas),
				WIN_BIT_GRAVITY, 	ForgetGravity,
				WIN_CONSUME_EVENTS,
						WIN_RESIZE,
						0,
				WIN_EVENT_PROC, 	EventProc,
				0);

    drop_image = xv_create(NULL, SERVER_IMAGE, 
				SERVER_IMAGE_BITMAP_FILE, "./bullseye.bm",
				0);

    drop_image_inv = xv_create(NULL, SERVER_IMAGE, 
				SERVER_IMAGE_BITMAP_FILE, "./bullseyeI.bm",
				0);

				/* Selection requestor object that will be 
				 * passed into dnd_decode_drop() and later used
				 * to make requests to the source of the
				 * drop.
				 */
    sel = xv_create(canvas, SELECTION_REQUESTOR, 0);

				/* This application has one drop site with
				 * site id BULLSEYE_SITE and whose shape will
				 * be described by a rectangle.  If 
				 * animation is supported, it would like to
				 * receive LOC_DRAG, LOC_WINENTER and
				 * LOC_WINEXIT events.
				 */
    drop_site = xv_create(canvas_paint_window(canvas), DROP_SITE_ITEM,
				DROP_SITE_ID,   	BULLSEYE_SITE,
				DROP_SITE_EVENT_MASK,   DND_ENTERLEAVE,
				0);

    inverted = False;

    window_fit(frame);
    xv_main_loop(frame);
    exit(0);
}

void
EventProc(window, event)
    Xv_Window       window;
    Event           *event;
{
    void PaintCanvas();
    switch (event_action(event)) {
				/* When drop previewing is available, if
				 * the drop site has selected for previewing
				 * events (DROP_SITE_EVENT_MASK) then it will
				 * receive ACTION_DRAG_PREVIEW events from
				 * the source as requested.
				 */
      case ACTION_DRAG_PREVIEW:
	switch(event_id(event)) {
	  case LOC_WINENTER:
	    inverted = True; 
	    break;
	  case LOC_WINEXIT:
	    inverted = False; 
	    break;
	  case LOC_DRAG:
	    break;
	}
	PaintCanvas(NULL, window, XV_DISPLAY_FROM_WINDOW(window),
		    xv_get(window, XV_XID), NULL);
	break;
      case ACTION_DRAG_COPY:
      case ACTION_DRAG_MOVE: {
        Xv_drop_site    ds;
	Xv_Server	server = XV_SERVER_FROM_WINDOW(event_window(event));

				/* If the user dropped over an acceptable
				 * drop site, the owner of the drop site will
				 * be sent an ACTION_DROP_{COPY, MOVE} event.
				 */
				/* To acknowledge the drop and to associate the
				 * rank of the source's selection to our
				 * requestor selection object, we call
				 * dnd_decode_drop().
				 */
	if ((ds = dnd_decode_drop(sel, event)) != XV_ERROR) {
				/* We can use the macro dnd_site_id() to access
				 * the site id of the drop site that was
				 * dropped on.
				 */
	    if (xv_get(ds, DROP_SITE_ID) == BULLSEYE_SITE)
		UpdatePanel(server, sel);

				/* If this is a move operation, we must ask
				 * the source to delete the selection object.
				 * We should only do this if the transfer of
				 * data was successful.
				 */
	    if (event_action(event) == ACTION_DRAG_MOVE) {
		int length, format;

                xv_set(sel, SEL_TYPE_NAME, "DELETE", 0);
                (void)xv_get(sel, SEL_DATA, &length, &format);
	    }

				/* To complete the drag and drop operation,
				 * we tell the source that we are all done.
				 */
	    dnd_done(sel);
	    inverted = False;
	    PaintCanvas(NULL, window, XV_DISPLAY_FROM_WINDOW(window),
		        xv_get(window, XV_XID), NULL);

	} else
	    printf ("drop error\n");
	break;
      }
      default:
        break;
    }
}


UpdatePanel(server, sel)
    Xv_Server		server;
    Selection_requestor	sel;
{
    int 	 length,
		 format,
		*string_length;
    char 	 buf[7],
		*string,
		*hostname;

    xv_set(sel, SEL_TYPE, XA_STRING, 0);
    string = (char *)xv_get(sel, SEL_DATA, &length, &format);
    if (length != SEL_ERROR) {
    	xv_set(p_string, PANEL_VALUE, string, 0);
    	free (string);
    }

    xv_set(sel, SEL_TYPE, xv_get(server, SERVER_ATOM, "LENGTH"), 0);
    string_length = (int *)xv_get(sel, SEL_DATA, &length, &format);
    if (length != SEL_ERROR) {
	sprintf(buf, "%d", *string_length);
        xv_set(p_length, PANEL_VALUE, buf, 0);
        free ((char *)string_length);
    }

    xv_set(sel, SEL_TYPE, xv_get(server, SERVER_ATOM, "HOST_NAME"), 0);
    hostname = (char *)xv_get(sel, SEL_DATA, &length, &format);
    if (length != SEL_ERROR) {
    	xv_set(p_host, PANEL_VALUE, hostname, 0);
    	free (hostname);
    }
    xv_set(sel, SEL_TYPE_NAME, "_SUN_SELECTION_END", 0);
    (void)xv_get(sel, SEL_DATA, &length, &format);
}

void
PaintCanvas(canvas, paint_window, dpy, xwin, xrects)
    Canvas        canvas;         /* unused */
    Xv_Window     paint_window;   /* unused */
    Display      *dpy;
    Window        xwin;
    Xv_xrectlist *xrects;         /* unused */
{
    static GC   gc;
    static int  gcCreated = False;
    static int  lastMode = False;
    int         width, height;  
    int         x, y;  
    Rect	*r;

    if (!gcCreated) {
        XGCValues gcv;
        gcv.stipple = (Pixmap) xv_get(drop_image, XV_XID);
        gcv.foreground = BlackPixel(dpy, XDefaultScreen(dpy));
        gcv.background = WhitePixel(dpy, XDefaultScreen(dpy));
        gcv.fill_style = FillStippled;
        gc = XCreateGC(dpy, xwin, GCStipple|GCForeground|GCBackground|
                                                             GCFillStyle, &gcv);
    }

    if (lastMode != inverted) {
	if (!inverted)
	    XSetStipple(dpy, gc, (Pixmap) xv_get(drop_image, XV_XID));
	else 
	    XSetStipple(dpy, gc, (Pixmap) xv_get(drop_image_inv, XV_XID));
	lastMode = inverted;
    }

    width = xv_get(paint_window, XV_WIDTH);
    height = xv_get(paint_window, XV_HEIGHT);

    x = (width/2)-(DROP_WIDTH/2);
    y = (height/2)-(DROP_HEIGHT/2);

    XClearArea(dpy, xwin, x, y, DROP_WIDTH, DROP_HEIGHT, False);
    XSetTSOrigin(dpy, gc, x, y);
    XFillRectangle(dpy, xwin, gc, x, y, DROP_WIDTH, DROP_HEIGHT);
}

void 
ResizeCanvas(canvas, width, height)
    Canvas 	canvas;
    int 	width;
    int 	height;
{
    int         x, y;  
    Rect	rect;

    x = (width/2)-(DROP_WIDTH/2);
    y = (height/2)-(DROP_HEIGHT/2);

    rect.r_left = x;
    rect.r_top = y;
    rect.r_width = DROP_WIDTH;
    rect.r_height = DROP_HEIGHT;

			/* Update the drop site information. */
    xv_set(drop_site, DROP_SITE_DELETE_REGION_PTR, NULL,
		      DROP_SITE_REGION, &rect,
		      0);
}
