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