Home  >  Magazine  >  #64 August 1999  >  Motif/Lesstif  >  Listing 1
June 30, 1999 | Last Updated 10:40am

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