/*
 * af_window.c
 *
 * Overrides Reaction window class to replace yucky
 * scrollbars with our standard issue ones.
 *
 * Also maintains Window->UserData pointer to track
 * the window object
 *
 * $Id :$
 * $Log:$
 *
 */

#include "defs.h"

#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/classes.h>
#include <intuition/icclass.h>
#include <intuition/gadgetclass.h>
#include <intuition/imageclass.h>

#include <classact.h>

#include <proto/dos.h>
#include <proto/intuition.h>
#include <clib/alib_protos.h>

#include <stdlib.h>
#include "af_scroller.h"
#include "boopsi_misc.h"


#define     THISCLASS_ID            NULL
#define     SUPERCLASS_ID           NULL
#define     SUPERCLASS_PTR          WINDOW_GetClass()


/*
 * Private class types
 */

struct InstanceData
{
    Object *HorizScroller;
    Object *VertScroller;
};


/*
 * Private functions
 */


static ULONG SAVEDS dispatcher( REG(a0) Class *cl, REG(a2) Object *o, REG(a1) Msg msg );

static ULONG new_method( Class *cl, Object *obj, struct opSet *msg );
static ULONG dispose_method( Class *cl, Object *obj );
static ULONG get_method( Class *cl, Object *obj, struct opGet *msg );
static ULONG open_window_method( Class *cl, Object *obj, Msg msg );
static ULONG close_window_method( Class *cl, Object *obj, Msg msg );

static void  set_scroller_tags( struct TagItem *horiz_tags, struct TagItem *vert_tags, struct Screen *screen );


/*
 * Private data
 */

static Class *class_base;



/*
 * Public functions
 */

Class
*SCRWINDOW_GetClass( void )
{
    return class_base;
}


VOID
_INIT_7_SCRWINDOW_MakeClass( void )
{
    if( class_base = MakeClass( THISCLASS_ID, SUPERCLASS_ID, SUPERCLASS_PTR, sizeof(struct InstanceData), 0L ) )
        class_base->cl_Dispatcher.h_Entry = (ULONG (*)()) dispatcher;

    return;
}


VOID
_EXIT_7_SCRWINDOW_FreeClass( void )
{
    if( class_base )
        FreeClass( class_base );
}


/*
 * Class dispatcher
 */

static ULONG SAVEDS
dispatcher( REG(a0) Class *cl, REG(a2) Object *o, REG(a1) Msg msg )
{
    switch( msg->MethodID )
    {
        /* overridden methods */
        case OM_NEW:     return new_method(  cl, o, (struct opSet *) msg );
        case OM_DISPOSE: return dispose_method( cl, o );
        case OM_GET:     return get_method( cl, o, (struct opGet *) msg );
        case WM_OPEN:    return open_window_method(  cl, o, msg );
        case WM_CLOSE:   return close_window_method( cl, o, msg );

        /* pass any other methods to superclass for handling */
        default:       return( DoSuperMethodA( cl, o, msg ) );
    }
}


/*
 * Class methods
 */

static ULONG
new_method( Class *cl, Object *obj, struct opSet *msg )
{
    struct TagItem  *attrs = msg->ops_AttrList;
    struct Screen *screen;
    BOOL locked            = FALSE;
    BOOL hprop;
    BOOL vprop;

    /* Find the screen we're going to open this window on */
    screen = (struct Screen *) GetTagData( WA_PubScreen, NULL, attrs );

    if(!screen)
        screen = (struct Screen *) GetTagData( WA_CustomScreen, NULL, attrs );

    if(!screen)
    {
        screen = LockPubScreen( NULL );
        locked = TRUE;
    }

    /* Do we want scroll bars ? */
    hprop = GetTagData( WINDOW_HorizProp, FALSE, attrs );
    vprop = GetTagData( WINDOW_VertProp,  FALSE, attrs );

    /* Override these tags - filter them out so they don't get sent to parent */
    if( hprop || vprop )
    {
        const Tag filter_array[] = { WINDOW_HorizProp, WINDOW_VertProp };

        FilterTagItems( attrs, filter_array, TAGFILTER_NOT );
    }

    /* make the parent window */
    if( obj = (Object *) DoSuperMethodA( cl, obj, (Msg) msg ) )
    {
        struct InstanceData *data = INST_DATA( cl, obj );
        struct TagItem tags[10];

        set_scroller_tags( &tags[0], &tags[5], screen );

        /* create scroll bars */
        if( hprop )
            data->HorizScroller = NewObject( AFSCROLLER_GetClass(), NULL,
                                            GA_BottomBorder, TRUE,
                                            PGA_Freedom,    FREEHORIZ,
                                        TAG_MORE, &tags[0] );

        if( vprop )
            data->VertScroller  = NewObject( AFSCROLLER_GetClass(), NULL,
                                            GA_RightBorder,  TRUE,
                                            PGA_Freedom,    FREEVERT,
                                        TAG_MORE, &tags[5] );


    }

    if( locked )
        UnlockPubScreen( NULL, screen );

    return (ULONG) obj;
}


