1 //========================================================================
2 //
3 // XPDFApp.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 "GString.h"
16 #include "GList.h"
17 #include "Error.h"
18 #include "XPDFViewer.h"
19 #include "XPDFApp.h"
20 #include "config.h"
21
22 // these macro defns conflict with xpdf's Object class
23 #ifdef LESSTIF_VERSION
24 #undef XtDisplay
25 #undef XtScreen
26 #undef XtWindow
27 #undef XtParent
28 #undef XtIsRealized
29 #endif
30
31 //------------------------------------------------------------------------
32
33 #define remoteCmdSize 512
34
35 //------------------------------------------------------------------------
36
37 static String fallbackResources[] = {
38 "*.zoomComboBox*fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1",
39 "*XmTextField.fontList: -*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1",
40 "*.fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1",
41 "*XmTextField.translations: #override\\n"
42 " Ctrl<Key>a:beginning-of-line()\\n"
43 " Ctrl<Key>b:backward-character()\\n"
44 " Ctrl<Key>d:delete-next-character()\\n"
45 " Ctrl<Key>e:end-of-line()\\n"
46 " Ctrl<Key>f:forward-character()\\n"
47 " Ctrl<Key>u:beginning-of-line()delete-to-end-of-line()\\n"
48 " Ctrl<Key>k:delete-to-end-of-line()\\n",
49 "*.toolTipEnable: True",
50 "*.toolTipPostDelay: 1500",
51 "*.toolTipPostDuration: 0",
52 "*.TipLabel.foreground: black",
53 "*.TipLabel.background: LightYellow",
54 "*.TipShell.borderWidth: 1",
55 "*.TipShell.borderColor: black",
56 NULL
57 };
58
59 static XrmOptionDescRec xOpts[] = {
60 {"-display", ".display", XrmoptionSepArg, NULL},
61 {"-foreground", "*Foreground", XrmoptionSepArg, NULL},
62 {"-fg", "*Foreground", XrmoptionSepArg, NULL},
63 {"-background", "*Background", XrmoptionSepArg, NULL},
64 {"-bg", "*Background", XrmoptionSepArg, NULL},
65 {"-geometry", ".geometry", XrmoptionSepArg, NULL},
66 {"-g", ".geometry", XrmoptionSepArg, NULL},
67 {"-font", "*.fontList", XrmoptionSepArg, NULL},
68 {"-fn", "*.fontList", XrmoptionSepArg, NULL},
69 {"-title", ".title", XrmoptionSepArg, NULL},
70 {"-cmap", ".installCmap", XrmoptionNoArg, (XPointer)"on"},
71 {"-rgb", ".rgbCubeSize", XrmoptionSepArg, NULL},
72 {"-rv", ".reverseVideo", XrmoptionNoArg, (XPointer)"true"},
73 {"-papercolor", ".paperColor", XrmoptionSepArg, NULL},
74 {"-mattecolor", ".matteColor", XrmoptionSepArg, NULL},
75 {"-z", ".initialZoom", XrmoptionSepArg, NULL}
76 };
77
78 #define nXOpts (sizeof(xOpts) / sizeof(XrmOptionDescRec))
79
80 struct XPDFAppResources {
81 String geometry;
82 String title;
83 Bool installCmap;
84 int rgbCubeSize;
85 Bool reverseVideo;
86 String paperColor;
87 String matteColor;
88 String fullScreenMatteColor;
89 String initialZoom;
90 };
91
92 static Bool defInstallCmap = False;
93 static int defRGBCubeSize = defaultRGBCube;
94 static Bool defReverseVideo = False;
95
96 static XtResource xResources[] = {
97 { "geometry", "Geometry", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, geometry), XtRString, (XtPointer)NULL },
98 { "title", "Title", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, title), XtRString, (XtPointer)NULL },
99 { "installCmap", "InstallCmap", XtRBool, sizeof(Bool), XtOffsetOf(XPDFAppResources, installCmap), XtRBool, (XtPointer)&defInstallCmap },
100 { "rgbCubeSize", "RgbCubeSize", XtRInt, sizeof(int), XtOffsetOf(XPDFAppResources, rgbCubeSize), XtRInt, (XtPointer)&defRGBCubeSize },
101 { "reverseVideo", "ReverseVideo", XtRBool, sizeof(Bool), XtOffsetOf(XPDFAppResources, reverseVideo), XtRBool, (XtPointer)&defReverseVideo },
102 { "paperColor", "PaperColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, paperColor), XtRString, (XtPointer)NULL },
103 { "matteColor", "MatteColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, matteColor), XtRString, (XtPointer)"gray50" },
104 { "fullScreenMatteColor", "FullScreenMatteColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, fullScreenMatteColor), XtRString, (XtPointer)"black" },
105 { "initialZoom", "InitialZoom", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, initialZoom), XtRString, (XtPointer)NULL }
106 };
107
108 #define nXResources (sizeof(xResources) / sizeof(XtResource))
109
110 //------------------------------------------------------------------------
111 // XPDFApp
112 //------------------------------------------------------------------------
113
114 #if 0 //~ for debugging
115 static int xErrorHandler(Display *display, XErrorEvent *ev) {
116 printf("X error:\n");
117 printf(" resource ID = %08lx\n", ev->resourceid);
118 printf(" serial = %lu\n", ev->serial);
119 printf(" error_code = %d\n", ev->error_code);
120 printf(" request_code = %d\n", ev->request_code);
121 printf(" minor_code = %d\n", ev->minor_code);
122 fflush(stdout);
123 abort();
124 }
125 #endif
126
XPDFApp(int * argc,char * argv[])127 XPDFApp::XPDFApp(int *argc, char *argv[]) {
128 appShell = XtAppInitialize(&appContext, xpdfAppName, xOpts, nXOpts,
129 argc, argv, fallbackResources, NULL, 0);
130 display = XtDisplay(appShell);
131 screenNum = XScreenNumberOfScreen(XtScreen(appShell));
132 #if XmVERSION > 1
133 XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)),
134 XmNenableButtonTab, True, NULL);
135 #endif
136 #if XmVERSION > 1
137 // Drag-and-drop appears to be buggy -- I'm seeing weird crashes
138 // deep in the Motif code when I destroy widgets in the XpdfForms
139 // code. Xpdf doesn't use it, so just turn it off.
140 XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)),
141 XmNdragInitiatorProtocolStyle, XmDRAG_NONE,
142 XmNdragReceiverProtocolStyle, XmDRAG_NONE,
143 NULL);
144 #endif
145
146 #if 0 //~ for debugging
147 XSynchronize(display, True);
148 XSetErrorHandler(&xErrorHandler);
149 #endif
150
151 fullScreen = gFalse;
152 remoteAtom = None;
153 remoteViewer = NULL;
154 remoteWin = None;
155
156 getResources();
157
158 viewers = new GList();
159 }
160
getResources()161 void XPDFApp::getResources() {
162 XPDFAppResources resources;
163 XColor xcol, xcol2;
164 Colormap colormap;
165
166 XtGetApplicationResources(appShell, &resources, xResources, nXResources,
167 NULL, 0);
168 geometry = resources.geometry ? new GString(resources.geometry)
169 : (GString *)NULL;
170 title = resources.title ? new GString(resources.title) : (GString *)NULL;
171 installCmap = (GBool)resources.installCmap;
172 rgbCubeSize = resources.rgbCubeSize;
173 reverseVideo = (GBool)resources.reverseVideo;
174 if (reverseVideo) {
175 paperRGB[0] = paperRGB[1] = paperRGB[2] = 0;
176 paperPixel = BlackPixel(display, screenNum);
177 } else {
178 paperRGB[0] = paperRGB[1] = paperRGB[2] = 0xff;
179 paperPixel = WhitePixel(display, screenNum);
180 }
181 XtVaGetValues(appShell, XmNcolormap, &colormap, NULL);
182 if (resources.paperColor) {
183 if (XAllocNamedColor(display, colormap, resources.paperColor,
184 &xcol, &xcol2)) {
185 paperRGB[0] = xcol.red >> 8;
186 paperRGB[1] = xcol.green >> 8;
187 paperRGB[2] = xcol.blue >> 8;
188 paperPixel = xcol.pixel;
189 } else {
190 error(errIO, -1, "Couldn't allocate color '{0:s}'",
191 resources.paperColor);
192 }
193 }
194 if (XAllocNamedColor(display, colormap, resources.matteColor,
195 &xcol, &xcol2)) {
196 mattePixel = xcol.pixel;
197 } else {
198 mattePixel = paperPixel;
199 }
200 if (XAllocNamedColor(display, colormap, resources.fullScreenMatteColor,
201 &xcol, &xcol2)) {
202 fullScreenMattePixel = xcol.pixel;
203 } else {
204 fullScreenMattePixel = paperPixel;
205 }
206 initialZoom = resources.initialZoom ? new GString(resources.initialZoom)
207 : (GString *)NULL;
208 }
209
~XPDFApp()210 XPDFApp::~XPDFApp() {
211 deleteGList(viewers, XPDFViewer);
212 if (geometry) {
213 delete geometry;
214 }
215 if (title) {
216 delete title;
217 }
218 if (initialZoom) {
219 delete initialZoom;
220 }
221 }
222
open(GString * fileName,int page,GString * ownerPassword,GString * userPassword)223 XPDFViewer *XPDFApp::open(GString *fileName, int page,
224 GString *ownerPassword, GString *userPassword) {
225 XPDFViewer *viewer;
226
227 viewer = new XPDFViewer(this, fileName, page, NULL, fullScreen,
228 ownerPassword, userPassword);
229 if (!viewer->isOk()) {
230 delete viewer;
231 return NULL;
232 }
233 if (remoteAtom != None) {
234 remoteViewer = viewer;
235 remoteWin = viewer->getWindow();
236 XtAddEventHandler(remoteWin, PropertyChangeMask, False,
237 &remoteMsgCbk, this);
238 XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
239 }
240 viewers->append(viewer);
241 return viewer;
242 }
243
openAtDest(GString * fileName,GString * dest,GString * ownerPassword,GString * userPassword)244 XPDFViewer *XPDFApp::openAtDest(GString *fileName, GString *dest,
245 GString *ownerPassword,
246 GString *userPassword) {
247 XPDFViewer *viewer;
248
249 viewer = new XPDFViewer(this, fileName, 1, dest, fullScreen,
250 ownerPassword, userPassword);
251 if (!viewer->isOk()) {
252 delete viewer;
253 return NULL;
254 }
255 if (remoteAtom != None) {
256 remoteViewer = viewer;
257 remoteWin = viewer->getWindow();
258 XtAddEventHandler(remoteWin, PropertyChangeMask, False,
259 &remoteMsgCbk, this);
260 XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
261 }
262 viewers->append(viewer);
263 return viewer;
264 }
265
reopen(XPDFViewer * viewer,PDFDoc * doc,int page,GBool fullScreenA)266 XPDFViewer *XPDFApp::reopen(XPDFViewer *viewer, PDFDoc *doc, int page,
267 GBool fullScreenA) {
268 int i;
269
270 for (i = 0; i < viewers->getLength(); ++i) {
271 if (((XPDFViewer *)viewers->get(i)) == viewer) {
272 viewers->del(i);
273 delete viewer;
274 }
275 }
276 viewer = new XPDFViewer(this, doc, page, NULL, fullScreenA);
277 if (!viewer->isOk()) {
278 delete viewer;
279 return NULL;
280 }
281 if (remoteAtom != None) {
282 remoteViewer = viewer;
283 remoteWin = viewer->getWindow();
284 XtAddEventHandler(remoteWin, PropertyChangeMask, False,
285 &remoteMsgCbk, this);
286 XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
287 }
288 viewers->append(viewer);
289 return viewer;
290 }
291
close(XPDFViewer * viewer,GBool closeLast)292 void XPDFApp::close(XPDFViewer *viewer, GBool closeLast) {
293 int i;
294
295 if (viewers->getLength() == 1) {
296 if (viewer != (XPDFViewer *)viewers->get(0)) {
297 return;
298 }
299 if (closeLast) {
300 quit();
301 } else {
302 viewer->clear();
303 }
304 } else {
305 for (i = 0; i < viewers->getLength(); ++i) {
306 if (((XPDFViewer *)viewers->get(i)) == viewer) {
307 viewers->del(i);
308 if (remoteAtom != None && remoteViewer == viewer) {
309 remoteViewer = (XPDFViewer *)viewers->get(viewers->getLength() - 1);
310 remoteWin = remoteViewer->getWindow();
311 XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin),
312 CurrentTime);
313 }
314 delete viewer;
315 return;
316 }
317 }
318 }
319 }
320
quit()321 void XPDFApp::quit() {
322 if (remoteAtom != None) {
323 XSetSelectionOwner(display, remoteAtom, None, CurrentTime);
324 }
325 while (viewers->getLength() > 0) {
326 delete (XPDFViewer *)viewers->del(0);
327 }
328 #if HAVE_XTAPPSETEXITFLAG
329 XtAppSetExitFlag(appContext);
330 #else
331 exit(0);
332 #endif
333 }
334
run()335 void XPDFApp::run() {
336 XtAppMainLoop(appContext);
337 }
338
setRemoteName(char * remoteName)339 void XPDFApp::setRemoteName(char *remoteName) {
340 remoteAtom = XInternAtom(display, remoteName, False);
341 remoteXWin = XGetSelectionOwner(display, remoteAtom);
342 }
343
remoteServerRunning()344 GBool XPDFApp::remoteServerRunning() {
345 return remoteXWin != None;
346 }
347
remoteExec(char * cmd)348 void XPDFApp::remoteExec(char *cmd) {
349 char cmd2[remoteCmdSize];
350 int n;
351
352 n = strlen(cmd);
353 if (n > remoteCmdSize - 2) {
354 n = remoteCmdSize - 2;
355 }
356 memcpy(cmd2, cmd, n);
357 cmd2[n] = '\n';
358 cmd2[n+1] = '\0';
359 XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
360 PropModeReplace, (Guchar *)cmd2, n + 2);
361 XFlush(display);
362 }
363
remoteOpen(GString * fileName,int page,GBool raise)364 void XPDFApp::remoteOpen(GString *fileName, int page, GBool raise) {
365 char cmd[remoteCmdSize];
366
367 sprintf(cmd, "openFileAtPage(%.200s,%d)\n",
368 fileName->getCString(), page);
369 if (raise) {
370 strcat(cmd, "raise\n");
371 }
372 XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
373 PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
374 XFlush(display);
375 }
376
remoteOpenAtDest(GString * fileName,GString * dest,GBool raise)377 void XPDFApp::remoteOpenAtDest(GString *fileName, GString *dest, GBool raise) {
378 char cmd[remoteCmdSize];
379
380 sprintf(cmd, "openFileAtDest(%.200s,%.256s)\n",
381 fileName->getCString(), dest->getCString());
382 if (raise) {
383 strcat(cmd, "raise\n");
384 }
385 XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
386 PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
387 XFlush(display);
388 }
389
remoteReload(GBool raise)390 void XPDFApp::remoteReload(GBool raise) {
391 char cmd[remoteCmdSize];
392
393 strcpy(cmd, "reload\n");
394 if (raise) {
395 strcat(cmd, "raise\n");
396 }
397 XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
398 PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
399 XFlush(display);
400 }
401
remoteRaise()402 void XPDFApp::remoteRaise() {
403 XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
404 PropModeReplace, (Guchar *)"raise\n", 7);
405 XFlush(display);
406 }
407
remoteQuit()408 void XPDFApp::remoteQuit() {
409 XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
410 PropModeReplace, (Guchar *)"quit\n", 6);
411 XFlush(display);
412 }
413
remoteMsgCbk(Widget widget,XtPointer ptr,XEvent * event,Boolean * cont)414 void XPDFApp::remoteMsgCbk(Widget widget, XtPointer ptr,
415 XEvent *event, Boolean *cont) {
416 XPDFApp *app = (XPDFApp *)ptr;
417 char *cmd, *p0, *p1;
418 Atom type;
419 int format;
420 Gulong size, remain;
421 GString *cmdStr;
422
423 if (event->xproperty.atom != app->remoteAtom) {
424 *cont = True;
425 return;
426 }
427 *cont = False;
428
429 if (XGetWindowProperty(app->display, XtWindow(app->remoteWin),
430 app->remoteAtom, 0, remoteCmdSize/4,
431 True, app->remoteAtom,
432 &type, &format, &size, &remain,
433 (Guchar **)&cmd) != Success) {
434 return;
435 }
436 if (!cmd) {
437 return;
438 }
439 p0 = cmd;
440 while (*p0 && (p1 = strchr(p0, '\n'))) {
441 cmdStr = new GString(p0, p1 - p0);
442 app->remoteViewer->execCmd(cmdStr, NULL);
443 delete cmdStr;
444 p0 = p1 + 1;
445 }
446 XFree((XPointer)cmd);
447 }
448