Appendix E. The xbitmap Application

This appendix shows the complete code for the BitmapEdit widget and a relatively complex version of xbitmap, which is described in "Chapter 4, An Example Application."

This appendix shows the complete code for the BitmapEdit widget (without selections), and for an advanced version of the xbitmap application (xbitmap5 in the example distribution) which is similar to xbitmap3 described in Chapter 4 except that it both reads and writes X11 bitmap files (xbitmap3 was capable of writing them only).

All source code from this book is available free from numerous sources, as described in the Preface.

The BitmapEdit Widget

Example E-1. BitmapEdit: complete widget code

/*
 * BitmapEdit.c - bitmap editor widget.
 */
#include <Xm/XmP.h>

#include <X11/StringDefs.h>


#include <stdio.h>


#include "BitmapEdiP.h"


#define INTERNAL_WIDTH    2
#define INTERNAL_HEIGHT 4

#define DEFAULT_PIXMAP_WIDTH    32  /* in cells */
#define DEFAULT_PIXMAP_HEIGHT   32  /* in cells */

#define DEFAULT_CELL_SIZE   30  /* in pixels */

/* values for instance variable is_drawn */
#define DRAWN 1
#define UNDRAWN 0

/* modes for drawing */
#define DRAW 1
#define UNDRAW 0

#define MAXLINES 1000   /* max of horiz or vertical cells */
#define SCROLLBARWIDTH 15

#define DEFAULTWIDTH 300  /* widget size when show_all is False */
#define offset(field) XtOffsetOf(BitmapEditRec, field)

static XtResource resources[] = {
/* not needed in Motif - already defined by Primitive.
 *    {
 *   XtNforeground,
 *   XtCForeground,
 *   XtRPixel,
 *   sizeof(Pixel),
 *   offset(bitmapEdit.foreground),
 *   XtRString,
 *   XtDefaultForeground
 *    },
 */
     {
    XtNtoggleCallback,
    XtCToggleCallback,
    XtRCallback,
    sizeof(XtPointer),
    offset(bitmapEdit.callback),
    XtRCallback,
    NULL
     },
     {
    XtNcellSizeInPixels,
    XtCCellSizeInPixels,
    XtRInt, sizeof(int),
    offset(bitmapEdit.cell_size_in_pixels),
    XtRImmediate,
    (XtPointer)DEFAULT_CELL_SIZE
     },
     {
    XtNpixmapWidthInCells,
    XtCPixmapWidthInCells,
    XtRDimension,
    sizeof(Dimension),
    offset(bitmapEdit.pixmap_width_in_cells),
    XtRImmediate,
    (XtPointer)DEFAULT_PIXMAP_WIDTH
     },
     {
    XtNpixmapHeightInCells,
    XtCPixmapHeightInCells,
    XtRDimension,
    sizeof(Dimension),
    offset(bitmapEdit.pixmap_height_in_cells),
    XtRImmediate,
    (XtPointer)DEFAULT_PIXMAP_HEIGHT
     },
     {
    XtNcurX,
    XtCCurX,
    XtRInt,
    sizeof(int),
    offset(bitmapEdit.cur_x),
    XtRImmediate,
    (XtPointer) 0
     },
     {
    XtNcurY,
    XtCCurY,
    XtRInt,
    sizeof(int),
    offset(bitmapEdit.cur_y),
    XtRString,
    (XtPointer) NULL
     },
     {
    XtNcellArray,
    XtCCellArray,
    XtRString,
    sizeof(String),
    offset(bitmapEdit.cell),
    XtRImmediate,
    (XtPointer) 0
     },
     {
    XtNshowEntireBitmap,
    XtCShowEntireBitmap,
    XtRBoolean,
    sizeof(Boolean),
    offset(bitmapEdit.show_all),
    XtRImmediate,
    (XtPointer) True
     },
};
/* Declaration of methods */
static void Initialize();
static void Redisplay();
static void Destroy();
static void Resize();
static Boolean SetValues();
static XtGeometryResult QueryGeometry();
/* these Core methods not needed by BitmapEdit:
 *
 * static void ClassInitialize();
 * static void Realize();
 */
/* the following are private functions unique to BitmapEdit */
static void DrawPixmaps(), DoCell(), ChangeCellSize();
/* the following are actions of BitmapEdit */
static void DrawCell(), UndrawCell(), ToggleCell();
/* The following are public functions of BitmapEdit, declared extern
 * in the public include file: */
char *BitmapEditGetArray();
static char defaultTranslations[] =
    "<Btn1Down>:    DrawCell()             \n\
    <Btn2Down>:    UndrawCell()           \n\
    <Btn3Down>:    ToggleCell()            
    <Btn1Motion>:  DrawCell()              
    <Btn2Motion>:  UndrawCell()            
    <Btn3Motion>:  ToggleCell()";
