/*-*-C-*-
 * Coords dialogue box for Window CSE
 */

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

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"
#include "newmsgs.h"
#include "dbox.h"
#include "interactor.h"
#include "registry.h"

#include "format.h"
#include "windowedit.h"
#include "coords.h"
#include "gui.h"
#include "icondefs.h"
#include "protocol.h"


static WindowPtr coordswin = NULL;
static Bool asmenu = FALSE;


/*
 * Load templates, etc.
 */

error * coords_load_prototypes ()
{
    ER ( wimp_load_template("Coords", &coordswin) );
    ER ( swi (Wimp_CreateWindow,  R1, &coordswin->visarea,
                OUT,  R0, &coordswin->handle,  END) );

    /* must be registered for interactive help */
    return registry_register_window (coordswin->handle,
                                       CoordsDbox, (void *) coordswin);
}


/*
 * Called to initialise an option icon/value field pair
 */

static void put_field_pair (int opticon, int valicon, Bool ok, int val)
{
    dbox_setbutton (coordswin, opticon, ok);
    if (ok)
        dbox_setint (coordswin, valicon, val);
    else
        dbox_setstring (coordswin, valicon, "");
    dbox_shade (coordswin, valicon, !ok);

    return;
}


/*
 * Get values from a field pair
 */

static void get_field_pair (int opticon, int valicon, Bool *set, int *val)
{
    *set = dbox_getbutton (coordswin, opticon);
    if (*set)
        *val = dbox_getint (coordswin, valicon);

    return;
}


/*
 * Called to initialise the dbox contents
 */

static void initialise_coords (WindowObjPtr window)
{
    Bool xok = TRUE, yok = TRUE, widthok = TRUE, heightok = TRUE;
    int x, y, width, height;
    Bool first = TRUE;
    GadgetPtr gadget = window->gadgets;

    while (gadget != NULL)
    {
        if (gadget->selected)
        {
            if (first)
            {
                first = FALSE;
                x = gadget->hdr.bbox.minx;
                y = gadget->hdr.bbox.maxy;
                width = gadget->hdr.bbox.maxx - x;
                height = y - gadget->hdr.bbox.miny;
            }
            else
            {
                if (xok && x != gadget->hdr.bbox.minx)
                    xok = FALSE;
                if (yok && y != gadget->hdr.bbox.maxy)
                    yok = FALSE;
                if (widthok &&
                  width != (gadget->hdr.bbox.maxx - gadget->hdr.bbox.minx))
                    widthok = FALSE;
                if (heightok &&
                  height != (gadget->hdr.bbox.maxy - gadget->hdr.bbox.miny))
                    heightok = FALSE;
            }
        }
        gadget = gadget->next;
    }

    put_field_pair (I_COORDS_SETX, I_COORDS_X, xok, x);
    put_field_pair (I_COORDS_SETY, I_COORDS_Y, yok, y);
    put_field_pair (I_COORDS_SETWIDTH, I_COORDS_WIDTH, widthok, width);
    put_field_pair (I_COORDS_SETHEIGHT, I_COORDS_HEIGHT, heightok, height);

    return;
}


/*
 * Called when coords dialogue is complete
 */

