1 //========================================================================
5 // Copyright 2002 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
15 #include <X11/keysym.h>
16 #include <X11/cursorfont.h>
21 #include "GlobalParams.h"
23 #include "ErrorCodes.h"
25 #include "PSOutputDev.h"
26 #include "TextOutputDev.h"
27 #include "XPixmapOutputDev.h"
30 // these macro defns conflict with xpdf's Object class
31 #ifdef LESSTIF_VERSION
39 // hack around old X includes which are missing these symbols
41 #define XK_Page_Up 0xFF55
44 #define XK_Page_Down 0xFF56
47 #define XK_KP_Home 0xFF95
50 #define XK_KP_Left 0xFF96
53 #define XK_KP_Up 0xFF97
56 #define XK_KP_Right 0xFF98
59 #define XK_KP_Down 0xFF99
62 #define XK_KP_Prior 0xFF9A
65 #define XK_KP_Page_Up 0xFF9A
68 #define XK_KP_Next 0xFF9B
70 #ifndef XK_KP_Page_Down
71 #define XK_KP_Page_Down 0xFF9B
74 #define XK_KP_End 0xFF9C
77 #define XK_KP_Begin 0xFF9D
80 #define XK_KP_Insert 0xFF9E
83 #define XK_KP_Delete 0xFF9F
86 //------------------------------------------------------------------------
88 #define highlightNone 0
89 #define highlightNormal 1
90 #define highlightSelected 2
92 //------------------------------------------------------------------------
94 static int zoomDPI[maxZoom - minZoom + 1] = {
97 86, 104, 124, 149, 179
100 //------------------------------------------------------------------------
102 GString *XPDFCore::currentSelection = NULL;
103 XPDFCore *XPDFCore::currentSelectionOwner = NULL;
105 //------------------------------------------------------------------------
107 //------------------------------------------------------------------------
109 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
110 Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
111 GBool installCmap, int rgbCubeSize) {
112 GString *initialZoom;
116 parentWidget = parentWidgetA;
117 display = XtDisplay(parentWidget);
118 screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
120 paperColor = paperColorA;
121 fullScreen = fullScreenA;
123 // for some reason, querying XmNvisual doesn't work (even if done
124 // after the window is mapped)
125 visual = DefaultVisual(display, screenNum);
126 XtVaGetValues(shell, XmNcolormap, &colormap, NULL);
131 drawAreaFrame = NULL;
139 // get the initial zoom value
140 initialZoom = globalParams->getInitialZoom();
141 if (!initialZoom->cmp("page")) {
143 } else if (!initialZoom->cmp("width")) {
146 zoom = atoi(initialZoom->getCString());
147 if (zoom < minZoom) {
149 } else if (zoom > maxZoom) {
158 selectXMin = selectXMax = 0;
159 selectYMin = selectYMax = 0;
161 lastDragLeft = lastDragTop = gTrue;
170 reqPasswordCbk = NULL;
173 historyCur = xpdfHistorySize - 1;
174 historyBLen = historyFLen = 0;
175 for (i = 0; i < xpdfHistorySize; ++i) {
176 history[i].fileName = NULL;
179 // optional features default to on
180 hyperlinksEnabled = gTrue;
181 selectEnabled = gTrue;
183 // do X-specific initialization and create the widgets
186 // create the OutputDev
187 out = new XPixmapOutputDev(display, screenNum, visual, colormap,
188 reverseVideo, paperColor,
189 installCmap, rgbCubeSize, gTrue,
190 &outputDevRedrawCbk, this);
194 XPDFCore::~XPDFCore() {
203 if (currentSelectionOwner == this && currentSelection) {
204 delete currentSelection;
205 currentSelection = NULL;
206 currentSelectionOwner = NULL;
208 for (i = 0; i < xpdfHistorySize; ++i) {
209 if (history[i].fileName) {
210 delete history[i].fileName;
214 XFreeGC(display, selectGC);
215 XFreeGC(display, highlightGC);
218 XFreeGC(display, drawAreaGC);
221 XtDestroyWidget(drawArea);
224 XtDestroyWidget(drawAreaFrame);
227 XtDestroyWidget(vScrollBar);
230 XtDestroyWidget(hScrollBar);
233 XtDestroyWidget(scrolledWin);
236 XFreeCursor(display, busyCursor);
239 XFreeCursor(display, linkCursor);
242 XFreeCursor(display, selectCursor);
246 //------------------------------------------------------------------------
247 // loadFile / displayPage / displayDest
248 //------------------------------------------------------------------------
250 int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
251 GString *userPassword) {
258 setCursor(busyCursor);
261 newDoc = new PDFDoc(fileName->copy(), ownerPassword, userPassword);
262 if (!newDoc->isOk()) {
263 err = newDoc->getErrorCode();
265 if (err != errEncrypted || !reqPasswordCbk) {
270 // try requesting a password
271 again = ownerPassword != NULL || userPassword != NULL;
273 if (!(password = (*reqPasswordCbk)(reqPasswordCbkData, again))) {
277 newDoc = new PDFDoc(fileName->copy(), password, password);
278 if (newDoc->isOk()) {
281 err = newDoc->getErrorCode();
283 if (err != errEncrypted) {
291 // replace old document
297 out->startDoc(doc->getXRef());
300 // nothing displayed yet
303 // save the modification time
304 modTime = getModTime(doc->getFileName()->getCString());
306 // update the parent window
308 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
309 doc->getNumPages(), NULL);
312 // back to regular cursor
318 void XPDFCore::resizeToPage(int pg) {
319 Dimension width, height;
320 double width1, height1;
321 Dimension topW, topH, topBorder, daW, daH;
322 Dimension displayW, displayH;
324 displayW = DisplayWidth(display, screenNum);
325 displayH = DisplayHeight(display, screenNum);
330 if (pg < 0 || pg > doc->getNumPages()) {
333 } else if (doc->getPageRotate(pg) == 90 ||
334 doc->getPageRotate(pg) == 270) {
335 width1 = doc->getPageHeight(pg);
336 height1 = doc->getPageWidth(pg);
338 width1 = doc->getPageWidth(pg);
339 height1 = doc->getPageHeight(pg);
341 if (zoom == zoomPage || zoom == zoomWidth) {
342 width = (Dimension)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
343 height = (Dimension)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
345 width = (Dimension)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
346 height = (Dimension)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
348 if (width > displayW - 100) {
349 width = displayW - 100;
351 if (height > displayH - 150) {
352 height = displayH - 150;
356 if (XtIsRealized(shell)) {
357 XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
358 XmNborderWidth, &topBorder, NULL);
359 XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
360 XtVaSetValues(shell, XmNwidth, width + (topW - daW),
361 XmNheight, height + (topH - daH), NULL);
363 XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
367 void XPDFCore::clear() {
381 scrollX = scrollY = 0;
383 redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
386 void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
387 GBool scrollToTop, GBool addToHist) {
394 int oldScrollX, oldScrollY;
396 // update the zoom and rotate values
397 newZoom = zoomA != zoom;
401 // check for document and valid page number
402 if (!doc || pageA <= 0 || pageA > doc->getNumPages()) {
407 setCursor(busyCursor);
410 // check for changes to the file
411 newModTime = getModTime(doc->getFileName()->getCString());
412 if (newModTime != modTime) {
413 if (loadFile(doc->getFileName()) == errNone) {
414 if (pageA > doc->getNumPages()) {
415 pageA = doc->getNumPages();
418 modTime = newModTime;
423 XFreeGC(display, selectGC);
424 XFreeGC(display, highlightGC);
435 // if zoom level changed, scroll to the top-left corner
437 scrollX = scrollY = 0;
440 // initialize mouse-related stuff
442 selectXMin = selectXMax = 0;
443 selectYMin = selectYMax = 0;
445 lastDragLeft = lastDragTop = gTrue;
448 rot = rotate + doc->getPageRotate(page);
451 } else if (rotate < 0) {
454 if (zoom == zoomPage) {
455 if (rot == 90 || rot == 270) {
456 hDPI = (drawAreaWidth / doc->getPageHeight(page)) * 72;
457 vDPI = (drawAreaHeight / doc->getPageWidth(page)) * 72;
459 hDPI = (drawAreaWidth / doc->getPageWidth(page)) * 72;
460 vDPI = (drawAreaHeight / doc->getPageHeight(page)) * 72;
462 dpi = (hDPI < vDPI) ? hDPI : vDPI;
463 } else if (zoom == zoomWidth) {
464 if (rot == 90 || rot == 270) {
465 dpi = (drawAreaWidth / doc->getPageHeight(page)) * 72;
467 dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
470 dpi = zoomDPI[zoom - minZoom];
472 out->setWindow(XtWindow(drawArea));
473 doc->displayPage(out, page, dpi, rotate, gTrue);
474 oldScrollX = scrollX;
475 oldScrollY = scrollY;
477 if (scrollX != oldScrollX || scrollY != oldScrollY) {
478 redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
484 if (++historyCur == xpdfHistorySize) {
487 h = &history[historyCur];
491 h->fileName = doc->getFileName()->copy();
493 if (historyBLen < xpdfHistorySize) {
499 // update the parent window
501 (*updateCbk)(updateCbkData, NULL, page, -1, "");
505 gcValues.foreground = BlackPixel(display, screenNum) ^
506 WhitePixel(display, screenNum);
507 gcValues.function = GXxor;
508 selectGC = XCreateGC(display, out->getPixmap(),
509 GCForeground | GCFunction, &gcValues);
510 highlightGC = XCreateGC(display, out->getPixmap(),
511 GCForeground | GCFunction, &gcValues);
513 // back to regular cursor
517 void XPDFCore::displayDest(LinkDest *dest, int zoomA, int rotateA,
523 if (dest->isPageRef()) {
524 pageRef = dest->getPageRef();
525 pg = doc->findPage(pageRef.num, pageRef.gen);
527 pg = dest->getPageNum();
529 if (pg <= 0 || pg > doc->getNumPages()) {
533 displayPage(pg, zoomA, rotateA, gTrue, addToHist);
539 switch (dest->getKind()) {
541 out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
542 if (dest->getChangeLeft() || dest->getChangeTop()) {
543 scrollTo(dest->getChangeLeft() ? dx : scrollX,
544 dest->getChangeTop() ? dy : scrollY);
546 //~ what is the zoom parameter?
556 out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
562 out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
567 out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
573 //------------------------------------------------------------------------
574 // page/position changes
575 //------------------------------------------------------------------------
577 void XPDFCore::gotoNextPage(int inc, GBool top) {
580 if (!doc || doc->getNumPages() == 0) {
583 if (page < doc->getNumPages()) {
584 if ((pg = page + inc) > doc->getNumPages()) {
585 pg = doc->getNumPages();
587 displayPage(pg, zoom, rotate, top, gTrue);
593 void XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
596 if (!doc || doc->getNumPages() == 0) {
600 if (!fullScreen && bottom) {
601 scrollY = out->getPixmapHeight() - drawAreaHeight;
605 // displayPage will call updateScrollBars()
607 if ((pg = page - dec) < 1) {
610 displayPage(pg, zoom, rotate, top, gTrue);
616 void XPDFCore::goForward() {
617 if (historyFLen == 0) {
621 if (++historyCur == xpdfHistorySize) {
626 if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
627 if (loadFile(history[historyCur].fileName) != errNone) {
632 displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
635 void XPDFCore::goBackward() {
636 if (historyBLen <= 1) {
640 if (--historyCur < 0) {
641 historyCur = xpdfHistorySize - 1;
645 if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
646 if (loadFile(history[historyCur].fileName) != errNone) {
651 displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
654 void XPDFCore::scrollLeft(int nCols) {
655 scrollTo(scrollX - nCols * 16, scrollY);
658 void XPDFCore::scrollRight(int nCols) {
659 scrollTo(scrollX + nCols * 16, scrollY);
662 void XPDFCore::scrollUp(int nLines) {
663 scrollTo(scrollX, scrollY - nLines * 16);
666 void XPDFCore::scrollDown(int nLines) {
667 scrollTo(scrollX, scrollY + nLines * 16);
670 void XPDFCore::scrollPageUp() {
672 gotoPrevPage(1, gFalse, gTrue);
674 scrollTo(scrollX, scrollY - drawAreaHeight);
678 void XPDFCore::scrollPageDown() {
679 if (scrollY >= out->getPixmapHeight() - drawAreaHeight) {
680 gotoNextPage(1, gTrue);
682 scrollTo(scrollX, scrollY + drawAreaHeight);
686 void XPDFCore::scrollTo(int x, int y) {
692 maxPos = out ? out->getPixmapWidth() : 1;
693 if (maxPos < drawAreaWidth) {
694 maxPos = drawAreaWidth;
698 } else if (x > maxPos - drawAreaWidth) {
699 pos = maxPos - drawAreaWidth;
703 if (scrollX != pos) {
705 XmScrollBarSetValues(hScrollBar, scrollX, drawAreaWidth, 16,
706 drawAreaWidth, False);
710 maxPos = out ? out->getPixmapHeight() : 1;
711 if (maxPos < drawAreaHeight) {
712 maxPos = drawAreaHeight;
716 } else if (y > maxPos - drawAreaHeight) {
717 pos = maxPos - drawAreaHeight;
721 if (scrollY != pos) {
723 XmScrollBarSetValues(vScrollBar, scrollY, drawAreaHeight, 16,
724 drawAreaHeight, False);
729 redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
733 //------------------------------------------------------------------------
735 //------------------------------------------------------------------------
737 void XPDFCore::setSelection(int newXMin, int newYMin,
738 int newXMax, int newYMax) {
741 GBool needRedraw, needScroll;
742 GBool moveLeft, moveRight, moveTop, moveBottom;
744 pixmap = out->getPixmap();
747 // erase old selection on off-screen bitmap
749 if (selectXMin < selectXMax && selectYMin < selectYMax) {
750 XFillRectangle(display, pixmap,
751 selectGC, selectXMin, selectYMin,
752 selectXMax - selectXMin, selectYMax - selectYMin);
756 // draw new selection on off-screen bitmap
757 if (newXMin < newXMax && newYMin < newYMax) {
758 XFillRectangle(display, pixmap,
759 selectGC, newXMin, newYMin,
760 newXMax - newXMin, newYMax - newYMin);
764 // check which edges moved
765 moveLeft = newXMin != selectXMin;
766 moveTop = newYMin != selectYMin;
767 moveRight = newXMax != selectXMax;
768 moveBottom = newYMax != selectYMax;
770 // redraw currently visible part of bitmap
773 redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
774 (newYMin < selectYMin) ? newYMin : selectYMin,
775 (newXMin > selectXMin) ? newXMin : selectXMin,
776 (newYMax > selectYMax) ? newYMax : selectYMax);
779 redrawRectangle((newXMax < selectXMax) ? newXMax : selectXMax,
780 (newYMin < selectYMin) ? newYMin : selectYMin,
781 (newXMax > selectXMax) ? newXMax : selectXMax,
782 (newYMax > selectYMax) ? newYMax : selectYMax);
785 redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
786 (newYMin < selectYMin) ? newYMin : selectYMin,
787 (newXMax > selectXMax) ? newXMax : selectXMax,
788 (newYMin > selectYMin) ? newYMin : selectYMin);
791 redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
792 (newYMax < selectYMax) ? newYMax : selectYMax,
793 (newXMax > selectXMax) ? newXMax : selectXMax,
794 (newYMax > selectYMax) ? newYMax : selectYMax);
798 // switch to new selection coords
799 selectXMin = newXMin;
800 selectXMax = newXMax;
801 selectYMin = newYMin;
802 selectYMax = newYMax;
804 // scroll if necessary
811 if (moveLeft && selectXMin < x) {
814 } else if (moveRight && selectXMax >= x + drawAreaWidth) {
815 x = selectXMax - drawAreaWidth;
817 } else if (moveLeft && selectXMin >= x + drawAreaWidth) {
818 x = selectXMin - drawAreaWidth;
820 } else if (moveRight && selectXMax < x) {
824 if (moveTop && selectYMin < y) {
827 } else if (moveBottom && selectYMax >= y + drawAreaHeight) {
828 y = selectYMax - drawAreaHeight;
830 } else if (moveTop && selectYMin >= y + drawAreaHeight) {
831 y = selectYMin - drawAreaHeight;
833 } else if (moveBottom && selectYMax < y) {
842 void XPDFCore::moveSelection(int mx, int my) {
843 int xMin, yMin, xMax, yMax;
848 } else if (mx >= out->getPixmapWidth()) {
849 mx = out->getPixmapWidth() - 1;
853 } else if (my >= out->getPixmapHeight()) {
854 my = out->getPixmapHeight() - 1;
857 // move appropriate edges of selection
859 if (mx < selectXMax) {
865 lastDragLeft = gFalse;
868 if (mx > selectXMin) {
874 lastDragLeft = gTrue;
878 if (my < selectYMax) {
884 lastDragTop = gFalse;
887 if (my > selectYMin) {
897 // redraw the selection
898 setSelection(xMin, yMin, xMax, yMax);
901 // X's copy-and-paste mechanism is brain damaged. Xt doesn't help
902 // any, but doesn't make it too much worse, either. Motif, on the
903 // other hand, adds significant complexity to the mess. So here we
904 // blow off the Motif junk and stick to plain old Xt. The next two
905 // functions (copySelection and convertSelectionCbk) implement the
906 // magic needed to deal with Xt's mechanism. Note that this requires
907 // global variables (currentSelection and currentSelectionOwner).
909 void XPDFCore::copySelection() {
910 if (!doc->okToCopy()) {
913 if (currentSelection) {
914 delete currentSelection;
916 //~ for multithreading: need a mutex here
917 currentSelection = out->getText(selectXMin, selectYMin,
918 selectXMax, selectYMax);
919 currentSelectionOwner = this;
920 XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
921 &convertSelectionCbk, NULL, NULL);
924 Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
925 Atom *target, Atom *type,
926 XtPointer *value, unsigned long *length,
928 if (*target != XA_STRING) {
931 //~ for multithreading: need a mutex here
932 *value = XtNewString(currentSelection->getCString());
933 *length = currentSelection->getLength();
935 *format = 8; // 8-bit elements
939 GBool XPDFCore::getSelection(int *xMin, int *yMin, int *xMax, int *yMax) {
940 if (selectXMin >= selectXMax || selectYMin >= selectYMax) {
950 GString *XPDFCore::extractText(int xMin, int yMin, int xMax, int yMax) {
951 if (!doc->okToCopy()) {
954 return out->getText(xMin, yMin, xMax, yMax);
957 GString *XPDFCore::extractText(int pageNum,
958 int xMin, int yMin, int xMax, int yMax) {
959 TextOutputDev *textOut;
962 if (!doc->okToCopy()) {
965 textOut = new TextOutputDev(NULL, gFalse, gFalse, gFalse);
966 if (!textOut->isOk()) {
970 doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
971 s = textOut->getText(xMin, yMin, xMax, yMax);
976 //------------------------------------------------------------------------
978 //------------------------------------------------------------------------
980 void XPDFCore::doLink(int mx, int my) {
985 out->cvtDevToUser(mx, my, &x, &y);
986 if ((action = doc->findLink(x, y))) {
991 void XPDFCore::doAction(LinkAction *action) {
996 GString *fileName, *fileName2;
999 Object movieAnnot, obj1, obj2;
1003 switch (kind = action->getKind()) {
1005 // GoTo / GoToR action
1008 if (kind == actionGoTo) {
1011 if ((dest = ((LinkGoTo *)action)->getDest())) {
1012 dest = dest->copy();
1013 } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
1014 namedDest = namedDest->copy();
1019 if ((dest = ((LinkGoToR *)action)->getDest())) {
1020 dest = dest->copy();
1021 } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
1022 namedDest = namedDest->copy();
1024 s = ((LinkGoToR *)action)->getFileName()->getCString();
1025 //~ translate path name for VMS (deal with '/')
1026 if (isAbsolutePath(s)) {
1027 fileName = new GString(s);
1029 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
1031 if (loadFile(fileName) != errNone) {
1044 dest = doc->findDest(namedDest);
1048 displayDest(dest, zoom, rotate, gTrue);
1051 if (kind == actionGoToR) {
1052 displayPage(1, zoom, 0, gFalse, gTrue);
1059 fileName = ((LinkLaunch *)action)->getFileName();
1060 s = fileName->getCString();
1061 if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
1062 !strcmp(s + fileName->getLength() - 4, ".PDF")) {
1063 //~ translate path name for VMS (deal with '/')
1064 if (isAbsolutePath(s)) {
1065 fileName = fileName->copy();
1067 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
1069 if (loadFile(fileName) != errNone) {
1074 displayPage(1, zoom, rotate, gFalse, gTrue);
1076 fileName = fileName->copy();
1077 if (((LinkLaunch *)action)->getParams()) {
1078 fileName->append(' ');
1079 fileName->append(((LinkLaunch *)action)->getParams());
1082 fileName->insert(0, "spawn/nowait ");
1083 #elif defined(__EMX__)
1084 fileName->insert(0, "start /min /n ");
1086 fileName->append(" &");
1088 msg = new GString("About to execute the command:\n");
1089 msg->append(fileName);
1090 if (doQuestionDialog("Launching external application", msg)) {
1091 system(fileName->getCString());
1100 if (!(cmd = globalParams->getURLCommand())) {
1101 error(-1, "No urlCommand defined in config file");
1104 runCommand(cmd, ((LinkURI *)action)->getURI());
1109 actionName = ((LinkNamed *)action)->getName();
1110 if (!actionName->cmp("NextPage")) {
1111 gotoNextPage(1, gTrue);
1112 } else if (!actionName->cmp("PrevPage")) {
1113 gotoPrevPage(1, gTrue, gFalse);
1114 } else if (!actionName->cmp("FirstPage")) {
1116 displayPage(1, zoom, rotate, gTrue, gTrue);
1118 } else if (!actionName->cmp("LastPage")) {
1119 if (page != doc->getNumPages()) {
1120 displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
1122 } else if (!actionName->cmp("GoBack")) {
1124 } else if (!actionName->cmp("GoForward")) {
1126 } else if (!actionName->cmp("Quit")) {
1128 (*actionCbk)(actionCbkData, "Quit");
1131 error(-1, "Unknown named action: '%s'", actionName->getCString());
1137 if (!(cmd = globalParams->getMovieCommand())) {
1138 error(-1, "No movieCommand defined in config file");
1141 if (((LinkMovie *)action)->hasAnnotRef()) {
1142 doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
1143 ((LinkMovie *)action)->getAnnotRef()->gen,
1146 doc->getCatalog()->getPage(page)->getAnnots(&obj1);
1147 if (obj1.isArray()) {
1148 for (i = 0; i < obj1.arrayGetLength(); ++i) {
1149 if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
1150 if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
1161 if (movieAnnot.isDict()) {
1162 if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
1163 if (obj1.dictLookup("F", &obj2)) {
1164 if ((fileName = LinkAction::getFileSpecName(&obj2))) {
1165 if (!isAbsolutePath(fileName->getCString())) {
1166 fileName2 = appendToPath(
1167 grabPath(doc->getFileName()->getCString()),
1168 fileName->getCString());
1170 fileName = fileName2;
1172 runCommand(cmd, fileName);
1183 // unknown action type
1185 error(-1, "Unknown link action type: '%s'",
1186 ((LinkUnknown *)action)->getAction()->getCString());
1191 // Run a command, given a <cmdFmt> string with one '%s' in it, and an
1192 // <arg> string to insert in place of the '%s'.
1193 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
1198 if ((s = strstr(cmdFmt->getCString(), "%s"))) {
1200 // filter out any quote marks (' or ") to avoid a potential
1203 while (i < cmd->getLength()) {
1204 if (cmd->getChar(i) == '"') {
1206 cmd->insert(i, "%22");
1208 } else if (cmd->getChar(i) == '\'') {
1210 cmd->insert(i, "%27");
1216 cmd->insert(0, cmdFmt->getCString(),
1217 s - cmdFmt->getCString());
1220 cmd = cmdFmt->copy();
1223 cmd->insert(0, "spawn/nowait ");
1224 #elif defined(__EMX__)
1225 cmd->insert(0, "start /min /n ");
1229 system(cmd->getCString());
1234 //------------------------------------------------------------------------
1236 //------------------------------------------------------------------------
1238 void XPDFCore::find(char *s) {
1240 TextOutputDev *textOut;
1241 int xMin, yMin, xMax, yMax;
1242 double xMin1, yMin1, xMax1, yMax1;
1247 // check for zero-length string
1253 // set cursor to watch
1254 setCursor(busyCursor);
1256 // convert to Unicode
1257 #if 1 //~ should do something more intelligent here
1259 u = (Unicode *)gmalloc(len * sizeof(Unicode));
1260 for (i = 0; i < len; ++i) {
1261 u[i] = (Unicode)(s[i] & 0xff);
1265 // search current page starting at current selection or top of page
1266 xMin = yMin = xMax = yMax = 0;
1267 if (selectXMin < selectXMax && selectYMin < selectYMax) {
1269 yMin = (selectYMin + selectYMax) / 2;
1274 if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
1278 // search following pages
1279 textOut = new TextOutputDev(NULL, gFalse, gFalse, gFalse);
1280 if (!textOut->isOk()) {
1284 for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
1285 doc->displayPage(textOut, pg, 72, 0, gFalse);
1286 if (textOut->findText(u, len, gTrue, gTrue,
1287 &xMin1, &yMin1, &xMax1, &yMax1)) {
1292 // search previous pages
1293 for (pg = 1; pg < page; ++pg) {
1294 doc->displayPage(textOut, pg, 72, 0, gFalse);
1295 if (textOut->findText(u, len, gTrue, gTrue,
1296 &xMin1, &yMin1, &xMax1, &yMax1)) {
1302 // search current page ending at current selection
1303 if (selectXMin < selectXMax && selectYMin < selectYMax) {
1305 yMax = (selectYMin + selectYMax) / 2;
1306 if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
1315 // found on a different page
1318 displayPage(pg, zoom, rotate, gTrue, gTrue);
1319 if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
1320 // this can happen if coalescing is bad
1324 // found: change the selection
1326 setSelection(xMin, yMin, xMax, yMax);
1327 #ifndef NO_TEXT_SELECT
1334 // reset cursors to normal
1338 //------------------------------------------------------------------------
1340 //------------------------------------------------------------------------
1342 void XPDFCore::setBusyCursor(GBool busy) {
1343 setCursor(busy ? busyCursor : None);
1346 void XPDFCore::takeFocus() {
1347 XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
1350 //------------------------------------------------------------------------
1352 //------------------------------------------------------------------------
1354 void XPDFCore::initWindow() {
1358 // create the cursors
1359 busyCursor = XCreateFontCursor(display, XC_watch);
1360 linkCursor = XCreateFontCursor(display, XC_hand2);
1361 selectCursor = XCreateFontCursor(display, XC_cross);
1364 // create the scrolled window and scrollbars
1366 XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
1367 XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
1368 scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
1369 XtManageChild(scrolledWin);
1371 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
1372 XtSetArg(args[n], XmNminimum, 0); ++n;
1373 XtSetArg(args[n], XmNmaximum, 1); ++n;
1374 XtSetArg(args[n], XmNsliderSize, 1); ++n;
1375 XtSetArg(args[n], XmNvalue, 0); ++n;
1376 XtSetArg(args[n], XmNincrement, 1); ++n;
1377 XtSetArg(args[n], XmNpageIncrement, 1); ++n;
1378 hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
1379 XtManageChild(hScrollBar);
1380 XtAddCallback(hScrollBar, XmNvalueChangedCallback,
1381 &hScrollChangeCbk, (XtPointer)this);
1382 #ifndef DISABLE_SMOOTH_SCROLL
1383 XtAddCallback(hScrollBar, XmNdragCallback,
1384 &hScrollDragCbk, (XtPointer)this);
1387 XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
1388 XtSetArg(args[n], XmNminimum, 0); ++n;
1389 XtSetArg(args[n], XmNmaximum, 1); ++n;
1390 XtSetArg(args[n], XmNsliderSize, 1); ++n;
1391 XtSetArg(args[n], XmNvalue, 0); ++n;
1392 XtSetArg(args[n], XmNincrement, 1); ++n;
1393 XtSetArg(args[n], XmNpageIncrement, 1); ++n;
1394 vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
1395 XtManageChild(vScrollBar);
1396 XtAddCallback(vScrollBar, XmNvalueChangedCallback,
1397 &vScrollChangeCbk, (XtPointer)this);
1398 #ifndef DISABLE_SMOOTH_SCROLL
1399 XtAddCallback(vScrollBar, XmNdragCallback,
1400 &vScrollDragCbk, (XtPointer)this);
1403 // create the drawing area
1405 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
1406 XtSetArg(args[n], XmNmarginWidth, 0); ++n;
1407 XtSetArg(args[n], XmNmarginHeight, 0); ++n;
1409 XtSetArg(args[n], XmNshadowThickness, 0); ++n;
1411 drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
1412 XtManageChild(drawAreaFrame);
1414 XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
1415 XtSetArg(args[n], XmNbackground, paperColor); ++n;
1416 XtSetArg(args[n], XmNwidth, 700); ++n;
1417 XtSetArg(args[n], XmNheight, 500); ++n;
1418 drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
1419 XtManageChild(drawArea);
1420 XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
1421 XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
1422 XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
1423 resizeCbk(drawArea, this, NULL);
1425 // set up mouse motion translations
1426 XtOverrideTranslations(drawArea, XtParseTranslationTable(
1427 "<Btn1Down>:DrawingAreaInput()\n"
1428 "<Btn1Up>:DrawingAreaInput()\n"
1429 "<Btn1Motion>:DrawingAreaInput()\n"
1430 "<Motion>:DrawingAreaInput()"));
1432 // can't create a GC until the window gets mapped
1438 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
1439 XtPointer callData) {
1440 XPDFCore *core = (XPDFCore *)ptr;
1441 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1443 core->scrollTo(data->value, core->scrollY);
1446 void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
1447 XtPointer callData) {
1448 XPDFCore *core = (XPDFCore *)ptr;
1449 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1451 core->scrollTo(data->value, core->scrollY);
1454 void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
1455 XtPointer callData) {
1456 XPDFCore *core = (XPDFCore *)ptr;
1457 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1459 core->scrollTo(core->scrollX, data->value);
1462 void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
1463 XtPointer callData) {
1464 XPDFCore *core = (XPDFCore *)ptr;
1465 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1467 core->scrollTo(core->scrollX, data->value);
1470 void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1471 XPDFCore *core = (XPDFCore *)ptr;
1477 XtSetArg(args[n], XmNwidth, &w); ++n;
1478 XtSetArg(args[n], XmNheight, &h); ++n;
1479 XtGetValues(core->drawArea, args, n);
1480 core->drawAreaWidth = (int)w;
1481 core->drawAreaHeight = (int)h;
1482 if (core->page >= 0 &&
1483 (core->zoom == zoomPage || core->zoom == zoomWidth)) {
1484 core->displayPage(core->page, core->zoom, core->rotate,
1487 core->updateScrollBars();
1491 void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1492 XPDFCore *core = (XPDFCore *)ptr;
1493 XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1496 if (data->reason == XmCR_EXPOSE) {
1497 x = core->scrollX + data->event->xexpose.x;
1498 y = core->scrollY + data->event->xexpose.y;
1499 w = data->event->xexpose.width;
1500 h = data->event->xexpose.height;
1504 w = core->drawAreaWidth;
1505 h = core->drawAreaHeight;
1507 core->redrawRectangle(x, y, w, h);
1510 void XPDFCore::outputDevRedrawCbk(void *data) {
1511 XPDFCore *core = (XPDFCore *)data;
1513 core->redrawRectangle(core->scrollX, core->scrollY,
1514 core->drawAreaWidth, core->drawAreaHeight);
1517 void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1518 XPDFCore *core = (XPDFCore *)ptr;
1519 XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1528 switch (data->event->type) {
1530 if (data->event->xbutton.button == 1) {
1532 if (core->doc && core->doc->getNumPages() > 0) {
1533 if (core->selectEnabled) {
1534 mx = core->scrollX + data->event->xbutton.x;
1535 my = core->scrollY + data->event->xbutton.y;
1536 core->setSelection(mx, my, mx, my);
1537 core->setCursor(core->selectCursor);
1538 core->dragging = gTrue;
1541 } else if (data->event->xbutton.button == 2) {
1542 if (!core->fullScreen) {
1543 core->panning = gTrue;
1544 core->panMX = data->event->xbutton.x;
1545 core->panMY = data->event->xbutton.y;
1547 } else if (data->event->xbutton.button == 4) { // mouse wheel up
1548 if (core->fullScreen) {
1549 core->gotoPrevPage(1, gTrue, gFalse);
1550 } else if (core->scrollY == 0) {
1551 core->gotoPrevPage(1, gFalse, gTrue);
1555 } else if (data->event->xbutton.button == 5) { // mouse wheel down
1556 if (core->fullScreen ||
1558 core->out->getPixmapHeight() - core->drawAreaHeight) {
1559 core->gotoNextPage(1, gTrue);
1561 core->scrollDown(1);
1563 } else if (data->event->xbutton.button == 6) { // second mouse wheel right
1564 if (!core->fullScreen) {
1565 core->scrollRight(1);
1567 } else if (data->event->xbutton.button == 7) { // second mouse wheel left
1568 if (!core->fullScreen) {
1569 core->scrollLeft(1);
1572 if (*core->mouseCbk) {
1573 (*core->mouseCbk)(core->mouseCbkData, data->event);
1578 if (data->event->xbutton.button == 1) {
1579 if (core->doc && core->doc->getNumPages() > 0) {
1580 mx = core->scrollX + data->event->xbutton.x;
1581 my = core->scrollY + data->event->xbutton.y;
1582 if (core->dragging) {
1583 core->dragging = gFalse;
1584 core->setCursor(None);
1585 core->moveSelection(mx, my);
1586 #ifndef NO_TEXT_SELECT
1587 if (core->selectXMin != core->selectXMax &&
1588 core->selectYMin != core->selectYMax) {
1589 if (core->doc->okToCopy()) {
1590 core->copySelection();
1592 error(-1, "Copying of text from this document is not allowed.");
1597 if (core->hyperlinksEnabled) {
1598 if (core->selectXMin == core->selectXMax ||
1599 core->selectYMin == core->selectYMax) {
1600 core->doLink(mx, my);
1604 } else if (data->event->xbutton.button == 2) {
1605 core->panning = gFalse;
1607 if (*core->mouseCbk) {
1608 (*core->mouseCbk)(core->mouseCbkData, data->event);
1613 if (core->doc && core->doc->getNumPages() > 0) {
1614 mx = core->scrollX + data->event->xbutton.x;
1615 my = core->scrollY + data->event->xbutton.y;
1616 if (core->dragging) {
1617 core->moveSelection(mx, my);
1618 } else if (core->hyperlinksEnabled) {
1619 core->out->cvtDevToUser(mx, my, &x, &y);
1620 if ((action = core->doc->findLink(x, y))) {
1621 core->setCursor(core->linkCursor);
1622 if (action != core->linkAction) {
1623 core->linkAction = action;
1624 if (core->updateCbk) {
1626 switch (action->getKind()) {
1628 s = "[internal link]";
1631 s = ((LinkGoToR *)action)->getFileName()->getCString();
1634 s = ((LinkLaunch *)action)->getFileName()->getCString();
1637 s = ((LinkURI *)action)->getURI()->getCString();
1640 s = ((LinkNamed *)action)->getName()->getCString();
1646 s = "[unknown link]";
1649 (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
1653 core->setCursor(None);
1654 if (core->linkAction) {
1655 core->linkAction = NULL;
1656 if (core->updateCbk) {
1657 (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
1663 if (core->panning) {
1664 core->scrollTo(core->scrollX - (data->event->xbutton.x - core->panMX),
1665 core->scrollY - (data->event->xbutton.y - core->panMY));
1666 core->panMX = data->event->xbutton.x;
1667 core->panMY = data->event->xbutton.y;
1671 n = XLookupString(&data->event->xkey, buf, sizeof(buf) - 1,
1673 core->keyPress(buf, key, data->event->xkey.state);
1678 void XPDFCore::keyPress(char *s, KeySym key, Guint modifiers) {
1682 if (modifiers & ControlMask) {
1683 displayPage(1, zoom, rotate, gTrue, gTrue);
1684 } else if (!fullScreen) {
1690 if (modifiers & ControlMask) {
1691 displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
1692 } else if (!fullScreen) {
1693 scrollTo(out->getPixmapWidth() - drawAreaWidth,
1694 out->getPixmapHeight() - drawAreaHeight);
1700 gotoPrevPage(1, gTrue, gFalse);
1706 case XK_KP_Page_Down:
1708 gotoNextPage(1, gTrue);
1740 (*keyPressCbk)(keyPressCbkData, s, key, modifiers);
1744 void XPDFCore::redrawRectangle(int x, int y, int w, int h) {
1753 if (x + w > scrollX + drawAreaWidth) {
1754 w = scrollX + drawAreaWidth - x;
1760 if (y + h > scrollY + drawAreaHeight) {
1761 h = scrollY + drawAreaHeight - y;
1764 // create a GC for the drawing area
1765 drawAreaWin = XtWindow(drawArea);
1767 gcValues.foreground = paperColor;
1768 drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
1771 // draw white background past the edges of the document
1772 if (x + w > out->getPixmapWidth()) {
1773 XFillRectangle(display, drawAreaWin, drawAreaGC,
1774 out->getPixmapWidth() - scrollX, y - scrollY,
1775 x + w - out->getPixmapWidth(), h);
1776 w = out->getPixmapWidth() - x;
1778 if (y + h > out->getPixmapHeight()) {
1779 XFillRectangle(display, drawAreaWin, drawAreaGC,
1780 x - scrollX, out->getPixmapHeight() - scrollY,
1781 w, y + h - out->getPixmapHeight());
1782 h = out->getPixmapHeight() - y;
1785 // redraw (checking to see if pixmap has been allocated yet)
1786 if (out->getPixmapWidth() > 0) {
1787 XCopyArea(display, out->getPixmap(), drawAreaWin, drawAreaGC,
1788 x, y, w, h, x - scrollX, y - scrollY);
1792 void XPDFCore::updateScrollBars() {
1797 maxPos = out ? out->getPixmapWidth() : 1;
1798 if (maxPos < drawAreaWidth) {
1799 maxPos = drawAreaWidth;
1801 if (scrollX > maxPos - drawAreaWidth) {
1802 scrollX = maxPos - drawAreaWidth;
1805 XtSetArg(args[n], XmNvalue, scrollX); ++n;
1806 XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1807 XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
1808 XtSetArg(args[n], XmNincrement, 16); ++n;
1809 XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
1810 XtSetValues(hScrollBar, args, n);
1812 maxPos = out ? out->getPixmapHeight() : 1;
1813 if (maxPos < drawAreaHeight) {
1814 maxPos = drawAreaHeight;
1816 if (scrollY > maxPos - drawAreaHeight) {
1817 scrollY = maxPos - drawAreaHeight;
1820 XtSetArg(args[n], XmNvalue, scrollY); ++n;
1821 XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1822 XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
1823 XtSetArg(args[n], XmNincrement, 16); ++n;
1824 XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
1825 XtSetValues(vScrollBar, args, n);
1828 void XPDFCore::setCursor(Cursor cursor) {
1831 if (cursor == currentCursor) {
1834 if (!(topWin = XtWindow(shell))) {
1837 if (cursor == None) {
1838 XUndefineCursor(display, topWin);
1840 XDefineCursor(display, topWin, cursor);
1843 currentCursor = cursor;
1846 GBool XPDFCore::doQuestionDialog(char *title, GString *msg) {
1847 return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
1850 void XPDFCore::doInfoDialog(char *title, GString *msg) {
1851 doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
1854 void XPDFCore::doErrorDialog(char *title, GString *msg) {
1855 doDialog(XmDIALOG_ERROR, gFalse, title, msg);
1858 GBool XPDFCore::doDialog(int type, GBool hasCancel,
1859 char *title, GString *msg) {
1861 XtAppContext appContext;
1868 XtSetArg(args[n], XmNdialogType, type); ++n;
1869 XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1870 s1 = XmStringCreateLocalized(title);
1871 XtSetArg(args[n], XmNdialogTitle, s1); ++n;
1872 s2 = XmStringCreateLocalized(msg->getCString());
1873 XtSetArg(args[n], XmNmessageString, s2); ++n;
1874 dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
1877 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
1878 XtAddCallback(dialog, XmNokCallback,
1879 &dialogOkCbk, (XtPointer)this);
1881 XtAddCallback(dialog, XmNcancelCallback,
1882 &dialogCancelCbk, (XtPointer)this);
1884 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
1887 XtManageChild(dialog);
1889 appContext = XtWidgetToApplicationContext(dialog);
1892 XtAppNextEvent(appContext, &event);
1893 XtDispatchEvent(&event);
1894 } while (!dialogDone);
1896 XtUnmanageChild(dialog);
1897 XtDestroyWidget(dialog);
1899 return dialogDone > 0;
1902 void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
1903 XtPointer callData) {
1904 XPDFCore *core = (XPDFCore *)ptr;
1906 core->dialogDone = 1;
1909 void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
1910 XtPointer callData) {
1911 XPDFCore *core = (XPDFCore *)ptr;
1913 core->dialogDone = -1;