static XtActionsRec actions[] = {
        {"DrawCell", DrawCell},
        {"UndrawCell", UndrawCell},
        {"ToggleCell", ToggleCell},
};
/* definition in BitmapEdit.h */
static BitmapEditPointInfo info;
BitmapEditClassRec bitmapEditClassRec = {
    {
    /* core_class fields */
    /* superclass            */ (WidgetClass) &xmPrimitiveClassRec,
    /* class_name            */ "BitmapEdit",
    /* widget_size           */ sizeof(BitmapEditRec),
    /* class_initialize      */ NULL,
    /* class_part_initialize */ NULL,
    /* class_inited          */ False,
    /* initialize            */ Initialize,
    /* initialize_hook       */ NULL,
    /* realize               */ XtInheritRealize,
    /* actions               */ actions,
    /* num_actions           */ XtNumber(actions),
    /* resources             */ resources,
    /* num_resources         */ XtNumber(resources),
    /* xrm_class             */ NULLQUARK,
    /* compress_motion       */ True,
    /* compress_exposure     */ XtExposeCompressMultiple,
    /* compress_enterleave   */ True,
    /* visible_interest      */ False,
    /* destroy               */ Destroy,
    /* resize                */ Resize,
    /* expose                */ Redisplay,
    /* set_values            */ SetValues,
    /* set_values_hook       */ NULL,
    /* set_values_almost     */ XtInheritSetValuesAlmost,
    /* get_values_hook       */ NULL,
    /* accept_focus          */ NULL,
    /* version               */ XtVersion,
    /* callback_private      */ NULL,
    /* tm_table              */ defaultTranslations,
    /* query_geometry        */ QueryGeometry,
    /* display_accelerator   */ XtInheritDisplayAccelerator,
    /* extension             */ NULL
    },
    {  /* Primitive class fields */
    /* border_highlight   */        _XtInherit,
    /* border_unhighlight */        _XtInherit,
    /* translations       */        XtInheritTranslations,
    /* arm_and_activate   */        NULL,
    /* syn resources      */        NULL,
    /* num_syn_resources  */        0,
    /* extension          */        NULL,
    },
    {
    /* extension          */        0,
    },
};
WidgetClass bitmapEditWidgetClass = (WidgetClass) & bitmapEditClassRec;
static void
GetDrawGC(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    XGCValues values;
    XtGCMask mask = GCForeground | GCBackground | GCDashOffset |
            GCDashList | GCLineStyle;
    /*
     * Setting foreground and background to 1 and 0 looks like a
     * kludge but isn't.  This GC is used for drawing
     * into a pixmap of depth one.  Real colors are applied with a
     * separate GC when the pixmap is copied into the window.
     */
    values.foreground = 1;
    values.background = 0;
    values.dashes = 1;
    values.dash_offset = 0;
    values.line_style = LineOnOffDash;
    cw->bitmapEdit.draw_gc = XCreateGC(XtDisplay(cw),
             cw->bitmapEdit.big_picture, mask, &values);
}
static void
GetUndrawGC(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    XGCValues values;
    XtGCMask mask = GCForeground | GCBackground;
    /* this looks like a kludge but isn't.  This GC is used for drawing
     * into a pixmap of depth one.  Real colors are applied as the
     * pixmap is copied into the window.
     */
    values.foreground = 0;
    values.background = 1;
    cw->bitmapEdit.undraw_gc = XCreateGC(XtDisplay(cw),
              cw->bitmapEdit.big_picture, mask, &values);
}
static void
GetCopyGC(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    XGCValues values;
    XtGCMask mask = GCForeground | GCBackground;
    values.foreground = cw->primitive.foreground;
    values.background = cw->core.background_pixel;
    cw->bitmapEdit.copy_gc = XtGetGC(cw, mask, &values);
}
/* ARGSUSED */
static void
Initialize(treq, tnew, args, num_args)
Widget treq, tnew;
ArgList args;
Cardinal *num_args;
{
    BitmapEditWidget new = (BitmapEditWidget) tnew;
    new->bitmapEdit.cur_x = 0;
    new->bitmapEdit.cur_y = 0;
    /*
     *  Check instance values set by resources that may be invalid.
     */
    if ((new->bitmapEdit.pixmap_width_in_cells < 1) ||
            (new->bitmapEdit.pixmap_height_in_cells < 1))  {
        XtWarning("BitmapEdit: pixmapWidth and/or pixmapHeight is too\
                small (using 10 x 10).");
        new->bitmapEdit.pixmap_width_in_cells = 10;
        new->bitmapEdit.pixmap_height_in_cells = 10;
    }
    if (new->bitmapEdit.cell_size_in_pixels < 5) {
        XtWarning("BitmapEdit: cellSize is too small (using 5).");
        new->bitmapEdit.cell_size_in_pixels = 5;
    }
    if ((new->bitmapEdit.cur_x < 0) ||  (new->bitmapEdit.cur_y < 0)) {
        XtWarning("BitmapEdit: cur_x and cur_y must be non-negative\
                (using 0, 0).");
        new->bitmapEdit.cur_x = 0;
        new->bitmapEdit.cur_y = 0;
    }
    if (new->bitmapEdit.cell == NULL)
        new->bitmapEdit.cell = XtCalloc(
                new->bitmapEdit.pixmap_width_in_cells *
                new->bitmapEdit.pixmap_height_in_cells, sizeof(char));
    else
        new->bitmapEdit.user_allocated = True;/* user supplied cell array */
    new->bitmapEdit.pixmap_width_in_pixels =
           new->bitmapEdit.pixmap_width_in_cells *
           new->bitmapEdit.cell_size_in_pixels;
    new->bitmapEdit.pixmap_height_in_pixels =
           new->bitmapEdit.pixmap_height_in_cells *
           new->bitmapEdit.cell_size_in_pixels;
    /*
     * Motif Primitive sets width and height to provide enough room for
     * the highlight and shadow around a widget.  BitmapEdit
     * doesn't use these features.  A widget that did use these
     * features would *add* its desired dimensions to those set
     * by Primitive.  To use this widget with another widget set, remove
     * the following two lines.
     */
    new->core.width = 0;
    new->core.height = 0;
    if (new->core.width == 0) {
        if (new->bitmapEdit.show_all == False)
            new->core.width = (new->bitmapEdit.pixmap_width_in_pixels
                    > DEFAULTWIDTH) ? DEFAULTWIDTH :
                    (new->bitmapEdit.pixmap_width_in_pixels);
        else
            new->core.width = new->bitmapEdit.pixmap_width_in_pixels;
    }
    if (new->core.height == 0) {
        if (new->bitmapEdit.show_all == False)
            new->core.height =
                    (new->bitmapEdit.pixmap_height_in_pixels >
                    DEFAULTWIDTH) ? DEFAULTWIDTH :
                    (new->bitmapEdit.pixmap_height_in_pixels);
        else
            new->core.height = new->bitmapEdit.pixmap_height_in_pixels;
    }
    /* tell Primitive not to allow tabbing to this widget */
    XtVaSetValues(new,
            XmNtraversalOn, False,
            NULL);
    CreateBigPixmap(new);
    GetDrawGC(new);
    GetUndrawGC(new);
    GetCopyGC(new);
    DrawIntoBigPixmap(new);
}
/* ARGSUSED */
static void
Redisplay(w, event)
Widget w;
XExposeEvent *event;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    register int x, y;
    unsigned int width, height;
    if (!XtIsRealized(cw))
        return;
    if (event) {  /* called from btn-event or expose */
        x = event->x;
        y = event->y;
        width = event->width;
        height =  event->height;
    }
    else {        /* called because complete redraw */
        x = 0;
        y = 0;
        width = cw->bitmapEdit.pixmap_width_in_pixels;
        height = cw->bitmapEdit.pixmap_height_in_pixels;
    }
    if (DefaultDepthOfScreen(XtScreen(cw)) == 1)
        XCopyArea(XtDisplay(cw), cw->bitmapEdit.big_picture,
                XtWindow(cw), cw->bitmapEdit.copy_gc, x +
                cw->bitmapEdit.cur_x, y + cw->bitmapEdit.cur_y,
                width, height, x, y);
    else
        XCopyPlane(XtDisplay(cw), cw->bitmapEdit.big_picture,
                XtWindow(cw), cw->bitmapEdit.copy_gc, x +
                cw->bitmapEdit.cur_x, y + cw->bitmapEdit.cur_y,
                width, height, x, y, 1);
}
/* ARGSUSED */
static Boolean
SetValues(current, request, new, args, num_args)
Widget current, request, new;
ArgList args;
Cardinal *num_args;
{
    BitmapEditWidget curcw = (BitmapEditWidget) current;
    BitmapEditWidget newcw = (BitmapEditWidget) new;
    Boolean do_redisplay = False;
    if (curcw->primitive.foreground != newcw->primitive.foreground) {
        XtReleaseGC(curcw, curcw->bitmapEdit.copy_gc);
        GetCopyGC(newcw);
        do_redisplay = True;
    }
    if ((curcw->bitmapEdit.cur_x != newcw->bitmapEdit.cur_x) ||
            (curcw->bitmapEdit.cur_y != newcw->bitmapEdit.cur_y))
        do_redisplay = True;
    if (curcw->bitmapEdit.cell_size_in_pixels !=
            newcw->bitmapEdit.cell_size_in_pixels) {
        ChangeCellSize(curcw, newcw->bitmapEdit.cell_size_in_pixels);
        do_redisplay = True;
    }
    if (curcw->bitmapEdit.pixmap_width_in_cells !=
            newcw->bitmapEdit.pixmap_width_in_cells)  {
        newcw->bitmapEdit.pixmap_width_in_cells =
                curcw->bitmapEdit.pixmap_width_in_cells;
        XtWarning("BitmapEdit: pixmap_width_in_cells cannot be set\
                by XtSetValues.);
    }
    if (curcw->bitmapEdit.pixmap_height_in_cells !=
            newcw->bitmapEdit.pixmap_height_in_cells) {
        newcw->bitmapEdit.pixmap_height_in_cells =
                curcw->bitmapEdit.pixmap_height_in_cells;
        XtWarning("BitmapEdit: pixmap_height_in_cells cannot be set\
                by XtSetValues.);
    }
    return do_redisplay;
}
static void
Destroy(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    if (cw->bitmapEdit.big_picture)
        XFreePixmap(XtDisplay(cw), cw->bitmapEdit.big_picture);
    if (cw->bitmapEdit.draw_gc)
        XFreeGC(XtDisplay(cw), cw->bitmapEdit.draw_gc);
    if (cw->bitmapEdit.undraw_gc)
        XFreeGC(XtDisplay(cw), cw->bitmapEdit.undraw_gc);
    if (cw->bitmapEdit.copy_gc)
        XFreeGC(XtDisplay(cw), cw->bitmapEdit.copy_gc);
    /* Free memory allocated with Calloc.  This was done
     * only if application didn't supply cell array.
     */
    if (!cw->bitmapEdit.user_allocated)
        XtFree(cw->bitmapEdit.cell);
}
static void
DrawCell(w, event)
Widget w;
XEvent *event;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    DrawPixmaps(cw->bitmapEdit.draw_gc, DRAW, cw, event);
}
static void
UndrawCell(w, event)
Widget w;
XEvent *event;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    DrawPixmaps(cw->bitmapEdit.undraw_gc, UNDRAW, cw, event);
}
static void
ToggleCell(w, event)
Widget w;
XEvent *event;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    static int oldx = -1, oldy = -1;
    GC gc;
    int mode;
    int newx, newy;
    /* This is strictly correct, but doesn't
     * seem to be necessary */
    if (event->type == ButtonPress) {
        newx = (cw->bitmapEdit.cur_x + ((XButtonEvent *)event)->x) /
        cw->bitmapEdit.cell_size_in_pixels;
        newy = (cw->bitmapEdit.cur_y + ((XButtonEvent *)event)->y) /
        cw->bitmapEdit.cell_size_in_pixels;
    }
    else  {
        newx = (cw->bitmapEdit.cur_x + ((XMotionEvent *)event)->x) /
        cw->bitmapEdit.cell_size_in_pixels;
        newy = (cw->bitmapEdit.cur_y + ((XMotionEvent *)event)->y) /
        cw->bitmapEdit.cell_size_in_pixels;
    }
    if ((mode = cw->bitmapEdit.cell[newx + newy *
            cw->bitmapEdit.pixmap_width_in_cells]) == DRAWN) {
        gc = cw->bitmapEdit.undraw_gc;
        mode = UNDRAW;
    }
    else {
        gc = cw->bitmapEdit.draw_gc;
        mode = DRAW;
    }
    if (oldx != newx || oldy != newy) {
        oldx = newx;
        oldy = newy;
        DrawPixmaps(gc, mode, cw, event);
    }
}
static void
DrawPixmaps(gc, mode, w, event)
GC gc;
int mode;
Widget w;
XButtonEvent *event;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    int newx = (cw->bitmapEdit.cur_x + event->x) /
             cw->bitmapEdit.cell_size_in_pixels;
    int newy = (cw->bitmapEdit.cur_y + event->y) /
             cw->bitmapEdit.cell_size_in_pixels;
    XExposeEvent fake_event;
    /* if already done, return */
    if (cw->bitmapEdit.cell[newx + newy *
            cw->bitmapEdit.pixmap_width_in_cells] == mode)
        return;
    /* otherwise, draw or undraw */
    XFillRectangle(XtDisplay(cw), cw->bitmapEdit.big_picture, gc,
            cw->bitmapEdit.cell_size_in_pixels*newx + 2,
            cw->bitmapEdit.cell_size_in_pixels*newy + 2,
            (unsigned int)cw->bitmapEdit.cell_size_in_pixels - 3,
            (unsigned int)cw->bitmapEdit.cell_size_in_pixels - 3);
    cw->bitmapEdit.cell[newx + newy *
            cw->bitmapEdit.pixmap_width_in_cells] = mode;
    info.mode = mode;
    info.newx = newx;
    info.newy = newy;
    fake_event.x = cw->bitmapEdit.cell_size_in_pixels * newx -
            cw->bitmapEdit.cur_x;
    fake_event.y = cw->bitmapEdit.cell_size_in_pixels * newy -
            cw->bitmapEdit.cur_y;
    fake_event.width = cw->bitmapEdit.cell_size_in_pixels;
    fake_event.height = cw->bitmapEdit.cell_size_in_pixels;
    Redisplay(cw, &fake_event);
    XtCallCallbacks(cw, XtNtoggleCallback, &info);
}
CreateBigPixmap(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    /* always a 1 bit deep pixmap, regardless of screen depth */
    cw->bitmapEdit.big_picture = XCreatePixmap(XtDisplay(cw),
            RootWindow(XtDisplay(cw), DefaultScreen(XtDisplay(cw))),
            cw->bitmapEdit.pixmap_width_in_pixels + 2,
            cw->bitmapEdit.pixmap_height_in_pixels + 2, 1);
}
DrawIntoBigPixmap(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    int n_horiz_segments, n_vert_segments;
    XSegment segment[MAXLINES];
    register int x, y;
    XFillRectangle(XtDisplay(cw), cw->bitmapEdit.big_picture,
            cw->bitmapEdit.undraw_gc, 0, 0,
            cw->bitmapEdit.pixmap_width_in_pixels
            + 2, cw->bitmapEdit.pixmap_height_in_pixels + 2);
    n_horiz_segments = cw->bitmapEdit.pixmap_height_in_cells + 1;
    n_vert_segments = cw->bitmapEdit.pixmap_width_in_cells + 1;
    for (x = 0; x < n_horiz_segments; x++) {
        segment[x].x1 = 0;
        segment[x].x2 = (short) cw->bitmapEdit.pixmap_width_in_pixels;
        segment[x].y1 = (short) cw->bitmapEdit.cell_size_in_pixels * x;
        segment[x].y2 = (short) cw->bitmapEdit.cell_size_in_pixels * x;
    }
    XDrawSegments(XtDisplay(cw), cw->bitmapEdit.big_picture,
            cw->bitmapEdit.draw_gc, segment, n_horiz_segments);
    for (y = 0; y < n_vert_segments; y++) {
        segment[y].x1 = (short) y * cw->bitmapEdit.cell_size_in_pixels;
        segment[y].x2 = (short) y * cw->bitmapEdit.cell_size_in_pixels;
        segment[y].y1 = 0;
        segment[y].y2 = (short) cw->bitmapEdit.pixmap_height_in_pixels;
    }
    XDrawSegments(XtDisplay(cw), cw->bitmapEdit.big_picture,
            cw->bitmapEdit.draw_gc, segment, n_vert_segments);
    /* draw current cell array into pixmap */
    for (x = 0; x < cw->bitmapEdit.pixmap_width_in_cells; x++) {
        for (y = 0; y < cw->bitmapEdit.pixmap_height_in_cells; y++) {
            if (cw->bitmapEdit.cell[x + (y *
                    cw->bitmapEdit.pixmap_width_in_cells)] == DRAWN)
                DoCell(cw, x, y, cw->bitmapEdit.draw_gc);
            else
                DoCell(cw, x, y, cw->bitmapEdit.undraw_gc);
        }
    }
}
/* A Public function, not static */
char *
BitmapEditGetArray(w, width_in_cells, height_in_cells)
Widget w;
int *width_in_cells, *height_in_cells;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    *width_in_cells = cw->bitmapEdit.pixmap_width_in_cells;
    *height_in_cells = cw->bitmapEdit.pixmap_height_in_cells;
    return (cw->bitmapEdit.cell);
}
/* ARGSUSED */
static void
Resize(w)
Widget w;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    /* resize does nothing unless new size is bigger than entire pixmap */
    if ((cw->core.width > cw->bitmapEdit.pixmap_width_in_pixels) &&
            (cw->core.height > cw->bitmapEdit.pixmap_height_in_pixels)) {
        /*
         * Calculate the maximum cell size that will allow the
         * entire bitmap to be displayed.
         */
        Dimension w_temp_cell_size_in_pixels, h_temp_cell_size_in_pixels;
        Dimension new_cell_size_in_pixels;
        w_temp_cell_size_in_pixels =
                cw->core.width / cw->bitmapEdit.pixmap_width_in_cells;
        h_temp_cell_size_in_pixels =
                cw->core.height / cw->bitmapEdit.pixmap_height_in_cells;
        if (w_temp_cell_size_in_pixels < h_temp_cell_size_in_pixels)
            new_cell_size_in_pixels = w_temp_cell_size_in_pixels;
        else
            new_cell_size_in_pixels = h_temp_cell_size_in_pixels;
        /* if size change mandates a new pixmap, make one */
        if (new_cell_size_in_pixels != cw->bitmapEdit.cell_size_in_pixels)
            ChangeCellSize(cw, new_cell_size_in_pixels);
    }
}
static void
ChangeCellSize(w, new_cell_size)
Widget w;
int new_cell_size;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    int x, y;
    cw->bitmapEdit.cell_size_in_pixels = new_cell_size;
    /* recalculate variables based on cell size */
    cw->bitmapEdit.pixmap_width_in_pixels =
             cw->bitmapEdit.pixmap_width_in_cells *
             cw->bitmapEdit.cell_size_in_pixels;
    cw->bitmapEdit.pixmap_height_in_pixels =
             cw->bitmapEdit.pixmap_height_in_cells *
             cw->bitmapEdit.cell_size_in_pixels;
    /* destroy old and create new pixmap of correct size */
    XFreePixmap(XtDisplay(cw), cw->bitmapEdit.big_picture);
    CreateBigPixmap(cw);
    /* draw lines into new pixmap */
    DrawIntoBigPixmap(cw);
    /* draw current cell array into pixmap */
    for (x = 0; x < cw->bitmapEdit.pixmap_width_in_cells; x++) {
        for (y = 0; y < cw->bitmapEdit.pixmap_height_in_cells; y++) {
            if (cw->bitmapEdit.cell[x + (y *
                    ppcw->bitmapEdit.pixmap_width_in_cells)] == DRAWN)
                DoCell(cw, x, y, cw->bitmapEdit.draw_gc);
            else
                DoCell(cw, x, y, cw->bitmapEdit.undraw_gc);
        }
    }
}
static void
DoCell(w, x, y, gc)
Widget w;
int x, y;
GC gc;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
        /* otherwise, draw or undraw */
    XFillRectangle(XtDisplay(cw), cw->bitmapEdit.big_picture, gc,
             cw->bitmapEdit.cell_size_in_pixels * x + 2,
             cw->bitmapEdit.cell_size_in_pixels * y + 2,
             (unsigned int)cw->bitmapEdit.cell_size_in_pixels - 3,
             (unsigned int)cw->bitmapEdit.cell_size_in_pixels - 3);
}
static XtGeometryResult QueryGeometry(w, proposed, answer)
Widget w;
XtWidgetGeometry *proposed, *answer;
{
    BitmapEditWidget cw = (BitmapEditWidget) w;
    /* set fields we care about */
    answer->request_mode = CWWidth | CWHeight;
    /* initial width and height */
    if (cw->bitmapEdit.show_all == True)
        answer->width = cw->bitmapEdit.pixmap_width_in_pixels;
    else
        answer->width = (cw->bitmapEdit.pixmap_width_in_pixels >
                DEFAULTWIDTH) ? DEFAULTWIDTH :
                cw->bitmapEdit.pixmap_width_in_pixels;
    if (cw->bitmapEdit.show_all == True)
        answer->height = cw->bitmapEdit.pixmap_height_in_pixels;
    else
        answer->height = (cw->bitmapEdit.pixmap_height_in_pixels >
                DEFAULTWIDTH) ? DEFAULTWIDTH :
                cw->bitmapEdit.pixmap_height_in_pixels;
    if (  ((proposed->request_mode & (CWWidth | CWHeight))
            == (CWWidth | CWHeight)) &&
            proposed->width == answer->width &&
            proposed->height == answer->height)
        return XtGeometryYes;
    else if (answer->width == cw->core.width &&
            answer->height == cw->core.height)
        return XtGeometryNo;
    else
        return XtGeometryAlmost;
}


