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