/* Written by Dan Heller and Paula Ferguson.  
 * Copyright 1994, O'Reilly & Associates, Inc.
 * Permission to use, copy, and modify this program without
 * restriction is hereby granted, as long as this copyright
 * notice appears in each copy of the program source code.
 * This program is freely distributable without licensing fees and
 * is provided without guarantee or warrantee expressed or implied.
 * This program is -not- in the public domain.
 */

/* draw2.c -- extremely simple drawing program that demonstrates
 * how to draw into an off screen pixmap in order to retain the
 * contents of the DrawingArea widget.  This allows us to redisplay
 * the widget if it needs repainting (expose events).
 */
#include <Xm/DrawingA.h>
#include <Xm/PushBG.h>
#include <Xm/RowColumn.h>

#define WIDTH 400    /* arbitrary width and height values */
#define HEIGHT 300

Pixmap pixmap; /* used to redraw the DrawingArea */

main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, drawing_a, pb;
    XtAppContext app;
    GC gc;
    void drawing_area_callback();

    XtSetLanguageProc (NULL, NULL, NULL);

    toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, 
        &argc, argv, NULL,
        XmNwidth,  WIDTH,
        XmNheight, HEIGHT,
        NULL);

    /* Create a DrawingArea widget. */
    drawing_a = XtVaCreateWidget ("drawing_a",
        xmDrawingAreaWidgetClass, toplevel,
        NULL);
    /* add callback for all mouse and keyboard input events */
    XtAddCallback (drawing_a, XmNinputCallback, drawing_area_callback, NULL);
    XtAddCallback (drawing_a, XmNexposeCallback, drawing_area_callback, NULL);

    gc = XCreateGC (XtDisplay (drawing_a),
        RootWindowOfScreen (XtScreen (drawing_a)), 0, NULL);
    XtVaSetValues (drawing_a, XmNuserData, gc, NULL);

    XSetForeground (XtDisplay (drawing_a), gc,
        WhitePixelOfScreen (XtScreen (drawing_a)));
    /* create a pixmap the same size as the drawing area. */
    pixmap = XCreatePixmap (XtDisplay (drawing_a),
        RootWindowOfScreen (XtScreen (drawing_a)), WIDTH, HEIGHT,
        DefaultDepthOfScreen (XtScreen (drawing_a)));
    /* clear pixmap with white */
    XFillRectangle (XtDisplay (drawing_a), pixmap, gc, 0, 0, WIDTH, HEIGHT);
    /* drawing is now drawn into with "black"; change the gc for future */
    XSetForeground (XtDisplay (drawing_a), gc,
        BlackPixelOfScreen (XtScreen (drawing_a)));

    /* add a pushbutton the user can use to clear the canvas */
    pb = XtVaCreateManagedWidget ("Clear",
        xmPushButtonGadgetClass, drawing_a,
        NULL);
    /* if activated, call same callback as XmNinputCallback. */
    XtAddCallback (pb, XmNactivateCallback, drawing_area_callback, NULL);

    XtManageChild (drawing_a);
    XtRealizeWidget (toplevel);
    XtAppMainLoop (app);
}

/* Callback routine for DrawingArea's input and expose callbacks
 * as well as the PushButton's activate callback.  Determine which
 * it is by testing the cbs->reason field.
 */
void
drawing_area_callback(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
    static Position x, y;
    XmDrawingAreaCallbackStruct *cbs = 
        (XmDrawingAreaCallbackStruct *) call_data;
    XEvent *event = cbs->event;
    Display *dpy = event->xany.display;

    if (cbs->reason == XmCR_INPUT) {
        /* activated by DrawingArea input event -- draw lines.
         * Button Down events anchor the initial point and Button
         * Up draws from the anchor point to the button-up point.
         */
        if (event->xany.type == ButtonPress) {
            /* anchor initial point (i.e., save its value) */
            x = event->xbutton.x;
            y = event->xbutton.y;
        } else if (event->xany.type == ButtonRelease) {
            /* draw full line; get GC and use in XDrawLine() */
            GC gc;
            XtVaGetValues (widget, XmNuserData, &gc, NULL);
            XDrawLine (dpy, cbs->window, gc, x, y,
                event->xbutton.x, event->xbutton.y);
            /* draw into the pixmap as well for redrawing later */
            XDrawLine (dpy, pixmap, gc, x, y,
                event->xbutton.x, event->xbutton.y);
            x = event->xbutton.x;
            y = event->xbutton.y;
        }
    }

    if (cbs->reason == XmCR_EXPOSE || cbs->reason == XmCR_ACTIVATE) {
        GC gc;
        if (cbs->reason == XmCR_ACTIVATE) /* Clear button pushed */
            widget = XtParent (widget); /* get the DrawingArea widget */
        XtVaGetValues (widget, XmNuserData, &gc, NULL);
        if (cbs->reason == XmCR_ACTIVATE) { /* Clear button pushed */
            /* to clear a pixmap, reverse foreground and background */
            XSetForeground (dpy, gc, WhitePixelOfScreen (XtScreen (widget)));
            /* ...and fill rectangle the size of the pixmap */
            XFillRectangle (dpy, pixmap, gc, 0, 0, WIDTH, HEIGHT);
            /* don't foreget to reset */
            XSetForeground (dpy, gc, BlackPixelOfScreen (XtScreen (widget)));
        }
        /* Note: we don't have to use WIDTH and HEIGHT--we could pull the
         * exposed area out of the event structure, but only if the reason
         * was XmCR_EXPOSE... make it simple for the demo; optimize as needed.
         */
        XCopyArea (dpy, pixmap, event->xany.window, gc,
            0, 0, WIDTH, HEIGHT, 0, 0);
    }
}