The BitmapEdiP.h Private Header File

Example E-2. BitmapEdiP.h: complete private header file

/*
 * BitmapEditP.h - Private definitions for BitmapEdit widget
 */
#ifndef _ORABitmapEditP_h
#define _ORABitmapEditP_h
/*
 * Include private header file of superclass.
 */
#include <Xm/PrimitiveP.h>
/*
 * Include public header file for this widget.
 */
#include "BitmapEdit.h"
/* New fields for the BitmapEdit widget class record */
typedef struct {
    int make_compiler_happy;    /* keep compiler happy */
} BitmapEditClassPart;
/* Full class record declaration */
typedef struct _BitmapEditClassRec {
    CoreClassPart    core_class;
    XmPrimitiveClassPart   primitive_class;
    BitmapEditClassPart    bitmapEdit_class;
} BitmapEditClassRec;
extern BitmapEditClassRec bitmapEditClassRec;
/* New fields for the BitmapEdit widget record */
typedef struct {
    /* resources */
    Pixel    foreground;
    XtCallbackList callback;/* application installed callback function(s) */
    Dimension    pixmap_width_in_cells;
    Dimension     pixmap_height_in_cells;
    int cell_size_in_pixels;
    int cur_x, cur_y;       /* position of visible corner in big pixmap */
    char *cell;             /* array for keeping track of array of bits */
    Boolean show_all;       /* whether bitmap should display entire bitmap */
    /* private state */
    Dimension    pixmap_width_in_pixels;
    Dimension    pixmap_height_in_pixels;
    Pixmap big_picture;
    GC        draw_gc;      /* one plane, for drawing into pixmap */
    GC        undraw_gc;    /* one plane, for drawing into pixmap */
    GC        copy_gc; /* defaultdepthofscreen, for copy'g pixmap into window */
    Boolean user_allocated; /* whether user allocated cell array */
} BitmapEditPart;
/*
 * Full instance record declaration
 */