static error * execute_coords (WindowObjPtr window)
{
    Bool setx, sety, setwidth, setheight;
    int x, y, width, height;
    GadgetPtr gadget = window->gadgets;
    RectRec bbox;
    Bool modified = FALSE;

    get_field_pair (I_COORDS_SETX, I_COORDS_X, &setx, &x);
    get_field_pair (I_COORDS_SETY, I_COORDS_Y, &sety, &y);
    get_field_pair (I_COORDS_SETWIDTH, I_COORDS_WIDTH, &setwidth, &width);
    get_field_pair (I_COORDS_SETHEIGHT, I_COORDS_HEIGHT, &setheight,
                                                               &height);

    /* check that gadget minimum size constraints are not violated */
    if (setwidth || setheight)
        while (gadget != NULL)
        {
            if (gadget->selected)
            {
                if (setwidth && width < gadget->def->minsize.x)
                    return error_lookup ((window->numselected == 1) ?
                                                 "BadWidth1" : "BadWidth");
                if (setheight && height < gadget->def->minsize.y)
                    return error_lookup ((window->numselected == 1) ?
                                                 "BadHeight1" : "BadHeight");
            }
            gadget = gadget->next;
        }

    /* note bbox of original selection, allowing for resize ears */
    windowedit_get_selection_bbox (window, &bbox);
    windowedit_add_ears_to_bbox (&bbox, &bbox);

    gadget = window->gadgets;
    while (gadget != NULL)
    {
        if (gadget->selected)
        {
            if (setx || setwidth)
            {
                if (!setwidth)
                    width = gadget->hdr.bbox.maxx - gadget->hdr.bbox.minx;
                if (!setx)
                    x = gadget->hdr.bbox.minx;
                {
                    int newminx = WIMP_ALIGN_COORD(x);
                    int newmaxx = newminx + WIMP_ALIGN_COORD(width);

                    if (gadget->hdr.bbox.minx != newminx)
                    {
                        modified = TRUE;
                        gadget->hdr.bbox.minx = newminx;
                    }
                    if (gadget->hdr.bbox.maxx != newmaxx)
                    {
                        modified = TRUE;
                        gadget->hdr.bbox.maxx = newmaxx;
                    }
                }
            }
            if (sety || setheight)
            {
                if (!setheight)
                    height = gadget->hdr.bbox.maxy - gadget->hdr.bbox.miny;
                if (!sety)
                    y = gadget->hdr.bbox.maxy;
                {
                    int newmaxy = WIMP_ALIGN_COORD(y);
                    int newminy = newmaxy - WIMP_ALIGN_COORD(height);

                    if (gadget->hdr.bbox.maxy != newmaxy)
                    {
                        modified = TRUE;
                        gadget->hdr.bbox.maxy = newmaxy;
                    }
                    if (gadget->hdr.bbox.miny != newminy)
                    {
                        modified = TRUE;
                        gadget->hdr.bbox.miny = newminy;
                    }
                }
            }
        }

        gadget = gadget->next;
    }

    if (modified)
    {
        /* note modification */
        protocol_send_resed_object_modified (window);

        /* invalidate original selection */
        wimp_invalidate (window->window, &bbox);

        /* and resized selection, allowing for resize ears */
        windowedit_get_selection_bbox (window, &bbox);
        windowedit_add_ears_to_bbox (&bbox, &bbox);
        wimp_invalidate (window->window, &bbox);
    }

    return NULL;
}


/*
 * Interactor for the coords window.  Expects to be pushed on top of the
 * menu.c interactor, so needs to be careful about popping itself correctly.
 */

