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

/* dynapix.c -- Display a bitmap in a MainWindow, but allow the user
 * to change the bitmap and its color dynamically.  The design of the
 * program is structured on the pulldown menus of the menubar and the
 * callback routines associated with them.  To allow the user to choose
 * a new bitmap, the "Open" button pops up a FileSelectionDialog where
 * a new bitmap file can be chosen.
#include <Xm/MainW.h>
#include <Xm/Label.h>
#include <Xm/MessageB.h>
#include <Xm/FileSB.h>

/* Globals: the toplevel window/widget and the label for the bitmap.
 * "colors" defines the colors we use, "cur_color" is the current
 * color being used, and "cur_bitmap" references the current bitmap file.
Widget toplevel, label;
String colors[] = { "Black", "Red", "Green", "Blue" };
Pixel cur_color;
char cur_bitmap[1024] = "xlogo64"; /* make large enough for full pathnames */

main(argc, argv)
int argc;
char *argv[];
    Widget main_w, menubar, menu, widget;
    XtAppContext app;
    Pixmap pixmap;
    XmString file, edit, help, open_, quit, red, green, blue, black;
    void file_cb(), change_color(), help_cb();

    XtSetLanguageProc (NULL, NULL, NULL);

    /* Initialize toolkit and parse command line options. */
    toplevel = XtVaAppInitialize (&app, "Demos",
        NULL, 0, &argc, argv, NULL, NULL);

    /* main window contains a MenuBar and a Label displaying a pixmap */
    main_w = XtVaCreateManagedWidget ("main_window",
        xmMainWindowWidgetClass,   toplevel,
        XmNscrollBarDisplayPolicy, XmAS_NEEDED,
        XmNscrollingPolicy,        XmAUTOMATIC,

    /* Create a simple MenuBar that contains three menus */
    file = XmStringCreateLocalized ("File");
    edit = XmStringCreateLocalized ("Edit");
    help = XmStringCreateLocalized ("Help");
    menubar = XmVaCreateSimpleMenuBar (main_w, "menubar",
        XmVaCASCADEBUTTON, file, 'F',
        XmVaCASCADEBUTTON, edit, 'E',
        XmVaCASCADEBUTTON, help, 'H',
    XmStringFree (file);
    XmStringFree (edit);
    /* don't free "help" compound string yet -- reuse it later */

    /* Tell the menubar which button is the help menu  */
    if (widget = XtNameToWidget (menubar, "button_2"))
        XtVaSetValues (menubar, XmNmenuHelpWidget, widget, NULL);

    /* First menu is the File menu -- callback is file_cb() */
    open_ = XmStringCreateLocalized ("Open...");
    quit = XmStringCreateLocalized ("Quit");
    XmVaCreateSimplePulldownMenu (menubar, "file_menu", 0, file_cb,
        XmVaPUSHBUTTON, open_, 'N', NULL, NULL,
        XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
    XmStringFree (open_);
    XmStringFree (quit);

    /* Second menu is the Edit menu -- callback is change_color() */
    black = XmStringCreateLocalized (colors[0]);
    red = XmStringCreateLocalized (colors[1]);
    green = XmStringCreateLocalized (colors[2]);
    blue = XmStringCreateLocalized (colors[3]);
    menu = XmVaCreateSimplePulldownMenu (menubar, "edit_menu", 1, change_color,
        XmVaRADIOBUTTON, black, 'k', NULL, NULL,
        XmVaRADIOBUTTON, red, 'R', NULL, NULL,
        XmVaRADIOBUTTON, green, 'G', NULL, NULL,
        XmVaRADIOBUTTON, blue, 'B', NULL, NULL,
        XmNradioBehavior, True,     /* RowColumn resources to enforce */
        XmNradioAlwaysOne, True,    /* radio behavior in Menu */
    XmStringFree (black);
    XmStringFree (red);
    XmStringFree (green);
    XmStringFree (blue);

    /* Initialize menu so that "black" is selected. */
    if (widget = XtNameToWidget (menu, "button_0"))
        XtVaSetValues (widget, XmNset, True, NULL);

    /* Third menu is the help menu -- callback is help_cb() */
    XmVaCreateSimplePulldownMenu (menubar, "help_menu", 2, help_cb,
        XmVaPUSHBUTTON, help, 'H', NULL, NULL,
    XmStringFree (help); /* we're done with it; now we can free it */

    XtManageChild (menubar);

    /* user can still specify the initial bitmap */
    if (argv[1])
        strcpy (cur_bitmap, argv[1]);
    /* initialize color */
    cur_color = BlackPixelOfScreen (XtScreen (toplevel)),

    /* create initial bitmap */
    pixmap = XmGetPixmap (XtScreen (toplevel), cur_bitmap,
        cur_color, WhitePixelOfScreen (XtScreen (toplevel)));

    if (pixmap == XmUNSPECIFIED_PIXMAP) {
        puts ("can't create initial pixmap");
        exit (1);

    /* Now create label using pixmap */
    label = XtVaCreateManagedWidget ("label", xmLabelWidgetClass, main_w,
        XmNlabelType,   XmPIXMAP,
        XmNlabelPixmap, pixmap,

    /* set the label as the "work area" of the main window */
    XtVaSetValues (main_w,
        XmNmenuBar,    menubar,
        XmNworkWindow, label,

    XtRealizeWidget (toplevel);
    XtAppMainLoop (app);

void unmanagechild (widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;

/* Any item the user selects from the File menu calls this function.
 * It will either be "Open" (item_no == 0) or "Quit" (item_no == 1).
file_cb(widget, client_data, call_data)
Widget widget;     	/* menu item that was selected */
XtPointer client_data;  /* the index into the menu */
XtPointer call_data;	/* unused */
    static Widget dialog; /* make it static for reuse */
    extern void load_pixmap();
    int item_no = (int) client_data;

    if (item_no == 1) /* the "quit" item */
        exit (0);

    /* "Open" was selected.  Create a Motif FileSelectionDialog w/callback */
    if (!dialog) {
        dialog = XmCreateFileSelectionDialog (toplevel, "file_sel", NULL, 0);
        XtAddCallback (dialog, XmNokCallback, load_pixmap, NULL);
        XtAddCallback (dialog, XmNcancelCallback, unmanagechild, NULL);
    XtManageChild (dialog);
    XtPopup (XtParent (dialog), XtGrabNone);

/* The OK button was selected from the FileSelectionDialog (or, the user
 * double-clicked on a file selection).  Try to read the file as a bitmap.
 * If the user changed colors, we call this function directly from change_color()
 * to reload the pixmap.  In this case, we pass NULL as the callback struct
 * so we can identify this special case.
load_pixmap(dialog, client_data, call_data)
Widget dialog;	
XtPointer client_data;
XtPointer call_data;
    Pixmap pixmap;
    char *file = NULL;
    XmFileSelectionBoxCallbackStruct *cbs =
        (XmFileSelectionBoxCallbackStruct *) call_data;

    if (cbs) {
        if (!XmStringGetLtoR (cbs->value, XmFONTLIST_DEFAULT_TAG, &file))
            return; /* internal error */
        (void) strcpy (cur_bitmap, file);
        XtFree (file); /* free allocated data from XmStringGetLtoR() */

    pixmap = XmGetPixmap (XtScreen (toplevel), cur_bitmap,
        cur_color, WhitePixelOfScreen (XtScreen (toplevel)));

    if (pixmap == XmUNSPECIFIED_PIXMAP)
        printf ("Can't create pixmap from %s\n", cur_bitmap);
    else {
        Pixmap old;
        XtVaGetValues (label, XmNlabelPixmap, &old, NULL);
        XmDestroyPixmap (XtScreen (toplevel), old);
        XtVaSetValues (label,
            XmNlabelType,   XmPIXMAP,
            XmNlabelPixmap, pixmap,

/* called from any of the "Edit" menu items.  Change the color of the
 * current bitmap being displayed.  Do this by calling load_pixmap().
change_color(widget, client_data, call_data)
Widget widget;		/* menu item that was selected */
XtPointer client_data;	/* the index into the menu */
XtPointer call_data;	/* unused */
    XColor xcolor, unused;
    Display *dpy = XtDisplay (label);
    Colormap cmap = DefaultColormapOfScreen (XtScreen (label));
    int item_no = (int) client_data;

    if (XAllocNamedColor (dpy, cmap, colors[item_no], &xcolor, &unused) == 0 ||
        cur_color == xcolor.pixel)

    cur_color = xcolor.pixel;
    load_pixmap (widget, NULL, NULL);

#define MSG \
"Use the FileSelection dialog to find bitmap files to\n\
display in the scrolling area in the main window.  Use\n\
the edit menu to display the bitmap in different colors."

/* The help button in the help menu from the menubar was selected.
 * Display help information defined above for how to use the program.
 * This is done by creating a Motif information dialog box.  Again,
 * make the dialog static so we can reuse it.
help_cb(widget, client_data, call_data)
Widget widget;		
XtPointer client_data;	
XtPointer call_data;	
    static Widget dialog;

    if (!dialog) {
        Arg args[5];
        int n = 0;
        XmString msg = XmStringCreateLtoR (MSG, XmFONTLIST_DEFAULT_TAG);
        XtSetArg (args[n], XmNmessageString, msg); n++;
        dialog = XmCreateInformationDialog (toplevel, "help_dialog", args, n);
    XtManageChild (dialog);
    XtPopup (XtParent (dialog), XtGrabNone);