filters

XPDFCore.cc

00001 //========================================================================
00002 //
00003 // XPDFCore.cc
00004 //
00005 // Copyright 2002 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <X11/keysym.h>
00016 #include <X11/cursorfont.h>
00017 #include "gmem.h"
00018 #include "GString.h"
00019 #include "GList.h"
00020 #include "Error.h"
00021 #include "GlobalParams.h"
00022 #include "PDFDoc.h"
00023 #include "ErrorCodes.h"
00024 #include "GfxState.h"
00025 #include "PSOutputDev.h"
00026 #include "TextOutputDev.h"
00027 #include "XPixmapOutputDev.h"
00028 #include "XPDFCore.h"
00029 
00030 // these macro defns conflict with xpdf's Object class
00031 #ifdef LESSTIF_VERSION
00032 #undef XtDisplay
00033 #undef XtScreen
00034 #undef XtWindow
00035 #undef XtParent
00036 #undef XtIsRealized
00037 #endif
00038 
00039 // hack around old X includes which are missing these symbols
00040 #ifndef XK_Page_Up
00041 #define XK_Page_Up              0xFF55
00042 #endif
00043 #ifndef XK_Page_Down
00044 #define XK_Page_Down            0xFF56
00045 #endif
00046 #ifndef XK_KP_Home
00047 #define XK_KP_Home              0xFF95
00048 #endif
00049 #ifndef XK_KP_Left
00050 #define XK_KP_Left              0xFF96
00051 #endif
00052 #ifndef XK_KP_Up
00053 #define XK_KP_Up                0xFF97
00054 #endif
00055 #ifndef XK_KP_Right
00056 #define XK_KP_Right             0xFF98
00057 #endif
00058 #ifndef XK_KP_Down
00059 #define XK_KP_Down              0xFF99
00060 #endif
00061 #ifndef XK_KP_Prior
00062 #define XK_KP_Prior             0xFF9A
00063 #endif
00064 #ifndef XK_KP_Page_Up
00065 #define XK_KP_Page_Up           0xFF9A
00066 #endif
00067 #ifndef XK_KP_Next
00068 #define XK_KP_Next              0xFF9B
00069 #endif
00070 #ifndef XK_KP_Page_Down
00071 #define XK_KP_Page_Down         0xFF9B
00072 #endif
00073 #ifndef XK_KP_End
00074 #define XK_KP_End               0xFF9C
00075 #endif
00076 #ifndef XK_KP_Begin
00077 #define XK_KP_Begin             0xFF9D
00078 #endif
00079 #ifndef XK_KP_Insert
00080 #define XK_KP_Insert            0xFF9E
00081 #endif
00082 #ifndef XK_KP_Delete
00083 #define XK_KP_Delete            0xFF9F
00084 #endif
00085 
00086 //------------------------------------------------------------------------
00087 
00088 #define highlightNone     0
00089 #define highlightNormal   1
00090 #define highlightSelected 2
00091 
00092 //------------------------------------------------------------------------
00093 
00094 static int zoomDPI[maxZoom - minZoom + 1] = {
00095   29, 35, 42, 50, 60,
00096   72,
00097   86, 104, 124, 149, 179
00098 };
00099 
00100 //------------------------------------------------------------------------
00101 
00102 GString *XPDFCore::currentSelection = NULL;
00103 XPDFCore *XPDFCore::currentSelectionOwner = NULL;
00104 
00105 //------------------------------------------------------------------------
00106 // XPDFCore
00107 //------------------------------------------------------------------------
00108 
00109 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
00110            Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
00111            GBool installCmap, int rgbCubeSize) {
00112   GString *initialZoom;
00113   int i;
00114 
00115   shell = shellA;
00116   parentWidget = parentWidgetA;
00117   display = XtDisplay(parentWidget);
00118   screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
00119 
00120   paperColor = paperColorA;
00121   fullScreen = fullScreenA;
00122 
00123   // for some reason, querying XmNvisual doesn't work (even if done
00124   // after the window is mapped)
00125   visual = DefaultVisual(display, screenNum);
00126   XtVaGetValues(shell, XmNcolormap, &colormap, NULL);
00127 
00128   scrolledWin = NULL;
00129   hScrollBar = NULL;
00130   vScrollBar = NULL;
00131   drawAreaFrame = NULL;
00132   drawArea = NULL;
00133   out = NULL;
00134 
00135   doc = NULL;
00136   page = 0;
00137   rotate = 0;
00138 
00139   // get the initial zoom value
00140   initialZoom = globalParams->getInitialZoom();
00141   if (!initialZoom->cmp("page")) {
00142     zoom = zoomPage;
00143   } else if (!initialZoom->cmp("width")) {
00144     zoom = zoomWidth;
00145   } else {
00146     zoom = atoi(initialZoom->getCString());
00147     if (zoom < minZoom) {
00148       zoom = minZoom;
00149     } else if (zoom > maxZoom) {
00150       zoom = maxZoom;
00151     }
00152   }
00153 
00154   scrollX = 0;
00155   scrollY = 0;
00156   linkAction = NULL;
00157   selectXMin = selectXMax = 0;
00158   selectYMin = selectYMax = 0;
00159   dragging = gFalse;
00160   lastDragLeft = lastDragTop = gTrue;
00161 
00162   panning = gFalse;
00163 
00164 
00165   updateCbk = NULL;
00166   actionCbk = NULL;
00167   keyPressCbk = NULL;
00168   mouseCbk = NULL;
00169   reqPasswordCbk = NULL;
00170 
00171   // no history yet
00172   historyCur = xpdfHistorySize - 1;
00173   historyBLen = historyFLen = 0;
00174   for (i = 0; i < xpdfHistorySize; ++i) {
00175     history[i].fileName = NULL;
00176   }
00177 
00178   // optional features default to on
00179   hyperlinksEnabled = gTrue;
00180   selectEnabled = gTrue;
00181 
00182   // do X-specific initialization and create the widgets
00183   initWindow();
00184 
00185   // create the OutputDev
00186   out = new XPixmapOutputDev(display, screenNum, visual, colormap,
00187                  reverseVideo, paperColor,
00188                  installCmap, rgbCubeSize, gTrue,
00189                  &outputDevRedrawCbk, this);
00190   out->startDoc(NULL);
00191 }
00192 
00193 XPDFCore::~XPDFCore() {
00194   int i;
00195 
00196   if (out) {
00197     delete out;
00198   }
00199   if (doc) {
00200     delete doc;
00201   }
00202   if (currentSelectionOwner == this && currentSelection) {
00203     delete currentSelection;
00204     currentSelection = NULL;
00205     currentSelectionOwner = NULL;
00206   }
00207   for (i = 0; i < xpdfHistorySize; ++i) {
00208     if (history[i].fileName) {
00209       delete history[i].fileName;
00210     }
00211   }
00212   if (selectGC) {
00213     XFreeGC(display, selectGC);
00214     XFreeGC(display, highlightGC);
00215   }
00216   if (drawAreaGC) {
00217     XFreeGC(display, drawAreaGC);
00218   }
00219   if (drawArea) {
00220     XtDestroyWidget(drawArea);
00221   }
00222   if (drawAreaFrame) {
00223     XtDestroyWidget(drawAreaFrame);
00224   }
00225   if (vScrollBar) {
00226     XtDestroyWidget(vScrollBar);
00227   }
00228   if (hScrollBar) {
00229     XtDestroyWidget(hScrollBar);
00230   }
00231   if (scrolledWin) {
00232     XtDestroyWidget(scrolledWin);
00233   }
00234   if (busyCursor) {
00235     XFreeCursor(display, busyCursor);
00236   }
00237   if (linkCursor) {
00238     XFreeCursor(display, linkCursor);
00239   }
00240   if (selectCursor) {
00241     XFreeCursor(display, selectCursor);
00242   }
00243 }
00244 
00245 //------------------------------------------------------------------------
00246 // loadFile / displayPage / displayDest
00247 //------------------------------------------------------------------------
00248 
00249 int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
00250                GString *userPassword) {
00251   PDFDoc *newDoc;
00252   GString *password;
00253   GBool again;
00254   int err;
00255 
00256   // busy cursor
00257   setCursor(busyCursor);
00258 
00259   // open the PDF file
00260   newDoc = new PDFDoc(fileName->copy(), ownerPassword, userPassword);
00261   if (!newDoc->isOk()) {
00262     err = newDoc->getErrorCode();
00263     delete newDoc;
00264     if (err != errEncrypted || !reqPasswordCbk) {
00265       setCursor(None);
00266       return err;
00267     }
00268 
00269     // try requesting a password
00270     again = ownerPassword != NULL || userPassword != NULL;
00271     while (1) {
00272       if (!(password = (*reqPasswordCbk)(reqPasswordCbkData, again))) {
00273     setCursor(None);
00274     return errEncrypted;
00275       }
00276       newDoc = new PDFDoc(fileName->copy(), password, password);
00277       if (newDoc->isOk()) {
00278     break;
00279       }
00280       err = newDoc->getErrorCode();
00281       delete newDoc;
00282       if (err != errEncrypted) {
00283     setCursor(None);
00284     return err;
00285       }
00286       again = gTrue;
00287     }
00288   }
00289 
00290   // replace old document
00291   if (doc) {
00292     delete doc;
00293   }
00294   doc = newDoc;
00295   if (out) {
00296     out->startDoc(doc->getXRef());
00297   }
00298 
00299   // nothing displayed yet
00300   page = -99;
00301 
00302   // save the modification time
00303   modTime = getModTime(doc->getFileName()->getCString());
00304 
00305   // update the parent window
00306   if (updateCbk) {
00307     (*updateCbk)(updateCbkData, doc->getFileName(), -1,
00308          doc->getNumPages(), NULL);
00309   }
00310 
00311   // back to regular cursor
00312   setCursor(None);
00313 
00314   return errNone;
00315 }
00316 
00317 void XPDFCore::resizeToPage(int pg) {
00318   Dimension width, height;
00319   double width1, height1;
00320   Dimension topW, topH, topBorder, daW, daH;
00321   Dimension displayW, displayH;
00322 
00323   displayW = DisplayWidth(display, screenNum);
00324   displayH = DisplayHeight(display, screenNum);
00325   if (fullScreen) {
00326     width = displayW;
00327     height = displayH;
00328   } else {
00329     if (pg < 0 || pg > doc->getNumPages()) {
00330       width1 = 612;
00331       height1 = 792;
00332     } else if (doc->getPageRotate(pg) == 90 ||
00333            doc->getPageRotate(pg) == 270) {
00334       width1 = doc->getPageHeight(pg);
00335       height1 = doc->getPageWidth(pg);
00336     } else {
00337       width1 = doc->getPageWidth(pg);
00338       height1 = doc->getPageHeight(pg);
00339     }
00340     if (zoom == zoomPage || zoom == zoomWidth) {
00341       width = (Dimension)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
00342       height = (Dimension)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
00343     } else {
00344       width = (Dimension)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
00345       height = (Dimension)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
00346     }
00347     if (width > displayW - 100) {
00348       width = displayW - 100;
00349     }
00350     if (height > displayH - 150) {
00351       height = displayH - 150;
00352     }
00353   }
00354 
00355   if (XtIsRealized(shell)) {
00356     XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
00357           XmNborderWidth, &topBorder, NULL);
00358     XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
00359     XtVaSetValues(shell, XmNwidth, width + (topW - daW),
00360           XmNheight, height + (topH - daH), NULL);
00361   } else {
00362     XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
00363   }
00364 }
00365 
00366 void XPDFCore::clear() {
00367   if (!doc) {
00368     return;
00369   }
00370 
00371   // no document
00372   delete doc;
00373   doc = NULL;
00374   out->clear();
00375 
00376   // no page displayed
00377   page = -99;
00378 
00379   // redraw
00380   scrollX = scrollY = 0;
00381   updateScrollBars();
00382   redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
00383 }
00384 
00385 void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
00386                GBool scrollToTop, GBool addToHist) {
00387   double hDPI, vDPI;
00388   int rot;
00389   XPDFHistory *h;
00390   GBool newZoom;
00391   XGCValues gcValues;
00392   time_t newModTime;
00393   int oldScrollX, oldScrollY;
00394 
00395   // update the zoom and rotate values
00396   newZoom = zoomA != zoom;
00397   zoom = zoomA;
00398   rotate = rotateA;
00399 
00400   // check for document and valid page number
00401   if (!doc || pageA <= 0 || pageA > doc->getNumPages()) {
00402     return;
00403   }
00404 
00405   // busy cursor
00406   setCursor(busyCursor);
00407 
00408 
00409   // check for changes to the file
00410   newModTime = getModTime(doc->getFileName()->getCString());
00411   if (newModTime != modTime) {
00412     if (loadFile(doc->getFileName()) == errNone) {
00413       if (pageA > doc->getNumPages()) {
00414     pageA = doc->getNumPages();
00415       }
00416     }
00417     modTime = newModTime;
00418   }
00419 
00420   // free the old GCs
00421   if (selectGC) {
00422     XFreeGC(display, selectGC);
00423     XFreeGC(display, highlightGC);
00424   }
00425 
00426   // new page number
00427   page = pageA;
00428 
00429   // scroll to top
00430   if (scrollToTop) {
00431     scrollY = 0;
00432   }
00433 
00434   // if zoom level changed, scroll to the top-left corner
00435   if (newZoom) {
00436     scrollX = scrollY = 0;
00437   }
00438 
00439   // initialize mouse-related stuff
00440   linkAction = NULL;
00441   selectXMin = selectXMax = 0;
00442   selectYMin = selectYMax = 0;
00443   dragging = gFalse;
00444   lastDragLeft = lastDragTop = gTrue;
00445 
00446   // draw the page
00447   rot = rotate + doc->getPageRotate(page);
00448   if (rot >= 360) {
00449     rot -= 360;
00450   } else if (rotate < 0) {
00451     rot += 360;
00452   }
00453   if (zoom == zoomPage) {
00454     if (rot == 90 || rot == 270) {
00455       hDPI = (drawAreaWidth / doc->getPageHeight(page)) * 72;
00456       vDPI = (drawAreaHeight / doc->getPageWidth(page)) * 72;
00457     } else {
00458       hDPI = (drawAreaWidth / doc->getPageWidth(page)) * 72;
00459       vDPI = (drawAreaHeight / doc->getPageHeight(page)) * 72;
00460     }
00461     dpi = (hDPI < vDPI) ? hDPI : vDPI;
00462   } else if (zoom == zoomWidth) {
00463     if (rot == 90 || rot == 270) {
00464       dpi = (drawAreaWidth / doc->getPageHeight(page)) * 72;
00465     } else {
00466       dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
00467     }
00468   } else {
00469     dpi = zoomDPI[zoom - minZoom];
00470   }
00471   out->setWindow(XtWindow(drawArea));
00472   doc->displayPage(out, page, dpi, rotate, gTrue);
00473   oldScrollX = scrollX;
00474   oldScrollY = scrollY;
00475   updateScrollBars();
00476   if (scrollX != oldScrollX || scrollY != oldScrollY) {
00477     redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
00478   }
00479 
00480 
00481   // add to history
00482   if (addToHist) {
00483     if (++historyCur == xpdfHistorySize) {
00484       historyCur = 0;
00485     }
00486     h = &history[historyCur];
00487     if (h->fileName) {
00488       delete h->fileName;
00489     }
00490     h->fileName = doc->getFileName()->copy();
00491     h->page = page;
00492     if (historyBLen < xpdfHistorySize) {
00493       ++historyBLen;
00494     }
00495     historyFLen = 0;
00496   }
00497 
00498   // update the parent window
00499   if (updateCbk) {
00500     (*updateCbk)(updateCbkData, NULL, page, -1, "");
00501   }
00502 
00503   // allocate new GCs
00504   gcValues.foreground = BlackPixel(display, screenNum) ^
00505                         WhitePixel(display, screenNum);
00506   gcValues.function = GXxor;
00507   selectGC = XCreateGC(display, out->getPixmap(),
00508                GCForeground | GCFunction, &gcValues);
00509   highlightGC = XCreateGC(display, out->getPixmap(),
00510                GCForeground | GCFunction, &gcValues);
00511 
00512   // back to regular cursor
00513   setCursor(None);
00514 }
00515 
00516 void XPDFCore::displayDest(LinkDest *dest, int zoomA, int rotateA,
00517                GBool addToHist) {
00518   Ref pageRef;
00519   int pg;
00520   int dx, dy;
00521 
00522   if (dest->isPageRef()) {
00523     pageRef = dest->getPageRef();
00524     pg = doc->findPage(pageRef.num, pageRef.gen);
00525   } else {
00526     pg = dest->getPageNum();
00527   }
00528   if (pg <= 0 || pg > doc->getNumPages()) {
00529     pg = 1;
00530   }
00531   if (pg != page) {
00532     displayPage(pg, zoomA, rotateA, gTrue, addToHist);
00533   }
00534 
00535   if (fullScreen) {
00536     return;
00537   }
00538   switch (dest->getKind()) {
00539   case destXYZ:
00540     out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
00541     if (dest->getChangeLeft() || dest->getChangeTop()) {
00542       scrollTo(dest->getChangeLeft() ? dx : scrollX,
00543            dest->getChangeTop() ? dy : scrollY);
00544     }
00545     //~ what is the zoom parameter?
00546     break;
00547   case destFit:
00548   case destFitB:
00549     //~ do fit
00550     scrollTo(0, 0);
00551     break;
00552   case destFitH:
00553   case destFitBH:
00554     //~ do fit
00555     out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
00556     scrollTo(0, dy);
00557     break;
00558   case destFitV:
00559   case destFitBV:
00560     //~ do fit
00561     out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
00562     scrollTo(dx, 0);
00563     break;
00564   case destFitR:
00565     //~ do fit
00566     out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
00567     scrollTo(dx, dy);
00568     break;
00569   }
00570 }
00571 
00572 //------------------------------------------------------------------------
00573 // page/position changes
00574 //------------------------------------------------------------------------
00575 
00576 void XPDFCore::gotoNextPage(int inc, GBool top) {
00577   int pg;
00578 
00579   if (!doc || doc->getNumPages() == 0) {
00580     return;
00581   }
00582   if (page < doc->getNumPages()) {
00583     if ((pg = page + inc) > doc->getNumPages()) {
00584       pg = doc->getNumPages();
00585     }
00586     displayPage(pg, zoom, rotate, top, gTrue);
00587   } else {
00588     XBell(display, 0);
00589   }
00590 }
00591 
00592 void XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
00593   int pg;
00594 
00595   if (!doc || doc->getNumPages() == 0) {
00596     return;
00597   }
00598   if (page > 1) {
00599     if (!fullScreen && bottom) {
00600       scrollY = out->getPixmapHeight() - drawAreaHeight;
00601       if (scrollY < 0) {
00602     scrollY = 0;
00603       }
00604       // displayPage will call updateScrollBars()
00605     }
00606     if ((pg = page - dec) < 1) {
00607       pg = 1;
00608     }
00609     displayPage(pg, zoom, rotate, top, gTrue);
00610   } else {
00611     XBell(display, 0);
00612   }
00613 }
00614 
00615 void XPDFCore::goForward() {
00616   if (historyFLen == 0) {
00617     XBell(display, 0);
00618     return;
00619   }
00620   if (++historyCur == xpdfHistorySize) {
00621     historyCur = 0;
00622   }
00623   --historyFLen;
00624   ++historyBLen;
00625   if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
00626     if (loadFile(history[historyCur].fileName) != errNone) {
00627       XBell(display, 0);
00628       return;
00629     }
00630   }
00631   displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
00632 }
00633 
00634 void XPDFCore::goBackward() {
00635   if (historyBLen <= 1) {
00636     XBell(display, 0);
00637     return;
00638   }
00639   if (--historyCur < 0) {
00640     historyCur = xpdfHistorySize - 1;
00641   }
00642   --historyBLen;
00643   ++historyFLen;
00644   if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
00645     if (loadFile(history[historyCur].fileName) != errNone) {
00646       XBell(display, 0);
00647       return;
00648     }
00649   }
00650   displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
00651 }
00652 
00653 void XPDFCore::scrollLeft(int nCols) {
00654   scrollTo(scrollX - nCols * 16, scrollY);
00655 }
00656 
00657 void XPDFCore::scrollRight(int nCols) {
00658   scrollTo(scrollX + nCols * 16, scrollY);
00659 }
00660 
00661 void XPDFCore::scrollUp(int nLines) {
00662   scrollTo(scrollX, scrollY - nLines * 16);
00663 }
00664 
00665 void XPDFCore::scrollDown(int nLines) {
00666   scrollTo(scrollX, scrollY + nLines * 16);
00667 }
00668 
00669 void XPDFCore::scrollPageUp() {
00670   if (scrollY == 0) {
00671     gotoPrevPage(1, gFalse, gTrue);
00672   } else {
00673     scrollTo(scrollX, scrollY - drawAreaHeight);
00674   }
00675 }
00676 
00677 void XPDFCore::scrollPageDown() {
00678   if (scrollY >= out->getPixmapHeight() - drawAreaHeight) {
00679     gotoNextPage(1, gTrue);
00680   } else {
00681     scrollTo(scrollX, scrollY + drawAreaHeight);
00682   }
00683 }
00684 
00685 void XPDFCore::scrollTo(int x, int y) {
00686   GBool needRedraw;
00687   int maxPos, pos;
00688 
00689   needRedraw = gFalse;
00690 
00691   maxPos = out ? out->getPixmapWidth() : 1;
00692   if (maxPos < drawAreaWidth) {
00693     maxPos = drawAreaWidth;
00694   }
00695   if (x < 0) {
00696     pos = 0;
00697   } else if (x > maxPos - drawAreaWidth) {
00698     pos = maxPos - drawAreaWidth;
00699   } else {
00700     pos = x;
00701   }
00702   if (scrollX != pos) {
00703     scrollX = pos;
00704     XmScrollBarSetValues(hScrollBar, scrollX, drawAreaWidth, 16,
00705              drawAreaWidth, False);
00706     needRedraw = gTrue;
00707   }
00708 
00709   maxPos = out ? out->getPixmapHeight() : 1;
00710   if (maxPos < drawAreaHeight) {
00711     maxPos = drawAreaHeight;
00712   }
00713   if (y < 0) {
00714     pos = 0;
00715   } else if (y > maxPos - drawAreaHeight) {
00716     pos = maxPos - drawAreaHeight;
00717   } else {
00718     pos = y;
00719   }
00720   if (scrollY != pos) {
00721     scrollY = pos;
00722     XmScrollBarSetValues(vScrollBar, scrollY, drawAreaHeight, 16,
00723              drawAreaHeight, False);
00724     needRedraw = gTrue;
00725   }
00726 
00727   if (needRedraw) {
00728     redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
00729   }
00730 }
00731 
00732 //------------------------------------------------------------------------
00733 // selection
00734 //------------------------------------------------------------------------
00735 
00736 void XPDFCore::setSelection(int newXMin, int newYMin,
00737                 int newXMax, int newYMax) {
00738   Pixmap pixmap;
00739   int x, y;
00740   GBool needRedraw, needScroll;
00741   GBool moveLeft, moveRight, moveTop, moveBottom;
00742 
00743   pixmap = out->getPixmap();
00744 
00745 
00746   // erase old selection on off-screen bitmap
00747   needRedraw = gFalse;
00748   if (selectXMin < selectXMax && selectYMin < selectYMax) {
00749     XFillRectangle(display, pixmap,
00750            selectGC, selectXMin, selectYMin,
00751            selectXMax - selectXMin, selectYMax - selectYMin);
00752     needRedraw = gTrue;
00753   }
00754 
00755   // draw new selection on off-screen bitmap
00756   if (newXMin < newXMax && newYMin < newYMax) {
00757     XFillRectangle(display, pixmap,
00758            selectGC, newXMin, newYMin,
00759            newXMax - newXMin, newYMax - newYMin);
00760     needRedraw = gTrue;
00761   }
00762 
00763   // check which edges moved
00764   moveLeft = newXMin != selectXMin;
00765   moveTop = newYMin != selectYMin;
00766   moveRight = newXMax != selectXMax;
00767   moveBottom = newYMax != selectYMax;
00768 
00769   // redraw currently visible part of bitmap
00770   if (needRedraw) {
00771     if (moveLeft) {
00772       redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
00773               (newYMin < selectYMin) ? newYMin : selectYMin,
00774               (newXMin > selectXMin) ? newXMin : selectXMin,
00775               (newYMax > selectYMax) ? newYMax : selectYMax);
00776     }
00777     if (moveRight) {
00778       redrawRectangle((newXMax < selectXMax) ? newXMax : selectXMax,
00779               (newYMin < selectYMin) ? newYMin : selectYMin,
00780               (newXMax > selectXMax) ? newXMax : selectXMax,
00781               (newYMax > selectYMax) ? newYMax : selectYMax);
00782     }
00783     if (moveTop) {
00784       redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
00785               (newYMin < selectYMin) ? newYMin : selectYMin,
00786               (newXMax > selectXMax) ? newXMax : selectXMax,
00787               (newYMin > selectYMin) ? newYMin : selectYMin);
00788     }
00789     if (moveBottom) {
00790       redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
00791               (newYMax < selectYMax) ? newYMax : selectYMax,
00792               (newXMax > selectXMax) ? newXMax : selectXMax,
00793               (newYMax > selectYMax) ? newYMax : selectYMax);
00794     }
00795   }
00796 
00797   // switch to new selection coords
00798   selectXMin = newXMin;
00799   selectXMax = newXMax;
00800   selectYMin = newYMin;
00801   selectYMax = newYMax;
00802 
00803   // scroll if necessary
00804   if (fullScreen) {
00805     return;
00806   }
00807   needScroll = gFalse;
00808   x = scrollX;
00809   y = scrollY;
00810   if (moveLeft && selectXMin < x) {
00811     x = selectXMin;
00812     needScroll = gTrue;
00813   } else if (moveRight && selectXMax >= x + drawAreaWidth) {
00814     x = selectXMax - drawAreaWidth;
00815     needScroll = gTrue;
00816   } else if (moveLeft && selectXMin >= x + drawAreaWidth) {
00817     x = selectXMin - drawAreaWidth;
00818     needScroll = gTrue;
00819   } else if (moveRight && selectXMax < x) {
00820     x = selectXMax;
00821     needScroll = gTrue;
00822   }
00823   if (moveTop && selectYMin < y) {
00824     y = selectYMin;
00825     needScroll = gTrue;
00826   } else if (moveBottom && selectYMax >= y + drawAreaHeight) {
00827     y = selectYMax - drawAreaHeight;
00828     needScroll = gTrue;
00829   } else if (moveTop && selectYMin >= y + drawAreaHeight) {
00830     y = selectYMin - drawAreaHeight;
00831     needScroll = gTrue;
00832   } else if (moveBottom && selectYMax < y) {
00833     y = selectYMax;
00834     needScroll = gTrue;
00835   }
00836   if (needScroll) {
00837     scrollTo(x, y);
00838   }
00839 }
00840 
00841 void XPDFCore::moveSelection(int mx, int my) {
00842   int xMin, yMin, xMax, yMax;
00843 
00844   // clip mouse coords
00845   if (mx < 0) {
00846     mx = 0;
00847   } else if (mx >= out->getPixmapWidth()) {
00848     mx = out->getPixmapWidth() - 1;
00849   }
00850   if (my < 0) {
00851     my = 0;
00852   } else if (my >= out->getPixmapHeight()) {
00853     my = out->getPixmapHeight() - 1;
00854   }
00855 
00856   // move appropriate edges of selection
00857   if (lastDragLeft) {
00858     if (mx < selectXMax) {
00859       xMin = mx;
00860       xMax = selectXMax;
00861     } else {
00862       xMin = selectXMax;
00863       xMax = mx;
00864       lastDragLeft = gFalse;
00865     }
00866   } else {
00867     if (mx > selectXMin) {
00868       xMin = selectXMin;
00869       xMax = mx;
00870     } else {
00871       xMin = mx;
00872       xMax = selectXMin;
00873       lastDragLeft = gTrue;
00874     }
00875   }
00876   if (lastDragTop) {
00877     if (my < selectYMax) {
00878       yMin = my;
00879       yMax = selectYMax;
00880     } else {
00881       yMin = selectYMax;
00882       yMax = my;
00883       lastDragTop = gFalse;
00884     }
00885   } else {
00886     if (my > selectYMin) {
00887       yMin = selectYMin;
00888       yMax = my;
00889     } else {
00890       yMin = my;
00891       yMax = selectYMin;
00892       lastDragTop = gTrue;
00893     }
00894   }
00895 
00896   // redraw the selection
00897   setSelection(xMin, yMin, xMax, yMax);
00898 }
00899 
00900 // X's copy-and-paste mechanism is brain damaged.  Xt doesn't help
00901 // any, but doesn't make it too much worse, either.  Motif, on the
00902 // other hand, adds significant complexity to the mess.  So here we
00903 // blow off the Motif junk and stick to plain old Xt.  The next two
00904 // functions (copySelection and convertSelectionCbk) implement the
00905 // magic needed to deal with Xt's mechanism.  Note that this requires
00906 // global variables (currentSelection and currentSelectionOwner).
00907 
00908 void XPDFCore::copySelection() {
00909   if (!doc->okToCopy()) {
00910     return;
00911   }
00912   if (currentSelection) {
00913     delete currentSelection;
00914   }
00915   //~ for multithreading: need a mutex here
00916   currentSelection = out->getText(selectXMin, selectYMin,
00917                   selectXMax, selectYMax);
00918   currentSelectionOwner = this;
00919   XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
00920          &convertSelectionCbk, NULL, NULL);
00921 }
00922 
00923 Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
00924                       Atom *target, Atom *type,
00925                       XtPointer *value, unsigned long *length,
00926                       int *format) {
00927   if (*target != XA_STRING) {
00928     return False;
00929   }
00930   //~ for multithreading: need a mutex here
00931   *value = XtNewString(currentSelection->getCString());
00932   *length = currentSelection->getLength();
00933   *type = XA_STRING;
00934   *format = 8; // 8-bit elements
00935   return True;
00936 }
00937 
00938 GBool XPDFCore::getSelection(int *xMin, int *yMin, int *xMax, int *yMax) {
00939   if (selectXMin >= selectXMax || selectYMin >= selectYMax) {
00940     return gFalse;
00941   }
00942   *xMin = selectXMin;
00943   *yMin = selectYMin;
00944   *xMax = selectXMax;
00945   *yMax = selectYMax;
00946   return gTrue;
00947 }
00948 
00949 GString *XPDFCore::extractText(int xMin, int yMin, int xMax, int yMax) {
00950   if (!doc->okToCopy()) {
00951     return NULL;
00952   }
00953   return out->getText(xMin, yMin, xMax, yMax);
00954 }
00955 
00956 GString *XPDFCore::extractText(int pageNum,
00957                    int xMin, int yMin, int xMax, int yMax) {
00958   TextOutputDev *textOut;
00959   GString *s;
00960 
00961   if (!doc->okToCopy()) {
00962     return NULL;
00963   }
00964   textOut = new TextOutputDev(NULL, gFalse, gFalse);
00965   if (!textOut->isOk()) {
00966     delete textOut;
00967     return NULL;
00968   }
00969   doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
00970   s = textOut->getText(xMin, yMin, xMax, yMax);
00971   delete textOut;
00972   return s;
00973 }
00974 
00975 //------------------------------------------------------------------------
00976 // hyperlinks
00977 //------------------------------------------------------------------------
00978 
00979 void XPDFCore::doLink(int mx, int my) {
00980   double x, y;
00981   LinkAction *action;
00982 
00983   // look for a link
00984   out->cvtDevToUser(mx, my, &x, &y);
00985   if ((action = doc->findLink(x, y))) {
00986     doAction(action);
00987   }
00988 }
00989 
00990 void XPDFCore::doAction(LinkAction *action) {
00991   LinkActionKind kind;
00992   LinkDest *dest;
00993   GString *namedDest;
00994   char *s;
00995   GString *fileName, *fileName2;
00996   GString *cmd;
00997   GString *actionName;
00998   Object movieAnnot, obj1, obj2;
00999   GString *msg;
01000   int i;
01001 
01002   switch (kind = action->getKind()) {
01003 
01004   // GoTo / GoToR action
01005   case actionGoTo:
01006   case actionGoToR:
01007     if (kind == actionGoTo) {
01008       dest = NULL;
01009       namedDest = NULL;
01010       if ((dest = ((LinkGoTo *)action)->getDest())) {
01011     dest = dest->copy();
01012       } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
01013     namedDest = namedDest->copy();
01014       }
01015     } else {
01016       dest = NULL;
01017       namedDest = NULL;
01018       if ((dest = ((LinkGoToR *)action)->getDest())) {
01019     dest = dest->copy();
01020       } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
01021     namedDest = namedDest->copy();
01022       }
01023       s = ((LinkGoToR *)action)->getFileName()->getCString();
01024       //~ translate path name for VMS (deal with '/')
01025       if (isAbsolutePath(s)) {
01026     fileName = new GString(s);
01027       } else {
01028     fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
01029       }
01030       if (loadFile(fileName) != errNone) {
01031     if (dest) {
01032       delete dest;
01033     }
01034     if (namedDest) {
01035       delete namedDest;
01036     }
01037     delete fileName;
01038     return;
01039       }
01040       delete fileName;
01041     }
01042     if (namedDest) {
01043       dest = doc->findDest(namedDest);
01044       delete namedDest;
01045     }
01046     if (dest) {
01047       displayDest(dest, zoom, rotate, gTrue);
01048       delete dest;
01049     } else {
01050       if (kind == actionGoToR) {
01051     displayPage(1, zoom, 0, gFalse, gTrue);
01052       }
01053     }
01054     break;
01055 
01056   // Launch action
01057   case actionLaunch:
01058     fileName = ((LinkLaunch *)action)->getFileName();
01059     s = fileName->getCString();
01060     if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
01061     !strcmp(s + fileName->getLength() - 4, ".PDF")) {
01062       //~ translate path name for VMS (deal with '/')
01063       if (isAbsolutePath(s)) {
01064     fileName = fileName->copy();
01065       } else {
01066     fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
01067       }
01068       if (loadFile(fileName) != errNone) {
01069     delete fileName;
01070     return;
01071       }
01072       delete fileName;
01073       displayPage(1, zoom, rotate, gFalse, gTrue);
01074     } else {
01075       fileName = fileName->copy();
01076       if (((LinkLaunch *)action)->getParams()) {
01077     fileName->append(' ');
01078     fileName->append(((LinkLaunch *)action)->getParams());
01079       }
01080 #ifdef VMS
01081       fileName->insert(0, "spawn/nowait ");
01082 #elif defined(__EMX__)
01083       fileName->insert(0, "start /min /n ");
01084 #else
01085       fileName->append(" &");
01086 #endif
01087       msg = new GString("About to execute the command:\n");
01088       msg->append(fileName);
01089       if (doQuestionDialog("Launching external application", msg)) {
01090     system(fileName->getCString());
01091       }
01092       delete fileName;
01093       delete msg;
01094     }
01095     break;
01096 
01097   // URI action
01098   case actionURI:
01099     if (!(cmd = globalParams->getURLCommand())) {
01100       error(-1, "No urlCommand defined in config file");
01101       break;
01102     }
01103     runCommand(cmd, ((LinkURI *)action)->getURI());
01104     break;
01105 
01106   // Named action
01107   case actionNamed:
01108     actionName = ((LinkNamed *)action)->getName();
01109     if (!actionName->cmp("NextPage")) {
01110       gotoNextPage(1, gTrue);
01111     } else if (!actionName->cmp("PrevPage")) {
01112       gotoPrevPage(1, gTrue, gFalse);
01113     } else if (!actionName->cmp("FirstPage")) {
01114       if (page != 1) {
01115     displayPage(1, zoom, rotate, gTrue, gTrue);
01116       }
01117     } else if (!actionName->cmp("LastPage")) {
01118       if (page != doc->getNumPages()) {
01119     displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
01120       }
01121     } else if (!actionName->cmp("GoBack")) {
01122       goBackward();
01123     } else if (!actionName->cmp("GoForward")) {
01124       goForward();
01125     } else if (!actionName->cmp("Quit")) {
01126       if (actionCbk) {
01127     (*actionCbk)(actionCbkData, "Quit");
01128       }
01129     } else {
01130       error(-1, "Unknown named action: '%s'", actionName->getCString());
01131     }
01132     break;
01133 
01134   // Movie action
01135   case actionMovie:
01136     if (!(cmd = globalParams->getMovieCommand())) {
01137       error(-1, "No movieCommand defined in config file");
01138       break;
01139     }
01140     if (((LinkMovie *)action)->hasAnnotRef()) {
01141       doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
01142                 ((LinkMovie *)action)->getAnnotRef()->gen,
01143                 &movieAnnot);
01144     } else {
01145       doc->getCatalog()->getPage(page)->getAnnots(&obj1);
01146       if (obj1.isArray()) {
01147     for (i = 0; i < obj1.arrayGetLength(); ++i) {
01148       if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
01149         if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
01150           obj2.free();
01151           break;
01152         }
01153         obj2.free();
01154       }
01155       movieAnnot.free();
01156     }
01157     obj1.free();
01158       }
01159     }
01160     if (movieAnnot.isDict()) {
01161       if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
01162     if (obj1.dictLookup("F", &obj2)) {
01163       if ((fileName = LinkAction::getFileSpecName(&obj2))) {
01164         if (!isAbsolutePath(fileName->getCString())) {
01165           fileName2 = appendToPath(
01166                   grabPath(doc->getFileName()->getCString()),
01167                   fileName->getCString());
01168           delete fileName;
01169           fileName = fileName2;
01170         }
01171         runCommand(cmd, fileName);
01172         delete fileName;
01173       }
01174       obj2.free();
01175     }
01176     obj1.free();
01177       }
01178     }
01179     movieAnnot.free();
01180     break;
01181 
01182   // unknown action type
01183   case actionUnknown:
01184     error(-1, "Unknown link action type: '%s'",
01185       ((LinkUnknown *)action)->getAction()->getCString());
01186     break;
01187   }
01188 }
01189 
01190 // Run a command, given a <cmdFmt> string with one '%s' in it, and an
01191 // <arg> string to insert in place of the '%s'.
01192 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
01193   GString *cmd;
01194   char *s;
01195   int i;
01196 
01197   if ((s = strstr(cmdFmt->getCString(), "%s"))) {
01198     cmd = arg->copy();
01199     // filter out any quote marks (' or ") to avoid a potential
01200     // security hole
01201     i = 0;
01202     while (i < cmd->getLength()) {
01203       if (cmd->getChar(i) == '"') {
01204     cmd->del(i);
01205     cmd->insert(i, "%22");
01206     i += 3;
01207       } else if (cmd->getChar(i) == '\'') {
01208     cmd->del(i);
01209     cmd->insert(i, "%27");
01210     i += 3;
01211       } else {
01212     ++i;
01213       }
01214     }
01215     cmd->insert(0, cmdFmt->getCString(),
01216         s - cmdFmt->getCString());
01217     cmd->append(s + 2);
01218   } else {
01219     cmd = cmdFmt->copy();
01220   }
01221 #ifdef VMS
01222   cmd->insert(0, "spawn/nowait ");
01223 #elif defined(__EMX__)
01224   cmd->insert(0, "start /min /n ");
01225 #else
01226   cmd->append(" &");
01227 #endif
01228   system(cmd->getCString());
01229   delete cmd;
01230 }
01231 
01232 
01233 //------------------------------------------------------------------------
01234 // find
01235 //------------------------------------------------------------------------
01236 
01237 void XPDFCore::find(char *s) {
01238   Unicode *u;
01239   TextOutputDev *textOut;
01240   int xMin, yMin, xMax, yMax;
01241   double xMin1, yMin1, xMax1, yMax1;
01242   int pg;
01243   GBool top;
01244   int len, i;
01245 
01246   // check for zero-length string
01247   if (!s[0]) {
01248     XBell(display, 0);
01249     return;
01250   }
01251 
01252   // set cursor to watch
01253   setCursor(busyCursor);
01254 
01255   // convert to Unicode
01256 #if 1 //~ should do something more intelligent here
01257   len = strlen(s);
01258   u = (Unicode *)gmalloc(len * sizeof(Unicode));
01259   for (i = 0; i < len; ++i) {
01260     u[i] = (Unicode)(s[i] & 0xff);
01261   }
01262 #endif
01263 
01264   // search current page starting at current selection or top of page
01265   xMin = yMin = xMax = yMax = 0;
01266   if (selectXMin < selectXMax && selectYMin < selectYMax) {
01267     xMin = selectXMax;
01268     yMin = (selectYMin + selectYMax) / 2;
01269     top = gFalse;
01270   } else {
01271     top = gTrue;
01272   }
01273   if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
01274     goto found;
01275   }
01276 
01277   // search following pages
01278   textOut = new TextOutputDev(NULL, gFalse, gFalse);
01279   if (!textOut->isOk()) {
01280     delete textOut;
01281     goto done;
01282   }
01283   for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
01284     doc->displayPage(textOut, pg, 72, 0, gFalse);
01285     if (textOut->findText(u, len, gTrue, gTrue,
01286               &xMin1, &yMin1, &xMax1, &yMax1)) {
01287       goto foundPage;
01288     }
01289   }
01290 
01291   // search previous pages
01292   for (pg = 1; pg < page; ++pg) {
01293     doc->displayPage(textOut, pg, 72, 0, gFalse);
01294     if (textOut->findText(u, len, gTrue, gTrue,
01295               &xMin1, &yMin1, &xMax1, &yMax1)) {
01296       goto foundPage;
01297     }
01298   }
01299   delete textOut;
01300 
01301   // search current page ending at current selection
01302   if (selectXMin < selectXMax && selectYMin < selectYMax) {
01303     xMax = selectXMin;
01304     yMax = (selectYMin + selectYMax) / 2;
01305     if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
01306       goto found;
01307     }
01308   }
01309 
01310   // not found
01311   XBell(display, 0);
01312   goto done;
01313 
01314   // found on a different page
01315  foundPage:
01316   delete textOut;
01317   displayPage(pg, zoom, rotate, gTrue, gTrue);
01318   if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
01319     // this can happen if coalescing is bad
01320     goto done;
01321   }
01322 
01323   // found: change the selection
01324  found:
01325   setSelection(xMin, yMin, xMax, yMax);
01326 #ifndef NO_TEXT_SELECT
01327   copySelection();
01328 #endif
01329 
01330  done:
01331   gfree(u);
01332 
01333   // reset cursors to normal
01334   setCursor(None);
01335 }
01336 
01337 //------------------------------------------------------------------------
01338 // misc access
01339 //------------------------------------------------------------------------
01340 
01341 void XPDFCore::setBusyCursor(GBool busy) {
01342   setCursor(busy ? busyCursor : None);
01343 }
01344 
01345 void XPDFCore::takeFocus() {
01346   XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
01347 }
01348 
01349 //------------------------------------------------------------------------
01350 // GUI code
01351 //------------------------------------------------------------------------
01352 
01353 void XPDFCore::initWindow() {
01354   Arg args[20];
01355   int n;
01356 
01357   // create the cursors
01358   busyCursor = XCreateFontCursor(display, XC_watch);
01359   linkCursor = XCreateFontCursor(display, XC_hand2);
01360   selectCursor = XCreateFontCursor(display, XC_cross);
01361   currentCursor = 0;
01362 
01363   // create the scrolled window and scrollbars
01364   n = 0;
01365   XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
01366   XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
01367   scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
01368   XtManageChild(scrolledWin);
01369   n = 0;
01370   XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
01371   XtSetArg(args[n], XmNminimum, 0); ++n;
01372   XtSetArg(args[n], XmNmaximum, 1); ++n;
01373   XtSetArg(args[n], XmNsliderSize, 1); ++n;
01374   XtSetArg(args[n], XmNvalue, 0); ++n;
01375   XtSetArg(args[n], XmNincrement, 1); ++n;
01376   XtSetArg(args[n], XmNpageIncrement, 1); ++n;
01377   hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
01378   XtManageChild(hScrollBar);
01379   XtAddCallback(hScrollBar, XmNvalueChangedCallback,
01380         &hScrollChangeCbk, (XtPointer)this);
01381 #ifndef DISABLE_SMOOTH_SCROLL
01382   XtAddCallback(hScrollBar, XmNdragCallback,
01383         &hScrollDragCbk, (XtPointer)this);
01384 #endif
01385   n = 0;
01386   XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
01387   XtSetArg(args[n], XmNminimum, 0); ++n;
01388   XtSetArg(args[n], XmNmaximum, 1); ++n;
01389   XtSetArg(args[n], XmNsliderSize, 1); ++n;
01390   XtSetArg(args[n], XmNvalue, 0); ++n;
01391   XtSetArg(args[n], XmNincrement, 1); ++n;
01392   XtSetArg(args[n], XmNpageIncrement, 1); ++n;
01393   vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
01394   XtManageChild(vScrollBar);
01395   XtAddCallback(vScrollBar, XmNvalueChangedCallback,
01396         &vScrollChangeCbk, (XtPointer)this);
01397 #ifndef DISABLE_SMOOTH_SCROLL
01398   XtAddCallback(vScrollBar, XmNdragCallback,
01399         &vScrollDragCbk, (XtPointer)this);
01400 #endif
01401 
01402   // create the drawing area
01403   n = 0;
01404   XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
01405   XtSetArg(args[n], XmNmarginWidth, 0); ++n;
01406   XtSetArg(args[n], XmNmarginHeight, 0); ++n;
01407   if (fullScreen) {
01408     XtSetArg(args[n], XmNshadowThickness, 0); ++n;
01409   }
01410   drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
01411   XtManageChild(drawAreaFrame);
01412   n = 0;
01413   XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
01414   XtSetArg(args[n], XmNbackground, paperColor); ++n;
01415   XtSetArg(args[n], XmNwidth, 700); ++n;
01416   XtSetArg(args[n], XmNheight, 500); ++n;
01417   drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
01418   XtManageChild(drawArea);
01419   XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
01420   XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
01421   XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
01422   resizeCbk(drawArea, this, NULL);
01423 
01424   // set up mouse motion translations
01425   XtOverrideTranslations(drawArea, XtParseTranslationTable(
01426       "<Btn1Down>:DrawingAreaInput()\n"
01427       "<Btn1Up>:DrawingAreaInput()\n"
01428       "<Btn1Motion>:DrawingAreaInput()\n"
01429       "<Motion>:DrawingAreaInput()"));
01430 
01431   // can't create a GC until the window gets mapped
01432   drawAreaGC = NULL;
01433   selectGC = NULL;
01434   highlightGC = NULL;
01435 }
01436 
01437 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
01438                  XtPointer callData) {
01439   XPDFCore *core = (XPDFCore *)ptr;
01440   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
01441 
01442   core->scrollTo(data->value, core->scrollY);
01443 }
01444 
01445 void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
01446                   XtPointer callData) {
01447   XPDFCore *core = (XPDFCore *)ptr;
01448   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
01449 
01450   core->scrollTo(data->value, core->scrollY);
01451 }
01452 
01453 void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
01454                  XtPointer callData) {
01455   XPDFCore *core = (XPDFCore *)ptr;
01456   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
01457 
01458   core->scrollTo(core->scrollX, data->value);
01459 }
01460 
01461 void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
01462                   XtPointer callData) {
01463   XPDFCore *core = (XPDFCore *)ptr;
01464   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
01465 
01466   core->scrollTo(core->scrollX, data->value);
01467 }
01468 
01469 void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
01470   XPDFCore *core = (XPDFCore *)ptr;
01471   Arg args[2];
01472   int n;
01473   Dimension w, h;
01474 
01475   n = 0;
01476   XtSetArg(args[n], XmNwidth, &w); ++n;
01477   XtSetArg(args[n], XmNheight, &h); ++n;
01478   XtGetValues(core->drawArea, args, n);
01479   core->drawAreaWidth = (int)w;
01480   core->drawAreaHeight = (int)h;
01481   if (core->page >= 0 &&
01482       (core->zoom == zoomPage || core->zoom == zoomWidth)) {
01483     core->displayPage(core->page, core->zoom, core->rotate,
01484               gFalse, gFalse);
01485   } else {
01486     core->updateScrollBars();
01487   }
01488 }
01489 
01490 void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
01491   XPDFCore *core = (XPDFCore *)ptr;
01492   XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
01493   int x, y, w, h;
01494 
01495   if (data->reason == XmCR_EXPOSE) {
01496     x = core->scrollX + data->event->xexpose.x;
01497     y = core->scrollY + data->event->xexpose.y;
01498     w = data->event->xexpose.width;
01499     h = data->event->xexpose.height;
01500   } else {
01501     x = core->scrollX;
01502     y = core->scrollY;
01503     w = core->drawAreaWidth;
01504     h = core->drawAreaHeight;
01505   }
01506   core->redrawRectangle(x, y, w, h);
01507 }
01508 
01509 void XPDFCore::outputDevRedrawCbk(void *data) {
01510   XPDFCore *core = (XPDFCore *)data;
01511 
01512   core->redrawRectangle(core->scrollX, core->scrollY,
01513             core->drawAreaWidth, core->drawAreaHeight);
01514 }
01515 
01516 void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
01517   XPDFCore *core = (XPDFCore *)ptr;
01518   XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
01519   LinkAction *action;
01520   int mx, my;
01521   double x, y;
01522   char *s;
01523   KeySym key;
01524   char buf[20];
01525   int n;
01526 
01527   switch (data->event->type) {
01528   case ButtonPress:
01529     if (data->event->xbutton.button == 1) {
01530       core->takeFocus();
01531       if (core->doc && core->doc->getNumPages() > 0) {
01532     if (core->selectEnabled) {
01533       mx = core->scrollX + data->event->xbutton.x;
01534       my = core->scrollY + data->event->xbutton.y;
01535       core->setSelection(mx, my, mx, my);
01536       core->setCursor(core->selectCursor);
01537       core->dragging = gTrue;
01538     }
01539       }
01540     } else if (data->event->xbutton.button == 2) {
01541       if (!core->fullScreen) {
01542     core->panning = gTrue;
01543     core->panMX = data->event->xbutton.x;
01544     core->panMY = data->event->xbutton.y;
01545       }
01546     } else if (data->event->xbutton.button == 4) { // mouse wheel up
01547       if (core->fullScreen) {
01548     core->gotoPrevPage(1, gTrue, gFalse);
01549       } else if (core->scrollY == 0) {
01550     core->gotoPrevPage(1, gFalse, gTrue);
01551       } else {
01552     core->scrollUp(1);
01553       }
01554     } else if (data->event->xbutton.button == 5) { // mouse wheel down
01555       if (core->fullScreen ||
01556       core->scrollY >=
01557         core->out->getPixmapHeight() - core->drawAreaHeight) {
01558     core->gotoNextPage(1, gTrue);
01559       } else {
01560     core->scrollDown(1);
01561       }
01562     } else if (data->event->xbutton.button == 6) { // second mouse wheel right
01563       if (!core->fullScreen) {
01564     core->scrollRight(1);
01565       }
01566     } else if (data->event->xbutton.button == 7) { // second mouse wheel left
01567       if (!core->fullScreen) {
01568     core->scrollLeft(1);
01569       }
01570     } else {
01571       if (*core->mouseCbk) {
01572     (*core->mouseCbk)(core->mouseCbkData, data->event);
01573       }
01574     }
01575     break;
01576   case ButtonRelease:
01577     if (data->event->xbutton.button == 1) {
01578       if (core->doc && core->doc->getNumPages() > 0) {
01579     mx = core->scrollX + data->event->xbutton.x;
01580     my = core->scrollY + data->event->xbutton.y;
01581     if (core->dragging) {
01582       core->dragging = gFalse;
01583       core->setCursor(None);
01584       core->moveSelection(mx, my);
01585 #ifndef NO_TEXT_SELECT
01586       if (core->selectXMin != core->selectXMax &&
01587           core->selectYMin != core->selectYMax) {
01588         if (core->doc->okToCopy()) {
01589           core->copySelection();
01590         } else {
01591           error(-1, "Copying of text from this document is not allowed.");
01592         }
01593       }
01594 #endif
01595     }
01596     if (core->hyperlinksEnabled) {
01597       if (core->selectXMin == core->selectXMax ||
01598         core->selectYMin == core->selectYMax) {
01599         core->doLink(mx, my);
01600       }
01601     }
01602       }
01603     } else if (data->event->xbutton.button == 2) {
01604       core->panning = gFalse;
01605     } else {
01606       if (*core->mouseCbk) {
01607     (*core->mouseCbk)(core->mouseCbkData, data->event);
01608       }
01609     }
01610     break;
01611   case MotionNotify:
01612     if (core->doc && core->doc->getNumPages() > 0) {
01613       mx = core->scrollX + data->event->xbutton.x;
01614       my = core->scrollY + data->event->xbutton.y;
01615       if (core->dragging) {
01616     core->moveSelection(mx, my);
01617       } else if (core->hyperlinksEnabled) {
01618     core->out->cvtDevToUser(mx, my, &x, &y);
01619     if ((action = core->doc->findLink(x, y))) {
01620       core->setCursor(core->linkCursor);
01621       if (action != core->linkAction) {
01622         core->linkAction = action;
01623         if (core->updateCbk) {
01624           s = "";
01625           switch (action->getKind()) {
01626           case actionGoTo:
01627         s = "[internal link]";
01628         break;
01629           case actionGoToR:
01630         s = ((LinkGoToR *)action)->getFileName()->getCString();
01631         break;
01632           case actionLaunch:
01633         s = ((LinkLaunch *)action)->getFileName()->getCString();
01634         break;
01635           case actionURI:
01636         s = ((LinkURI *)action)->getURI()->getCString();
01637         break;
01638           case actionNamed:
01639         s = ((LinkNamed *)action)->getName()->getCString();
01640         break;
01641           case actionMovie:
01642         s = "[movie]";
01643         break;
01644           case actionUnknown:
01645         s = "[unknown link]";
01646         break;
01647           }
01648           (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
01649         }
01650       }
01651     } else {
01652       core->setCursor(None);
01653       if (core->linkAction) {
01654         core->linkAction = NULL;
01655         if (core->updateCbk) {
01656           (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
01657         }
01658       }
01659     }
01660       }
01661     }
01662     if (core->panning) {
01663       core->scrollTo(core->scrollX - (data->event->xbutton.x - core->panMX),
01664              core->scrollY - (data->event->xbutton.y - core->panMY));
01665       core->panMX = data->event->xbutton.x;
01666       core->panMY = data->event->xbutton.y;
01667     }
01668     break;
01669   case KeyPress:
01670     n = XLookupString(&data->event->xkey, buf, sizeof(buf) - 1,
01671               &key, NULL);
01672     core->keyPress(buf, key, data->event->xkey.state);
01673     break;
01674   }
01675 }
01676 
01677 void XPDFCore::keyPress(char *s, KeySym key, Guint modifiers) {
01678   switch (key) {
01679   case XK_Home:
01680   case XK_KP_Home:
01681     if (modifiers & ControlMask) {
01682       displayPage(1, zoom, rotate, gTrue, gTrue);
01683     } else if (!fullScreen) {
01684       scrollTo(0, 0);
01685     }
01686     return;
01687   case XK_End:
01688   case XK_KP_End:
01689     if (modifiers & ControlMask) {
01690       displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
01691     } else if (!fullScreen) {
01692       scrollTo(out->getPixmapWidth() - drawAreaWidth,
01693            out->getPixmapHeight() - drawAreaHeight);
01694     }
01695     return;
01696   case XK_Page_Up:
01697   case XK_KP_Page_Up:
01698     if (fullScreen) {
01699       gotoPrevPage(1, gTrue, gFalse);
01700     } else {
01701       scrollPageUp();
01702     }
01703     return;
01704   case XK_Page_Down:
01705   case XK_KP_Page_Down:
01706     if (fullScreen) {
01707       gotoNextPage(1, gTrue);
01708     } else {
01709       scrollPageDown();
01710     }
01711     return;
01712   case XK_Left:
01713   case XK_KP_Left:
01714     if (!fullScreen) {
01715       scrollLeft();
01716     }
01717     return;
01718   case XK_Right:
01719   case XK_KP_Right:
01720     if (!fullScreen) {
01721       scrollRight();
01722     }
01723     return;
01724   case XK_Up:
01725   case XK_KP_Up:
01726     if (!fullScreen) {
01727       scrollUp();
01728     }
01729     return;
01730   case XK_Down:
01731   case XK_KP_Down:
01732     if (!fullScreen) {
01733       scrollDown();
01734     }
01735     return;
01736   }
01737 
01738   if (*keyPressCbk) {
01739     (*keyPressCbk)(keyPressCbkData, s, key, modifiers);
01740   }
01741 }
01742 
01743 void XPDFCore::redrawRectangle(int x, int y, int w, int h) {
01744   XGCValues gcValues;
01745   Window drawAreaWin;
01746 
01747   // clip to window
01748   if (x < scrollX) {
01749     w -= scrollX - x;
01750     x = scrollX;
01751   }
01752   if (x + w > scrollX + drawAreaWidth) {
01753     w = scrollX + drawAreaWidth - x;
01754   }
01755   if (y < scrollY) {
01756     h -= scrollY - y;
01757     y = scrollY;
01758   }
01759   if (y + h > scrollY + drawAreaHeight) {
01760     h = scrollY + drawAreaHeight - y;
01761   }
01762 
01763   // create a GC for the drawing area
01764   drawAreaWin = XtWindow(drawArea);
01765   if (!drawAreaGC) {
01766     gcValues.foreground = paperColor;
01767     drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
01768   }
01769 
01770   // draw white background past the edges of the document
01771   if (x + w > out->getPixmapWidth()) {
01772     XFillRectangle(display, drawAreaWin, drawAreaGC,
01773            out->getPixmapWidth() - scrollX, y - scrollY,
01774            x + w - out->getPixmapWidth(), h);
01775     w = out->getPixmapWidth() - x;
01776   }
01777   if (y + h > out->getPixmapHeight()) {
01778     XFillRectangle(display, drawAreaWin, drawAreaGC,
01779            x - scrollX, out->getPixmapHeight() - scrollY,
01780            w, y + h - out->getPixmapHeight());
01781     h = out->getPixmapHeight() - y;
01782   }
01783 
01784   // redraw (checking to see if pixmap has been allocated yet)
01785   if (out->getPixmapWidth() > 0) {
01786     XCopyArea(display, out->getPixmap(), drawAreaWin, drawAreaGC,
01787           x, y, w, h, x - scrollX, y - scrollY);
01788   }
01789 }
01790 
01791 void XPDFCore::updateScrollBars() {
01792   Arg args[20];
01793   int n;
01794   int maxPos;
01795 
01796   maxPos = out ? out->getPixmapWidth() : 1;
01797   if (maxPos < drawAreaWidth) {
01798     maxPos = drawAreaWidth;
01799   }
01800   if (scrollX > maxPos - drawAreaWidth) {
01801     scrollX = maxPos - drawAreaWidth;
01802   }
01803   n = 0;
01804   XtSetArg(args[n], XmNvalue, scrollX); ++n;
01805   XtSetArg(args[n], XmNmaximum, maxPos); ++n;
01806   XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
01807   XtSetArg(args[n], XmNincrement, 16); ++n;
01808   XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
01809   XtSetValues(hScrollBar, args, n);
01810 
01811   maxPos = out ? out->getPixmapHeight() : 1;
01812   if (maxPos < drawAreaHeight) {
01813     maxPos = drawAreaHeight;
01814   }
01815   if (scrollY > maxPos - drawAreaHeight) {
01816     scrollY = maxPos - drawAreaHeight;
01817   }
01818   n = 0;
01819   XtSetArg(args[n], XmNvalue, scrollY); ++n;
01820   XtSetArg(args[n], XmNmaximum, maxPos); ++n;
01821   XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
01822   XtSetArg(args[n], XmNincrement, 16); ++n;
01823   XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
01824   XtSetValues(vScrollBar, args, n);
01825 }
01826 
01827 void XPDFCore::setCursor(Cursor cursor) {
01828   Window topWin;
01829 
01830   if (cursor == currentCursor) {
01831     return;
01832   }
01833   if (!(topWin = XtWindow(shell))) {
01834     return;
01835   }
01836   if (cursor == None) {
01837     XUndefineCursor(display, topWin);
01838   } else {
01839     XDefineCursor(display, topWin, cursor);
01840   }
01841   XFlush(display);
01842   currentCursor = cursor;
01843 }
01844 
01845 GBool XPDFCore::doQuestionDialog(char *title, GString *msg) {
01846   return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
01847 }
01848 
01849 void XPDFCore::doInfoDialog(char *title, GString *msg) {
01850   doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
01851 }
01852 
01853 void XPDFCore::doErrorDialog(char *title, GString *msg) {
01854   doDialog(XmDIALOG_ERROR, gFalse, title, msg);
01855 }
01856 
01857 GBool XPDFCore::doDialog(int type, GBool hasCancel,
01858              char *title, GString *msg) {
01859   Widget dialog;
01860   XtAppContext appContext;
01861   Arg args[20];
01862   int n;
01863   XmString s1, s2;
01864   XEvent event;
01865 
01866   n = 0;
01867   XtSetArg(args[n], XmNdialogType, type); ++n;
01868   XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
01869   s1 = XmStringCreateLocalized(title);
01870   XtSetArg(args[n], XmNdialogTitle, s1); ++n;
01871   s2 = XmStringCreateLocalized(msg->getCString());
01872   XtSetArg(args[n], XmNmessageString, s2); ++n;
01873   dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
01874   XmStringFree(s1);
01875   XmStringFree(s2);
01876   XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
01877   XtAddCallback(dialog, XmNokCallback,
01878         &dialogOkCbk, (XtPointer)this);
01879   if (hasCancel) {
01880     XtAddCallback(dialog, XmNcancelCallback,
01881           &dialogCancelCbk, (XtPointer)this);
01882   } else {
01883     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
01884   }
01885 
01886   XtManageChild(dialog);
01887 
01888   appContext = XtWidgetToApplicationContext(dialog);
01889   dialogDone = 0;
01890   do {
01891     XtAppNextEvent(appContext, &event);
01892     XtDispatchEvent(&event);
01893   } while (!dialogDone);
01894 
01895   XtUnmanageChild(dialog);
01896   XtDestroyWidget(dialog);
01897 
01898   return dialogDone > 0;
01899 }
01900 
01901 void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
01902                XtPointer callData) {
01903   XPDFCore *core = (XPDFCore *)ptr;
01904 
01905   core->dialogDone = 1;
01906 }
01907 
01908 void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
01909                    XtPointer callData) {
01910   XPDFCore *core = (XPDFCore *)ptr;
01911 
01912   core->dialogDone = -1;
01913 }
KDE Home | KDE Accessibility Home | Description of Access Keys