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