typedef struct _BitmapEditRec {
    CorePart            core;
    XmPrimitivePart        primitive;
    BitmapEditPart        bitmapEdit;
} BitmapEditRec;
#endif /* _ORABitmapEditP_h */


The BitmapEdit.h Public Header File

Example E-3. BitmapEdit.h: complete public header file

#ifndef _ORABitmapEdit_h
#define _ORABitmapEdit_h
/* BitmapEdit Widget public include file */
/*
 * The public header file for the immediate superclass normally
 * must be included.  However, not in this case because the public
 * header file for Primitive is in Xm.h, which is already included
 * in all Motif applications.
 */
/* #include <Xm/Superclass.h>  */
/*
 * This public structure is used as call_data to the callback.
 * It passes the x, y position of the cell toggled (in units of
 * cells, not pixels) and a mode flag that indicates whether the
 * cell was turned on (1) or off (0).
 */
typedef struct {
    int mode;
    int newx;
    int newy;
} BitmapEditPointInfo;
#define XtNtoggleCallback "toggleCallback"
#define XtNcellSizeInPixels "cellSizeInPixels"
#define XtNpixmapWidthInCells "pixmapWidthInCells"
#define XtNpixmapHeightInCells "pixmapHeightInCells"
#define XtNcurX "curX"
#define XtNcurY "curY"
#define XtNcellArray "cellArray"
#define XtNshowEntireBitmap "showEntireBitmap"
#define XtCToggleCallback "ToggleCallback"
#define XtCCellSizeInPixels "CellSizeInPixels"
#define XtCPixmapWidthInCells "PixmapWidthInCells"
#define XtCPixmapHeightInCells "PixmapHeightInCells"
#define XtCCurX "CurX"
#define XtCCurY "CurY"
#define XtCCellArray "CellArray"
#define XtCShowEntireBitmap "ShowEntireBitmap"
extern char *BitmapEditGetArray(); /* w */
    /* Widget w; */
