1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 #include <X11/cursorfont.h>
13 #include <stdio.h>
14 
15 //
16 //
17 //
18 #include <errno.h> // for errno
19 #include <fcntl.h> // for stat
20 #include <ctype.h> // for tolower
21 
22 #if defined(HAVE_UNISTD_H)
23 #include <unistd.h>
24 #endif
25 
26 #if defined(HAVE_SYS_STAT_H)
27 #include <sys/stat.h>
28 #endif
29 
30 
31 #include "Application.h"
32 #include "Client.h"
33 #include "Command.h"
34 #include "DXStrings.h"
35 #include "TimedMessage.h"
36 
37 #if defined(HAVE_XINERAMA)
38 #include <X11/extensions/Xinerama.h>
39 #endif
40 
41 Application* theApplication = NUL(Application*);
42 
43 
44 boolean Application::ApplicationClassInitialized = FALSE;
45 Cursor  Application::BusyCursor                  = NUL(Cursor);
46 
47 
48 Symbol Application::MsgCreate        = NUL(Symbol);
49 Symbol Application::MsgManage        = NUL(Symbol);
50 Symbol Application::MsgUnmanage      = NUL(Symbol);
51 Symbol Application::MsgSetBusyCursor = NUL(Symbol);
52 Symbol Application::MsgResetCursor   = NUL(Symbol);
53 
54 Symbol Application::MsgManageByLeafClassName   = NUL(Symbol);
55 Symbol Application::MsgUnmanageByLeafClassName = NUL(Symbol);
56 Symbol Application::MsgManageByTitle 		= NUL(Symbol);
57 Symbol Application::MsgUnmanageByTitle 		= NUL(Symbol);
58 
59 //
60 // This is used by the ASSERT macro in defines.h
61 // It should NOT be an Application method, because otherwise
62 // it requires that defines.h include Application.h.
63 //
AssertionFailure(const char * file,int line)64 extern "C" void AssertionFailure(const char *file, int line)
65 {
66     fprintf(stderr,"Internal error detected at \"%s\":%d.\n",
67 		file, line);
68     if (theApplication)
69 	theApplication->abortApplication();
70     else
71 	abort();
72 }
73 
Application(char * className)74 Application::Application(char* className): UIComponent(className)
75 {
76     ASSERT(className);
77 
78     //
79     // Perform class initializtion, if necessary.
80     //
81     if (NOT Application::ApplicationClassInitialized)
82     {
83 	ASSERT(theSymbolManager);
84 
85 	Application::MsgCreate =
86 	    theSymbolManager->registerSymbol("Create");
87 	Application::MsgManage =
88 	    theSymbolManager->registerSymbol("Manage");
89 	Application::MsgUnmanage =
90 	    theSymbolManager->registerSymbol("Unmanage");
91 	Application::MsgSetBusyCursor =
92 	    theSymbolManager->registerSymbol("SetBusyCursor");
93 	Application::MsgResetCursor =
94 	    theSymbolManager->registerSymbol("ResetCursor");
95 	Application::MsgManageByLeafClassName   =
96 	    theSymbolManager->registerSymbol("ManageByLeafClassName");
97 	Application::MsgUnmanageByLeafClassName =
98 	    theSymbolManager->registerSymbol("UnmanageByLeafClassName");
99 	Application::MsgManageByTitle =
100 	    theSymbolManager->registerSymbol("ManageByTitle");
101 	Application::MsgUnmanageByTitle=
102 	    theSymbolManager->registerSymbol("UnmanageByTitle");
103 
104 	Application::ApplicationClassInitialized = TRUE;
105     }
106 
107     //
108     // Set the global Application pointer.
109     //
110     theApplication = this;
111 
112     //
113     // Initialize member data.
114     //
115     this->busyCursors	     = 0;
116     this->display            = NUL(Display*);
117     this->applicationContext = NUL(XtAppContext);
118 
119     this->applicationClass   = DuplicateString(className);
120     this->show_bubbles 	     = FALSE;
121     this->help_viewer	     = NUL(Widget);
122 }
123 
124 
~Application()125 Application::~Application()
126 {
127     delete[] this->applicationClass;
128     theApplication = NULL;
129 }
130 
131 //
132 // Install the default resources for this class.
133 //
installDefaultResources(Widget baseWidget)134 void Application::installDefaultResources(Widget baseWidget)
135 {
136     //this->setDefaultResources(baseWidget, Application::DefaultResources);
137 }
138 
initializeWindowSystem(int * argcp,char ** argv)139 boolean Application::initializeWindowSystem(int *argcp, char **argv)
140 {
141 
142     //
143     // Initialize Xt Intrinsics; create the initial shell widget.
144     //
145     this->setRootWidget(
146 	XtAppInitialize
147 	    (&this->applicationContext, // returned application context
148 	     this->applicationClass,	// application class name
149 	     NULL,			// command line options table
150 	     0,				// number of entries in options table
151 #if XtSpecificationRelease > 4
152 	     (int*)argcp,
153 #else
154 	     argcp,
155 #endif
156 	     argv,			 // "argv" command line arguments
157 #if XtSpecificationRelease > 4
158 	     NULL,			 // fallback resource list
159 #else
160 	     NUL(const char**),		 // fallback resource list
161 #endif
162 	     NUL(ArgList),		 // override argument list
163 	     0),			 // number of entries in argument list
164 	     FALSE			// Don't install destroy callback
165     );
166 
167 
168     //
169     // Get and save the X display structure pointer.
170     //
171     this->display = XtDisplay(this->getRootWidget());
172 
173 	int xmnx=0, xmny=0;
174 
175 #if defined(HAVE_XINERAMA)
176 	// Do some Xinerama Magic if Xinerama is present to
177 	// center the first popup-shell on the largest screen.
178 	int dummy_a, dummy_b;
179 	int screens;
180 	XineramaScreenInfo   *screeninfo = NULL;
181 	int x=0, y=0, width=0, height=0;
182 
183 	if ((XineramaQueryExtension (this->display, &dummy_a, &dummy_b)) &&
184 		(screeninfo = XineramaQueryScreens(this->display, &screens))) {
185 		// Xinerama Detected
186 
187 		if (XineramaIsActive(this->display)) {
188 			int i = dummy_a;
189 			while ( i < screens ) {
190 				if(screeninfo[i].width > width) {
191 					width = screeninfo[i].width;
192 					height = screeninfo[i].height;
193 					x = screeninfo[i].x_org;
194 					y = screeninfo[i].y_org;
195 					xmnx = x + width / 2;
196 					xmny = y + height / 2;
197 				}
198 				i++;
199 			}
200 		}
201 	}
202 #endif
203 
204 	if(xmnx == 0 || xmny == 0) {
205 		xmnx = DisplayWidth(this->display, 0) / 2;
206 		xmny = DisplayHeight(this->display, 0) / 2;
207 	}
208 
209     //
210     // Center the shell and make sure it is not visible.
211     //
212     XtVaSetValues
213 	(this->getRootWidget(),
214 	 XmNmappedWhenManaged, FALSE,
215 	 XmNx,                 xmnx,
216 	 XmNy,                 xmny,
217 	 XmNwidth,             1,
218 	 XmNheight,            1,
219 	 NULL);
220 
221 
222     //
223     // Force the initial shell window to exist so dialogs popped up
224     // from this shell behave correctly.
225     //
226     XtRealizeWidget(this->getRootWidget());
227 
228 
229     //
230     // Install error and warning handlers.
231     //
232     XtSetWarningHandler((XtErrorHandler)Application_XtWarningHandler);
233     XSetErrorHandler(Application_XErrorHandler);
234 
235     return TRUE;
236 }
237 
parseCommand(int * argcp,char ** argv,XrmOptionDescList optlist,int optlistsize)238 void Application::parseCommand(int* argcp, char** argv,
239                                XrmOptionDescList optlist, int optlistsize)
240 {
241     char res_file[256];
242     XrmDatabase resourceDatabase = 0;
243     //
244     // if the file exists, use it, but don't create an empty file.
245     //
246     if (this->getApplicationDefaultsFileName(res_file, FALSE))
247 	resourceDatabase = XrmGetFileDatabase(res_file);
248 
249     //
250     // XrmParseCommand is spec'd to accept NULL in the resourceDatabase argument.
251     //
252     char *appname = GetFileBaseName(argv[0],NULL);
253     XrmParseCommand(&resourceDatabase, optlist, optlistsize,
254 	                  appname, (int *)argcp, argv);
255     delete[] appname;
256 
257     //
258     // Merge the resources into the Xt database with highest precedence.
259     //
260     if (resourceDatabase)
261     {
262 	//
263 	// display->db overrides contents of resourceDatabase.
264 	// O'Reilly, R5 prog. supplement, pg. 127
265 	//
266 #if defined XlibSpecificationRelease && XlibSpecificationRelease > 4
267         XrmDatabase db = XrmGetDatabase(display);
268         XrmCombineDatabase(resourceDatabase, &db, True);
269 #else
270 
271 	XrmMergeDatabases(resourceDatabase, &(display->db));
272 #endif
273     }
274 
275     //
276     // It's seems as though a call to XrmDestroyDatabase(resourceDatabase)
277     // is in order here.  A quick reading of the doc doesn't explain to
278     // me why that's wrong.  If I add the call however, there will be
279     // a crash.
280     //
281 }
282 
initialize(int * argcp,char ** argv)283 boolean Application::initialize(int* argcp, char** argv)
284 {
285     //
286     // Initialize the window system if not done already.
287     //
288     if (!this->getRootWidget() &&
289 	!this->initializeWindowSystem(argcp, argv))
290 	return FALSE;
291 
292     //
293     // Since the instance name of this object was set in the UIComponent
294     // constructor before the name of the program was visible, delete the
295     // old name and set it to argv[0].
296     //
297     delete[] this->name;
298     this->name = DuplicateString(argv[0]);
299 
300     //
301     // Add Application specific actions.
302     //
303     this->addActions();
304 
305     //
306     // Create the busy status indicator cursor.
307     //
308     Application::BusyCursor = XCreateFontCursor(this->display, XC_watch);
309 
310     //
311     // Initialize and manage any windows registered with this
312     // application at this point.
313     //
314     this->notifyClients(Application::MsgCreate);
315 
316     return TRUE;
317 }
318 
319 
320 //
321 // Post the copyright notice that is returned by this->getCopyrightNotice().
322 // If it returns NULL, then don't post any notice.
323 //
postCopyrightNotice()324 void Application::postCopyrightNotice()
325 {
326     const char *c = this->getCopyrightNotice();
327 
328     if (c) {
329 	//
330 	// Post the copyright message.
331 	//
332 	TimedMessage *copyright =
333 	    new TimedMessage("copyrightMessage",
334 	    this->getRootWidget(),
335 	    c,
336 	    "Welcome",
337 	    5000);
338 	copyright->post();
339     }
340 }
341 
handleEvents()342 void Application::handleEvents()
343 {
344 XEvent event;
345     //
346     // Loop forever...
347     //
348     for (;;) {
349 	XtAppNextEvent (this->applicationContext, &event);
350 	this->handleEvent(&event);
351     }
352 }
353 
handleEvent(XEvent * xev)354 void Application::handleEvent (XEvent *xev)
355 {
356     XtDispatchEvent (xev);
357 }
358 
359 
manage()360 void Application::manage()
361 {
362     //
363     // Notify the client windows to manage themselves.
364     //
365     this->notifyClients(Application::MsgManage);
366 }
367 
368 
unmanage()369 void Application::unmanage()
370 {
371     //
372     // Notify the client windows to unmanage themselves.
373     //
374     this->notifyClients(Application::MsgUnmanage);
375 }
376 
377 
378 //
379 // Calls to this routine can be 'stacked' so that the first call
380 // sets the cursor and the last call resets the cursor.
381 // setBusyCursor(TRUE);		// Sets busy cursor
382 // setBusyCursor(TRUE);		// does not effect cursor
383 // setBusyCursor(TRUE);		// does not effect cursor
384 // setBusyCursor(FALSE);	// does not effect cursor
385 // setBusyCursor(TRUE);		// does not effect cursor
386 // setBusyCursor(FALSE);	// does not effect cursor
387 // setBusyCursor(FALSE);	// does not effect cursor
388 // setBusyCursor(FALSE);	// resets cursor
389 //
setBusyCursor(boolean setting)390 void Application::setBusyCursor(boolean setting)
391 {
392     ASSERT(this->getRootWidget());
393     ASSERT(Application::BusyCursor);
394     ASSERT(this->busyCursors >= 0);
395 
396     if (setting)
397     {
398 	this->busyCursors++;
399     }
400     else
401     {
402 	this->busyCursors--;
403     }
404 
405     switch (this->busyCursors) {
406 	case 0:
407 	    this->notifyClients(Application::MsgResetCursor);
408 	    break;
409 	case 1:
410 	    if (setting)
411 		this->notifyClients(Application::MsgSetBusyCursor);
412 	    break;
413     }
414 
415     ASSERT(this->busyCursors >= 0);
416 }
417 
418 
419 //
420 // This is currently only used for debugging.
421 //
DumpApplicationResources(const char * filename)422 void Application::DumpApplicationResources(const char *filename)
423 {
424     Display *display = theApplication->getDisplay();
425 
426 #if defined XlibSpecificationRelease && XlibSpecificationRelease > 4
427     XrmPutFileDatabase(XrmGetDatabase(display), filename);
428 #else
429     XrmPutFileDatabase(display->db, filename);
430 #endif
431 
432 }
433 //
434 // Virtual methods that are called by Command::ExecuteCommandCallback()
435 // before and after Command::execute().
436 //
startCommandInterfaceExecution()437 void Application::startCommandInterfaceExecution()
438 {
439     this->notifyClients(Command::MsgBeginExecuting);
440 }
endCommandInterfaceExecution()441 void Application::endCommandInterfaceExecution()
442 {
443     this->notifyClients(Command::MsgEndExecuting);
444 }
445 
446 
getFormalName()447 const char *Application::getFormalName()
448 {
449     return "'Your Application's Formal Name Here'";
450 }
451 
getInformalName()452 const char *Application::getInformalName()
453 {
454     return "'Your Application's Informal Name Here'";
455 }
456 
getCopyrightNotice()457 const char *Application::getCopyrightNotice()
458 {
459     return "'Your Application's Copyright Notice Here'";
460 }
461 
helpOn(const char * topic)462 void Application::helpOn(const char *topic)
463 {
464     printf("Your Application specific help on `%s' here\n", topic);
465 }
getHelpDirectory()466 const char *Application::getHelpDirectory()
467 {
468     return ".";
469 }
470 
getHelpDirFileName()471 const char *Application::getHelpDirFileName()
472 {
473     return "HelpDir";
474 }
getHTMLDirectory()475 const char *Application::getHTMLDirectory()
476 {
477     return ".";
478 }
479 
getHTMLDirFileName()480 const char *Application::getHTMLDirFileName()
481 {
482     return "Help.idx";
483 }
484 
Application_XErrorHandler(Display * display,XErrorEvent * event)485 extern "C" int Application_XErrorHandler(Display *display, XErrorEvent *event)
486 {
487     if (theApplication)
488         return theApplication->handleXError(display, event);
489     else
490 	return 1;
491 }
492 
handleXError(Display * display,XErrorEvent * event)493 int Application::handleXError(Display *display, XErrorEvent *event)
494 {
495     char buffer[BUFSIZ];
496     char mesg[BUFSIZ];
497     char number[32];
498     XGetErrorText(display, event->error_code, buffer, BUFSIZ);
499     XGetErrorDatabaseText(display, "XlibMessage", "XError",
500         "X Error", mesg, BUFSIZ);
501     fprintf(stderr, "%s:  %s\n", mesg, buffer);
502     XGetErrorDatabaseText(display, "XlibMessage", "MajorCode",
503         "Request Major code %d", mesg, BUFSIZ);
504     fprintf(stderr, mesg, event->request_code);
505     if (event->request_code < 128) {
506         sprintf(number, "%d", event->request_code);
507         XGetErrorDatabaseText(display, "XRequest", number, "", buffer, BUFSIZ);
508     } else {
509         sprintf(buffer, "Extension %d", event->request_code);
510     }
511     fprintf(stderr, " (%s)\n  ", buffer);
512     XGetErrorDatabaseText(display, "XlibMessage", "MinorCode",
513         "Request Minor code %d", mesg, BUFSIZ);
514     fprintf(stderr, mesg, event->minor_code);
515     if (event->request_code >= 128) {
516         sprintf(mesg, "Extension %d.%d",
517             event->request_code, event->minor_code);
518         XGetErrorDatabaseText(display, "XRequest", mesg, "", buffer, BUFSIZ);
519         fprintf(stderr, " (%s)", buffer);
520     }
521     fputs("\n  ", stderr);
522     XGetErrorDatabaseText(display, "XlibMessage", "ResourceID",
523         "ResourceID 0x%x", mesg, BUFSIZ);
524     fprintf(stderr, mesg, event->resourceid);
525     fputs("\n  ", stderr);
526     XGetErrorDatabaseText(display, "XlibMessage", "ErrorSerial",
527         "Error Serial #%d", mesg, BUFSIZ);
528     fprintf(stderr, mesg, event->serial);
529     fputs("\n  ", stderr);
530 
531 #if defined(XlibSpecificationRelease) && XlibSpecificationRelease <= 4
532     // R5 does not allow one to get at display->request.
533     XGetErrorDatabaseText(display, "XlibMessage", "CurrentSerial",
534         "Current Serial #%d", mesg, BUFSIZ);
535     fprintf(stderr, mesg, display->request);
536     fputs("\n", stderr);
537 #endif
538 
539     if (event->error_code == BadImplementation) return 0;
540 
541     return 1;
542 }
543 
Application_XtWarningHandler(char * message)544 extern "C" void Application_XtWarningHandler(char *message)
545 {
546     if(theApplication)
547         theApplication->handleXtWarning(message);
548 }
handleXtWarning(char * message)549 void Application::handleXtWarning(char *message)
550 {
551    if(strstr(message, "non-existant accelerators") ||
552       strstr(message, "to remove non-existant passive grab") ||
553       strstr(message, "remove accelerators"))
554         return;
555 
556    fprintf(stderr, "XtWarning: %s\n", message);
557 
558 }
559 //
560 // Start a tutorial on behalf of the application.
561 // Return TRUE if successful.  At this level in the class hierachy
562 // we don't know how to start a tutorial so we always return FALSE.
563 //
startTutorial()564 boolean Application::startTutorial()
565 {
566     return FALSE;
567 }
568 //
569 // A virtual method that allows other applications to handle ASSERT
570 // failures among other things.
571 //
abortApplication()572 void Application::abortApplication()
573 {
574     abort();
575 }
576 
577 
578 //
579 // this is normally something like $HOME/DX.  There is a virtual version
580 // of this method in IBMApplication that uses UIRoot on the pc.
581 //
getApplicationDefaultsFileName(char * res_file,boolean create)582 boolean Application::getApplicationDefaultsFileName(char* res_file, boolean create)
583 {
584     const char* class_name = this->getApplicationClass();
585     char* home = (char*)getenv("HOME");
586     int len = strlen(home);
587     strcpy (res_file, home);
588     res_file[len++] = '/';
589     res_file[len++] = '.';
590     char* cp = (char*)class_name;
591     while (*cp) {
592 	res_file[len++] = tolower(*cp);
593 	cp++;
594     }
595     strcpy (&res_file[len], "-ad");
596     return this->isUsableDefaultsFile(res_file, create);
597 }
598 
isUsableDefaultsFile(const char * res_file,boolean create)599 boolean Application::isUsableDefaultsFile(const char* res_file, boolean create)
600 {
601 #if !defined(DXD_OS_NON_UNIX)
602     int ru = S_IRUSR;
603     int wu = S_IWUSR;
604     int rg = S_IRGRP;
605     int ro = S_IROTH;
606     int reg = S_IFREG;
607 #else
608     int ru = _S_IREAD;
609     int wu = _S_IWRITE;
610     int rg = 0;
611     int ro = 0;
612     int reg = _S_IFREG;
613 #endif
614     //
615     // If the file isn't writable, then return FALSE so we
616     // won't try using it to store settings.
617     //
618     boolean writable=TRUE;
619     boolean erase_the_file=FALSE;
620     struct STATSTRUCT statb;
621     if (STATFUNC(res_file, &statb)!=-1) {
622 	//if (S_ISREG(statb.st_mode)) {
623 	if (statb.st_mode & reg) {
624 	    if ((statb.st_mode & wu) == 0) {
625 		writable = FALSE;
626 	    } else if ((statb.st_size==0) && (!create)) {
627 		// file is usable.  If we don't need the file
628 		// and the file size is 1, then erase it.  This
629 		// deals with the mistake I made in creating the
630 		// file in situations where it wouldn't ever be used.
631 		erase_the_file = TRUE;
632 	    }
633 	} else {
634 	    writable = FALSE;
635 	}
636     } else if ((errno==ENOENT)&&(create)) {
637 	int fd = creat(res_file, ru | wu | rg | ro); //S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
638 	if (fd >= 0) {
639 	    close(fd);
640 	} else {
641 	    writable = FALSE;
642 	    //perror(res_file);
643 	}
644     } else {
645 	//perror(res_file);
646 	writable = FALSE;
647     }
648 
649     if ((writable) && (erase_the_file)) {
650 	unlink(res_file);
651 	writable = FALSE;
652     }
653 
654     return writable;
655 }
656 
657