static ULONG
dispose_method( Class *cl, Object *obj )
{
    struct InstanceData *data = INST_DATA( cl, obj );

    if( data->HorizScroller ) DisposeObject( data->HorizScroller );
    if( data->VertScroller )  DisposeObject( data->VertScroller  );

    DoSuperMethod( cl, obj, OM_DISPOSE );

    return 0L;
}


static ULONG
get_method( Class *cl, Object *obj, struct opGet *msg )
{
    struct InstanceData *data = INST_DATA( cl, obj );

    switch (msg->opg_AttrID)
    {
        case WINDOW_HorizObject:
                    *(msg->opg_Storage) = (ULONG) data->HorizScroller;
                    break;

        case WINDOW_VertObject:
                    *(msg->opg_Storage) = (ULONG) data->VertScroller;
                    break;

        /* let parent class handle it */
        default:
             return DoSuperMethodA( cl, obj, (Msg) msg );
    }

    return 1L;
}


static ULONG
open_window_method( Class *cl, Object *obj, Msg msg )
{
    struct Window       *win;

    /* let parent open the window */
    if( win = (struct Window *) DoSuperMethodA( cl, obj, msg ) )
    {
        struct InstanceData *data = INST_DATA( cl, obj );
        struct Screen       *scr  = win->WScreen;
        struct TagItem       tags[10];

        set_scroller_tags( &tags[0], &tags[5], scr );

        if( data->HorizScroller )
        {
            SetAttrs( data->HorizScroller, GA_BottomBorder, TRUE,
                                           PGA_Freedom,    FREEHORIZ,
                                       TAG_MORE, &tags[0] );

            AddGadget( win, (struct Gadget *) data->HorizScroller, -1L );
            RefreshGadgets( (struct Gadget *) data->HorizScroller, win, NULL );
        }

        if( data->VertScroller )
        {
            SetAttrs( data->VertScroller, GA_RightBorder,  TRUE,
                                          PGA_Freedom,    FREEVERT,
                                      TAG_MORE, &tags[5] );

            AddGadget( win, (struct Gadget *) data->VertScroller, -1L );
            RefreshGadgets( (struct Gadget *) data->VertScroller, win, NULL );
        }

        /* set up pointer from Inutiion window to BOOPSI object */
        win->UserData = (APTR) obj;
    }
    return (ULONG) win;
}


static ULONG
close_window_method( Class *cl, Object *obj, Msg msg )
{
    struct InstanceData *data = INST_DATA( cl, obj );

    struct Window *win;

    if( win = (struct Window *) QuickGetAttr( obj, WINDOW_Window ) )
    {
        if(data->HorizScroller)
            RemoveGadget( win, (struct Gadget *) data->HorizScroller );

        if(data->VertScroller)
            RemoveGadget( win, (struct Gadget *) data->VertScroller );
    }

    return DoSuperMethodA( cl, obj, msg );
}


/*
 * Private functions
 */


/*
 * set_scroller_tags
 *
 * A quick hack to initialize tags for our scrollerbars
 * Need a more elegant solution!
 */

static void
set_scroller_tags( struct TagItem *horiz_tags, struct TagItem *vert_tags, struct Screen *screen )
{
    struct DrawInfo *dri = GetScreenDrawInfo( screen );
    struct Image    *size_image;

    /* get a temporary Size gagdet image
       for working out scrollbar size/positions */

    if( size_image = NewObject( NULL, SYSICLASS,
                                    SYSIA_Which, SIZEIMAGE,
                                    SYSIA_DrawInfo, dri,
                                    SYSIA_Size, SYSISIZE_MEDRES,
                                TAG_DONE ) )
    {
        WORD bar_height = dri->dri_Font->tf_YSize + 2;

        horiz_tags->ti_Tag      = GA_Left;
        (horiz_tags++)->ti_Data = 0;
        horiz_tags->ti_Tag      = GA_RelBottom;
        (horiz_tags++)->ti_Data = 1 - size_image->Height;
        horiz_tags->ti_Tag      = GA_RelWidth;
        (horiz_tags++)->ti_Data = - size_image->Width;
        horiz_tags->ti_Tag      = GA_DrawInfo;
        (horiz_tags++)->ti_Data = (ULONG) dri;
        horiz_tags->ti_Tag      = TAG_DONE;

        vert_tags->ti_Tag      = GA_RelRight;
        (vert_tags++)->ti_Data = 1 - size_image->Width;
        vert_tags->ti_Tag      = GA_Top;
        (vert_tags++)->ti_Data = bar_height;
        vert_tags->ti_Tag      = GA_RelHeight;
        (vert_tags++)->ti_Data = - bar_height - size_image->Height;
        vert_tags->ti_Tag      = GA_DrawInfo;
        (vert_tags++)->ti_Data = (ULONG) dri;
        vert_tags->ti_Tag      = TAG_DONE;

        DisposeObject( size_image );
    }
    FreeScreenDrawInfo( screen, dri );
}


