]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/xpdf.cc
don't initialize embeddable (where did that come from?)
[evince.git] / pdf / xpdf / xpdf.cc
index edd1c758e29caad1f432d05c47eb61f2758a6bec..ff5455031535202237a758ff383dab047c40cfa6 100644 (file)
@@ -2,14 +2,16 @@
 //
 // xpdf.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
+#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
+#include <time.h>
 #include <X11/X.h>
 #include <X11/cursorfont.h>
 #include <X11/keysym.h>
@@ -18,6 +20,7 @@
 #include "parseargs.h"
 #include "gfile.h"
 #include "gmem.h"
+#include "GlobalParams.h"
 #include "LTKAll.h"
 #include "Object.h"
 #include "Stream.h"
@@ -32,7 +35,6 @@
 #include "LTKOutputDev.h"
 #include "PSOutputDev.h"
 #include "TextOutputDev.h"
-#include "Params.h"
 #include "Error.h"
 #include "config.h"
 
@@ -51,18 +53,58 @@ typedef char *XPointer;
 #ifndef XK_Page_Down
 #define XK_Page_Down            0xFF56
 #endif
+#ifndef XK_KP_Home
+#define XK_KP_Home              0xFF95
+#endif
+#ifndef XK_KP_Left
+#define XK_KP_Left              0xFF96
+#endif
+#ifndef XK_KP_Up
+#define XK_KP_Up                0xFF97
+#endif
+#ifndef XK_KP_Right
+#define XK_KP_Right             0xFF98
+#endif
+#ifndef XK_KP_Down
+#define XK_KP_Down              0xFF99
+#endif
+#ifndef XK_KP_Prior
+#define XK_KP_Prior             0xFF9A
+#endif
+#ifndef XK_KP_Page_Up
+#define XK_KP_Page_Up           0xFF9A
+#endif
+#ifndef XK_KP_Next
+#define XK_KP_Next              0xFF9B
+#endif
+#ifndef XK_KP_Page_Down
+#define XK_KP_Page_Down         0xFF9B
+#endif
+#ifndef XK_KP_End
+#define XK_KP_End               0xFF9C
+#endif
+#ifndef XK_KP_Begin
+#define XK_KP_Begin             0xFF9D
+#endif
+#ifndef XK_KP_Insert
+#define XK_KP_Insert            0xFF9E
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete            0xFF9F
+#endif
 
 //------------------------------------------------------------------------
 // misc constants / enums
 //------------------------------------------------------------------------
 
-#define remoteCmdLength 256
+#define remoteCmdLength 512
 
 enum XpdfMenuItem {
   menuOpen,
+  menuReload,
   menuSavePDF,
-  menuRotateLeft,
-  menuRotateRight,
+  menuRotateCCW,
+  menuRotateCW,
   menuQuit
 };
 
