XPDFCore.cpp
1//========================================================================
2//
3// XPDFCore.cpp
4//
5// Copyright 2002 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <aconf.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <X11/keysym.h>
16#include <X11/cursorfont.h>
17#include "gmem.h"
18#include "GString.h"
19#include "GList.h"
20#include "Error.h"
21#include "GlobalParams.h"
22#include "PDFDoc.h"
23#include "ErrorCodes.h"
24#include "GfxState.h"
25#include "PSOutputDev.h"
26#include "TextOutputDev.h"
27#include "XPixmapOutputDev.h"
28#include "XPDFCore.h"
29
30// these macro defns conflict with xpdf's Object class
31#ifdef LESSTIF_VERSION
32#undef XtDisplay
33#undef XtScreen
34#undef XtWindow
35#undef XtParent
36#undef XtIsRealized
37#endif
38
39// hack around old X includes which are missing these symbols
40#ifndef XK_Page_Up
41#define XK_Page_Up 0xFF55
42#endif
43#ifndef XK_Page_Down
44#define XK_Page_Down 0xFF56
45#endif
46#ifndef XK_KP_Home
47#define XK_KP_Home 0xFF95
48#endif
49#ifndef XK_KP_Left
50#define XK_KP_Left 0xFF96
51#endif
52#ifndef XK_KP_Up
53#define XK_KP_Up 0xFF97
54#endif
55#ifndef XK_KP_Right
56#define XK_KP_Right 0xFF98
57#endif
58#ifndef XK_KP_Down
59#define XK_KP_Down 0xFF99
60#endif
61#ifndef XK_KP_Prior
62#define XK_KP_Prior 0xFF9A
63#endif
64#ifndef XK_KP_Page_Up
65#define XK_KP_Page_Up 0xFF9A
66#endif
67#ifndef XK_KP_Next
68#define XK_KP_Next 0xFF9B
69#endif
70#ifndef XK_KP_Page_Down
71#define XK_KP_Page_Down 0xFF9B
72#endif
73#ifndef XK_KP_End
74#define XK_KP_End 0xFF9C
75#endif
76#ifndef XK_KP_Begin
77#define XK_KP_Begin 0xFF9D
78#endif
79#ifndef XK_KP_Insert
80#define XK_KP_Insert 0xFF9E
81#endif
82#ifndef XK_KP_Delete
83#define XK_KP_Delete 0xFF9F
84#endif
85
86//------------------------------------------------------------------------
87
88#define highlightNone 0
89#define highlightNormal 1
90#define highlightSelected 2
91
92//------------------------------------------------------------------------
93
94static int zoomDPI[maxZoom - minZoom + 1] = {
95 29, 35, 42, 50, 60,
96 72,
97 86, 104, 124, 149, 179
98};
99
100//------------------------------------------------------------------------
101
102GString *XPDFCore::currentSelection = NULL;
103XPDFCore *XPDFCore::currentSelectionOwner = NULL;
104
105//------------------------------------------------------------------------
106// XPDFCore
107//------------------------------------------------------------------------
108
109XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
110 Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
111 GBool installCmap, int rgbCubeSize) {
112 GString *initialZoom;
113 int i;
114
115 shell = shellA;
116 parentWidget = parentWidgetA;
117 display = XtDisplay(parentWidget);
118 screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
119
120 paperColor = paperColorA;
121 fullScreen = fullScreenA;
122
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);
127
128 scrolledWin = NULL;
129 hScrollBar = NULL;
130 vScrollBar = NULL;
131 drawAreaFrame = NULL;
132 drawArea = NULL;
133 out = NULL;
134
135 doc = NULL;
136 page = 0;
137 rotate = 0;
138
139 // get the initial zoom value
140 initialZoom = globalParams->getInitialZoom();
141 if (!initialZoom->cmp("page")) {
142 zoom = zoomPage;
143 } else if (!initialZoom->cmp("width")) {
144 zoom = zoomWidth;
145 } else {
146 zoom = atoi(initialZoom->getCString());
147 if (zoom < minZoom) {
148 zoom = minZoom;
149 } else if (zoom > maxZoom) {
150 zoom = maxZoom;
151 }
152 }
153
154 scrollX = 0;
155 scrollY = 0;
156 linkAction = NULL;
157 selectXMin = selectXMax = 0;
158 selectYMin = selectYMax = 0;
159 dragging = gFalse;
160 lastDragLeft = lastDragTop = gTrue;
161
162 panning = gFalse;
163
164
165 updateCbk = NULL;
166 actionCbk = NULL;
167 keyPressCbk = NULL;
168 mouseCbk = NULL;
169 reqPasswordCbk = NULL;
170
171 // no history yet
172 historyCur = xpdfHistorySize - 1;
173 historyBLen = historyFLen = 0;
174 for (i = 0; i < xpdfHistorySize; ++i) {
175 history[i].fileName = NULL;
176 }
177
178 // optional features default to on
179 hyperlinksEnabled = gTrue;
180 selectEnabled = gTrue;
181
182 // do X-specific initialization and create the widgets
183 initWindow();
184
185 // create the OutputDev
186 out = new XPixmapOutputDev(display, screenNum, visual, colormap,
187 reverseVideo, paperColor,
188 installCmap, rgbCubeSize, gTrue,
189 &outputDevRedrawCbk, this);
190 out->startDoc(NULL);
191}
192
193XPDFCore::~XPDFCore() {
194 int i;
195
196 if (out) {
197 delete out;
198 }
199 if (doc) {
200 delete doc;
201 }
202 if (currentSelectionOwner == this && currentSelection) {
203 delete currentSelection;
204 currentSelection = NULL;
205 currentSelectionOwner = NULL;
206 }
207 for (i = 0; i < xpdfHistorySize; ++i) {
208 if (history[i].fileName) {
209 delete history[i].fileName;
210 }
211 }
212 if (selectGC) {
213 XFreeGC(display, selectGC);
214 XFreeGC(display, highlightGC);
215 }
216 if (drawAreaGC) {
217 XFreeGC(display, drawAreaGC);
218 }
219 if (drawArea) {
220 XtDestroyWidget(drawArea);
221 }
222 if (drawAreaFrame) {
223 XtDestroyWidget(drawAreaFrame);
224 }
225 if (vScrollBar) {
226 XtDestroyWidget(vScrollBar);
227 }
228 if (hScrollBar) {
229 XtDestroyWidget(hScrollBar);
230 }
231 if (scrolledWin) {
232 XtDestroyWidget(scrolledWin);
233 }
234 if (busyCursor) {
235 XFreeCursor(display, busyCursor);
236 }
237 if (linkCursor) {
238 XFreeCursor(display, linkCursor);
239 }
240 if (selectCursor) {
241 XFreeCursor(display, selectCursor);
242 }
243}
244
245//------------------------------------------------------------------------
246// loadFile / displayPage / displayDest
247//------------------------------------------------------------------------
248
249int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
250 GString *userPassword) {
251 PDFDoc *newDoc;
252 GString *password;
253 GBool again;
254 int err;
255
256 // busy cursor
257 setCursor(busyCursor);
258
259 // open the PDF file
260 newDoc = new PDFDoc(fileName->copy(), ownerPassword, userPassword);
261 if (!newDoc->isOk()) {
262 err = newDoc->getErrorCode();
263 delete newDoc;
264 if (err != errEncrypted || !reqPasswordCbk) {
265 setCursor(None);
266 return err;
267 }
268
269 // try requesting a password
270 again = ownerPassword != NULL || userPassword != NULL;
271 while (1) {
272 if (!(password = (*reqPasswordCbk)(reqPasswordCbkData, again))) {
273 setCursor(None);
274 return errEncrypted;
275 }
276 newDoc = new PDFDoc(fileName->copy(), password, password);
277 if (newDoc->isOk()) {
278 break;
279 }
280 err = newDoc->getErrorCode();
281 delete newDoc;
282 if (err != errEncrypted) {
283 setCursor(None);
284 return err;
285 }
286 again = gTrue;
287 }
288 }
289
290 // replace old document
291 if (doc) {
292 delete doc;
293 }
294 doc = newDoc;
295 if (out) {
296 out->startDoc(doc->getXRef());
297 }
298
299 // nothing displayed yet
300 page = -99;
301
302 // save the modification time
303 modTime = getModTime(doc->getFileName()->getCString());
304
305 // update the parent window
306 if (updateCbk) {
307 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
308 doc->getNumPages(), NULL);
309 }
310
311 // back to regular cursor
312 setCursor(None);
313
314 return errNone;
315}
316
317void XPDFCore::resizeToPage(int pg) {
318 Dimension width, height;
319 double width1, height1;
320 Dimension topW, topH, topBorder, daW, daH;
321 Dimension displayW, displayH;
322
323 displayW = DisplayWidth(display, screenNum);
324 displayH = DisplayHeight(display, screenNum);
325 if (fullScreen) {
326 width = displayW;
327 height = displayH;
328 } else {
329 if (pg < 0 || pg > doc->getNumPages()) {
330 width1 = 612;
331 height1 = 792;
332 } else if (doc->getPageRotate(pg) == 90 ||
333 doc->getPageRotate(pg) == 270) {
334 width1 = doc->getPageHeight(pg);
335 height1 = doc->getPageWidth(pg);
336 } else {
337 width1 = doc->getPageWidth(pg);
338 height1 = doc->getPageHeight(pg);
339 }
340 if (zoom == zoomPage || zoom == zoomWidth) {
341 width = (Dimension)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
342 height = (Dimension)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
343 } else {
344 width = (Dimension)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
345 height = (Dimension)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
346 }
347 if (width > displayW - 100) {
348 width = displayW - 100;
349 }
350 if (height > displayH - 150) {
351 height = displayH - 150;
352 }
353 }
354
355 if (XtIsRealized(shell)) {
356 XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
357 XmNborderWidth, &topBorder, NULL);
358 XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
359 XtVaSetValues(shell, XmNwidth, width + (topW - daW),
360 XmNheight, height + (topH - daH), NULL);
361 } else {
362 XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
363 }
364}
365
366void XPDFCore::clear() {
367 if (!doc) {
368 return;
369 }
370
371 // no document
372 delete doc;
373 doc = NULL;
374 out->clear();
375
376 // no page displayed
377 page = -99;
378
379 // redraw
380 scrollX = scrollY = 0;
381 updateScrollBars();
382 redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
383}
384
385void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
386 GBool scrollToTop, GBool addToHist) {
387 double hDPI, vDPI;
388 int rot;
389 XPDFHistory *h;
390 GBool newZoom;
391 XGCValues gcValues;
392 time_t newModTime;
393 int oldScrollX, oldScrollY;
394
395 // update the zoom and rotate values
396 newZoom = zoomA != zoom;
397 zoom = zoomA;
398 rotate = rotateA;
399
400 // check for document and valid page number
401 if (!doc || pageA <= 0 || pageA > doc->getNumPages()) {
402 return;
403 }
404
405 // busy cursor
406 setCursor(busyCursor);
407
408
409 // check for changes to the file
410 newModTime = getModTime(doc->getFileName()->getCString());
411 if (newModTime != modTime) {
412 if (loadFile(doc->getFileName()) == errNone) {
413 if (pageA > doc->getNumPages()) {
414 pageA = doc->getNumPages();
415 }
416 }
417 modTime = newModTime;
418 }
419
420 // free the old GCs
421 if (selectGC) {
422 XFreeGC(display, selectGC);
423 XFreeGC(display, highlightGC);
424 }
425
426 // new page number
427 page = pageA;
428
429 // scroll to top
430 if (scrollToTop) {
431 scrollY = 0;
432 }
433
434 // if zoom level changed, scroll to the top-left corner
435 if (newZoom) {
436 scrollX = scrollY = 0;
437 }
438
439 // initialize mouse-related stuff
440 linkAction = NULL;
441 selectXMin = selectXMax = 0;
442 selectYMin = selectYMax = 0;
443 dragging = gFalse;
444 lastDragLeft = lastDragTop = gTrue;
445
446 // draw the page
447 rot = rotate + doc->getPageRotate(page);
448 if (rot >= 360) {
449 rot -= 360;
450 } else if (rotate < 0) {
451 rot += 360;
452 }
453 if (zoom == zoomPage) {
454 if (rot == 90 || rot == 270) {
455 hDPI = (drawAreaWidth / doc->getPageHeight(page)) * 72;
456 vDPI = (drawAreaHeight / doc->getPageWidth(page)) * 72;
457 } else {
458 hDPI = (drawAreaWidth / doc->getPageWidth(page)) * 72;
459 vDPI = (drawAreaHeight / doc->getPageHeight(page)) * 72;
460 }
461 dpi = (hDPI < vDPI) ? hDPI : vDPI;
462 } else if (zoom == zoomWidth) {
463 if (rot == 90 || rot == 270) {
464 dpi = (drawAreaWidth / doc->getPageHeight(page)) * 72;
465 } else {
466 dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
467 }
468 } else {
469 dpi = zoomDPI[zoom - minZoom];
470 }
471 out->setWindow(XtWindow(drawArea));
472 doc->displayPage(out, page, dpi, rotate, gTrue);
473 oldScrollX = scrollX;
474 oldScrollY = scrollY;
475 updateScrollBars();
476 if (scrollX != oldScrollX || scrollY != oldScrollY) {
477 redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
478 }
479
480
481 // add to history
482 if (addToHist) {
483 if (++historyCur == xpdfHistorySize) {
484 historyCur = 0;
485 }
486 h = &history[historyCur];
487 if (h->fileName) {
488 delete h->fileName;
489 }
490 h->fileName = doc->getFileName()->copy();
491 h->page = page;
492 if (historyBLen < xpdfHistorySize) {
493 ++historyBLen;
494 }
495 historyFLen = 0;
496 }
497
498 // update the parent window
499 if (updateCbk) {
500 (*updateCbk)(updateCbkData, NULL, page, -1, "");
501 }
502
503 // allocate new GCs
504 gcValues.foreground = BlackPixel(display, screenNum) ^
505 WhitePixel(display, screenNum);
506 gcValues.function = GXxor;
507 selectGC = XCreateGC(display, out->getPixmap(),
508 GCForeground | GCFunction, &gcValues);
509 highlightGC = XCreateGC(display, out->getPixmap(),
510 GCForeground | GCFunction, &gcValues);
511
512 // back to regular cursor
513 setCursor(None);
514}
515
516void XPDFCore::displayDest(LinkDest *dest, int zoomA, int rotateA,
517 GBool addToHist) {
518 Ref pageRef;
519 int pg;
520 int dx, dy;
521
522 if (dest->isPageRef()) {
523 pageRef = dest->getPageRef();
524 pg = doc->findPage(pageRef.num, pageRef.gen);
525 } else {
526 pg = dest->getPageNum();
527 }
528 if (pg <= 0 || pg > doc->getNumPages()) {
529 pg = 1;
530 }
531 if (pg != page) {
532 displayPage(pg, zoomA, rotateA, gTrue, addToHist);
533 }
534
535 if (fullScreen) {
536 return;
537 }
538 switch (dest->getKind()) {
539 case destXYZ:
540 out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
541 if (dest->getChangeLeft() || dest->getChangeTop()) {
542 scrollTo(dest->getChangeLeft() ? dx : scrollX,
543 dest->getChangeTop() ? dy : scrollY);
544 }
545 //~ what is the zoom parameter?
546 break;
547 case destFit:
548 case destFitB:
549 //~ do fit
550 scrollTo(0, 0);
551 break;
552 case destFitH:
553 case destFitBH:
554 //~ do fit
555 out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
556 scrollTo(0, dy);
557 break;
558 case destFitV:
559 case destFitBV:
560 //~ do fit
561 out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
562 scrollTo(dx, 0);
563 break;
564 case destFitR:
565 //~ do fit
566 out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
567 scrollTo(dx, dy);
568 break;
569 }
570}
571
572//------------------------------------------------------------------------
573// page/position changes
574//------------------------------------------------------------------------
575
576void XPDFCore::gotoNextPage(int inc, GBool top) {
577 int pg;
578
579 if (!doc || doc->getNumPages() == 0) {
580 return;
581 }
582 if (page < doc->getNumPages()) {
583 if ((pg = page + inc) > doc->getNumPages()) {
584 pg = doc->getNumPages();
585 }
586 displayPage(pg, zoom, rotate, top, gTrue);
587 } else {
588 XBell(display, 0);
589 }
590}
591
592void XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
593 int pg;
594
595 if (!doc || doc->getNumPages() == 0) {
596 return;
597 }
598 if (page > 1) {
599 if (!fullScreen && bottom) {
600 scrollY = out->getPixmapHeight() - drawAreaHeight;
601 if (scrollY < 0) {
602 scrollY = 0;
603 }
604 // displayPage will call updateScrollBars()
605 }
606 if ((pg = page - dec) < 1) {
607 pg = 1;
608 }
609 displayPage(pg, zoom, rotate, top, gTrue);
610 } else {
611 XBell(display, 0);
612 }
613}
614
615void XPDFCore::goForward() {
616 if (historyFLen == 0) {
617 XBell(display, 0);
618 return;
619 }
620 if (++historyCur == xpdfHistorySize) {
621 historyCur = 0;
622 }
623 --historyFLen;
624 ++historyBLen;
625 if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
626 if (loadFile(history[historyCur].fileName) != errNone) {
627 XBell(display, 0);
628 return;
629 }
630 }
631 displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
632}
633
634void XPDFCore::goBackward() {
635 if (historyBLen <= 1) {
636 XBell(display, 0);
637 return;
638 }
639 if (--historyCur < 0) {
640 historyCur = xpdfHistorySize - 1;
641 }
642 --historyBLen;
643 ++historyFLen;
644 if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
645 if (loadFile(history[historyCur].fileName) != errNone) {
646 XBell(display, 0);
647 return;
648 }
649 }
650 displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
651}
652
653void XPDFCore::scrollLeft(int nCols) {
654 scrollTo(scrollX - nCols * 16, scrollY);
655}
656
657void XPDFCore::scrollRight(int nCols) {
658 scrollTo(scrollX + nCols * 16, scrollY);
659}
660
661void XPDFCore::scrollUp(int nLines) {
662 scrollTo(scrollX, scrollY - nLines * 16);
663}
664
665void XPDFCore::scrollDown(int nLines) {
666 scrollTo(scrollX, scrollY + nLines * 16);
667}
668
669void XPDFCore::scrollPageUp() {
670 if (scrollY == 0) {
671 gotoPrevPage(1, gFalse, gTrue);
672 } else {
673 scrollTo(scrollX, scrollY - drawAreaHeight);
674 }
675}
676
677void XPDFCore::scrollPageDown() {
678 if (scrollY >= out->getPixmapHeight() - drawAreaHeight) {
679 gotoNextPage(1, gTrue);
680 } else {
681 scrollTo(scrollX, scrollY + drawAreaHeight);
682 }
683}
684
685void XPDFCore::scrollTo(int x, int y) {
686 GBool needRedraw;
687 int maxPos, pos;
688
689 needRedraw = gFalse;
690
691 maxPos = out ? out->getPixmapWidth() : 1;
692 if (maxPos < drawAreaWidth) {
693 maxPos = drawAreaWidth;
694 }
695 if (x < 0) {
696 pos = 0;
697 } else if (x > maxPos - drawAreaWidth) {
698 pos = maxPos - drawAreaWidth;
699 } else {
700 pos = x;
701 }
702 if (scrollX != pos) {
703 scrollX = pos;
704 XmScrollBarSetValues(hScrollBar, scrollX, drawAreaWidth, 16,
705 drawAreaWidth, False);
706 needRedraw = gTrue;
707 }
708
709 maxPos = out ? out->getPixmapHeight() : 1;
710 if (maxPos < drawAreaHeight) {
711 maxPos = drawAreaHeight;
712 }
713 if (y < 0) {
714 pos = 0;
715 } else if (y > maxPos - drawAreaHeight) {
716 pos = maxPos - drawAreaHeight;
717 } else {
718 pos = y;
719 }
720 if (scrollY != pos) {
721 scrollY = pos;
722 XmScrollBarSetValues(vScrollBar, scrollY, drawAreaHeight, 16,
723 drawAreaHeight, False);
724 needRedraw = gTrue;
725 }
726
727 if (needRedraw) {
728 redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
729 }
730}
731
732//------------------------------------------------------------------------
733// selection
734//------------------------------------------------------------------------
735
736void XPDFCore::setSelection(int newXMin, int newYMin,
737 int newXMax, int newYMax) {
738 Pixmap pixmap;
739 int x, y;
740 GBool needRedraw, needScroll;
741 GBool moveLeft, moveRight, moveTop, moveBottom;
742
743 pixmap = out->getPixmap();
744
745
746 // erase old selection on off-screen bitmap
747 needRedraw = gFalse;
748 if (selectXMin < selectXMax && selectYMin < selectYMax) {
749 XFillRectangle(display, pixmap,
750 selectGC, selectXMin, selectYMin,
751 selectXMax - selectXMin, selectYMax - selectYMin);
752 needRedraw = gTrue;
753 }
754
755 // draw new selection on off-screen bitmap
756 if (newXMin < newXMax && newYMin < newYMax) {
757 XFillRectangle(display, pixmap,
758 selectGC, newXMin, newYMin,
759 newXMax - newXMin, newYMax - newYMin);
760 needRedraw = gTrue;
761 }
762
763 // check which edges moved
764 moveLeft = newXMin != selectXMin;
765 moveTop = newYMin != selectYMin;
766 moveRight = newXMax != selectXMax;
767 moveBottom = newYMax != selectYMax;
768
769 // redraw currently visible part of bitmap
770 if (needRedraw) {
771 if (moveLeft) {
772 redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
773 (newYMin < selectYMin) ? newYMin : selectYMin,
774 (newXMin > selectXMin) ? newXMin : selectXMin,
775 (newYMax > selectYMax) ? newYMax : selectYMax);
776 }
777 if (moveRight) {
778 redrawRectangle((newXMax < selectXMax) ? newXMax : selectXMax,
779 (newYMin < selectYMin) ? newYMin : selectYMin,
780 (newXMax > selectXMax) ? newXMax : selectXMax,
781 (newYMax > selectYMax) ? newYMax : selectYMax);
782 }
783 if (moveTop) {
784 redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
785 (newYMin < selectYMin) ? newYMin : selectYMin,
786 (newXMax > selectXMax) ? newXMax : selectXMax,
787 (newYMin > selectYMin) ? newYMin : selectYMin);
788 }
789 if (moveBottom) {
790 redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
791 (newYMax < selectYMax) ? newYMax : selectYMax,
792 (newXMax > selectXMax) ? newXMax : selectXMax,
793 (newYMax > selectYMax) ? newYMax : selectYMax);
794 }
795 }
796
797 // switch to new selection coords
798 selectXMin = newXMin;
799 selectXMax = newXMax;
800 selectYMin = newYMin;
801 selectYMax = newYMax;
802
803 // scroll if necessary
804 if (fullScreen) {
805 return;
806 }
807 needScroll = gFalse;
808 x = scrollX;
809 y = scrollY;
810 if (moveLeft && selectXMin < x) {
811 x = selectXMin;
812 needScroll = gTrue;
813 } else if (moveRight && selectXMax >= x + drawAreaWidth) {
814 x = selectXMax - drawAreaWidth;
815 needScroll = gTrue;
816 } else if (moveLeft && selectXMin >= x + drawAreaWidth) {
817 x = selectXMin - drawAreaWidth;
818 needScroll = gTrue;
819 } else if (moveRight && selectXMax < x) {
820 x = selectXMax;
821 needScroll = gTrue;
822 }
823 if (moveTop && selectYMin < y) {
824 y = selectYMin;
825 needScroll = gTrue;
826 } else if (moveBottom && selectYMax >= y + drawAreaHeight) {
827 y = selectYMax - drawAreaHeight;
828 needScroll = gTrue;
829 } else if (moveTop && selectYMin >= y + drawAreaHeight) {
830 y = selectYMin - drawAreaHeight;
831 needScroll = gTrue;
832 } else if (moveBottom && selectYMax < y) {
833 y = selectYMax;
834 needScroll = gTrue;
835 }
836 if (needScroll) {
837 scrollTo(x, y);
838 }
839}
840
841void XPDFCore::moveSelection(int mx, int my) {
842 int xMin, yMin, xMax, yMax;
843
844 // clip mouse coords
845 if (mx < 0) {
846 mx = 0;
847 } else if (mx >= out->getPixmapWidth()) {
848 mx = out->getPixmapWidth() - 1;
849 }
850 if (my < 0) {
851 my = 0;
852 } else if (my >= out->getPixmapHeight()) {
853 my = out->getPixmapHeight() - 1;
854 }
855
856 // move appropriate edges of selection
857 if (lastDragLeft) {
858 if (mx < selectXMax) {
859 xMin = mx;
860 xMax = selectXMax;
861 } else {
862 xMin = selectXMax;
863 xMax = mx;
864 lastDragLeft = gFalse;
865 }
866 } else {
867 if (mx > selectXMin) {
868 xMin = selectXMin;
869 xMax = mx;
870 } else {
871 xMin = mx;
872 xMax = selectXMin;
873 lastDragLeft = gTrue;
874 }
875 }
876 if (lastDragTop) {
877 if (my < selectYMax) {
878 yMin = my;
879 yMax = selectYMax;
880 } else {
881 yMin = selectYMax;
882 yMax = my;
883 lastDragTop = gFalse;
884 }
885 } else {
886 if (my > selectYMin) {
887 yMin = selectYMin;
888 yMax = my;
889 } else {
890 yMin = my;
891 yMax = selectYMin;
892 lastDragTop = gTrue;
893 }
894 }
895
896 // redraw the selection
897 setSelection(xMin, yMin, xMax, yMax);
898}
899
900// X's copy-and-paste mechanism is brain damaged. Xt doesn't help
901// any, but doesn't make it too much worse, either. Motif, on the
902// other hand, adds significant complexity to the mess. So here we
903// blow off the Motif junk and stick to plain old Xt. The next two
904// functions (copySelection and convertSelectionCbk) implement the
905// magic needed to deal with Xt's mechanism. Note that this requires
906// global variables (currentSelection and currentSelectionOwner).
907
908void XPDFCore::copySelection() {
909 if (!doc->okToCopy()) {
910 return;
911 }
912 if (currentSelection) {
913 delete currentSelection;
914 }
915 //~ for multithreading: need a mutex here
916 currentSelection = out->getText(selectXMin, selectYMin,
917 selectXMax, selectYMax);
918 currentSelectionOwner = this;
919 XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
920 &convertSelectionCbk, NULL, NULL);
921}
922
923Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
924 Atom *target, Atom *type,
925 XtPointer *value, unsigned long *length,
926 int *format) {
927 if (*target != XA_STRING) {
928 return False;
929 }
930 //~ for multithreading: need a mutex here
931 *value = XtNewString(currentSelection->getCString());
932 *length = currentSelection->getLength();
933 *type = XA_STRING;
934 *format = 8; // 8-bit elements
935 return True;
936}
937
938GBool XPDFCore::getSelection(int *xMin, int *yMin, int *xMax, int *yMax) {
939 if (selectXMin >= selectXMax || selectYMin >= selectYMax) {
940 return gFalse;
941 }
942 *xMin = selectXMin;
943 *yMin = selectYMin;
944 *xMax = selectXMax;
945 *yMax = selectYMax;
946 return gTrue;
947}
948
949GString *XPDFCore::extractText(int xMin, int yMin, int xMax, int yMax) {
950 if (!doc->okToCopy()) {
951 return NULL;
952 }
953 return out->getText(xMin, yMin, xMax, yMax);
954}
955
956GString *XPDFCore::extractText(int pageNum,
957 int xMin, int yMin, int xMax, int yMax) {
958 TextOutputDev *textOut;
959 GString *s;
960
961 if (!doc->okToCopy()) {
962 return NULL;
963 }
964 textOut = new TextOutputDev(NULL, gFalse, gFalse);
965 if (!textOut->isOk()) {
966 delete textOut;
967 return NULL;
968 }
969 doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
970 s = textOut->getText(xMin, yMin, xMax, yMax);
971 delete textOut;
972 return s;
973}
974
975//------------------------------------------------------------------------
976// hyperlinks
977//------------------------------------------------------------------------
978
979void XPDFCore::doLink(int mx, int my) {
980 double x, y;
981 LinkAction *action;
982
983 // look for a link
984 out->cvtDevToUser(mx, my, &x, &y);
985 if ((action = doc->findLink(x, y))) {
986 doAction(action);
987 }
988}
989
990void XPDFCore::doAction(LinkAction *action) {
991 LinkActionKind kind;
992 LinkDest *dest;
993 GString *namedDest;
994 char *s;
995 GString *fileName, *fileName2;
996 GString *cmd;
997 GString *actionName;
998 Object movieAnnot, obj1, obj2;
999 GString *msg;
1000 int i;
1001
1002 switch (kind = action->getKind()) {
1003
1004 // GoTo / GoToR action
1005 case actionGoTo:
1006 case actionGoToR:
1007 if (kind == actionGoTo) {
1008 dest = NULL;
1009 namedDest = NULL;
1010 if ((dest = ((LinkGoTo *)action)->getDest())) {
1011 dest = dest->copy();
1012 } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
1013 namedDest = namedDest->copy();
1014 }
1015 } else {
1016 dest = NULL;
1017 namedDest = NULL;
1018 if ((dest = ((LinkGoToR *)action)->getDest())) {
1019 dest = dest->copy();
1020 } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
1021 namedDest = namedDest->copy();
1022 }
1023 s = ((LinkGoToR *)action)->getFileName()->getCString();
1024 //~ translate path name for VMS (deal with '/')
1025 if (isAbsolutePath(s)) {
1026 fileName = new GString(s);
1027 } else {
1028 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
1029 }
1030 if (loadFile(fileName) != errNone) {
1031 if (dest) {
1032 delete dest;
1033 }
1034 if (namedDest) {
1035 delete namedDest;
1036 }
1037 delete fileName;
1038 return;
1039 }
1040 delete fileName;
1041 }
1042 if (namedDest) {
1043 dest = doc->findDest(namedDest);
1044 delete namedDest;
1045 }
1046 if (dest) {
1047 displayDest(dest, zoom, rotate, gTrue);
1048 delete dest;
1049 } else {
1050 if (kind == actionGoToR) {
1051 displayPage(1, zoom, 0, gFalse, gTrue);
1052 }
1053 }
1054 break;
1055
1056 // Launch action
1057 case actionLaunch:
1058 fileName = ((LinkLaunch *)action)->getFileName();
1059 s = fileName->getCString();
1060 if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
1061 !strcmp(s + fileName->getLength() - 4, ".PDF")) {
1062 //~ translate path name for VMS (deal with '/')
1063 if (isAbsolutePath(s)) {
1064 fileName = fileName->copy();
1065 } else {
1066 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
1067 }
1068 if (loadFile(fileName) != errNone) {
1069 delete fileName;
1070 return;
1071 }
1072 delete fileName;
1073 displayPage(1, zoom, rotate, gFalse, gTrue);
1074 } else {
1075 fileName = fileName->copy();
1076 if (((LinkLaunch *)action)->getParams()) {
1077 fileName->append(' ');
1078 fileName->append(((LinkLaunch *)action)->getParams());
1079 }
1080#ifdef VMS
1081 fileName->insert(0, "spawn/nowait ");
1082#elif defined(__EMX__)
1083 fileName->insert(0, "start /min /n ");
1084#else
1085 fileName->append(" &");
1086#endif
1087 msg = new GString("About to execute the command:\n");
1088 msg->append(fileName);
1089 if (doQuestionDialog("Launching external application", msg)) {
1090 system(fileName->getCString());
1091 }
1092 delete fileName;
1093 delete msg;
1094 }
1095 break;
1096
1097 // URI action
1098 case actionURI:
1099 if (!(cmd = globalParams->getURLCommand())) {
1100 error(-1, "No urlCommand defined in config file");
1101 break;
1102 }
1103 runCommand(cmd, ((LinkURI *)action)->getURI());
1104 break;
1105
1106 // Named action
1107 case actionNamed:
1108 actionName = ((LinkNamed *)action)->getName();
1109 if (!actionName->cmp("NextPage")) {
1110 gotoNextPage(1, gTrue);
1111 } else if (!actionName->cmp("PrevPage")) {
1112 gotoPrevPage(1, gTrue, gFalse);
1113 } else if (!actionName->cmp("FirstPage")) {
1114 if (page != 1) {
1115 displayPage(1, zoom, rotate, gTrue, gTrue);
1116 }
1117 } else if (!actionName->cmp("LastPage")) {
1118 if (page != doc->getNumPages()) {
1119 displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
1120 }
1121 } else if (!actionName->cmp("GoBack")) {
1122 goBackward();
1123 } else if (!actionName->cmp("GoForward")) {
1124 goForward();
1125 } else if (!actionName->cmp("Quit")) {
1126 if (actionCbk) {
1127 (*actionCbk)(actionCbkData, "Quit");
1128 }
1129 } else {
1130 error(-1, "Unknown named action: '%s'", actionName->getCString());
1131 }
1132 break;
1133
1134 // Movie action
1135 case actionMovie:
1136 if (!(cmd = globalParams->getMovieCommand())) {
1137 error(-1, "No movieCommand defined in config file");
1138 break;
1139 }
1140 if (((LinkMovie *)action)->hasAnnotRef()) {
1141 doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
1142 ((LinkMovie *)action)->getAnnotRef()->gen,
1143 &movieAnnot);
1144 } else {
1145 doc->getCatalog()->getPage(page)->getAnnots(&obj1);
1146 if (obj1.isArray()) {
1147 for (i = 0; i < obj1.arrayGetLength(); ++i) {
1148 if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
1149 if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
1150 obj2.free();
1151 break;
1152 }
1153 obj2.free();
1154 }
1155 movieAnnot.free();
1156 }
1157 obj1.free();
1158 }
1159 }
1160 if (movieAnnot.isDict()) {
1161 if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
1162 if (obj1.dictLookup("F", &obj2)) {
1163 if ((fileName = LinkAction::getFileSpecName(&obj2))) {
1164 if (!isAbsolutePath(fileName->getCString())) {
1165 fileName2 = appendToPath(
1166 grabPath(doc->getFileName()->getCString()),
1167 fileName->getCString());
1168 delete fileName;
1169 fileName = fileName2;
1170 }
1171 runCommand(cmd, fileName);
1172 delete fileName;
1173 }
1174 obj2.free();
1175 }
1176 obj1.free();
1177 }
1178 }
1179 movieAnnot.free();
1180 break;
1181
1182 // unknown action type
1183 case actionUnknown:
1184 error(-1, "Unknown link action type: '%s'",
1185 ((LinkUnknown *)action)->getAction()->getCString());
1186 break;
1187 }
1188}
1189
1190// Run a command, given a <cmdFmt> string with one '%s' in it, and an
1191// <arg> string to insert in place of the '%s'.
1192void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
1193 GString *cmd;
1194 char *s;
1195 int i;
1196
1197 if ((s = strstr(cmdFmt->getCString(), "%s"))) {
1198 cmd = arg->copy();
1199 // filter out any quote marks (' or ") to avoid a potential
1200 // security hole
1201 i = 0;
1202 while (i < cmd->getLength()) {
1203 if (cmd->getChar(i) == '"') {
1204 cmd->del(i);
1205 cmd->insert(i, "%22");
1206 i += 3;
1207 } else if (cmd->getChar(i) == '\'') {
1208 cmd->del(i);
1209 cmd->insert(i, "%27");
1210 i += 3;
1211 } else {
1212 ++i;
1213 }
1214 }
1215 cmd->insert(0, cmdFmt->getCString(),
1216 s - cmdFmt->getCString());
1217 cmd->append(s + 2);
1218 } else {
1219 cmd = cmdFmt->copy();
1220 }
1221#ifdef VMS
1222 cmd->insert(0, "spawn/nowait ");
1223#elif defined(__EMX__)
1224 cmd->insert(0, "start /min /n ");
1225#else
1226 cmd->append(" &");
1227#endif
1228 system(cmd->getCString());
1229 delete cmd;
1230}
1231
1232
1233//------------------------------------------------------------------------
1234// find
1235//------------------------------------------------------------------------
1236
1237void XPDFCore::find(char *s) {
1238 Unicode *u;
1239 TextOutputDev *textOut;
1240 int xMin, yMin, xMax, yMax;
1241 double xMin1, yMin1, xMax1, yMax1;
1242 int pg;
1243 GBool top;
1244 int len, i;
1245
1246 // check for zero-length string
1247 if (!s[0]) {
1248 XBell(display, 0);
1249 return;
1250 }
1251
1252 // set cursor to watch
1253 setCursor(busyCursor);
1254
1255 // convert to Unicode
1256#if 1 //~ should do something more intelligent here
1257 len = strlen(s);
1258 u = (Unicode *)gmalloc(len * sizeof(Unicode));
1259 for (i = 0; i < len; ++i) {
1260 u[i] = (Unicode)(s[i] & 0xff);
1261 }
1262#endif
1263
1264 // search current page starting at current selection or top of page
1265 xMin = yMin = xMax = yMax = 0;
1266 if (selectXMin < selectXMax && selectYMin < selectYMax) {
1267 xMin = selectXMax;
1268 yMin = (selectYMin + selectYMax) / 2;
1269 top = gFalse;
1270 } else {
1271 top = gTrue;
1272 }
1273 if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
1274 goto found;
1275 }
1276
1277 // search following pages
1278 textOut = new TextOutputDev(NULL, gFalse, gFalse);
1279 if (!textOut->isOk()) {
1280 delete textOut;
1281 goto done;
1282 }
1283 for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
1284 doc->displayPage(textOut, pg, 72, 0, gFalse);
1285 if (textOut->findText(u, len, gTrue, gTrue,
1286 &xMin1, &yMin1, &xMax1, &yMax1)) {
1287 goto foundPage;
1288 }
1289 }
1290
1291 // search previous pages
1292 for (pg = 1; pg < page; ++pg) {
1293 doc->displayPage(textOut, pg, 72, 0, gFalse);
1294 if (textOut->findText(u, len, gTrue, gTrue,
1295 &xMin1, &yMin1, &xMax1, &yMax1)) {
1296 goto foundPage;
1297 }
1298 }
1299 delete textOut;
1300
1301 // search current page ending at current selection
1302 if (selectXMin < selectXMax && selectYMin < selectYMax) {
1303 xMax = selectXMin;
1304 yMax = (selectYMin + selectYMax) / 2;
1305 if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
1306 goto found;
1307 }
1308 }
1309
1310 // not found
1311 XBell(display, 0);
1312 goto done;
1313
1314 // found on a different page
1315 foundPage:
1316 delete textOut;
1317 displayPage(pg, zoom, rotate, gTrue, gTrue);
1318 if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
1319 // this can happen if coalescing is bad
1320 goto done;
1321 }
1322
1323 // found: change the selection
1324 found:
1325 setSelection(xMin, yMin, xMax, yMax);
1326#ifndef NO_TEXT_SELECT
1327 copySelection();
1328#endif
1329
1330 done:
1331 gfree(u);
1332
1333 // reset cursors to normal
1334 setCursor(None);
1335}
1336
1337//------------------------------------------------------------------------
1338// misc access
1339//------------------------------------------------------------------------
1340
1341void XPDFCore::setBusyCursor(GBool busy) {
1342 setCursor(busy ? busyCursor : None);
1343}
1344
1345void XPDFCore::takeFocus() {
1346 XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
1347}
1348
1349//------------------------------------------------------------------------
1350// GUI code
1351//------------------------------------------------------------------------
1352
1353void XPDFCore::initWindow() {
1354 Arg args[20];
1355 int n;
1356
1357 // create the cursors
1358 busyCursor = XCreateFontCursor(display, XC_watch);
1359 linkCursor = XCreateFontCursor(display, XC_hand2);
1360 selectCursor = XCreateFontCursor(display, XC_cross);
1361 currentCursor = 0;
1362
1363 // create the scrolled window and scrollbars
1364 n = 0;
1365 XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
1366 XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
1367 scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
1368 XtManageChild(scrolledWin);
1369 n = 0;
1370 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
1371 XtSetArg(args[n], XmNminimum, 0); ++n;
1372 XtSetArg(args[n], XmNmaximum, 1); ++n;
1373 XtSetArg(args[n], XmNsliderSize, 1); ++n;
1374 XtSetArg(args[n], XmNvalue, 0); ++n;
1375 XtSetArg(args[n], XmNincrement, 1); ++n;
1376 XtSetArg(args[n], XmNpageIncrement, 1); ++n;
1377 hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
1378 XtManageChild(hScrollBar);
1379 XtAddCallback(hScrollBar, XmNvalueChangedCallback,
1380 &hScrollChangeCbk, (XtPointer)this);
1381#ifndef DISABLE_SMOOTH_SCROLL
1382 XtAddCallback(hScrollBar, XmNdragCallback,
1383 &hScrollDragCbk, (XtPointer)this);
1384#endif
1385 n = 0;
1386 XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
1387 XtSetArg(args[n], XmNminimum, 0); ++n;
1388 XtSetArg(args[n], XmNmaximum, 1); ++n;
1389 XtSetArg(args[n], XmNsliderSize, 1); ++n;
1390 XtSetArg(args[n], XmNvalue, 0); ++n;
1391 XtSetArg(args[n], XmNincrement, 1); ++n;
1392 XtSetArg(args[n], XmNpageIncrement, 1); ++n;
1393 vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
1394 XtManageChild(vScrollBar);
1395 XtAddCallback(vScrollBar, XmNvalueChangedCallback,
1396 &vScrollChangeCbk, (XtPointer)this);
1397#ifndef DISABLE_SMOOTH_SCROLL
1398 XtAddCallback(vScrollBar, XmNdragCallback,
1399 &vScrollDragCbk, (XtPointer)this);
1400#endif
1401
1402 // create the drawing area
1403 n = 0;
1404 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
1405 XtSetArg(args[n], XmNmarginWidth, 0); ++n;
1406 XtSetArg(args[n], XmNmarginHeight, 0); ++n;
1407 if (fullScreen) {
1408 XtSetArg(args[n], XmNshadowThickness, 0); ++n;
1409 }
1410 drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
1411 XtManageChild(drawAreaFrame);
1412 n = 0;
1413 XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
1414 XtSetArg(args[n], XmNbackground, paperColor); ++n;
1415 XtSetArg(args[n], XmNwidth, 700); ++n;
1416 XtSetArg(args[n], XmNheight, 500); ++n;
1417 drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
1418 XtManageChild(drawArea);
1419 XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
1420 XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
1421 XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
1422 resizeCbk(drawArea, this, NULL);
1423
1424 // set up mouse motion translations
1425 XtOverrideTranslations(drawArea, XtParseTranslationTable(
1426 "<Btn1Down>:DrawingAreaInput()\n"
1427 "<Btn1Up>:DrawingAreaInput()\n"
1428 "<Btn1Motion>:DrawingAreaInput()\n"
1429 "<Motion>:DrawingAreaInput()"));
1430
1431 // can't create a GC until the window gets mapped
1432 drawAreaGC = NULL;
1433 selectGC = NULL;
1434 highlightGC = NULL;
1435}
1436
1437void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
1438 XtPointer callData) {
1439 XPDFCore *core = (XPDFCore *)ptr;
1440 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1441
1442 core->scrollTo(data->value, core->scrollY);
1443}
1444
1445void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
1446 XtPointer callData) {
1447 XPDFCore *core = (XPDFCore *)ptr;
1448 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1449
1450 core->scrollTo(data->value, core->scrollY);
1451}
1452
1453void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
1454 XtPointer callData) {
1455 XPDFCore *core = (XPDFCore *)ptr;
1456 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1457
1458 core->scrollTo(core->scrollX, data->value);
1459}
1460
1461void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
1462 XtPointer callData) {
1463 XPDFCore *core = (XPDFCore *)ptr;
1464 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1465
1466 core->scrollTo(core->scrollX, data->value);
1467}
1468
1469void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1470 XPDFCore *core = (XPDFCore *)ptr;
1471 Arg args[2];
1472 int n;
1473 Dimension w, h;
1474
1475 n = 0;
1476 XtSetArg(args[n], XmNwidth, &w); ++n;
1477 XtSetArg(args[n], XmNheight, &h); ++n;
1478 XtGetValues(core->drawArea, args, n);
1479 core->drawAreaWidth = (int)w;
1480 core->drawAreaHeight = (int)h;
1481 if (core->page >= 0 &&
1482 (core->zoom == zoomPage || core->zoom == zoomWidth)) {
1483 core->displayPage(core->page, core->zoom, core->rotate,
1484 gFalse, gFalse);
1485 } else {
1486 core->updateScrollBars();
1487 }
1488}
1489
1490void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1491 XPDFCore *core = (XPDFCore *)ptr;
1492 XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1493 int x, y, w, h;
1494
1495 if (data->reason == XmCR_EXPOSE) {
1496 x = core->scrollX + data->event->xexpose.x;
1497 y = core->scrollY + data->event->xexpose.y;
1498 w = data->event->xexpose.width;
1499 h = data->event->xexpose.height;
1500 } else {
1501 x = core->scrollX;
1502 y = core->scrollY;
1503 w = core->drawAreaWidth;
1504 h = core->drawAreaHeight;
1505 }
1506 core->redrawRectangle(x, y, w, h);
1507}
1508
1509void XPDFCore::outputDevRedrawCbk(void *data) {
1510 XPDFCore *core = (XPDFCore *)data;
1511
1512 core->redrawRectangle(core->scrollX, core->scrollY,
1513 core->drawAreaWidth, core->drawAreaHeight);
1514}
1515
1516void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1517 XPDFCore *core = (XPDFCore *)ptr;
1518 XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1519 LinkAction *action;
1520 int mx, my;
1521 double x, y;
1522 char *s;
1523 KeySym key;
1524 char buf[20];
1525 int n;
1526
1527 switch (data->event->type) {
1528 case ButtonPress:
1529 if (data->event->xbutton.button == 1) {
1530 core->takeFocus();
1531 if (core->doc && core->doc->getNumPages() > 0) {
1532 if (core->selectEnabled) {
1533 mx = core->scrollX + data->event->xbutton.x;
1534 my = core->scrollY + data->event->xbutton.y;
1535 core->setSelection(mx, my, mx, my);
1536 core->setCursor(core->selectCursor);
1537 core->dragging = gTrue;
1538 }
1539 }
1540 } else if (data->event->xbutton.button == 2) {
1541 if (!core->fullScreen) {
1542 core->panning = gTrue;
1543 core->panMX = data->event->xbutton.x;
1544 core->panMY = data->event->xbutton.y;
1545 }
1546 } else if (data->event->xbutton.button == 4) { // mouse wheel up
1547 if (core->fullScreen) {
1548 core->gotoPrevPage(1, gTrue, gFalse);
1549 } else if (core->scrollY == 0) {
1550 core->gotoPrevPage(1, gFalse, gTrue);
1551 } else {
1552 core->scrollUp(1);
1553 }
1554 } else if (data->event->xbutton.button == 5) { // mouse wheel down
1555 if (core->fullScreen ||
1556 core->scrollY >=
1557 core->out->getPixmapHeight() - core->drawAreaHeight) {
1558 core->gotoNextPage(1, gTrue);
1559 } else {
1560 core->scrollDown(1);
1561 }
1562 } else if (data->event->xbutton.button == 6) { // second mouse wheel right
1563 if (!core->fullScreen) {
1564 core->scrollRight(1);
1565 }
1566 } else if (data->event->xbutton.button == 7) { // second mouse wheel left
1567 if (!core->fullScreen) {
1568 core->scrollLeft(1);
1569 }
1570 } else {
1571 if (*core->mouseCbk) {
1572 (*core->mouseCbk)(core->mouseCbkData, data->event);
1573 }
1574 }
1575 break;
1576 case ButtonRelease:
1577 if (data->event->xbutton.button == 1) {
1578 if (core->doc && core->doc->getNumPages() > 0) {
1579 mx = core->scrollX + data->event->xbutton.x;
1580 my = core->scrollY + data->event->xbutton.y;
1581 if (core->dragging) {
1582 core->dragging = gFalse;
1583 core->setCursor(None);
1584 core->moveSelection(mx, my);
1585#ifndef NO_TEXT_SELECT
1586 if (core->selectXMin != core->selectXMax &&
1587 core->selectYMin != core->selectYMax) {
1588 if (core->doc->okToCopy()) {
1589 core->copySelection();
1590 } else {
1591 error(-1, "Copying of text from this document is not allowed.");
1592 }
1593 }
1594#endif
1595 }
1596 if (core->hyperlinksEnabled) {
1597 if (core->selectXMin == core->selectXMax ||
1598 core->selectYMin == core->selectYMax) {
1599 core->doLink(mx, my);
1600 }
1601 }
1602 }
1603 } else if (data->event->xbutton.button == 2) {
1604 core->panning = gFalse;
1605 } else {
1606 if (*core->mouseCbk) {
1607 (*core->mouseCbk)(core->mouseCbkData, data->event);
1608 }
1609 }
1610 break;
1611 case MotionNotify:
1612 if (core->doc && core->doc->getNumPages() > 0) {
1613 mx = core->scrollX + data->event->xbutton.x;
1614 my = core->scrollY + data->event->xbutton.y;
1615 if (core->dragging) {
1616 core->moveSelection(mx, my);
1617 } else if (core->hyperlinksEnabled) {
1618 core->out->cvtDevToUser(mx, my, &x, &y);
1619 if ((action = core->doc->findLink(x, y))) {
1620 core->setCursor(core->linkCursor);
1621 if (action != core->linkAction) {
1622 core->linkAction = action;
1623 if (core->updateCbk) {
1624 s = "";
1625 switch (action->getKind()) {
1626 case actionGoTo:
1627 s = "[internal link]";
1628 break;
1629 case actionGoToR:
1630 s = ((LinkGoToR *)action)->getFileName()->getCString();
1631 break;
1632 case actionLaunch:
1633 s = ((LinkLaunch *)action)->getFileName()->getCString();
1634 break;
1635 case actionURI:
1636 s = ((LinkURI *)action)->getURI()->getCString();
1637 break;
1638 case actionNamed:
1639 s = ((LinkNamed *)action)->getName()->getCString();
1640 break;
1641 case actionMovie:
1642 s = "[movie]";
1643 break;
1644 case actionUnknown:
1645 s = "[unknown link]";
1646 break;
1647 }
1648 (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
1649 }
1650 }
1651 } else {
1652 core->setCursor(None);
1653 if (core->linkAction) {
1654 core->linkAction = NULL;
1655 if (core->updateCbk) {
1656 (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
1657 }
1658 }
1659 }
1660 }
1661 }
1662 if (core->panning) {
1663 core->scrollTo(core->scrollX - (data->event->xbutton.x - core->panMX),
1664 core->scrollY - (data->event->xbutton.y - core->panMY));
1665 core->panMX = data->event->xbutton.x;
1666 core->panMY = data->event->xbutton.y;
1667 }
1668 break;
1669 case KeyPress:
1670 n = XLookupString(&data->event->xkey, buf, sizeof(buf) - 1,
1671 &key, NULL);
1672 core->keyPress(buf, key, data->event->xkey.state);
1673 break;
1674 }
1675}
1676
1677void XPDFCore::keyPress(char *s, KeySym key, Guint modifiers) {
1678 switch (key) {
1679 case XK_Home:
1680 case XK_KP_Home:
1681 if (modifiers & ControlMask) {
1682 displayPage(1, zoom, rotate, gTrue, gTrue);
1683 } else if (!fullScreen) {
1684 scrollTo(0, 0);
1685 }
1686 return;
1687 case XK_End:
1688 case XK_KP_End:
1689 if (modifiers & ControlMask) {
1690 displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
1691 } else if (!fullScreen) {
1692 scrollTo(out->getPixmapWidth() - drawAreaWidth,
1693 out->getPixmapHeight() - drawAreaHeight);
1694 }
1695 return;
1696 case XK_Page_Up:
1697 case XK_KP_Page_Up:
1698 if (fullScreen) {
1699 gotoPrevPage(1, gTrue, gFalse);
1700 } else {
1701 scrollPageUp();
1702 }
1703 return;
1704 case XK_Page_Down:
1705 case XK_KP_Page_Down:
1706 if (fullScreen) {
1707 gotoNextPage(1, gTrue);
1708 } else {
1709 scrollPageDown();
1710 }
1711 return;
1712 case XK_Left:
1713 case XK_KP_Left:
1714 if (!fullScreen) {
1715 scrollLeft();
1716 }
1717 return;
1718 case XK_Right:
1719 case XK_KP_Right:
1720 if (!fullScreen) {
1721 scrollRight();
1722 }
1723 return;
1724 case XK_Up:
1725 case XK_KP_Up:
1726 if (!fullScreen) {
1727 scrollUp();
1728 }
1729 return;
1730 case XK_Down:
1731 case XK_KP_Down:
1732 if (!fullScreen) {
1733 scrollDown();
1734 }
1735 return;
1736 }
1737
1738 if (*keyPressCbk) {
1739 (*keyPressCbk)(keyPressCbkData, s, key, modifiers);
1740 }
1741}
1742
1743void XPDFCore::redrawRectangle(int x, int y, int w, int h) {
1744 XGCValues gcValues;
1745 Window drawAreaWin;
1746
1747 // clip to window
1748 if (x < scrollX) {
1749 w -= scrollX - x;
1750 x = scrollX;
1751 }
1752 if (x + w > scrollX + drawAreaWidth) {
1753 w = scrollX + drawAreaWidth - x;
1754 }
1755 if (y < scrollY) {
1756 h -= scrollY - y;
1757 y = scrollY;
1758 }
1759 if (y + h > scrollY + drawAreaHeight) {
1760 h = scrollY + drawAreaHeight - y;
1761 }
1762
1763 // create a GC for the drawing area
1764 drawAreaWin = XtWindow(drawArea);
1765 if (!drawAreaGC) {
1766 gcValues.foreground = paperColor;
1767 drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
1768 }
1769
1770 // draw white background past the edges of the document
1771 if (x + w > out->getPixmapWidth()) {
1772 XFillRectangle(display, drawAreaWin, drawAreaGC,
1773 out->getPixmapWidth() - scrollX, y - scrollY,
1774 x + w - out->getPixmapWidth(), h);
1775 w = out->getPixmapWidth() - x;
1776 }
1777 if (y + h > out->getPixmapHeight()) {
1778 XFillRectangle(display, drawAreaWin, drawAreaGC,
1779 x - scrollX, out->getPixmapHeight() - scrollY,
1780 w, y + h - out->getPixmapHeight());
1781 h = out->getPixmapHeight() - y;
1782 }
1783
1784 // redraw (checking to see if pixmap has been allocated yet)
1785 if (out->getPixmapWidth() > 0) {
1786 XCopyArea(display, out->getPixmap(), drawAreaWin, drawAreaGC,
1787 x, y, w, h, x - scrollX, y - scrollY);
1788 }
1789}
1790
1791void XPDFCore::updateScrollBars() {
1792 Arg args[20];
1793 int n;
1794 int maxPos;
1795
1796 maxPos = out ? out->getPixmapWidth() : 1;
1797 if (maxPos < drawAreaWidth) {
1798 maxPos = drawAreaWidth;
1799 }
1800 if (scrollX > maxPos - drawAreaWidth) {
1801 scrollX = maxPos - drawAreaWidth;
1802 }
1803 n = 0;
1804 XtSetArg(args[n], XmNvalue, scrollX); ++n;
1805 XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1806 XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
1807 XtSetArg(args[n], XmNincrement, 16); ++n;
1808 XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
1809 XtSetValues(hScrollBar, args, n);
1810
1811 maxPos = out ? out->getPixmapHeight() : 1;
1812 if (maxPos < drawAreaHeight) {
1813 maxPos = drawAreaHeight;
1814 }
1815 if (scrollY > maxPos - drawAreaHeight) {
1816 scrollY = maxPos - drawAreaHeight;
1817 }
1818 n = 0;
1819 XtSetArg(args[n], XmNvalue, scrollY); ++n;
1820 XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1821 XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
1822 XtSetArg(args[n], XmNincrement, 16); ++n;
1823 XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
1824 XtSetValues(vScrollBar, args, n);
1825}
1826
1827void XPDFCore::setCursor(Cursor cursor) {
1828 Window topWin;
1829
1830 if (cursor == currentCursor) {
1831 return;
1832 }
1833 if (!(topWin = XtWindow(shell))) {
1834 return;
1835 }
1836 if (cursor == None) {
1837 XUndefineCursor(display, topWin);
1838 } else {
1839 XDefineCursor(display, topWin, cursor);
1840 }
1841 XFlush(display);
1842 currentCursor = cursor;
1843}
1844
1845GBool XPDFCore::doQuestionDialog(char *title, GString *msg) {
1846 return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
1847}
1848
1849void XPDFCore::doInfoDialog(char *title, GString *msg) {
1850 doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
1851}
1852
1853void XPDFCore::doErrorDialog(char *title, GString *msg) {
1854 doDialog(XmDIALOG_ERROR, gFalse, title, msg);
1855}
1856
1857GBool XPDFCore::doDialog(int type, GBool hasCancel,
1858 char *title, GString *msg) {
1859 Widget dialog;
1860 XtAppContext appContext;
1861 Arg args[20];
1862 int n;
1863 XmString s1, s2;
1864 XEvent event;
1865
1866 n = 0;
1867 XtSetArg(args[n], XmNdialogType, type); ++n;
1868 XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1869 s1 = XmStringCreateLocalized(title);
1870 XtSetArg(args[n], XmNdialogTitle, s1); ++n;
1871 s2 = XmStringCreateLocalized(msg->getCString());
1872 XtSetArg(args[n], XmNmessageString, s2); ++n;
1873 dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
1874 XmStringFree(s1);
1875 XmStringFree(s2);
1876 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
1877 XtAddCallback(dialog, XmNokCallback,
1878 &dialogOkCbk, (XtPointer)this);
1879 if (hasCancel) {
1880 XtAddCallback(dialog, XmNcancelCallback,
1881 &dialogCancelCbk, (XtPointer)this);
1882 } else {
1883 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
1884 }
1885
1886 XtManageChild(dialog);
1887
1888 appContext = XtWidgetToApplicationContext(dialog);
1889 dialogDone = 0;
1890 do {
1891 XtAppNextEvent(appContext, &event);
1892 XtDispatchEvent(&event);
1893 } while (!dialogDone);
1894
1895 XtUnmanageChild(dialog);
1896 XtDestroyWidget(dialog);
1897
1898 return dialogDone > 0;
1899}
1900
1901void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
1902 XtPointer callData) {
1903 XPDFCore *core = (XPDFCore *)ptr;
1904
1905 core->dialogDone = 1;
1906}
1907
1908void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
1909 XtPointer callData) {
1910 XPDFCore *core = (XPDFCore *)ptr;
1911
1912 core->dialogDone = -1;
1913}