X-Designer - The Leading X/Motif GUI Builder - Click to download a FREE evaluation |
Appendix A
Additional Example Programs
This appendix provides some additional example programs that illustrate techniques not discussed in the body of the book.This appendix contains a number of programs that provide more realistic examples of how the Motif toolkit is used. Most of the examples are also intended to encourage further investigation into other X-related topics, such as the use of app-defaults files, fallback resources, and command-line option parsing. Our discussion of the examples is fairly limited; see the comments in the code for explanations of various implementation details.
A Bitmap Display Utility
The xshowbitmap program is a useful utility for reviewing a group of bitmap files. The filenames for the bitmaps can be specified on the command line, sent through a pipe, or typed into stdin. All of the bitmaps are drawn into a pixmap, which is rendered into a DrawingArea widget. The DrawingArea is used as the work window for a ScrolledWindow, so that we can demonstrate application-defined scrolling for the Motif ScrolledWindow. The bitmaps are displayed in an equal number of rows and columns if possible. Alternatively, you can specify either the number of rows or the number of columns using the -rows or -columns command-line option, respectively.The example in Example A-1 demonstrates the use of Xt mechanisms for adding command-line options and application-level resources in an application. For an explanation of these Xt features, see Volume 4. For details on the Xlib functions for reading and manipulating bitmaps, see Volume 1.1
The output of the example is shown in Figure A-1/* Written by Dan Heller and Paula Ferguson. ** Copyright 1994, O'Reilly & Associates, Inc. ** ** The X Consortium, and any party obtaining a copy of these files from ** the X Consortium, directly or indirectly, is granted, free of charge, a ** full and unrestricted irrevocable, world-wide, paid up, royalty-free, ** nonexclusive right and license to deal in this software and ** documentation files (the "Software"), including without limitation the ** rights to use, copy, modify, merge, publish, distribute, sublicense, ** and/or sell copies of the Software, and to permit persons who receive ** copies from any such party to do so. This license includes without ** limitation a license to do the foregoing actions under any patents of ** the party supplying this software to the X Consortium. */ /* xshowbitmap.c -- displays a set of bitmaps specified on the command ** line, from a pipe, or typed into stdin. Bitmaps must be specified ** as file names. ** ** Usage: xshowbitmap ** -s sorts the bitmaps in order of size with largest first ** -v verbose mode for when input is redirected to stdin ** -w width of viewport window ** -h height of viewport window ** -fg foreground color ** -bg background color ** -label labels each bitmap with its corresponding filename ** (this is the default behavior) ** -nolabel doesn't label each bitmap with its filename ** -grid N line width for grid between bitmaps; defaults to 1 ** -rows N number of rows; cannot be used with -cols ** -cols N number of columns; cannot be used with -rows ** -fn font font for bitmap filenames ** -bw max-width excludes bitmaps larger than this width; default is 64 ** -bh max-height excludes bitmaps larger than this height; default is 64 ** - indicates to read from stdin; piping doesn't require ** the `-' argument ** [no arguments] reads from stdin ** ** Modified by A.J.Fountain, IST ** for Motif 2.1/X11R6, ANSI ** ** Example usage: ** xshowbitmaps /usr/X11R6/include/X11/bitmaps/* */ #include <stdio.h> #include <X11/Xos.h> #include <Xm/ScrolledW.h> #include <Xm/DrawingA.h> #include <Xm/ScrollBar.h> #ifdef max #undef max #endif #define max(a,b) ((int)(a)>(int)(b)?(int)(a):(int)(b)) #define min(a,b) ((int)(a)<(int)(b)?(int)(a):(int)(b)) typedef struct { char *name; int len; unsigned int width, height; Pixmap bitmap; } Bitmap; /* Resrcs is an object that contains global variables that we want the ** user to be able to initialize through resources or command line options. ** XtAppInitialize() initializes the fields in this data structure to values ** indicated by the XrmOptionsDescRec structure defined later. */ struct _resrcs { Boolean sort; /* sort the bitmaps */ Boolean verbose; /* loading bitmaps verbosely */ Boolean label_bitmap; /* whether to label bitmaps */ int max_width, max_height; /* largest allowable bitmap */ unsigned int grid; /* line width between bitmaps */ Pixel fg, bg; /* colors of bitmaps */ XFontStruct *font; /* font for bitmap labels */ Dimension view_width, view_height; /* initial clip window size */ int rows, cols; /* forcefully set #rows/cols */ } Resrcs; /* .Xdefaults or app-defaults resources. The last field in each structure ** is used as the default values for the field in the Resrcs struct above. */ static XtResource resources[] = { { "sort", "Sort", XmRBoolean, sizeof (Boolean), XtOffsetOf (struct _resrcs, sort), XmRImmediate, False }, { "verbose", "Verbose", XmRBoolean, sizeof (Boolean), XtOffsetOf (struct _resrcs,verbose), XmRImmediate, False }, { "labelBitmap", "LabelBitmap", XmRBoolean, sizeof (Boolean), XtOffsetOf (struct _resrcs, label_bitmap), XmRImmediate, (char *) True }, { "grid", "Grid", XmRInt, sizeof (int), XtOffsetOf (struct _resrcs, grid), XmRImmediate, (char *) 1 }, { "bitmapWidth", "BitmapWidth", XmRInt, sizeof (int), XtOffsetOf (struct _resrcs, max_width), XmRImmediate, (char *) 64 }, { "bitmapHeight", "BitmapHeight", XmRInt, sizeof (int), XtOffsetOf (struct _resrcs, max_height), XmRImmediate, (char *) 64 }, { XmNfont, XmCFont, XmRFontStruct, sizeof (XFontStruct *), XtOffsetOf (struct _resrcs, font), XmRString, XtDefaultFont }, { XmNforeground, XmCForeground, XmRPixel, sizeof (Pixel), XtOffsetOf (struct _resrcs, fg), XmRString, XtDefaultForeground }, { XmNbackground, XmCBackground, XmRPixel, sizeof (Pixel), XtOffsetOf (struct _resrcs, bg), XmRString, XtDefaultBackground }, { "view-width", "View-width", XmRDimension, sizeof (Dimension), XtOffsetOf (struct _resrcs, view_width), XmRImmediate, (char *) 500 }, { "view-height", "View-height", XmRDimension, sizeof (Dimension), XtOffsetOf (struct _resrcs, view_height), XmRImmediate, (char *) 300 }, { "rows", "Rows", XmRInt, sizeof (int), XtOffsetOf (struct _resrcs, rows), XmRImmediate, 0 }, { "cols", "Cols", XmRInt, sizeof (int), XtOffsetOf (struct _resrcs, cols), XmRImmediate, 0 }, }; /* If the following command line args (1st field) are found, set the ** associated resource values (2nd field) to the given value (4th field). */ static XrmOptionDescRec options[] = { { "-sort", "sort", XrmoptionNoArg, "True" }, { "-v", "verbose", XrmoptionNoArg, "True" }, { "-fn", "font", XrmoptionSepArg, NULL }, { "-fg", "foreground", XrmoptionSepArg, NULL }, { "-bg", "background", XrmoptionSepArg, NULL }, { "-w", "view-width", XrmoptionSepArg, NULL }, { "-h", "view-height", XrmoptionSepArg, NULL }, { "-rows", "rows", XrmoptionSepArg, NULL }, { "-cols", "cols", XrmoptionSepArg, NULL }, { "-bw", "bitmapWidth", XrmoptionSepArg, NULL }, { "-bh", "bitmapHeight", XrmoptionSepArg, NULL }, { "-bitmap_width", "bitmapWidth", XrmoptionSepArg, NULL }, { "-bitmap_height", "bitmapHeight", XrmoptionSepArg, NULL }, { "-label", "labelBitmap", XrmoptionNoArg, "True" }, { "-nolabel", "labelBitmap", XrmoptionNoArg, "False" }, { "-grid", "grid", XrmoptionSepArg, NULL }, }; /* size_cmp() -- used by qsort to sort bitmaps into alphabetical order ** This is used when the "sort" resource is true or when -sort is given. */ int size_cmp(Bitmap *b1, Bitmap *b2) { int n = (int) (b1->width * b1->height) - (int) (b2->width * b2->height); if (n) return n; return strcmp (b1->name, b2->name); } /* int_sqrt() -- get the integer square root of n. Used to put the ** bitmaps in an equal number of rows and colums. */ int int_sqrt(register int n) { register int i, s = 0, t; for (i = 15; i >= 0; i--) { t = (s | (1L << i)); if (t * t <= n) s = t; } return s; } /* global variables that are not changable thru resources or command ** line options. */ Widget drawing_a, vsb, hsb; Pixmap pixmap; /* used the as image for Label widget */ GC gc; Display *dpy; unsigned int cell_width, cell_height; unsigned int pix_hoffset, pix_voffset, sw_hoffset, sw_voffset; void redraw(Window); main(int argc, char *argv[]) { extern char *strcpy(); XtAppContext app; Widget toplevel, scrolled_w; Bitmap *list = (Bitmap *) NULL; char buf[128], *p; Arg args[12]; int n; XFontStruct *font; int istty = isatty(0), redirect = !istty, i = 0, total = 0; unsigned int bitmap_error; int j, k; void scrolled(Widget, XtPointer, XtPointer); void expose_resize(Widget, XtPointer, XtPointer); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaOpenApplication (&app, "XShowbitmap", options, XtNumber (options), &argc, argv, NULL, sessionShellWidgetClass, NULL); dpy = XtDisplay (toplevel); XtGetApplicationResources (toplevel, &Resrcs, resources, XtNumber (resources), NULL, 0); if (Resrcs.rows && Resrcs.cols) XtWarning ("You can't specify both rows *and* columns."); font = Resrcs.font; /* check to see if we have to load the bitmaps from stdin */ if (!argv[1] || !strcmp(argv[1], "-")) { printf ("Loading bitmap names from standard input. "); if (istty) { puts ("End with EOF or ."); redirect++; } else puts ("Use -v to view bitmap names being loaded."); } else if (!istty && strcmp(argv[1], "-")) { printf ("%s: either use pipes or specify bitmap names.\n", argv[0]); exit (1); } /* Now, load the bitmap file names */ while (*++argv || redirect) { if (!redirect) /* this may appear at the end of a list of filenames */ if (!strcmp (*argv, "-")) redirect++; /* switch to stdin prompting */ else (void) strcpy (buf, *argv); if (redirect) { if (istty) printf ("Bitmap file: "), fflush(stdout); if (!fgets (buf, sizeof buf - 1, stdin) || !strcmp (buf, ".\n")) break; buf[strlen (buf) - 1] = 0; /* plug a null at the newline */ } if (!buf[0]) continue; if (Resrcs.verbose) printf ("Loading \"%s\"...", buf), fflush(stdout); if (i == total) { total += 10; /* allocate bitmap structures in groups of 10 */ if (!(list = (Bitmap *) XtRealloc ((char *) list, total * sizeof (Bitmap)))) XtError ("Not enough memory for bitmap data"); } if ((bitmap_error = XReadBitmapFile (dpy, DefaultRootWindow(dpy), buf, &list[i].width, &list[i].height, &list[i].bitmap, &j, &k)) == BitmapSuccess) { if (p = rindex (buf, `/')) p++; else p = buf; if (Resrcs.max_height && list[i].height > Resrcs.max_height || Resrcs.max_width && list[i].width > Resrcs.max_width) { printf ("%s: bitmap too big\n", p); XFreePixmap (dpy, list[i].bitmap); continue; } list[i].len = strlen (p); list[i].name = strcpy (XtMalloc (list[i].len + 1), p); if (Resrcs.verbose) printf ("size: %dx%d\n", list[i].width, list[i].height); i++; } else { printf ("Couldn't load bitmap: "); if (!istty && !Resrcs.verbose) printf("\"%s\": ", buf); switch (bitmap_error) { case BitmapOpenFailed : puts ("open failed."); break; case BitmapFileInvalid : puts ("bad file format."); break; case BitmapNoMemory : puts ("not enough memory."); break; } } } if ((total = i) == 0) { puts ("couldn't load any bitmaps."); exit (1); } printf ("Total bitmaps loaded: %d\n", total); if (Resrcs.sort) { printf ("Sorting bitmaps..."); fflush (stdout); qsort (list, total, sizeof (Bitmap), size_cmp); putchar (`\n'); } /* calculate size for pixmap by getting the dimensions of each bitmap. */ printf ("Calculating sizes for pixmap..."); fflush (stdout); for (i = 0; i < total; i++) { if (list[i].width > cell_width) cell_width = list[i].width; if (list[i].height > cell_height) cell_height = list[i].height; if (Resrcs.label_bitmap && (j = XTextWidth (font, list[i].name, list[i].len)) > cell_width) cell_width = j; } /* Compensate for vertical font height if label_bitmap is true. ** Add value of grid line weight and a 6 pixel padding for aesthetics. */ cell_height += Resrcs.grid + 6 + Resrcs.label_bitmap * (font->ascent + font->descent); cell_width += Resrcs.grid + 6; /* if user didn't specify row/column layout figure it out ourselves. ** optimize layout by making it "square". */ if (!Resrcs.rows && !Resrcs.cols) { Resrcs.cols = int_sqrt (total); Resrcs.rows = (total + Resrcs.cols - 1) / Resrcs.cols; } else if (Resrcs.rows) /* user specified rows -- figure out columns */ Resrcs.cols = (total + Resrcs.rows - 1) / Resrcs.rows; else /* user specified cols -- figure out rows */ Resrcs.rows = (total + Resrcs.cols - 1) / Resrcs.cols; printf ("Creating pixmap area of size %dx%d (%d rows, %d cols)\n", Resrcs.cols * cell_width, Resrcs.rows * cell_height, Resrcs.rows, Resrcs.cols); if (!(pixmap = XCreatePixmap (dpy, DefaultRootWindow(dpy), Resrcs.cols * cell_width, Resrcs.rows * cell_height, DefaultDepthOfScreen (XtScreen (toplevel))))) XtError ("Can't Create pixmap."); if (!(gc = XCreateGC (dpy, pixmap, NULL, 0))) XtError ("Can't create gc."); XSetForeground (dpy, gc, Resrcs.bg); /* init GC's foreground to bg */ XFillRectangle (dpy, pixmap, gc, 0, 0, Resrcs.cols * cell_width, Resrcs.rows * cell_height); XSetForeground (dpy, gc, Resrcs.fg); XSetBackground (dpy, gc, Resrcs.bg); XSetFont (dpy, gc, font->fid); if (Resrcs.grid) { if (Resrcs.grid != 1) /* Line weight of 1 is faster when left as 0 (the default) */ XSetLineAttributes (dpy, gc, Resrcs.grid, 0, 0, 0); for (j = 0; j <= Resrcs.rows * cell_height; j += cell_height) XDrawLine (dpy, pixmap, gc, 0, j, Resrcs.cols * cell_width, j); for (j = 0; j <= Resrcs.cols * cell_width; j += cell_width) XDrawLine (dpy, pixmap, gc, j, 0, j, Resrcs.rows * cell_height); } /* Draw each of the bitmaps into the big picture */ for (i = 0; i < total; i++) { int x = cell_width * (i % Resrcs.cols); int y = cell_height * (i / Resrcs.cols); if (Resrcs.label_bitmap) XDrawString (dpy, pixmap, gc, x + 5 + Resrcs.grid / 2, y + font->ascent + Resrcs.grid / 2, list[i].name, list[i].len); if (DefaultDepthOfScreen (XtScreen (toplevel)) > 1) XCopyPlane (dpy, list[i].bitmap, pixmap, gc, 0, 0, list[i].width, list[i].height, x + 5 + Resrcs.grid / 2, y + font->ascent + font->descent + Resrcs.grid / 2, 1L); else XCopyArea (dpy, list[i].bitmap, pixmap, gc, 0, 0, list[i].width, list[i].height, x + 5 + Resrcs.grid / 2, y + font->ascent + font->descent + Resrcs.grid / 2); XFreePixmap (dpy, list[i].bitmap); XtFree (list[i].name); } XtFree ((char *) list); /* Now we get into the Motif stuff */ /* Create automatic Scrolled Window */ n = 0; XtSetArg (args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); n++; XtSetArg (args[n], XmNvisualPolicy, XmVARIABLE); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; scrolled_w = XmCreateScrolledWindow (toplevel, "scrolled_w", args, n) ; /* Create a drawing area as a child of the ScrolledWindow. ** The DA's size is initialized (arbitrarily) to view_width and ** view_height. The ScrolledWindow will expand to this size. */ n = 0; XtSetArg (args[n], XmNwidth, Resrcs.view_width); n++; XtSetArg (args[n], XmNheight, Resrcs.view_height); n++; drawing_a = XmCreateDrawingArea (scrolled_w, "drawing_a", args, n) ; XtAddCallback (drawing_a, XmNexposeCallback, expose_resize, NULL); XtAddCallback (drawing_a, XmNresizeCallback, expose_resize, NULL); XtManageChild (drawing_a) ; /* Application-defined ScrolledWindows won't create their own ** ScrollBars. So, we create them ourselves as children of the ** ScrolledWindow widget. The vertical ScrollBar's maximum size is ** the number of rows that exist (in unit values). The horizontal ** ScrollBar's maximum width is represented by the number of columns. */ n = 0; j = min (Resrcs.view_height / cell_height, Resrcs.rows); XtSetArg (args[n], XmNorientation, XmVERTICAL) ; n++; XtSetArg (args[n], XmNmaximum, Resrcs.rows) ; n++; XtSetArg (args[n], XmNsliderSize, j) ; n++; vsb = XmCreateScrollBar (scrolled_w, "vsb", args, n) ; XtManageChild (vsb) ; if (Resrcs.view_height / cell_height > Resrcs.rows) sw_voffset = (Resrcs.view_height - Resrcs.rows * cell_height) / 2; n = 0; j = min (Resrcs.view_width / cell_width, Resrcs.cols); XtSetArg (args[n], XmNorientation, XmHORIZONTAL) ; n++; XtSetArg (args[n], XmNmaximum, Resrcs.cols) ; n++; XtSetArg (args[n], XmNsliderSize, j) ; n++; hsb = XmCreateScrollBar (scrolled_w, "hsb", args, n); XtManageChild (hsb); if (Resrcs.view_width / cell_width > Resrcs.cols) sw_hoffset = (Resrcs.view_width - Resrcs.cols * cell_width) / 2; /* Allow the ScrolledWindow to initialize itself accordingly...*/ XtVaSetValues (scrolled_w, XmNhorizontalScrollBar, hsb, XmNverticalScrollBar, vsb, XmNworkWindow, drawing_a, NULL); XtAddCallback (vsb, XmNvalueChangedCallback, scrolled, (XtPointer) XmVERTICAL); XtAddCallback (hsb, XmNvalueChangedCallback, scrolled, (XtPointer) XmHORIZONTAL); XtAddCallback (vsb, XmNdragCallback, scrolled, (XtPointer) XmVERTICAL); XtAddCallback (hsb, XmNdragCallback, scrolled, (XtPointer) XmHORIZONTAL); XtManageChild (scrolled_w); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* scrolled() -- react to scrolling actions; cbs->value is ScrollBar's ** new position. */ void scrolled(Widget scrollbar, XtPointer client_data, XtPointer call_data) { int orientation = (int) client_data; XmScrollBarCallbackStruct *cbs = (XmScrollBarCallbackStruct *) call_data; if (orientation == XmVERTICAL) pix_voffset = cbs->value * cell_height; else pix_hoffset = cbs->value * cell_width; redraw (XtWindow (drawing_a)); } /* expose_resize() -- handles both expose and resize (configure) events. ** For XmCR_EXPOSE, just call redraw() and return. For resizing, ** we must calculate the new size of the viewable area and possibly ** reposition the pixmap's display and position offset. Since we ** are also responsible for the ScrollBars, adjust them accordingly. */ void expose_resize(Widget drawing_a, XtPointer client_data, XtPointer call_data) { XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *) call_data; Dimension view_width, view_height, oldw, oldh; int do_clear = 0; if (cbs->reason == XmCR_EXPOSE) { redraw (cbs->window); return; } oldw = Resrcs.view_width; oldh = Resrcs.view_height; /* Unfortunately, the cbs->event field is NULL, we have to have ** get the size of the drawing area manually. */ XtVaGetValues (drawing_a, XmNwidth, &Resrcs.view_width, XmNheight, &Resrcs.view_height, NULL); /* Get the size of the viewable area in "units lengths" where ** each unit is the cell size for each dimension. This prevents ** rounding error for the {vert,horiz}_start values later. */ view_width = Resrcs.view_width / cell_width; view_height = Resrcs.view_height / cell_height; /* When the user resizes the frame bigger, expose events are generated, ** so that's not a problem, since the expose handler will repaint the ** whole viewport. However, when the window resizes smaller, then no ** expose event is generated. In this case, the window does not need ** to be redisplayed if the old viewport was smaller than the pixmap. ** (The existing image is still valid--no redisplay is necessary.) ** The window WILL need to be redisplayed if: ** 1) new view size is larger than pixmap (pixmap needs to be centered). ** 2) new view size is smaller than pixmap, but the OLD view size was ** larger than pixmap. */ if ( (int) view_height >= Resrcs.rows) { /* The height of the viewport is taller than the pixmap, so set * pix_voffset = 0, so the top origin of the pixmap is shown, * and the pixmap is centered vertically in viewport. */ pix_voffset = 0; sw_voffset = (Resrcs.view_height - Resrcs.rows * cell_height) / 2; /* Case 1 above */ do_clear = 1; /* scrollbar is maximum size */ view_height = Resrcs.rows; } else { /* Pixmap is larger than viewport, so viewport will be completely ** redrawn on the redisplay. (So, we don't need to clear window.) ** Make sure upper side has origin of a cell (bitmap). */ pix_voffset = min (pix_voffset, (Resrcs.rows-view_height) * cell_height); sw_voffset = 0; /* no centering is done */ /* Case 2 above */ if (oldh > Resrcs.rows * cell_height) do_clear = 1; } XtVaSetValues (vsb, XmNsliderSize, max (view_height, 1), XmNvalue, pix_voffset / cell_height, XmNpageIncrement, max (view_height - 1, 1), NULL); /* identical to vertical case above */ if ( (int) view_width >= Resrcs.cols) { /* The width of the viewport is wider than the pixmap, so set ** pix_hoffset = 0, so the left origin of the pixmap is shown, ** and the pixmap is centered horizontally in viewport. */ pix_hoffset = 0; sw_hoffset = (Resrcs.view_width - Resrcs.cols * cell_width) / 2; /* Case 1 above */ do_clear = 1; /* scrollbar is maximum size */ view_width = Resrcs.cols; } else { /* Pixmap is larger than viewport, so viewport will be completely ** redrawn on the redisplay. (So, we don't need to clear window.) ** Make sure left side has origin of a cell (bitmap). */ pix_hoffset = min (pix_hoffset, (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) { XCopyArea (dpy, pixmap, window, gc, pix_hoffset, pix_voffset, Resrcs.view_width, Resrcs.view_height, sw_hoffset, sw_voffset); }
A Memo Calendar
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. Figure A-2 shows the output of the program.The program shown in Example A-2 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 12, 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 \xdf at 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.2
The output from the xmemo program is given in Figure A-2./* Written by Dan Heller and Paula Ferguson. ** Copyright 1994, O'Reilly & Associates, Inc. ** ** The X Consortium, and any party obtaining a copy of these files from ** the X Consortium, directly or indirectly, is granted, free of charge, a ** full and unrestricted irrevocable, world-wide, paid up, royalty-free, ** nonexclusive right and license to deal in this software and ** documentation files (the "Software"), including without limitation the ** rights to use, copy, modify, merge, publish, distribute, sublicense, ** and/or sell copies of the Software, and to permit persons who receive ** copies from any such party to do so. This license includes without ** limitation a license to do the foregoing actions under any patents of ** the party supplying this software to the X Consortium. ** ** Modified by A.J.Fountain, IST ** for Motif 2.1, X11R6, ANSI. */ /* 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> #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 date_dialog(Widget, XtPointer, XtPointer); void set_month(Widget, XtPointer, XtPointer); 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[] = { "*bold.fontName: -*-courier-bold-r-*--18-*", "*bold.fontType: FONT_IS_FONT", "*medium.fontName: -*-courier-medium-r-*--18-*", "*medium.fontType: FONT_IS_FONT", "*XmPushButton*.renderTable: bold", "*XmLabelGadget*.renderTable: medium", NULL }; main (int argc, char *argv[]) { Widget toplevel, frame, rowcol, rowcol2, label; XtAppContext app; int month; Arg args[8]; int n; XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaOpenApplication (&app, "XMemo", NULL, 0, &argc, argv, fallback_resources, sessionShellWidgetClass, 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. */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; rowcol = XmCreateRowColumn (toplevel, "rowcol", args, n) ; /* Place a frame around the calendar... */ frame = XmCreateFrame (rowcol, "frame1", NULL, 0) ; /* the calendar is placed inside of a RowColumn widget */ rowcol2 = XmCreateRowColumn (frame, "rowcol2", NULL, 0) ; /* the month label changes dynamically as each month is selected */ month_label = XmCreateLabelGadget (rowcol2, "month_label", NULL, 0); XtManageChild (month_label); label = XmCreateLabelGadget (rowcol2, " Su Mo Tu We Th Fr Sa", NULL, 0); XtManageChild (label); /* 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(long *), t = time ((long *) 0); struct tm *today = localtime (&t); year = 1900 + today->tm_year; month = today->tm_mon + 1; } XmListSelectPos (list_w, month, True); XtManageChild (rowcol2); XtManageChild (frame); 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(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; XmString xms; Arg args[8]; int n; if (list_cbs->item_position == month + 1) 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); xms = XmStringGenerate ((XtPointer) text, NULL, XmCHARSET_TEXT, NULL); XtVaSetValues (month_label, XmNlabelString, xms, NULL); XmStringFree(xms); 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 */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNnumColumns, 6); n++; XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++; months[month].form = XmCreateRowColumn (XtParent(month_label), "month_form", args, n); /* 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; } n = 0; /* this is where we will hold the dialog later. */ XtSetArg (args[n], XmNuserData, NULL); n++; XtSetArg (args[n], XmNsensitive, (j % 7 == m && tot > 0)); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; months[month].dates[i][j] = XmCreatePushButton (months[month]. form, name, args, n); XtManageChild (months[month].dates[i][j]); XtAddCallback (months[month].dates[i][j], XmNactivateCallback, date_dialog,(XtPointer) 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 ** for the problem, we need to set the shadow thickness of at least ** one of the buttons to 2, so that the entire RowColumn is ** initialized to the right size. But this will cause the button to ** have a visible border and make it appear preselected, so, we have ** to make it appear invisible. If it is invisible then it cannot be ** selected, but it just so happens that the last 5 days in ** the month will never have selectable dates, so we can use any one ** of those. To make the button invisible, we need to unmap the ** widget. We can't simply unmanage it or the parent won't consider ** its size, which defeats the whole purpose. We can't create the ** widget and then unmap it because it has not been realized, so it ** does not have a window yet. We don't want to realize and manage ** the entire application just to realize this one widget, so we ** set XmNmappedWhenManaged to False along with the shadow thickness ** being set to 2. Now the RowColumn is the right size. */ XtVaSetValues (months[month].dates[5][6], XmNshadowThickness, 2, XmNmappedWhenManaged, False, NULL); } /* date_dialog() -- when a date is selected, this function is called. ** Create a dialog (toplevel shell) that contains a multiline text ** widget for memos about this date. */ void date_dialog(Widget w, XtPointer client_data, XtPointer call_data) { int date = (int) client_data; Widget dialog; XWindowAttributes xwa; /* the dialog is stored in the PushButton's XmNuserData */ XtVaGetValues (w, XmNuserData, &dialog, NULL); if (!dialog) { /* it doesn't exist yet, create it. */ char buf[32]; Arg args[5]; int n, n_pos, *list; /* get the month that was selected -- we just need it for its name */ if (!XmListGetSelectedPos (list_w, &list, &n_pos)) return; sprintf (buf, "%s %d %d", months[list[0]-1].name, date, year); XtFree ((char *) list); dialog = XtVaCreatePopupShell ("popup", topLevelShellWidgetClass, XtParent (w), XmNtitle, buf, XmNallowShellResize, True, XmNdeleteResponse, XmUNMAP, NULL); n = 0; XtSetArg (args[n], XmNrows, 10); n++; XtSetArg (args[n], XmNcolumns, 40); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtManageChild (XmCreateScrolledText (dialog, "text", args, n)); /* set the shadow thickness to 2 so user knows there is a memo ** attached to this date. */ XtVaSetValues (w, XmNuserData, dialog, XmNshadowThickness, 2, NULL); } /* See if the dialog is realized and is visible. If so, pop it down */ if (XtIsRealized (dialog) && XGetWindowAttributes (XtDisplay (dialog), XtWindow (dialog), &xwa) && xwa.map_state == IsViewable) { XtPopdown (dialog); } else XtPopup (dialog, XtGrabNone); } /* the rest of the file is junk to support finding the current date. */ static int mtbl[] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 }; int days_in_month(int year, int month) { int days; days = mtbl[month] - mtbl[month - 1]; if (month == 2 && year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) days++; return days; } int day_number(int year, int month, int day) { /* Lots of foolishness with casts for Xenix-286 16-bit ints */ /* Oh Good Grief */ long days_ctr; /* 16-bit ints overflowed Sept 12, 1989 */ year -= 1900; days_ctr = ((long)year * 365L) + ((year + 3) / 4); days_ctr += mtbl[month - 1] + day + 6; if (month > 2 && (year % 4 == 0)) days_ctr++; return (int) (days_ctr % 7L); }
1 XtVaAppInitialize() is considered deprecated in X11R6. The SessionShell and XtVaOpenApplication() are only available in X11R6.2 XtVaAppInitialize() is considered deprecated in X11R6. The SessionShell and XtVaOpenApplication() are only available in X11R6. XmStringGenerate() is only available in Motif 2.0 and later.
|