Listing 1
/* xtxtvw.c
X11/Motif based text file viewer - example program meant to accompany
Linux Journal article on Motif development
the following command should build the application on most linux systems
gcc -o xtxtvw xtxtvw.c -I/usr/X11R6/include -L/usr/X11R6/lib -lXm -lXt -lX11
the following command should build the application on many Solaris systems
gcc -o xtxtvw xtxtvw.c -I/usr/dt/include -L/usr/openwin/lib -lXm -lXt -lX11
*/
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xmu/Editres.h>
#include <Xm/FileSB.h>
#include <Xm/Form.h>
#include <Xm/LabelG.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h> /* for the menu widget */
#include <Xm/SelectioB.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
/* the class is used to identify application resources */
const char *X_APP_CLASS = "XTxtVw";
/* reasonable default values for some essential resources */
const String FALLBACKRES[] =
{
"*background: grey"
, "*forground: black"
/* don't use * here otherwise all widgets will have these dimensions */
, "XTxtVw.width: 400"
, "XTxtVw.height: 400"
, NULL
};
/* most applications have only 1 top level shell */
Widget g_topshell;
/* the file selection dialog is invoked from multiple routines
so it is easiest to make the widget global */
Widget g_filedlg;
/* we use the widget to keep the file name */
Widget g_filenamew;
/* we would like to alter the text display from many routines so... */
Widget g_filetextw;
/* callback routine names are suffixed with CB by convention */
void fileCB(Widget widget, XtPointer client_data, XtPointer call_data);
void readfileCB(Widget widget, XtPointer client_data, XtPointer call_data);
void fileselCB(Widget widget, XtPointer client_data, XtPointer call_data);
void file_okCB(Widget widget, XtPointer client_data, XtPointer call_data);
void setMainTitle(char *fn);
/*---------------------------------------- main */
int
main(int argc, char *argv[])
{
XtAppContext app;
Widget menubar;
Widget form;
Widget label;
Widget browseb;
Widget w;
XmString xms_file;
XmString xms_open;
XmString xms_exit;
XmString xms_openacc;
XmString xms_exitacc;
char *fn;
Arg args[10];
int nargs;
/* initialize toolkit and create top level shell */
XtSetLanguageProc(NULL, NULL, NULL);
g_topshell = XtAppInitialize(&app, X_APP_CLASS, NULL, 0, &argc, argv
, (String *) FALLBACKRES, NULL, 0);
/* enable editres support */
XtAddEventHandler(g_topshell, (EventMask) 0, True
, (XtEventHandler) _XEditResCheckMessages, 0);
setMainTitle(NULL);
/* handle command line arguments that remain - Xt has already taken
care of any arguments intended for it, what remains is yours */
if(argc > 1)
fn = argv[1];
/* build GUI components */
form = XmCreateForm(g_topshell, "form", NULL, 0);
/* build the menu bar */
/* most of the strings that get displayed by Motif are handled as the
XmString type in order to support localization */
xms_file = XmStringCreateLocalized("File");
/* XmVa* functions require a trailing 'NULL' argument */
menubar = XmVaCreateSimpleMenuBar(form, "menubar"
, XmVaCASCADEBUTTON, xms_file, 'F'
, XmNtopAttachment, XmATTACH_FORM
, XmNleftAttachment, XmATTACH_FORM
, XmNrightAttachment, XmATTACH_FORM
, NULL);
/* if Motif Creates or Gets an XmString then you must normally
explicitly release the associated storage */
XmStringFree(xms_file);
/* build the File pulldown menu */
xms_open = XmStringCreateLocalized("Open");
xms_openacc = XmStringCreateLocalized("Ctrl+O");
xms_exit = XmStringCreateLocalized("Exit");
xms_exitacc = XmStringCreateLocalized("Ctrl+X");
XmVaCreateSimplePulldownMenu(menubar, "filemenu", 0, fileCB
, XmVaPUSHBUTTON, xms_open, 'O', "Ctrl<Key>O", xms_openacc
, XmVaSEPARATOR
, XmVaPUSHBUTTON, xms_exit, 'x', "Ctrl<Key>X", xms_exitacc
, NULL);
XmStringFree(xms_open);
XmStringFree(xms_exit);
XtManageChild(menubar);
/* build file selection dialog that will be used by the Browse button */
g_filedlg = XmCreateFileSelectionDialog(g_topshell, "filesb", NULL, 0);
XtAddCallback(g_filedlg, XmNokCallback, fileselCB, NULL);
XtAddCallback(g_filedlg, XmNcancelCallback, fileselCB, NULL);
w = XmSelectionBoxGetChild(g_filedlg, XmDIALOG_HELP_BUTTON);
XtUnmanageChild(w);
w = XmSelectionBoxGetChild(g_filedlg, XmDIALOG_APPLY_BUTTON);
XtUnmanageChild(w);
/* build the filename entry/display area */
label = XtVaCreateManagedWidget("Filename: ", xmLabelGadgetClass, form
, XmNtopAttachment, XmATTACH_WIDGET
, XmNtopWidget, menubar
, XmNtopOffset, 10
, XmNleftAttachment, XmATTACH_FORM
, NULL);
g_filenamew = XtVaCreateManagedWidget("filename", xmTextFieldWidgetClass, form
, XmNtopAttachment, XmATTACH_WIDGET
, XmNtopWidget, menubar
, XmNtopOffset, 5
, XmNleftAttachment, XmATTACH_WIDGET
, XmNleftWidget, label
, XmNleftOffset, 2
, NULL);
browseb = XtVaCreateManagedWidget("Browse", xmPushButtonWidgetClass, form
, XmNtopAttachment, XmATTACH_WIDGET
, XmNtopWidget, menubar
, XmNtopOffset, 6
, XmNrightAttachment, XmATTACH_FORM
, XmNrightOffset, 10
, NULL);
XtVaSetValues(g_filenamew
, XmNrightAttachment, XmATTACH_WIDGET
, XmNrightWidget, browseb
, XmNrightOffset, 10
, NULL);
/* build the file contents area */
/* an alternative to using the XmVa*() is to build an argument list
and passing it to the function. I have noticed that sometimes
Lesstif/gcc and the XmVa*() functions can choke the compiler
*/
nargs = 0;
XtSetArg(args[nargs], XmNtopAttachment, XmATTACH_WIDGET); nargs++;
XtSetArg(args[nargs], XmNtopWidget, g_filenamew); nargs++;
XtSetArg(args[nargs], XmNleftAttachment, XmATTACH_FORM); nargs++;
XtSetArg(args[nargs], XmNrightAttachment, XmATTACH_FORM); nargs++;
XtSetArg(args[nargs], XmNbottomAttachment, XmATTACH_FORM); nargs++;
XtSetArg(args[nargs], XmNeditable, False); nargs++;
XtSetArg(args[nargs], XmNeditMode, XmMULTI_LINE_EDIT); nargs++;
g_filetextw = XmCreateScrolledText(form, "filetext", args, nargs);
XtManageChild(g_filetextw);
/* tie in some callbacks to make this thing live */
XtAddCallback(g_filenamew, XmNactivateCallback, readfileCB, NULL);
XtAddCallback(browseb, XmNactivateCallback, fileCB, NULL);
/* start up the X event loop and expose the GUI */
XtManageChild(form);
XtRealizeWidget(g_topshell);
XtAppMainLoop(app);
return 0;
} /* main */
/*---------------------------------------- fileCB
file menu callback
client_data is a reserved for your use normally, call_data is
typically valued by Motif - see fileselCB
*/
void
fileCB(Widget widget, XtPointer client_data, XtPointer call_data)
{
static Widget filename;
int menuitem;
if(call_data == NULL)
{
filename = (Widget) client_data;
return;
}
menuitem = (int) client_data;
switch(menuitem)
{
case 0: /* open */
XtManageChild(g_filedlg);
break;
case 1: /* exit */
exit(0);
break;
default:
break;
} /* switch */
return;
} /* fileCB */
/*---------------------------------------- fileselCB
callback used by the action buttons in the file selection dialog
Motif callbacks usually populate the call_data parameter with a
widget specific Callback structure, in this case it is a superset
of XmSelectionBoxCallbackStruct (the first few members are identical)
*/
void
fileselCB(Widget widget, XtPointer client_data, XtPointer call_data)
{
char *fn;
int allswell = 0;
XmFileSelectionBoxCallbackStruct *cbs;
cbs = (XmFileSelectionBoxCallbackStruct *) call_data;
if(cbs->reason == XmCR_OK)
{
if(!XmStringGetLtoR(cbs->value, XmFONTLIST_DEFAULT_TAG, &fn))
return;
if(strlen(fn) > 0 && access(fn, R_OK) == 0)
{
XmTextFieldSetString(g_filenamew, fn);
allswell = 1;
}
XtFree(fn);
}
/* dialog will disappear but it is not deallocated - may need it later */
XtUnmanageChild(g_filedlg);
if(allswell)
{
XmUpdateDisplay(g_topshell);
readfileCB(NULL, NULL, NULL);
}
return;
} /* fileselCB */
/*---------------------------------------- readfileCB
callback used to trigger file-read
invoked by user pressing Enter on the filename text field or by
selecting OK from the file selection box
*/
void
readfileCB(Widget widget, XtPointer client_data, XtPointer call_data)
{
const int bufsz = 1024;
FILE *fh;
char *fn;
char buf[bufsz];
char *txt;
char *msg;
int pos = 0;
/* clear the text display */
XmTextSetString(g_filetextw, "");
fn = XmTextFieldGetString(g_filenamew);
fh = fopen(fn, "r");
if(fh)
{
setMainTitle(fn);
while(fgets(buf, bufsz, fh))
{
XmTextInsert(g_filetextw, pos, buf);
pos += strlen(buf);
}
fclose(fh);
}
else
{
msg = (char *) malloc(strlen(fn) + 9);
sprintf(msg, "fopen (%s)", fn);
perror(msg);
free(msg);
}
XtFree(fn);
return;
} /* readfileCB */
/*---------------------------------------- setMainTitle
set the application title - icon titles should also be assigned here
*/
void
setMainTitle(char *fn)
{
Arg args[3];
int nargs = 0;
char title[256];
char *p = NULL;
/* strip any path info from the filename */
if(fn)
{
p = strrchr(fn, '/');
if(p)
p++;
else
p = fn;
}
sprintf(title, "xTxtVw%s%s", (p ? ": " : ""), (p ? p : ""));
XtSetArg(args[nargs], XmNtitle, title); nargs++;
XtSetArg(args[nargs], XmNiconName, title); nargs++;
XtSetValues(g_topshell, args, nargs);
return;
} /* setMainTitle */
/* xtxtvw.c */