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