Skip to content

Instantly share code, notes, and snippets.

@bert
Created May 20, 2010 20:07
Show Gist options
  • Save bert/408014 to your computer and use it in GitHub Desktop.
Save bert/408014 to your computer and use it in GitHub Desktop.
X11/Motif based text file viewer
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
# EOF #
/*!
* \ file xtxtvw.c
*
* X11/Motif based text file viewer.
*
* Example program meant to accompany Linux Journal article on Motif development.
*/
#include <stdlib.h>
#include <stdio.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>
#include "xtxtvw.h"
const char *X_APP_CLASS = "XTxtVw";
/*!< the class is used to identify application 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
};
/*!< reasonable default values for some essential resources. */
Widget g_topshell;
/*!< most applications have only 1 top level shell. */
Widget g_filedlg;
/*!< the file selection dialog is invoked from multiple routines so it is easiest to make the widget global. */
Widget g_filenamew;
/*!< we use the widget to keep the file name. */
Widget g_filetextw;
/*!< we would like to alter the text display from many routines so... */
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
);
#if 0
/* enable editres support */
XtAddEventHandler
(
g_topshell,
(EventMask) 0,
True,
(XtEventHandler) _XEditResCheckMessages,
0
);
#endif
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 */
/*!
* \brief 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 */
/*!
* \brief 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,
* we may need it later. */
XtUnmanageChild(g_filedlg);
if (allswell)
{
XmUpdateDisplay (g_topshell);
readfileCB (NULL, NULL, NULL);
}
return;
} /* fileselCB */
/*!
* \brief 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 */
/*!
* 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 */
/* EOF */
/*!
* \ file xtxtvw.h
*
* Header file for the X11/Motif based text file viewer.
*/
/* 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);
/* EOF */
@MarkowEduard
Copy link

Hi Bert
Thanks for publishing this program which helped me a lot to understand Motif.
I found that the command line parameter for the file name is read but not used.
If we change
if (argc > 1) ...
to
if (1 < argc) fn = argv[1]; else fn = NULL;
and insert
if (1 < argc) fn = argv[1]; else fn = NULL;
just before
XtAppMainLoop (app);
both in function "main"
one can supply a file name on the command line and the program tries to read in the file with that name.
Kind regards
Eduard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment