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