home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga 12 / MA_Cover_12.iso / source / xpdf-0.80 / xpdf / xpdf.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1999-04-27  |  46.0 KB  |  1,737 lines

  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. }
  1737.