/*
 * Logo.c -- a XView object class that paints an X logo in a window.
 * This object is subclassed from the window object to take advantage
 * of the window it creates.  This object has no attributes, so the
 * set and get functions are virtually empty.  The only internal
 * fields used by this object are a GC and a Pixmap.  The GC is used
 * to paint the Pixmap into the window.  The window object has no GC
 * associated with it or we would have inherited it.  This will
 * probably go away in the next version of XView.
 */
#include "logo_impl.h"
#include <xview/notify.h>
#include <xview/cms.h>
#include <X11/bitmaps/xlogo32>

/* declare the "methods" used by the logo class. */
static int logo_init(), logo_destroy();
static Xv_opaque logo_set(), logo_get();
static void logo_repaint();

Xv_pkg logo_pkg =
{
    "Logo",                     /* package name */
    ATTR_PKG_UNUSED_FIRST,      /* package ID */
    sizeof(Logo_public),        /* size of the public struct */
    WINDOW,                     /* subclassed from the window package */
    logo_init,
    logo_set,
    logo_get,
    logo_destroy,
    NULL                        /* disable the use of xv_find() */
};

/* the only thing this object does is paint an X into its own window.
 * This is the event handling routine that is used to check for
 * Expose or Configure event requests.  the configure event clears
 * the window and the "expose" event causes a repaint of the X image.
 * The GC has it's foreground and background colros set from the
 * CMS of the window from which this logo object is subclassed.
 */
static void
logo_redraw(logo_public, event)
Logo_public     *logo_public;
Event           *event;
{
    Logo_private *logo_private = LOGO_PRIVATE(logo_public);
    XEvent *xevent = event_xevent(event);

    if(xevent->xany.type == Expose && xevent->xexpose.count == 0)
    {
        Display *dpy = (Display *)xv_get(logo_public, XV_DISPLAY);
        Window window = (Window)xv_get(logo_public, XV_XID);
        int width = (int)xv_get(logo_public, XV_WIDTH);
        int height = (int)xv_get(logo_public, XV_HEIGHT);
        int x = (width - xlogo32_width) / 2;
        int y = (height - xlogo32_height) / 2;

        XCopyPlane(dpy, logo_private->bitmap, window, logo_private->gc,
                   0, 0, xlogo32_width, xlogo32_height, x, y, 1L);
    }
    else if(xevent->xany.type == ConfigureNotify)
        XClearArea(xv_get(logo_public, XV_DISPLAY),
                   xv_get(logo_public, XV_XID), 0, 0,
                   xevent->xconfigure.width, xevent->xconfigure.height, True);
}

/* initialize the logo object -- create (alloc) an instance of it.
 * There are two parts to an object class: a public part and a private
 * part.  Each contains a pointer to the other, so link the two
 * together and initialize the remaining fields of the logo data
 * structure.  This includes creating the Xlogo pixmap.  However,
 * we do no initialize the logo's GC because it is dependent on its
 * window's cms and that isn't assigned to the window till the "set"
 * method.  See logo_set() below.
 */
static int
logo_init(owner, logo_public, avlist)
Xv_opaque       owner;
Logo_public     *logo_public;
Attr_avlist     avlist; /* ignored here */
{
    Logo_private *logo_private = xv_alloc(Logo_private);
    Display *dpy;
    Window win;

    if(!logo_private)
        return XV_ERROR;

    dpy = (Display *)xv_get(owner, XV_DISPLAY);
    win = (Window)xv_get(logo_public, XV_XID);

    /* link the public to the private and vice-versa */
    logo_public->private_data = (Xv_opaque)logo_private;
    logo_private->public_self = (Xv_opaque)logo_public;

    /* create the 1-bit deep pixmap of the X logo */
    if((logo_private->bitmap = XCreatePixmapFromBitmapData(dpy, win,
                               xlogo32_bits, xlogo32_width, xlogo32_height,
                               1, 0, 1)) == NULL)
    {
        free(logo_private);
        return XV_ERROR;
    }

    /* set up event handlers to get resize and repaint events */
    xv_set(logo_public,
           WIN_NOTIFY_SAFE_EVENT_PROC,      logo_redraw,
           WIN_NOTIFY_IMMEDIATE_EVENT_PROC, logo_redraw,
           NULL);

    return XV_OK;
}

/* logo_set() -- the function called to set attributes in a logo
 * object.  This function is called when a logo is created after
 * the init routine as well as when the programmer calls xv_set.
 */
static Xv_opaque
logo_set(logo_public, avlist)
Logo_public *logo_public;
Attr_avlist avlist;
{
    Logo_private *logo_private = LOGO_PRIVATE(logo_public);
    Attr_attribute *attrs;

    for(attrs = avlist; *attrs; attrs = attr_next(attrs))
        switch((int) attrs[0])
        {
        case XV_END_CREATE :
        {
            /* this stuff *must* be here rather than in the "init"
             * routine because the CMS is not loaded into the
             * window object until the "set" routines are called.
             */
            Cms cms = xv_get(logo_public, WIN_CMS);
            XGCValues gcvalues;
            Display *dpy =
                (Display *)xv_get(logo_public, XV_DISPLAY);
            gcvalues.foreground =
                (unsigned long)xv_get(cms, CMS_FOREGROUND_PIXEL);
            gcvalues.background =
                (unsigned long)xv_get(cms, CMS_BACKGROUND_PIXEL);
            gcvalues.graphics_exposures = False;
            logo_private->gc = XCreateGC(dpy,
                                         xv_get(logo_public, XV_XID),
                                         GCForeground | GCBackground | GCGraphicsExposures,
                                         &gcvalues);
        }

        default :
            xv_check_bad_attr(LOGO, attrs[0]);
            break;
        }

    return XV_OK;
}

/* logo_get() -- There are no logo attributes to get, so just return */
static Xv_opaque
logo_get(logo_public, status, attr, args)
Logo_public     *logo_public;
int             *status;
Attr_attribute  attr;
Attr_avlist     args;
{
    *status = xv_check_bad_attr(LOGO, attr);
    return (Xv_opaque)XV_OK;
}

/* destroy method: free the pixmap and the GC before freeing the object */
static int
logo_destroy(logo_public, status)
Logo_public     *logo_public;
Destroy_status   status;
{
    Logo_private *logo_private = LOGO_PRIVATE(logo_public);

    if(status == DESTROY_CLEANUP)
    {
        XFreePixmap(xv_get(logo_public, XV_DISPLAY),
                    logo_private->bitmap);
        XFreeGC(xv_get(logo_public, XV_DISPLAY), logo_private->gc);
        free(logo_private);
    }

    return XV_OK;
}