/* Class record constants */
extern WidgetClass bitmapEditWidgetClass;
typedef struct _BitmapEditClassRec *BitmapEditWidgetClass;
typedef struct _BitmapEditRec      *BitmapEditWidget;
#endif /* _ORABitmapEdit_h */
/* DON'T ADD STUFF AFTER THIS #endif */


xbitmap5

Example E-4. xbitmap5: complete application code

/*
 * Copyright 1989 O'Reilly and Associates, Inc.
 * See ../Copyright for complete rights and liability information.
 */
/*
 *  xbitmap5.c - bitmap in main window with small pixmaps
 */
/*
 *  So that we can use fprintf:
 */
#include <stdio.h>
/*
 * Standard Toolkit include files:
 */
#include <X11/Intrinsic.h>  
#include <Xm/Xm.h>  
/*
 * Public include files for widgets used in this file.
 */
#include <Xm/PanedW.h>    /* paned window */
#include <Xm/PushB.h>     /* push button */
#include <Xm/MessageB.h>  /* message box */
#include <Xm/CascadeB.h>  /* cascade button */
#include <Xm/RowColumn.h> /* row column (for menus) */
#include <Xm/DrawingA.h>  /* drawing area */
#include <Xm/Form.h>      /* pixmap box */
#include <Xm/Frame.h>     /* frame */
#include <Xm/ScrolledW.h> /* scrolled window */
#include "BitmapEdit.h"

