/*
 * 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);
}
