(Resrcs.cols − view_width) * cell_width);
sw_hoffset = 0;
/* Case 2 above */
if (oldw > Resrcs.cols * cell_width)
do_clear = 1;
}
XtVaSetValues (hsb,
XmNsliderSize, max (view_width, 1),
XmNvalue, pix_hoffset / cell_width,
XmNpageIncrement, max (view_width − 1, 1),
NULL);
if (do_clear)
/* XClearWindow() doesn't generate an ExposeEvent */
XClearArea (dpy, cbs−>window, 0, 0, 0, 0, True);
}
void
redraw(window)
Window window;
{
XCopyArea (dpy, pixmap, window, gc, pix_hoffset, pix_voffset,
Resrcs.view_width, Resrcs.view_height, sw_hoffset, sw_voffset);
}
The output of the example is shown in the figure.
Output of xshowbitmap.c
28.3 A Memo Calendar
28 Additional Example Programs 28.3 A Memo Calendar
759
The xmemo program creates a main application window that contains a calendar and a list of months. Selecting a
month changes the calendar, while selecting a day causes that date to become activated. When a date is activated, the
application displays another window that contains a Text widget. The Text widget could be used to keep a memo for
that day if you were to add code to save and retrieve the contents of the memo. If you select the same day a second
time, the window is popped down. the figure shows the output of the program.
The program shown in the source code demonstrates a number of very subtle quirks about X and Motif programming.
What separates simple programs from sophisticated ones is how well you get around quirks like the ones
demonstrated in this example. For example, the way the dates in the calendar are handled is not as simple as it might
appear. Unlike the xcal example in Chapter 11, Labels and Buttons, which used a single Label widget as the calendar,
here each date in a month is a separate PushButton widget. To give the appearance that the calendar is a single flat
area, the XmNShadowThickness of each PushButton is initialized to 0. When a date is selected, the shadow
thickness for that PushButton is reset to 2 (the default) to provide visual feedback that there is a memo associated with
it. XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.
XmStringCreateLocalized() is only available in Motif 1.2; XmStringCreateSimple() is the
corresponding function in Motif 1.1.
Output of xmemo.c
/* xmemo.c −− a memo calendar program that creates a calendar on the
* left and a list of months on the right. Selecting a month changes
* the calendar. Selecting a day causes that date to become activated
* and a popup window is displayed that contains a text widget. This
* widget is presumably used to keep memos for that day. You can pop
* up and down the window by continuing to select the date on that month.
*/
#include <stdio.h>
28 Additional Example Programs 28.3 A Memo Calendar
760
#include <X11/Xos.h>
#include <Xm/List.h>
#include <Xm/Frame.h>
#include <Xm/LabelG.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/Text.h>
int year;
void XmStringFreeTable(), date_dialog(), set_month();
Widget list_w, month_label;
typedef struct _month {
char *name;
Widget form, dates[6][7];
} Month;
Month months[] = { /* only initialize "known" data */
{ "January" }, { "February" }, { "March" }, { "April" },
{ "May" }, { "June" }, { "July" }, { "August" }, { "September" },
{ "October" }, { "November" }, { "December" }
};
/* These only take effect if the app−defaults file is not found */
String fallback_resources[] = {
"*XmPushButton.fontList: −*−courier−bold−r−*−−18−*",
"*XmLabelGadget.fontList: −*−courier−bold−r−*−−18−*",
"*XmList.fontList: −*−courier−medium−r−*−−18−*",
NULL
};
main(argc, argv)
int argc;
char *argv[];
{
Widget toplevel, frame, rowcol, rowcol2;
XtAppContext app;
int month;
XtSetLanguageProc (NULL, NULL, NULL);
toplevel = XtVaAppInitialize (&app, "XMemo", NULL, 0,
&argc, argv, fallback_resources, NULL);
/* The form is the general layout manager for the application.
* It will contain two widgets (the calendary and the list of months).
* These widgets are laid out horizontally.
*/
rowcol = XtVaCreateWidget ("rowcol",
xmRowColumnWidgetClass, toplevel,
XmNorientation, XmHORIZONTAL,
NULL);
/* Place a frame around the calendar... */
frame = XtVaCreateManagedWidget ("frame1",
xmFrameWidgetClass, rowcol, NULL);
/* the calendar is placed inside of a RowColumn widget */
rowcol2 = XtVaCreateManagedWidget ("rowcol2",
xmRowColumnWidgetClass, frame, NULL);
/* the month label changes dynamically as each month is selected */
28 Additional Example Programs 28.3 A Memo Calendar
761
month_label = XtVaCreateManagedWidget ("month_label",
xmLabelGadgetClass, rowcol2, NULL);
XtVaCreateManagedWidget (" Su Mo Tu We Th Fr Sa",
xmLabelGadgetClass, rowcol2, NULL);
/* Create a ScrolledText that contains the months. You probably won't
* see the ScrollBar unless the list is resized so that not all of
* the month names are visible.
*/
{
XmString strs[XtNumber (months)];
for (month = 0; month < XtNumber (months); month++)
strs[month] = XmStringCreateLocalized (months[month].name);
list_w = XmCreateScrolledList (rowcol, "list", NULL, 0);
XtVaSetValues (list_w,
XmNitems, strs,
XmNitemCount, XtNumber (months),
NULL);
for (month = 0; month < XtNumber (months); month++)
XmStringFree (strs[month]);
XtAddCallback (list_w, XmNbrowseSelectionCallback, set_month, NULL);
XtManageChild (list_w);
}
/* Determine the year we're dealing with and establish today's month */
if (argc > 1)
year = atoi (argv[1]);
else {
long time(), t = time (0);
struct tm *today = localtime (&t);
year = 1900 + today−>tm_year;
month = today−>tm_mon + 1;
}
XmListSelectPos (list_w, month, True);
XtManageChild (rowcol);
XtRealizeWidget (toplevel);
XtAppMainLoop (app);
}
/* set_month() −− callback routine for when a month is selected.
* Each month is a separate, self−contained widget that contains the
* dates as PushButton widgets. New months do not overwrite old ones,
* so the old month must be "unmanaged" before the new month is managed.
* If the month has not yet been created, then figure out the dates and
* which days of the week they fall on using clever math computations...
*/
void
set_month(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
XmListCallbackStruct *list_cbs = (XmListCallbackStruct *) call_data;
char text[BUFSIZ];
register char *p;
int i, j, m, tot, day;
static int month = −1;
if (list_cbs−>item_position == month + 1)
28 Additional Example Programs 28.3 A Memo Calendar
762
return; /* same month, don't bother redrawing */
if (month >= 0 && months[month].form)
XtUnmanageChild (months[month].form); /* unmanage last month */
month = list_cbs−>item_position − 1; /* set new month */
sprintf (text, "%s %d", months[month].name, year);
XtVaSetValues (month_label,
XtVaTypedArg, XmNlabelString, XmRString, text, strlen (text) + 1,
NULL);
if (months[month].form) {
/* it's already been created −− just manage and return */
XtManageChild (months[month].form);
return;
}
/* Create the month Form widget and dates PushButton widgets */
months[month].form = XtVaCreateWidget ("month_form",
xmRowColumnWidgetClass, XtParent (month_label),
XmNorientation, XmHORIZONTAL,
XmNnumColumns, 6,
XmNpacking, XmPACK_COLUMN,
NULL);
/* calculate the dates of the month using science */
/* day_number() takes day−of−month (1−31), returns day−of−week (0−6) */
m = day_number (year, month + 1, 1);
tot = days_in_month (year, month + 1);
/* We are creating a whole bunch of PushButtons, but not all of
* them have dates associated with them. The buttons that have
* dates get the number sprintf'ed into it. All others get two blanks.
*/
for (day = i = 0; i < 6; i++) {
for (j = 0; j < 7; j++, m += (j > m && −−tot > 0)) {
char *name;
if (j != m || tot < 1)
name = " ";
else {
sprintf(text, "%2d", ++day);
name = text;
}
months[month].dates[i][j] =
XtVaCreateManagedWidget (name,
xmPushButtonWidgetClass, months[month].form,
/* this is where we will hold the dialog later. */
XmNuserData, NULL,
XmNsensitive, (j % 7 == m && tot > 0),
XmNshadowThickness, 0,
NULL);
XtAddCallback (months[month].dates[i][j],
XmNactivateCallback, date_dialog, day);
}
m = 0;
}
XtManageChild (months[month].form);
/* The RowColumn widget creates equally sized boxes for each child
* it manages. If one child is bigger than the rest, all children
* are that big. If we create all the PushButtons with a 0 shadow
* thickness, as soon as one PushButton is selected and its thickness
* is set to 2, the entire RowColumn resizes itself. To compensate
28 Additional Example Programs 28.3 A Memo Calendar
763
Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.