diff options
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-3.02/xpdf/XPDFCore.cc')
-rw-r--r-- | Build/source/libs/xpdf/xpdf-3.02/xpdf/XPDFCore.cc | 1655 |
1 files changed, 1655 insertions, 0 deletions
diff --git a/Build/source/libs/xpdf/xpdf-3.02/xpdf/XPDFCore.cc b/Build/source/libs/xpdf/xpdf-3.02/xpdf/XPDFCore.cc new file mode 100644 index 00000000000..827a86d3a3b --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-3.02/xpdf/XPDFCore.cc @@ -0,0 +1,1655 @@ +//======================================================================== +// +// XPDFCore.cc +// +// Copyright 2002-2003 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <X11/keysym.h> +#include <X11/cursorfont.h> +#include <string.h> +#include "gmem.h" +#include "GString.h" +#include "GList.h" +#include "Error.h" +#include "GlobalParams.h" +#include "PDFDoc.h" +#include "Link.h" +#include "ErrorCodes.h" +#include "GfxState.h" +#include "CoreOutputDev.h" +#include "PSOutputDev.h" +#include "TextOutputDev.h" +#include "SplashBitmap.h" +#include "SplashPattern.h" +#include "XPDFApp.h" +#include "XPDFCore.h" + +// these macro defns conflict with xpdf's Object class +#ifdef LESSTIF_VERSION +#undef XtDisplay +#undef XtScreen +#undef XtWindow +#undef XtParent +#undef XtIsRealized +#endif + +//------------------------------------------------------------------------ + +// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. +static inline Guchar div255(int x) { + return (Guchar)((x + (x >> 8) + 0x80) >> 8); +} + +//------------------------------------------------------------------------ + +GString *XPDFCore::currentSelection = NULL; +XPDFCore *XPDFCore::currentSelectionOwner = NULL; +Atom XPDFCore::targetsAtom; + +//------------------------------------------------------------------------ +// XPDFCoreTile +//------------------------------------------------------------------------ + +class XPDFCoreTile: public PDFCoreTile { +public: + XPDFCoreTile(int xDestA, int yDestA); + virtual ~XPDFCoreTile(); + XImage *image; +}; + +XPDFCoreTile::XPDFCoreTile(int xDestA, int yDestA): + PDFCoreTile(xDestA, yDestA) +{ + image = NULL; +} + +XPDFCoreTile::~XPDFCoreTile() { + if (image) { + gfree(image->data); + image->data = NULL; + XDestroyImage(image); + } +} + +//------------------------------------------------------------------------ +// XPDFCore +//------------------------------------------------------------------------ + +XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA, + SplashColorPtr paperColorA, Gulong paperPixelA, + Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA, + GBool installCmap, int rgbCubeSizeA): + PDFCore(splashModeRGB8, 4, reverseVideoA, paperColorA, !fullScreenA) +{ + GString *initialZoom; + + shell = shellA; + parentWidget = parentWidgetA; + display = XtDisplay(parentWidget); + screenNum = XScreenNumberOfScreen(XtScreen(parentWidget)); + targetsAtom = XInternAtom(display, "TARGETS", False); + + paperPixel = paperPixelA; + mattePixel = mattePixelA; + fullScreen = fullScreenA; + + setupX(installCmap, rgbCubeSizeA); + + scrolledWin = NULL; + hScrollBar = NULL; + vScrollBar = NULL; + drawAreaFrame = NULL; + drawArea = NULL; + + // get the initial zoom value + if (fullScreen) { + zoom = zoomPage; + } else { + initialZoom = globalParams->getInitialZoom(); + if (!initialZoom->cmp("page")) { + zoom = zoomPage; + } else if (!initialZoom->cmp("width")) { + zoom = zoomWidth; + } else { + zoom = atoi(initialZoom->getCString()); + if (zoom <= 0) { + zoom = defZoom; + } + } + delete initialZoom; + } + + linkAction = NULL; + + panning = gFalse; + + updateCbk = NULL; + actionCbk = NULL; + keyPressCbk = NULL; + mouseCbk = NULL; + + // optional features default to on + hyperlinksEnabled = gTrue; + selectEnabled = gTrue; + + // do X-specific initialization and create the widgets + initWindow(); + initPasswordDialog(); +} + +XPDFCore::~XPDFCore() { + if (currentSelectionOwner == this && currentSelection) { + delete currentSelection; + currentSelection = NULL; + currentSelectionOwner = NULL; + } + if (drawAreaGC) { + XFreeGC(display, drawAreaGC); + } + if (scrolledWin) { + XtDestroyWidget(scrolledWin); + } + if (busyCursor) { + XFreeCursor(display, busyCursor); + } + if (linkCursor) { + XFreeCursor(display, linkCursor); + } + if (selectCursor) { + XFreeCursor(display, selectCursor); + } +} + +//------------------------------------------------------------------------ +// loadFile / displayPage / displayDest +//------------------------------------------------------------------------ + +int XPDFCore::loadFile(GString *fileName, GString *ownerPassword, + GString *userPassword) { + int err; + + err = PDFCore::loadFile(fileName, ownerPassword, userPassword); + if (err == errNone) { + // save the modification time + modTime = getModTime(doc->getFileName()->getCString()); + + // update the parent window + if (updateCbk) { + (*updateCbk)(updateCbkData, doc->getFileName(), -1, + doc->getNumPages(), NULL); + } + } + return err; +} + +int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword, + GString *userPassword) { + int err; + + err = PDFCore::loadFile(stream, ownerPassword, userPassword); + if (err == errNone) { + // no file + modTime = 0; + + // update the parent window + if (updateCbk) { + (*updateCbk)(updateCbkData, doc->getFileName(), -1, + doc->getNumPages(), NULL); + } + } + return err; +} + +void XPDFCore::loadDoc(PDFDoc *docA) { + PDFCore::loadDoc(docA); + + // save the modification time + if (doc->getFileName()) { + modTime = getModTime(doc->getFileName()->getCString()); + } + + // update the parent window + if (updateCbk) { + (*updateCbk)(updateCbkData, doc->getFileName(), -1, + doc->getNumPages(), NULL); + } +} + +void XPDFCore::resizeToPage(int pg) { + Dimension width, height; + double width1, height1; + Dimension topW, topH, topBorder, daW, daH; + Dimension displayW, displayH; + + displayW = DisplayWidth(display, screenNum); + displayH = DisplayHeight(display, screenNum); + if (fullScreen) { + width = displayW; + height = displayH; + } else { + if (!doc || pg <= 0 || pg > doc->getNumPages()) { + width1 = 612; + height1 = 792; + } else if (doc->getPageRotate(pg) == 90 || + doc->getPageRotate(pg) == 270) { + width1 = doc->getPageCropHeight(pg); + height1 = doc->getPageCropWidth(pg); + } else { + width1 = doc->getPageCropWidth(pg); + height1 = doc->getPageCropHeight(pg); + } + if (zoom == zoomPage || zoom == zoomWidth) { + width = (Dimension)(width1 * 0.01 * defZoom + 0.5); + height = (Dimension)(height1 * 0.01 * defZoom + 0.5); + } else { + width = (Dimension)(width1 * 0.01 * zoom + 0.5); + height = (Dimension)(height1 * 0.01 * zoom + 0.5); + } + if (continuousMode) { + height += continuousModePageSpacing; + } + if (width > displayW - 100) { + width = displayW - 100; + } + if (height > displayH - 100) { + height = displayH - 100; + } + } + + if (XtIsRealized(shell)) { + XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH, + XmNborderWidth, &topBorder, NULL); + XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL); + XtVaSetValues(shell, XmNwidth, width + (topW - daW), + XmNheight, height + (topH - daH), NULL); + } else { + XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL); + } +} + +void XPDFCore::update(int topPageA, int scrollXA, int scrollYA, + double zoomA, int rotateA, + GBool force, GBool addToHist) { + int oldPage; + + oldPage = topPage; + PDFCore::update(topPageA, scrollXA, scrollYA, zoomA, rotateA, + force, addToHist); + linkAction = NULL; + if (doc && topPage != oldPage) { + if (updateCbk) { + (*updateCbk)(updateCbkData, NULL, topPage, -1, ""); + } + } +} + +GBool XPDFCore::checkForNewFile() { + time_t newModTime; + + if (doc->getFileName()) { + newModTime = getModTime(doc->getFileName()->getCString()); + if (newModTime != modTime) { + modTime = newModTime; + return gTrue; + } + } + return gFalse; +} + +//------------------------------------------------------------------------ +// page/position changes +//------------------------------------------------------------------------ + +GBool XPDFCore::gotoNextPage(int inc, GBool top) { + if (!PDFCore::gotoNextPage(inc, top)) { + XBell(display, 0); + return gFalse; + } + return gTrue; +} + +GBool XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) { + if (!PDFCore::gotoPrevPage(dec, top, bottom)) { + XBell(display, 0); + return gFalse; + } + return gTrue; +} + +GBool XPDFCore::goForward() { + if (!PDFCore::goForward()) { + XBell(display, 0); + return gFalse; + } + return gTrue; +} + +GBool XPDFCore::goBackward() { + if (!PDFCore::goBackward()) { + XBell(display, 0); + return gFalse; + } + return gTrue; +} + +void XPDFCore::startPan(int wx, int wy) { + panning = gTrue; + panMX = wx; + panMY = wy; +} + +void XPDFCore::endPan(int wx, int wy) { + panning = gFalse; +} + +//------------------------------------------------------------------------ +// selection +//------------------------------------------------------------------------ + +void XPDFCore::startSelection(int wx, int wy) { + int pg, x, y; + + takeFocus(); + if (doc && doc->getNumPages() > 0) { + if (selectEnabled) { + if (cvtWindowToDev(wx, wy, &pg, &x, &y)) { + setSelection(pg, x, y, x, y); + setCursor(selectCursor); + dragging = gTrue; + } + } + } +} + +void XPDFCore::endSelection(int wx, int wy) { + int pg, x, y; + GBool ok; + + if (doc && doc->getNumPages() > 0) { + ok = cvtWindowToDev(wx, wy, &pg, &x, &y); + if (dragging) { + dragging = gFalse; + setCursor(None); + if (ok) { + moveSelection(pg, x, y); + } +#ifndef NO_TEXT_SELECT + if (selectULX != selectLRX && + selectULY != selectLRY) { + if (doc->okToCopy()) { + copySelection(); + } else { + error(-1, "Copying of text from this document is not allowed."); + } + } +#endif + } + } +} + +// X's copy-and-paste mechanism is brain damaged. Xt doesn't help +// any, but doesn't make it too much worse, either. Motif, on the +// other hand, adds significant complexity to the mess. So here we +// blow off the Motif junk and stick to plain old Xt. The next two +// functions (copySelection and convertSelectionCbk) implement the +// magic needed to deal with Xt's mechanism. Note that this requires +// global variables (currentSelection and currentSelectionOwner). + +void XPDFCore::copySelection() { + int pg; + double ulx, uly, lrx, lry; + + if (!doc->okToCopy()) { + return; + } + if (getSelection(&pg, &ulx, &uly, &lrx, &lry)) { + //~ for multithreading: need a mutex here + if (currentSelection) { + delete currentSelection; + } + currentSelection = extractText(pg, ulx, uly, lrx, lry); + currentSelectionOwner = this; + XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display), + &convertSelectionCbk, NULL, NULL); + } +} + +Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection, + Atom *target, Atom *type, + XtPointer *value, unsigned long *length, + int *format) { + Atom *array; + + // send back a list of supported conversion targets + if (*target == targetsAtom) { + if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) { + return False; + } + array[0] = XA_STRING; + *value = (XtPointer)array; + *type = XA_ATOM; + *format = 32; + *length = 1; + return True; + + // send the selected text + } else if (*target == XA_STRING) { + //~ for multithreading: need a mutex here + *value = XtNewString(currentSelection->getCString()); + *length = currentSelection->getLength(); + *type = XA_STRING; + *format = 8; // 8-bit elements + return True; + } + + return False; +} + +//------------------------------------------------------------------------ +// hyperlinks +//------------------------------------------------------------------------ + +void XPDFCore::doAction(LinkAction *action) { + LinkActionKind kind; + LinkDest *dest; + GString *namedDest; + char *s; + GString *fileName, *fileName2; + GString *cmd; + GString *actionName; + Object movieAnnot, obj1, obj2; + GString *msg; + int i; + + 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) != errNone) { + if (dest) { + delete dest; + } + if (namedDest) { + delete namedDest; + } + delete fileName; + return; + } + delete fileName; + } + 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, gFalse, 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) != errNone) { + delete fileName; + return; + } + delete fileName; + displayPage(1, zoom, rotate, gFalse, 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 + msg = new GString("About to execute the command:\n"); + msg->append(fileName); + if (doQuestionDialog("Launching external application", msg)) { + system(fileName->getCString()); + } + delete fileName; + delete msg; + } + break; + + // URI action + case actionURI: + if (!(cmd = globalParams->getURLCommand())) { + error(-1, "No urlCommand defined in config file"); + break; + } + runCommand(cmd, ((LinkURI *)action)->getURI()); + 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 (topPage != 1) { + displayPage(1, zoom, rotate, gTrue, gTrue); + } + } else if (!actionName->cmp("LastPage")) { + if (topPage != doc->getNumPages()) { + displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue); + } + } else if (!actionName->cmp("GoBack")) { + goBackward(); + } else if (!actionName->cmp("GoForward")) { + goForward(); + } else if (!actionName->cmp("Quit")) { + if (actionCbk) { + (*actionCbk)(actionCbkData, actionName->getCString()); + } + } else { + error(-1, "Unknown named action: '%s'", actionName->getCString()); + } + break; + + // Movie action + case actionMovie: + if (!(cmd = globalParams->getMovieCommand())) { + error(-1, "No movieCommand defined in config file"); + break; + } + if (((LinkMovie *)action)->hasAnnotRef()) { + doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num, + ((LinkMovie *)action)->getAnnotRef()->gen, + &movieAnnot); + } else { + //~ need to use the correct page num here + doc->getCatalog()->getPage(topPage)->getAnnots(&obj1); + if (obj1.isArray()) { + for (i = 0; i < obj1.arrayGetLength(); ++i) { + if (obj1.arrayGet(i, &movieAnnot)->isDict()) { + if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) { + obj2.free(); + break; + } + obj2.free(); + } + movieAnnot.free(); + } + obj1.free(); + } + } + if (movieAnnot.isDict()) { + if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) { + if (obj1.dictLookup("F", &obj2)) { + if ((fileName = LinkAction::getFileSpecName(&obj2))) { + if (!isAbsolutePath(fileName->getCString())) { + fileName2 = appendToPath( + grabPath(doc->getFileName()->getCString()), + fileName->getCString()); + delete fileName; + fileName = fileName2; + } + runCommand(cmd, fileName); + delete fileName; + } + obj2.free(); + } + obj1.free(); + } + } + movieAnnot.free(); + break; + + // unknown action type + case actionUnknown: + error(-1, "Unknown link action type: '%s'", + ((LinkUnknown *)action)->getAction()->getCString()); + break; + } +} + +// Run a command, given a <cmdFmt> string with one '%s' in it, and an +// <arg> string to insert in place of the '%s'. +void XPDFCore::runCommand(GString *cmdFmt, GString *arg) { + GString *cmd; + char *s; + + if ((s = strstr(cmdFmt->getCString(), "%s"))) { + cmd = mungeURL(arg); + cmd->insert(0, cmdFmt->getCString(), + s - cmdFmt->getCString()); + cmd->append(s + 2); + } else { + cmd = cmdFmt->copy(); + } +#ifdef VMS + cmd->insert(0, "spawn/nowait "); +#elif defined(__EMX__) + cmd->insert(0, "start /min /n "); +#else + cmd->append(" &"); +#endif + system(cmd->getCString()); + delete cmd; +} + +// Escape any characters in a URL which might cause problems when +// calling system(). +GString *XPDFCore::mungeURL(GString *url) { + static char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-_.~/?:@&=+,#%"; + GString *newURL; + char c; + char buf[4]; + int i; + + newURL = new GString(); + for (i = 0; i < url->getLength(); ++i) { + c = url->getChar(i); + if (strchr(allowed, c)) { + newURL->append(c); + } else { + sprintf(buf, "%%%02x", c & 0xff); + newURL->append(buf); + } + } + return newURL; +} + +//------------------------------------------------------------------------ +// find +//------------------------------------------------------------------------ + +GBool XPDFCore::find(char *s, GBool caseSensitive, + GBool next, GBool backward, GBool onePageOnly) { + if (!PDFCore::find(s, caseSensitive, next, backward, onePageOnly)) { + XBell(display, 0); + return gFalse; + } +#ifndef NO_TEXT_SELECT + copySelection(); +#endif + return gTrue; +} + +GBool XPDFCore::findU(Unicode *u, int len, GBool caseSensitive, + GBool next, GBool backward, GBool onePageOnly) { + if (!PDFCore::findU(u, len, caseSensitive, next, backward, onePageOnly)) { + XBell(display, 0); + return gFalse; + } +#ifndef NO_TEXT_SELECT + copySelection(); +#endif + return gTrue; +} + +//------------------------------------------------------------------------ +// misc access +//------------------------------------------------------------------------ + +void XPDFCore::setBusyCursor(GBool busy) { + setCursor(busy ? busyCursor : None); +} + +void XPDFCore::takeFocus() { + XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT); +} + +//------------------------------------------------------------------------ +// GUI code +//------------------------------------------------------------------------ + +void XPDFCore::setupX(GBool installCmap, int rgbCubeSizeA) { + XVisualInfo visualTempl; + XVisualInfo *visualList; + Gulong mask; + int nVisuals; + XColor xcolor; + XColor *xcolors; + int r, g, b, n, m; + GBool ok; + + // for some reason, querying XmNvisual doesn't work (even if done + // after the window is mapped) + visual = DefaultVisual(display, screenNum); + XtVaGetValues(shell, XmNcolormap, &colormap, NULL); + + // check for TrueColor visual + //~ this should scan the list, not just look at the first one + visualTempl.visualid = XVisualIDFromVisual(visual); + visualList = XGetVisualInfo(display, VisualIDMask, + &visualTempl, &nVisuals); + if (nVisuals < 1) { + // this shouldn't happen + XFree((XPointer)visualList); + visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl, + &nVisuals); + } + depth = visualList->depth; + if (visualList->c_class == TrueColor) { + trueColor = gTrue; + for (mask = visualList->red_mask, rShift = 0; + mask && !(mask & 1); + mask >>= 1, ++rShift) ; + for (rDiv = 8; mask; mask >>= 1, --rDiv) ; + for (mask = visualList->green_mask, gShift = 0; + mask && !(mask & 1); + mask >>= 1, ++gShift) ; + for (gDiv = 8; mask; mask >>= 1, --gDiv) ; + for (mask = visualList->blue_mask, bShift = 0; + mask && !(mask & 1); + mask >>= 1, ++bShift) ; + for (bDiv = 8; mask; mask >>= 1, --bDiv) ; + } else { + trueColor = gFalse; + } + XFree((XPointer)visualList); + + // allocate a color cube + if (!trueColor) { + + // set colors in private colormap + if (installCmap) { + for (rgbCubeSize = xMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) { + m = rgbCubeSize * rgbCubeSize * rgbCubeSize; + if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) { + break; + } + } + if (rgbCubeSize >= 2) { + m = rgbCubeSize * rgbCubeSize * rgbCubeSize; + xcolors = (XColor *)gmallocn(m, sizeof(XColor)); + n = 0; + for (r = 0; r < rgbCubeSize; ++r) { + for (g = 0; g < rgbCubeSize; ++g) { + for (b = 0; b < rgbCubeSize; ++b) { + xcolors[n].pixel = colors[n]; + xcolors[n].red = (r * 65535) / (rgbCubeSize - 1); + xcolors[n].green = (g * 65535) / (rgbCubeSize - 1); + xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1); + xcolors[n].flags = DoRed | DoGreen | DoBlue; + ++n; + } + } + } + XStoreColors(display, colormap, xcolors, m); + gfree(xcolors); + } else { + rgbCubeSize = 1; + colors[0] = BlackPixel(display, screenNum); + colors[1] = WhitePixel(display, screenNum); + } + + // allocate colors in shared colormap + } else { + if (rgbCubeSize > xMaxRGBCube) { + rgbCubeSize = xMaxRGBCube; + } + ok = gFalse; + for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) { + ok = gTrue; + n = 0; + for (r = 0; r < rgbCubeSize && ok; ++r) { + for (g = 0; g < rgbCubeSize && ok; ++g) { + for (b = 0; b < rgbCubeSize && ok; ++b) { + if (n == 0) { + colors[n] = BlackPixel(display, screenNum); + ++n; + } else { + xcolor.red = (r * 65535) / (rgbCubeSize - 1); + xcolor.green = (g * 65535) / (rgbCubeSize - 1); + xcolor.blue = (b * 65535) / (rgbCubeSize - 1); + if (XAllocColor(display, colormap, &xcolor)) { + colors[n++] = xcolor.pixel; + } else { + ok = gFalse; + } + } + } + } + } + if (ok) { + break; + } + XFreeColors(display, colormap, &colors[1], n-1, 0); + } + if (!ok) { + rgbCubeSize = 1; + colors[0] = BlackPixel(display, screenNum); + colors[1] = WhitePixel(display, screenNum); + } + } + } +} + +void XPDFCore::initWindow() { + Arg args[20]; + int n; + + // create the cursors + busyCursor = XCreateFontCursor(display, XC_watch); + linkCursor = XCreateFontCursor(display, XC_hand2); + selectCursor = XCreateFontCursor(display, XC_cross); + currentCursor = 0; + + // create the scrolled window and scrollbars + n = 0; + XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n; + XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n; + scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n); + XtManageChild(scrolledWin); + n = 0; + XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; + XtSetArg(args[n], XmNminimum, 0); ++n; + XtSetArg(args[n], XmNmaximum, 1); ++n; + XtSetArg(args[n], XmNsliderSize, 1); ++n; + XtSetArg(args[n], XmNvalue, 0); ++n; + XtSetArg(args[n], XmNincrement, 1); ++n; + XtSetArg(args[n], XmNpageIncrement, 1); ++n; + hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n); + if (!fullScreen) { + XtManageChild(hScrollBar); + } + XtAddCallback(hScrollBar, XmNvalueChangedCallback, + &hScrollChangeCbk, (XtPointer)this); +#ifndef DISABLE_SMOOTH_SCROLL + XtAddCallback(hScrollBar, XmNdragCallback, + &hScrollDragCbk, (XtPointer)this); +#endif + n = 0; + XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n; + XtSetArg(args[n], XmNminimum, 0); ++n; + XtSetArg(args[n], XmNmaximum, 1); ++n; + XtSetArg(args[n], XmNsliderSize, 1); ++n; + XtSetArg(args[n], XmNvalue, 0); ++n; + XtSetArg(args[n], XmNincrement, 1); ++n; + XtSetArg(args[n], XmNpageIncrement, 1); ++n; + vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n); + if (!fullScreen) { + XtManageChild(vScrollBar); + } + XtAddCallback(vScrollBar, XmNvalueChangedCallback, + &vScrollChangeCbk, (XtPointer)this); +#ifndef DISABLE_SMOOTH_SCROLL + XtAddCallback(vScrollBar, XmNdragCallback, + &vScrollDragCbk, (XtPointer)this); +#endif + + // create the drawing area + n = 0; + XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n; + XtSetArg(args[n], XmNmarginWidth, 0); ++n; + XtSetArg(args[n], XmNmarginHeight, 0); ++n; + if (fullScreen) { + XtSetArg(args[n], XmNshadowThickness, 0); ++n; + } + drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n); + XtManageChild(drawAreaFrame); + n = 0; + XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n; + XtSetArg(args[n], XmNwidth, 700); ++n; + XtSetArg(args[n], XmNheight, 500); ++n; + drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n); + XtManageChild(drawArea); + XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this); + XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this); + XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this); + resizeCbk(drawArea, this, NULL); + + // set up mouse motion translations + XtOverrideTranslations(drawArea, XtParseTranslationTable( + "<Btn1Down>:DrawingAreaInput()\n" + "<Btn1Up>:DrawingAreaInput()\n" + "<Btn1Motion>:DrawingAreaInput()\n" + "<Motion>:DrawingAreaInput()")); + + // can't create a GC until the window gets mapped + drawAreaGC = NULL; +} + +void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; + + core->scrollTo(data->value, core->scrollY); +} + +void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; + + core->scrollTo(data->value, core->scrollY); +} + +void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; + + core->scrollTo(core->scrollX, data->value); +} + +void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; + + core->scrollTo(core->scrollX, data->value); +} + +void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XEvent event; + Widget top; + Window rootWin; + int x1, y1; + Guint w1, h1, bw1, depth1; + Arg args[2]; + int n; + Dimension w, h; + int sx, sy; + + // find the top-most widget which has an associated window, and look + // for a pending ConfigureNotify in the event queue -- if there is + // one, and it specifies a different width or height, that means + // we're still resizing, and we want to skip the current event + for (top = core->parentWidget; + XtParent(top) && XtWindow(XtParent(top)); + top = XtParent(top)) ; + if (XCheckTypedWindowEvent(core->display, XtWindow(top), + ConfigureNotify, &event)) { + XPutBackEvent(core->display, &event); + XGetGeometry(core->display, event.xconfigure.window, + &rootWin, &x1, &y1, &w1, &h1, &bw1, &depth1); + if ((Guint)event.xconfigure.width != w1 || + (Guint)event.xconfigure.height != h1) { + return; + } + } + + n = 0; + XtSetArg(args[n], XmNwidth, &w); ++n; + XtSetArg(args[n], XmNheight, &h); ++n; + XtGetValues(core->drawArea, args, n); + core->drawAreaWidth = (int)w; + core->drawAreaHeight = (int)h; + if (core->zoom == zoomPage || core->zoom == zoomWidth) { + sx = sy = -1; + } else { + sx = core->scrollX; + sy = core->scrollY; + } + core->update(core->topPage, sx, sy, core->zoom, core->rotate, gTrue, gFalse); +} + +void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData; + int x, y, w, h; + + if (data->reason == XmCR_EXPOSE) { + x = data->event->xexpose.x; + y = data->event->xexpose.y; + w = data->event->xexpose.width; + h = data->event->xexpose.height; + } else { + x = 0; + y = 0; + w = core->drawAreaWidth; + h = core->drawAreaHeight; + } + core->redrawWindow(x, y, w, h, gFalse); +} + +void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData; + LinkAction *action; + int pg, x, y; + double xu, yu; + char *s; + KeySym key; + GBool ok; + + switch (data->event->type) { + case ButtonPress: + if (*core->mouseCbk) { + (*core->mouseCbk)(core->mouseCbkData, data->event); + } + break; + case ButtonRelease: + if (*core->mouseCbk) { + (*core->mouseCbk)(core->mouseCbkData, data->event); + } + break; + case MotionNotify: + if (core->doc && core->doc->getNumPages() > 0) { + ok = core->cvtWindowToDev(data->event->xmotion.x, data->event->xmotion.y, + &pg, &x, &y); + if (core->dragging) { + if (ok) { + core->moveSelection(pg, x, y); + } + } else if (core->hyperlinksEnabled) { + core->cvtDevToUser(pg, x, y, &xu, &yu); + if (ok && (action = core->findLink(pg, xu, yu))) { + core->setCursor(core->linkCursor); + if (action != core->linkAction) { + core->linkAction = action; + if (core->updateCbk) { + s = ""; + switch (action->getKind()) { + case actionGoTo: + s = "[internal link]"; + break; + case actionGoToR: + s = ((LinkGoToR *)action)->getFileName()->getCString(); + break; + case actionLaunch: + s = ((LinkLaunch *)action)->getFileName()->getCString(); + break; + case actionURI: + s = ((LinkURI *)action)->getURI()->getCString(); + break; + case actionNamed: + s = ((LinkNamed *)action)->getName()->getCString(); + break; + case actionMovie: + s = "[movie]"; + break; + case actionUnknown: + s = "[unknown link]"; + break; + } + (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s); + } + } + } else { + core->setCursor(None); + if (core->linkAction) { + core->linkAction = NULL; + if (core->updateCbk) { + (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, ""); + } + } + } + } + } + if (core->panning) { + core->scrollTo(core->scrollX - (data->event->xmotion.x - core->panMX), + core->scrollY - (data->event->xmotion.y - core->panMY)); + core->panMX = data->event->xmotion.x; + core->panMY = data->event->xmotion.y; + } + break; + case KeyPress: + if (core->keyPressCbk) { + key = XLookupKeysym(&data->event->xkey, + (data->event->xkey.state & ShiftMask) ? 1 : 0); + (*core->keyPressCbk)(core->keyPressCbkData, + key, data->event->xkey.state, data->event); + } + break; + } +} + +PDFCoreTile *XPDFCore::newTile(int xDestA, int yDestA) { + return new XPDFCoreTile(xDestA, yDestA); +} + +void XPDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc, + int width, int height, GBool composited) { + XPDFCoreTile *tile = (XPDFCoreTile *)tileA; + XImage *image; + SplashColorPtr dataPtr, p; + Gulong pixel; + Guchar *ap; + Guchar alpha, alpha1; + int w, h, bw, x, y, r, g, b, gray; + int *errDownR, *errDownG, *errDownB; + int errRightR, errRightG, errRightB; + int errDownRightR, errDownRightG, errDownRightB; + int r0, g0, b0, re, ge, be; + + if (!tile->image) { + w = tile->xMax - tile->xMin; + h = tile->yMax - tile->yMin; + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, w, h, 8, 0); + image->data = (char *)gmalloc(h * image->bytes_per_line); + tile->image = image; + } else { + image = (XImage *)tile->image; + } + + //~ optimize for known XImage formats + bw = tile->bitmap->getRowSize(); + dataPtr = tile->bitmap->getDataPtr(); + + if (trueColor) { + for (y = 0; y < height; ++y) { + p = dataPtr + (ySrc + y) * bw + xSrc * 3; + if (!composited && tile->bitmap->getAlphaPtr()) { + ap = tile->bitmap->getAlphaPtr() + + (ySrc + y) * tile->bitmap->getWidth() + xSrc; + } else { + ap = NULL; + } + for (x = 0; x < width; ++x) { + r = splashRGB8R(p); + g = splashRGB8G(p); + b = splashRGB8B(p); + if (ap) { + alpha = *ap++; + alpha1 = 255 - alpha; + r = div255(alpha1 * paperColor[0] + alpha * r); + g = div255(alpha1 * paperColor[1] + alpha * g); + b = div255(alpha1 * paperColor[2] + alpha * b); + } + r >>= rDiv; + g >>= gDiv; + b >>= bDiv; + pixel = ((Gulong)r << rShift) + + ((Gulong)g << gShift) + + ((Gulong)b << bShift); + XPutPixel(image, xSrc + x, ySrc + y, pixel); + p += 3; + } + } + } else if (rgbCubeSize == 1) { + //~ this should really use splashModeMono, with non-clustered dithering + for (y = 0; y < height; ++y) { + p = dataPtr + (ySrc + y) * bw + xSrc * 3; + if (!composited && tile->bitmap->getAlphaPtr()) { + ap = tile->bitmap->getAlphaPtr() + + (ySrc + y) * tile->bitmap->getWidth() + xSrc; + } else { + ap = NULL; + } + for (x = 0; x < width; ++x) { + r = splashRGB8R(p); + g = splashRGB8G(p); + b = splashRGB8B(p); + if (ap) { + alpha = *ap++; + alpha1 = 255 - alpha; + r = div255(alpha1 * paperColor[0] + alpha * r); + g = div255(alpha1 * paperColor[1] + alpha * g); + b = div255(alpha1 * paperColor[2] + alpha * b); + } + gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5); + if (gray < 128) { + pixel = colors[0]; + } else { + pixel = colors[1]; + } + XPutPixel(image, xSrc + x, ySrc + y, pixel); + p += 3; + } + } + } else { + // do Floyd-Steinberg dithering on the whole bitmap + errDownR = (int *)gmallocn(width + 2, sizeof(int)); + errDownG = (int *)gmallocn(width + 2, sizeof(int)); + errDownB = (int *)gmallocn(width + 2, sizeof(int)); + errRightR = errRightG = errRightB = 0; + errDownRightR = errDownRightG = errDownRightB = 0; + memset(errDownR, 0, (width + 2) * sizeof(int)); + memset(errDownG, 0, (width + 2) * sizeof(int)); + memset(errDownB, 0, (width + 2) * sizeof(int)); + for (y = 0; y < height; ++y) { + p = dataPtr + (ySrc + y) * bw + xSrc * 3; + if (!composited && tile->bitmap->getAlphaPtr()) { + ap = tile->bitmap->getAlphaPtr() + + (ySrc + y) * tile->bitmap->getWidth() + xSrc; + } else { + ap = NULL; + } + for (x = 0; x < width; ++x) { + r = splashRGB8R(p); + g = splashRGB8G(p); + b = splashRGB8B(p); + if (ap) { + alpha = *ap++; + alpha1 = 255 - alpha; + r = div255(alpha1 * paperColor[0] + alpha * r); + g = div255(alpha1 * paperColor[1] + alpha * g); + b = div255(alpha1 * paperColor[2] + alpha * b); + } + r0 = r + errRightR + errDownR[x+1]; + g0 = g + errRightG + errDownG[x+1]; + b0 = b + errRightB + errDownB[x+1]; + if (r0 < 0) { + r = 0; + } else if (r0 >= 255) { + r = rgbCubeSize - 1; + } else { + r = div255(r0 * (rgbCubeSize - 1)); + } + if (g0 < 0) { + g = 0; + } else if (g0 >= 255) { + g = rgbCubeSize - 1; + } else { + g = div255(g0 * (rgbCubeSize - 1)); + } + if (b0 < 0) { + b = 0; + } else if (b0 >= 255) { + b = rgbCubeSize - 1; + } else { + b = div255(b0 * (rgbCubeSize - 1)); + } + re = r0 - ((r << 8) - r) / (rgbCubeSize - 1); + ge = g0 - ((g << 8) - g) / (rgbCubeSize - 1); + be = b0 - ((b << 8) - b) / (rgbCubeSize - 1); + errRightR = (re * 7) >> 4; + errRightG = (ge * 7) >> 4; + errRightB = (be * 7) >> 4; + errDownR[x] += (re * 3) >> 4; + errDownG[x] += (ge * 3) >> 4; + errDownB[x] += (be * 3) >> 4; + errDownR[x+1] = ((re * 5) >> 4) + errDownRightR; + errDownG[x+1] = ((ge * 5) >> 4) + errDownRightG; + errDownB[x+1] = ((be * 5) >> 4) + errDownRightB; + errDownRightR = re >> 4; + errDownRightG = ge >> 4; + errDownRightB = be >> 4; + pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b]; + XPutPixel(image, xSrc + x, ySrc + y, pixel); + p += 3; + } + } + gfree(errDownR); + gfree(errDownG); + gfree(errDownB); + } +} + +void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc, + int xDest, int yDest, int width, int height, + GBool composited) { + XPDFCoreTile *tile = (XPDFCoreTile *)tileA; + Window drawAreaWin; + XGCValues gcValues; + + // create a GC for the drawing area + drawAreaWin = XtWindow(drawArea); + if (!drawAreaGC) { + gcValues.foreground = mattePixel; + drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues); + } + + // draw the document + if (tile) { + XPutImage(display, drawAreaWin, drawAreaGC, tile->image, + xSrc, ySrc, xDest, yDest, width, height); + + // draw the background + } else { + XFillRectangle(display, drawAreaWin, drawAreaGC, + xDest, yDest, width, height); + } +} + +void XPDFCore::updateScrollbars() { + Arg args[20]; + int n; + int maxPos; + + if (pages->getLength() > 0) { + if (continuousMode) { + maxPos = maxPageW; + } else { + maxPos = ((PDFCorePage *)pages->get(0))->w; + } + } else { + maxPos = 1; + } + if (maxPos < drawAreaWidth) { + maxPos = drawAreaWidth; + } + n = 0; + XtSetArg(args[n], XmNvalue, scrollX); ++n; + XtSetArg(args[n], XmNmaximum, maxPos); ++n; + XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n; + XtSetArg(args[n], XmNincrement, 16); ++n; + XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n; + XtSetValues(hScrollBar, args, n); + + if (pages->getLength() > 0) { + if (continuousMode) { + maxPos = totalDocH; + } else { + maxPos = ((PDFCorePage *)pages->get(0))->h; + } + } else { + maxPos = 1; + } + if (maxPos < drawAreaHeight) { + maxPos = drawAreaHeight; + } + n = 0; + XtSetArg(args[n], XmNvalue, scrollY); ++n; + XtSetArg(args[n], XmNmaximum, maxPos); ++n; + XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n; + XtSetArg(args[n], XmNincrement, 16); ++n; + XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n; + XtSetValues(vScrollBar, args, n); +} + +void XPDFCore::setCursor(Cursor cursor) { + Window topWin; + + if (cursor == currentCursor) { + return; + } + if (!(topWin = XtWindow(shell))) { + return; + } + if (cursor == None) { + XUndefineCursor(display, topWin); + } else { + XDefineCursor(display, topWin, cursor); + } + XFlush(display); + currentCursor = cursor; +} + +GBool XPDFCore::doQuestionDialog(char *title, GString *msg) { + return doDialog(XmDIALOG_QUESTION, gTrue, title, msg); +} + +void XPDFCore::doInfoDialog(char *title, GString *msg) { + doDialog(XmDIALOG_INFORMATION, gFalse, title, msg); +} + +void XPDFCore::doErrorDialog(char *title, GString *msg) { + doDialog(XmDIALOG_ERROR, gFalse, title, msg); +} + +GBool XPDFCore::doDialog(int type, GBool hasCancel, + char *title, GString *msg) { + Widget dialog, scroll, text; + XtAppContext appContext; + Arg args[20]; + int n; + XmString s1, s2; + XEvent event; + + n = 0; + XtSetArg(args[n], XmNdialogType, type); ++n; + XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; + s1 = XmStringCreateLocalized(title); + XtSetArg(args[n], XmNdialogTitle, s1); ++n; + s2 = NULL; // make gcc happy + if (msg->getLength() <= 80) { + s2 = XmStringCreateLocalized(msg->getCString()); + XtSetArg(args[n], XmNmessageString, s2); ++n; + } + dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n); + XmStringFree(s1); + if (msg->getLength() <= 80) { + XmStringFree(s2); + } else { + n = 0; + XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n; + if (drawAreaWidth > 300) { + XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n; + } + scroll = XmCreateScrolledWindow(dialog, "scroll", args, n); + XtManageChild(scroll); + n = 0; + XtSetArg(args[n], XmNeditable, False); ++n; + XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n; + XtSetArg(args[n], XmNvalue, msg->getCString()); ++n; + XtSetArg(args[n], XmNshadowThickness, 0); ++n; + text = XmCreateText(scroll, "text", args, n); + XtManageChild(text); + } + XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON)); + XtAddCallback(dialog, XmNokCallback, + &dialogOkCbk, (XtPointer)this); + if (hasCancel) { + XtAddCallback(dialog, XmNcancelCallback, + &dialogCancelCbk, (XtPointer)this); + } else { + XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON)); + } + + XtManageChild(dialog); + + appContext = XtWidgetToApplicationContext(dialog); + dialogDone = 0; + do { + XtAppNextEvent(appContext, &event); + XtDispatchEvent(&event); + } while (!dialogDone); + + XtUnmanageChild(dialog); + XtDestroyWidget(dialog); + + return dialogDone > 0; +} + +void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + + core->dialogDone = 1; +} + +void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + + core->dialogDone = -1; +} + +//------------------------------------------------------------------------ +// password dialog +//------------------------------------------------------------------------ + +void XPDFCore::initPasswordDialog() { + Widget row, label, okBtn, cancelBtn; + Arg args[20]; + int n; + XmString s; + + //----- dialog + n = 0; + s = XmStringCreateLocalized(xpdfAppName ": Password"); + XtSetArg(args[n], XmNdialogTitle, s); ++n; + XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; + passwordDialog = XmCreateFormDialog(drawArea, "passwordDialog", args, n); + XmStringFree(s); + + //----- message + n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNtopOffset, 4); ++n; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNleftOffset, 4); ++n; + s = XmStringCreateLocalized("This document requires a password."); + XtSetArg(args[n], XmNlabelString, s); ++n; + label = XmCreateLabel(passwordDialog, "msg", args, n); + XmStringFree(s); + XtManageChild(label); + + //----- label and password entry + n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNtopWidget, label); ++n; + XtSetArg(args[n], XmNtopOffset, 4); ++n; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNleftOffset, 4); ++n; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNleftOffset, 4); ++n; + XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; + XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n; + row = XmCreateRowColumn(passwordDialog, "row", args, n); + XtManageChild(row); + n = 0; + s = XmStringCreateLocalized("Password: "); + XtSetArg(args[n], XmNlabelString, s); ++n; + label = XmCreateLabel(row, "label", args, n); + XmStringFree(s); + XtManageChild(label); + n = 0; + XtSetArg(args[n], XmNcolumns, 16); ++n; + passwordText = XmCreateTextField(row, "text", args, n); + XtManageChild(passwordText); + XtAddCallback(passwordText, XmNmodifyVerifyCallback, + &passwordTextVerifyCbk, this); + + //----- "Ok" and "Cancel" buttons + n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNtopWidget, row); ++n; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNleftOffset, 4); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomOffset, 4); ++n; + XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n; + okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n); + XtManageChild(okBtn); + XtAddCallback(okBtn, XmNactivateCallback, + &passwordOkCbk, (XtPointer)this); + n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNtopWidget, row); ++n; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNrightOffset, 4); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomOffset, 4); ++n; + XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n; + cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n); + XtManageChild(cancelBtn); + XtAddCallback(cancelBtn, XmNactivateCallback, + &passwordCancelCbk, (XtPointer)this); + n = 0; + XtSetArg(args[n], XmNdefaultButton, okBtn); ++n; + XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n; +#if XmVersion > 1001 + XtSetArg(args[n], XmNinitialFocus, passwordText); ++n; +#endif + XtSetValues(passwordDialog, args, n); +} + +void XPDFCore::passwordTextVerifyCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmTextVerifyCallbackStruct *data = + (XmTextVerifyCallbackStruct *)callData; + int i, n; + + i = (int)data->startPos; + n = (int)data->endPos - i; + if (i > core->password->getLength()) { + i = core->password->getLength(); + } + if (i + n > core->password->getLength()) { + n = core->password->getLength() - i; + } + core->password->del(i, n); + core->password->insert(i, data->text->ptr, data->text->length); + + for (i = 0; i < data->text->length; ++i) { + data->text->ptr[i] = '*'; + } + data->doit = True; +} + +void XPDFCore::passwordOkCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + + core->dialogDone = 1; +} + +void XPDFCore::passwordCancelCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + + core->dialogDone = -1; +} + +GString *XPDFCore::getPassword() { + XtAppContext appContext; + XEvent event; + + // NB: set <password> before calling XmTextFieldSetString, because + // SetString will trigger a call to passwordTextVerifyCbk, which + // expects <password> to be valid + password = new GString(); + XmTextFieldSetString(passwordText, ""); + XtManageChild(passwordDialog); + + appContext = XtWidgetToApplicationContext(passwordDialog); + dialogDone = 0; + do { + XtAppNextEvent(appContext, &event); + XtDispatchEvent(&event); + } while (!dialogDone); + XtUnmanageChild(passwordDialog); + + if (dialogDone < 0) { + delete password; + return NULL; + } + return password; +} |