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