1
//========================================================================
5
// Copyright 2002-2003 Glyph & Cog, LLC
7
// Modified for Debian by Hamish Moffatt, 22 May 2002.
9
//========================================================================
13
#ifdef USE_GCC_PRAGMAS
14
#pragma implementation
17
#include <X11/keysym.h>
18
#include <X11/cursorfont.h>
24
#include "GlobalParams.h"
27
#include "ErrorCodes.h"
29
#include "CoreOutputDev.h"
30
#include "PSOutputDev.h"
31
#include "TextOutputDev.h"
32
#include "SplashBitmap.h"
33
#include "SplashPattern.h"
37
// these macro defns conflict with xpdf's Object class
38
#ifdef LESSTIF_VERSION
46
//------------------------------------------------------------------------
48
// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
49
static inline Guchar div255(int x) {
50
return (Guchar)((x + (x >> 8) + 0x80) >> 8);
53
//------------------------------------------------------------------------
55
GString *XPDFCore::currentSelection = NULL;
56
XPDFCore *XPDFCore::currentSelectionOwner = NULL;
57
Atom XPDFCore::targetsAtom;
59
//------------------------------------------------------------------------
61
//------------------------------------------------------------------------
63
class XPDFCoreTile: public PDFCoreTile {
65
XPDFCoreTile(int xDestA, int yDestA);
66
virtual ~XPDFCoreTile();
70
XPDFCoreTile::XPDFCoreTile(int xDestA, int yDestA):
71
PDFCoreTile(xDestA, yDestA)
76
XPDFCoreTile::~XPDFCoreTile() {
84
//------------------------------------------------------------------------
86
//------------------------------------------------------------------------
88
XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
89
SplashColorPtr paperColorA, Gulong paperPixelA,
90
Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA,
91
GBool installCmap, int rgbCubeSizeA):
92
PDFCore(splashModeRGB8, 4, reverseVideoA, paperColorA, !fullScreenA)
97
parentWidget = parentWidgetA;
98
display = XtDisplay(parentWidget);
99
screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
100
targetsAtom = XInternAtom(display, "TARGETS", False);
102
paperPixel = paperPixelA;
103
mattePixel = mattePixelA;
104
fullScreen = fullScreenA;
106
setupX(installCmap, rgbCubeSizeA);
111
drawAreaFrame = NULL;
114
// get the initial zoom value
118
initialZoom = globalParams->getInitialZoom();
119
if (!initialZoom->cmp("page")) {
121
} else if (!initialZoom->cmp("width")) {
124
zoom = atoi(initialZoom->getCString());
141
// optional features default to on
142
hyperlinksEnabled = gTrue;
143
selectEnabled = gTrue;
145
// do X-specific initialization and create the widgets
147
initPasswordDialog();
150
XPDFCore::~XPDFCore() {
151
if (currentSelectionOwner == this && currentSelection) {
152
delete currentSelection;
153
currentSelection = NULL;
154
currentSelectionOwner = NULL;
157
XFreeGC(display, drawAreaGC);
160
XtDestroyWidget(scrolledWin);
163
XFreeCursor(display, busyCursor);
166
XFreeCursor(display, linkCursor);
169
XFreeCursor(display, selectCursor);
173
//------------------------------------------------------------------------
174
// loadFile / displayPage / displayDest
175
//------------------------------------------------------------------------
177
int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
178
GString *userPassword) {
181
err = PDFCore::loadFile(fileName, ownerPassword, userPassword);
182
if (err == errNone) {
183
// save the modification time
184
modTime = getModTime(doc->getFileName()->getCString());
186
// update the parent window
188
(*updateCbk)(updateCbkData, doc->getFileName(), -1,
189
doc->getNumPages(), NULL);
195
int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
196
GString *userPassword) {
199
err = PDFCore::loadFile(stream, ownerPassword, userPassword);
200
if (err == errNone) {
204
// update the parent window
206
(*updateCbk)(updateCbkData, doc->getFileName(), -1,
207
doc->getNumPages(), NULL);
213
void XPDFCore::loadDoc(PDFDoc *docA) {
214
PDFCore::loadDoc(docA);
216
// save the modification time
217
if (doc->getFileName()) {
218
modTime = getModTime(doc->getFileName()->getCString());
221
// update the parent window
223
(*updateCbk)(updateCbkData, doc->getFileName(), -1,
224
doc->getNumPages(), NULL);
228
void XPDFCore::resizeToPage(int pg) {
229
Dimension width, height;
230
double width1, height1;
231
Dimension topW, topH, topBorder, daW, daH;
232
Dimension displayW, displayH;
234
displayW = DisplayWidth(display, screenNum);
235
displayH = DisplayHeight(display, screenNum);
240
if (!doc || pg <= 0 || pg > doc->getNumPages()) {
243
} else if (doc->getPageRotate(pg) == 90 ||
244
doc->getPageRotate(pg) == 270) {
245
width1 = doc->getPageCropHeight(pg);
246
height1 = doc->getPageCropWidth(pg);
248
width1 = doc->getPageCropWidth(pg);
249
height1 = doc->getPageCropHeight(pg);
251
if (zoom == zoomPage || zoom == zoomWidth) {
252
width = (Dimension)(width1 * 0.01 * defZoom + 0.5);
253
height = (Dimension)(height1 * 0.01 * defZoom + 0.5);
255
width = (Dimension)(width1 * 0.01 * zoom + 0.5);
256
height = (Dimension)(height1 * 0.01 * zoom + 0.5);
258
if (continuousMode) {
259
height += continuousModePageSpacing;
261
if (width > displayW - 100) {
262
width = displayW - 100;
264
if (height > displayH - 100) {
265
height = displayH - 100;
269
if (XtIsRealized(shell)) {
270
XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
271
XmNborderWidth, &topBorder, NULL);
272
XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
273
XtVaSetValues(shell, XmNwidth, width + (topW - daW),
274
XmNheight, height + (topH - daH), NULL);
276
XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
280
void XPDFCore::update(int topPageA, int scrollXA, int scrollYA,
281
double zoomA, int rotateA,
282
GBool force, GBool addToHist) {
286
PDFCore::update(topPageA, scrollXA, scrollYA, zoomA, rotateA,
289
if (doc && topPage != oldPage) {
291
(*updateCbk)(updateCbkData, NULL, topPage, -1, "");
296
GBool XPDFCore::checkForNewFile() {
299
if (doc->getFileName()) {
300
newModTime = getModTime(doc->getFileName()->getCString());
301
if (newModTime != modTime) {
302
modTime = newModTime;
309
//------------------------------------------------------------------------
310
// page/position changes
311
//------------------------------------------------------------------------
313
GBool XPDFCore::gotoNextPage(int inc, GBool top) {
314
if (!PDFCore::gotoNextPage(inc, top)) {
321
GBool XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
322
if (!PDFCore::gotoPrevPage(dec, top, bottom)) {
329
GBool XPDFCore::goForward() {
330
if (!PDFCore::goForward()) {
337
GBool XPDFCore::goBackward() {
338
if (!PDFCore::goBackward()) {
345
void XPDFCore::startPan(int wx, int wy) {
351
void XPDFCore::endPan(int wx, int wy) {
355
//------------------------------------------------------------------------
357
//------------------------------------------------------------------------
359
void XPDFCore::startSelection(int wx, int wy) {
363
if (doc && doc->getNumPages() > 0) {
365
if (cvtWindowToDev(wx, wy, &pg, &x, &y)) {
366
setSelection(pg, x, y, x, y);
367
setCursor(selectCursor);
374
void XPDFCore::endSelection(int wx, int wy) {
378
if (doc && doc->getNumPages() > 0) {
379
ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
384
moveSelection(pg, x, y);
386
#ifndef NO_TEXT_SELECT
387
if (selectULX != selectLRX &&
388
selectULY != selectLRY) {
389
#ifdef ENFORCE_PERMISSIONS
390
if (doc->okToCopy()) {
393
error(-1, "Copying of text from this document is not allowed.");
404
// X's copy-and-paste mechanism is brain damaged. Xt doesn't help
405
// any, but doesn't make it too much worse, either. Motif, on the
406
// other hand, adds significant complexity to the mess. So here we
407
// blow off the Motif junk and stick to plain old Xt. The next two
408
// functions (copySelection and convertSelectionCbk) implement the
409
// magic needed to deal with Xt's mechanism. Note that this requires
410
// global variables (currentSelection and currentSelectionOwner).
412
void XPDFCore::copySelection() {
414
double ulx, uly, lrx, lry;
416
#ifdef ENFORCE_PERMISSIONS
417
if (!doc->okToCopy()) {
421
if (getSelection(&pg, &ulx, &uly, &lrx, &lry)) {
422
//~ for multithreading: need a mutex here
423
if (currentSelection) {
424
delete currentSelection;
426
currentSelection = extractText(pg, ulx, uly, lrx, lry);
427
currentSelectionOwner = this;
428
XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
429
&convertSelectionCbk, NULL, NULL);
433
Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
434
Atom *target, Atom *type,
435
XtPointer *value, unsigned long *length,
439
// send back a list of supported conversion targets
440
if (*target == targetsAtom) {
441
if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) {
444
array[0] = XA_STRING;
445
*value = (XtPointer)array;
451
// send the selected text
452
} else if (*target == XA_STRING) {
453
//~ for multithreading: need a mutex here
454
*value = XtNewString(currentSelection->getCString());
455
*length = currentSelection->getLength();
457
*format = 8; // 8-bit elements
464
//------------------------------------------------------------------------
466
//------------------------------------------------------------------------
468
void XPDFCore::doAction(LinkAction *action) {
473
GString *fileName, *fileName2;
476
Object movieAnnot, obj1, obj2;
480
switch (kind = action->getKind()) {
482
// GoTo / GoToR action
485
if (kind == actionGoTo) {
488
if ((dest = ((LinkGoTo *)action)->getDest())) {
490
} else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
491
namedDest = namedDest->copy();
496
if ((dest = ((LinkGoToR *)action)->getDest())) {
498
} else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
499
namedDest = namedDest->copy();
501
s = ((LinkGoToR *)action)->getFileName()->getCString();
502
//~ translate path name for VMS (deal with '/')
503
if (isAbsolutePath(s)) {
504
fileName = new GString(s);
506
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
508
if (loadFile(fileName) != errNone) {
521
dest = doc->findDest(namedDest);
525
displayDest(dest, zoom, rotate, gTrue);
528
if (kind == actionGoToR) {
529
displayPage(1, zoom, 0, gFalse, gTrue);
536
fileName = ((LinkLaunch *)action)->getFileName();
537
s = fileName->getCString();
538
if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
539
!strcmp(s + fileName->getLength() - 4, ".PDF")) {
540
//~ translate path name for VMS (deal with '/')
541
if (isAbsolutePath(s)) {
542
fileName = fileName->copy();
544
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
546
if (loadFile(fileName) != errNone) {
551
displayPage(1, zoom, rotate, gFalse, gTrue);
553
fileName = fileName->copy();
554
if (((LinkLaunch *)action)->getParams()) {
555
fileName->append(' ');
556
fileName->append(((LinkLaunch *)action)->getParams());
559
fileName->insert(0, "spawn/nowait ");
560
#elif defined(__EMX__)
561
fileName->insert(0, "start /min /n ");
563
fileName->append(" &");
565
msg = new GString("About to execute the command:\n");
566
msg->append(fileName);
567
if (doQuestionDialog("Launching external application", msg)) {
568
system(fileName->getCString());
577
if (!(cmd = globalParams->getURLCommand())) {
578
error(-1, "No urlCommand defined in config file");
581
runCommand(cmd, ((LinkURI *)action)->getURI());
586
actionName = ((LinkNamed *)action)->getName();
587
if (!actionName->cmp("NextPage")) {
588
gotoNextPage(1, gTrue);
589
} else if (!actionName->cmp("PrevPage")) {
590
gotoPrevPage(1, gTrue, gFalse);
591
} else if (!actionName->cmp("FirstPage")) {
593
displayPage(1, zoom, rotate, gTrue, gTrue);
595
} else if (!actionName->cmp("LastPage")) {
596
if (topPage != doc->getNumPages()) {
597
displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
599
} else if (!actionName->cmp("GoBack")) {
601
} else if (!actionName->cmp("GoForward")) {
603
} else if (!actionName->cmp("Quit")) {
605
(*actionCbk)(actionCbkData, actionName->getCString());
608
error(-1, "Unknown named action: '%s'", actionName->getCString());
614
if (!(cmd = globalParams->getMovieCommand())) {
615
error(-1, "No movieCommand defined in config file");
618
if (((LinkMovie *)action)->hasAnnotRef()) {
619
doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
620
((LinkMovie *)action)->getAnnotRef()->gen,
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")) {
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
fileName2 = appendToPath(
645
grabPath(doc->getFileName()->getCString()),
646
fileName->getCString());
648
fileName = fileName2;
650
runCommand(cmd, fileName);
661
// unknown action type
663
error(-1, "Unknown link action type: '%s'",
664
((LinkUnknown *)action)->getAction()->getCString());
669
// Run a command, given a <cmdFmt> string with one '%s' in it, and an
670
// <arg> string to insert in place of the '%s'.
671
void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
675
if ((s = strstr(cmdFmt->getCString(), "%s"))) {
677
cmd->insert(0, cmdFmt->getCString(),
678
s - cmdFmt->getCString());
681
cmd = cmdFmt->copy();
684
cmd->insert(0, "spawn/nowait ");
685
#elif defined(__EMX__)
686
cmd->insert(0, "start /min /n ");
690
system(cmd->getCString());
694
// Escape any characters in a URL which might cause problems when
696
GString *XPDFCore::mungeURL(GString *url) {
697
static char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
698
"abcdefghijklmnopqrstuvwxyz"
706
newURL = new GString();
707
for (i = 0; i < url->getLength(); ++i) {
709
if (strchr(allowed, c)) {
712
sprintf(buf, "%%%02x", c & 0xff);
719
//------------------------------------------------------------------------
721
//------------------------------------------------------------------------
723
GBool XPDFCore::find(char *s, GBool caseSensitive,
724
GBool next, GBool backward, GBool onePageOnly) {
725
if (!PDFCore::find(s, caseSensitive, next, backward, onePageOnly)) {
729
#ifndef NO_TEXT_SELECT
735
GBool XPDFCore::findU(Unicode *u, int len, GBool caseSensitive,
736
GBool next, GBool backward, GBool onePageOnly) {
737
if (!PDFCore::findU(u, len, caseSensitive, next, backward, onePageOnly)) {
741
#ifndef NO_TEXT_SELECT
747
//------------------------------------------------------------------------
749
//------------------------------------------------------------------------
751
void XPDFCore::setBusyCursor(GBool busy) {
752
setCursor(busy ? busyCursor : None);
755
void XPDFCore::takeFocus() {
756
XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
759
//------------------------------------------------------------------------
761
//------------------------------------------------------------------------
763
void XPDFCore::setupX(GBool installCmap, int rgbCubeSizeA) {
764
XVisualInfo visualTempl;
765
XVisualInfo *visualList;
773
// for some reason, querying XmNvisual doesn't work (even if done
774
// after the window is mapped)
775
visual = DefaultVisual(display, screenNum);
776
XtVaGetValues(shell, XmNcolormap, &colormap, NULL);
778
// check for TrueColor visual
779
//~ this should scan the list, not just look at the first one
780
visualTempl.visualid = XVisualIDFromVisual(visual);
781
visualList = XGetVisualInfo(display, VisualIDMask,
782
&visualTempl, &nVisuals);
784
// this shouldn't happen
785
XFree((XPointer)visualList);
786
visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
789
depth = visualList->depth;
790
if (visualList->c_class == TrueColor) {
792
for (mask = visualList->red_mask, rShift = 0;
794
mask >>= 1, ++rShift) ;
795
for (rDiv = 8; mask; mask >>= 1, --rDiv) ;
796
for (mask = visualList->green_mask, gShift = 0;
798
mask >>= 1, ++gShift) ;
799
for (gDiv = 8; mask; mask >>= 1, --gDiv) ;
800
for (mask = visualList->blue_mask, bShift = 0;
802
mask >>= 1, ++bShift) ;
803
for (bDiv = 8; mask; mask >>= 1, --bDiv) ;
807
XFree((XPointer)visualList);
809
// allocate a color cube
812
// set colors in private colormap
814
for (rgbCubeSize = xMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) {
815
m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
816
if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
820
if (rgbCubeSize >= 2) {
821
m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
822
xcolors = (XColor *)gmallocn(m, sizeof(XColor));
824
for (r = 0; r < rgbCubeSize; ++r) {
825
for (g = 0; g < rgbCubeSize; ++g) {
826
for (b = 0; b < rgbCubeSize; ++b) {
827
xcolors[n].pixel = colors[n];
828
xcolors[n].red = (r * 65535) / (rgbCubeSize - 1);
829
xcolors[n].green = (g * 65535) / (rgbCubeSize - 1);
830
xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1);
831
xcolors[n].flags = DoRed | DoGreen | DoBlue;
836
XStoreColors(display, colormap, xcolors, m);
840
colors[0] = BlackPixel(display, screenNum);
841
colors[1] = WhitePixel(display, screenNum);
844
// allocate colors in shared colormap
846
if (rgbCubeSize > xMaxRGBCube) {
847
rgbCubeSize = xMaxRGBCube;
850
for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) {
853
for (r = 0; r < rgbCubeSize && ok; ++r) {
854
for (g = 0; g < rgbCubeSize && ok; ++g) {
855
for (b = 0; b < rgbCubeSize && ok; ++b) {
857
colors[n] = BlackPixel(display, screenNum);
860
xcolor.red = (r * 65535) / (rgbCubeSize - 1);
861
xcolor.green = (g * 65535) / (rgbCubeSize - 1);
862
xcolor.blue = (b * 65535) / (rgbCubeSize - 1);
863
if (XAllocColor(display, colormap, &xcolor)) {
864
colors[n++] = xcolor.pixel;
875
XFreeColors(display, colormap, &colors[1], n-1, 0);
879
colors[0] = BlackPixel(display, screenNum);
880
colors[1] = WhitePixel(display, screenNum);
886
void XPDFCore::initWindow() {
890
// create the cursors
891
busyCursor = XCreateFontCursor(display, XC_watch);
892
linkCursor = XCreateFontCursor(display, XC_hand2);
893
selectCursor = XCreateFontCursor(display, XC_cross);
896
// create the scrolled window and scrollbars
898
XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
899
XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
900
scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
901
XtManageChild(scrolledWin);
903
XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
904
XtSetArg(args[n], XmNminimum, 0); ++n;
905
XtSetArg(args[n], XmNmaximum, 1); ++n;
906
XtSetArg(args[n], XmNsliderSize, 1); ++n;
907
XtSetArg(args[n], XmNvalue, 0); ++n;
908
XtSetArg(args[n], XmNincrement, 1); ++n;
909
XtSetArg(args[n], XmNpageIncrement, 1); ++n;
910
hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
912
XtManageChild(hScrollBar);
914
XtAddCallback(hScrollBar, XmNvalueChangedCallback,
915
&hScrollChangeCbk, (XtPointer)this);
916
#ifndef DISABLE_SMOOTH_SCROLL
917
XtAddCallback(hScrollBar, XmNdragCallback,
918
&hScrollDragCbk, (XtPointer)this);
921
XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
922
XtSetArg(args[n], XmNminimum, 0); ++n;
923
XtSetArg(args[n], XmNmaximum, 1); ++n;
924
XtSetArg(args[n], XmNsliderSize, 1); ++n;
925
XtSetArg(args[n], XmNvalue, 0); ++n;
926
XtSetArg(args[n], XmNincrement, 1); ++n;
927
XtSetArg(args[n], XmNpageIncrement, 1); ++n;
928
vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
930
XtManageChild(vScrollBar);
932
XtAddCallback(vScrollBar, XmNvalueChangedCallback,
933
&vScrollChangeCbk, (XtPointer)this);
934
#ifndef DISABLE_SMOOTH_SCROLL
935
XtAddCallback(vScrollBar, XmNdragCallback,
936
&vScrollDragCbk, (XtPointer)this);
939
// create the drawing area
941
XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
942
XtSetArg(args[n], XmNmarginWidth, 0); ++n;
943
XtSetArg(args[n], XmNmarginHeight, 0); ++n;
945
XtSetArg(args[n], XmNshadowThickness, 0); ++n;
947
drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
948
XtManageChild(drawAreaFrame);
950
XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
951
XtSetArg(args[n], XmNwidth, 700); ++n;
952
XtSetArg(args[n], XmNheight, 500); ++n;
953
drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
954
XtManageChild(drawArea);
955
XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
956
XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
957
XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
958
resizeCbk(drawArea, this, NULL);
960
// set up mouse motion translations
961
XtOverrideTranslations(drawArea, XtParseTranslationTable(
962
"<Btn1Down>:DrawingAreaInput()\n"
963
"<Btn1Up>:DrawingAreaInput()\n"
964
"<Btn1Motion>:DrawingAreaInput()\n"
965
"<Motion>:DrawingAreaInput()"));
967
// can't create a GC until the window gets mapped
971
void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
972
XtPointer callData) {
973
XPDFCore *core = (XPDFCore *)ptr;
974
XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
976
core->scrollTo(data->value, core->scrollY);
979
void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
980
XtPointer callData) {
981
XPDFCore *core = (XPDFCore *)ptr;
982
XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
984
core->scrollTo(data->value, core->scrollY);
987
void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
988
XtPointer callData) {
989
XPDFCore *core = (XPDFCore *)ptr;
990
XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
992
core->scrollTo(core->scrollX, data->value);
995
void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
996
XtPointer callData) {
997
XPDFCore *core = (XPDFCore *)ptr;
998
XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1000
core->scrollTo(core->scrollX, data->value);
1003
void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1004
XPDFCore *core = (XPDFCore *)ptr;
1009
Guint w1, h1, bw1, depth1;
1015
// find the top-most widget which has an associated window, and look
1016
// for a pending ConfigureNotify in the event queue -- if there is
1017
// one, and it specifies a different width or height, that means
1018
// we're still resizing, and we want to skip the current event
1019
for (top = core->parentWidget;
1020
XtParent(top) && XtWindow(XtParent(top));
1021
top = XtParent(top)) ;
1022
if (XCheckTypedWindowEvent(core->display, XtWindow(top),
1023
ConfigureNotify, &event)) {
1024
XPutBackEvent(core->display, &event);
1025
XGetGeometry(core->display, event.xconfigure.window,
1026
&rootWin, &x1, &y1, &w1, &h1, &bw1, &depth1);
1027
if ((Guint)event.xconfigure.width != w1 ||
1028
(Guint)event.xconfigure.height != h1) {
1034
XtSetArg(args[n], XmNwidth, &w); ++n;
1035
XtSetArg(args[n], XmNheight, &h); ++n;
1036
XtGetValues(core->drawArea, args, n);
1037
core->drawAreaWidth = (int)w;
1038
core->drawAreaHeight = (int)h;
1039
if (core->zoom == zoomPage || core->zoom == zoomWidth) {
1045
core->update(core->topPage, sx, sy, core->zoom, core->rotate, gTrue, gFalse);
1048
void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1049
XPDFCore *core = (XPDFCore *)ptr;
1050
XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1053
if (data->reason == XmCR_EXPOSE) {
1054
x = data->event->xexpose.x;
1055
y = data->event->xexpose.y;
1056
w = data->event->xexpose.width;
1057
h = data->event->xexpose.height;
1061
w = core->drawAreaWidth;
1062
h = core->drawAreaHeight;
1064
core->redrawWindow(x, y, w, h, gFalse);
1067
void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1068
XPDFCore *core = (XPDFCore *)ptr;
1069
XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1077
switch (data->event->type) {
1079
if (*core->mouseCbk) {
1080
(*core->mouseCbk)(core->mouseCbkData, data->event);
1084
if (*core->mouseCbk) {
1085
(*core->mouseCbk)(core->mouseCbkData, data->event);
1089
if (core->doc && core->doc->getNumPages() > 0) {
1090
ok = core->cvtWindowToDev(data->event->xmotion.x, data->event->xmotion.y,
1092
if (core->dragging) {
1094
core->moveSelection(pg, x, y);
1096
} else if (core->hyperlinksEnabled) {
1097
core->cvtDevToUser(pg, x, y, &xu, &yu);
1098
if (ok && (action = core->findLink(pg, xu, yu))) {
1099
core->setCursor(core->linkCursor);
1100
if (action != core->linkAction) {
1101
core->linkAction = action;
1102
if (core->updateCbk) {
1104
switch (action->getKind()) {
1106
s = "[internal link]";
1109
s = ((LinkGoToR *)action)->getFileName()->getCString();
1112
s = ((LinkLaunch *)action)->getFileName()->getCString();
1115
s = ((LinkURI *)action)->getURI()->getCString();
1118
s = ((LinkNamed *)action)->getName()->getCString();
1124
s = "[unknown link]";
1127
(*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
1131
core->setCursor(None);
1132
if (core->linkAction) {
1133
core->linkAction = NULL;
1134
if (core->updateCbk) {
1135
(*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
1141
if (core->panning) {
1142
core->scrollTo(core->scrollX - (data->event->xmotion.x - core->panMX),
1143
core->scrollY - (data->event->xmotion.y - core->panMY));
1144
core->panMX = data->event->xmotion.x;
1145
core->panMY = data->event->xmotion.y;
1149
if (core->keyPressCbk) {
1150
key = XLookupKeysym(&data->event->xkey,
1151
(data->event->xkey.state & ShiftMask) ? 1 : 0);
1152
(*core->keyPressCbk)(core->keyPressCbkData,
1153
key, data->event->xkey.state, data->event);
1159
PDFCoreTile *XPDFCore::newTile(int xDestA, int yDestA) {
1160
return new XPDFCoreTile(xDestA, yDestA);
1163
void XPDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
1164
int width, int height, GBool composited) {
1165
XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
1167
SplashColorPtr dataPtr, p;
1170
Guchar alpha, alpha1;
1171
int w, h, bw, x, y, r, g, b, gray;
1172
int *errDownR, *errDownG, *errDownB;
1173
int errRightR, errRightG, errRightB;
1174
int errDownRightR, errDownRightG, errDownRightB;
1175
int r0, g0, b0, re, ge, be;
1178
w = tile->xMax - tile->xMin;
1179
h = tile->yMax - tile->yMin;
1180
image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, w, h, 8, 0);
1181
image->data = (char *)gmalloc(h * image->bytes_per_line);
1182
tile->image = image;
1184
image = (XImage *)tile->image;
1187
//~ optimize for known XImage formats
1188
bw = tile->bitmap->getRowSize();
1189
dataPtr = tile->bitmap->getDataPtr();
1192
for (y = 0; y < height; ++y) {
1193
p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1194
if (!composited && tile->bitmap->getAlphaPtr()) {
1195
ap = tile->bitmap->getAlphaPtr() +
1196
(ySrc + y) * tile->bitmap->getWidth() + xSrc;
1200
for (x = 0; x < width; ++x) {
1206
alpha1 = 255 - alpha;
1207
r = div255(alpha1 * paperColor[0] + alpha * r);
1208
g = div255(alpha1 * paperColor[1] + alpha * g);
1209
b = div255(alpha1 * paperColor[2] + alpha * b);
1214
pixel = ((Gulong)r << rShift) +
1215
((Gulong)g << gShift) +
1216
((Gulong)b << bShift);
1217
XPutPixel(image, xSrc + x, ySrc + y, pixel);
1221
} else if (rgbCubeSize == 1) {
1222
//~ this should really use splashModeMono, with non-clustered dithering
1223
for (y = 0; y < height; ++y) {
1224
p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1225
if (!composited && tile->bitmap->getAlphaPtr()) {
1226
ap = tile->bitmap->getAlphaPtr() +
1227
(ySrc + y) * tile->bitmap->getWidth() + xSrc;
1231
for (x = 0; x < width; ++x) {
1237
alpha1 = 255 - alpha;
1238
r = div255(alpha1 * paperColor[0] + alpha * r);
1239
g = div255(alpha1 * paperColor[1] + alpha * g);
1240
b = div255(alpha1 * paperColor[2] + alpha * b);
1242
gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
1248
XPutPixel(image, xSrc + x, ySrc + y, pixel);
1253
// do Floyd-Steinberg dithering on the whole bitmap
1254
errDownR = (int *)gmallocn(width + 2, sizeof(int));
1255
errDownG = (int *)gmallocn(width + 2, sizeof(int));
1256
errDownB = (int *)gmallocn(width + 2, sizeof(int));
1257
errRightR = errRightG = errRightB = 0;
1258
errDownRightR = errDownRightG = errDownRightB = 0;
1259
memset(errDownR, 0, (width + 2) * sizeof(int));
1260
memset(errDownG, 0, (width + 2) * sizeof(int));
1261
memset(errDownB, 0, (width + 2) * sizeof(int));
1262
for (y = 0; y < height; ++y) {
1263
p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1264
if (!composited && tile->bitmap->getAlphaPtr()) {
1265
ap = tile->bitmap->getAlphaPtr() +
1266
(ySrc + y) * tile->bitmap->getWidth() + xSrc;
1270
for (x = 0; x < width; ++x) {
1276
alpha1 = 255 - alpha;
1277
r = div255(alpha1 * paperColor[0] + alpha * r);
1278
g = div255(alpha1 * paperColor[1] + alpha * g);
1279
b = div255(alpha1 * paperColor[2] + alpha * b);
1281
r0 = r + errRightR + errDownR[x+1];
1282
g0 = g + errRightG + errDownG[x+1];
1283
b0 = b + errRightB + errDownB[x+1];
1286
} else if (r0 >= 255) {
1287
r = rgbCubeSize - 1;
1289
r = div255(r0 * (rgbCubeSize - 1));
1293
} else if (g0 >= 255) {
1294
g = rgbCubeSize - 1;
1296
g = div255(g0 * (rgbCubeSize - 1));
1300
} else if (b0 >= 255) {
1301
b = rgbCubeSize - 1;
1303
b = div255(b0 * (rgbCubeSize - 1));
1305
re = r0 - ((r << 8) - r) / (rgbCubeSize - 1);
1306
ge = g0 - ((g << 8) - g) / (rgbCubeSize - 1);
1307
be = b0 - ((b << 8) - b) / (rgbCubeSize - 1);
1308
errRightR = (re * 7) >> 4;
1309
errRightG = (ge * 7) >> 4;
1310
errRightB = (be * 7) >> 4;
1311
errDownR[x] += (re * 3) >> 4;
1312
errDownG[x] += (ge * 3) >> 4;
1313
errDownB[x] += (be * 3) >> 4;
1314
errDownR[x+1] = ((re * 5) >> 4) + errDownRightR;
1315
errDownG[x+1] = ((ge * 5) >> 4) + errDownRightG;
1316
errDownB[x+1] = ((be * 5) >> 4) + errDownRightB;
1317
errDownRightR = re >> 4;
1318
errDownRightG = ge >> 4;
1319
errDownRightB = be >> 4;
1320
pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b];
1321
XPutPixel(image, xSrc + x, ySrc + y, pixel);
1331
void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
1332
int xDest, int yDest, int width, int height,
1334
XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
1338
// create a GC for the drawing area
1339
drawAreaWin = XtWindow(drawArea);
1341
gcValues.foreground = mattePixel;
1342
drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
1345
// draw the document
1347
XPutImage(display, drawAreaWin, drawAreaGC, tile->image,
1348
xSrc, ySrc, xDest, yDest, width, height);
1350
// draw the background
1352
XFillRectangle(display, drawAreaWin, drawAreaGC,
1353
xDest, yDest, width, height);
1357
void XPDFCore::updateScrollbars() {
1362
if (pages->getLength() > 0) {
1363
if (continuousMode) {
1366
maxPos = ((PDFCorePage *)pages->get(0))->w;
1371
if (maxPos < drawAreaWidth) {
1372
maxPos = drawAreaWidth;
1375
XtSetArg(args[n], XmNvalue, scrollX); ++n;
1376
XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1377
XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
1378
XtSetArg(args[n], XmNincrement, 16); ++n;
1379
XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
1380
XtSetValues(hScrollBar, args, n);
1382
if (pages->getLength() > 0) {
1383
if (continuousMode) {
1386
maxPos = ((PDFCorePage *)pages->get(0))->h;
1391
if (maxPos < drawAreaHeight) {
1392
maxPos = drawAreaHeight;
1395
XtSetArg(args[n], XmNvalue, scrollY); ++n;
1396
XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1397
XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
1398
XtSetArg(args[n], XmNincrement, 16); ++n;
1399
XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
1400
XtSetValues(vScrollBar, args, n);
1403
void XPDFCore::setCursor(Cursor cursor) {
1406
if (cursor == currentCursor) {
1409
if (!(topWin = XtWindow(shell))) {
1412
if (cursor == None) {
1413
XUndefineCursor(display, topWin);
1415
XDefineCursor(display, topWin, cursor);
1418
currentCursor = cursor;
1421
GBool XPDFCore::doQuestionDialog(char *title, GString *msg) {
1422
return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
1425
void XPDFCore::doInfoDialog(char *title, GString *msg) {
1426
doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
1429
void XPDFCore::doErrorDialog(char *title, GString *msg) {
1430
doDialog(XmDIALOG_ERROR, gFalse, title, msg);
1433
GBool XPDFCore::doDialog(int type, GBool hasCancel,
1434
char *title, GString *msg) {
1435
Widget dialog, scroll, text;
1436
XtAppContext appContext;
1443
XtSetArg(args[n], XmNdialogType, type); ++n;
1444
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1445
s1 = XmStringCreateLocalized(title);
1446
XtSetArg(args[n], XmNdialogTitle, s1); ++n;
1447
s2 = NULL; // make gcc happy
1448
if (msg->getLength() <= 80) {
1449
s2 = XmStringCreateLocalized(msg->getCString());
1450
XtSetArg(args[n], XmNmessageString, s2); ++n;
1452
dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
1454
if (msg->getLength() <= 80) {
1458
XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
1459
if (drawAreaWidth > 300) {
1460
XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n;
1462
scroll = XmCreateScrolledWindow(dialog, "scroll", args, n);
1463
XtManageChild(scroll);
1465
XtSetArg(args[n], XmNeditable, False); ++n;
1466
XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n;
1467
XtSetArg(args[n], XmNvalue, msg->getCString()); ++n;
1468
XtSetArg(args[n], XmNshadowThickness, 0); ++n;
1469
text = XmCreateText(scroll, "text", args, n);
1470
XtManageChild(text);
1472
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
1473
XtAddCallback(dialog, XmNokCallback,
1474
&dialogOkCbk, (XtPointer)this);
1476
XtAddCallback(dialog, XmNcancelCallback,
1477
&dialogCancelCbk, (XtPointer)this);
1479
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
1482
XtManageChild(dialog);
1484
appContext = XtWidgetToApplicationContext(dialog);
1487
XtAppNextEvent(appContext, &event);
1488
XtDispatchEvent(&event);
1489
} while (!dialogDone);
1491
XtUnmanageChild(dialog);
1492
XtDestroyWidget(dialog);
1494
return dialogDone > 0;
1497
void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
1498
XtPointer callData) {
1499
XPDFCore *core = (XPDFCore *)ptr;
1501
core->dialogDone = 1;
1504
void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
1505
XtPointer callData) {
1506
XPDFCore *core = (XPDFCore *)ptr;
1508
core->dialogDone = -1;
1511
//------------------------------------------------------------------------
1513
//------------------------------------------------------------------------
1515
void XPDFCore::initPasswordDialog() {
1516
Widget row, label, okBtn, cancelBtn;
1523
s = XmStringCreateLocalized(xpdfAppName ": Password");
1524
XtSetArg(args[n], XmNdialogTitle, s); ++n;
1525
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1526
passwordDialog = XmCreateFormDialog(drawArea, "passwordDialog", args, n);
1531
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
1532
XtSetArg(args[n], XmNtopOffset, 4); ++n;
1533
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1534
XtSetArg(args[n], XmNleftOffset, 4); ++n;
1535
s = XmStringCreateLocalized("This document requires a password.");
1536
XtSetArg(args[n], XmNlabelString, s); ++n;
1537
label = XmCreateLabel(passwordDialog, "msg", args, n);
1539
XtManageChild(label);
1541
//----- label and password entry
1543
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1544
XtSetArg(args[n], XmNtopWidget, label); ++n;
1545
XtSetArg(args[n], XmNtopOffset, 4); ++n;
1546
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1547
XtSetArg(args[n], XmNleftOffset, 4); ++n;
1548
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
1549
XtSetArg(args[n], XmNleftOffset, 4); ++n;
1550
XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
1551
XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
1552
row = XmCreateRowColumn(passwordDialog, "row", args, n);
1555
s = XmStringCreateLocalized("Password: ");
1556
XtSetArg(args[n], XmNlabelString, s); ++n;
1557
label = XmCreateLabel(row, "label", args, n);
1559
XtManageChild(label);
1561
XtSetArg(args[n], XmNcolumns, 16); ++n;
1562
passwordText = XmCreateTextField(row, "text", args, n);
1563
XtManageChild(passwordText);
1564
XtAddCallback(passwordText, XmNmodifyVerifyCallback,
1565
&passwordTextVerifyCbk, this);
1567
//----- "Ok" and "Cancel" buttons
1569
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1570
XtSetArg(args[n], XmNtopWidget, row); ++n;
1571
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1572
XtSetArg(args[n], XmNleftOffset, 4); ++n;
1573
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
1574
XtSetArg(args[n], XmNbottomOffset, 4); ++n;
1575
XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
1576
okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n);
1577
XtManageChild(okBtn);
1578
XtAddCallback(okBtn, XmNactivateCallback,
1579
&passwordOkCbk, (XtPointer)this);
1581
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1582
XtSetArg(args[n], XmNtopWidget, row); ++n;
1583
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
1584
XtSetArg(args[n], XmNrightOffset, 4); ++n;
1585
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
1586
XtSetArg(args[n], XmNbottomOffset, 4); ++n;
1587
XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
1588
cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n);
1589
XtManageChild(cancelBtn);
1590
XtAddCallback(cancelBtn, XmNactivateCallback,
1591
&passwordCancelCbk, (XtPointer)this);
1593
XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
1594
XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
1595
#if XmVersion > 1001
1596
XtSetArg(args[n], XmNinitialFocus, passwordText); ++n;
1598
XtSetValues(passwordDialog, args, n);
1601
void XPDFCore::passwordTextVerifyCbk(Widget widget, XtPointer ptr,
1602
XtPointer callData) {
1603
XPDFCore *core = (XPDFCore *)ptr;
1604
XmTextVerifyCallbackStruct *data =
1605
(XmTextVerifyCallbackStruct *)callData;
1608
i = (int)data->startPos;
1609
n = (int)data->endPos - i;
1610
if (i > core->password->getLength()) {
1611
i = core->password->getLength();
1613
if (i + n > core->password->getLength()) {
1614
n = core->password->getLength() - i;
1616
core->password->del(i, n);
1617
core->password->insert(i, data->text->ptr, data->text->length);
1619
for (i = 0; i < data->text->length; ++i) {
1620
data->text->ptr[i] = '*';
1625
void XPDFCore::passwordOkCbk(Widget widget, XtPointer ptr,
1626
XtPointer callData) {
1627
XPDFCore *core = (XPDFCore *)ptr;
1629
core->dialogDone = 1;
1632
void XPDFCore::passwordCancelCbk(Widget widget, XtPointer ptr,
1633
XtPointer callData) {
1634
XPDFCore *core = (XPDFCore *)ptr;
1636
core->dialogDone = -1;
1639
GString *XPDFCore::getPassword() {
1640
XtAppContext appContext;
1643
// NB: set <password> before calling XmTextFieldSetString, because
1644
// SetString will trigger a call to passwordTextVerifyCbk, which
1645
// expects <password> to be valid
1646
password = new GString();
1647
XmTextFieldSetString(passwordText, "");
1648
XtManageChild(passwordDialog);
1650
appContext = XtWidgetToApplicationContext(passwordDialog);
1653
XtAppNextEvent(appContext, &event);
1654
XtDispatchEvent(&event);
1655
} while (!dialogDone);
1656
XtUnmanageChild(passwordDialog);
1658
if (dialogDone < 0) {