xditview.c revision 09bee7c9
1/*
2
3Copyright (c) 1991  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from the X Consortium.
28
29*/
30/*
31 * xditview --
32 *
33 *   Display ditroff output in an X window
34 */
35
36#include <X11/Intrinsic.h>
37#include <X11/StringDefs.h>
38#include <X11/Xatom.h>
39#include <X11/Shell.h>
40#include <X11/Xos.h>		/* rindex declaration */
41#include <X11/Xaw/Paned.h>
42#include <X11/Xaw/Panner.h>
43#include <X11/Xaw/Porthole.h>
44#include <X11/Xaw/Viewport.h>
45#include <X11/Xaw/Box.h>
46#include <X11/Xaw/Command.h>
47#include <X11/Xaw/Dialog.h>
48#include <X11/Xaw/Label.h>
49#include <X11/Xaw/MenuButton.h>
50#include <X11/Xaw/SimpleMenu.h>
51#include <X11/Xaw/SmeBSB.h>
52#include <X11/Xaw/AsciiText.h>
53
54#include "Dvi.h"
55
56#include "xdit.bm"
57#include "xdit_mask.bm"
58#include <stdio.h>
59#include <stdlib.h>
60#include <sys/stat.h>
61
62/* Command line options table.  Only resources are entered here...there is a
63   pass over the remaining options after XtParseCommand is let loose. */
64
65static XrmOptionDescRec options[] = {
66{"-page",	    "*dvi.pageNumber",	    XrmoptionSepArg,	NULL},
67{"-backingStore",   "*dvi.backingStore",    XrmoptionSepArg,	NULL},
68{"-noPolyText",	    "*dvi.noPolyText",	    XrmoptionNoArg,	"TRUE"},
69{"-resolution",	    "*dvi.screenResolution",XrmoptionSepArg,    NULL},
70};
71
72static char	current_file_name[1024];
73static FILE	*current_file;
74
75static void MakePrompt(Widget, const char *, void (*)(char *), char *);
76
77/*
78 * Report the syntax for calling xditview.
79 */
80
81static void
82Syntax(const char *call)
83{
84    (void) printf ("Usage: %s [-fg <color>] [-bg <color>]\n%s\n", call,
85                   "       [-bd <color>] [-bw <pixels>] [-help]\n"
86                   "       [-display displayname] [-geometry geom]\n"
87                   "       [-page <page-number>] [-backing <backing-store>]\n"
88                   "       [-resolution <screen-resolution>]\n");
89    exit(1);
90}
91
92static void	NewResolution (char *resString);
93static void	NewFile (char *name);
94static void	DisplayPageNumber (void);
95static void	VisitFile (char *name, Boolean resetPage);
96static Widget	toplevel, paned, porthole, dvi;
97static Widget	popupMenu;
98static Widget	menuBar;
99static Widget	fileMenuButton, fileMenu;
100static Widget	pageNumber;
101
102static void	NextPage(Widget entry, XtPointer name, XtPointer data);
103static void	PreviousPage(Widget entry, XtPointer name, XtPointer data);
104static void	SetResolution(Widget entry, XtPointer name, XtPointer data);
105static void	OpenFile(Widget entry, XtPointer name, XtPointer data);
106static void	RevisitFile(Widget entry, XtPointer name, XtPointer data);
107static void	Quit(Widget entry, XtPointer closure, XtPointer data);
108
109struct menuEntry {
110    char    *name;
111    void    (*function)(Widget entry, XtPointer name, XtPointer data);
112};
113
114static struct menuEntry popupMenuEntries[] = {
115    { "nextPage",	    NextPage },
116    { "previousPage",	    PreviousPage },
117    { "setResolution",	    SetResolution },
118    { "openFile",	    OpenFile },
119    { "revisitFile",	    RevisitFile },
120    { "quit",		    Quit }
121};
122
123static struct menuEntry fileMenuEntries[] = {
124    { "openFile",	    OpenFile },
125    { "revisitFile",	    RevisitFile },
126    { "setResolution",	    SetResolution },
127    { "quit",		    Quit }
128};
129
130static void	NextPageAction(Widget, XEvent *, String *, Cardinal *);
131static void	PreviousPageAction(Widget, XEvent *, String *, Cardinal *);
132static void	SetResolutionAction(Widget, XEvent *, String *, Cardinal *);
133static void	OpenFileAction(Widget, XEvent *, String *, Cardinal *);
134static void	RevisitFileAction(Widget, XEvent *, String *, Cardinal *);
135static void	QuitAction(Widget, XEvent *, String *, Cardinal *);
136static void	AcceptAction(Widget, XEvent *, String *, Cardinal *);
137static void	CancelAction(Widget, XEvent *, String *, Cardinal *);
138static void	UpdatePageNumber(Widget, XEvent *, String *, Cardinal *);
139static void	Noop(Widget, XEvent *, String *, Cardinal *);
140
141static XtActionsRec xditview_actions[] = {
142    { "NextPage",	    NextPageAction },
143    { "PreviousPage",	    PreviousPageAction },
144    { "SetResolution",	    SetResolutionAction },
145    { "OpenFile",	    OpenFileAction },
146    { "Quit",		    QuitAction },
147    { "Accept",		    AcceptAction },
148    { "Cancel",		    CancelAction },
149    { "SetPageNumber",	    UpdatePageNumber },
150    { "Noop",		    Noop }
151};
152
153static Atom wm_delete_window;
154
155
156int
157main(int argc, char **argv)
158{
159    char	    *file_name = NULL;
160    int		    i;
161    XtAppContext    xtcontext;
162    Arg		    topLevelArgs[2];
163    Widget          entry;
164
165    XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL);
166
167    toplevel = XtAppInitialize(&xtcontext, "Xditview",
168			       options, XtNumber (options),
169			       &argc, argv, NULL, NULL, 0);
170    if (argc > 2)
171	Syntax(argv[0]);
172
173    XtAppAddActions(xtcontext, xditview_actions, XtNumber (xditview_actions));
174    XtOverrideTranslations
175	(toplevel, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()"));
176
177    XtSetArg (topLevelArgs[0], XtNiconPixmap,
178	      XCreateBitmapFromData (XtDisplay (toplevel),
179				     XtScreen(toplevel)->root,
180				     (char *) xdit_bits,
181				     xdit_width, xdit_height));
182
183    XtSetArg (topLevelArgs[1], XtNiconMask,
184	      XCreateBitmapFromData (XtDisplay (toplevel),
185				     XtScreen(toplevel)->root,
186				     (char *) xdit_mask_bits,
187				     xdit_mask_width, xdit_mask_height));
188    XtSetValues (toplevel, topLevelArgs, 2);
189    if (argc > 1)
190	file_name = argv[1];
191
192    /*
193     * create the popup menu and insert the entries
194     */
195    popupMenu = XtCreatePopupShell ("popupMenu", simpleMenuWidgetClass, toplevel,
196				    NULL, 0);
197    for (i = 0; i < XtNumber (popupMenuEntries); i++) {
198	entry = XtCreateManagedWidget(popupMenuEntries[i].name,
199				      smeBSBObjectClass, popupMenu,
200				      NULL, (Cardinal) 0);
201	XtAddCallback(entry, XtNcallback, popupMenuEntries[i].function, NULL);
202    }
203
204    paned = XtCreateManagedWidget("paned", panedWidgetClass, toplevel,
205				    NULL, (Cardinal) 0);
206    menuBar = XtCreateManagedWidget ("menuBar", boxWidgetClass, paned, NULL, 0);
207
208    fileMenuButton = XtCreateManagedWidget ("fileMenuButton", menuButtonWidgetClass,
209				    menuBar, NULL, (Cardinal) 0);
210    fileMenu = XtCreatePopupShell ("fileMenu", simpleMenuWidgetClass,
211				    fileMenuButton, NULL, (Cardinal) 0);
212    for (i = 0; i < XtNumber (fileMenuEntries); i++) {
213	entry = XtCreateManagedWidget(fileMenuEntries[i].name,
214				      smeBSBObjectClass, fileMenu,
215				      NULL, (Cardinal) 0);
216	XtAddCallback (entry, XtNcallback, fileMenuEntries[i].function, NULL);
217    }
218
219    (void) XtCreateManagedWidget ("prevButton", commandWidgetClass,
220				  menuBar, NULL, (Cardinal) 0);
221
222    pageNumber = XtCreateManagedWidget("pageNumber", asciiTextWidgetClass,
223					menuBar, NULL, (Cardinal) 0);
224
225    (void) XtCreateManagedWidget ("nextButton", commandWidgetClass,
226				  menuBar, NULL, (Cardinal) 0);
227
228    porthole = XtCreateManagedWidget ("viewport", viewportWidgetClass,
229				      paned, NULL, 0);
230    dvi = XtCreateManagedWidget ("dvi", dviWidgetClass, porthole, NULL, 0);
231    if (file_name)
232	VisitFile (file_name, FALSE);
233    XtRealizeWidget (toplevel);
234    wm_delete_window = XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW",
235				   False);
236    (void) XSetWMProtocols (XtDisplay(toplevel), XtWindow(toplevel),
237                            &wm_delete_window, 1);
238    XtAppMainLoop(xtcontext);
239
240    return 0;
241}
242
243static void
244DisplayPageNumber (void)
245{
246    Arg	arg[2];
247    int	actual_number, last_page;
248    XawTextBlock    text;
249    int		    length;
250    char	    value[128];
251    char	    *cur;
252
253    XtSetArg (arg[0], XtNpageNumber, &actual_number);
254    XtSetArg (arg[1], XtNlastPageNumber, &last_page);
255    XtGetValues (dvi, arg, 2);
256    if (actual_number == 0)
257	snprintf (value, sizeof(value), "<none>");
258    else if (last_page > 0)
259	snprintf (value, sizeof(value), "%d of %d", actual_number, last_page);
260    else
261	snprintf (value, sizeof(value), "%d", actual_number);
262    text.firstPos = 0;
263    text.length = strlen (value);
264    text.ptr = value;
265    text.format = FMT8BIT;
266    XtSetArg (arg[0], XtNstring, &cur);
267    XtGetValues (XawTextGetSource (pageNumber), arg, 1);
268    length = strlen (cur);
269    XawTextReplace (pageNumber, 0, length, &text);
270}
271
272static void
273SetPageNumber (int number)
274{
275    Arg	arg[1];
276
277    XtSetArg (arg[0], XtNpageNumber, number);
278    XtSetValues (dvi, arg, 1);
279    DisplayPageNumber ();
280}
281
282static void
283UpdatePageNumber (Widget w, XEvent *xev, String *s, Cardinal *c)
284{
285    char    *string;
286    Arg	    arg[1];
287
288    XtSetArg (arg[0], XtNstring, &string);
289    XtGetValues (XawTextGetSource(pageNumber), arg, 1);
290    SetPageNumber (atoi(string));
291}
292
293static void
294NewResolution(char *resString)
295{
296    int	res;
297    Arg	arg[1];
298
299    res = atoi (resString);
300    if (res <= 0)
301	return;
302    XtSetArg (arg[0], XtNscreenResolution, res);
303    XtSetValues (dvi, arg, 1);
304}
305
306static void
307VisitFile (char *name, Boolean resetPage)
308{
309    Arg	    arg[3];
310    char    *n;
311    FILE    *new_file;
312    Boolean seek = 0;
313    int	    i;
314
315    if (current_file) {
316	if (!strcmp (current_file_name, "-"))
317	    ;
318	else if (current_file_name[0] == '|')
319	    pclose (current_file);
320	else
321	    fclose (current_file);
322    }
323    if (!strcmp (name, "-"))
324	new_file = stdin;
325    else if (name[0] == '|')
326	new_file = popen (name+1, "r");
327    else {
328        struct stat stbuf;
329
330	new_file = fopen (name, "r");
331
332        if (!new_file) {
333            perror(name);
334            return;
335        }
336        /* Make sure it is a regular file */
337        if (fstat(fileno(new_file), &stbuf) != 0) {
338            perror(name);
339            return;
340        }
341        if (! S_ISREG(stbuf.st_mode)){
342            fprintf(stderr, "%s is not a regular file.\n", name);
343            return;
344        }
345
346	seek = 1;
347    }
348    if (!new_file) {
349	/* XXX display error message */
350	return;
351    }
352    i = 0;
353    XtSetArg (arg[i], XtNfile, new_file); i++;
354    XtSetArg (arg[i], XtNseek, seek); i++;
355    if (resetPage) {
356	XtSetArg (arg[i], XtNpageNumber, 1); i++;
357    }
358    XtSetValues (dvi, arg, i);
359    XtSetArg (arg[0], XtNtitle, name);
360    if (name[0] != '/' && (n = rindex (name, '/')))
361	n = n + 1;
362    else
363	n = name;
364    XtSetArg (arg[1], XtNiconName, n);
365    XtSetValues (toplevel, arg, 2);
366    strcpy (current_file_name, name);
367    current_file = new_file;
368    DisplayPageNumber ();
369}
370
371static void
372NewFile (char *name)
373{
374    VisitFile (name, TRUE);
375}
376
377static char fileBuf[1024];
378static char resolutionBuf[1024];
379
380static void
381ResetMenuEntry (Widget entry)
382{
383    Arg	arg[1];
384
385    XtSetArg (arg[0], XtNpopupOnEntry, entry);
386    XtSetValues (XtParent(entry) , arg, (Cardinal) 1);
387}
388
389/*ARGSUSED*/
390static void
391NextPage (Widget entry, XtPointer name, XtPointer data)
392{
393    NextPageAction(entry, NULL, NULL, NULL);
394    ResetMenuEntry (entry);
395}
396
397static void
398NextPageAction (Widget w, XEvent *xev, String *s, Cardinal *c)
399{
400    Arg	args[1];
401    int	number;
402
403    XtSetArg (args[0], XtNpageNumber, &number);
404    XtGetValues (dvi, args, 1);
405    SetPageNumber (number+1);
406}
407
408/*ARGSUSED*/
409static void
410PreviousPage (Widget entry, XtPointer name, XtPointer data)
411{
412    PreviousPageAction (entry, NULL, NULL, NULL);
413    ResetMenuEntry (entry);
414}
415
416static void
417PreviousPageAction (Widget w, XEvent *xev, String *s, Cardinal *c)
418{
419    Arg	args[1];
420    int	number;
421
422    XtSetArg (args[0], XtNpageNumber, &number);
423    XtGetValues (dvi, args, 1);
424    SetPageNumber (number-1);
425}
426
427/*ARGSUSED*/
428static void
429SetResolution (Widget entry, XtPointer name, XtPointer data)
430{
431    SetResolutionAction (entry, NULL, NULL, NULL);
432    ResetMenuEntry (entry);
433}
434
435static void
436SetResolutionAction (Widget w, XEvent *xev, String *s, Cardinal *c)
437{
438    Arg	    args[1];
439    int	    cur;
440
441    XtSetArg (args[0], XtNscreenResolution, &cur);
442    XtGetValues (dvi, args, 1);
443    snprintf (resolutionBuf, sizeof(resolutionBuf), "%d", cur);
444    MakePrompt (toplevel, "Screen resolution:", NewResolution, resolutionBuf);
445}
446
447/*ARGSUSED*/
448static void
449OpenFile (Widget entry, XtPointer name, XtPointer data)
450{
451    OpenFileAction (entry, NULL, NULL, NULL);
452    ResetMenuEntry (entry);
453}
454
455static void
456OpenFileAction (Widget w, XEvent *xev, String *s, Cardinal *c)
457{
458    if (current_file_name[0])
459	strcpy (fileBuf, current_file_name);
460    else
461	fileBuf[0] = '\0';
462    MakePrompt (toplevel, "File to open:", NewFile, fileBuf);
463}
464
465/*ARGSUSED*/
466static void
467RevisitFile (Widget entry, XtPointer name, XtPointer data)
468{
469    RevisitFileAction (entry, NULL, NULL, NULL);
470    ResetMenuEntry (entry);
471}
472
473static void
474RevisitFileAction (Widget w, XEvent *xev, String *s, Cardinal *c)
475{
476    if (current_file_name[0])
477	VisitFile (current_file_name, FALSE);
478}
479
480/*ARGSUSED*/
481static void
482Quit (Widget entry, XtPointer closure, XtPointer data)
483{
484    QuitAction (entry, NULL, NULL, NULL);
485}
486
487static void
488QuitAction (Widget w, XEvent *xev, String *s, Cardinal *c)
489{
490    exit (0);
491}
492
493static Widget promptShell, promptDialog;
494static void (*promptfunction)(char *);
495
496/* ARGSUSED */
497static
498void CancelAction (Widget widget, XEvent *event,
499		   String *params, Cardinal *num_params)
500{
501    if (promptShell) {
502	XtSetKeyboardFocus(toplevel, (Widget) None);
503	XtDestroyWidget(promptShell);
504	promptShell = (Widget) 0;
505    }
506}
507
508
509/* ARGSUSED */
510static
511void AcceptAction (Widget widget, XEvent *event,
512		   String *params, Cardinal *num_params)
513{
514    (*promptfunction)(XawDialogGetValueString(promptDialog));
515    CancelAction (widget, event, params, num_params);
516}
517
518static
519void Noop (Widget w, XEvent *xev, String *s, Cardinal *c)
520{
521}
522
523static void
524MakePrompt(Widget centerw, const char *prompt, void (*func)(char *), char *def)
525{
526    static Arg dialogArgs[] = {
527	{XtNlabel, (XtArgVal) 0},
528	{XtNvalue, (XtArgVal) 0},
529    };
530    Arg valueArgs[1];
531    Arg centerArgs[2];
532    Position	source_x, source_y;
533    Position	dest_x, dest_y;
534    Dimension center_width, center_height;
535    Dimension prompt_width, prompt_height;
536    Widget  valueWidget;
537
538    CancelAction ((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
539    promptShell = XtCreatePopupShell ("promptShell", transientShellWidgetClass,
540				      toplevel, NULL, (Cardinal) 0);
541    dialogArgs[0].value = (XtArgVal)prompt;
542    dialogArgs[1].value = (XtArgVal)def;
543    promptDialog = XtCreateManagedWidget( "promptDialog", dialogWidgetClass,
544		    promptShell, dialogArgs, XtNumber (dialogArgs));
545    XawDialogAddButton(promptDialog, "accept", NULL, NULL);
546    XawDialogAddButton(promptDialog, "cancel", NULL, NULL);
547    valueWidget = XtNameToWidget (promptDialog, "value");
548    if (valueWidget) {
549    	XtSetArg (valueArgs[0], XtNresizable, TRUE);
550    	XtSetValues (valueWidget, valueArgs, 1);
551	/*
552	 * as resizable isn't set until just above, the
553	 * default value will be displayed incorrectly.
554	 * rectify the situation by resetting the values
555	 */
556        XtSetValues (promptDialog, dialogArgs, XtNumber (dialogArgs));
557    }
558    XtSetKeyboardFocus (promptDialog, valueWidget);
559    XtSetKeyboardFocus (toplevel, valueWidget);
560    XtRealizeWidget (promptShell);
561    /*
562     * place the widget in the center of the "parent"
563     */
564    XtSetArg (centerArgs[0], XtNwidth, &center_width);
565    XtSetArg (centerArgs[1], XtNheight, &center_height);
566    XtGetValues (centerw, centerArgs, 2);
567    XtSetArg (centerArgs[0], XtNwidth, &prompt_width);
568    XtSetArg (centerArgs[1], XtNheight, &prompt_height);
569    XtGetValues (promptShell, centerArgs, 2);
570    source_x = (int)(center_width - prompt_width) / 2;
571    source_y = (int)(center_height - prompt_height) / 3;
572    XtTranslateCoords (centerw, source_x, source_y, &dest_x, &dest_y);
573    XtSetArg (centerArgs[0], XtNx, dest_x);
574    XtSetArg (centerArgs[1], XtNy, dest_y);
575    XtSetValues (promptShell, centerArgs, 2);
576    XtMapWidget(promptShell);
577    promptfunction = func;
578}
579