/*-*-C-*-
 * Window extent dialogue box for Windows CSE
 */

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

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

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


static WindowPtr extentdboxproto;
static int extentdboxsize;



/*
 * Load any templates required - called from load_prototypes(..) in main.c
 */

error * extent_load_prototypes (void)

{
    return wimp_load_template_returning_size (
                  "Extent", &extentdboxproto, &extentdboxsize);
}


/*
 * Called by extent_dbox_mouse_click(..) or extent_dbox_key_pressed(..) after
 * the user has indicated that he wishes to apply the contents of the dbox.
 *
 */

static error * apply_dbox (WindowObjPtr window, Bool stayopen)
{
    WindowPtr dbox = window->extentdbox;
    WindowPtr win = window->window;
    int minx, miny, maxx, maxy;
    int excess;
    int minwidth, minheight;
    RectRec oldworkarea = win->workarea;
    ShortSizeRec oldminsize = win->minsize;

    minx = WIMP_ALIGN_COORD(dbox_getint (dbox, I_EXTENT_XMIN));
    maxx = WIMP_ALIGN_COORD(dbox_getint (dbox, I_EXTENT_XMAX));
    miny = WIMP_ALIGN_COORD(dbox_getint (dbox, I_EXTENT_YMIN));
    maxy = WIMP_ALIGN_COORD(dbox_getint (dbox, I_EXTENT_YMAX));

    minwidth =  WIMP_ALIGN_COORD(dbox_getint (dbox, I_EXTENT_MINWIDTH));
    minheight = WIMP_ALIGN_COORD(dbox_getint (dbox, I_EXTENT_MINHEIGHT));

    /* swap corners if wrong way round */
    if (minx >= maxx) { int t = minx; minx = maxx; maxx = t; }
    if (miny >= maxy) { int t = miny; miny = maxy; maxy = t; }

    /* trim visible width and height if too big */
    excess = (win->visarea.maxx - win->visarea.minx) - (maxx - minx);
    if (excess > 0)
        win->visarea.maxx -= excess;
    excess = (win->visarea.maxy - win->visarea.miny) - (maxy - miny);
    if (excess > 0)
        win->visarea.miny += excess;

    /* validate X scrollbar */
    if (win->scrolloffset.x < minx)
        win->scrolloffset.x = minx;
    excess = maxx - (win->visarea.maxx - win->visarea.minx);
    if (win->scrolloffset.x > excess)
        win->scrolloffset.x = excess;

    /* validate Y scrollbar */
    if (win->scrolloffset.y > maxy)
        win->scrolloffset.y = maxy;
    excess = miny + (win->visarea.maxy - win->visarea.miny);
    if (win->scrolloffset.y < excess)
        win->scrolloffset.y = excess;

    /* ensure minimum sizes are within extent */
    if (minwidth > (maxx - minx))
        minwidth = maxx - minx;
    if (minheight > (maxy - miny))
        minheight = maxy - miny;

    /* finally copy values across to window structure */
    win->workarea.minx = minx;
    win->workarea.maxx = maxx;
    win->workarea.miny = miny;
    win->workarea.maxy = maxy;

    win->minsize.width = (unsigned short) minwidth;
    win->minsize.height = (unsigned short) minheight;

    /* reflect any changes that have been made */
    if (oldworkarea.minx != minx ||
        oldworkarea.maxx != maxx ||
        oldworkarea.miny != miny ||
        oldworkarea.maxy != maxy ||
        oldminsize.width != minwidth ||
        oldminsize.height != minheight)
    {  
        /* delete and recreate window to reflect changes */
        windowedit_redisplay_window (window);

        /* inform shell that window object may have been modified */
        protocol_send_resed_object_modified (window);
    }

    /* redisplay Extent dbox if ADJ click - otherwise destroy it */
    if (stayopen)
        return extent_update_dbox (window, TRUE, FALSE);
    else
        return extent_close_dbox (window);
}


/*
 * Update contents of dialogue box - only called if dialogue box exists.
 *
 * Called from windowedit_rename_window(..) - when the object is renamed.
 *        from windowedit_load(..) - when a new version of the object is
 *                                    force loaded [not at present]
 *        from apply_dbox(..) - if the extent dbox is to remain on view
 *        from extent_open_dbox(..) - after the dbox is created
 *        from extent_dbox_mouse_click(..) - after an ADJ-CANCEL click
 */

error * extent_update_dbox (
    WindowObjPtr window,
    Bool contents,
    Bool title
)
{
    WindowPtr dbox = window->extentdbox;
    WindowPtr win = window->window;

    if (contents)
    {
        dbox_setint (dbox, I_EXTENT_MINWIDTH, win->minsize.width);
        dbox_setint (dbox, I_EXTENT_MINHEIGHT, win->minsize.height);
        dbox_setint (dbox, I_EXTENT_XMAX, win->workarea.maxx);
        dbox_setint (dbox, I_EXTENT_YMAX, win->workarea.maxy);
        dbox_setint (dbox, I_EXTENT_XMIN, win->workarea.minx);
        dbox_setint (dbox, I_EXTENT_YMIN, win->workarea.miny);
    }

    if (title)
    {
        /* Get the printf pattern from the prototype's titlebar which
         * should have %s in it where the name is wanted
         */
        char buf[256];
        sprintf (buf, dbox_gettitle (extentdboxproto), window->name);
        ER ( dbox_settitle (dbox, buf, TRUE) );
    }

    return NULL;
}


/*
 * Called from:
 *  windowedit_menu_cb(..) - when "Extent..." is chosen;
 *  windowedit_key_pressed(..) - when ^E is pressed
 *
 * If the corresponding extent dbox is already open, it is simply raised
 *  to the top of the window stack.
 *
 * Otherwise, a new extent window is created and opened.
 *
 * Any new window will be displayed at the same position as the most recent
 *  position of any other window created from the "extent" template;
 *  this is achieved by copying co-ordinates to the template whenever an
 *  existing window is reopened - see extent_reopen_dbox(..).
 */

error * extent_open_dbox (WindowObjPtr window)
{
    if (window->extentdbox == NULL)
    {
        ER ( wimp_copy_template (extentdboxproto,
                                 &window->extentdbox, extentdboxsize) );
        ER ( swi (Wimp_CreateWindow,  R1, &window->extentdbox->visarea,
                        OUT,  R0, &window->extentdbox->handle,  END) );
        ER ( registry_register_window (window->extentdbox->handle,
                                       ExtentDbox, (void *) window) );
        window->extentdbox->behind = -1;
        ER ( swi (Wimp_OpenWindow,  R1, window->extentdbox,  END) );
        ER ( extent_update_dbox (window, TRUE, TRUE) );
    }
    else
    {
        window->extentdbox->behind = -1;
        ER ( swi (Wimp_OpenWindow,  R1, window->extentdbox,  END) );
    }   

    return dbox_set_caret_to (window->extentdbox, I_EXTENT_MINWIDTH);
}


/*
 * Called when user clicks on Clip button
 */

static void clip_to_visarea (WindowObjPtr window)
{
    WindowPtr win = window->window;
    WindowPtr dbox = window->extentdbox;
    int minx, miny, maxx, maxy;

    minx = win->scrolloffset.x;
    maxx = minx + (win->visarea.maxx - win->visarea.minx);
    maxy = win->scrolloffset.y;
    miny = maxy - (win->visarea.maxy - win->visarea.miny);

    dbox_setint (dbox, I_EXTENT_XMAX, maxx);
    dbox_setint (dbox, I_EXTENT_XMIN, minx);
    dbox_setint (dbox, I_EXTENT_YMAX, maxy);
    dbox_setint (dbox, I_EXTENT_YMIN, miny);

    return;
}


/*
 * Called from mouse_click(..) in main.c when the user clicks inside the
 *  dialogue box.
 */