@@ -72,7 +114,9 @@ enum XpdfMenuItem {
 
 // loadFile / displayPage
 static GBool loadFile(GString *fileName);
-static void displayPage(int page1, int zoom1, int rotate1);
+static void displayPage(int page1, int zoomA, int rotateA, GBool addToHist);
+static void displayDest(LinkDest *dest, int zoomA, int rotateA,
+                       GBool addToHist);
 
 // key press and menu callbacks
 static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
@@ -92,11 +136,14 @@ static void mouseDragCbk(LTKWidget *widget, int widgetNum,
 // button callbacks
 static void nextPageCbk(LTKWidget *button, int n, GBool on);
 static void nextTenPageCbk(LTKWidget *button, int n, GBool on);
+static void gotoNextPage(int inc, GBool top);
 static void prevPageCbk(LTKWidget *button, int n, GBool on);
 static void prevTenPageCbk(LTKWidget *button, int n, GBool on);
+static void gotoPrevPage(int dec, GBool top, GBool bottom);
+static void backCbk(LTKWidget *button, int n, GBool on);
+static void forwardCbk(LTKWidget *button, int n, GBool on);
 static void pageNumCbk(LTKWidget *textIn, int n, GString *text);
-static void zoomInCbk(LTKWidget *button, int n, GBool on);
-static void zoomOutCbk(LTKWidget *button, int n, GBool on);
+static void zoomMenuCbk(LTKMenuItem *item);
 static void postScriptCbk(LTKWidget *button, int n, GBool on);
 static void aboutCbk(LTKWidget *button, int n, GBool on);
 static void quitCbk(LTKWidget *button, int n, GBool on);
@@ -107,6 +154,7 @@ static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val);
 
 // misc callbacks
 static void layoutCbk(LTKWindow *win1);
+static void updateScrollbars();
 static void propChangeCbk(LTKWindow *win1, Atom atom);
 
 // selection
@@ -117,6 +165,9 @@ static void mapOpenDialog();
 static void openButtonCbk(LTKWidget *button, int n, GBool on);
 static void openSelectCbk(LTKWidget *widget, int n, GString *name);
 
+// "Reload"
+static void reloadCbk();
+
 // "Save PDF" dialog
 static void mapSaveDialog();
 static void saveButtonCbk(LTKWidget *button, int n, GBool on);
@@ -129,6 +180,9 @@ static void psButtonCbk(LTKWidget *button, int n, GBool on);
 // "About" window
 static void mapAboutWin();
 static void closeAboutCbk(LTKWidget *button, int n, GBool on);
+static void aboutLayoutCbk(LTKWindow *winA);
+static void aboutScrollVertCbk(LTKWidget *scrollbar, int n, int val);
+static void aboutScrollHorizCbk(LTKWidget *scrollbar, int n, int val);
 
 // "Find" window
 static void findCbk(LTKWidget *button, int n, GBool on);
@@ -140,87 +194,172 @@ static void doFind(char *s);
 static void killCbk(LTKWindow *win1);
 
 //------------------------------------------------------------------------
-// GUI includes
+// "About" window text
 //------------------------------------------------------------------------
 
-#include "xpdfIcon.xpm"
-#include "leftArrow.xbm"
-#include "dblLeftArrow.xbm"
-#include "rightArrow.xbm"
-#include "dblRightArrow.xbm"
-#include "zoomIn.xbm"
-#include "zoomOut.xbm"
-#include "find.xbm"
-#include "postscript.xbm"
-#include "about.xbm"
-#include "xpdf-ltk.h"
+static char *aboutWinText[] = {
+  "X     X           d   fff",
+  " X   X            d  f   f",
+  "  X X             d  f",
+  "   X    pppp   dddd ffff",
+  "  X X   p   p d   d  f",
+  " X   X  p   p d   d  f",
+  "X     X pppp   dddd  f      Version " xpdfVersion,
+  "        p",
+  "        p",
+  "        p",
+  "",
+  xpdfCopyright,
+  "",
+  "http://www.foolabs.com/xpdf/",
+  "derekn@foolabs.com",
+  "",
+  "Licensed under the GNU General Public License (GPL).",
+  "See the 'COPYING' file for details.",
+  "",
+  "Supports PDF version " supportedPDFVersionStr ".",
+  "",
+  "The PDF data structures, operators, and specification",
+  "are copyright 1985-2001 Adobe Systems Inc.",
+  "",
+  "Mouse bindings:",
+  "  button 1: select text / follow link",
+  "  button 2: pan window",
+  "  button 3: menu",
+  "",
+  "Key bindings:",
+  "  o              = open file",
+  "  r              = reload",
+  "  f              = find text",
+  "  n = <PgDn>     = next page",
+  "  p = <PgUp>     = previous page",
+  "  <space>        = scroll down",
+  "  <backspace>    = <delete> = scroll up",
+  "  v              = forward (history path)",
+  "  b              = backward (history path)",
+  "  0 / + / -      = zoom zero / in / out",
+  "  z / w          = zoom page / page width",
+  "  ctrl-L         = redraw",
+  "  q              = quit",
+  "  <home> / <end> = top / bottom of page",
+  "  <arrows>       = scroll",
+  "",
+  "For more information, please read the xpdf(1) man page.",
+  NULL
+};
 
 //------------------------------------------------------------------------
 // command line options
 //------------------------------------------------------------------------
 
-static XrmOptionDescRec opts[] = {
-  {"-display",       ".display",       XrmoptionSepArg,  NULL},
-  {"-foreground",    ".foreground",    XrmoptionSepArg,  NULL},
-  {"-fg",            ".foreground",    XrmoptionSepArg,  NULL},
-  {"-background",    ".background",    XrmoptionSepArg,  NULL},
-  {"-bg",            ".background",    XrmoptionSepArg,  NULL},
-  {"-geometry",      ".geometry",      XrmoptionSepArg,  NULL},
-  {"-g",             ".geometry",      XrmoptionSepArg,  NULL},
-  {"-font",          ".font",          XrmoptionSepArg,  NULL},
-  {"-fn",            ".font",          XrmoptionSepArg,  NULL},
-  {"-cmap",          ".installCmap",   XrmoptionNoArg,   (XPointer)"on"},
-  {"-rgb",           ".rgbCubeSize",   XrmoptionSepArg,  NULL},
-  {"-papercolor",    ".paperColor",    XrmoptionSepArg,  NULL},
-  {"-z",             ".initialZoom",   XrmoptionSepArg,  NULL},
-  {"-ps",            ".psFile",        XrmoptionSepArg,  NULL},
-  {"-paperw",        ".psPaperWidth",  XrmoptionSepArg,  NULL},
-  {"-paperh",        ".psPaperHeight", XrmoptionSepArg,  NULL},
-  {"-level1",        ".psLevel1",      XrmoptionNoArg,   (XPointer)"false"},
-  {NULL}
-};
-
-GBool printCommands = gFalse;
-static GBool printHelp = gFalse;
+static char initialZoomStr[32] = "";
+static char t1libControlStr[16] = "";
+static char freetypeControlStr[16] = "";
+static char psFileArg[256];
+static char paperSize[15] = "";
+static int paperWidth = 0;
+static int paperHeight = 0;
+static GBool level1 = gFalse;
+static char textEncName[128] = "";
+static char textEOL[16] = "";
+static char ownerPassword[33] = "";
+static char userPassword[33] = "";
+static GBool fullScreen = gFalse;
 static char remoteName[100] = "xpdf_";
 static GBool doRemoteRaise = gFalse;
 static GBool doRemoteQuit = gFalse;
+static GBool printCommands = gFalse;
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+static GBool viKeys = gFalse;
 
 static ArgDesc argDesc[] = {
-  {"-err",        argFlag,        &errorsToTTY,   0,
-   "send error messages to /dev/tty instead of stderr"},
-  {"-z",          argIntDummy,    NULL,           0,
-   "initial zoom level (-5..5)"},
   {"-g",          argStringDummy, NULL,           0,
    "initial window geometry"},
   {"-geometry",   argStringDummy, NULL,           0,
    "initial window geometry"},
-  {"-remote",     argString,      remoteName + 5, sizeof(remoteName) - 5,
-   "start/contact xpdf remote server with specified name"},
-  {"-raise",      argFlag,        &doRemoteRaise, 0,
-   "raise xpdf remote server window (with -remote only)"},
-  {"-quit",       argFlag,        &doRemoteQuit,  0,
-   "kill xpdf remote server (with -remote only)"},
+  {"-title",      argStringDummy, NULL,           0,
+   "window title"},
   {"-cmap",       argFlagDummy,   NULL,           0,
    "install a private colormap"},
   {"-rgb",        argIntDummy,    NULL,           0,
    "biggest RGB cube to allocate (default is 5)"},
+  {"-rv",         argFlagDummy,   NULL,           0,
+   "reverse video"},
   {"-papercolor", argStringDummy, NULL,           0,
    "color of paper background"},
-  {"-ps",         argStringDummy, NULL,           0,
-   "default PostScript file/command name"},
-  {"-paperw",     argIntDummy,    NULL,           0,
+  {"-z",          argString,      initialZoomStr, sizeof(initialZoomStr),
+   "initial zoom level (-5..5, page, width)"},
+#if HAVE_T1LIB_H
+  {"-t1lib",      argString,      t1libControlStr, sizeof(t1libControlStr),
+   "t1lib font rasterizer control: none, plain, low, high"},
+#endif
+#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
+  {"-freetype",   argString,      freetypeControlStr, sizeof(freetypeControlStr),
+   "FreeType font rasterizer control: none, plain, low, high"},
+#endif
+  {"-ps",         argString,      psFileArg,      sizeof(psFileArg),
+   "default PostScript file name or command"},
+  {"-paper",      argString,      paperSize,      sizeof(paperSize),
+   "paper size (letter, legal, A4, A3)"},
+  {"-paperw",     argInt,         &paperWidth,    0,
    "paper width, in points"},
-  {"-paperh",     argIntDummy,    NULL,           0,
+  {"-paperh",     argInt,         &paperHeight,   0,
    "paper height, in points"},
-  {"-level1",     argFlagDummy,   NULL,           0,
+  {"-level1",     argFlag,        &level1,        0,
    "generate Level 1 PostScript"},
+  {"-enc",    argString,   textEncName,    sizeof(textEncName),
+   "output text encoding name"},
+  {"-eol",    argString,   textEOL,        sizeof(textEOL),
+   "output end-of-line convention (unix, dos, or mac)"},
+  {"-opw",        argString,      ownerPassword,  sizeof(ownerPassword),
+   "owner password (for encrypted files)"},
+  {"-upw",        argString,      userPassword,   sizeof(userPassword),
+   "user password (for encrypted files)"},
+  {"-fullscreen", argFlag,        &fullScreen,    0,
+   "run in full-screen (presentation) mode"},
+  {"-remote",     argString,      remoteName + 5, sizeof(remoteName) - 5,
+   "start/contact xpdf remote server with specified name"},
+  {"-raise",      argFlag,        &doRemoteRaise, 0,
+   "raise xpdf remote server window (with -remote only)"},
+  {"-quit",       argFlag,        &doRemoteQuit,  0,
+   "kill xpdf remote server (with -remote only)"},
   {"-cmd",        argFlag,        &printCommands, 0,
    "print commands as they're executed"},
+  {"-q",          argFlag,        &quiet,         0,
+   "don't print any messages or errors"},
+  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
+   "configuration file to use in place of .xpdfrc"},
+  {"-v",          argFlag,        &printVersion,  0,
+   "print copyright and version info"},
   {"-h",          argFlag,        &printHelp,     0,
    "print usage information"},
   {"-help",       argFlag,        &printHelp,     0,
    "print usage information"},
+  {"--help",  argFlag,     &printHelp,     0,
+   "print usage information"},
+  {"-?",      argFlag,     &printHelp,     0,
+   "print usage information"},
+  {NULL}
+};
+
+static XrmOptionDescRec opts[] = {
+  {"-display",       ".display",         XrmoptionSepArg,  NULL},
+  {"-foreground",    ".foreground",      XrmoptionSepArg,  NULL},
+  {"-fg",            ".foreground",      XrmoptionSepArg,  NULL},
+  {"-background",    ".background",      XrmoptionSepArg,  NULL},
+  {"-bg",            ".background",      XrmoptionSepArg,  NULL},
+  {"-geometry",      ".geometry",        XrmoptionSepArg,  NULL},
+  {"-g",             ".geometry",        XrmoptionSepArg,  NULL},
+  {"-font",          ".font",            XrmoptionSepArg,  NULL},
+  {"-fn",            ".font",            XrmoptionSepArg,  NULL},
+  {"-title",         ".title",           XrmoptionSepArg,  NULL},
+  {"-cmap",          ".installCmap",     XrmoptionNoArg,   (XPointer)"on"},
+  {"-rgb",           ".rgbCubeSize",     XrmoptionSepArg,  NULL},
+  {"-rv",            ".reverseVideo",    XrmoptionNoArg,   (XPointer)"true"},
+  {"-papercolor",    ".paperColor",      XrmoptionSepArg,  NULL},
   {NULL}
 };
 
@@ -229,14 +368,16 @@ static ArgDesc argDesc[] = {
 //------------------------------------------------------------------------
 
 // zoom factor is 1.2 (similar to DVI magsteps)
-#define minZoom -5
-#define maxZoom  5
+#define minZoom    -5
+#define maxZoom     5
+#define zoomPage  100
+#define zoomWidth 101
+#define defZoom     1
 static int zoomDPI[maxZoom - minZoom + 1] = {
   29, 35, 42, 50, 60,
   72,
   86, 104, 124, 149, 179
 };
-#define defZoom 1
 
 static PDFDoc *doc;
 
@@ -247,6 +388,8 @@ static int zoom;
 static int rotate;
 static GBool quit;
 
+static time_t modTime;         // last modification time of PDF file
+
 static LinkAction *linkAction; // mouse pointer is over this link
 static int                     // coordinates of current selection:
   selectXMin, selectYMin,      //   (xMin==xMax || yMin==yMax) means there
@@ -255,7 +398,19 @@ static GBool lastDragLeft; // last dragged selection edge was left/right
 static GBool lastDragTop;      // last dragged selection edge was top/bottom
 static int panMX, panMY;       // last mouse position for pan
 
-static GString *defPSFileName;
+struct History {
+  GString *fileName;
+  int page;
+};
+#define historySize 50
+static History                 // page history queue
+  history[historySize];
+static int historyCur;         // currently displayed page
+static int historyBLen;                // number of valid entries backward from
+                               //   current entry
+static int historyFLen;                // number of valid entries forward from
+                               //   current entry
+
 static GString *psFileName;
 static int psFirstPage, psLastPage;
 
@@ -263,22 +418,45 @@ static GString *fileReqDir;       // current directory for file requesters
 
 static GString *urlCommand;    // command to execute for URI links
 
+static GString *windowTitle;   // window title string
+
 static LTKApp *app;
 static Display *display;
 static LTKWindow *win;
+static LTKMenu *zoomMenu;
 static LTKScrollingCanvas *canvas;
 static LTKScrollbar *hScrollbar, *vScrollbar;
 static LTKTextIn *pageNumText;
 static LTKLabel *numPagesLabel;
 static LTKLabel *linkLabel;
+static LTKMenuButton *zoomMenuBtn;
 static LTKWindow *aboutWin;
+static LTKList *aboutList;
+static LTKScrollbar *aboutHScrollbar, *aboutVScrollbar;
 static LTKWindow *psDialog;
 static LTKWindow *openDialog;
 static LTKWindow *saveDialog;
 static LTKWindow *findWin;
+static LTKTextIn *findTextIn;
 static Atom remoteAtom;
 static GC selectGC;
 
+//------------------------------------------------------------------------
+// GUI includes
+//------------------------------------------------------------------------
+
+#include "xpdfIcon.xpm"
+#include "leftArrow.xbm"
+#include "dblLeftArrow.xbm"
+#include "rightArrow.xbm"
+#include "dblRightArrow.xbm"
+#include "backArrow.xbm"
+#include "forwardArrow.xbm"
+#include "find.xbm"
+#include "postscript.xbm"
+#include "about.xbm"
+#include "xpdf-ltk.h"
+
 //------------------------------------------------------------------------
 // main program
 //------------------------------------------------------------------------
@@ -290,12 +468,20 @@ int main(int argc, char *argv[]) {
   LTKMenu *menu;
   GString *name;
   GString *title;
+  GString *initialZoom;
+  GBool reverseVideo;
   unsigned long paperColor;
+  GBool installCmap;
+  int rgbCubeSize;
   int pg;
+  GString *destName;
+  LinkDest *dest;
   int x, y;
   Guint width, height;
+  double width1, height1;
   GBool ok;
   char s[20];
+  int i;
   int ret;
 
   // initialize
@@ -304,23 +490,60 @@ int main(int argc, char *argv[]) {
   out = NULL;
   remoteAtom = None;
   doc = NULL;
-  xref = NULL;
   psFileName = NULL;
   fileReqDir = getCurrentDir();
   ret = 0;
 
   // parse args
-  paperWidth = paperHeight = -1;
   ok = parseArgs(argDesc, &argc, argv);
 
-  // init error file
-  errorInit();
-
   // read config file
-  initParams(xpdfConfigFile);
+  globalParams = new GlobalParams(cfgFileName);
+  if (psFileArg[0]) {
+    globalParams->setPSFile(psFileArg);
+  }
+  if (paperSize[0]) {
+    if (!globalParams->setPSPaperSize(paperSize)) {
+      fprintf(stderr, "Invalid paper size\n");
+    }
+  } else {
+    if (paperWidth) {
+      globalParams->setPSPaperWidth(paperWidth);
+    }
+    if (paperHeight) {
+      globalParams->setPSPaperHeight(paperHeight);
+    }
+  }
+  if (level1) {
+    globalParams->setPSLevel(psLevel1);
+  }
+  if (textEncName[0]) {
+    globalParams->setTextEncoding(textEncName);
+  }
+  if (textEOL[0]) {
+    if (!globalParams->setTextEOL(textEOL)) {
+      fprintf(stderr, "Bad '-eol' value on command line\n");
+    }
+  }
+  if (initialZoomStr[0]) {
+    globalParams->setInitialZoom(initialZoomStr);
+  }
+  if (t1libControlStr[0]) {
+    if (!globalParams->setT1libControl(t1libControlStr)) {
+      fprintf(stderr, "Bad '-t1lib' value on command line\n");
+    }
+  }
+  if (freetypeControlStr[0]) {
+    if (!globalParams->setFreeTypeControl(freetypeControlStr)) {
+      fprintf(stderr, "Bad '-freetype' value on command line\n");
+    }
+  }
+  if (quiet) {
+    globalParams->setErrQuiet(quiet);
+  }
 
   // create LTKApp (and parse X-related args)
-  app = new LTKApp("xpdf", opts, &argc, argv);
+  app = new LTKApp("xpdf", "Xpdf", opts, &argc, argv);
   app->setKillCbk(&killCbk);
   display = app->getDisplay();
 
@@ -331,21 +554,29 @@ int main(int argc, char *argv[]) {
     ok = ok && remoteName[5] && argc == 1;
   else
     ok = ok && argc >= 1 && argc <= 3;
-  if (!ok || printHelp) {
+  if (!ok || printVersion || printHelp) {
     fprintf(stderr, "xpdf version %s\n", xpdfVersion);
     fprintf(stderr, "%s\n", xpdfCopyright);
-    printUsage("xpdf", "[<PDF-file> [<page>]]", argDesc);
+    if (!printVersion) {
+      printUsage("xpdf", "[<PDF-file> [<page> | +<dest>]]", argDesc);
+    }
     ret = 1;
     goto done2;
   }
-  if (argc >= 2)
+  if (argc >= 2) {
     name = new GString(argv[1]);
-  else
+  } else {
     name = NULL;
-  if (argc == 3)
-    pg = atoi(argv[2]);
-  else
-    pg = 1;
+  }
+  pg = 1;
+  destName = NULL;
+  if (argc == 3) {
+    if (argv[2][0] == '+') {
+      destName = new GString(&argv[2][1]);
+    } else {
+      pg = atoi(argv[2]);
+    }
+  }
 
   // look for already-running remote server
   if (remoteName[5]) {
@@ -353,8 +584,14 @@ int main(int argc, char *argv[]) {
     xwin = XGetSelectionOwner(display, remoteAtom);
     if (xwin != None) {
       if (name) {
-       sprintf(cmd, "%c %d %.200s", doRemoteRaise ? 'D' : 'd',
-               pg, name->getCString());
+       if (destName) {
+         sprintf(cmd, "%c +%.256s %.200s", doRemoteRaise ? 'D' : 'd',
+                 destName->getCString(), name->getCString());
+         delete destName;
+       } else {
+         sprintf(cmd, "%c %d %.200s", doRemoteRaise ? 'D' : 'd',
+                 pg, name->getCString());
+       }
        XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
                        PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
        delete name;
@@ -367,16 +604,21 @@ int main(int argc, char *argv[]) {
       }
       goto done2;
     }
-    if (doRemoteQuit)
+    if (doRemoteQuit) {
+      if (destName) {
+       delete destName;
+      }
       goto done2;
+    }
   }
 
-  // print banner
-  fprintf(errFile, "xpdf version %s\n", xpdfVersion);
-  fprintf(errFile, "%s\n", xpdfCopyright);
+  // no history yet
+  historyCur = historySize - 1;
+  historyBLen = historyFLen = 0;
+  for (i = 0; i < historySize; ++i)
+    history[i].fileName = NULL;
 
   // open PDF file
-  defPSFileName = app->getStringResource("psFile", NULL);
   if (name) {
     if (!loadFile(name)) {
       ret = 1;
@@ -387,12 +629,20 @@ int main(int argc, char *argv[]) {
   }
 
   // check for legal page number
-  if (doc && (pg < 1 || pg > doc->getNumPages()))
+  if (doc && (pg < 1 || pg > doc->getNumPages())) {
     pg = 1;
+  }
 
   // create window
-  win = makeWindow(app);
   menu = makeMenu();
+  if (fullScreen) {
+    zoomMenu = NULL;
+    win = makeFullScreenWindow(app);
+    win->setDecorated(gFalse);
+  } else {
+    zoomMenu = makeZoomMenu();
+    win = makeWindow(app);
+  }
   win->setMenu(menu);
   canvas = (LTKScrollingCanvas *)win->findWidget("canvas");
   hScrollbar = (LTKScrollbar *)win->findWidget("hScrollbar");
@@ -400,58 +650,105 @@ int main(int argc, char *argv[]) {
   pageNumText = (LTKTextIn *)win->findWidget("pageNum");
   numPagesLabel = (LTKLabel *)win->findWidget("numPages");
   linkLabel = (LTKLabel *)win->findWidget("link");
+  zoomMenuBtn = (LTKMenuButton *)win->findWidget("zoom");
   win->setKeyCbk(&keyPressCbk);
   win->setLayoutCbk(&layoutCbk);
   canvas->setButtonPressCbk(&buttonPressCbk);
   canvas->setButtonReleaseCbk(&buttonReleaseCbk);
   canvas->setMouseMoveCbk(&mouseMoveCbk);
   canvas->setMouseDragCbk(&mouseDragCbk);
-  hScrollbar->setRepeatPeriod(0);
-  vScrollbar->setRepeatPeriod(0);
+  if (!fullScreen) {
+    hScrollbar->setRepeatPeriod(0);
+    vScrollbar->setRepeatPeriod(0);
+  }
+
+  // get parameters
+  urlCommand = globalParams->getURLCommand();
 
   // get X resources
-  paperWidth = app->getIntResource("psPaperWidth", defPaperWidth);
-  paperHeight = app->getIntResource("psPaperHeight", defPaperHeight);
-  psOutLevel1 = app->getBoolResource("psLevel1", gFalse);
-  urlCommand = app->getStringResource("urlCommand", NULL);
+  windowTitle = app->getStringResource("title", NULL);
   installCmap = app->getBoolResource("installCmap", gFalse);
-  if (installCmap)
+  if (installCmap) {
     win->setInstallCmap(gTrue);
+  }
   rgbCubeSize = app->getIntResource("rgbCubeSize", defaultRGBCube);
-  paperColor = app->getColorResource("paperColor", "white",
-                                    WhitePixel(display, app->getScreenNum()),
-                                    NULL);
-  zoom = app->getIntResource("initialZoom", defZoom);
-  if (zoom < minZoom)
-    zoom = minZoom;
-  else if (zoom > maxZoom)
-    zoom = maxZoom;
+  reverseVideo = app->getBoolResource("reverseVideo", gFalse);
+  paperColor = app->getColorResource(
+                 "paperColor",
+                 reverseVideo ? (char *)"black" : (char *)"white",
+                 reverseVideo ? BlackPixel(display, app->getScreenNum())
+                              : WhitePixel(display, app->getScreenNum()),
+                 NULL);
+  if (fullScreen) {
+    zoom = zoomPage;
+  } else {
+    initialZoom = globalParams->getInitialZoom();
+    if (!initialZoom->cmp("page")) {
+      zoom = zoomPage;
+      i = maxZoom - minZoom + 2;
+    } else if (!initialZoom->cmp("width")) {
+      zoom = zoomWidth;
+      i = maxZoom - minZoom + 3;
+    } else {
+      zoom = atoi(initialZoom->getCString());
+      if (zoom < minZoom) {
+       zoom = minZoom;
+      } else if (zoom > maxZoom) {
+       zoom = maxZoom;
+      }
+      i = zoom - minZoom;
+    }
+    zoomMenuBtn->setInitialMenuItem(zoomMenu->getItem(i));
+  }
+  viKeys = app->getBoolResource("viKeys", gFalse);
 
   // get geometry
-  x = -1;
-  y = -1;
-  if (!doc) {
-    width = 612;
-    height = 792;
-  } else if (doc->getPageRotate(pg) == 90 || doc->getPageRotate(pg) == 270) {
-    width = (int)(doc->getPageHeight(pg) + 0.5);
-    height = (int)(doc->getPageWidth(pg) + 0.5);
+  if (fullScreen) {
+    x = y = 0;
+    width = app->getDisplayWidth();
+    height = app->getDisplayHeight();
   } else {
-    width = (int)(doc->getPageWidth(pg) + 0.5);
-    height = (int)(doc->getPageHeight(pg) + 0.5);
+    x = y = -1;
+    width = height = 0;
+    app->getGeometryResource("geometry", &x, &y, &width, &height);
+    if (width == 0 || height == 0) {
+      if (!doc || doc->getNumPages() == 0) {
+       width1 = 612;
+       height1 = 792;
+      } else if (doc->getPageRotate(pg) == 90 ||
+                doc->getPageRotate(pg) == 270) {
+       width1 = doc->getPageHeight(pg);
+       height1 = doc->getPageWidth(pg);
+      } else {
+       width1 = doc->getPageWidth(pg);
+       height1 = doc->getPageHeight(pg);
+      }
+      if (zoom == zoomPage || zoom == zoomWidth) {
+       width = (int)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
+       height = (int)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
+      } else {
+       width = (int)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
+       height = (int)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
+      }
+      width += 28;
+      height += 56;
+      if (width > (Guint)app->getDisplayWidth() - 100) {
+       width = app->getDisplayWidth() - 100;
+      }
+      if (height > (Guint)app->getDisplayHeight() - 100) {
+       height = app->getDisplayHeight() - 100;
+      }
+    }
   }
-  width = (width * zoomDPI[zoom - minZoom]) / 72 + 28;
-  if (width > (Guint)app->getDisplayWidth() - 100)
-    width = app->getDisplayWidth() - 100;
-  height = (height * zoomDPI[zoom - minZoom]) / 72 + 56;
-  if (height > (Guint)app->getDisplayHeight() - 100)
-    height = app->getDisplayHeight() - 100;
-  app->getGeometryResource("geometry", &x, &y, &width, &height);
 
   // finish setting up window
-  sprintf(s, "of %d", doc ? doc->getNumPages() : 0);
-  numPagesLabel->setText(s);
-  if (name) {
+  if (!fullScreen) {
+    sprintf(s, "of %d", doc ? doc->getNumPages() : 0);
+    numPagesLabel->setText(s);
+  }
+  if (windowTitle) {
+    title = windowTitle->copy();
+  } else if (name) {
     title = new GString("xpdf: ");
     title->append(name);
   } else {
@@ -479,10 +776,25 @@ int main(int argc, char *argv[]) {
   }
 
   // create output device
-  out = new LTKOutputDev(win, paperColor);
+  out = new LTKOutputDev(win, reverseVideo, paperColor,
+                        installCmap, rgbCubeSize, !fullScreen);
+  out->startDoc(doc ? doc->getXRef() : (XRef *)NULL);
 
   // display first page
-  displayPage(pg, zoom, 0);
+  if (destName) {
+    if (doc) {
+      if ((dest = doc->findDest(destName))) {
+       displayDest(dest, zoom, 0, gTrue);
+       delete dest;
+      } else {
+       error(-1, "Unknown named destination '%s'", destName->getCString());
+       displayPage(1, zoom, 0, gTrue);
+      }
+    }
+    delete destName;
+  } else {
+    displayPage(pg, zoom, 0, gTrue);
+  }
 
   // event loop
   quit = gFalse;
@@ -497,42 +809,55 @@ int main(int argc, char *argv[]) {
 
  done2:
   // free stuff
-  if (out)
+  if (out) {
     delete out;
-  if (win)
+  }
+  if (win) {
     delete win;
-  if (aboutWin)
+  }
+  if (aboutWin) {
     delete aboutWin;
-  if (findWin)
+  }
+  if (findWin) {
     delete findWin;
-  if (app)
+  }
+  if (app) {
     delete app;
-  if (doc)
+  }
+  if (doc) {
     delete doc;
-  if (psFileName)
+  }
+  if (psFileName) {
     delete psFileName;
-  if (defPSFileName)
-    delete defPSFileName;
-  if (fileReqDir)
+  }
+  if (fileReqDir) {
     delete fileReqDir;
-  if (urlCommand)
-    delete urlCommand;
-  freeParams();
+  }
+  if (windowTitle) {
+    delete windowTitle;
+  }
+  for (i = 0; i < historySize; ++i) {
+    if (history[i].fileName) {
+      delete history[i].fileName;
+    }
+  }
+  delete globalParams;
 
   // check for memory leaks
-  Object::memCheck(errFile);
-  gMemReport(errFile);
+  Object::memCheck(stderr);
+  gMemReport(stderr);
 
   return ret;
 }
 
 //------------------------------------------------------------------------
-// loadFile / displayPage
+// loadFile / displayPage / displayDest
 //------------------------------------------------------------------------
 
 static GBool loadFile(GString *fileName) {
   GString *title;
   PDFDoc *newDoc;
+  GString *ownerPW, *userPW;
   char s[20];
   char *p;
 
@@ -541,7 +866,23 @@ static GBool loadFile(GString *fileName) {
     win->setBusyCursor(gTrue);
 
   // open PDF file
-  newDoc = new PDFDoc(fileName);
+  if (ownerPassword[0]) {
+    ownerPW = new GString(ownerPassword);
+  } else {
+    ownerPW = NULL;
+  }
+  if (userPassword[0]) {
+    userPW = new GString(userPassword);
+  } else {
+    userPW = NULL;
+  }
+  newDoc = new PDFDoc(fileName, ownerPW, userPW, printCommands);
+  if (userPW) {
+    delete userPW;
+  }
+  if (ownerPW) {
+    delete ownerPW;
+  }
   if (!newDoc->isOk()) {
     delete newDoc;
     if (win)
@@ -553,15 +894,21 @@ static GBool loadFile(GString *fileName) {
   if (doc)
     delete doc;
   doc = newDoc;
+  if (out) {
+    out->startDoc(doc->getXRef());
+  }
 
   // nothing displayed yet
   page = -99;
 
+  // save the modification time
+  modTime = getModTime(fileName->getCString());
+
   // init PostScript output params
   if (psFileName)
     delete psFileName;
-  if (defPSFileName) {
-    psFileName = defPSFileName->copy();
+  if (globalParams->getPSFile()) {
+    psFileName = globalParams->getPSFile()->copy();
   } else {
     p = fileName->getCString() + fileName->getLength() - 4;
     if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF"))
@@ -576,11 +923,15 @@ static GBool loadFile(GString *fileName) {
 
   // set up title, number-of-pages display; back to normal cursor
   if (win) {
-    title = new GString("xpdf: ");
-    title->append(fileName);
-    win->setTitle(title);
-    sprintf(s, "of %d", doc->getNumPages());
-    numPagesLabel->setText(s);
+    if (!windowTitle) {
+      title = new GString("xpdf: ");
+      title->append(fileName);
+      win->setTitle(title);
+    }
+    if (!fullScreen) {
+      sprintf(s, "of %d", doc->getNumPages());
+      numPagesLabel->setText(s);
+    }
     win->setBusyCursor(gFalse);
   }
 
@@ -588,42 +939,176 @@ static GBool loadFile(GString *fileName) {
   return gTrue;
 }
 
-static void displayPage(int page1, int zoom1, int rotate1) {
+static void displayPage(int pageA, int zoomA, int rotateA, GBool addToHist) {
+  time_t modTime1;
+  double hDPI, vDPI, dpi;
+  int rot;
   char s[20];
+  History *h;
 
   // check for document
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0)
     return;
 
+  // check for changes to the file
+  modTime1 = getModTime(doc->getFileName()->getCString());
+  if (modTime1 != modTime) {
+    if (loadFile(doc->getFileName()->copy())) {
+      if (pageA > doc->getNumPages()) {
+       pageA = doc->getNumPages();
+      }
+    }
+    modTime = modTime1;
+  }
+
   // busy cursor
   if (win)
     win->setBusyCursor(gTrue);
 
   // new page/zoom/rotate values
-  page = page1;
-  zoom = zoom1;
-  rotate = rotate1;
+  page = pageA;
+  zoom = zoomA;
+  rotate = rotateA;
 
   // initialize mouse-related stuff
   linkAction = NULL;
   win->setDefaultCursor();
-  linkLabel->setText(NULL);
+  if (!fullScreen) {
+    linkLabel->setText(NULL);
+  }
   selectXMin = selectXMax = 0;
   selectYMin = selectYMax = 0;
   lastDragLeft = lastDragTop = gTrue;
 
   // draw the page
-  doc->displayPage(out, page, zoomDPI[zoom - minZoom], rotate, gTrue);
-  layoutCbk(win);
+  rot = rotate + doc->getPageRotate(page);
+  if (rot >= 360)
+    rot -= 360;
+  else if (rotate < 0)
+    rot += 360;
+  if (fullScreen) {
+    if (rot == 90 || rot == 270) {
+      hDPI = (win->getWidth() / doc->getPageHeight(page)) * 72;
+      vDPI = (win->getHeight() / doc->getPageWidth(page)) * 72;
+    } else {
+      hDPI = (win->getWidth() / doc->getPageWidth(page)) * 72;
+      vDPI = (win->getHeight() / doc->getPageHeight(page)) * 72;
+    }
+    dpi = (hDPI < vDPI) ? hDPI : vDPI;
+  } else if (zoom == zoomPage) {
+    if (rot == 90 || rot == 270) {
+      hDPI = (canvas->getWidth() / doc->getPageHeight(page)) * 72;
+      vDPI = (canvas->getHeight() / doc->getPageWidth(page)) * 72;
+    } else {
+      hDPI = (canvas->getWidth() / doc->getPageWidth(page)) * 72;
+      vDPI = (canvas->getHeight() / doc->getPageHeight(page)) * 72;
+    }
+    dpi = (hDPI < vDPI) ? hDPI : vDPI;
+  } else if (zoom == zoomWidth) {
+    if (rot == 90 || rot == 270) {
+      dpi = (canvas->getWidth() / doc->getPageHeight(page)) * 72;
+    } else {
+      dpi = (canvas->getWidth() / doc->getPageWidth(page)) * 72;
+    }
+  } else {
+    dpi = zoomDPI[zoom - minZoom];
+  }
+  doc->displayPage(out, page, dpi, rotate, gTrue);
+  updateScrollbars();
 
   // update page number display
-  sprintf(s, "%d", page);
-  pageNumText->setText(s);
+  if (!fullScreen) {
+    sprintf(s, "%d", page);
+    pageNumText->setText(s);
+  }
+
+  // add to history
+  if (addToHist) {
+    if (++historyCur == historySize)
+      historyCur = 0;
+    h = &history[historyCur];
+    if (h->fileName)
+      delete h->fileName;
+    h->fileName = doc->getFileName()->copy();
+    h->page = page;
+    if (historyBLen < historySize)
+      ++historyBLen;
+    historyFLen = 0;
+  }
 
   // back to regular cursor
   win->setBusyCursor(gFalse);
 }
 
+static void displayDest(LinkDest *dest, int zoomA, int rotateA,
+                       GBool addToHist) {
+  Ref pageRef;
+  int pg;
+  int dx, dy;
+
+  if (dest->isPageRef()) {
+    pageRef = dest->getPageRef();
+    pg = doc->findPage(pageRef.num, pageRef.gen);
+  } else {
+    pg = dest->getPageNum();
+  }
+  if (pg <= 0 || pg > doc->getNumPages()) {
+    pg = 1;
+  }
+  if (pg != page) {
+    displayPage(pg, zoomA, rotateA, addToHist);
+  }
+
+  if (fullScreen) {
+    return;
+  }
+  switch (dest->getKind()) {
+  case destXYZ:
+    out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
+    if (dest->getChangeLeft() || dest->getChangeTop()) {
+      if (dest->getChangeLeft()) {
+       hScrollbar->setPos(dx, canvas->getWidth());
+      }
+      if (dest->getChangeTop()) {
+       vScrollbar->setPos(dy, canvas->getHeight());
+      }
+      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    }
+    //~ what is the zoom parameter?
+    break;
+  case destFit:
+  case destFitB:
+    //~ do fit
+    hScrollbar->setPos(0, canvas->getWidth());
+    vScrollbar->setPos(0, canvas->getHeight());
+    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    break;
+  case destFitH:
+  case destFitBH:
+    //~ do fit
+    out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
+    hScrollbar->setPos(0, canvas->getWidth());
+    vScrollbar->setPos(dy, canvas->getHeight());
+    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    break;
+  case destFitV:
+  case destFitBV:
+    //~ do fit
+    out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
+    hScrollbar->setPos(dx, canvas->getWidth());
+    vScrollbar->setPos(0, canvas->getHeight());
+    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    break;
+  case destFitR:
+    //~ do fit
+    out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
+    hScrollbar->setPos(dx, canvas->getWidth());
+    vScrollbar->setPos(dy, canvas->getHeight());
+    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    break;
+  }
+}
+
 //------------------------------------------------------------------------
 // key press and menu callbacks
 //------------------------------------------------------------------------
@@ -636,22 +1121,27 @@ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
     case 'o':
       mapOpenDialog();
       break;
+    case 'R':
+    case 'r':
+      reloadCbk();
+      break;
     case 'F':
     case 'f':
       mapFindWin();
       break;
     case 'N':
     case 'n':
-      nextPageCbk(NULL, 0, gTrue);
+      gotoNextPage(1, !(modifiers & Mod5Mask));
       break;
     case 'P':
     case 'p':
-      prevPageCbk(NULL, 0, gTrue);
+      gotoPrevPage(1, !(modifiers & Mod5Mask), gFalse);
       break;
     case ' ':
-      if (vScrollbar->getPos() >=
-         canvas->getRealHeight() - canvas->getHeight()) {
-       nextPageCbk(NULL, 0, gTrue);
+      if (fullScreen ||
+         vScrollbar->getPos() >=
+           canvas->getRealHeight() - canvas->getHeight()) {
+       gotoNextPage(1, gTrue);
       } else {
        vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(),
                           canvas->getHeight());
@@ -660,17 +1150,104 @@ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
       break;
     case '\b':                 // bs
     case '\177':               // del
-      if (vScrollbar->getPos() == 0) {
-       prevPageCbk(NULL, 0, gTrue);
+      if (fullScreen) {
+       gotoPrevPage(1, gTrue, gFalse);
+      } else if (vScrollbar->getPos() == 0) {
+       gotoPrevPage(1, gFalse, gTrue);
       } else {
        vScrollbar->setPos(vScrollbar->getPos() - canvas->getHeight(),
                           canvas->getHeight());
        canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       }
       break;
+    case 'v':
+      forwardCbk(NULL, 0, gTrue);
+      break;
+    case 'b':
+      backCbk(NULL, 0, gTrue);
+      break;
+    case 'g':
+      if (fullScreen) {
+       break;
+      }
+      pageNumText->selectAll();
+      pageNumText->activate(gTrue);
+      break;
+    case 'h':                  // vi-style left
+      if (fullScreen) {
+       break;
+      }
+      if (viKeys) {
+       hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
+       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+      }
+      break;
+    case 'l':                  // vi-style right
+      if (fullScreen) {
+       break;
+      }
+      if (viKeys) {
+       hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
+       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+      }
+      break;
+    case 'k':                  // vi-style up
+      if (fullScreen) {
+       break;
+      }
+      if (viKeys) {
+       vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
+       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+      }
+      break;
+    case 'j':                  // vi-style down
+      if (fullScreen) {
+       break;
+      }
+      if (viKeys) {
+       vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
+       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+      }
+      break;
+    case '0':
+      if (fullScreen) {
+       break;
+      }
+      if (zoom != defZoom) {
+       zoomMenuBtn->setMenuItem(zoomMenu->getItem(defZoom - minZoom));
+      }
+      break;
+    case '+':
+      if (fullScreen) {
+       break;
+      }
+      if (zoom >= minZoom && zoom < maxZoom) {
+       zoomMenuBtn->setMenuItem(zoomMenu->getItem(zoom + 1 - minZoom));
+      }
+      break;
+    case '-':
+      if (fullScreen) {
+       break;
+      }
+      if (zoom > minZoom && zoom <= maxZoom) {
+       zoomMenuBtn->setMenuItem(zoomMenu->getItem(zoom - 1 - minZoom));
+      }
+      break;
+    case 'z':
+      if (fullScreen) {
+       break;
+      }
+      zoomMenuBtn->setMenuItem(zoomMenu->getItem(maxZoom - minZoom + 2));
+      break;
+    case 'w':
+      if (fullScreen) {
+       break;
+      }
+      zoomMenuBtn->setMenuItem(zoomMenu->getItem(maxZoom - minZoom + 3));
+      break;
     case '\014':               // ^L
       win->redraw();
-      displayPage(page, zoom, rotate);
+      displayPage(page, zoom, rotate, gFalse);
       break;
     case 'Q':
     case 'q':
@@ -680,11 +1257,19 @@ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
   } else {
     switch (key) {
     case XK_Home:
+    case XK_KP_Home:
+      if (fullScreen) {
+       break;
+      }
       hScrollbar->setPos(0, canvas->getWidth());
       vScrollbar->setPos(0, canvas->getHeight());
       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       break;
     case XK_End:
+    case XK_KP_End:
+      if (fullScreen) {
+       break;
+      }
       hScrollbar->setPos(canvas->getRealWidth() - canvas->getWidth(),
                         canvas->getWidth());
       vScrollbar->setPos(canvas->getRealHeight() - canvas->getHeight(),
@@ -692,8 +1277,11 @@ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       break;
     case XK_Page_Up:
-      if (vScrollbar->getPos() == 0) {
-       prevPageCbk(NULL, 0, gTrue);
+    case XK_KP_Page_Up:
+      if (fullScreen) {
+       gotoPrevPage(1, gTrue, gFalse);
+      } else if (vScrollbar->getPos() == 0) {
+       gotoPrevPage(1, gFalse, gTrue);
       } else {
        vScrollbar->setPos(vScrollbar->getPos() - canvas->getHeight(),
                           canvas->getHeight());
@@ -701,9 +1289,11 @@ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
       }
       break;
     case XK_Page_Down:
-      if (vScrollbar->getPos() >=
-         canvas->getRealHeight() - canvas->getHeight()) {
-       nextPageCbk(NULL, 0, gTrue);
+    case XK_KP_Page_Down:
+      if (fullScreen ||
+         vScrollbar->getPos() >=
+           canvas->getRealHeight() - canvas->getHeight()) {
+       gotoNextPage(1, gTrue);
       } else {
        vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(),
                           canvas->getHeight());
@@ -711,18 +1301,34 @@ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
       }
       break;
     case XK_Left:
+    case XK_KP_Left:
+      if (fullScreen) {
+       break;
+      }
       hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       break;
     case XK_Right:
+    case XK_KP_Right:
+      if (fullScreen) {
+       break;
+      }
       hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       break;
     case XK_Up:
+    case XK_KP_Up:
+      if (fullScreen) {
+       break;
+      }
       vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       break;
     case XK_Down:
+    case XK_KP_Down:
+      if (fullScreen) {
+       break;
+      }
       vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
       break;
@@ -737,20 +1343,23 @@ static void menuCbk(LTKMenuItem *item) {
   case menuOpen:
     mapOpenDialog();
     break;
+  case menuReload:
+    reloadCbk();
+    break;
   case menuSavePDF:
     if (doc)
       mapSaveDialog();
     break;
-  case menuRotateLeft:
+  case menuRotateCCW:
     if (doc) {
       r = (rotate == 0) ? 270 : rotate - 90;
-      displayPage(page, zoom, r);
+      displayPage(page, zoom, r, gFalse);
     }
     break;
-  case menuRotateRight:
+  case menuRotateCW:
     if (doc) {
       r = (rotate == 270) ? 0 : rotate + 90;
-      displayPage(page, zoom, r);
+      displayPage(page, zoom, r, gFalse);
     }
     break;
   case menuQuit:
@@ -765,13 +1374,53 @@ static void menuCbk(LTKMenuItem *item) {
 
 static void buttonPressCbk(LTKWidget *canvas1, int n,
                           int mx, int my, int button, GBool dblClick) {
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0) {
     return;
-  if (button == 1) {
+  }
+  switch (button) {
+  case 1:
     setSelection(mx, my, mx, my);
-  } else if (button == 2) {
-    panMX = mx - hScrollbar->getPos();
-    panMY = my - vScrollbar->getPos();
+    break;
+  case 2:
+    if (!fullScreen) {
+      panMX = mx - hScrollbar->getPos();
+      panMY = my - vScrollbar->getPos();
+    }
+    break;
+  case 4: // mouse wheel up
+    if (fullScreen) {
+      gotoPrevPage(1, gTrue, gFalse);
+    } else if (vScrollbar->getPos() == 0) {
+      gotoPrevPage(1, gFalse, gTrue);
+    } else {
+      vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
+      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    }
+    break;
+  case 5: // mouse wheel down
+    if (fullScreen ||
+       vScrollbar->getPos() >=
+       canvas->getRealHeight() - canvas->getHeight()) {
+      gotoNextPage(1, gTrue);
+    } else {
+      vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
+      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    }
+    break;
+  case 6: // second mouse wheel right
+    if (fullScreen) {
+      return;
+    }
+    hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
+    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    break;
+  case 7: // second mouse wheel left
+    if (fullScreen) {
+      return;
+    }
+    hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
+    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    break;
   }
 }
 
@@ -779,7 +1428,7 @@ static void buttonReleaseCbk(LTKWidget *canvas1, int n,
                             int mx, int my, int button, GBool click) {
   GString *s;
 
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0)
     return;
 
   if (button == 1) {
@@ -789,6 +1438,8 @@ static void buttonReleaseCbk(LTKWidget *canvas1, int n,
       if (doc->okToCopy()) {
        s = out->getText(selectXMin, selectYMin, selectXMax, selectYMax);
        win->setSelection(NULL, s);
+      } else {
+       error(-1, "Copying of text from this document is not allowed.");
       }
 #endif
 
@@ -807,11 +1458,10 @@ static void doLink(int mx, int my) {
   GString *namedDest;
   char *s;
   GString *fileName;
-  Ref pageRef;
-  int pg;
+  GString *actionName;
   double x, y;
-  int dx, dy;
   LTKButtonDialog *dialog;
+  int i;
 
   // look for a link
   out->cvtDevToUser(mx, my, &x, &y);
@@ -854,64 +1504,13 @@ static void doLink(int mx, int my) {
        dest = doc->findDest(namedDest);
        delete namedDest;
       }
-      if (!dest) {
-       if (kind == actionGoToR)
-         displayPage(1, zoom, 0);
+      if (dest) {
+       displayDest(dest, zoom, rotate, gTrue);
+       delete dest;
       } else {
-       if (dest->isPageRef()) {
-         pageRef = dest->getPageRef();
-         pg = doc->findPage(pageRef.num, pageRef.gen);
-       } else {
-         pg = dest->getPageNum();
+       if (kind == actionGoToR) {
+         displayPage(1, zoom, 0, gTrue);
        }
-       if (pg > 0 && pg != page)
-         displayPage(pg, zoom, rotate);
-       else if (pg <= 0)
-         displayPage(1, zoom, rotate);
-       switch (dest->getKind()) {
-       case destXYZ:
-         out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
-         if (dest->getChangeLeft() || dest->getChangeTop()) {
-           if (dest->getChangeLeft())
-             hScrollbar->setPos(dx, canvas->getWidth());
-           if (dest->getChangeTop())
-             vScrollbar->setPos(dy, canvas->getHeight());
-           canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-         }
-         //~ what is the zoom parameter?
-         break;
-       case destFit:
-       case destFitB:
-         //~ do fit
-         hScrollbar->setPos(0, canvas->getWidth());
-         vScrollbar->setPos(0, canvas->getHeight());
-         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-         break;
-       case destFitH:
-       case destFitBH:
-         //~ do fit
-         out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
-         hScrollbar->setPos(0, canvas->getWidth());
-         vScrollbar->setPos(dy, canvas->getHeight());
-         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-         break;
-       case destFitV:
-       case destFitBV:
-         //~ do fit
-         out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
-         hScrollbar->setPos(dx, canvas->getWidth());
-         vScrollbar->setPos(0, canvas->getHeight());
-         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-         break;
-       case destFitR:
-         //~ do fit
-         out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
-         hScrollbar->setPos(dx, canvas->getWidth());
-         vScrollbar->setPos(dy, canvas->getHeight());
-         canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-         break;
-       }
-       delete dest;
       }
       break;
 
@@ -929,7 +1528,7 @@ static void doLink(int mx, int my) {
                         grabPath(doc->getFileName()->getCString()), s);
        if (!loadFile(fileName))
          return;
-       displayPage(1, zoom, rotate);
+       displayPage(1, zoom, rotate, gTrue);
       } else {
        fileName = fileName->copy();
        if (((LinkLaunch *)action)->getParams()) {
@@ -958,13 +1557,30 @@ static void doLink(int mx, int my) {
     case actionURI:
       if (urlCommand) {
        for (s = urlCommand->getCString(); *s; ++s) {
-         if (s[0] == '%' && s[1] == 's')
+         if (s[0] == '%' && s[1] == 's') {
            break;
+         }
        }
        if (s) {
-         fileName = new GString(urlCommand->getCString(),
-                                s - urlCommand->getCString());
-         fileName->append(((LinkURI *)action)->getURI());
+         fileName = ((LinkURI *)action)->getURI()->copy();
+         // filter out any quote marks (' or ") to avoid a potential
+         // security hole
+         i = 0;
+         while (i < fileName->getLength()) {
+           if (fileName->getChar(i) == '"') {
+             fileName->del(i);
+             fileName->insert(i, "%22");
+             i += 3;
+           } else if (fileName->getChar(i) == '\'') {
+             fileName->del(i);
+             fileName->insert(i, "%27");
+             i += 3;
+           } else {
+             ++i;
+           }
+         }
+         fileName->insert(0, urlCommand->getCString(),
+                          s - urlCommand->getCString());
          fileName->append(s+2);
        } else {
          fileName = urlCommand->copy();
@@ -979,8 +1595,33 @@ static void doLink(int mx, int my) {
        system(fileName->getCString());
        delete fileName;
       } else {
-       fprintf(errFile, "URI: %s\n",
-               ((LinkURI *)action)->getURI()->getCString());
+       printf("URI: %s\n", ((LinkURI *)action)->getURI()->getCString());
+      }
+      break;
+
+    // Named action
+    case actionNamed:
+      actionName = ((LinkNamed *)action)->getName();
+      if (!actionName->cmp("NextPage")) {
+       gotoNextPage(1, gTrue);
+      } else if (!actionName->cmp("PrevPage")) {
+       gotoPrevPage(1, gTrue, gFalse);
+      } else if (!actionName->cmp("FirstPage")) {
+       if (page != 1) {
+         displayPage(1, zoom, rotate, gTrue);
+       }
+      } else if (!actionName->cmp("LastPage")) {
+       if (page != doc->getNumPages()) {
+         displayPage(doc->getNumPages(), zoom, rotate, gTrue);
+       }
+      } else if (!actionName->cmp("GoBack")) {
+       backCbk(NULL, 0, gTrue);
+      } else if (!actionName->cmp("GoForward")) {
+       forwardCbk(NULL, 0, gTrue);
+      } else if (!actionName->cmp("Quit")) {
+       quitCbk(NULL, 0, gTrue);
+      } else {
+       error(-1, "Unknown named action: '%s'", actionName->getCString());
       }
       break;
 
@@ -998,39 +1639,47 @@ static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my) {
   LinkAction *action;
   char *s;
 
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0)
     return;
   out->cvtDevToUser(mx, my, &x, &y);
   if ((action = doc->findLink(x, y))) {
     if (action != linkAction) {
-      if (!linkAction)
+      if (!linkAction) {
        win->setCursor(XC_hand2);
+      }
       linkAction = action;
-      s = NULL;
-      switch (linkAction->getKind()) {
-      case actionGoTo:
-       s = "[internal link]";
-       break;
-      case actionGoToR:
-       s = ((LinkGoToR *)linkAction)->getFileName()->getCString();
-       break;
-      case actionLaunch:
-       s = ((LinkLaunch *)linkAction)->getFileName()->getCString();
-       break;
-      case actionURI:
-       s = ((LinkURI *)action)->getURI()->getCString();
-       break;
-      case actionUnknown:
-       s = "[unknown link]";
-       break;
+      if (!fullScreen) {
+       s = NULL;
+       switch (linkAction->getKind()) {
+       case actionGoTo:
+         s = "[internal link]";
+         break;
+       case actionGoToR:
+         s = ((LinkGoToR *)linkAction)->getFileName()->getCString();
+         break;
+       case actionLaunch:
+         s = ((LinkLaunch *)linkAction)->getFileName()->getCString();
+         break;
+       case actionURI:
+         s = ((LinkURI *)action)->getURI()->getCString();
+         break;
+       case actionNamed:
+         s = ((LinkNamed *)linkAction)->getName()->getCString();
+         break;
+       case actionUnknown:
+         s = "[unknown link]";
+         break;
+       }
+       linkLabel->setText(s);
       }
-      linkLabel->setText(s);
     }
   } else {
     if (linkAction) {
       linkAction = NULL;
       win->setDefaultCursor();
-      linkLabel->setText(NULL);
+      if (!fullScreen) {
+       linkLabel->setText(NULL);
+      }
     }
   }
 }
@@ -1099,7 +1748,7 @@ static void mouseDragCbk(LTKWidget *widget, int widgetNum,
     setSelection(xMin, yMin, xMax, yMax);
 
   // button 2: pan
-  } else if (button == 2) {
+  } else if (!fullScreen && button == 2) {
     mx -= hScrollbar->getPos();
     my -= vScrollbar->getPos();
     hScrollbar->setPos(hScrollbar->getPos() - (mx - panMX),
@@ -1117,71 +1766,111 @@ static void mouseDragCbk(LTKWidget *widget, int widgetNum,
 //------------------------------------------------------------------------
 
 static void nextPageCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc)
-    return;
-  if (page < doc->getNumPages()) {
-    vScrollbar->setPos(0, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    displayPage(page + 1, zoom, rotate);
-  } else {
-    XBell(display, 0);
-  }
+  gotoNextPage(1, gTrue);
 }
 
 static void nextTenPageCbk(LTKWidget *button, int n, GBool on) {
+  gotoNextPage(10, gTrue);
+}
+
+static void gotoNextPage(int inc, GBool top) {
   int pg;
 
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0) {
     return;
+  }
   if (page < doc->getNumPages()) {
-    vScrollbar->setPos(0, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    if ((pg = page + 10) > doc->getNumPages())
+    if (top && !fullScreen) {
+      vScrollbar->setPos(0, canvas->getHeight());
+      canvas->setScrollPos(hScrollbar->getPos(), vScrollbar->getPos());
+    }
+    if ((pg = page + inc) > doc->getNumPages()) {
       pg = doc->getNumPages();
-    displayPage(pg, zoom, rotate);
+    }
+    displayPage(pg, zoom, rotate, gTrue);
   } else {
     XBell(display, 0);
   }
 }
 
 static void prevPageCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc)
-    return;
-  if (page > 1) {
-    vScrollbar->setPos(0, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    displayPage(page - 1, zoom, rotate);
-  } else {
-    XBell(display, 0);
-  }
+  gotoPrevPage(1, gTrue, gFalse);
 }
 
 static void prevTenPageCbk(LTKWidget *button, int n, GBool on) {
+  gotoPrevPage(10, gTrue, gFalse);
+}
+
+static void gotoPrevPage(int dec, GBool top, GBool bottom) {
   int pg;
 
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0) {
     return;
+  }
   if (page > 1) {
-    vScrollbar->setPos(0, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    if ((pg = page - 10) < 1)
+    if (top && !fullScreen) {
+      vScrollbar->setPos(0, canvas->getHeight());
+      canvas->setScrollPos(hScrollbar->getPos(), vScrollbar->getPos());
+    } else if (bottom && !fullScreen) {
+      vScrollbar->setPos(canvas->getRealHeight() - canvas->getHeight(),
+                        canvas->getHeight());
+      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    }
+    if ((pg = page - dec) < 1) {
       pg = 1;
-    displayPage(pg, zoom, rotate);
+    }
+    displayPage(pg, zoom, rotate, gTrue);
   } else {
     XBell(display, 0);
   }
 }
 
+static void backCbk(LTKWidget *button, int n, GBool on) {
+  if (historyBLen <= 1) {
+    XBell(display, 0);
+    return;
+  }
+  if (--historyCur < 0)
+    historyCur = historySize - 1;
+  --historyBLen;
+  ++historyFLen;
+  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+    if (!loadFile(history[historyCur].fileName->copy())) {
+      XBell(display, 0);
+      return;
+    }
+  }
+  displayPage(history[historyCur].page, zoom, rotate, gFalse);
+}
+
+static void forwardCbk(LTKWidget *button, int n, GBool on) {
+  if (historyFLen == 0) {
+    XBell(display, 0);
+    return;
+  }
+  if (++historyCur == historySize)
+    historyCur = 0;
+  --historyFLen;
+  ++historyBLen;
+  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+    if (!loadFile(history[historyCur].fileName->copy())) {
+      XBell(display, 0);
+      return;
+    }
+  }
+  displayPage(history[historyCur].page, zoom, rotate, gFalse);
+}
+
 static void pageNumCbk(LTKWidget *textIn, int n, GString *text) {
   int page1;
   char s[20];
 
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0)
     return;
   page1 = atoi(text->getCString());
   if (page1 >= 1 && page1 <= doc->getNumPages()) {
     if (page1 != page)
-      displayPage(page1, zoom, rotate);
+      displayPage(page1, zoom, rotate, gTrue);
   } else {
     XBell(display, 0);
     sprintf(s, "%d", page);
@@ -1189,22 +1878,16 @@ static void pageNumCbk(LTKWidget *textIn, int n, GString *text) {
   }
 }
 
-static void zoomInCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc)
-    return;
-  if (zoom < maxZoom)
-    displayPage(page, zoom + 1, rotate);
-  else
-    XBell(display, 0);
-}
+static void zoomMenuCbk(LTKMenuItem *item) {
+  int z;
 
-static void zoomOutCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0) {
     return;
-  if (zoom > minZoom)
-    displayPage(page, zoom - 1, rotate);
-  else
-    XBell(display, 0);
+  }
+  z = item->getItemNum();
+  if (z != zoom) {
+    displayPage(page, z, rotate, gFalse);
+  }
 }
 
 static void postScriptCbk(LTKWidget *button, int n, GBool on) {
@@ -1240,6 +1923,17 @@ static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val) {
 //------------------------------------------------------------------------
 
 static void layoutCbk(LTKWindow *win1) {
+  if (page >= 0 && (zoom == zoomPage || zoom == zoomWidth)) {
+    displayPage(page, zoom, rotate, gFalse);
+  } else {
+    updateScrollbars();
+  }
+}
+
+static void updateScrollbars() {
+  if (fullScreen) {
+    return;
+  }
   hScrollbar->setLimits(0, canvas->getRealWidth() - 1);
   hScrollbar->setPos(hScrollbar->getPos(), canvas->getWidth());
   hScrollbar->setScrollDelta(16);
@@ -1255,19 +1949,23 @@ static void propChangeCbk(LTKWindow *win1, Atom atom) {
   Atom type;
   int format;
   Gulong size, remain;
-  char *p;
+  char *p, *q;
   GString *newFileName;
   int newPage;
+  GString *destName;
+  LinkDest *dest;
 
   // get command
   xwin = win1->getXWindow();
   if (XGetWindowProperty(display, xwin, remoteAtom,
                         0, remoteCmdLength/4, True, remoteAtom,
                         &type, &format, &size, &remain,
-                        (Guchar **)&cmd) != Success)
+                        (Guchar **)&cmd) != Success) {
     return;
-  if (size == 0)
+  }
+  if (size == 0) {
     return;
+  }
 
   // raise window
   if (cmd[0] == 'D' || cmd[0] == 'r'){
@@ -1278,10 +1976,22 @@ static void propChangeCbk(LTKWindow *win1, Atom atom) {
   // display file / page
   if (cmd[0] == 'd' || cmd[0] == 'D') {
     p = cmd + 2;
-    newPage = atoi(p);
-    if (!(p = strchr(p, ' ')))
+    q = strchr(p, ' ');
+    if (!q) {
       return;
-    newFileName = new GString(p + 1);
+    }
+    *q++ = '\0';
+    newPage = 1;
+    destName = NULL;
+    if (*p == '+') {
+      destName = new GString(p + 1);
+    } else {
+      newPage = atoi(p);
+    }
+    if (!q) {
+      return;
+    }
+    newFileName = new GString(q);
     XFree((XPointer)cmd);
     if (!doc || newFileName->cmp(doc->getFileName())) {
       if (!loadFile(newFileName))
@@ -1289,8 +1999,16 @@ static void propChangeCbk(LTKWindow *win1, Atom atom) {
     } else {
       delete newFileName;
     }
-    if (newPage != page && newPage >= 1 && newPage <= doc->getNumPages())
-      displayPage(newPage, zoom, rotate);
+    if (destName) {
+      if ((dest = doc->findDest(destName))) {
+       displayDest(dest, zoom, rotate, gTrue);
+       delete dest;
+      }
+      delete destName;
+    } else if (newPage != page &&
+              newPage >= 1 && newPage <= doc->getNumPages()) {
+      displayPage(newPage, zoom, rotate, gTrue);
+    }
 
   // quit
   } else if (cmd[0] == 'q') {
@@ -1365,6 +2083,9 @@ static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax) {
   selectYMax = newYMax;
 
   // scroll canvas if necessary
+  if (fullScreen) {
+    return;
+  }
   needScroll = gFalse;
   w = canvas->getWidth();
   h = canvas->getHeight();
@@ -1421,10 +2142,10 @@ static void openButtonCbk(LTKWidget *button, int n, GBool on) {
   sel = NULL;
   if (n == 1) {
     fileReq = (LTKFileReq *)openDialog->findWidget("fileReq");
-    if ((sel = fileReq->getSelection()))
-      openSelectCbk(fileReq, 0, sel);
-    else
-      XBell(display, 0);
+    if (!(sel = fileReq->getSelection())) {
+      return;
+    }
+    openSelectCbk(fileReq, 0, sel);
   }
   if (openDialog) {
     if (sel) {
@@ -1446,8 +2167,32 @@ static void openSelectCbk(LTKWidget *widget, int n, GString *name) {
     delete openDialog;
     openDialog = NULL;
   }
-  if (loadFile(name1))
-    displayPage(1, zoom, rotate);
+  if (loadFile(name1)) {
+    if (!fullScreen) {
+      vScrollbar->setPos(0, canvas->getHeight());
+      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
+    }
+    displayPage(1, zoom, rotate, gTrue);
+  }
+}
+
+//------------------------------------------------------------------------
+// "Reload"
+//------------------------------------------------------------------------
+
+static void reloadCbk() {
+  int pg;
+
+  if (!doc) {
+    return;
+  }
+  pg = page;
+  if (loadFile(doc->getFileName()->copy())) {
+    if (pg > doc->getNumPages()) {
+      pg = doc->getNumPages();
+    }
+    displayPage(pg, zoom, rotate, gFalse);
+  }
 }
 
 //------------------------------------------------------------------------
@@ -1470,10 +2215,10 @@ static void saveButtonCbk(LTKWidget *button, int n, GBool on) {
   sel = NULL;
   if (n == 1) {
     fileReq = (LTKFileReq *)saveDialog->findWidget("fileReq");
-    if ((sel = fileReq->getSelection()))
-      saveSelectCbk(fileReq, 0, sel);
-    else
-      XBell(display, 0);
+    if (!(sel = fileReq->getSelection())) {
+      return;
+    }
+    saveSelectCbk(fileReq, 0, sel);
   }
   if (saveDialog) {
     if (sel) {
@@ -1554,13 +2299,15 @@ static void psButtonCbk(LTKWidget *button, int n, GBool on) {
     psDialog->setBusyCursor(gTrue);
     win->setBusyCursor(gTrue);
     if (doc->okToPrint()) {
-      psOut = new PSOutputDev(psFileName->getCString(), doc->getCatalog(),
-                             psFirstPage, psLastPage, gTrue, gFalse);
+      psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(),
+                             doc->getCatalog(), psFirstPage, psLastPage,
+                             psModePS);
       if (psOut->isOk()) {
-       doc->displayPages(psOut, psFirstPage, psLastPage,
-                         zoomDPI[zoom - minZoom], rotate);
+       doc->displayPages(psOut, psFirstPage, psLastPage, 72, 0, gFalse);
       }
       delete psOut;
+    } else {
+      error(-1, "Printing this document is not allowed.");
     }
 
     delete psDialog;
@@ -1577,10 +2324,19 @@ static void psButtonCbk(LTKWidget *button, int n, GBool on) {
 //------------------------------------------------------------------------
 
 static void mapAboutWin() {
+  int i;
+
   if (aboutWin) {
     aboutWin->raise();
   } else {
     aboutWin = makeAboutWindow(app);
+    aboutWin->setLayoutCbk(&aboutLayoutCbk);
+    aboutList = (LTKList *)aboutWin->findWidget("list");
+    aboutHScrollbar = (LTKScrollbar *)aboutWin->findWidget("hScrollbar");
+    aboutVScrollbar = (LTKScrollbar *)aboutWin->findWidget("vScrollbar");
+    for (i = 0; aboutWinText[i]; ++i) {
+      aboutList->addLine(aboutWinText[i]);
+    }
     aboutWin->layout(-1, -1, -1, -1);
     aboutWin->map();
   }
@@ -1591,12 +2347,30 @@ static void closeAboutCbk(LTKWidget *button, int n, GBool on) {
   aboutWin = NULL;
 }
 
+static void aboutLayoutCbk(LTKWindow *winA) {
+  aboutHScrollbar->setLimits(0, aboutList->getMaxWidth() - 1);
+  aboutHScrollbar->setPos(aboutHScrollbar->getPos(), aboutList->getWidth());
+  aboutVScrollbar->setLimits(0, aboutList->getNumLines() - 1);
+  aboutVScrollbar->setPos(aboutVScrollbar->getPos(),
+                         aboutList->getDisplayedLines());
+}
+
+static void aboutScrollVertCbk(LTKWidget *scrollbar, int n, int val) {
+  aboutList->scrollTo(val, aboutHScrollbar->getPos());
+  XSync(display, False);
+}
+
+static void aboutScrollHorizCbk(LTKWidget *scrollbar, int n, int val) {
+  aboutList->scrollTo(aboutVScrollbar->getPos(), val);
+  XSync(display, False);
+}
+
 //------------------------------------------------------------------------
 // "Find" window
 //------------------------------------------------------------------------
 
 static void findCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0)
     return;
   mapFindWin();
 }
@@ -1608,17 +2382,16 @@ static void mapFindWin() {
     findWin = makeFindWindow(app);
     findWin->layout(-1, -1, -1, -1);
     findWin->map();
+    findTextIn = (LTKTextIn *)findWin->findWidget("text");
   }
+  findTextIn->activate(gTrue);
 }
 
 static void findButtonCbk(LTKWidget *button, int n, GBool on) {
-  LTKTextIn *textIn;
-
-  if (!doc)
+  if (!doc || doc->getNumPages() == 0)
     return;
   if (n == 1) {
-    textIn = (LTKTextIn *)findWin->findWidget("text");
-    doFind(textIn->getText()->getCString());
+    doFind(findTextIn->getText()->getCString());
   } else {
     delete findWin;
     findWin = NULL;
@@ -1626,12 +2399,14 @@ static void findButtonCbk(LTKWidget *button, int n, GBool on) {
 }
 
 static void doFind(char *s) {
+  Unicode *u;
   TextOutputDev *textOut;
   int xMin, yMin, xMax, yMax;
   double xMin1, yMin1, xMax1, yMax1;
   int pg;
   GBool top;
   GString *s1;
+  int len, i;
 
   // check for zero-length string
   if (!s[0]) {
@@ -1643,6 +2418,15 @@ static void doFind(char *s) {
   win->setBusyCursor(gTrue);
   findWin->setBusyCursor(gTrue);
 
+  // convert to Unicode
+#if 1 //~ should do something more intelligent here
+  len = strlen(s);
+  u = (Unicode *)gmalloc(len * sizeof(Unicode));
+  for (i = 0; i < len; ++i) {
+    u[i] = (Unicode)(s[i] & 0xff);
+  }
+#endif
+
   // search current page starting at current selection or top of page
   xMin = yMin = xMax = yMax = 0;
   if (selectXMin < selectXMax && selectYMin < selectYMax) {
@@ -1652,26 +2436,31 @@ static void doFind(char *s) {
   } else {
     top = gTrue;
   }
-  if (out->findText(s, top, gTrue, &xMin, &yMin, &xMax, &yMax))
+  if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
     goto found;
+  }
 
   // search following pages
-  textOut = new TextOutputDev(NULL, gFalse);
+  textOut = new TextOutputDev(NULL, gFalse, gFalse);
   if (!textOut->isOk()) {
     delete textOut;
     goto done;
   }
   for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
     doc->displayPage(textOut, pg, 72, 0, gFalse);
-    if (textOut->findText(s, gTrue, gTrue, &xMin1, &yMin1, &xMax1, &yMax1))
+    if (textOut->findText(u, len, gTrue, gTrue,
+                         &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
+    }
   }
 
   // search previous pages
   for (pg = 1; pg < page; ++pg) {
     doc->displayPage(textOut, pg, 72, 0, gFalse);
-    if (textOut->findText(s, gTrue, gTrue, &xMin1, &yMin1, &xMax1, &yMax1))
+    if (textOut->findText(u, len, gTrue, gTrue,
+                         &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
+    }
   }
   delete textOut;
 
@@ -1679,8 +2468,9 @@ static void doFind(char *s) {
   if (selectXMin < selectXMax && selectYMin < selectYMax) {
     xMax = selectXMin;
     yMax = (selectYMin + selectYMax) / 2;
-    if (out->findText(s, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax))
+    if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
       goto found;
+    }
   }
 
   // not found
@@ -1690,9 +2480,11 @@ static void doFind(char *s) {
   // found on a different page
  foundPage:
   delete textOut;
-  displayPage(pg, zoom, rotate);
-  if (!out->findText(s, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax))
-    goto done; // this can happen if coalescing is bad
+  displayPage(pg, zoom, rotate, gTrue);
+  if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+    // this can happen if coalescing is bad
+    goto done;
+  }
 
   // found: change the selection
  found:
@@ -1705,6 +2497,8 @@ static void doFind(char *s) {
 #endif
 
  done:
+  gfree(u);
+
   // reset cursors to normal
   win->setBusyCursor(gFalse);
   findWin->setBusyCursor(gFalse);