//======================================================================== // // xpdf.cc // // Copyright 1996-2002 Glyph & Cog, LLC // //======================================================================== #include #include #include #include #include #include #include #include #include #include "gtypes.h" #include "GString.h" #include "parseargs.h" #include "gfile.h" #include "gmem.h" #include "GlobalParams.h" #include "LTKAll.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "Link.h" #include "PDFDoc.h" #include "XOutputDev.h" #include "LTKOutputDev.h" #include "PSOutputDev.h" #include "TextOutputDev.h" #include "Error.h" #include "config.h" #ifdef XlibSpecificationRelease #if XlibSpecificationRelease < 5 typedef char *XPointer; #endif #else typedef char *XPointer; #endif // hack around old X includes which are missing these symbols #ifndef XK_Page_Up #define XK_Page_Up 0xFF55 #endif #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 512 enum XpdfMenuItem { menuOpen, menuReload, menuSavePDF, menuRotateCCW, menuRotateCW, menuQuit }; //------------------------------------------------------------------------ // prototypes //------------------------------------------------------------------------ // loadFile / displayPage static GBool loadFile(GString *fileName); 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, char *s, int n); static void menuCbk(LTKMenuItem *item); // mouse callbacks static void buttonPressCbk(LTKWidget *canvas1, int n, int mx, int my, int button, GBool dblClick); static void buttonReleaseCbk(LTKWidget *canvas1, int n, int mx, int my, int button, GBool click); static void doLink(int mx, int my); static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my); static void mouseDragCbk(LTKWidget *widget, int widgetNum, int mx, int my, int button); // 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 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); // scrollbar callbacks static void scrollVertCbk(LTKWidget *scrollbar, int n, int val); 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 static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax); // "Open" dialog 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); static void saveSelectCbk(LTKWidget *widget, int n, GString *name); // "PostScript" dialog static void mapPSDialog(); 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); static void mapFindWin(); static void findButtonCbk(LTKWidget *button, int n, GBool on); static void doFind(char *s); // app kill callback static void killCbk(LTKWindow *win1); //------------------------------------------------------------------------ // "About" window text //------------------------------------------------------------------------ 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 = = next page", " p = = previous page", " = scroll down", " = = 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", " / = top / bottom of page", " = scroll", "", "For more information, please read the xpdf(1) man page.", NULL }; //------------------------------------------------------------------------ // command line options //------------------------------------------------------------------------ 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[] = { {"-g", argStringDummy, NULL, 0, "initial window geometry"}, {"-geometry", argStringDummy, NULL, 0, "initial window geometry"}, {"-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"}, {"-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", argInt, &paperHeight, 0, "paper height, in points"}, {"-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} }; //------------------------------------------------------------------------ // global variables //------------------------------------------------------------------------ // zoom factor is 1.2 (similar to DVI magsteps) #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 }; static PDFDoc *doc; static LTKOutputDev *out; static int page; 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 selectXMax, selectYMax; // is no selection 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 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; 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 //------------------------------------------------------------------------ int main(int argc, char *argv[]) { Window xwin; XGCValues gcValues; char cmd[remoteCmdLength]; 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 app = NULL; win = NULL; out = NULL; remoteAtom = None; doc = NULL; psFileName = NULL; fileReqDir = getCurrentDir(); ret = 0; // parse args ok = parseArgs(argDesc, &argc, argv); // read config file 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", "Xpdf", opts, &argc, argv); app->setKillCbk(&killCbk); display = app->getDisplay(); // check command line if (doRemoteRaise) ok = ok && remoteName[5] && !doRemoteQuit && argc >= 1 && argc <= 3; else if (doRemoteQuit) ok = ok && remoteName[5] && argc == 1; else ok = ok && argc >= 1 && argc <= 3; if (!ok || printVersion || printHelp) { fprintf(stderr, "xpdf version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("xpdf", "[ [ | +]]", argDesc); } ret = 1; goto done2; } if (argc >= 2) { name = new GString(argv[1]); } else { name = NULL; } 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]) { remoteAtom = XInternAtom(display, remoteName, False); xwin = XGetSelectionOwner(display, remoteAtom); if (xwin != None) { if (name) { 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; } else if (doRemoteRaise) { XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)"r", 2); } else if (doRemoteQuit) { XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)"q", 2); } goto done2; } if (doRemoteQuit) { if (destName) { delete destName; } goto done2; } } // no history yet historyCur = historySize - 1; historyBLen = historyFLen = 0; for (i = 0; i < historySize; ++i) history[i].fileName = NULL; // open PDF file if (name) { if (!loadFile(name)) { ret = 1; goto done1; } delete fileReqDir; fileReqDir = makePathAbsolute(grabPath(name->getCString())); } // check for legal page number if (doc && (pg < 1 || pg > doc->getNumPages())) { pg = 1; } // create window 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"); vScrollbar = (LTKScrollbar *)win->findWidget("vScrollbar"); 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); if (!fullScreen) { hScrollbar->setRepeatPeriod(0); vScrollbar->setRepeatPeriod(0); } // get parameters urlCommand = globalParams->getURLCommand(); // get X resources windowTitle = app->getStringResource("title", NULL); installCmap = app->getBoolResource("installCmap", gFalse); if (installCmap) { win->setInstallCmap(gTrue); } rgbCubeSize = app->getIntResource("rgbCubeSize", defaultRGBCube); 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 if (fullScreen) { x = y = 0; width = app->getDisplayWidth(); height = app->getDisplayHeight(); } else { 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; } } } // finish setting up window 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 { title = new GString("xpdf"); } win->setTitle(title); win->layout(x, y, width, height); win->map(); aboutWin = NULL; psDialog = NULL; openDialog = NULL; saveDialog = NULL; findWin = NULL; gcValues.foreground = BlackPixel(display, win->getScreenNum()) ^ WhitePixel(display, win->getScreenNum()); gcValues.function = GXxor; selectGC = XCreateGC(display, win->getXWindow(), GCForeground | GCFunction, &gcValues); // set up remote server if (remoteAtom != None) { win->setPropChangeCbk(&propChangeCbk); xwin = win->getXWindow(); XSetSelectionOwner(display, remoteAtom, xwin, CurrentTime); } // create output device out = new LTKOutputDev(win, reverseVideo, paperColor, installCmap, rgbCubeSize, !fullScreen); out->startDoc(doc ? doc->getXRef() : (XRef *)NULL); // display first page 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; do { app->doEvent(gTrue); } while (!quit); done1: // release remote control atom if (remoteAtom != None) XSetSelectionOwner(display, remoteAtom, None, CurrentTime); done2: // free stuff if (out) { delete out; } if (win) { delete win; } if (aboutWin) { delete aboutWin; } if (findWin) { delete findWin; } if (app) { delete app; } if (doc) { delete doc; } if (psFileName) { delete psFileName; } if (fileReqDir) { delete fileReqDir; } 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(stderr); gMemReport(stderr); return ret; } //------------------------------------------------------------------------ // loadFile / displayPage / displayDest //------------------------------------------------------------------------ static GBool loadFile(GString *fileName) { GString *title; PDFDoc *newDoc; GString *ownerPW, *userPW; char s[20]; char *p; // busy cursor if (win) win->setBusyCursor(gTrue); // open PDF file 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) win->setBusyCursor(gFalse); return gFalse; } // replace old document 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 (globalParams->getPSFile()) { psFileName = globalParams->getPSFile()->copy(); } else { p = fileName->getCString() + fileName->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) psFileName = new GString(fileName->getCString(), fileName->getLength() - 4); else psFileName = fileName->copy(); psFileName->append(".ps"); } psFirstPage = 1; psLastPage = doc->getNumPages(); // set up title, number-of-pages display; back to normal cursor if (win) { 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); } // done return gTrue; } 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 || 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 = pageA; zoom = zoomA; rotate = rotateA; // initialize mouse-related stuff linkAction = NULL; win->setDefaultCursor(); if (!fullScreen) { linkLabel->setText(NULL); } selectXMin = selectXMax = 0; selectYMin = selectYMax = 0; lastDragLeft = lastDragTop = gTrue; // draw the page 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 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 //------------------------------------------------------------------------ static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers, char *s, int n) { if (n > 0) { switch (s[0]) { case 'O': case 'o': mapOpenDialog(); break; case 'R': case 'r': reloadCbk(); break; case 'F': case 'f': mapFindWin(); break; case 'N': case 'n': gotoNextPage(1, !(modifiers & Mod5Mask)); break; case 'P': case 'p': gotoPrevPage(1, !(modifiers & Mod5Mask), gFalse); break; case ' ': if (fullScreen || vScrollbar->getPos() >= canvas->getRealHeight() - canvas->getHeight()) { gotoNextPage(1, gTrue); } else { vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(), canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); } break; case '\b': // bs case '\177': // del 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, gFalse); break; case 'Q': case 'q': quitCbk(NULL, 0, gTrue); break; } } 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(), canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); break; case XK_Page_Up: 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()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); } break; case XK_Page_Down: 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()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); } 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; } } } static void menuCbk(LTKMenuItem *item) { int r; switch (item->getItemNum()) { case menuOpen: mapOpenDialog(); break; case menuReload: reloadCbk(); break; case menuSavePDF: if (doc) mapSaveDialog(); break; case menuRotateCCW: if (doc) { r = (rotate == 0) ? 270 : rotate - 90; displayPage(page, zoom, r, gFalse); } break; case menuRotateCW: if (doc) { r = (rotate == 270) ? 0 : rotate + 90; displayPage(page, zoom, r, gFalse); } break; case menuQuit: quit = gTrue; break; } } //------------------------------------------------------------------------ // mouse callbacks //------------------------------------------------------------------------ static void buttonPressCbk(LTKWidget *canvas1, int n, int mx, int my, int button, GBool dblClick) { if (!doc || doc->getNumPages() == 0) { return; } switch (button) { case 1: setSelection(mx, my, mx, my); 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; } } static void buttonReleaseCbk(LTKWidget *canvas1, int n, int mx, int my, int button, GBool click) { GString *s; if (!doc || doc->getNumPages() == 0) return; if (button == 1) { // selection if (selectXMin < selectXMax && selectYMin < selectYMax) { #ifndef NO_TEXT_SELECT 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 // link } else { setSelection(mx, my, mx, my); doLink(mx, my); } } } static void doLink(int mx, int my) { LinkActionKind kind; LinkAction *action = NULL; LinkDest *dest; GString *namedDest; char *s; GString *fileName; GString *actionName; double x, y; LTKButtonDialog *dialog; int i; // look for a link out->cvtDevToUser(mx, my, &x, &y); if ((action = doc->findLink(x, y))) { switch (kind = action->getKind()) { // GoTo / GoToR action case actionGoTo: case actionGoToR: if (kind == actionGoTo) { dest = NULL; namedDest = NULL; if ((dest = ((LinkGoTo *)action)->getDest())) dest = dest->copy(); else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) namedDest = namedDest->copy(); } else { dest = NULL; namedDest = NULL; if ((dest = ((LinkGoToR *)action)->getDest())) dest = dest->copy(); else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) namedDest = namedDest->copy(); s = ((LinkGoToR *)action)->getFileName()->getCString(); //~ translate path name for VMS (deal with '/') if (isAbsolutePath(s)) fileName = new GString(s); else fileName = appendToPath( grabPath(doc->getFileName()->getCString()), s); if (!loadFile(fileName)) { if (dest) delete dest; if (namedDest) delete namedDest; return; } } if (namedDest) { dest = doc->findDest(namedDest); delete namedDest; } if (dest) { displayDest(dest, zoom, rotate, gTrue); delete dest; } else { if (kind == actionGoToR) { displayPage(1, zoom, 0, gTrue); } } break; // Launch action case actionLaunch: fileName = ((LinkLaunch *)action)->getFileName(); s = fileName->getCString(); if (!strcmp(s + fileName->getLength() - 4, ".pdf") || !strcmp(s + fileName->getLength() - 4, ".PDF")) { //~ translate path name for VMS (deal with '/') if (isAbsolutePath(s)) fileName = fileName->copy(); else fileName = appendToPath( grabPath(doc->getFileName()->getCString()), s); if (!loadFile(fileName)) return; displayPage(1, zoom, rotate, gTrue); } else { fileName = fileName->copy(); if (((LinkLaunch *)action)->getParams()) { fileName->append(' '); fileName->append(((LinkLaunch *)action)->getParams()); } #ifdef VMS fileName->insert(0, "spawn/nowait "); #elif defined(__EMX__) fileName->insert(0, "start /min /n "); #else fileName->append(" &"); #endif dialog = new LTKButtonDialog(win, "xpdf: Launch", "Execute the command:", fileName->getCString(), NULL, "Ok", "Cancel"); if (dialog->go()) system(fileName->getCString()); delete dialog; delete fileName; } break; // URI action case actionURI: if (urlCommand) { for (s = urlCommand->getCString(); *s; ++s) { if (s[0] == '%' && s[1] == 's') { break; } } if (s) { 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(); } #ifdef VMS fileName->insert(0, "spawn/nowait "); #elif defined(__EMX__) fileName->insert(0, "start /min /n "); #else fileName->append(" &"); #endif system(fileName->getCString()); delete fileName; } else { 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; // unknown action type case actionUnknown: error(-1, "Unknown link action type: '%s'", ((LinkUnknown *)action)->getAction()->getCString()); break; } } } static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my) { double x, y; LinkAction *action; char *s; if (!doc || doc->getNumPages() == 0) return; out->cvtDevToUser(mx, my, &x, &y); if ((action = doc->findLink(x, y))) { if (action != linkAction) { if (!linkAction) { win->setCursor(XC_hand2); } linkAction = action; 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); } } } else { if (linkAction) { linkAction = NULL; win->setDefaultCursor(); if (!fullScreen) { linkLabel->setText(NULL); } } } } static void mouseDragCbk(LTKWidget *widget, int widgetNum, int mx, int my, int button) { int x, y; int xMin, yMin, xMax, yMax; // button 1: select if (button == 1) { // clip mouse coords x = mx; if (x < 0) x = 0; else if (x >= canvas->getRealWidth()) x = canvas->getRealWidth() - 1; y = my; if (y < 0) y = 0; else if (y >= canvas->getRealHeight()) y = canvas->getRealHeight() - 1; // move appropriate edges of selection if (lastDragLeft) { if (x < selectXMax) { xMin = x; xMax = selectXMax; } else { xMin = selectXMax; xMax = x; lastDragLeft = gFalse; } } else { if (x > selectXMin) { xMin = selectXMin; xMax = x; } else { xMin = x; xMax = selectXMin; lastDragLeft = gTrue; } } if (lastDragTop) { if (y < selectYMax) { yMin = y; yMax = selectYMax; } else { yMin = selectYMax; yMax = y; lastDragTop = gFalse; } } else { if (y > selectYMin) { yMin = selectYMin; yMax = y; } else { yMin = y; yMax = selectYMin; lastDragTop = gTrue; } } // redraw the selection setSelection(xMin, yMin, xMax, yMax); // button 2: pan } else if (!fullScreen && button == 2) { mx -= hScrollbar->getPos(); my -= vScrollbar->getPos(); hScrollbar->setPos(hScrollbar->getPos() - (mx - panMX), canvas->getWidth()); vScrollbar->setPos(vScrollbar->getPos() - (my - panMY), canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); panMX = mx; panMY = my; } } //------------------------------------------------------------------------ // button callbacks //------------------------------------------------------------------------ static void nextPageCbk(LTKWidget *button, int n, GBool on) { 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 || doc->getNumPages() == 0) { return; } if (page < 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, gTrue); } else { XBell(display, 0); } } static void prevPageCbk(LTKWidget *button, int n, GBool on) { 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 || doc->getNumPages() == 0) { return; } if (page > 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, 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 || doc->getNumPages() == 0) return; page1 = atoi(text->getCString()); if (page1 >= 1 && page1 <= doc->getNumPages()) { if (page1 != page) displayPage(page1, zoom, rotate, gTrue); } else { XBell(display, 0); sprintf(s, "%d", page); pageNumText->setText(s); } } static void zoomMenuCbk(LTKMenuItem *item) { int z; if (!doc || doc->getNumPages() == 0) { return; } z = item->getItemNum(); if (z != zoom) { displayPage(page, z, rotate, gFalse); } } static void postScriptCbk(LTKWidget *button, int n, GBool on) { if (!doc) return; mapPSDialog(); } static void aboutCbk(LTKWidget *button, int n, GBool on) { mapAboutWin(); } static void quitCbk(LTKWidget *button, int n, GBool on) { quit = gTrue; } //------------------------------------------------------------------------ // scrollbar callbacks //------------------------------------------------------------------------ static void scrollVertCbk(LTKWidget *scrollbar, int n, int val) { canvas->scroll(hScrollbar->getPos(), val); XSync(display, False); } static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val) { canvas->scroll(val, vScrollbar->getPos()); XSync(display, False); } //------------------------------------------------------------------------ // misc callbacks //------------------------------------------------------------------------ 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); vScrollbar->setLimits(0, canvas->getRealHeight() - 1); vScrollbar->setPos(vScrollbar->getPos(), canvas->getHeight()); vScrollbar->setScrollDelta(16); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); } static void propChangeCbk(LTKWindow *win1, Atom atom) { Window xwin; char *cmd; Atom type; int format; Gulong size, remain; 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) { return; } if (size == 0) { return; } // raise window if (cmd[0] == 'D' || cmd[0] == 'r'){ win->raise(); XFlush(display); } // display file / page if (cmd[0] == 'd' || cmd[0] == 'D') { p = cmd + 2; q = strchr(p, ' '); if (!q) { return; } *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)) return; } else { delete newFileName; } 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') { quit = gTrue; } } //------------------------------------------------------------------------ // selection //------------------------------------------------------------------------ static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax) { int x, y, w, h; GBool needRedraw, needScroll; GBool moveLeft, moveRight, moveTop, moveBottom; // erase old selection on canvas pixmap needRedraw = gFalse; if (selectXMin < selectXMax && selectYMin < selectYMax) { XFillRectangle(canvas->getDisplay(), canvas->getPixmap(), selectGC, selectXMin, selectYMin, selectXMax - selectXMin, selectYMax - selectYMin); needRedraw = gTrue; } // draw new selection on canvas pixmap if (newXMin < newXMax && newYMin < newYMax) { XFillRectangle(canvas->getDisplay(), canvas->getPixmap(), selectGC, newXMin, newYMin, newXMax - newXMin, newYMax - newYMin); needRedraw = gTrue; } // check which edges moved moveLeft = newXMin != selectXMin; moveTop = newYMin != selectYMin; moveRight = newXMax != selectXMax; moveBottom = newYMax != selectYMax; // redraw currently visible part of canvas if (needRedraw) { if (moveLeft) { canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin, (newYMin < selectYMin) ? newYMin : selectYMin, (newXMin > selectXMin) ? newXMin : selectXMin, (newYMax > selectYMax) ? newYMax : selectYMax); } if (moveRight) { canvas->redrawRect((newXMax < selectXMax) ? newXMax : selectXMax, (newYMin < selectYMin) ? newYMin : selectYMin, (newXMax > selectXMax) ? newXMax : selectXMax, (newYMax > selectYMax) ? newYMax : selectYMax); } if (moveTop) { canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin, (newYMin < selectYMin) ? newYMin : selectYMin, (newXMax > selectXMax) ? newXMax : selectXMax, (newYMin > selectYMin) ? newYMin : selectYMin); } if (moveBottom) { canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin, (newYMax < selectYMax) ? newYMax : selectYMax, (newXMax > selectXMax) ? newXMax : selectXMax, (newYMax > selectYMax) ? newYMax : selectYMax); } } // switch to new selection coords selectXMin = newXMin; selectXMax = newXMax; selectYMin = newYMin; selectYMax = newYMax; // scroll canvas if necessary if (fullScreen) { return; } needScroll = gFalse; w = canvas->getWidth(); h = canvas->getHeight(); x = hScrollbar->getPos(); y = vScrollbar->getPos(); if (moveLeft && selectXMin < x) { x = selectXMin; needScroll = gTrue; } else if (moveRight && selectXMax >= x + w) { x = selectXMax - w; needScroll = gTrue; } else if (moveLeft && selectXMin >= x + w) { x = selectXMin - w; needScroll = gTrue; } else if (moveRight && selectXMax < x) { x = selectXMax; needScroll = gTrue; } if (moveTop && selectYMin < y) { y = selectYMin; needScroll = gTrue; } else if (moveBottom && selectYMax >= y + h) { y = selectYMax - h; needScroll = gTrue; } else if (moveTop && selectYMin >= y + h) { y = selectYMin - h; needScroll = gTrue; } else if (moveBottom && selectYMax < y) { y = selectYMax; needScroll = gTrue; } if (needScroll) { hScrollbar->setPos(x, w); vScrollbar->setPos(y, h); canvas->scroll(x, y); } } //------------------------------------------------------------------------ // "Open" dialog //------------------------------------------------------------------------ static void mapOpenDialog() { openDialog = makeOpenDialog(app); ((LTKFileReq *)openDialog->findWidget("fileReq"))->setDir(fileReqDir); openDialog->layoutDialog(win, -1, -1); openDialog->map(); } static void openButtonCbk(LTKWidget *button, int n, GBool on) { LTKFileReq *fileReq; GString *sel; sel = NULL; if (n == 1) { fileReq = (LTKFileReq *)openDialog->findWidget("fileReq"); if (!(sel = fileReq->getSelection())) { return; } openSelectCbk(fileReq, 0, sel); } if (openDialog) { if (sel) { delete fileReqDir; fileReqDir = ((LTKFileReq *)openDialog->findWidget("fileReq"))->getDir(); } delete openDialog; openDialog = NULL; } } static void openSelectCbk(LTKWidget *widget, int n, GString *name) { GString *name1; name1 = name->copy(); if (openDialog) { delete fileReqDir; fileReqDir = ((LTKFileReq *)openDialog->findWidget("fileReq"))->getDir(); delete openDialog; openDialog = NULL; } 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); } } //------------------------------------------------------------------------ // "Save PDF" dialog //------------------------------------------------------------------------ static void mapSaveDialog() { saveDialog = makeSaveDialog(app); ((LTKFileReq *)saveDialog->findWidget("fileReq"))->setDir(fileReqDir); saveDialog->layoutDialog(win, -1, -1); saveDialog->map(); } static void saveButtonCbk(LTKWidget *button, int n, GBool on) { LTKFileReq *fileReq; GString *sel; if (!doc) return; sel = NULL; if (n == 1) { fileReq = (LTKFileReq *)saveDialog->findWidget("fileReq"); if (!(sel = fileReq->getSelection())) { return; } saveSelectCbk(fileReq, 0, sel); } if (saveDialog) { if (sel) { delete fileReqDir; fileReqDir = ((LTKFileReq *)saveDialog->findWidget("fileReq"))->getDir(); } delete saveDialog; saveDialog = NULL; } } static void saveSelectCbk(LTKWidget *widget, int n, GString *name) { GString *name1; name1 = name->copy(); if (saveDialog) { delete fileReqDir; fileReqDir = ((LTKFileReq *)saveDialog->findWidget("fileReq"))->getDir(); delete saveDialog; saveDialog = NULL; } win->setBusyCursor(gTrue); doc->saveAs(name1); delete name1; win->setBusyCursor(gFalse); } //------------------------------------------------------------------------ // "PostScript" dialog //------------------------------------------------------------------------ static void mapPSDialog() { LTKTextIn *widget; char s[20]; psDialog = makePostScriptDialog(app); sprintf(s, "%d", psFirstPage); widget = (LTKTextIn *)psDialog->findWidget("firstPage"); widget->setText(s); sprintf(s, "%d", psLastPage); widget = (LTKTextIn *)psDialog->findWidget("lastPage"); widget->setText(s); widget = (LTKTextIn *)psDialog->findWidget("fileName"); widget->setText(psFileName->getCString()); psDialog->layoutDialog(win, -1, -1); psDialog->map(); } static void psButtonCbk(LTKWidget *button, int n, GBool on) { PSOutputDev *psOut; LTKTextIn *widget; if (!doc) return; // "Ok" button if (n == 1) { // extract params and close the dialog widget = (LTKTextIn *)psDialog->findWidget("firstPage"); psFirstPage = atoi(widget->getText()->getCString()); if (psFirstPage < 1) psFirstPage = 1; widget = (LTKTextIn *)psDialog->findWidget("lastPage"); psLastPage = atoi(widget->getText()->getCString()); if (psLastPage < psFirstPage) psLastPage = psFirstPage; else if (psLastPage > doc->getNumPages()) psLastPage = doc->getNumPages(); widget = (LTKTextIn *)psDialog->findWidget("fileName"); if (psFileName) delete psFileName; psFileName = widget->getText()->copy(); if (!(psFileName->getChar(0) == '|' || psFileName->cmp("-") == 0)) makePathAbsolute(psFileName); // do the PostScript output psDialog->setBusyCursor(gTrue); win->setBusyCursor(gTrue); if (doc->okToPrint()) { psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(), doc->getCatalog(), psFirstPage, psLastPage, psModePS); if (psOut->isOk()) { doc->displayPages(psOut, psFirstPage, psLastPage, 72, 0, gFalse); } delete psOut; } else { error(-1, "Printing this document is not allowed."); } delete psDialog; win->setBusyCursor(gFalse); // "Cancel" button } else { delete psDialog; } } //------------------------------------------------------------------------ // "About" window //------------------------------------------------------------------------ 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(); } } static void closeAboutCbk(LTKWidget *button, int n, GBool on) { delete aboutWin; 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 || doc->getNumPages() == 0) return; mapFindWin(); } static void mapFindWin() { if (findWin) { findWin->raise(); } else { 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) { if (!doc || doc->getNumPages() == 0) return; if (n == 1) { doFind(findTextIn->getText()->getCString()); } else { delete findWin; findWin = NULL; } } 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]) { XBell(display, 0); return; } // set cursors to watch 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) { xMin = selectXMax; yMin = (selectYMin + selectYMax) / 2; top = gFalse; } else { top = gTrue; } if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) { goto found; } // search following pages 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(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(u, len, gTrue, gTrue, &xMin1, &yMin1, &xMax1, &yMax1)) { goto foundPage; } } delete textOut; // search current page ending at current selection if (selectXMin < selectXMax && selectYMin < selectYMax) { xMax = selectXMin; yMax = (selectYMin + selectYMax) / 2; if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) { goto found; } } // not found XBell(display, 0); goto done; // found on a different page foundPage: delete textOut; 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: setSelection(xMin, yMin, xMax, yMax); #ifndef NO_TEXT_SELECT if (doc->okToCopy()) { s1 = out->getText(selectXMin, selectYMin, selectXMax, selectYMax); win->setSelection(NULL, s1); } #endif done: gfree(u); // reset cursors to normal win->setBusyCursor(gFalse); findWin->setBusyCursor(gFalse); } //------------------------------------------------------------------------ // app kill callback //------------------------------------------------------------------------ static void killCbk(LTKWindow *win1) { if (win1 == win) { quit = gTrue; } else if (win1 == aboutWin) { delete aboutWin; aboutWin = NULL; } else if (win1 == psDialog) { delete psDialog; psDialog = NULL; } else if (win1 == openDialog) { delete openDialog; openDialog = NULL; } else if (win1 == saveDialog) { delete saveDialog; saveDialog = NULL; } else if (win1 == findWin) { delete findWin; findWin = NULL; } }