#define DRAWN 1
#define UNDRAWN 0

struct {
    GC draw_gc, undraw_gc, invert_gc;
    Pixmap normal_bitmap, reverse_bitmap;
    Widget showNormalBitmap, showReverseBitmap;
    String filename;    /* filename to read and write */
    Dimension pixmap_width_in_cells, pixmap_height_in_cells;
} bitmap_stuff;

static Boolean file_contained_good_data = False;

static void CellToggled(), SetUpThings();

/*ARGSUSED*/
static void
RedrawSmallPicture(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
    Pixmap pixmap;
    if (w == bitmap_stuff.showNormalBitmap)
        pixmap = bitmap_stuff.normal_bitmap;
    else
        pixmap = bitmap_stuff.reverse_bitmap;
    if (DefaultDepthOfScreen(XtScreen(w)) == 1)
        XCopyArea(XtDisplay(w), pixmap, XtWindow(w),
                DefaultGCOfScreen(XtScreen(w)), 0, 0,
                bitmap_stuff.pixmap_width_in_cells,
        bitmap_stuff.pixmap_height_in_cells,
                0, 0);
    else
        XCopyPlane(XtDisplay(w), pixmap, XtWindow(w),
                DefaultGCOfScreen(XtScreen(w)), 0, 0,
                bitmap_stuff.pixmap_width_in_cells,
        bitmap_stuff.pixmap_height_in_cells,
                0, 0, 1);
}
/*
 * The printout routine writes the data into a standard X11 bitmap file.
 */
/* ARGSUSED */
static void
PrintOut(widget, client_data, call_data)
Widget widget;
XtPointer client_data;   /* unused */
XtPointer call_data;     /* unused */
{
    XWriteBitmapFile(XtDisplay(widget), bitmap_stuff.filename,
            bitmap_stuff.normal_bitmap,
            bitmap_stuff.pixmap_width_in_cells,
            bitmap_stuff.pixmap_height_in_cells, 0, 0);
}
String
FillCell(w)
Widget w;
{
    String cell;
    int x, y;
    XImage *image;
    cell = XtCalloc(bitmap_stuff.pixmap_width_in_cells
            * bitmap_stuff.pixmap_height_in_cells, sizeof(char));
    /* Convert pixmap into image, so that we can
     * read individual pixels */
    image = XGetImage(XtDisplay(w), bitmap_stuff.normal_bitmap, 0, 0,
              bitmap_stuff.pixmap_width_in_cells,
              bitmap_stuff.pixmap_height_in_cells,
              AllPlanes, XYPixmap);
    for (x = 0; x < bitmap_stuff.pixmap_width_in_cells; x++) {
        for (y = 0; y < bitmap_stuff.pixmap_height_in_cells; y++) {
            cell[x + (y * bitmap_stuff.pixmap_width_in_cells)] =
                         XGetPixel(image, x, y);
        }
    }
    return(cell);
}
/*
 * callback to pop up help dialog widget
 */
/*ARGSUSED*/
void ShowHelp(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
    Widget dialog = (Widget) client_data;
    XtManageChild(dialog);
}
/*
 * quit button callback function
 */
