/*-*-C-*-
 * Move/Resize window by modified mouse drag for the Windows CSE
 */

#include "resed.h"
#include "main.h"

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"
#include "newmsgs.h"
#include "dragdrop.h"
#include "interactor.h"

#include "format.h"
#include "windowedit.h"
#include "protocol.h"
#include "winddrag.h"


typedef struct
{
    WindowObjPtr window;
    PointRec pos;
    Bool moving;
    RectRec origvis;      /* pre-drag position of window    */
    PointRec origscroll;  /* pre-drag window scroll offsets */
} WinddragClosureRec, *WinddragClosurePtr;


       
/*
 * Interactor for the window dragging move/resize operation
 */

static error * winddrag_interactor (
    unsigned int event,
    int *buf,
    void *closure,
    Bool *consumed
)
{
    WinddragClosurePtr clos = (WinddragClosurePtr) closure;

    if (buf == NULL)            /* we are being asked to cancel */
    {
        WindowPtr win = clos->window->window;

        /* ensure final resting place is aligned */
        wimp_align_rect (&win->visarea);
        swi (Wimp_OpenWindow, R1, win, END);

        /* note modification if window was actually moved */
        if ( clos->origvis.minx != win->visarea.minx ||
             clos->origvis.miny != win->visarea.miny ||
             clos->origvis.maxx != win->visarea.maxx ||
             clos->origvis.maxy != win->visarea.maxy ||
             clos->origscroll.x != win->scrolloffset.x ||
             clos->origscroll.y != win->scrolloffset.y )
            protocol_send_resed_object_modified (clos->window);

        /* cancel wimp dragbox operation */
        return swi(Wimp_DragBox,  R1, 0,  END);
    }
    
    switch (event)
    {
    case EV_NULL_REASON_CODE:
        {
            PointerInfoRec pointer;
            Bool moving = clos->moving;

            (void) swi (Wimp_GetPointerInfo,  R1, &pointer,  END);
            
            if (pointer.position.x != clos->pos.x ||
                pointer.position.y != clos->pos.y)
            {
                WindowPtr win = clos->window->window;
                int dx = pointer.position.x - clos->pos.x;
                int dy = pointer.position.y - clos->pos.y;

                if (moving)
                {
                    win->visarea.minx += dx;
                    win->visarea.maxy += dy;
                }
                win->visarea.maxx += dx;
                win->visarea.miny += dy;

                swi (Wimp_OpenWindow, R1, win, END);

                clos->pos = pointer.position;
            }
        }
        break;

    case EV_KEY_PRESSED:
        {
            KeyPressPtr key = (KeyPressPtr) buf;

            /* window drag has priority over keyboard shortcuts
               (although you need three hands to prove this ...) */
            *consumed = (key->code != 0x1b);
        }
        break;

    case EV_USER_DRAG_BOX:
        interactor_cancel();
        *consumed = TRUE;
        break;
    }

    return NULL;
}


/*
 * Called from windowedit_mouse_click(..) to process a CTRL/SHFT/SEL/DRAG.
 */

error * winddrag_move_start (WindowObjPtr window, PointPtr mousepos)
{
    DragBoxRec drag;
    static WinddragClosureRec closure;

    interactor_cancel();

    closure.moving = TRUE;
    closure.pos = *mousepos;
    closure.window = window;
    closure.origvis = window->window->visarea;
    closure.origscroll = window->window->scrolloffset;

    windowedit_raise_window (window);

    drag.windowhandle = window->window->handle;
    drag.type = 7;

    /* constrain mouse pointer as necessary */
    {
        Bool topleft, bottomright;
        WindowOutlineRec outline;

        wimp_read_window_constraints (&topleft, &bottomright);

        outline.handle = window->window->handle;
        swi (Wimp_GetWindowOutline, R1, &outline, END);

        if (topleft)
        {
            drag.constrain.minx = 0;
            drag.constrain.maxy = screeny - scaley;
        }
        else
        {
            drag.constrain.minx = mousepos->x - outline.bbox.minx;
            drag.constrain.maxy = screeny -
                                  (outline.bbox.maxy - mousepos->y);
        }

        if (bottomright)
        {
            drag.constrain.maxx = screenx - scalex;
            drag.constrain.miny = 0;
        }
        else
        {
            drag.constrain.maxx = screenx -
                                  (outline.bbox.maxx - mousepos->x);
            drag.constrain.miny = mousepos->y - outline.bbox.miny;
        }
    }

    ER ( swi (Wimp_DragBox,  R1, &drag,  END) );

    interactor_install (winddrag_interactor, (void *) &closure);
    interactor_enable_events (BIT(EV_NULL_REASON_CODE));
    interactor_set_timeout (5);

    return NULL;
}


/*
 * Called from windowedit_mouse_click(..) to process a CTRL/SHFT/ADJ/DRAG.
 */

error * winddrag_resize_start (WindowObjPtr window, PointPtr mousepos)
{
    DragBoxRec drag;
    static WinddragClosureRec closure;

    interactor_cancel();

    closure.moving = FALSE;
    closure.pos = *mousepos;
    closure.window = window;
    closure.origvis = window->window->visarea;
    closure.origscroll = window->window->scrolloffset;

    windowedit_raise_window (window);

    drag.windowhandle = window->window->handle;
    drag.type = 7;

    /* constrain mouse pointer as appropriate */
    {
        WindowPtr win = window->window;
        PointRec pos;

        /* top left hand corner */
        drag.constrain.minx = win->visarea.minx;
        drag.constrain.maxy = win->visarea.maxy;

        /* ensure this is on the screen */
        if (drag.constrain.minx < 0)
            drag.constrain.minx = 0;
        if (drag.constrain.maxy > screeny - scaley)
            drag.constrain.maxy = screeny - scaley;

        /* determine mouse position corresponding to maximum size */
        pos.x = mousepos->x + ((win->workarea.maxx - win->workarea.minx) -
                               (win->visarea.maxx - win->visarea.minx));
        pos.y = mousepos->y - ((win->workarea.maxy - win->workarea.miny) -
                               (win->visarea.maxy - win->visarea.miny));

        /* constrain this to be on the screen */
        if (pos.x > screenx - scalex)
            pos.x = screenx - scalex;
        if (pos.y < 0)
            pos.y = 0;

        drag.constrain.maxx = pos.x;
        drag.constrain.miny = pos.y;
    }

    ER ( swi (Wimp_DragBox,  R1, &drag,  END) );

    interactor_install (winddrag_interactor, (void *) &closure);
    interactor_enable_events (BIT(EV_NULL_REASON_CODE));
    interactor_set_timeout (5);

    return NULL;
}
