]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/xpdf.cc
Synched with Xpdf 0.92
[evince.git] / pdf / xpdf / xpdf.cc
1 //========================================================================
2 //
3 // xpdf.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stddef.h>
12 #include <string.h>
13 #include <time.h>
14 #include <X11/X.h>
15 #include <X11/cursorfont.h>
16 #include <X11/keysym.h>
17 #include "gtypes.h"
18 #include "GString.h"
19 #include "parseargs.h"
20 #include "gfile.h"
21 #include "gmem.h"
22 #include "LTKAll.h"
23 #include "Object.h"
24 #include "Stream.h"
25 #include "Array.h"
26 #include "Dict.h"
27 #include "XRef.h"
28 #include "Catalog.h"
29 #include "Page.h"
30 #include "Link.h"
31 #include "PDFDoc.h"
32 #include "XOutputDev.h"
33 #include "LTKOutputDev.h"
34 #include "PSOutputDev.h"
35 #include "TextOutputDev.h"
36 #include "Params.h"
37 #include "Error.h"
38 #include "config.h"
39
40 #ifdef XlibSpecificationRelease
41 #if XlibSpecificationRelease < 5
42 typedef char *XPointer;
43 #endif
44 #else
45 typedef char *XPointer;
46 #endif
47
48 // hack around old X includes which are missing these symbols
49 #ifndef XK_Page_Up
50 #define XK_Page_Up              0xFF55
51 #endif
52 #ifndef XK_Page_Down
53 #define XK_Page_Down            0xFF56
54 #endif
55 #ifndef XK_KP_Home
56 #define XK_KP_Home              0xFF95
57 #endif
58 #ifndef XK_KP_Left
59 #define XK_KP_Left              0xFF96
60 #endif
61 #ifndef XK_KP_Up
62 #define XK_KP_Up                0xFF97
63 #endif
64 #ifndef XK_KP_Right
65 #define XK_KP_Right             0xFF98
66 #endif
67 #ifndef XK_KP_Down
68 #define XK_KP_Down              0xFF99
69 #endif
70 #ifndef XK_KP_Prior
71 #define XK_KP_Prior             0xFF9A
72 #endif
73 #ifndef XK_KP_Page_Up
74 #define XK_KP_Page_Up           0xFF9A
75 #endif
76 #ifndef XK_KP_Next
77 #define XK_KP_Next              0xFF9B
78 #endif
79 #ifndef XK_KP_Page_Down
80 #define XK_KP_Page_Down         0xFF9B
81 #endif
82 #ifndef XK_KP_End
83 #define XK_KP_End               0xFF9C
84 #endif
85 #ifndef XK_KP_Begin
86 #define XK_KP_Begin             0xFF9D
87 #endif
88 #ifndef XK_KP_Insert
89 #define XK_KP_Insert            0xFF9E
90 #endif
91 #ifndef XK_KP_Delete
92 #define XK_KP_Delete            0xFF9F
93 #endif
94
95 //------------------------------------------------------------------------
96 // misc constants / enums
97 //------------------------------------------------------------------------
98
99 #define remoteCmdLength 256
100
101 enum XpdfMenuItem {
102   menuOpen,
103   menuReload,
104   menuSavePDF,
105   menuRotateLeft,
106   menuRotateRight,
107   menuQuit
108 };
109
110 //------------------------------------------------------------------------
111 // prototypes
112 //------------------------------------------------------------------------
113
114 // loadFile / displayPage
115 static GBool loadFile(GString *fileName);
116 static void displayPage(int page1, int zoom1, int rotate1, GBool addToHist);
117
118 // key press and menu callbacks
119 static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
120                         char *s, int n);
121 static void menuCbk(LTKMenuItem *item);
122
123 // mouse callbacks
124 static void buttonPressCbk(LTKWidget *canvas1, int n,
125                            int mx, int my, int button, GBool dblClick);
126 static void buttonReleaseCbk(LTKWidget *canvas1, int n,
127                              int mx, int my, int button, GBool click);
128 static void doLink(int mx, int my);
129 static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my);
130 static void mouseDragCbk(LTKWidget *widget, int widgetNum,
131                          int mx, int my, int button);
132
133 // button callbacks
134 static void nextPageCbk(LTKWidget *button, int n, GBool on);
135 static void nextTenPageCbk(LTKWidget *button, int n, GBool on);
136 static void gotoNextPage(int inc, GBool top);
137 static void prevPageCbk(LTKWidget *button, int n, GBool on);
138 static void prevTenPageCbk(LTKWidget *button, int n, GBool on);
139 static void gotoPrevPage(int dec, GBool top, GBool bottom);
140 static void backCbk(LTKWidget *button, int n, GBool on);
141 static void forwardCbk(LTKWidget *button, int n, GBool on);
142 static void pageNumCbk(LTKWidget *textIn, int n, GString *text);
143 static void zoomMenuCbk(LTKMenuItem *item);
144 static void postScriptCbk(LTKWidget *button, int n, GBool on);
145 static void aboutCbk(LTKWidget *button, int n, GBool on);
146 static void quitCbk(LTKWidget *button, int n, GBool on);
147
148 // scrollbar callbacks
149 static void scrollVertCbk(LTKWidget *scrollbar, int n, int val);
150 static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val);
151
152 // misc callbacks
153 static void layoutCbk(LTKWindow *win1);
154 static void updateScrollbars();
155 static void propChangeCbk(LTKWindow *win1, Atom atom);
156
157 // selection
158 static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax);
159
160 // "Open" dialog
161 static void mapOpenDialog();
162 static void openButtonCbk(LTKWidget *button, int n, GBool on);
163 static void openSelectCbk(LTKWidget *widget, int n, GString *name);
164
165 // "Reload"
166 static void reloadCbk();
167
168 // "Save PDF" dialog
169 static void mapSaveDialog();
170 static void saveButtonCbk(LTKWidget *button, int n, GBool on);
171 static void saveSelectCbk(LTKWidget *widget, int n, GString *name);
172
173 // "PostScript" dialog
174 static void mapPSDialog();
175 static void psButtonCbk(LTKWidget *button, int n, GBool on);
176
177 // "About" window
178 static void mapAboutWin();
179 static void closeAboutCbk(LTKWidget *button, int n, GBool on);
180
181 // "Find" window
182 static void findCbk(LTKWidget *button, int n, GBool on);
183 static void mapFindWin();
184 static void findButtonCbk(LTKWidget *button, int n, GBool on);
185 static void doFind(char *s);
186
187 // app kill callback
188 static void killCbk(LTKWindow *win1);
189
190 //------------------------------------------------------------------------
191 // command line options
192 //------------------------------------------------------------------------
193
194 static XrmOptionDescRec opts[] = {
195   {"-display",       ".display",         XrmoptionSepArg,  NULL},
196   {"-foreground",    ".foreground",      XrmoptionSepArg,  NULL},
197   {"-fg",            ".foreground",      XrmoptionSepArg,  NULL},
198   {"-background",    ".background",      XrmoptionSepArg,  NULL},
199   {"-bg",            ".background",      XrmoptionSepArg,  NULL},
200   {"-geometry",      ".geometry",        XrmoptionSepArg,  NULL},
201   {"-g",             ".geometry",        XrmoptionSepArg,  NULL},
202   {"-font",          ".font",            XrmoptionSepArg,  NULL},
203   {"-fn",            ".font",            XrmoptionSepArg,  NULL},
204   {"-title",         ".title",           XrmoptionSepArg,  NULL},
205   {"-cmap",          ".installCmap",     XrmoptionNoArg,   (XPointer)"on"},
206   {"-rgb",           ".rgbCubeSize",     XrmoptionSepArg,  NULL},
207   {"-papercolor",    ".paperColor",      XrmoptionSepArg,  NULL},
208 #if JAPANESE_SUPPORT
209   {"-eucjp",         ".eucjp",           XrmoptionNoArg,   (XPointer)"off"},
210 #endif
211 #if HAVE_T1LIB_H
212   {"-t1lib",         ".t1libControl",    XrmoptionSepArg,  NULL},
213 #endif
214 #if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
215   {"-freetype",      ".freeTypeControl", XrmoptionSepArg,  NULL},
216 #endif
217   {"-z",             ".initialZoom",     XrmoptionSepArg,  NULL},
218   {"-ps",            ".psFile",          XrmoptionSepArg,  NULL},
219   {"-paperw",        ".psPaperWidth",    XrmoptionSepArg,  NULL},
220   {"-paperh",        ".psPaperHeight",   XrmoptionSepArg,  NULL},
221   {"-level1",        ".psLevel1",        XrmoptionNoArg,   (XPointer)"false"},
222   {NULL}
223 };
224
225 static GBool printVersion = gFalse;
226 static GBool printHelp = gFalse;
227 static char remoteName[100] = "xpdf_";
228 static GBool doRemoteRaise = gFalse;
229 static GBool doRemoteQuit = gFalse;
230 static GBool viKeys = gFalse;
231 static char userPassword[33] = "";
232 static GBool fullScreen = gFalse;
233
234 static ArgDesc argDesc[] = {
235   {"-z",          argStringDummy, NULL,           0,
236    "initial zoom level (-5..5, page, width)"},
237   {"-g",          argStringDummy, NULL,           0,
238    "initial window geometry"},
239   {"-geometry",   argStringDummy, NULL,           0,
240    "initial window geometry"},
241   {"-title",      argStringDummy, NULL,           0,
242    "window title"},
243   {"-remote",     argString,      remoteName + 5, sizeof(remoteName) - 5,
244    "start/contact xpdf remote server with specified name"},
245   {"-raise",      argFlag,        &doRemoteRaise, 0,
246    "raise xpdf remote server window (with -remote only)"},
247   {"-quit",       argFlag,        &doRemoteQuit,  0,
248    "kill xpdf remote server (with -remote only)"},
249   {"-cmap",       argFlagDummy,   NULL,           0,
250    "install a private colormap"},
251   {"-rgb",        argIntDummy,    NULL,           0,
252    "biggest RGB cube to allocate (default is 5)"},
253   {"-papercolor", argStringDummy, NULL,           0,
254    "color of paper background"},
255 #if JAPANESE_SUPPORT
256   {"-eucjp",      argStringDummy, NULL,           0,
257    "convert Japanese text to EUC-JP"},
258 #endif
259 #if HAVE_T1LIB_H
260   {"-t1lib",      argStringDummy, NULL,           0,
261    "t1lib font control: none, plain, low, high"},
262 #endif
263 #if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
264   {"-freetype",   argStringDummy, NULL,           0,
265    "FreeType font control: none, plain, aa"},
266 #endif
267   {"-ps",         argStringDummy, NULL,           0,
268    "default PostScript file/command name"},
269   {"-paperw",     argIntDummy,    NULL,           0,
270    "paper width, in points"},
271   {"-paperh",     argIntDummy,    NULL,           0,
272    "paper height, in points"},
273   {"-level1",     argFlagDummy,   NULL,           0,
274    "generate Level 1 PostScript"},
275   {"-upw",        argString,      userPassword,   sizeof(userPassword),
276    "user password (for encrypted files)"},
277   {"-fullscreen", argFlag,        &fullScreen,    0,
278    "run in full-screen (presentation) mode"},
279   {"-cmd",        argFlag,        &printCommands, 0,
280    "print commands as they're executed"},
281   {"-q",          argFlag,        &errQuiet,      0,
282    "don't print any messages or errors"},
283   {"-v",          argFlag,        &printVersion,  0,
284    "print copyright and version info"},
285   {"-h",          argFlag,        &printHelp,     0,
286    "print usage information"},
287   {"-help",       argFlag,        &printHelp,     0,
288    "print usage information"},
289   {NULL}
290 };
291
292 //------------------------------------------------------------------------
293 // global variables
294 //------------------------------------------------------------------------
295
296 // zoom factor is 1.2 (similar to DVI magsteps)
297 #define minZoom    -5
298 #define maxZoom     5
299 #define zoomPage  100
300 #define zoomWidth 101
301 static int zoomDPI[maxZoom - minZoom + 1] = {
302   29, 35, 42, 50, 60,
303   72,
304   86, 104, 124, 149, 179
305 };
306 #define defZoom     1
307 #define defZoomStr "1"
308
309 static PDFDoc *doc;
310
311 static LTKOutputDev *out;
312
313 static int page;
314 static int zoom;
315 static int rotate;
316 static GBool quit;
317
318 static time_t modTime;          // last modification time of PDF file
319
320 static LinkAction *linkAction;  // mouse pointer is over this link
321 static int                      // coordinates of current selection:
322   selectXMin, selectYMin,       //   (xMin==xMax || yMin==yMax) means there
323   selectXMax, selectYMax;       //   is no selection
324 static GBool lastDragLeft;      // last dragged selection edge was left/right
325 static GBool lastDragTop;       // last dragged selection edge was top/bottom
326 static int panMX, panMY;        // last mouse position for pan
327
328 struct History {
329   GString *fileName;
330   int page;
331 };
332 #define historySize 50
333 static History                  // page history queue
334   history[historySize];
335 static int historyCur;          // currently displayed page
336 static int historyBLen;         // number of valid entries backward from
337                                 //   current entry
338 static int historyFLen;         // number of valid entries forward from
339                                 //   current entry
340
341 static GString *defPSFileName;
342 static GString *psFileName;
343 static int psFirstPage, psLastPage;
344
345 static GString *fileReqDir;     // current directory for file requesters
346
347 static GString *urlCommand;     // command to execute for URI links
348
349 static GString *windowTitle;    // window title string
350
351 static LTKApp *app;
352 static Display *display;
353 static LTKWindow *win;
354 static LTKMenu *zoomMenu;
355 static LTKScrollingCanvas *canvas;
356 static LTKScrollbar *hScrollbar, *vScrollbar;
357 static LTKTextIn *pageNumText;
358 static LTKLabel *numPagesLabel;
359 static LTKLabel *linkLabel;
360 static LTKMenuButton *zoomMenuBtn;
361 static LTKWindow *aboutWin;
362 static LTKWindow *psDialog;
363 static LTKWindow *openDialog;
364 static LTKWindow *saveDialog;
365 static LTKWindow *findWin;
366 static Atom remoteAtom;
367 static GC selectGC;
368
369 //------------------------------------------------------------------------
370 // GUI includes
371 //------------------------------------------------------------------------
372
373 #include "xpdfIcon.xpm"
374 #include "leftArrow.xbm"
375 #include "dblLeftArrow.xbm"
376 #include "rightArrow.xbm"
377 #include "dblRightArrow.xbm"
378 #include "backArrow.xbm"
379 #include "forwardArrow.xbm"
380 #include "find.xbm"
381 #include "postscript.xbm"
382 #include "about.xbm"
383 #include "xpdf-ltk.h"
384
385 //------------------------------------------------------------------------
386 // main program
387 //------------------------------------------------------------------------
388
389 int main(int argc, char *argv[]) {
390   Window xwin;
391   XGCValues gcValues;
392   char cmd[remoteCmdLength];
393   LTKMenu *menu;
394   GString *name;
395   GString *title;
396   unsigned long paperColor;
397   int pg;
398   int x, y;
399   Guint width, height;
400   double width1, height1;
401   GString *zoomStr;
402   GBool ok;
403   char s[20];
404   int i;
405   int ret;
406
407   // initialize
408   app = NULL;
409   win = NULL;
410   out = NULL;
411   remoteAtom = None;
412   doc = NULL;
413   xref = NULL;
414   psFileName = NULL;
415   fileReqDir = getCurrentDir();
416   ret = 0;
417
418   // parse args
419   paperWidth = paperHeight = -1;
420   ok = parseArgs(argDesc, &argc, argv);
421
422   // init error file
423   errorInit();
424
425   // read config file
426   initParams(xpdfConfigFile);
427
428   // create LTKApp (and parse X-related args)
429   app = new LTKApp("xpdf", opts, &argc, argv);
430   app->setKillCbk(&killCbk);
431   display = app->getDisplay();
432
433   // check command line
434   if (doRemoteRaise)
435     ok = ok && remoteName[5] && !doRemoteQuit && argc >= 1 && argc <= 3;
436   else if (doRemoteQuit)
437     ok = ok && remoteName[5] && argc == 1;
438   else
439     ok = ok && argc >= 1 && argc <= 3;
440   if (!ok || printVersion || printHelp) {
441     fprintf(stderr, "xpdf version %s\n", xpdfVersion);
442     fprintf(stderr, "%s\n", xpdfCopyright);
443     if (!printVersion) {
444       printUsage("xpdf", "[<PDF-file> [<page>]]", argDesc);
445     }
446     ret = 1;
447     goto done2;
448   }
449   if (argc >= 2)
450     name = new GString(argv[1]);
451   else
452     name = NULL;
453   if (argc == 3)
454     pg = atoi(argv[2]);
455   else
456     pg = 1;
457
458   // look for already-running remote server
459   if (remoteName[5]) {
460     remoteAtom = XInternAtom(display, remoteName, False);
461     xwin = XGetSelectionOwner(display, remoteAtom);
462     if (xwin != None) {
463       if (name) {
464         sprintf(cmd, "%c %d %.200s", doRemoteRaise ? 'D' : 'd',
465                 pg, name->getCString());
466         XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
467                         PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
468         delete name;
469       } else if (doRemoteRaise) {
470         XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
471                         PropModeReplace, (Guchar *)"r", 2);
472       } else if (doRemoteQuit) {
473         XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
474                         PropModeReplace, (Guchar *)"q", 2);
475       }
476       goto done2;
477     }
478     if (doRemoteQuit)
479       goto done2;
480   }
481
482   // no history yet
483   historyCur = historySize - 1;
484   historyBLen = historyFLen = 0;
485   for (i = 0; i < historySize; ++i)
486     history[i].fileName = NULL;
487
488   // open PDF file
489   defPSFileName = app->getStringResource("psFile", NULL);
490   if (name) {
491     if (!loadFile(name)) {
492       ret = 1;
493       goto done1;
494     }
495     delete fileReqDir;
496     fileReqDir = makePathAbsolute(grabPath(name->getCString()));
497   }
498
499   // check for legal page number
500   if (doc && (pg < 1 || pg > doc->getNumPages()))
501     pg = 1;
502
503   // create window
504   menu = makeMenu();
505   if (fullScreen) {
506     zoomMenu = NULL;
507     win = makeFullScreenWindow(app);
508     win->setDecorated(gFalse);
509   } else {
510     zoomMenu = makeZoomMenu();
511     win = makeWindow(app);
512   }
513   win->setMenu(menu);
514   canvas = (LTKScrollingCanvas *)win->findWidget("canvas");
515   hScrollbar = (LTKScrollbar *)win->findWidget("hScrollbar");
516   vScrollbar = (LTKScrollbar *)win->findWidget("vScrollbar");
517   pageNumText = (LTKTextIn *)win->findWidget("pageNum");
518   numPagesLabel = (LTKLabel *)win->findWidget("numPages");
519   linkLabel = (LTKLabel *)win->findWidget("link");
520   zoomMenuBtn = (LTKMenuButton *)win->findWidget("zoom");
521   win->setKeyCbk(&keyPressCbk);
522   win->setLayoutCbk(&layoutCbk);
523   canvas->setButtonPressCbk(&buttonPressCbk);
524   canvas->setButtonReleaseCbk(&buttonReleaseCbk);
525   canvas->setMouseMoveCbk(&mouseMoveCbk);
526   canvas->setMouseDragCbk(&mouseDragCbk);
527   if (!fullScreen) {
528     hScrollbar->setRepeatPeriod(0);
529     vScrollbar->setRepeatPeriod(0);
530   }
531
532   // get X resources
533   paperWidth = app->getIntResource("psPaperWidth", defPaperWidth);
534   paperHeight = app->getIntResource("psPaperHeight", defPaperHeight);
535   psOutLevel1 = app->getBoolResource("psLevel1", gFalse);
536   urlCommand = app->getStringResource("urlCommand", NULL);
537   windowTitle = app->getStringResource("title", NULL);
538   installCmap = app->getBoolResource("installCmap", gFalse);
539   if (installCmap)
540     win->setInstallCmap(gTrue);
541   rgbCubeSize = app->getIntResource("rgbCubeSize", defaultRGBCube);
542   paperColor = app->getColorResource("paperColor", "white",
543                                      WhitePixel(display, app->getScreenNum()),
544                                      NULL);
545 #if JAPANESE_SUPPORT
546   useEUCJP = app->getBoolResource("eucjp", gFalse);
547 #else
548   useEUCJP = gFalse;
549 #endif
550 #if HAVE_T1LIB_H
551   t1libControl = app->getStringResource("t1libControl", "low");
552 #endif
553 #if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
554   freeTypeControl = app->getStringResource("freeTypeControl", "aa");
555 #endif
556   t1Courier = app->getStringResource("t1Courier", NULL);
557   t1CourierBold = app->getStringResource("t1CourierBold", NULL);
558   t1CourierBoldOblique = app->getStringResource("t1CourierBoldOblique", NULL);
559   t1CourierOblique = app->getStringResource("t1CourierOblique", NULL);
560   t1Helvetica = app->getStringResource("t1Helvetica", NULL);
561   t1HelveticaBold = app->getStringResource("t1HelveticaBold", NULL);
562   t1HelveticaBoldOblique =
563     app->getStringResource("t1HelveticaBoldOblique", NULL);
564   t1HelveticaOblique = app->getStringResource("t1HelveticaOblique", NULL);
565   t1Symbol = app->getStringResource("t1Symbol", NULL);
566   t1TimesBold = app->getStringResource("t1TimesBold", NULL);
567   t1TimesBoldItalic = app->getStringResource("t1TimesBoldItalic", NULL);
568   t1TimesItalic = app->getStringResource("t1TimesItalic", NULL);
569   t1TimesRoman = app->getStringResource("t1TimesRoman", NULL);
570   t1ZapfDingbats = app->getStringResource("t1ZapfDingbats", NULL);
571 #if JAPANESE_SUPPORT
572   japan12Font = app->getStringResource("japaneseFont", NULL);
573 #endif
574 #if CHINESE_GB_SUPPORT
575   gb12Font = app->getStringResource("chineseGBFont", NULL);
576 #endif
577 #if CHINESE_CNS_SUPPORT
578   cns13Font = app->getStringResource("chineseCNSFont", NULL);
579 #endif
580   if (fullScreen) {
581     zoom = zoomPage;
582   } else {
583     zoomStr = app->getStringResource("initialZoom", defZoomStr);
584     if (!zoomStr->cmp("page")) {
585       zoom = zoomPage;
586       i = maxZoom - minZoom + 2;
587     } else if (!zoomStr->cmp("width")) {
588       zoom = zoomWidth;
589       i = maxZoom - minZoom + 3;
590     } else {
591       zoom = atoi(zoomStr->getCString());
592       if (zoom < minZoom)
593         zoom = minZoom;
594       else if (zoom > maxZoom)
595         zoom = maxZoom;
596       i = zoom - minZoom;
597     }
598     delete zoomStr;
599     zoomMenuBtn->setInitialMenuItem(zoomMenu->getItem(i));
600   }
601   viKeys = app->getBoolResource("viKeys", gFalse);
602
603   // get geometry
604   if (fullScreen) {
605     x = y = 0;
606     width = app->getDisplayWidth();
607     height = app->getDisplayHeight();
608   } else {
609     x = y = -1;
610     width = height = 0;
611     app->getGeometryResource("geometry", &x, &y, &width, &height);
612     if (width == 0 || height == 0) {
613       if (!doc || doc->getNumPages() == 0) {
614         width1 = 612;
615         height1 = 792;
616       } else if (doc->getPageRotate(pg) == 90 ||
617                  doc->getPageRotate(pg) == 270) {
618         width1 = doc->getPageHeight(pg);
619         height1 = doc->getPageWidth(pg);
620       } else {
621         width1 = doc->getPageWidth(pg);
622         height1 = doc->getPageHeight(pg);
623       }
624       if (zoom == zoomPage || zoom == zoomWidth) {
625         width = (int)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
626         height = (int)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
627       } else {
628         width = (int)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
629         height = (int)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
630       }
631       width += 28;
632       height += 56;
633       if (width > (Guint)app->getDisplayWidth() - 100) {
634         width = app->getDisplayWidth() - 100;
635       }
636       if (height > (Guint)app->getDisplayHeight() - 100) {
637         height = app->getDisplayHeight() - 100;
638       }
639     }
640   }
641
642   // finish setting up window
643   if (!fullScreen) {
644     sprintf(s, "of %d", doc ? doc->getNumPages() : 0);
645     numPagesLabel->setText(s);
646   }
647   if (windowTitle) {
648     title = windowTitle->copy();
649   } else if (name) {
650     title = new GString("xpdf: ");
651     title->append(name);
652   } else {
653     title = new GString("xpdf");
654   }
655   win->setTitle(title);
656   win->layout(x, y, width, height);
657   win->map();
658   aboutWin = NULL;
659   psDialog = NULL;
660   openDialog = NULL;
661   saveDialog = NULL;
662   findWin = NULL;
663   gcValues.foreground = BlackPixel(display, win->getScreenNum()) ^
664                         WhitePixel(display, win->getScreenNum());
665   gcValues.function = GXxor;
666   selectGC = XCreateGC(display, win->getXWindow(),
667                        GCForeground | GCFunction, &gcValues);
668
669   // set up remote server
670   if (remoteAtom != None) {
671     win->setPropChangeCbk(&propChangeCbk);
672     xwin = win->getXWindow();
673     XSetSelectionOwner(display, remoteAtom, xwin, CurrentTime);
674   }
675
676   // create output device
677   out = new LTKOutputDev(win, paperColor);
678   out->startDoc();
679
680   // display first page
681   displayPage(pg, zoom, 0, gTrue);
682
683   // event loop
684   quit = gFalse;
685   do {
686     app->doEvent(gTrue);
687   } while (!quit);
688
689  done1:
690   // release remote control atom
691   if (remoteAtom != None)
692     XSetSelectionOwner(display, remoteAtom, None, CurrentTime);
693
694  done2:
695   // free stuff
696   if (out) {
697     delete out;
698   }
699   if (win) {
700     delete win;
701   }
702   if (aboutWin) {
703     delete aboutWin;
704   }
705   if (findWin) {
706     delete findWin;
707   }
708   if (app) {
709     delete app;
710   }
711   if (doc) {
712     delete doc;
713   }
714   if (psFileName) {
715     delete psFileName;
716   }
717   if (defPSFileName) {
718     delete defPSFileName;
719   }
720   if (fileReqDir) {
721     delete fileReqDir;
722   }
723   if (urlCommand) {
724     delete urlCommand;
725   }
726   if (windowTitle) {
727     delete windowTitle;
728   }
729 #if HAVE_T1LIB_H
730   if (t1libControl) {
731     delete t1libControl;
732   }
733 #endif
734 #if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
735   if (freeTypeControl) {
736     delete freeTypeControl;
737   }
738 #endif
739   if (t1Courier) {
740     delete t1Courier;
741   }
742   if (t1CourierBold) {
743     delete t1CourierBold;
744   }
745   if (t1CourierBoldOblique) {
746     delete t1CourierBoldOblique;
747   }
748   if (t1CourierOblique) {
749     delete t1CourierOblique;
750   }
751   if (t1Helvetica) {
752     delete t1Helvetica;
753   }
754   if (t1HelveticaBold) {
755     delete t1HelveticaBold;
756   }
757   if (t1HelveticaBoldOblique) {
758     delete t1HelveticaBoldOblique;
759   }
760   if (t1HelveticaOblique) {
761     delete t1HelveticaOblique;
762   }
763   if (t1Symbol) {
764     delete t1Symbol;
765   }
766   if (t1TimesBold) {
767     delete t1TimesBold;
768   }
769   if (t1TimesBoldItalic) {
770     delete t1TimesBoldItalic;
771   }
772   if (t1TimesItalic) {
773     delete t1TimesItalic;
774   }
775   if (t1TimesRoman) {
776     delete t1TimesRoman;
777   }
778   if (t1ZapfDingbats) {
779     delete t1ZapfDingbats;
780   }
781 #if JAPANESE_SUPPORT
782   if (japan12Font) {
783     delete japan12Font;
784   }
785 #endif
786 #if CHINESE_GB_SUPPORT
787   if (gb12Font) {
788     delete gb12Font;
789   }
790 #endif
791 #if CHINESE_CNS_SUPPORT
792   if (cns13Font) {
793     delete cns13Font;
794   }
795 #endif
796   for (i = 0; i < historySize; ++i) {
797     if (history[i].fileName) {
798       delete history[i].fileName;
799     }
800   }
801   freeParams();
802
803   // check for memory leaks
804   Object::memCheck(errFile ? errFile : stderr);
805   gMemReport(errFile ? errFile : stderr);
806
807   return ret;
808 }
809
810 //------------------------------------------------------------------------
811 // loadFile / displayPage
812 //------------------------------------------------------------------------
813
814 static GBool loadFile(GString *fileName) {
815   GString *title;
816   PDFDoc *newDoc;
817   GString *userPW;
818   char s[20];
819   char *p;
820
821   // busy cursor
822   if (win)
823     win->setBusyCursor(gTrue);
824
825   // open PDF file
826   if (userPassword[0]) {
827     userPW = new GString(userPassword);
828   } else {
829     userPW = NULL;
830   }
831   newDoc = new PDFDoc(fileName, userPW);
832   if (userPW) {
833     delete userPW;
834   }
835   if (!newDoc->isOk()) {
836     delete newDoc;
837     if (win)
838       win->setBusyCursor(gFalse);
839     return gFalse;
840   }
841
842   // replace old document
843   if (doc)
844     delete doc;
845   doc = newDoc;
846   if (out)
847     out->startDoc();
848
849   // nothing displayed yet
850   page = -99;
851
852   // save the modification time
853   modTime = getModTime(fileName->getCString());
854
855   // init PostScript output params
856   if (psFileName)
857     delete psFileName;
858   if (defPSFileName) {
859     psFileName = defPSFileName->copy();
860   } else {
861     p = fileName->getCString() + fileName->getLength() - 4;
862     if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF"))
863       psFileName = new GString(fileName->getCString(),
864                                fileName->getLength() - 4);
865     else
866       psFileName = fileName->copy();
867     psFileName->append(".ps");
868   }
869   psFirstPage = 1;
870   psLastPage = doc->getNumPages();
871
872   // set up title, number-of-pages display; back to normal cursor
873   if (win) {
874     if (!windowTitle) {
875       title = new GString("xpdf: ");
876       title->append(fileName);
877       win->setTitle(title);
878     }
879     if (!fullScreen) {
880       sprintf(s, "of %d", doc->getNumPages());
881       numPagesLabel->setText(s);
882     }
883     win->setBusyCursor(gFalse);
884   }
885
886   // done
887   return gTrue;
888 }
889
890 static void displayPage(int page1, int zoom1, int rotate1, GBool addToHist) {
891   time_t modTime1;
892   double hDPI, vDPI, dpi;
893   int rot;
894   char s[20];
895   History *h;
896
897   // check for document
898   if (!doc || doc->getNumPages() == 0)
899     return;
900
901   // check for changes to the file
902   modTime1 = getModTime(doc->getFileName()->getCString());
903   if (modTime1 != modTime) {
904     if (loadFile(doc->getFileName()->copy())) {
905       if (page1 > doc->getNumPages()) {
906         page1 = doc->getNumPages();
907       }
908     }
909     modTime = modTime1;
910   }
911
912   // busy cursor
913   if (win)
914     win->setBusyCursor(gTrue);
915
916   // new page/zoom/rotate values
917   page = page1;
918   zoom = zoom1;
919   rotate = rotate1;
920
921   // initialize mouse-related stuff
922   linkAction = NULL;
923   win->setDefaultCursor();
924   if (!fullScreen) {
925     linkLabel->setText(NULL);
926   }
927   selectXMin = selectXMax = 0;
928   selectYMin = selectYMax = 0;
929   lastDragLeft = lastDragTop = gTrue;
930
931   // draw the page
932   rot = rotate + doc->getPageRotate(page);
933   if (rot >= 360)
934     rot -= 360;
935   else if (rotate < 0)
936     rot += 360;
937   if (fullScreen) {
938     if (rot == 90 || rot == 270) {
939       hDPI = (win->getWidth() / doc->getPageHeight(page)) * 72;
940       vDPI = (win->getHeight() / doc->getPageWidth(page)) * 72;
941     } else {
942       hDPI = (win->getWidth() / doc->getPageWidth(page)) * 72;
943       vDPI = (win->getHeight() / doc->getPageHeight(page)) * 72;
944     }
945     dpi = (hDPI < vDPI) ? hDPI : vDPI;
946   } else if (zoom == zoomPage) {
947     if (rot == 90 || rot == 270) {
948       hDPI = ((win->getWidth() - 28) / doc->getPageHeight(page)) * 72;
949       vDPI = ((win->getHeight() - 56) / doc->getPageWidth(page)) * 72;
950     } else {
951       hDPI = ((win->getWidth() - 28) / doc->getPageWidth(page)) * 72;
952       vDPI = ((win->getHeight() - 56) / doc->getPageHeight(page)) * 72;
953     }
954     dpi = (hDPI < vDPI) ? hDPI : vDPI;
955   } else if (zoom == zoomWidth) {
956     if (rot == 90 || rot == 270) {
957       dpi = ((win->getWidth() - 28) / doc->getPageHeight(page)) * 72;
958     } else {
959       dpi = ((win->getWidth() - 28) / doc->getPageWidth(page)) * 72;
960     }
961   } else {
962     dpi = zoomDPI[zoom - minZoom];
963   }
964   doc->displayPage(out, page, dpi, rotate, gTrue);
965   updateScrollbars();
966
967   // update page number display
968   if (!fullScreen) {
969     sprintf(s, "%d", page);
970     pageNumText->setText(s);
971   }
972
973   // add to history
974   if (addToHist) {
975     if (++historyCur == historySize)
976       historyCur = 0;
977     h = &history[historyCur];
978     if (h->fileName)
979       delete h->fileName;
980     h->fileName = doc->getFileName()->copy();
981     h->page = page;
982     if (historyBLen < historySize)
983       ++historyBLen;
984     historyFLen = 0;
985   }
986
987   // back to regular cursor
988   win->setBusyCursor(gFalse);
989 }
990
991 //------------------------------------------------------------------------
992 // key press and menu callbacks
993 //------------------------------------------------------------------------
994
995 static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
996                         char *s, int n) {
997   if (n > 0) {
998     switch (s[0]) {
999     case 'O':
1000     case 'o':
1001       mapOpenDialog();
1002       break;
1003     case 'R':
1004     case 'r':
1005       reloadCbk();
1006       break;
1007     case 'F':
1008     case 'f':
1009       mapFindWin();
1010       break;
1011     case 'N':
1012     case 'n':
1013       gotoNextPage(1, !(modifiers & Mod5Mask));
1014       break;
1015     case 'P':
1016     case 'p':
1017       gotoPrevPage(1, !(modifiers & Mod5Mask), gFalse);
1018       break;
1019     case ' ':
1020       if (fullScreen ||
1021           vScrollbar->getPos() >=
1022             canvas->getRealHeight() - canvas->getHeight()) {
1023         gotoNextPage(1, gTrue);
1024       } else {
1025         vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(),
1026                            canvas->getHeight());
1027         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1028       }
1029       break;
1030     case '\b':                  // bs
1031     case '\177':                // del
1032       if (fullScreen) {
1033         gotoPrevPage(1, gTrue, gFalse);
1034       } else if (vScrollbar->getPos() == 0) {
1035         gotoPrevPage(1, gFalse, gTrue);
1036       } else {
1037         vScrollbar->setPos(vScrollbar->getPos() - canvas->getHeight(),
1038                            canvas->getHeight());
1039         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1040       }
1041       break;
1042     case 'h':                   // vi-style left
1043       if (fullScreen) {
1044         break;
1045       }
1046       if (viKeys) {
1047         hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
1048         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1049       }
1050       break;
1051     case 'l':                   // vi-style right
1052       if (fullScreen) {
1053         break;
1054       }
1055       if (viKeys) {
1056         hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
1057         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1058       }
1059       break;
1060     case 'k':                   // vi-style up
1061       if (fullScreen) {
1062         break;
1063       }
1064       if (viKeys) {
1065         vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
1066         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1067       }
1068       break;
1069     case 'j':                   // vi-style down
1070       if (fullScreen) {
1071         break;
1072       }
1073       if (viKeys) {
1074         vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
1075         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1076       }
1077       break;
1078     case '\014':                // ^L
1079       win->redraw();
1080       displayPage(page, zoom, rotate, gFalse);
1081       break;
1082     case 'Q':
1083     case 'q':
1084       quitCbk(NULL, 0, gTrue);
1085       break;
1086     }
1087   } else {
1088     switch (key) {
1089     case XK_Home:
1090     case XK_KP_Home:
1091       if (fullScreen) {
1092         break;
1093       }
1094       hScrollbar->setPos(0, canvas->getWidth());
1095       vScrollbar->setPos(0, canvas->getHeight());
1096       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1097       break;
1098     case XK_End:
1099     case XK_KP_End:
1100       if (fullScreen) {
1101         break;
1102       }
1103       hScrollbar->setPos(canvas->getRealWidth() - canvas->getWidth(),
1104                          canvas->getWidth());
1105       vScrollbar->setPos(canvas->getRealHeight() - canvas->getHeight(),
1106                          canvas->getHeight());
1107       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1108       break;
1109     case XK_Page_Up:
1110     case XK_KP_Page_Up:
1111       if (fullScreen) {
1112         gotoPrevPage(1, gTrue, gFalse);
1113       } else if (vScrollbar->getPos() == 0) {
1114         gotoPrevPage(1, gFalse, gTrue);
1115       } else {
1116         vScrollbar->setPos(vScrollbar->getPos() - canvas->getHeight(),
1117                            canvas->getHeight());
1118         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1119       }
1120       break;
1121     case XK_Page_Down:
1122     case XK_KP_Page_Down:
1123       if (fullScreen ||
1124           vScrollbar->getPos() >=
1125             canvas->getRealHeight() - canvas->getHeight()) {
1126         gotoNextPage(1, gTrue);
1127       } else {
1128         vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(),
1129                            canvas->getHeight());
1130         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1131       }
1132       break;
1133     case XK_Left:
1134     case XK_KP_Left:
1135       if (fullScreen) {
1136         break;
1137       }
1138       hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
1139       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1140       break;
1141     case XK_Right:
1142     case XK_KP_Right:
1143       if (fullScreen) {
1144         break;
1145       }
1146       hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
1147       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1148       break;
1149     case XK_Up:
1150     case XK_KP_Up:
1151       if (fullScreen) {
1152         break;
1153       }
1154       vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
1155       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1156       break;
1157     case XK_Down:
1158     case XK_KP_Down:
1159       if (fullScreen) {
1160         break;
1161       }
1162       vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
1163       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1164       break;
1165     }
1166   }
1167 }
1168
1169 static void menuCbk(LTKMenuItem *item) {
1170   int r;
1171
1172   switch (item->getItemNum()) {
1173   case menuOpen:
1174     mapOpenDialog();
1175     break;
1176   case menuReload:
1177     reloadCbk();
1178     break;
1179   case menuSavePDF:
1180     if (doc)
1181       mapSaveDialog();
1182     break;
1183   case menuRotateLeft:
1184     if (doc) {
1185       r = (rotate == 0) ? 270 : rotate - 90;
1186       displayPage(page, zoom, r, gFalse);
1187     }
1188     break;
1189   case menuRotateRight:
1190     if (doc) {
1191       r = (rotate == 270) ? 0 : rotate + 90;
1192       displayPage(page, zoom, r, gFalse);
1193     }
1194     break;
1195   case menuQuit:
1196     quit = gTrue;
1197     break;
1198   }
1199 }
1200
1201 //------------------------------------------------------------------------
1202 // mouse callbacks
1203 //------------------------------------------------------------------------
1204
1205 static void buttonPressCbk(LTKWidget *canvas1, int n,
1206                            int mx, int my, int button, GBool dblClick) {
1207   if (!doc || doc->getNumPages() == 0)
1208     return;
1209   if (button == 1) {
1210     setSelection(mx, my, mx, my);
1211   } else if (!fullScreen && button == 2) {
1212     panMX = mx - hScrollbar->getPos();
1213     panMY = my - vScrollbar->getPos();
1214   }
1215 }
1216
1217 static void buttonReleaseCbk(LTKWidget *canvas1, int n,
1218                              int mx, int my, int button, GBool click) {
1219   GString *s;
1220
1221   if (!doc || doc->getNumPages() == 0)
1222     return;
1223
1224   if (button == 1) {
1225     // selection
1226     if (selectXMin < selectXMax && selectYMin < selectYMax) {
1227 #ifndef NO_TEXT_SELECT
1228       if (doc->okToCopy()) {
1229         s = out->getText(selectXMin, selectYMin, selectXMax, selectYMax);
1230         win->setSelection(NULL, s);
1231       } else {
1232         error(-1, "Copying of text from this document is not allowed.");
1233       }
1234 #endif
1235
1236     // link
1237     } else {
1238       setSelection(mx, my, mx, my);
1239       doLink(mx, my);
1240     }
1241   }
1242 }
1243
1244 static void doLink(int mx, int my) {
1245   LinkActionKind kind;
1246   LinkAction *action = NULL;
1247   LinkDest *dest;
1248   GString *namedDest;
1249   char *s;
1250   GString *fileName;
1251   GString *actionName;
1252   Ref pageRef;
1253   int pg;
1254   double x, y;
1255   int dx, dy;
1256   LTKButtonDialog *dialog;
1257   int i;
1258
1259   // look for a link
1260   out->cvtDevToUser(mx, my, &x, &y);
1261   if ((action = doc->findLink(x, y))) {
1262     switch (kind = action->getKind()) {
1263
1264     // GoTo / GoToR action
1265     case actionGoTo:
1266     case actionGoToR:
1267       if (kind == actionGoTo) {
1268         dest = NULL;
1269         namedDest = NULL;
1270         if ((dest = ((LinkGoTo *)action)->getDest()))
1271           dest = dest->copy();
1272         else if ((namedDest = ((LinkGoTo *)action)->getNamedDest()))
1273           namedDest = namedDest->copy();
1274       } else {
1275         dest = NULL;
1276         namedDest = NULL;
1277         if ((dest = ((LinkGoToR *)action)->getDest()))
1278           dest = dest->copy();
1279         else if ((namedDest = ((LinkGoToR *)action)->getNamedDest()))
1280           namedDest = namedDest->copy();
1281         s = ((LinkGoToR *)action)->getFileName()->getCString();
1282         //~ translate path name for VMS (deal with '/')
1283         if (isAbsolutePath(s))
1284           fileName = new GString(s);
1285         else
1286           fileName = appendToPath(
1287                          grabPath(doc->getFileName()->getCString()), s);
1288         if (!loadFile(fileName)) {
1289           if (dest)
1290             delete dest;
1291           if (namedDest)
1292             delete namedDest;
1293           return;
1294         }
1295       }
1296       if (namedDest) {
1297         dest = doc->findDest(namedDest);
1298         delete namedDest;
1299       }
1300       if (!dest) {
1301         if (kind == actionGoToR)
1302           displayPage(1, zoom, 0, gTrue);
1303       } else {
1304         if (dest->isPageRef()) {
1305           pageRef = dest->getPageRef();
1306           pg = doc->findPage(pageRef.num, pageRef.gen);
1307         } else {
1308           pg = dest->getPageNum();
1309         }
1310         if (pg > 0 && pg != page)
1311           displayPage(pg, zoom, rotate, gTrue);
1312         else if (pg <= 0)
1313           displayPage(1, zoom, rotate, gTrue);
1314         if (fullScreen) {
1315           delete dest;
1316           break;
1317         }
1318         switch (dest->getKind()) {
1319         case destXYZ:
1320           out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
1321           if (dest->getChangeLeft() || dest->getChangeTop()) {
1322             if (dest->getChangeLeft())
1323               hScrollbar->setPos(dx, canvas->getWidth());
1324             if (dest->getChangeTop())
1325               vScrollbar->setPos(dy, canvas->getHeight());
1326             canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1327           }
1328           //~ what is the zoom parameter?
1329           break;
1330         case destFit:
1331         case destFitB:
1332           //~ do fit
1333           hScrollbar->setPos(0, canvas->getWidth());
1334           vScrollbar->setPos(0, canvas->getHeight());
1335           canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1336           break;
1337         case destFitH:
1338         case destFitBH:
1339           //~ do fit
1340           out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
1341           hScrollbar->setPos(0, canvas->getWidth());
1342           vScrollbar->setPos(dy, canvas->getHeight());
1343           canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1344           break;
1345         case destFitV:
1346         case destFitBV:
1347           //~ do fit
1348           out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
1349           hScrollbar->setPos(dx, canvas->getWidth());
1350           vScrollbar->setPos(0, canvas->getHeight());
1351           canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1352           break;
1353         case destFitR:
1354           //~ do fit
1355           out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
1356           hScrollbar->setPos(dx, canvas->getWidth());
1357           vScrollbar->setPos(dy, canvas->getHeight());
1358           canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1359           break;
1360         }
1361         delete dest;
1362       }
1363       break;
1364
1365     // Launch action
1366     case actionLaunch:
1367       fileName = ((LinkLaunch *)action)->getFileName();
1368       s = fileName->getCString();
1369       if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
1370           !strcmp(s + fileName->getLength() - 4, ".PDF")) {
1371         //~ translate path name for VMS (deal with '/')
1372         if (isAbsolutePath(s))
1373           fileName = fileName->copy();
1374         else
1375           fileName = appendToPath(
1376                          grabPath(doc->getFileName()->getCString()), s);
1377         if (!loadFile(fileName))
1378           return;
1379         displayPage(1, zoom, rotate, gTrue);
1380       } else {
1381         fileName = fileName->copy();
1382         if (((LinkLaunch *)action)->getParams()) {
1383           fileName->append(' ');
1384           fileName->append(((LinkLaunch *)action)->getParams());
1385         }
1386 #ifdef VMS
1387         fileName->insert(0, "spawn/nowait ");
1388 #elif defined(__EMX__)
1389         fileName->insert(0, "start /min /n ");
1390 #else
1391         fileName->append(" &");
1392 #endif
1393         dialog = new LTKButtonDialog(win, "xpdf: Launch",
1394                                      "Execute the command:",
1395                                      fileName->getCString(),
1396                                      NULL, "Ok", "Cancel");
1397         if (dialog->go())
1398           system(fileName->getCString());
1399         delete dialog;
1400         delete fileName;
1401       }
1402       break;
1403
1404     // URI action
1405     case actionURI:
1406       if (urlCommand) {
1407         for (s = urlCommand->getCString(); *s; ++s) {
1408           if (s[0] == '%' && s[1] == 's') {
1409             break;
1410           }
1411         }
1412         if (s) {
1413           fileName = ((LinkURI *)action)->getURI()->copy();
1414           // filter out any quote marks (' or ") to avoid a potential
1415           // security hole
1416           i = 0;
1417           while (i < fileName->getLength()) {
1418             if (fileName->getChar(i) == '"') {
1419               fileName->del(i);
1420               fileName->insert(i, "%22");
1421               i += 3;
1422             } else if (fileName->getChar(i) == '\'') {
1423               fileName->del(i);
1424               fileName->insert(i, "%27");
1425               i += 3;
1426             } else {
1427               ++i;
1428             }
1429           }
1430           fileName->insert(0, urlCommand->getCString(),
1431                            s - urlCommand->getCString());
1432           fileName->append(s+2);
1433         } else {
1434           fileName = urlCommand->copy();
1435         }
1436 #ifdef VMS
1437         fileName->insert(0, "spawn/nowait ");
1438 #elif defined(__EMX__)
1439         fileName->insert(0, "start /min /n ");
1440 #else
1441         fileName->append(" &");
1442 #endif
1443         system(fileName->getCString());
1444         delete fileName;
1445       } else {
1446         fprintf(errFile, "URI: %s\n",
1447                 ((LinkURI *)action)->getURI()->getCString());
1448       }
1449       break;
1450
1451     // Named action
1452     case actionNamed:
1453       actionName = ((LinkNamed *)action)->getName();
1454       if (!actionName->cmp("NextPage")) {
1455         gotoNextPage(1, gTrue);
1456       } else if (!actionName->cmp("PrevPage")) {
1457         gotoPrevPage(1, gTrue, gFalse);
1458       } else if (!actionName->cmp("FirstPage")) {
1459         if (page != 1) {
1460           displayPage(1, zoom, rotate, gTrue);
1461         }
1462       } else if (!actionName->cmp("LastPage")) {
1463         if (page != doc->getNumPages()) {
1464           displayPage(doc->getNumPages(), zoom, rotate, gTrue);
1465         }
1466       } else if (!actionName->cmp("GoBack")) {
1467         backCbk(NULL, 0, gTrue);
1468       } else if (!actionName->cmp("GoForward")) {
1469         forwardCbk(NULL, 0, gTrue);
1470       } else if (!actionName->cmp("Quit")) {
1471         quitCbk(NULL, 0, gTrue);
1472       } else {
1473         error(-1, "Unknown named action: '%s'", actionName->getCString());
1474       }
1475       break;
1476
1477     // unknown action type
1478     case actionUnknown:
1479       error(-1, "Unknown link action type: '%s'",
1480             ((LinkUnknown *)action)->getAction()->getCString());
1481       break;
1482     }
1483   }
1484 }
1485
1486 static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my) {
1487   double x, y;
1488   LinkAction *action;
1489   char *s;
1490
1491   if (!doc || doc->getNumPages() == 0)
1492     return;
1493   out->cvtDevToUser(mx, my, &x, &y);
1494   if ((action = doc->findLink(x, y))) {
1495     if (action != linkAction) {
1496       if (!linkAction) {
1497         win->setCursor(XC_hand2);
1498       }
1499       linkAction = action;
1500       if (!fullScreen) {
1501         s = NULL;
1502         switch (linkAction->getKind()) {
1503         case actionGoTo:
1504           s = "[internal link]";
1505           break;
1506         case actionGoToR:
1507           s = ((LinkGoToR *)linkAction)->getFileName()->getCString();
1508           break;
1509         case actionLaunch:
1510           s = ((LinkLaunch *)linkAction)->getFileName()->getCString();
1511           break;
1512         case actionURI:
1513           s = ((LinkURI *)action)->getURI()->getCString();
1514           break;
1515         case actionNamed:
1516           s = ((LinkNamed *)linkAction)->getName()->getCString();
1517           break;
1518         case actionUnknown:
1519           s = "[unknown link]";
1520           break;
1521         }
1522         linkLabel->setText(s);
1523       }
1524     }
1525   } else {
1526     if (linkAction) {
1527       linkAction = NULL;
1528       win->setDefaultCursor();
1529       if (!fullScreen) {
1530         linkLabel->setText(NULL);
1531       }
1532     }
1533   }
1534 }
1535
1536 static void mouseDragCbk(LTKWidget *widget, int widgetNum,
1537                          int mx, int my, int button) {
1538   int x, y;
1539   int xMin, yMin, xMax, yMax;
1540
1541   // button 1: select
1542   if (button == 1) {
1543
1544     // clip mouse coords
1545     x = mx;
1546     if (x < 0)
1547       x = 0;
1548     else if (x >= canvas->getRealWidth())
1549       x = canvas->getRealWidth() - 1;
1550     y = my;
1551     if (y < 0)
1552       y = 0;
1553     else if (y >= canvas->getRealHeight())
1554       y = canvas->getRealHeight() - 1;
1555
1556     // move appropriate edges of selection
1557     if (lastDragLeft) {
1558       if (x < selectXMax) {
1559         xMin = x;
1560         xMax = selectXMax;
1561       } else {
1562         xMin = selectXMax;
1563         xMax = x;
1564         lastDragLeft = gFalse;
1565       }      
1566     } else {
1567       if (x > selectXMin) {
1568         xMin = selectXMin;
1569         xMax = x;
1570       } else {
1571         xMin = x;
1572         xMax = selectXMin;
1573         lastDragLeft = gTrue;
1574       }
1575     }
1576     if (lastDragTop) {
1577       if (y < selectYMax) {
1578         yMin = y;
1579         yMax = selectYMax;
1580       } else {
1581         yMin = selectYMax;
1582         yMax = y;
1583         lastDragTop = gFalse;
1584       }
1585     } else {
1586       if (y > selectYMin) {
1587         yMin = selectYMin;
1588         yMax = y;
1589       } else {
1590         yMin = y;
1591         yMax = selectYMin;
1592         lastDragTop = gTrue;
1593       }
1594     }
1595
1596     // redraw the selection
1597     setSelection(xMin, yMin, xMax, yMax);
1598
1599   // button 2: pan
1600   } else if (!fullScreen && button == 2) {
1601     mx -= hScrollbar->getPos();
1602     my -= vScrollbar->getPos();
1603     hScrollbar->setPos(hScrollbar->getPos() - (mx - panMX),
1604                        canvas->getWidth());
1605     vScrollbar->setPos(vScrollbar->getPos() - (my - panMY),
1606                        canvas->getHeight());
1607     canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1608     panMX = mx;
1609     panMY = my;
1610   }
1611 }
1612
1613 //------------------------------------------------------------------------
1614 // button callbacks
1615 //------------------------------------------------------------------------
1616
1617 static void nextPageCbk(LTKWidget *button, int n, GBool on) {
1618   gotoNextPage(1, gTrue);
1619 }
1620
1621 static void nextTenPageCbk(LTKWidget *button, int n, GBool on) {
1622   gotoNextPage(10, gTrue);
1623 }
1624
1625 static void gotoNextPage(int inc, GBool top) {
1626   int pg;
1627
1628   if (!doc || doc->getNumPages() == 0) {
1629     return;
1630   }
1631   if (page < doc->getNumPages()) {
1632     if (top && !fullScreen) {
1633       vScrollbar->setPos(0, canvas->getHeight());
1634       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1635     }
1636     if ((pg = page + inc) > doc->getNumPages()) {
1637       pg = doc->getNumPages();
1638     }
1639     displayPage(pg, zoom, rotate, gTrue);
1640   } else {
1641     XBell(display, 0);
1642   }
1643 }
1644
1645 static void prevPageCbk(LTKWidget *button, int n, GBool on) {
1646   gotoPrevPage(1, gTrue, gFalse);
1647 }
1648
1649 static void prevTenPageCbk(LTKWidget *button, int n, GBool on) {
1650   gotoPrevPage(10, gTrue, gFalse);
1651 }
1652
1653 static void gotoPrevPage(int dec, GBool top, GBool bottom) {
1654   int pg;
1655
1656   if (!doc || doc->getNumPages() == 0) {
1657     return;
1658   }
1659   if (page > 1) {
1660     if (top && !fullScreen) {
1661       vScrollbar->setPos(0, canvas->getHeight());
1662       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1663     } else if (bottom && !fullScreen) {
1664       vScrollbar->setPos(canvas->getRealHeight() - canvas->getHeight(),
1665                          canvas->getHeight());
1666       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1667     }
1668     if ((pg = page - dec) < 1) {
1669       pg = 1;
1670     }
1671     displayPage(pg, zoom, rotate, gTrue);
1672   } else {
1673     XBell(display, 0);
1674   }
1675 }
1676
1677 static void backCbk(LTKWidget *button, int n, GBool on) {
1678   if (historyBLen <= 1) {
1679     XBell(display, 0);
1680     return;
1681   }
1682   if (--historyCur < 0)
1683     historyCur = historySize - 1;
1684   --historyBLen;
1685   ++historyFLen;
1686   if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
1687     if (!loadFile(history[historyCur].fileName->copy())) {
1688       XBell(display, 0);
1689       return;
1690     }
1691   }
1692   displayPage(history[historyCur].page, zoom, rotate, gFalse);
1693 }
1694
1695 static void forwardCbk(LTKWidget *button, int n, GBool on) {
1696   if (historyFLen == 0) {
1697     XBell(display, 0);
1698     return;
1699   }
1700   if (++historyCur == historySize)
1701     historyCur = 0;
1702   --historyFLen;
1703   ++historyBLen;
1704   if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
1705     if (!loadFile(history[historyCur].fileName->copy())) {
1706       XBell(display, 0);
1707       return;
1708     }
1709   }
1710   displayPage(history[historyCur].page, zoom, rotate, gFalse);
1711 }
1712
1713 static void pageNumCbk(LTKWidget *textIn, int n, GString *text) {
1714   int page1;
1715   char s[20];
1716
1717   if (!doc || doc->getNumPages() == 0)
1718     return;
1719   page1 = atoi(text->getCString());
1720   if (page1 >= 1 && page1 <= doc->getNumPages()) {
1721     if (page1 != page)
1722       displayPage(page1, zoom, rotate, gTrue);
1723   } else {
1724     XBell(display, 0);
1725     sprintf(s, "%d", page);
1726     pageNumText->setText(s);
1727   }
1728 }
1729
1730 static void zoomMenuCbk(LTKMenuItem *item) {
1731   int z;
1732
1733   if (!doc || doc->getNumPages() == 0) {
1734     return;
1735   }
1736   z = item->getItemNum();
1737   if (z != zoom) {
1738     displayPage(page, z, rotate, gFalse);
1739   }
1740 }
1741
1742 static void postScriptCbk(LTKWidget *button, int n, GBool on) {
1743   if (!doc)
1744     return;
1745   mapPSDialog();
1746 }
1747
1748 static void aboutCbk(LTKWidget *button, int n, GBool on) {
1749   mapAboutWin();
1750 }
1751
1752 static void quitCbk(LTKWidget *button, int n, GBool on) {
1753   quit = gTrue;
1754 }
1755
1756 //------------------------------------------------------------------------
1757 // scrollbar callbacks
1758 //------------------------------------------------------------------------
1759
1760 static void scrollVertCbk(LTKWidget *scrollbar, int n, int val) {
1761   canvas->scroll(hScrollbar->getPos(), val);
1762   XSync(display, False);
1763 }
1764
1765 static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val) {
1766   canvas->scroll(val, vScrollbar->getPos());
1767   XSync(display, False);
1768 }
1769
1770 //------------------------------------------------------------------------
1771 // misc callbacks
1772 //------------------------------------------------------------------------
1773
1774 static void layoutCbk(LTKWindow *win1) {
1775   if (page >= 0 && (zoom == zoomPage || zoom == zoomWidth)) {
1776     displayPage(page, zoom, rotate, gFalse);
1777   } else {
1778     updateScrollbars();
1779   }
1780 }
1781
1782 static void updateScrollbars() {
1783   if (fullScreen) {
1784     return;
1785   }
1786   hScrollbar->setLimits(0, canvas->getRealWidth() - 1);
1787   hScrollbar->setPos(hScrollbar->getPos(), canvas->getWidth());
1788   hScrollbar->setScrollDelta(16);
1789   vScrollbar->setLimits(0, canvas->getRealHeight() - 1);
1790   vScrollbar->setPos(vScrollbar->getPos(), canvas->getHeight());
1791   vScrollbar->setScrollDelta(16);
1792   canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1793 }
1794
1795 static void propChangeCbk(LTKWindow *win1, Atom atom) {
1796   Window xwin;
1797   char *cmd;
1798   Atom type;
1799   int format;
1800   Gulong size, remain;
1801   char *p;
1802   GString *newFileName;
1803   int newPage;
1804
1805   // get command
1806   xwin = win1->getXWindow();
1807   if (XGetWindowProperty(display, xwin, remoteAtom,
1808                          0, remoteCmdLength/4, True, remoteAtom,
1809                          &type, &format, &size, &remain,
1810                          (Guchar **)&cmd) != Success)
1811     return;
1812   if (size == 0)
1813     return;
1814
1815   // raise window
1816   if (cmd[0] == 'D' || cmd[0] == 'r'){
1817     win->raise();
1818     XFlush(display);
1819   }
1820
1821   // display file / page
1822   if (cmd[0] == 'd' || cmd[0] == 'D') {
1823     p = cmd + 2;
1824     newPage = atoi(p);
1825     if (!(p = strchr(p, ' ')))
1826       return;
1827     newFileName = new GString(p + 1);
1828     XFree((XPointer)cmd);
1829     if (!doc || newFileName->cmp(doc->getFileName())) {
1830       if (!loadFile(newFileName))
1831         return;
1832     } else {
1833       delete newFileName;
1834     }
1835     if (newPage != page && newPage >= 1 && newPage <= doc->getNumPages())
1836       displayPage(newPage, zoom, rotate, gTrue);
1837
1838   // quit
1839   } else if (cmd[0] == 'q') {
1840     quit = gTrue;
1841   }
1842 }
1843
1844 //------------------------------------------------------------------------
1845 // selection
1846 //------------------------------------------------------------------------
1847
1848 static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax) {
1849   int x, y, w, h;
1850   GBool needRedraw, needScroll;
1851   GBool moveLeft, moveRight, moveTop, moveBottom;
1852
1853   // erase old selection on canvas pixmap
1854   needRedraw = gFalse;
1855   if (selectXMin < selectXMax && selectYMin < selectYMax) {
1856     XFillRectangle(canvas->getDisplay(), canvas->getPixmap(),
1857                    selectGC, selectXMin, selectYMin,
1858                    selectXMax - selectXMin, selectYMax - selectYMin);
1859     needRedraw = gTrue;
1860   }
1861
1862   // draw new selection on canvas pixmap
1863   if (newXMin < newXMax && newYMin < newYMax) {
1864     XFillRectangle(canvas->getDisplay(), canvas->getPixmap(),
1865                    selectGC, newXMin, newYMin,
1866                    newXMax - newXMin, newYMax - newYMin);
1867     needRedraw = gTrue;
1868   }
1869
1870   // check which edges moved
1871   moveLeft = newXMin != selectXMin;
1872   moveTop = newYMin != selectYMin;
1873   moveRight = newXMax != selectXMax;
1874   moveBottom = newYMax != selectYMax;
1875
1876   // redraw currently visible part of canvas
1877   if (needRedraw) {
1878     if (moveLeft) {
1879       canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin,
1880                          (newYMin < selectYMin) ? newYMin : selectYMin,
1881                          (newXMin > selectXMin) ? newXMin : selectXMin,
1882                          (newYMax > selectYMax) ? newYMax : selectYMax);
1883     }
1884     if (moveRight) {
1885       canvas->redrawRect((newXMax < selectXMax) ? newXMax : selectXMax,
1886                          (newYMin < selectYMin) ? newYMin : selectYMin,
1887                          (newXMax > selectXMax) ? newXMax : selectXMax,
1888                          (newYMax > selectYMax) ? newYMax : selectYMax);
1889     }
1890     if (moveTop) {
1891       canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin,
1892                          (newYMin < selectYMin) ? newYMin : selectYMin,
1893                          (newXMax > selectXMax) ? newXMax : selectXMax,
1894                          (newYMin > selectYMin) ? newYMin : selectYMin);
1895     }
1896     if (moveBottom) {
1897       canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin,
1898                          (newYMax < selectYMax) ? newYMax : selectYMax,
1899                          (newXMax > selectXMax) ? newXMax : selectXMax,
1900                          (newYMax > selectYMax) ? newYMax : selectYMax);
1901     }
1902   }
1903
1904   // switch to new selection coords
1905   selectXMin = newXMin;
1906   selectXMax = newXMax;
1907   selectYMin = newYMin;
1908   selectYMax = newYMax;
1909
1910   // scroll canvas if necessary
1911   if (fullScreen) {
1912     return;
1913   }
1914   needScroll = gFalse;
1915   w = canvas->getWidth();
1916   h = canvas->getHeight();
1917   x = hScrollbar->getPos();
1918   y = vScrollbar->getPos();
1919   if (moveLeft && selectXMin < x) {
1920     x = selectXMin;
1921     needScroll = gTrue;
1922   } else if (moveRight && selectXMax >= x + w) {
1923     x = selectXMax - w;
1924     needScroll = gTrue;
1925   } else if (moveLeft && selectXMin >= x + w) {
1926     x = selectXMin - w;
1927     needScroll = gTrue;
1928   } else if (moveRight && selectXMax < x) {
1929     x = selectXMax;
1930     needScroll = gTrue;
1931   }
1932   if (moveTop && selectYMin < y) {
1933     y = selectYMin;
1934     needScroll = gTrue;
1935   } else if (moveBottom && selectYMax >= y + h) {
1936     y = selectYMax - h;
1937     needScroll = gTrue;
1938   } else if (moveTop && selectYMin >= y + h) {
1939     y = selectYMin - h;
1940     needScroll = gTrue;
1941   } else if (moveBottom && selectYMax < y) {
1942     y = selectYMax;
1943     needScroll = gTrue;
1944   }
1945   if (needScroll) {
1946     hScrollbar->setPos(x, w);
1947     vScrollbar->setPos(y, h);
1948     canvas->scroll(x, y);
1949   }
1950 }
1951
1952 //------------------------------------------------------------------------
1953 // "Open" dialog
1954 //------------------------------------------------------------------------
1955
1956 static void mapOpenDialog() {
1957   openDialog = makeOpenDialog(app);
1958   ((LTKFileReq *)openDialog->findWidget("fileReq"))->setDir(fileReqDir);
1959   openDialog->layoutDialog(win, -1, -1);
1960   openDialog->map();
1961 }
1962
1963 static void openButtonCbk(LTKWidget *button, int n, GBool on) {
1964   LTKFileReq *fileReq;
1965   GString *sel;
1966
1967   sel = NULL;
1968   if (n == 1) {
1969     fileReq = (LTKFileReq *)openDialog->findWidget("fileReq");
1970     if (!(sel = fileReq->getSelection())) {
1971       return;
1972     }
1973     openSelectCbk(fileReq, 0, sel);
1974   }
1975   if (openDialog) {
1976     if (sel) {
1977       delete fileReqDir;
1978       fileReqDir = ((LTKFileReq *)openDialog->findWidget("fileReq"))->getDir();
1979     }
1980     delete openDialog;
1981     openDialog = NULL;
1982   }
1983 }
1984
1985 static void openSelectCbk(LTKWidget *widget, int n, GString *name) {
1986   GString *name1;
1987
1988   name1 = name->copy();
1989   if (openDialog) {
1990     delete fileReqDir;
1991     fileReqDir = ((LTKFileReq *)openDialog->findWidget("fileReq"))->getDir();
1992     delete openDialog;
1993     openDialog = NULL;
1994   }
1995   if (loadFile(name1)) {
1996     vScrollbar->setPos(0, canvas->getHeight());
1997     canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
1998     displayPage(1, zoom, rotate, gTrue);
1999   }
2000 }
2001
2002 //------------------------------------------------------------------------
2003 // "Reload"
2004 //------------------------------------------------------------------------
2005
2006 static void reloadCbk() {
2007   int pg;
2008
2009   if (!doc) {
2010     return;
2011   }
2012   pg = page;
2013   if (loadFile(doc->getFileName()->copy())) {
2014     if (pg > doc->getNumPages()) {
2015       pg = doc->getNumPages();
2016     }
2017     displayPage(pg, zoom, rotate, gFalse);
2018   }
2019 }
2020
2021 //------------------------------------------------------------------------
2022 // "Save PDF" dialog
2023 //------------------------------------------------------------------------
2024
2025 static void mapSaveDialog() {
2026   saveDialog = makeSaveDialog(app);
2027   ((LTKFileReq *)saveDialog->findWidget("fileReq"))->setDir(fileReqDir);
2028   saveDialog->layoutDialog(win, -1, -1);
2029   saveDialog->map();
2030 }
2031
2032 static void saveButtonCbk(LTKWidget *button, int n, GBool on) {
2033   LTKFileReq *fileReq;
2034   GString *sel;
2035
2036   if (!doc)
2037     return;
2038   sel = NULL;
2039   if (n == 1) {
2040     fileReq = (LTKFileReq *)saveDialog->findWidget("fileReq");
2041     if (!(sel = fileReq->getSelection())) {
2042       return;
2043     }
2044     saveSelectCbk(fileReq, 0, sel);
2045   }
2046   if (saveDialog) {
2047     if (sel) {
2048       delete fileReqDir;
2049       fileReqDir = ((LTKFileReq *)saveDialog->findWidget("fileReq"))->getDir();
2050     }
2051     delete saveDialog;
2052     saveDialog = NULL;
2053   }
2054 }
2055
2056 static void saveSelectCbk(LTKWidget *widget, int n, GString *name) {
2057   GString *name1;
2058
2059   name1 = name->copy();
2060   if (saveDialog) {
2061     delete fileReqDir;
2062     fileReqDir = ((LTKFileReq *)saveDialog->findWidget("fileReq"))->getDir();
2063     delete saveDialog;
2064     saveDialog = NULL;
2065   }
2066   win->setBusyCursor(gTrue);
2067   doc->saveAs(name1);
2068   delete name1;
2069   win->setBusyCursor(gFalse);
2070 }
2071
2072 //------------------------------------------------------------------------
2073 // "PostScript" dialog
2074 //------------------------------------------------------------------------
2075
2076 static void mapPSDialog() {
2077   LTKTextIn *widget;
2078   char s[20];
2079
2080   psDialog = makePostScriptDialog(app);
2081   sprintf(s, "%d", psFirstPage);
2082   widget = (LTKTextIn *)psDialog->findWidget("firstPage");
2083   widget->setText(s);
2084   sprintf(s, "%d", psLastPage);
2085   widget = (LTKTextIn *)psDialog->findWidget("lastPage");
2086   widget->setText(s);
2087   widget = (LTKTextIn *)psDialog->findWidget("fileName");
2088   widget->setText(psFileName->getCString());
2089   psDialog->layoutDialog(win, -1, -1);
2090   psDialog->map();
2091 }
2092
2093 static void psButtonCbk(LTKWidget *button, int n, GBool on) {
2094   PSOutputDev *psOut;
2095   LTKTextIn *widget;
2096
2097   if (!doc)
2098     return;
2099
2100   // "Ok" button
2101   if (n == 1) {
2102     // extract params and close the dialog
2103     widget = (LTKTextIn *)psDialog->findWidget("firstPage");
2104     psFirstPage = atoi(widget->getText()->getCString());
2105     if (psFirstPage < 1)
2106       psFirstPage = 1;
2107     widget = (LTKTextIn *)psDialog->findWidget("lastPage");
2108     psLastPage = atoi(widget->getText()->getCString());
2109     if (psLastPage < psFirstPage)
2110       psLastPage = psFirstPage;
2111     else if (psLastPage > doc->getNumPages())
2112       psLastPage = doc->getNumPages();
2113     widget = (LTKTextIn *)psDialog->findWidget("fileName");
2114     if (psFileName)
2115       delete psFileName;
2116     psFileName = widget->getText()->copy();
2117     if (!(psFileName->getChar(0) == '|' ||
2118           psFileName->cmp("-") == 0))
2119       makePathAbsolute(psFileName);
2120
2121     // do the PostScript output
2122     psDialog->setBusyCursor(gTrue);
2123     win->setBusyCursor(gTrue);
2124     if (doc->okToPrint()) {
2125       psOut = new PSOutputDev(psFileName->getCString(), doc->getCatalog(),
2126                               psFirstPage, psLastPage, gTrue, gFalse);
2127       if (psOut->isOk()) {
2128         doc->displayPages(psOut, psFirstPage, psLastPage, 72, 0, gFalse);
2129       }
2130       delete psOut;
2131     } else {
2132       error(-1, "Printing this document is not allowed.");
2133     }
2134
2135     delete psDialog;
2136     win->setBusyCursor(gFalse);
2137
2138   // "Cancel" button
2139   } else {
2140     delete psDialog;
2141   }
2142 }
2143
2144 //------------------------------------------------------------------------
2145 // "About" window
2146 //------------------------------------------------------------------------
2147
2148 static void mapAboutWin() {
2149   if (aboutWin) {
2150     aboutWin->raise();
2151   } else {
2152     aboutWin = makeAboutWindow(app);
2153     aboutWin->layout(-1, -1, -1, -1);
2154     aboutWin->map();
2155   }
2156 }
2157
2158 static void closeAboutCbk(LTKWidget *button, int n, GBool on) {
2159   delete aboutWin;
2160   aboutWin = NULL;
2161 }
2162
2163 //------------------------------------------------------------------------
2164 // "Find" window
2165 //------------------------------------------------------------------------
2166
2167 static void findCbk(LTKWidget *button, int n, GBool on) {
2168   if (!doc || doc->getNumPages() == 0)
2169     return;
2170   mapFindWin();
2171 }
2172
2173 static void mapFindWin() {
2174   if (findWin) {
2175     findWin->raise();
2176   } else {
2177     findWin = makeFindWindow(app);
2178     findWin->layout(-1, -1, -1, -1);
2179     findWin->map();
2180   }
2181 }
2182
2183 static void findButtonCbk(LTKWidget *button, int n, GBool on) {
2184   LTKTextIn *textIn;
2185
2186   if (!doc || doc->getNumPages() == 0)
2187     return;
2188   if (n == 1) {
2189     textIn = (LTKTextIn *)findWin->findWidget("text");
2190     doFind(textIn->getText()->getCString());
2191   } else {
2192     delete findWin;
2193     findWin = NULL;
2194   }
2195 }
2196
2197 static void doFind(char *s) {
2198   TextOutputDev *textOut;
2199   int xMin, yMin, xMax, yMax;
2200   double xMin1, yMin1, xMax1, yMax1;
2201   int pg;
2202   GBool top;
2203   GString *s1;
2204
2205   // check for zero-length string
2206   if (!s[0]) {
2207     XBell(display, 0);
2208     return;
2209   }
2210
2211   // set cursors to watch
2212   win->setBusyCursor(gTrue);
2213   findWin->setBusyCursor(gTrue);
2214
2215   // search current page starting at current selection or top of page
2216   xMin = yMin = xMax = yMax = 0;
2217   if (selectXMin < selectXMax && selectYMin < selectYMax) {
2218     xMin = selectXMax;
2219     yMin = (selectYMin + selectYMax) / 2;
2220     top = gFalse;
2221   } else {
2222     top = gTrue;
2223   }
2224   if (out->findText(s, top, gTrue, &xMin, &yMin, &xMax, &yMax))
2225     goto found;
2226
2227   // search following pages
2228   textOut = new TextOutputDev(NULL,
2229                               useEUCJP ? textOutASCII7 : textOutLatin1,
2230                               gFalse);
2231   if (!textOut->isOk()) {
2232     delete textOut;
2233     goto done;
2234   }
2235   for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
2236     doc->displayPage(textOut, pg, 72, 0, gFalse);
2237     if (textOut->findText(s, gTrue, gTrue, &xMin1, &yMin1, &xMax1, &yMax1))
2238       goto foundPage;
2239   }
2240
2241   // search previous pages
2242   for (pg = 1; pg < page; ++pg) {
2243     doc->displayPage(textOut, pg, 72, 0, gFalse);
2244     if (textOut->findText(s, gTrue, gTrue, &xMin1, &yMin1, &xMax1, &yMax1))
2245       goto foundPage;
2246   }
2247   delete textOut;
2248
2249   // search current page ending at current selection
2250   if (selectXMin < selectXMax && selectYMin < selectYMax) {
2251     xMax = selectXMin;
2252     yMax = (selectYMin + selectYMax) / 2;
2253     if (out->findText(s, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax))
2254       goto found;
2255   }
2256
2257   // not found
2258   XBell(display, 0);
2259   goto done;
2260
2261   // found on a different page
2262  foundPage:
2263   delete textOut;
2264   displayPage(pg, zoom, rotate, gTrue);
2265   if (!out->findText(s, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax))
2266     goto done; // this can happen if coalescing is bad
2267
2268   // found: change the selection
2269  found:
2270   setSelection(xMin, yMin, xMax, yMax);
2271 #ifndef NO_TEXT_SELECT
2272   if (doc->okToCopy()) {
2273     s1 = out->getText(selectXMin, selectYMin, selectXMax, selectYMax);
2274     win->setSelection(NULL, s1);
2275   }
2276 #endif
2277
2278  done:
2279   // reset cursors to normal
2280   win->setBusyCursor(gFalse);
2281   findWin->setBusyCursor(gFalse);
2282 }
2283
2284 //------------------------------------------------------------------------
2285 // app kill callback
2286 //------------------------------------------------------------------------
2287
2288 static void killCbk(LTKWindow *win1) {
2289   if (win1 == win) {
2290     quit = gTrue;
2291   } else if (win1 == aboutWin) {
2292     delete aboutWin;
2293     aboutWin = NULL;
2294   } else if (win1 == psDialog) {
2295     delete psDialog;
2296     psDialog = NULL;
2297   } else if (win1 == openDialog) {
2298     delete openDialog;
2299     openDialog = NULL;
2300   } else if (win1 == saveDialog) {
2301     delete saveDialog;
2302     saveDialog = NULL;
2303   } else if (win1 == findWin) {
2304     delete findWin;
2305     findWin = NULL;
2306   }
2307 }