static error * coords_interactor (unsigned int event, int *buf, void *closure, Bool *consumed)
{
    MouseClickPtr mouse = (MouseClickPtr) buf;
    WindowPtr win = (WindowPtr) buf;         /* only half there */
    KeyPressPtr key = (KeyPressPtr) buf;
    WindowObjPtr window = (WindowObjPtr) closure;
    error *err = NULL;

    if (buf == NULL)                     /* we are being asked to cancel */
        return NULL;

    switch (event)
    {
    case EV_OPEN_WINDOW_REQUEST:
        if (win->handle == coordswin->handle)
        {
            *consumed = TRUE;
            coordswin->visarea = win->visarea;
            coordswin->behind = win->behind;
            ER ( swi (Wimp_OpenWindow, R1, coordswin, END) );
        }
        break;

    case EV_KEY_PRESSED:
        if (key->caret.windowhandle == coordswin->handle)
        {
            unsigned int modifiers = wimp_read_modifiers ();

            *consumed = TRUE;
            switch (key->code)
            {
            case 0x0d:   /* RETURN */
                err = execute_coords (window);
                if (err)
                {
                    error_box (err);
                    break;
                }

            case 0x1b:   /* ESCAPE */
                if ((modifiers & MODIFIER_SHIFT) == 0)
                {
                    if (asmenu)
                        swi (Wimp_CreateMenu, R1, -1, END);
                    /* return input focus to parent window */
                    dbox_set_caret_to (window->window, -1);
                    interactor_cancel();
                    return NULL;
                }
                else
                    initialise_coords (window);
                break;

            default:
                *consumed = FALSE;
                return NULL;
            }

            /* ensure caret is not in faded icon */
            dbox_set_caret_to (coordswin, -1);
        }
        break;

    case EV_MOUSE_CLICK:
        if (mouse->windowhandle == coordswin->handle)
        {
            int valicon;

            *consumed = TRUE;
            if (mouse->buttons == MB_CLICK(MB_SELECT) ||
                mouse->buttons == MB_CLICK(MB_ADJUST))
            {
                switch (mouse->iconhandle)
                {
                case I_COORDS_SETX:
                    valicon = I_COORDS_X;
                    goto cont;
                case I_COORDS_SETY:
                    valicon = I_COORDS_Y;
                    goto cont;
                case I_COORDS_SETWIDTH:
                    valicon = I_COORDS_WIDTH;
                    goto cont;
                case I_COORDS_SETHEIGHT:
                    valicon = I_COORDS_HEIGHT;
                  cont:
                    GUI_TOGGLE_FADE (coordswin, valicon);
                    if ( (dbox_getflags (coordswin, valicon) & IF_SHADED)
                            == 0 )
                        dbox_place_caret (coordswin, valicon);
                    break;

                case I_COORDS_OK:
                    err = execute_coords (window);
                    if (err)
                    {
                        error_box (err);
                        break;
                    }

                case I_COORDS_CANCELT:
                    if (mouse->buttons == MB_CLICK(MB_SELECT))
                    {
                        if (asmenu)
                            swi (Wimp_CreateMenu, R1, -1, END);
                        /* return input focus to parent window */
                        dbox_set_caret_to (window->window, -1);
                        interactor_cancel ();
                        return NULL;
                    }
                    else
                        initialise_coords (window);
                    break;
                }

                /* ensure caret is not in faded icon */
                dbox_set_caret_to (coordswin, -1);
            }
        }
        break;
    }

    return NULL;
}


/*
 * Open the coords dialogue as a submenu at the appointed position.
 *
 * If position is NULL, then the dbox is to be opened as a transient dbox in
 *  its default position.
 *
 * If position is not NULL, then the dbox is to be opened as a submenu at the
 *  specified position.
 */

error * coords_open (WindowObjPtr window, PointPtr position)
{
    initialise_coords (window);

    if (position == NULL)
    {
        asmenu = TRUE;

        ER ( swi (Wimp_CreateMenu, R1, coordswin->handle,
                                   R2, coordswin->visarea.minx,
                                   R3, coordswin->visarea.maxy, END) );
        interactor_install (coords_interactor, (void *) window);

        /* give input focus to the dbox, ensure caret not in faded icon */
        ER ( dbox_set_caret_to (coordswin, -1) );
    }
    else
    {
        asmenu = FALSE;

        /* record new position of window in case opened by key next time */
        coordswin->visarea.maxx = position->x +
                         (coordswin->visarea.maxx - coordswin->visarea.minx);
        coordswin->visarea.minx = position->x;
        coordswin->visarea.miny = position->y -
                         (coordswin->visarea.maxy - coordswin->visarea.miny);
        coordswin->visarea.maxy = position->y;

        ER ( swi (Wimp_CreateSubMenu,  R1, coordswin->handle,
                                       R2, position->x,
                                       R3, position->y,  END) );

        /* give input focus to the dbox, ensure caret not in faded icon */
        ER ( dbox_set_caret_to (coordswin, -1) );
        interactor_push (coords_interactor, (void *) window);
    }

    return NULL;
}