/*ARGSUSED*/
void Quit(w, client_data, call_data)
Widget w;
XtPointer client_data, call_data;
{
    exit(0);
}
main(argc, argv)
int argc;
char **argv;
{
    XtAppContext app_context;
    Widget topLevel, mainWindow, menuBar;
    Widget fileButton, fileMenu, quit, helpButton, helpMenu,
                help, helpBox;
    Widget temp;
    Widget bigBitmap, output, smallPixmapBox;
    Widget scrolledWin, frame1, frame2;
    Arg args[5];
    int i;
    unsigned int width, height;
    int junk;
    String cell;
    /* never call a Widget variable "exit"! */
    extern exit();
    static XrmOptionDescRec table[] = {
        {"-pw",            "*pixmapWidthInCells",        XrmoptionSepArg, NULL},
        {"-pixmapwidth",   "*pixmapWidthInCells",        XrmoptionSepArg, NULL},
        {"-ph",            "*pixmapHeightInCells",       XrmoptionSepArg, NULL},
        {"-pixmapheight",  "*pixmapHeightInCells",       XrmoptionSepArg, NULL},
        {"-cellsize",      "*cellSizeInPixels",          XrmoptionSepArg, NULL},
    };
    XtSetLanguageProc(NULL, (XtLanguageProc)NULL, NULL);
    topLevel = XtVaAppInitialize(
            &app_context,           /* Application context */
            "XBitmap5",             /* Application class */
            table, XtNumber(table), /* command line option list */
            &argc, argv,            /* command line args */
            NULL,                   /* for missing app-defaults file */
            NULL);                  /* terminate varargs list */
    if (argv[1] != NULL)
        bitmap_stuff.filename = argv[1];
    else {
        fprintf(stderr, "xbitmap: must specify filename on command line);
        exit(1);
    }
    /* create main window */
    mainWindow = XtVaCreateManagedWidget(
            "mainWindow",               /* widget name */
            xmPanedWindowWidgetClass,   /* widget class */
            topLevel,                   /* parent widget */
            NULL);                      /* terminate varargs list */
    /* create menu bar along top inside of main window */
    menuBar = XmCreateMenuBar(
            mainWindow, /* parent widget */
            "menuBar",  /* widget name */
            NULL,       /* no arguments needed */
            0);         /* no arguments needed */
    XtManageChild(menuBar);
    scrolledWin = XtVaCreateManagedWidget("scrolledWin",
            xmScrolledWindowWidgetClass, mainWindow,
            NULL);
    switch (XReadBitmapFile(XtDisplay(topLevel),
            RootWindowOfScreen(XtScreen(topLevel)), bitmap_stuff.filename,
            &width, &height, &bitmap_stuff.normal_bitmap, &junk, &junk)) {
    case BitmapSuccess:
        file_contained_good_data = True;
        if ((bitmap_stuff.pixmap_width_in_cells != width) ||
                    (bitmap_stuff.pixmap_height_in_cells != height)) {
            i = 0;
            XtSetArg(args[i], XtNpixmapWidthInCells, width);   i++;
            XtSetArg(args[i], XtNpixmapHeightInCells, height);   i++;
            bitmap_stuff.pixmap_width_in_cells = width;
            bitmap_stuff.pixmap_height_in_cells = height;
            cell = FillCell(topLevel);
            XtSetArg(args[i], XtNcellArray, cell);   i++;
        }
        break;
    case BitmapOpenFailed:
        fprintf(stderr,
          "xbitmap: could not open bitmap file, using fresh bitmap.);
        i = 0;
        file_contained_good_data = False;
        break;
    case BitmapFileInvalid:
        fprintf(stderr, "xbitmap: bitmap file invalid.);
        exit(1);
    case BitmapNoMemory:
        fprintf(stderr,
          "xbitmap: insufficient server memory to create bitmap.);
        exit(1);
    default:
        fprintf(stderr, "xbitmap: programming error.);
        exit(1);
    }
    bigBitmap = XtCreateManagedWidget("bigBitmap",
            bitmapEditWidgetClass, scrolledWin, args, i);
    XtAddCallback(bigBitmap, XtNtoggleCallback, CellToggled, NULL);
    if (!file_contained_good_data) {
        XtVaGetValues(bigBitmap,
            XtNpixmapWidthInCells, &bitmap_stuff.pixmap_width_in_cells,
            XtNpixmapHeightInCells, &bitmap_stuff.pixmap_height_in_cells,
            NULL);
    }
    /*
     *  CREATE FILE MENU AND CHILDREN
     */
    /* create button that will pop up the menu */
    fileButton = XtVaCreateManagedWidget(
            "fileButton",               /* widget name */
            xmCascadeButtonWidgetClass, /* widget class */
            menuBar,                    /* parent widget */
            NULL);                      /* terminate varargs list */
    /* create menu (really a Shell widget and RowColumn widget combo) */
    fileMenu = XmCreatePulldownMenu(
            menuBar,    /* parent widget */
            "fileMenu", /* widget name */
            NULL,       /* no argument list needed */
            0);         /* no argument list needed */
    /*
     *  CREATE BUTTON TO OUTPUT BITMAP
     */
    /* create button that will pop up the menu */
    output = XtVaCreateManagedWidget(
            "output",                   /* widget name */
            xmPushButtonWidgetClass,    /* widget class */
            fileMenu,                   /* parent widget */
            NULL);                      /* terminate varargs list */
    XtAddCallback(output, XmNactivateCallback, PrintOut, 0);
    /* create the quit button up in the menu */
    quit = XtVaCreateManagedWidget(
            "quit",                     /* widget name */
            xmPushButtonWidgetClass,    /* widget class */
            fileMenu,                   /* parent widget */
            NULL);                      /* terminate varargs list */
    /*
     * Specify which menu fileButton will pop up.
     */
    XtVaSetValues(fileButton,
            XmNsubMenuId, fileMenu,
            NULL);
    /* arrange for quit button to call function that exits. */
    XtAddCallback(quit, XmNactivateCallback, Quit, 0);
    /*
     *  CREATE HELP BUTTON AND BOX
     */
    /* create button that will bring up help menu */
    helpButton = XtVaCreateManagedWidget( "helpButton",
        xmCascadeButtonWidgetClass, menuBar, NULL);
    /* tell menuBar which is the help button (will be specially positioned) */
    XtVaSetValues(menuBar,
          XmNmenuHelpWidget, helpButton,
          NULL);
    /* create menu (really a Shell widget and RowColumn widget combo) */
    helpMenu = XmCreatePulldownMenu( menuBar,
            "helpMenu", NULL, 0);
    /* create the help button up in the menu */
    help = XtVaCreateManagedWidget( "help",
            xmPushButtonWidgetClass, helpMenu, NULL);
    /*
     * Specify which menu helpButton will pop up.
     */
    XtVaSetValues(helpButton,
            XmNsubMenuId, helpMenu,
            NULL);
    /* create popup that will contain help */
    helpBox = XmCreateMessageDialog(
            help,       /* parent widget */
            "helpBox",  /* widget name   */
            NULL,       /* no arguments needed */
            0);         /* no arguments needed */
    temp = XmMessageBoxGetChild (helpBox, XmDIALOG_CANCEL_BUTTON);
    XtUnmanageChild (temp);
    temp = XmMessageBoxGetChild (helpBox, XmDIALOG_HELP_BUTTON);
    XtUnmanageChild (temp);
    /* arrange for getHelp button to pop up helpBox */
    XtAddCallback(help, XmNactivateCallback, ShowHelp, helpBox);
    smallPixmapBox = XtVaCreateManagedWidget("smallPixmapBox",
            xmFormWidgetClass, mainWindow,
            NULL);
    frame1 = XtVaCreateManagedWidget("frameNormal",
            xmFrameWidgetClass, smallPixmapBox,
            XmNleftAttachment, XmATTACH_FORM,
            NULL);
    SetUpThings(topLevel);
    bitmap_stuff.showNormalBitmap = XtVaCreateManagedWidget("showNormalBitmap",
            xmDrawingAreaWidgetClass, frame1,
            XmNwidth, bitmap_stuff.pixmap_width_in_cells,
            XmNheight, bitmap_stuff.pixmap_height_in_cells,
            NULL);
    frame2 = XtVaCreateManagedWidget("frameReverse",
            xmFrameWidgetClass, smallPixmapBox,
            XmNleftAttachment, XmATTACH_WIDGET,
            XmNleftWidget, frame1,
            NULL);
    bitmap_stuff.showReverseBitmap = XtVaCreateManagedWidget("showReverseBitmap",
            xmDrawingAreaWidgetClass, frame2,
            XmNwidth, bitmap_stuff.pixmap_width_in_cells,
            XmNheight, bitmap_stuff.pixmap_height_in_cells,
            NULL);
    XtAddCallback(bitmap_stuff.showNormalBitmap, XmNexposeCallback,
            RedrawSmallPicture, NULL);
    XtAddCallback(bitmap_stuff.showReverseBitmap, XmNexposeCallback,
            RedrawSmallPicture, NULL);
    XtRealizeWidget(topLevel);
    XtAppMainLoop(app_context);
}
static void
SetUpThings(w)
Widget w;
{
    XGCValues values;
    if (!file_contained_good_data) {
      bitmap_stuff.normal_bitmap = XCreatePixmap(XtDisplay(w),
            RootWindowOfScreen(XtScreen(w)),
            bitmap_stuff.pixmap_width_in_cells,
            bitmap_stuff.pixmap_height_in_cells, 1);
    }
    values.foreground = 1;
    values.background = 0;
    /* note that normal_bitmap is used as the drawable because it
     * is one bit deep.  The root window may not be one bit deep */
    bitmap_stuff.draw_gc = XCreateGC(XtDisplay(w),
            bitmap_stuff.normal_bitmap,
            GCForeground | GCBackground, &values);
    values.foreground = 0;
    values.background = 1;
    bitmap_stuff.undraw_gc = XCreateGC(XtDisplay(w),
            bitmap_stuff.normal_bitmap,
            GCForeground | GCBackground, &values);
    bitmap_stuff.reverse_bitmap = XCreatePixmap(XtDisplay(w),
            RootWindowOfScreen(XtScreen(w)),
            bitmap_stuff.pixmap_width_in_cells,
            bitmap_stuff.pixmap_height_in_cells, 1);
    if (file_contained_good_data) {
        XImage *image;
        int x, y;
        image = XGetImage (XtDisplay(w), bitmap_stuff.normal_bitmap, 0, 0,
             bitmap_stuff.pixmap_width_in_cells,
             bitmap_stuff.pixmap_height_in_cells,
             AllPlanes, XYPixmap);
        for (x = 0; x < bitmap_stuff.pixmap_width_in_cells; x++) {
            for (y = 0; y < bitmap_stuff.pixmap_height_in_cells; y++) {
                XDrawPoint(XtDisplay(w), bitmap_stuff.reverse_bitmap,
                        ((XGetPixel(image, x, y) == DRAWN) ?
                        bitmap_stuff.undraw_gc :
                        bitmap_stuff.draw_gc), x, y);
            }
        }
    }
    else {
    /* pixmaps must be cleared - may contain garbage */
      XFillRectangle(XtDisplay(w),
            bitmap_stuff.reverse_bitmap, bitmap_stuff.draw_gc,
            0, 0, bitmap_stuff.pixmap_width_in_cells + 1,
            bitmap_stuff.pixmap_height_in_cells + 1);
      XFillRectangle(XtDisplay(w),
            bitmap_stuff.normal_bitmap, bitmap_stuff.undraw_gc,
            0, 0, bitmap_stuff.pixmap_width_in_cells + 1,
            bitmap_stuff.pixmap_height_in_cells + 1);
    }
}
/* ARGSUSED */
static void
CellToggled(w, client_data, call_data)
Widget w;
XtPointer client_data;  /* unused */
XtPointer call_data;    /* will be cast to cur_info */
{
    /* cast pointer to needed type: */
    BitmapEditPointInfo *cur_info = (BitmapEditPointInfo *) call_data;
    /*
     * Note, BitmapEditPointInfo is defined in BitmapEdit.h
     */
    XDrawPoint(XtDisplay(w), bitmap_stuff.normal_bitmap,
            ((cur_info->mode == DRAWN) ? bitmap_stuff.draw_gc :
            bitmap_stuff.undraw_gc), cur_info->newx, cur_info->newy);
    XDrawPoint(XtDisplay(w), bitmap_stuff.reverse_bitmap,
            ((cur_info->mode == DRAWN) ? bitmap_stuff.undraw_gc :
            bitmap_stuff.draw_gc), cur_info->newx, cur_info->newy);
    RedrawSmallPicture(bitmap_stuff.showNormalBitmap,
            cur_info->newx, cur_info->newy);
    RedrawSmallPicture(bitmap_stuff.showReverseBitmap,
            cur_info->newx, cur_info->newy);
}