/* 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.
 */

/* color_draw.c -- simple drawing program using predefined colors.  */
#include <Xm/MainW.h>
#include <Xm/DrawingA.h>
#include <Xm/PushBG.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/ScrolledW.h>
#include <Xm/Form.h>

GC gc;
Pixmap pixmap;
/* dimensions of drawing area (pixmap) */
Dimension width, height;

String colors[] = {
    "Black", "Red", "Green", "Blue", "White", "Navy", "Orange", "Yellow",
    "Pink", "Magenta", "Cyan", "Brown", "Grey", "LimeGreen", "Turquoise",
    "Violet", "Wheat", "Purple"
};

void
Exit(drawing_a, client_data, call_data)
Widget    drawing_a;
XtPointer client_data;
XtPointer call_data;
{
  exit(0); 
}

main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, main_w, sw, rc, form, drawing_a, pb;
    XtAppContext app;
    XGCValues gcv;
    void draw(), redraw(), set_color(), exit(), clear_it();
    int i;
    XtActionsRec actions;
    String translations = /* for the DrawingArea widget */
        ":   draw(down)\n\
         :     draw(up)  \n\
         : draw(motion)";

    XtSetLanguageProc (NULL, NULL, NULL);

    toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, 
        &argc, argv, NULL, NULL);

    /* Create a MainWindow to contain the drawing area */
    main_w = XtVaCreateManagedWidget ("main_w",
        xmFormWidgetClass, toplevel, NULL);

    /* Create a GC for drawing (callback).  Used a lot -- make global */
    gcv.foreground = WhitePixelOfScreen (XtScreen (main_w));
    gc = XCreateGC (XtDisplay (main_w),
        RootWindowOfScreen (XtScreen (main_w)), GCForeground, &gcv);

    /* Create a 3-column array of color tiles */
    rc = XtVaCreateWidget ("rc", xmRowColumnWidgetClass, main_w,
        XmNnumColumns,      3,
        XmNpacking,         XmPACK_COLUMN,
        XmNleftAttachment,  XmATTACH_FORM,
        XmNtopAttachment,   XmATTACH_FORM,
        NULL);
    for (i = 0; i < XtNumber(colors); i++) {
        /* Create a single tile (pixmap) for each color */
        pixmap = XCreatePixmap (XtDisplay (rc), 
            RootWindowOfScreen (XtScreen (rc)),
            16, 16, DefaultDepthOfScreen (XtScreen (rc)));
        set_color (rc, colors[i], NULL); /* set the gc's color according to name */
        //set_color (rc, colors[i]); /* set the gc's color according to name */
        XFillRectangle (XtDisplay (main_w), pixmap, gc, 0, 0, 16, 16);
        pb = XtVaCreateManagedWidget (colors[i], xmPushButtonWidgetClass, rc,
            XmNlabelType, XmPIXMAP,
            XmNlabelPixmap, pixmap,
            NULL);
        /* callback for this pushbutton sets the current color */
        XtAddCallback (pb, XmNactivateCallback, set_color, colors[i]);
    }
    XtManageChild (rc);

    pb = XtVaCreateManagedWidget ("Quit",
        xmPushButtonGadgetClass, main_w,
        XmNleftAttachment,    XmATTACH_FORM,
        XmNtopAttachment,     XmATTACH_WIDGET,
        XmNtopWidget,         rc,
        NULL);
    /* XtAddCallback (pb, XmNactivateCallback, exit, NULL); */
    XtAddCallback (pb, XmNactivateCallback, Exit, NULL); 

    /* Clear button -- wait till DrawingArea is created so we can use
     * it to pass as client data.
     */
    pb = XtVaCreateManagedWidget ("Clear",
        xmPushButtonGadgetClass, main_w,
        XmNleftAttachment,    XmATTACH_WIDGET,
        XmNleftWidget,        pb,
        XmNtopAttachment,     XmATTACH_WIDGET,
        XmNtopWidget,         rc,
        NULL);

    sw = XtVaCreateManagedWidget ("scrolled_win",
        xmScrolledWindowWidgetClass, main_w,
        XmNwidth,                  300,
        XmNscrollingPolicy,        XmAUTOMATIC,
        XmNscrollBarDisplayPolicy, XmAS_NEEDED,
        XmNtopAttachment,          XmATTACH_FORM,
        XmNbottomAttachment,       XmATTACH_FORM,
        XmNleftAttachment,         XmATTACH_WIDGET,
        XmNleftWidget,             rc,
        XmNrightAttachment,        XmATTACH_FORM,
        NULL);

    /* Add the "draw" action/function used by the translation table
     * parsed by the translations resource below.
     */
    actions.string = "draw";
    actions.proc = draw;
    XtAppAddActions (app, &actions, 1);

    /* Create a DrawingArea widget.  Make it 5 inches wide by 6 inches tall.
     * Don't let it resize so the Clear Button doesn't force a resize.
     */
    drawing_a = XtVaCreateManagedWidget ("drawing_a",
        xmDrawingAreaWidgetClass, sw,
        XmNtranslations, XtParseTranslationTable (translations),
        XmNunitType,     Xm1000TH_INCHES,
        XmNwidth,        5000, /* 5 inches */
        XmNheight,       6000, /* 6 inches */
        XmNresizePolicy, XmNONE,  /* remain this a fixed size */
        NULL);
    /* When scrolled, the drawing area will get expose events */
    XtAddCallback (drawing_a, XmNexposeCallback, redraw, NULL);
    /* Pushing the clear button clears the drawing area widget */
    XtAddCallback (pb, XmNactivateCallback, clear_it, drawing_a);

    /* convert drawing area back to pixels to get its width and height */
    XtVaSetValues (drawing_a, XmNunitType, XmPIXELS, NULL);
    XtVaGetValues (drawing_a, XmNwidth, &width, XmNheight, &height, NULL);
    /* 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 */
    set_color (drawing_a, "White", NULL);
    //set_color (drawing_a, "White");
    XFillRectangle (XtDisplay (drawing_a), pixmap, gc, 0, 0, width, height);

    XtRealizeWidget (toplevel);
    XtAppMainLoop (app);
}

