1 //========================================================================
2 //
3 // XPDFCore.cc
4 //
5 // Copyright 2002-2003 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 <string.h>
18 #include "gmem.h"
19 #include "GString.h"
20 #include "GList.h"
21 #include "Error.h"
22 #include "GlobalParams.h"
23 #include "PDFDoc.h"
24 #include "Link.h"
25 #include "ErrorCodes.h"
26 #include "GfxState.h"
27 #include "CoreOutputDev.h"
28 #include "PSOutputDev.h"
29 #include "TextOutputDev.h"
30 #include "SplashBitmap.h"
31 #include "SplashPattern.h"
32 #include "XPDFApp.h"
33 #include "XPDFCore.h"
34
35 // these macro defns conflict with xpdf's Object class
36 #ifdef LESSTIF_VERSION
37 #undef XtDisplay
38 #undef XtScreen
39 #undef XtWindow
40 #undef XtParent
41 #undef XtIsRealized
42 #endif
43
44 //------------------------------------------------------------------------
45
46 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)47 static inline Guchar div255(int x) {
48 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
49 }
50
51 //------------------------------------------------------------------------
52
53 GString *XPDFCore::currentSelection = NULL;
54 XPDFCore *XPDFCore::currentSelectionOwner = NULL;
55 Atom XPDFCore::targetsAtom;
56
57 //------------------------------------------------------------------------
58 // XPDFCoreTile
59 //------------------------------------------------------------------------
60
61 class XPDFCoreTile: public PDFCoreTile {
62 public:
63 XPDFCoreTile(int xDestA, int yDestA);
64 virtual ~XPDFCoreTile();
65 XImage *image;
66 };
67
XPDFCoreTile(int xDestA,int yDestA)68 XPDFCoreTile::XPDFCoreTile(int xDestA, int yDestA):
69 PDFCoreTile(xDestA, yDestA)
70 {
71 image = NULL;
72 }
73
~XPDFCoreTile()74 XPDFCoreTile::~XPDFCoreTile() {
75 if (image) {
76 gfree(image->data);
77 image->data = NULL;
78 XDestroyImage(image);
79 }
80 }
81
82 //------------------------------------------------------------------------
83 // XPDFCore
84 //------------------------------------------------------------------------
85
XPDFCore(Widget shellA,Widget parentWidgetA,SplashColorPtr paperColorA,Gulong paperPixelA,Gulong mattePixelA,GBool fullScreenA,GBool reverseVideoA,GBool installCmap,int rgbCubeSizeA)86 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
87 SplashColorPtr paperColorA, Gulong paperPixelA,
88 Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA,
89 GBool installCmap, int rgbCubeSizeA):
90 PDFCore(splashModeRGB8, 4, reverseVideoA, paperColorA, !fullScreenA)
91 {
92 GString *initialZoom;
93
94 shell = shellA;
95 parentWidget = parentWidgetA;
96 display = XtDisplay(parentWidget);
97 screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
98 targetsAtom = XInternAtom(display, "TARGETS", False);
99
100 paperPixel = paperPixelA;
101 mattePixel = mattePixelA;
102 fullScreen = fullScreenA;
103
104 setupX(installCmap, rgbCubeSizeA);
105
106 scrolledWin = NULL;
107 hScrollBar = NULL;
108 vScrollBar = NULL;
109 drawAreaFrame = NULL;
110 drawArea = NULL;
111
112 // get the initial zoom value
113 if (fullScreen) {
114 zoom = zoomPage;
115 } else {
116 initialZoom = globalParams->getInitialZoom();
117 if (!initialZoom->cmp("page")) {
118 zoom = zoomPage;
119 } else if (!initialZoom->cmp("width")) {
120 zoom = zoomWidth;
121 } else {
122 zoom = atoi(initialZoom->getCString());
123 if (zoom <= 0) {
124 zoom = defZoom;
125 }
126 }
127 delete initialZoom;
128 }
129
130 linkAction = NULL;
131
132 panning = gFalse;
133
134 updateCbk = NULL;
135 actionCbk = NULL;
136 keyPressCbk = NULL;
137 mouseCbk = NULL;
138
139 // optional features default to on
140 hyperlinksEnabled = gTrue;
141 selectEnabled = gTrue;
142
143 // do X-specific initialization and create the widgets
144 initWindow();
145 initPasswordDialog();
146 }
147
~XPDFCore()148 XPDFCore::~XPDFCore() {
149 if (currentSelectionOwner == this && currentSelection) {
150 delete currentSelection;
151 currentSelection = NULL;
152 currentSelectionOwner = NULL;
153 }
154 if (drawAreaGC) {
155 XFreeGC(display, drawAreaGC);
156 }
157 if (scrolledWin) {
158 XtDestroyWidget(scrolledWin);
159 }
160 if (busyCursor) {
161 XFreeCursor(display, busyCursor);
162 }
163 if (linkCursor) {
164 XFreeCursor(display, linkCursor);
165 }
166 if (selectCursor) {
167 XFreeCursor(display, selectCursor);
168 }
169 }
170
171 //------------------------------------------------------------------------
172 // loadFile / displayPage / displayDest
173 //------------------------------------------------------------------------
174
loadFile(GString * fileName,GString * ownerPassword,GString * userPassword)175 int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
176 GString *userPassword) {
177 int err;
178
179 err = PDFCore::loadFile(fileName, ownerPassword, userPassword);
180 if (err == errNone) {
181 // save the modification time
182 modTime = getModTime(doc->getFileName()->getCString());
183
184 // update the parent window
185 if (updateCbk) {
186 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
187 doc->getNumPages(), NULL);
188 }
189 }
190 return err;
191 }
192
loadFile(BaseStream * stream,GString * ownerPassword,GString * userPassword)193 int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
194 GString *userPassword) {
195 int err;
196
197 err = PDFCore::loadFile(stream, ownerPassword, userPassword);
198 if (err == errNone) {
199 // no file
200 modTime = 0;
201
202 // update the parent window
203 if (updateCbk) {
204 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
205 doc->getNumPages(), NULL);
206 }
207 }
208 return err;
209 }
210
loadDoc(PDFDoc * docA)211 void XPDFCore::loadDoc(PDFDoc *docA) {
212 PDFCore::loadDoc(docA);
213
214 // save the modification time
215 if (doc->getFileName()) {
216 modTime = getModTime(doc->getFileName()->getCString());
217 }
218
219 // update the parent window
220 if (updateCbk) {
221 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
222 doc->getNumPages(), NULL);
223 }
224 }
225
resizeToPage(int pg)226 void XPDFCore::resizeToPage(int pg) {
227 Dimension width, height;
228 double width1, height1;
229 Dimension topW, topH, topBorder, daW, daH;
230 Dimension displayW, displayH;
231
232 displayW = DisplayWidth(display, screenNum);
233 displayH = DisplayHeight(display, screenNum);
234 if (fullScreen) {
235 width = displayW;
236 height = displayH;
237 } else {
238 if (!doc || pg <= 0 || pg > doc->getNumPages()) {
239 width1 = 612;
240 height1 = 792;
241 } else if (doc->getPageRotate(pg) == 90 ||
242 doc->getPageRotate(pg) == 270) {
243 width1 = doc->getPageCropHeight(pg);
244 height1 = doc->getPageCropWidth(pg);
245 } else {
246 width1 = doc->getPageCropWidth(pg);
247 height1 = doc->getPageCropHeight(pg);
248 }
249 if (zoom == zoomPage || zoom == zoomWidth) {
250 width = (Dimension)(width1 * 0.01 * defZoom + 0.5);
251 height = (Dimension)(height1 * 0.01 * defZoom + 0.5);
252 } else {
253 width = (Dimension)(width1 * 0.01 * zoom + 0.5);
254 height = (Dimension)(height1 * 0.01 * zoom + 0.5);
255 }
256 if (continuousMode) {
257 height += continuousModePageSpacing;
258 }
259 if (width > displayW - 100) {
260 width = displayW - 100;
261 }
262 if (height > displayH - 100) {
263 height = displayH - 100;
264 }
265 }
266
267 if (XtIsRealized(shell)) {
268 XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
269 XmNborderWidth, &topBorder, NULL);
270 XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
271 XtVaSetValues(shell, XmNwidth, width + (topW - daW),
272 XmNheight, height + (topH - daH), NULL);
273 } else {
274 XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
275 }
276 }
277
update(int topPageA,int scrollXA,int scrollYA,double zoomA,int rotateA,GBool force,GBool addToHist,GBool adjustScrollX)278 void XPDFCore::update(int topPageA, int scrollXA, int scrollYA,
279 double zoomA, int rotateA, GBool force,
280 GBool addToHist, GBool adjustScrollX) {
281 int oldPage;
282
283 oldPage = topPage;
284 PDFCore::update(topPageA, scrollXA, scrollYA, zoomA, rotateA,
285 force, addToHist, adjustScrollX);
286 linkAction = NULL;
287 if (doc && topPage != oldPage) {
288 if (updateCbk) {
289 (*updateCbk)(updateCbkData, NULL, topPage, -1, "");
290 }
291 }
292 }
293
checkForNewFile()294 GBool XPDFCore::checkForNewFile() {
295 time_t newModTime;
296
297 if (doc->getFileName()) {
298 newModTime = getModTime(doc->getFileName()->getCString());
299 if (newModTime != modTime) {
300 modTime = newModTime;
301 return gTrue;
302 }
303 }
304 return gFalse;
305 }
306
307 //------------------------------------------------------------------------
308 // page/position changes
309 //------------------------------------------------------------------------
310
gotoNextPage(int inc,GBool top)311 GBool XPDFCore::gotoNextPage(int inc, GBool top) {
312 if (!PDFCore::gotoNextPage(inc, top)) {
313 XBell(display, 0);
314 return gFalse;
315 }
316 return gTrue;
317 }
318
gotoPrevPage(int dec,GBool top,GBool bottom)319 GBool XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
320 if (!PDFCore::gotoPrevPage(dec, top, bottom)) {
321 XBell(display, 0);
322 return gFalse;
323 }
324 return gTrue;
325 }
326
goForward()327 GBool XPDFCore::goForward() {
328 if (!PDFCore::goForward()) {
329 XBell(display, 0);
330 return gFalse;
331 }
332 return gTrue;
333 }
334
goBackward()335 GBool XPDFCore::goBackward() {
336 if (!PDFCore::goBackward()) {
337 XBell(display, 0);
338 return gFalse;
339 }
340 return gTrue;
341 }
342
startPan(int wx,int wy)343 void XPDFCore::startPan(int wx, int wy) {
344 panning = gTrue;
345 panMX = wx;
346 panMY = wy;
347 }
348
endPan(int wx,int wy)349 void XPDFCore::endPan(int wx, int wy) {
350 panning = gFalse;
351 }
352
353 //------------------------------------------------------------------------
354 // selection
355 //------------------------------------------------------------------------
356
startSelection(int wx,int wy)357 void XPDFCore::startSelection(int wx, int wy) {
358 int pg, x, y;
359
360 takeFocus();
361 if (doc && doc->getNumPages() > 0) {
362 if (selectEnabled) {
363 if (cvtWindowToDev(wx, wy, &pg, &x, &y)) {
364 setSelection(pg, x, y, x, y);
365 setCursor(selectCursor);
366 dragging = gTrue;
367 }
368 }
369 }
370 }
371
endSelection(int wx,int wy)372 void XPDFCore::endSelection(int wx, int wy) {
373 int pg, x, y;
374 GBool ok;
375
376 if (doc && doc->getNumPages() > 0) {
377 ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
378 if (dragging) {
379 dragging = gFalse;
380 setCursor(None);
381 if (ok) {
382 moveSelection(pg, x, y);
383 }
384 #ifndef NO_TEXT_SELECT
385 if (selectULX != selectLRX &&
386 selectULY != selectLRY) {
387 if (doc->okToCopy()) {
388 copySelection();
389 } else {
390 error(errNotAllowed, -1,
391 "Copying of text from this document is not allowed.");
392 }
393 }
394 #endif
395 }
396 }
397 }
398
399 // X's copy-and-paste mechanism is brain damaged. Xt doesn't help
400 // any, but doesn't make it too much worse, either. Motif, on the
401 // other hand, adds significant complexity to the mess. So here we
402 // blow off the Motif junk and stick to plain old Xt. The next two
403 // functions (copySelection and convertSelectionCbk) implement the
404 // magic needed to deal with Xt's mechanism. Note that this requires
405 // global variables (currentSelection and currentSelectionOwner).
406
copySelection()407 void XPDFCore::copySelection() {
408 int pg;
409 double ulx, uly, lrx, lry;
410
411 if (!doc->okToCopy()) {
412 return;
413 }
414 if (getSelection(&pg, &ulx, &uly, &lrx, &lry)) {
415 //~ for multithreading: need a mutex here
416 if (currentSelection) {
417 delete currentSelection;
418 }
419 currentSelection = extractText(pg, ulx, uly, lrx, lry);
420 currentSelectionOwner = this;
421 XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
422 &convertSelectionCbk, NULL, NULL);
423 }
424 }
425
convertSelectionCbk(Widget widget,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)426 Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
427 Atom *target, Atom *type,
428 XtPointer *value, unsigned long *length,
429 int *format) {
430 Atom *array;
431
432 // send back a list of supported conversion targets
433 if (*target == targetsAtom) {
434 if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) {
435 return False;
436 }
437 array[0] = XA_STRING;
438 *value = (XtPointer)array;
439 *type = XA_ATOM;
440 *format = 32;
441 *length = 1;
442 return True;
443
444 // send the selected text
445 } else if (*target == XA_STRING) {
446 //~ for multithreading: need a mutex here
447 *value = XtNewString(currentSelection->getCString());
448 *length = currentSelection->getLength();
449 *type = XA_STRING;
450 *format = 8; // 8-bit elements
451 return True;
452 }
453
454 return False;
455 }
456
457 //------------------------------------------------------------------------
458 // hyperlinks
459 //------------------------------------------------------------------------
460
doAction(LinkAction * action)461 void XPDFCore::doAction(LinkAction *action) {
462 LinkActionKind kind;
463 LinkDest *dest;
464 GString *namedDest;
465 char *s;
466 GString *fileName, *fileName2;
467 GString *cmd;
468 GString *actionName;
469 Object movieAnnot, obj1, obj2;
470 GString *msg;
471 int i;
472
473 switch (kind = action->getKind()) {
474
475 // GoTo / GoToR action
476 case actionGoTo:
477 case actionGoToR:
478 if (kind == actionGoTo) {
479 dest = NULL;
480 namedDest = NULL;
481 if ((dest = ((LinkGoTo *)action)->getDest())) {
482 dest = dest->copy();
483 } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
484 namedDest = namedDest->copy();
485 }
486 } else {
487 dest = NULL;
488 namedDest = NULL;
489 if ((dest = ((LinkGoToR *)action)->getDest())) {
490 dest = dest->copy();
491 } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
492 namedDest = namedDest->copy();
493 }
494 s = ((LinkGoToR *)action)->getFileName()->getCString();
495 //~ translate path name for VMS (deal with '/')
496 if (isAbsolutePath(s) || !doc->getFileName()) {
497 fileName = new GString(s);
498 } else {
499 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
500 }
501 if (loadFile(fileName) != errNone) {
502 if (dest) {
503 delete dest;
504 }
505 if (namedDest) {
506 delete namedDest;
507 }
508 delete fileName;
509 return;
510 }
511 delete fileName;
512 }
513 if (namedDest) {
514 dest = doc->findDest(namedDest);
515 delete namedDest;
516 }
517 if (dest) {
518 displayDest(dest, zoom, rotate, gTrue);
519 delete dest;
520 } else {
521 if (kind == actionGoToR) {
522 displayPage(1, zoom, 0, gFalse, gTrue);
523 }
524 }
525 break;
526
527 // Launch action
528 case actionLaunch:
529 fileName = ((LinkLaunch *)action)->getFileName();
530 s = fileName->getCString();
531 if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
532 !strcmp(s + fileName->getLength() - 4, ".PDF")) {
533 //~ translate path name for VMS (deal with '/')
534 if (isAbsolutePath(s) || !doc->getFileName()) {
535 fileName = fileName->copy();
536 } else {
537 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
538 }
539 if (loadFile(fileName) != errNone) {
540 delete fileName;
541 return;
542 }
543 delete fileName;
544 displayPage(1, zoom, rotate, gFalse, gTrue);
545 } else {
546 fileName = fileName->copy();
547 if (((LinkLaunch *)action)->getParams()) {
548 fileName->append(' ');
549 fileName->append(((LinkLaunch *)action)->getParams());
550 }
551 #ifdef VMS
552 fileName->insert(0, "spawn/nowait ");
553 #elif defined(__EMX__)
554 fileName->insert(0, "start /min /n ");
555 #else
556 fileName->append(" &");
557 #endif
558 if (globalParams->getLaunchCommand()) {
559 fileName->insert(0, ' ');
560 fileName->insert(0, globalParams->getLaunchCommand());
561 system(fileName->getCString());
562 } else {
563 msg = new GString("About to execute the command:\n");
564 msg->append(fileName);
565 if (doQuestionDialog("Launching external application", msg)) {
566 system(fileName->getCString());
567 }
568 delete msg;
569 }
570 delete fileName;
571 }
572 break;
573
574 // URI action
575 case actionURI:
576 if (!(cmd = globalParams->getURLCommand())) {
577 error(errConfig, -1, "No urlCommand defined in config file");
578 break;
579 }
580 runCommand(cmd, ((LinkURI *)action)->getURI());
581 break;
582
583 // Named action
584 case actionNamed:
585 actionName = ((LinkNamed *)action)->getName();
586 if (!actionName->cmp("NextPage")) {
587 gotoNextPage(1, gTrue);
588 } else if (!actionName->cmp("PrevPage")) {
589 gotoPrevPage(1, gTrue, gFalse);
590 } else if (!actionName->cmp("FirstPage")) {
591 if (topPage != 1) {
592 displayPage(1, zoom, rotate, gTrue, gTrue);
593 }
594 } else if (!actionName->cmp("LastPage")) {
595 if (topPage != doc->getNumPages()) {
596 displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
597 }
598 } else if (!actionName->cmp("GoBack")) {
599 goBackward();
600 } else if (!actionName->cmp("GoForward")) {
601 goForward();
602 } else if (!actionName->cmp("Quit")) {
603 if (actionCbk) {
604 (*actionCbk)(actionCbkData, actionName->getCString());
605 }
606 } else {
607 error(errSyntaxError, -1,
608 "Unknown named action: '{0:t}'", actionName);
609 }
610 break;
611
612 // Movie action
613 case actionMovie:
614 if (!(cmd = globalParams->getMovieCommand())) {
615 error(errConfig, -1, "No movieCommand defined in config file");
616 break;
617 }
618 if (((LinkMovie *)action)->hasAnnotRef()) {
619 doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
620 ((LinkMovie *)action)->getAnnotRef()->gen,
621 &movieAnnot);
622 } else {
623 //~ need to use the correct page num here
624 doc->getCatalog()->getPage(topPage)->getAnnots(&obj1);
625 if (obj1.isArray()) {
626 for (i = 0; i < obj1.arrayGetLength(); ++i) {
627 if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
628 if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
629 obj2.free();
630 break;
631 }
632 obj2.free();
633 }
634 movieAnnot.free();
635 }
636 obj1.free();
637 }
638 }
639 if (movieAnnot.isDict()) {
640 if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
641 if (obj1.dictLookup("F", &obj2)) {
642 if ((fileName = LinkAction::getFileSpecName(&obj2))) {
643 if (!isAbsolutePath(fileName->getCString()) &&
644 doc->getFileName()) {
645 fileName2 = appendToPath(
646 grabPath(doc->getFileName()->getCString()),
647 fileName->getCString());
648 delete fileName;
649 fileName = fileName2;
650 }
651 runCommand(cmd, fileName);
652 delete fileName;
653 }
654 obj2.free();
655 }
656 obj1.free();
657 }
658 }
659 movieAnnot.free();
660 break;
661
662 // unsupported action types
663 case actionJavaScript:
664 case actionSubmitForm:
665 case actionHide:
666 error(errSyntaxError, -1, "Unsupported link action type");
667 break;
668
669 // unknown action type
670 case actionUnknown:
671 error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
672 ((LinkUnknown *)action)->getAction());
673 break;
674 }
675 }
676
677 // Run a command, given a <cmdFmt> string with one '%s' in it, and an
678 // <arg> string to insert in place of the '%s'.
runCommand(GString * cmdFmt,GString * arg)679 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
680 GString *cmd;
681 char *s;
682
683 if ((s = strstr(cmdFmt->getCString(), "%s"))) {
684 cmd = mungeURL(arg);
685 cmd->insert(0, cmdFmt->getCString(),
686 s - cmdFmt->getCString());
687 cmd->append(s + 2);
688 } else {
689 cmd = cmdFmt->copy();
690 }
691 #ifdef VMS
692 cmd->insert(0, "spawn/nowait ");
693 #elif defined(__EMX__)
694 cmd->insert(0, "start /min /n ");
695 #else
696 cmd->append(" &");
697 #endif
698 system(cmd->getCString());
699 delete cmd;
700 }
701
702 // Escape any characters in a URL which might cause problems when
703 // calling system().
mungeURL(GString * url)704 GString *XPDFCore::mungeURL(GString *url) {
705 static const char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
706 "abcdefghijklmnopqrstuvwxyz"
707 "0123456789"
708 "-_.~/?:@&=+,#%";
709 GString *newURL;
710 char c;
711 char buf[4];
712 int i;
713
714 newURL = new GString();
715 for (i = 0; i < url->getLength(); ++i) {
716 c = url->getChar(i);
717 if (strchr(allowed, c)) {
718 newURL->append(c);
719 } else {
720 sprintf(buf, "%%%02x", c & 0xff);
721 newURL->append(buf);
722 }
723 }
724 return newURL;
725 }
726
727 //------------------------------------------------------------------------
728 // find
729 //------------------------------------------------------------------------
730
find(char * s,GBool caseSensitive,GBool next,GBool backward,GBool wholeWord,GBool onePageOnly)731 GBool XPDFCore::find(char *s, GBool caseSensitive, GBool next,
732 GBool backward, GBool wholeWord, GBool onePageOnly) {
733 if (!PDFCore::find(s, caseSensitive, next,
734 backward, wholeWord, onePageOnly)) {
735 XBell(display, 0);
736 return gFalse;
737 }
738 #ifndef NO_TEXT_SELECT
739 copySelection();
740 #endif
741 return gTrue;
742 }
743
findU(Unicode * u,int len,GBool caseSensitive,GBool next,GBool backward,GBool wholeWord,GBool onePageOnly)744 GBool XPDFCore::findU(Unicode *u, int len, GBool caseSensitive,
745 GBool next, GBool backward,
746 GBool wholeWord, GBool onePageOnly) {
747 if (!PDFCore::findU(u, len, caseSensitive, next,
748 backward, wholeWord, onePageOnly)) {
749 XBell(display, 0);
750 return gFalse;
751 }
752 #ifndef NO_TEXT_SELECT
753 copySelection();
754 #endif
755 return gTrue;
756 }
757
758 //------------------------------------------------------------------------
759 // misc access
760 //------------------------------------------------------------------------
761
setBusyCursor(GBool busy)762 void XPDFCore::setBusyCursor(GBool busy) {
763 setCursor(busy ? busyCursor : None);
764 }
765
takeFocus()766 void XPDFCore::takeFocus() {
767 XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
768 }
769
770 //------------------------------------------------------------------------
771 // GUI code
772 //------------------------------------------------------------------------
773
setupX(GBool installCmap,int rgbCubeSizeA)774 void XPDFCore::setupX(GBool installCmap, int rgbCubeSizeA) {
775 XVisualInfo visualTempl;
776 XVisualInfo *visualList;
777 Gulong mask;
778 int nVisuals;
779 XColor xcolor;
780 XColor *xcolors;
781 int r, g, b, n, m;
782 GBool ok;
783
784 // for some reason, querying XmNvisual doesn't work (even if done
785 // after the window is mapped)
786 visual = DefaultVisual(display, screenNum);
787 XtVaGetValues(shell, XmNcolormap, &colormap, NULL);
788
789 // check for TrueColor visual
790 //~ this should scan the list, not just look at the first one
791 visualTempl.visualid = XVisualIDFromVisual(visual);
792 visualList = XGetVisualInfo(display, VisualIDMask,
793 &visualTempl, &nVisuals);
794 if (nVisuals < 1) {
795 // this shouldn't happen
796 XFree((XPointer)visualList);
797 visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
798 &nVisuals);
799 }
800 depth = visualList->depth;
801 if (visualList->c_class == TrueColor) {
802 trueColor = gTrue;
803 for (mask = visualList->red_mask, rShift = 0;
804 mask && !(mask & 1);
805 mask >>= 1, ++rShift) ;
806 for (rDiv = 8; mask; mask >>= 1, --rDiv) ;
807 for (mask = visualList->green_mask, gShift = 0;
808 mask && !(mask & 1);
809 mask >>= 1, ++gShift) ;
810 for (gDiv = 8; mask; mask >>= 1, --gDiv) ;
811 for (mask = visualList->blue_mask, bShift = 0;
812 mask && !(mask & 1);
813 mask >>= 1, ++bShift) ;
814 for (bDiv = 8; mask; mask >>= 1, --bDiv) ;
815 } else {
816 trueColor = gFalse;
817 }
818 XFree((XPointer)visualList);
819
820 // allocate a color cube
821 if (!trueColor) {
822
823 // set colors in private colormap
824 if (installCmap) {
825 for (rgbCubeSize = xMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) {
826 m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
827 if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
828 break;
829 }
830 }
831 if (rgbCubeSize >= 2) {
832 m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
833 xcolors = (XColor *)gmallocn(m, sizeof(XColor));
834 n = 0;
835 for (r = 0; r < rgbCubeSize; ++r) {
836 for (g = 0; g < rgbCubeSize; ++g) {
837 for (b = 0; b < rgbCubeSize; ++b) {
838 xcolors[n].pixel = colors[n];
839 xcolors[n].red = (r * 65535) / (rgbCubeSize - 1);
840 xcolors[n].green = (g * 65535) / (rgbCubeSize - 1);
841 xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1);
842 xcolors[n].flags = DoRed | DoGreen | DoBlue;
843 ++n;
844 }
845 }
846 }
847 XStoreColors(display, colormap, xcolors, m);
848 gfree(xcolors);
849 } else {
850 rgbCubeSize = 1;
851 colors[0] = BlackPixel(display, screenNum);
852 colors[1] = WhitePixel(display, screenNum);
853 }
854
855 // allocate colors in shared colormap
856 } else {
857 if (rgbCubeSize > xMaxRGBCube) {
858 rgbCubeSize = xMaxRGBCube;
859 }
860 ok = gFalse;
861 for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) {
862 ok = gTrue;
863 n = 0;
864 for (r = 0; r < rgbCubeSize && ok; ++r) {
865 for (g = 0; g < rgbCubeSize && ok; ++g) {
866 for (b = 0; b < rgbCubeSize && ok; ++b) {
867 if (n == 0) {
868 colors[n] = BlackPixel(display, screenNum);
869 ++n;
870 } else {
871 xcolor.red = (r * 65535) / (rgbCubeSize - 1);
872 xcolor.green = (g * 65535) / (rgbCubeSize - 1);
873 xcolor.blue = (b * 65535) / (rgbCubeSize - 1);
874 if (XAllocColor(display, colormap, &xcolor)) {
875 colors[n++] = xcolor.pixel;
876 } else {
877 ok = gFalse;
878 }
879 }
880 }
881 }
882 }
883 if (ok) {
884 break;
885 }
886 XFreeColors(display, colormap, &colors[1], n-1, 0);
887 }
888 if (!ok) {
889 rgbCubeSize = 1;
890 colors[0] = BlackPixel(display, screenNum);
891 colors[1] = WhitePixel(display, screenNum);
892 }
893 }
894 }
895 }
896
initWindow()897 void XPDFCore::initWindow() {
898 Arg args[20];
899 int n;
900
901 // create the cursors
902 busyCursor = XCreateFontCursor(display, XC_watch);
903 linkCursor = XCreateFontCursor(display, XC_hand2);
904 selectCursor = XCreateFontCursor(display, XC_cross);
905 currentCursor = 0;
906
907 // create the scrolled window and scrollbars
908 n = 0;
909 XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
910 XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
911 scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
912 XtManageChild(scrolledWin);
913 n = 0;
914 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
915 XtSetArg(args[n], XmNminimum, 0); ++n;
916 XtSetArg(args[n], XmNmaximum, 1); ++n;
917 XtSetArg(args[n], XmNsliderSize, 1); ++n;
918 XtSetArg(args[n], XmNvalue, 0); ++n;
919 XtSetArg(args[n], XmNincrement, 1); ++n;
920 XtSetArg(args[n], XmNpageIncrement, 1); ++n;
921 hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
922 if (!fullScreen) {
923 XtManageChild(hScrollBar);
924 }
925 XtAddCallback(hScrollBar, XmNvalueChangedCallback,
926 &hScrollChangeCbk, (XtPointer)this);
927 #ifndef DISABLE_SMOOTH_SCROLL
928 XtAddCallback(hScrollBar, XmNdragCallback,
929 &hScrollDragCbk, (XtPointer)this);
930 #endif
931 n = 0;
932 XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
933 XtSetArg(args[n], XmNminimum, 0); ++n;
934 XtSetArg(args[n], XmNmaximum, 1); ++n;
935 XtSetArg(args[n], XmNsliderSize, 1); ++n;
936 XtSetArg(args[n], XmNvalue, 0); ++n;
937 XtSetArg(args[n], XmNincrement, 1); ++n;
938 XtSetArg(args[n], XmNpageIncrement, 1); ++n;
939 vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
940 if (!fullScreen) {
941 XtManageChild(vScrollBar);
942 }
943 XtAddCallback(vScrollBar, XmNvalueChangedCallback,
944 &vScrollChangeCbk, (XtPointer)this);
945 #ifndef DISABLE_SMOOTH_SCROLL
946 XtAddCallback(vScrollBar, XmNdragCallback,
947 &vScrollDragCbk, (XtPointer)this);
948 #endif
949
950 // create the drawing area
951 n = 0;
952 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
953 XtSetArg(args[n], XmNmarginWidth, 0); ++n;
954 XtSetArg(args[n], XmNmarginHeight, 0); ++n;
955 if (fullScreen) {
956 XtSetArg(args[n], XmNshadowThickness, 0); ++n;
957 }
958 drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
959 XtManageChild(drawAreaFrame);
960 n = 0;
961 XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
962 XtSetArg(args[n], XmNwidth, 700); ++n;
963 XtSetArg(args[n], XmNheight, 500); ++n;
964 drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
965 XtManageChild(drawArea);
966 XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
967 XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
968 XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
969 resizeCbk(drawArea, this, NULL);
970
971 // set up mouse motion translations
972 XtOverrideTranslations(drawArea, XtParseTranslationTable(
973 "<Btn1Down>:DrawingAreaInput()\n"
974 "<Btn1Up>:DrawingAreaInput()\n"
975 "<Btn1Motion>:DrawingAreaInput()\n"
976 "<Motion>:DrawingAreaInput()"));
977
978 // can't create a GC until the window gets mapped
979 drawAreaGC = NULL;
980 }
981
hScrollChangeCbk(Widget widget,XtPointer ptr,XtPointer callData)982 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
983 XtPointer callData) {
984 XPDFCore *core = (XPDFCore *)ptr;
985 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
986
987 core->scrollTo(data->value, core->scrollY);
988 }
989
hScrollDragCbk(Widget widget,XtPointer ptr,XtPointer callData)990 void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
991 XtPointer callData) {
992 XPDFCore *core = (XPDFCore *)ptr;
993 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
994
995 core->scrollTo(data->value, core->scrollY);
996 }
997
vScrollChangeCbk(Widget widget,XtPointer ptr,XtPointer callData)998 void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
999 XtPointer callData) {
1000 XPDFCore *core = (XPDFCore *)ptr;
1001 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1002
1003 core->scrollTo(core->scrollX, data->value);
1004 }
1005
vScrollDragCbk(Widget widget,XtPointer ptr,XtPointer callData)1006 void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
1007 XtPointer callData) {
1008 XPDFCore *core = (XPDFCore *)ptr;
1009 XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1010
1011 core->scrollTo(core->scrollX, data->value);
1012 }
1013
resizeCbk(Widget widget,XtPointer ptr,XtPointer callData)1014 void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1015 XPDFCore *core = (XPDFCore *)ptr;
1016 XEvent event;
1017 Widget top;
1018 Window rootWin;
1019 int x1, y1;
1020 Guint w1, h1, bw1, depth1;
1021 Arg args[2];
1022 int n;
1023 Dimension w, h;
1024 int sx, sy;
1025
1026 // find the top-most widget which has an associated window, and look
1027 // for a pending ConfigureNotify in the event queue -- if there is
1028 // one, and it specifies a different width or height, that means
1029 // we're still resizing, and we want to skip the current event
1030 for (top = core->parentWidget;
1031 XtParent(top) && XtWindow(XtParent(top));
1032 top = XtParent(top)) ;
1033 if (XCheckTypedWindowEvent(core->display, XtWindow(top),
1034 ConfigureNotify, &event)) {
1035 XPutBackEvent(core->display, &event);
1036 XGetGeometry(core->display, event.xconfigure.window,
1037 &rootWin, &x1, &y1, &w1, &h1, &bw1, &depth1);
1038 if ((Guint)event.xconfigure.width != w1 ||
1039 (Guint)event.xconfigure.height != h1) {
1040 return;
1041 }
1042 }
1043
1044 n = 0;
1045 XtSetArg(args[n], XmNwidth, &w); ++n;
1046 XtSetArg(args[n], XmNheight, &h); ++n;
1047 XtGetValues(core->drawArea, args, n);
1048 core->drawAreaWidth = (int)w;
1049 core->drawAreaHeight = (int)h;
1050 if (core->zoom == zoomPage || core->zoom == zoomWidth) {
1051 sx = sy = -1;
1052 } else {
1053 sx = core->scrollX;
1054 sy = core->scrollY;
1055 }
1056 core->update(core->topPage, sx, sy, core->zoom, core->rotate, gTrue, gFalse,
1057 gFalse);
1058 }
1059
redrawCbk(Widget widget,XtPointer ptr,XtPointer callData)1060 void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1061 XPDFCore *core = (XPDFCore *)ptr;
1062 XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1063 int x, y, w, h;
1064
1065 if (data->reason == XmCR_EXPOSE) {
1066 x = data->event->xexpose.x;
1067 y = data->event->xexpose.y;
1068 w = data->event->xexpose.width;
1069 h = data->event->xexpose.height;
1070 } else {
1071 x = 0;
1072 y = 0;
1073 w = core->drawAreaWidth;
1074 h = core->drawAreaHeight;
1075 }
1076 core->redrawWindow(x, y, w, h, gFalse);
1077 }
1078
inputCbk(Widget widget,XtPointer ptr,XtPointer callData)1079 void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1080 XPDFCore *core = (XPDFCore *)ptr;
1081 XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1082 LinkAction *action;
1083 int pg, x, y;
1084 double xu, yu;
1085 const char *s;
1086 KeySym key;
1087 GBool ok;
1088
1089 switch (data->event->type) {
1090 case ButtonPress:
1091 if (*core->mouseCbk) {
1092 (*core->mouseCbk)(core->mouseCbkData, data->event);
1093 }
1094 break;
1095 case ButtonRelease:
1096 if (*core->mouseCbk) {
1097 (*core->mouseCbk)(core->mouseCbkData, data->event);
1098 }
1099 break;
1100 case MotionNotify:
1101 if (core->doc && core->doc->getNumPages() > 0) {
1102 ok = core->cvtWindowToDev(data->event->xmotion.x, data->event->xmotion.y,
1103 &pg, &x, &y);
1104 if (core->dragging) {
1105 if (ok) {
1106 core->moveSelection(pg, x, y);
1107 }
1108 } else if (core->hyperlinksEnabled) {
1109 core->cvtDevToUser(pg, x, y, &xu, &yu);
1110 if (ok && (action = core->findLink(pg, xu, yu))) {
1111 core->setCursor(core->linkCursor);
1112 if (action != core->linkAction) {
1113 core->linkAction = action;
1114 if (core->updateCbk) {
1115 s = "";
1116 switch (action->getKind()) {
1117 case actionGoTo:
1118 s = "[internal link]";
1119 break;
1120 case actionGoToR:
1121 s = ((LinkGoToR *)action)->getFileName()->getCString();
1122 break;
1123 case actionLaunch:
1124 s = ((LinkLaunch *)action)->getFileName()->getCString();
1125 break;
1126 case actionURI:
1127 s = ((LinkURI *)action)->getURI()->getCString();
1128 break;
1129 case actionNamed:
1130 s = ((LinkNamed *)action)->getName()->getCString();
1131 break;
1132 case actionMovie:
1133 s = "[movie]";
1134 break;
1135 case actionJavaScript:
1136 case actionSubmitForm:
1137 case actionHide:
1138 case actionUnknown:
1139 s = "[unknown link]";
1140 break;
1141 }
1142 (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
1143 }
1144 }
1145 } else {
1146 core->setCursor(None);
1147 if (core->linkAction) {
1148 core->linkAction = NULL;
1149 if (core->updateCbk) {
1150 (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
1151 }
1152 }
1153 }
1154 }
1155 }
1156 if (core->panning) {
1157 core->scrollTo(core->scrollX - (data->event->xmotion.x - core->panMX),
1158 core->scrollY - (data->event->xmotion.y - core->panMY));
1159 core->panMX = data->event->xmotion.x;
1160 core->panMY = data->event->xmotion.y;
1161 }
1162 break;
1163 case KeyPress:
1164 if (core->keyPressCbk) {
1165 key = XLookupKeysym(&data->event->xkey,
1166 (data->event->xkey.state & ShiftMask) ? 1 : 0);
1167 (*core->keyPressCbk)(core->keyPressCbkData,
1168 key, data->event->xkey.state, data->event);
1169 }
1170 break;
1171 }
1172 }
1173
newTile(int xDestA,int yDestA)1174 PDFCoreTile *XPDFCore::newTile(int xDestA, int yDestA) {
1175 return new XPDFCoreTile(xDestA, yDestA);
1176 }
1177
updateTileData(PDFCoreTile * tileA,int xSrc,int ySrc,int width,int height,GBool composited)1178 void XPDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
1179 int width, int height, GBool composited) {
1180 XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
1181 XImage *image;
1182 SplashColorPtr dataPtr, p;
1183 Gulong pixel;
1184 Guchar *ap;
1185 Guchar alpha, alpha1;
1186 int w, h, bw, x, y, r, g, b, gray;
1187 int *errDownR, *errDownG, *errDownB;
1188 int errRightR, errRightG, errRightB;
1189 int errDownRightR, errDownRightG, errDownRightB;
1190 int r0, g0, b0, re, ge, be;
1191
1192 if (!tile->image) {
1193 w = tile->xMax - tile->xMin;
1194 h = tile->yMax - tile->yMin;
1195 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, w, h, 8, 0);
1196 image->data = (char *)gmallocn(h, image->bytes_per_line);
1197 tile->image = image;
1198 } else {
1199 image = (XImage *)tile->image;
1200 }
1201
1202 //~ optimize for known XImage formats
1203 bw = tile->bitmap->getRowSize();
1204 dataPtr = tile->bitmap->getDataPtr();
1205
1206 if (trueColor) {
1207 for (y = 0; y < height; ++y) {
1208 p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1209 if (!composited && tile->bitmap->getAlphaPtr()) {
1210 ap = tile->bitmap->getAlphaPtr() +
1211 (ySrc + y) * tile->bitmap->getWidth() + xSrc;
1212 } else {
1213 ap = NULL;
1214 }
1215 for (x = 0; x < width; ++x) {
1216 r = splashRGB8R(p);
1217 g = splashRGB8G(p);
1218 b = splashRGB8B(p);
1219 if (ap) {
1220 alpha = *ap++;
1221 alpha1 = 255 - alpha;
1222 r = div255(alpha1 * paperColor[0] + alpha * r);
1223 g = div255(alpha1 * paperColor[1] + alpha * g);
1224 b = div255(alpha1 * paperColor[2] + alpha * b);
1225 }
1226 r >>= rDiv;
1227 g >>= gDiv;
1228 b >>= bDiv;
1229 pixel = ((Gulong)r << rShift) +
1230 ((Gulong)g << gShift) +
1231 ((Gulong)b << bShift);
1232 XPutPixel(image, xSrc + x, ySrc + y, pixel);
1233 p += 3;
1234 }
1235 }
1236 } else if (rgbCubeSize == 1) {
1237 //~ this should really use splashModeMono, with non-clustered dithering
1238 for (y = 0; y < height; ++y) {
1239 p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1240 if (!composited && tile->bitmap->getAlphaPtr()) {
1241 ap = tile->bitmap->getAlphaPtr() +
1242 (ySrc + y) * tile->bitmap->getWidth() + xSrc;
1243 } else {
1244 ap = NULL;
1245 }
1246 for (x = 0; x < width; ++x) {
1247 r = splashRGB8R(p);
1248 g = splashRGB8G(p);
1249 b = splashRGB8B(p);
1250 if (ap) {
1251 alpha = *ap++;
1252 alpha1 = 255 - alpha;
1253 r = div255(alpha1 * paperColor[0] + alpha * r);
1254 g = div255(alpha1 * paperColor[1] + alpha * g);
1255 b = div255(alpha1 * paperColor[2] + alpha * b);
1256 }
1257 gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
1258 if (gray < 128) {
1259 pixel = colors[0];
1260 } else {
1261 pixel = colors[1];
1262 }
1263 XPutPixel(image, xSrc + x, ySrc + y, pixel);
1264 p += 3;
1265 }
1266 }
1267 } else {
1268 // do Floyd-Steinberg dithering on the whole bitmap
1269 errDownR = (int *)gmallocn(width + 2, sizeof(int));
1270 errDownG = (int *)gmallocn(width + 2, sizeof(int));
1271 errDownB = (int *)gmallocn(width + 2, sizeof(int));
1272 errRightR = errRightG = errRightB = 0;
1273 errDownRightR = errDownRightG = errDownRightB = 0;
1274 memset(errDownR, 0, (width + 2) * sizeof(int));
1275 memset(errDownG, 0, (width + 2) * sizeof(int));
1276 memset(errDownB, 0, (width + 2) * sizeof(int));
1277 for (y = 0; y < height; ++y) {
1278 p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1279 if (!composited && tile->bitmap->getAlphaPtr()) {
1280 ap = tile->bitmap->getAlphaPtr() +
1281 (ySrc + y) * tile->bitmap->getWidth() + xSrc;
1282 } else {
1283 ap = NULL;
1284 }
1285 for (x = 0; x < width; ++x) {
1286 r = splashRGB8R(p);
1287 g = splashRGB8G(p);
1288 b = splashRGB8B(p);
1289 if (ap) {
1290 alpha = *ap++;
1291 alpha1 = 255 - alpha;
1292 r = div255(alpha1 * paperColor[0] + alpha * r);
1293 g = div255(alpha1 * paperColor[1] + alpha * g);
1294 b = div255(alpha1 * paperColor[2] + alpha * b);
1295 }
1296 r0 = r + errRightR + errDownR[x+1];
1297 g0 = g + errRightG + errDownG[x+1];
1298 b0 = b + errRightB + errDownB[x+1];
1299 if (r0 < 0) {
1300 r = 0;
1301 } else if (r0 >= 255) {
1302 r = rgbCubeSize - 1;
1303 } else {
1304 r = div255(r0 * (rgbCubeSize - 1));
1305 }
1306 if (g0 < 0) {
1307 g = 0;
1308 } else if (g0 >= 255) {
1309 g = rgbCubeSize - 1;
1310 } else {
1311 g = div255(g0 * (rgbCubeSize - 1));
1312 }
1313 if (b0 < 0) {
1314 b = 0;
1315 } else if (b0 >= 255) {
1316 b = rgbCubeSize - 1;
1317 } else {
1318 b = div255(b0 * (rgbCubeSize - 1));
1319 }
1320 re = r0 - ((r << 8) - r) / (rgbCubeSize - 1);
1321 ge = g0 - ((g << 8) - g) / (rgbCubeSize - 1);
1322 be = b0 - ((b << 8) - b) / (rgbCubeSize - 1);
1323 errRightR = (re * 7) >> 4;
1324 errRightG = (ge * 7) >> 4;
1325 errRightB = (be * 7) >> 4;
1326 errDownR[x] += (re * 3) >> 4;
1327 errDownG[x] += (ge * 3) >> 4;
1328 errDownB[x] += (be * 3) >> 4;
1329 errDownR[x+1] = ((re * 5) >> 4) + errDownRightR;
1330 errDownG[x+1] = ((ge * 5) >> 4) + errDownRightG;
1331 errDownB[x+1] = ((be * 5) >> 4) + errDownRightB;
1332 errDownRightR = re >> 4;
1333 errDownRightG = ge >> 4;
1334 errDownRightB = be >> 4;
1335 pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b];
1336 XPutPixel(image, xSrc + x, ySrc + y, pixel);
1337 p += 3;
1338 }
1339 }
1340 gfree(errDownR);
1341 gfree(errDownG);
1342 gfree(errDownB);
1343 }
1344 }
1345
redrawRect(PDFCoreTile * tileA,int xSrc,int ySrc,int xDest,int yDest,int width,int height,GBool composited)1346 void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
1347 int xDest, int yDest, int width, int height,
1348 GBool composited) {
1349 XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
1350 Window drawAreaWin;
1351 XGCValues gcValues;
1352
1353 // create a GC for the drawing area
1354 drawAreaWin = XtWindow(drawArea);
1355 if (!drawAreaGC) {
1356 gcValues.foreground = mattePixel;
1357 drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
1358 }
1359
1360 // draw the document
1361 if (tile) {
1362 XPutImage(display, drawAreaWin, drawAreaGC, tile->image,
1363 xSrc, ySrc, xDest, yDest, width, height);
1364
1365 // draw the background
1366 } else {
1367 XFillRectangle(display, drawAreaWin, drawAreaGC,
1368 xDest, yDest, width, height);
1369 }
1370
1371 XFlush(display);
1372 }
1373
updateScrollbars()1374 void XPDFCore::updateScrollbars() {
1375 Arg args[20];
1376 int n;
1377 int maxPos;
1378
1379 if (pages->getLength() > 0) {
1380 if (continuousMode) {
1381 maxPos = maxPageW;
1382 } else {
1383 maxPos = ((PDFCorePage *)pages->get(0))->w;
1384 }
1385 } else {
1386 maxPos = 1;
1387 }
1388 if (maxPos < drawAreaWidth) {
1389 maxPos = drawAreaWidth;
1390 }
1391 n = 0;
1392 XtSetArg(args[n], XmNvalue, scrollX); ++n;
1393 XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1394 XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
1395 XtSetArg(args[n], XmNincrement, 16); ++n;
1396 XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
1397 XtSetValues(hScrollBar, args, n);
1398
1399 if (pages->getLength() > 0) {
1400 if (continuousMode) {
1401 maxPos = totalDocH;
1402 } else {
1403 maxPos = ((PDFCorePage *)pages->get(0))->h;
1404 }
1405 } else {
1406 maxPos = 1;
1407 }
1408 if (maxPos < drawAreaHeight) {
1409 maxPos = drawAreaHeight;
1410 }
1411 n = 0;
1412 XtSetArg(args[n], XmNvalue, scrollY); ++n;
1413 XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1414 XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
1415 XtSetArg(args[n], XmNincrement, 16); ++n;
1416 XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
1417 XtSetValues(vScrollBar, args, n);
1418 }
1419
setCursor(Cursor cursor)1420 void XPDFCore::setCursor(Cursor cursor) {
1421 Window topWin;
1422
1423 if (cursor == currentCursor) {
1424 return;
1425 }
1426 if (!(topWin = XtWindow(shell))) {
1427 return;
1428 }
1429 if (cursor == None) {
1430 XUndefineCursor(display, topWin);
1431 } else {
1432 XDefineCursor(display, topWin, cursor);
1433 }
1434 XFlush(display);
1435 currentCursor = cursor;
1436 }
1437
doQuestionDialog(const char * title,GString * msg)1438 GBool XPDFCore::doQuestionDialog(const char *title, GString *msg) {
1439 return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
1440 }
1441
doInfoDialog(const char * title,GString * msg)1442 void XPDFCore::doInfoDialog(const char *title, GString *msg) {
1443 doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
1444 }
1445
doErrorDialog(const char * title,GString * msg)1446 void XPDFCore::doErrorDialog(const char *title, GString *msg) {
1447 doDialog(XmDIALOG_ERROR, gFalse, title, msg);
1448 }
1449
doDialog(int type,GBool hasCancel,const char * title,GString * msg)1450 GBool XPDFCore::doDialog(int type, GBool hasCancel,
1451 const char *title, GString *msg) {
1452 Widget dialog, scroll, text;
1453 XtAppContext appContext;
1454 Arg args[20];
1455 int n;
1456 XmString s1, s2;
1457 XEvent event;
1458
1459 n = 0;
1460 XtSetArg(args[n], XmNdialogType, type); ++n;
1461 XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1462 s1 = XmStringCreateLocalized((char *)title);
1463 XtSetArg(args[n], XmNdialogTitle, s1); ++n;
1464 s2 = NULL; // make gcc happy
1465 if (msg->getLength() <= 80) {
1466 s2 = XmStringCreateLocalized(msg->getCString());
1467 XtSetArg(args[n], XmNmessageString, s2); ++n;
1468 }
1469 dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
1470 XmStringFree(s1);
1471 if (msg->getLength() <= 80) {
1472 XmStringFree(s2);
1473 } else {
1474 n = 0;
1475 XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
1476 if (drawAreaWidth > 300) {
1477 XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n;
1478 }
1479 scroll = XmCreateScrolledWindow(dialog, "scroll", args, n);
1480 XtManageChild(scroll);
1481 n = 0;
1482 XtSetArg(args[n], XmNeditable, False); ++n;
1483 XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n;
1484 XtSetArg(args[n], XmNvalue, msg->getCString()); ++n;
1485 XtSetArg(args[n], XmNshadowThickness, 0); ++n;
1486 text = XmCreateText(scroll, "text", args, n);
1487 XtManageChild(text);
1488 }
1489 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
1490 XtAddCallback(dialog, XmNokCallback,
1491 &dialogOkCbk, (XtPointer)this);
1492 if (hasCancel) {
1493 XtAddCallback(dialog, XmNcancelCallback,
1494 &dialogCancelCbk, (XtPointer)this);
1495 } else {
1496 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
1497 }
1498
1499 XtManageChild(dialog);
1500
1501 appContext = XtWidgetToApplicationContext(dialog);
1502 dialogDone = 0;
1503 do {
1504 XtAppNextEvent(appContext, &event);
1505 XtDispatchEvent(&event);
1506 } while (!dialogDone);
1507
1508 XtUnmanageChild(dialog);
1509 XtDestroyWidget(dialog);
1510
1511 return dialogDone > 0;
1512 }
1513
dialogOkCbk(Widget widget,XtPointer ptr,XtPointer callData)1514 void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
1515 XtPointer callData) {
1516 XPDFCore *core = (XPDFCore *)ptr;
1517
1518 core->dialogDone = 1;
1519 }
1520
dialogCancelCbk(Widget widget,XtPointer ptr,XtPointer callData)1521 void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
1522 XtPointer callData) {
1523 XPDFCore *core = (XPDFCore *)ptr;
1524
1525 core->dialogDone = -1;
1526 }
1527
1528 //------------------------------------------------------------------------
1529 // password dialog
1530 //------------------------------------------------------------------------
1531
initPasswordDialog()1532 void XPDFCore::initPasswordDialog() {
1533 Widget row, label, okBtn, cancelBtn;
1534 Arg args[20];
1535 int n;
1536 XmString s;
1537
1538 //----- dialog
1539 n = 0;
1540 s = XmStringCreateLocalized(xpdfAppName ": Password");
1541 XtSetArg(args[n], XmNdialogTitle, s); ++n;
1542 XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1543 passwordDialog = XmCreateFormDialog(drawArea, "passwordDialog", args, n);
1544 XmStringFree(s);
1545
1546 //----- message
1547 n = 0;
1548 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
1549 XtSetArg(args[n], XmNtopOffset, 4); ++n;
1550 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1551 XtSetArg(args[n], XmNleftOffset, 4); ++n;
1552 s = XmStringCreateLocalized("This document requires a password.");
1553 XtSetArg(args[n], XmNlabelString, s); ++n;
1554 label = XmCreateLabel(passwordDialog, "msg", args, n);
1555 XmStringFree(s);
1556 XtManageChild(label);
1557
1558 //----- label and password entry
1559 n = 0;
1560 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1561 XtSetArg(args[n], XmNtopWidget, label); ++n;
1562 XtSetArg(args[n], XmNtopOffset, 4); ++n;
1563 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1564 XtSetArg(args[n], XmNleftOffset, 4); ++n;
1565 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
1566 XtSetArg(args[n], XmNleftOffset, 4); ++n;
1567 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
1568 XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
1569 row = XmCreateRowColumn(passwordDialog, "row", args, n);
1570 XtManageChild(row);
1571 n = 0;
1572 s = XmStringCreateLocalized("Password: ");
1573 XtSetArg(args[n], XmNlabelString, s); ++n;
1574 label = XmCreateLabel(row, "label", args, n);
1575 XmStringFree(s);
1576 XtManageChild(label);
1577 n = 0;
1578 XtSetArg(args[n], XmNcolumns, 16); ++n;
1579 passwordText = XmCreateTextField(row, "text", args, n);
1580 XtManageChild(passwordText);
1581 XtAddCallback(passwordText, XmNmodifyVerifyCallback,
1582 &passwordTextVerifyCbk, this);
1583
1584 //----- "Ok" and "Cancel" buttons
1585 n = 0;
1586 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1587 XtSetArg(args[n], XmNtopWidget, row); ++n;
1588 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1589 XtSetArg(args[n], XmNleftOffset, 4); ++n;
1590 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
1591 XtSetArg(args[n], XmNbottomOffset, 4); ++n;
1592 XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
1593 okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n);
1594 XtManageChild(okBtn);
1595 XtAddCallback(okBtn, XmNactivateCallback,
1596 &passwordOkCbk, (XtPointer)this);
1597 n = 0;
1598 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1599 XtSetArg(args[n], XmNtopWidget, row); ++n;
1600 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
1601 XtSetArg(args[n], XmNrightOffset, 4); ++n;
1602 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
1603 XtSetArg(args[n], XmNbottomOffset, 4); ++n;
1604 XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
1605 cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n);
1606 XtManageChild(cancelBtn);
1607 XtAddCallback(cancelBtn, XmNactivateCallback,
1608 &passwordCancelCbk, (XtPointer)this);
1609 n = 0;
1610 XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
1611 XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
1612 #if XmVersion > 1001
1613 XtSetArg(args[n], XmNinitialFocus, passwordText); ++n;
1614 #endif
1615 XtSetValues(passwordDialog, args, n);
1616 }
1617
passwordTextVerifyCbk(Widget widget,XtPointer ptr,XtPointer callData)1618 void XPDFCore::passwordTextVerifyCbk(Widget widget, XtPointer ptr,
1619 XtPointer callData) {
1620 XPDFCore *core = (XPDFCore *)ptr;
1621 XmTextVerifyCallbackStruct *data =
1622 (XmTextVerifyCallbackStruct *)callData;
1623 int i, n;
1624
1625 i = (int)data->startPos;
1626 n = (int)data->endPos - i;
1627 if (i > core->password->getLength()) {
1628 i = core->password->getLength();
1629 }
1630 if (i + n > core->password->getLength()) {
1631 n = core->password->getLength() - i;
1632 }
1633 core->password->del(i, n);
1634 core->password->insert(i, data->text->ptr, data->text->length);
1635
1636 for (i = 0; i < data->text->length; ++i) {
1637 data->text->ptr[i] = '*';
1638 }
1639 data->doit = True;
1640 }
1641
passwordOkCbk(Widget widget,XtPointer ptr,XtPointer callData)1642 void XPDFCore::passwordOkCbk(Widget widget, XtPointer ptr,
1643 XtPointer callData) {
1644 XPDFCore *core = (XPDFCore *)ptr;
1645
1646 core->dialogDone = 1;
1647 }
1648
passwordCancelCbk(Widget widget,XtPointer ptr,XtPointer callData)1649 void XPDFCore::passwordCancelCbk(Widget widget, XtPointer ptr,
1650 XtPointer callData) {
1651 XPDFCore *core = (XPDFCore *)ptr;
1652
1653 core->dialogDone = -1;
1654 }
1655
getPassword()1656 GString *XPDFCore::getPassword() {
1657 XtAppContext appContext;
1658 XEvent event;
1659
1660 // NB: set <password> before calling XmTextFieldSetString, because
1661 // SetString will trigger a call to passwordTextVerifyCbk, which
1662 // expects <password> to be valid
1663 password = new GString();
1664 XmTextFieldSetString(passwordText, "");
1665 XtManageChild(passwordDialog);
1666
1667 appContext = XtWidgetToApplicationContext(passwordDialog);
1668 dialogDone = 0;
1669 do {
1670 XtAppNextEvent(appContext, &event);
1671 XtDispatchEvent(&event);
1672 } while (!dialogDone);
1673 XtUnmanageChild(passwordDialog);
1674
1675 if (dialogDone < 0) {
1676 delete password;
1677 return NULL;
1678 }
1679 return password;
1680 }
1681