error * extent_dbox_mouse_click (
    MouseClickPtr mouse,
    unsigned int modifiers,
    WindowObjPtr window
)
{
    int buttons = mouse->buttons;
    int icon = mouse->iconhandle;
    WindowPtr dbox = window->extentdbox;
    int delta = buttons == MB_CLICK(MB_ADJUST) ? -4 : 4;

    /* give input focus to the dbox regardless */
    dbox_set_caret_to (dbox, -1);

    if (buttons == MB_CLICK(MB_SELECT) || buttons == MB_CLICK(MB_ADJUST))
    {
        switch (icon)
        {
        case I_EXTENT_CANCEL:
            if (delta > 0)
                return extent_close_dbox (window);
            else
                return extent_update_dbox (window, TRUE, FALSE);
            break;

        case I_EXTENT_OK:
            return apply_dbox (window, delta < 0);

        case I_EXTENT_CLIP:
            clip_to_visarea(window);
            break;

        case I_EXTENT_XMAX_ADJ_DOWN:
            delta = -delta;
        case I_EXTENT_XMAX_ADJ_UP:
            gui_adjust_coord (dbox, I_EXTENT_XMAX, delta, modifiers);
            break;

        case I_EXTENT_YMAX_ADJ_DOWN:
            delta = -delta;
        case I_EXTENT_YMAX_ADJ_UP:
            gui_adjust_coord (dbox, I_EXTENT_YMAX, delta, modifiers);
            break;

        case I_EXTENT_XMIN_ADJ_DOWN:
            delta = -delta;
        case I_EXTENT_XMIN_ADJ_UP:
            gui_adjust_coord (dbox, I_EXTENT_XMIN, delta, modifiers);
            break;

        case I_EXTENT_YMIN_ADJ_DOWN:
            delta = -delta;
        case I_EXTENT_YMIN_ADJ_UP:
            gui_adjust_coord (dbox, I_EXTENT_YMIN, delta, modifiers);
            break;
        }
    }

    return NULL;
}


/*
 * Called from open_window_request(..) in main.c.
 *
 * Copies coordinates of current position to the prototype;
 *  see extent_open_dbox(..) above.
 */

error * extent_reopen_dbox (WindowPtr win, WindowObjPtr window)
{
    window->extentdbox->visarea = extentdboxproto->visarea = win->visarea;
    window->extentdbox->scrolloffset =
                       extentdboxproto->scrolloffset = win->scrolloffset;
    window->extentdbox->behind = win->behind;
    return swi (Wimp_OpenWindow, R1, window->extentdbox, END);

    return NULL;
}


/*
 * Called from:
 *   close_window_request(..) in main.c - when the user clicks on the
 *          dialogue box's close icon.
 *   apply_dbox(..) -  after the new contents have been processed.
 *   extent_dbox_mouse_click(..) -  after a SEL-CANCEL click.
 *   windowedit_close_window(..) - when closing the parent editing window.
 *
 * The dialogue box is deleted.
 */

error * extent_close_dbox (WindowObjPtr window)
{
    WindowPtr extentdbox = window->extentdbox;

    ER ( registry_deregister_window (extentdbox->handle) );

    /*
     * This service call is fielded by BorderUtils 0.05 which fixes
     *  a bug associated with the deletion of windows which have pressed-in
     *  slabbed icons in wimps before 3.17
     */

    ER ( swi (OS_ServiceCall, R0, extentdbox->handle,
                              R1, 0x44ec5,
                              R2, taskhandle, END) );

    ER ( swi (Wimp_DeleteWindow,  R1, extentdbox,  END) );
    free ((char *) extentdbox);
    window->extentdbox = NULL;

    return windowedit_focus_claim (window);
}


/*
 * Called from key_pressed(..) in main(..), when the user depresses a key
 *  when the dialogue box has the input focus.
 */

error * extent_dbox_key_pressed (
    WindowObjPtr window,
    KeyPressPtr key,
    Bool *consumed
)
{
    if (key->code == 13)         /* RETURN */
    {
        *consumed = TRUE;
        apply_dbox (window, (wimp_read_modifiers() & MODIFIER_SHIFT) != 0);
    }

    else if (key->code == 0x1b)  /* ESCAPE */
    {
        *consumed = TRUE;
        if ((wimp_read_modifiers() & MODIFIER_SHIFT) == 0)
            extent_close_dbox (window);
        else
            extent_update_dbox (window, TRUE, FALSE);
    }

    return NULL;
}