/* Action procedure to respond to any of the events from the
 * translation table declared in main().  This function is called
 * in response to Button1 Down, Up and Motion events.  Basically,
 * we're just doing a freehand draw -- not lines or anything.
 */
void
draw(widget, event, args, num_args)
Widget widget;
XEvent *event;
String *args;
int *num_args;
{
    static Position x, y;
    XButtonEvent *bevent = (XButtonEvent *) event;

    if (*num_args != 1)
        XtError ("Wrong number of args!");

    if (strcmp (args[0], "down")) {
        /* if it's not "down", it must either be "up" or "motion"
         * draw full line from anchor point to new point.
         */
        XDrawLine (bevent->display, bevent->window, gc, x, y, 
            bevent->x, bevent->y);
        XDrawLine (bevent->display, pixmap, gc, x, y, bevent->x, bevent->y);
    }

    /* freehand is really a bunch of line segements; save this point */
    x = bevent->x;
    y = bevent->y;
}

/* Clear the window by clearing the pixmap and calling XCopyArea() */
void
clear_it(pb, client_data, call_data)
Widget pb;
XtPointer client_data;
XtPointer call_data;
{
    Widget drawing_a = (Widget) client_data;
    XmPushButtonCallbackStruct *cbs = 
        (XmPushButtonCallbackStruct *) call_data;
    
    /* clear pixmap with white */
    XSetForeground (XtDisplay (drawing_a), gc,
        WhitePixelOfScreen (XtScreen (drawing_a)));
    /* this clears the pixmap */
    XFillRectangle (XtDisplay (drawing_a), pixmap, gc, 0, 0, width, height);
    /* drawing is now done using black; change the gc */
    XSetForeground (XtDisplay (drawing_a), gc,
        BlackPixelOfScreen (XtScreen (drawing_a)));
    /* render the newly cleared pixmap onto the window */
    XCopyArea (cbs->event->xbutton.display, pixmap, XtWindow (drawing_a), gc,
        0, 0, width, height, 0, 0);
}

/* redraw is called whenever all or portions of the drawing area is
 * exposed.  This includes newly exposed portions of the widget resulting
 * from the user's interaction with the scrollbars.
 */
void
redraw(drawing_a, client_data, call_data)
Widget    drawing_a;
XtPointer client_data;
XtPointer call_data;
{
    XmDrawingAreaCallbackStruct *cbs = 
        (XmDrawingAreaCallbackStruct *) call_data;
    
    XCopyArea (cbs->event->xexpose.display, pixmap, cbs->window, gc,
        0, 0, width, height, 0, 0);
}

/* callback routine for when any of the color tiles are pressed.
 * This general function may also be used to set the global gc's
 * color directly.  Just provide a widget and a color name.
 */
void
set_color(widget, client_data, call_data)
 Widget widget;
XtPointer client_data;
XtPointer call_data;
{
    String color = (String) client_data;
    Display *dpy = XtDisplay (widget);
    Colormap cmap = DefaultColormapOfScreen (XtScreen (widget));
    XColor col, unused;

    if (!XAllocNamedColor (dpy, cmap, color, &col, &unused)) {
        char buf[32];
        sprintf (buf, "Can't alloc %s", color);
        XtWarning (buf);
        return;
    }
    XSetForeground (dpy, gc, col.pixel);
}