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 <defines.h>
11 
12 
13 
14 
15 #include <X11/Intrinsic.h>
16 #include <Xm/Xm.h>
17 #include <Xm/Label.h>
18 #include <Xm/Text.h>
19 #include <Xm/TextF.h>
20 #include <string.h>
21 
22 #include "UIComponent.h"
23 #include "Application.h"
24 #include "WarningDialogManager.h"
25 #include "WarningDialogManager.h"
26 #include "DXStrings.h"
27 
28 #if defined(HAVE_XINERAMA)
29 #include <X11/extensions/Xinerama.h>
30 #endif
31 
32 // SUITS to allow turn-off of stippled drawing on slow devices
33 #define RES_CONVERT(res, str) XtVaTypedArg, res, XmRString, str, strlen(str)+1
34 
35 //
36 // The following are constant understood by setGeometry() and used by
37 // setXYPosition() and setXYSize() and subclasses.
38 //
39 const int UIComponent::UnspecifiedPosition = 32767;
40 const int UIComponent::UnspecifiedDimension = 32767;
41 
42 XtIntervalId UIComponent::BubbleTimer = 0;
43 
UIComponent_WidgetDestroyCB(Widget,XtPointer clientData,XtPointer)44 extern "C" void UIComponent_WidgetDestroyCB(Widget    /* widget */,
45 					XtPointer  clientData,
46 					XtPointer /* callData */)
47 {
48     UIComponent* component;
49 
50     ASSERT(clientData);
51 
52     //
53     // Convert the client data into the UIComponent instance pointer it is.
54     //
55     component = (UIComponent*)clientData;
56 
57     //
58     // Notify the component that its widget is being destroyed.
59     //
60     component->widgetDestroyed();
61 }
62 
UIComponent_ComponentHelpCB(Widget,XtPointer clientData,XtPointer)63 extern "C" void UIComponent_ComponentHelpCB(Widget    /* widget */,
64 					XtPointer  clientData,
65 					XtPointer /* callData */)
66 {
67     UIComponent* component;
68 
69     ASSERT(clientData);
70 
71     //
72     // Convert the client data into the UIComponent instance pointer it is.
73     //
74     component = (UIComponent*)clientData;
75 
76     //
77     // Notify the component that its widget is being destroyed.
78     //
79     component->componentHelp();
80 }
81 
82 
UIComponent(const char * name)83 UIComponent::UIComponent(const char* name)
84 {
85     unsigned int i;
86 
87     ASSERT(name);
88     for(i = 0; i < STRLEN(name); i++)
89     {
90 	if( name[i] == ' '  ||
91 	    name[i] == '\t' ||
92 	    name[i] == '.'  ||
93 	    name[i] == '*')
94 	{
95 	    WarningMessage("UIComponent questionable name: %s", name);
96 	}
97     }
98     this->root   = NUL(Widget);
99     this->name   = DuplicateString(name);
100     this->active = TRUE;
101     this->help_msg = NUL(char*);
102     this->inactive_help_msg = NUL(char*);
103     this->help_widget = NUL(Widget);
104     this->deactivated = FALSE;
105     this->xtt = NULL;
106     this->fg = ((Pixel)-1);
107 }
108 
109 
~UIComponent()110 UIComponent::~UIComponent()
111 {
112     //
113     // Remove destroy callback so Xt Intrinsics canot call the
114     // callback with a pointer to an object that has already
115     // been freed.
116     //
117     if (this->root)
118     {
119 	XtRemoveCallback
120 	    (this->root,
121 	     XmNdestroyCallback,
122 	     (XtCallbackProc)UIComponent_WidgetDestroyCB,
123 	     (XtPointer)this);
124     }
125 
126     //
127     // Destroy the widget tree, if it still exists;
128     // delete the name string.
129     //
130     if (this->root)
131     {
132 	XtDestroyWidget(this->root);
133     }
134 
135     if (this->name)
136 	delete[] this->name;
137 
138     if (this->help_msg)
139 	delete[] this->help_msg;
140 
141     if (this->inactive_help_msg)
142 	delete[] this->inactive_help_msg;
143 }
144 
clearRootWidget()145 void UIComponent::clearRootWidget()
146 {
147     this->root = NULL;
148 }
setRootWidget(Widget root,boolean standardDestroy)149 void UIComponent::setRootWidget(Widget root, boolean standardDestroy)
150 {
151     if (this->root)
152     {
153 	XtRemoveCallback
154 	    (this->root,
155 	     XmNdestroyCallback,
156 	     (XtCallbackProc)UIComponent_WidgetDestroyCB,
157 	     (XtPointer)this);
158 	this->removeComponentHelpCallback();
159     }
160 
161     this->root = root;
162 
163     if (standardDestroy) {
164 	XtAddCallback
165 	    (this->root,
166 	     XmNdestroyCallback,
167 	     (XtCallbackProc)UIComponent_WidgetDestroyCB,
168 	     (XtPointer)this);
169     }
170     this->installComponentHelpCallback();
171 }
172 
setBubbleHelp(const char * msg,Widget w,boolean active_help)173 void UIComponent::setBubbleHelp (const char *msg, Widget w, boolean active_help)
174 {
175     if (active_help) {
176 	if (this->help_msg) delete[] this->help_msg;
177 	this->help_msg = NUL(char*);
178 	if ((!msg) || (!msg[0])) return ;
179 	this->help_msg = DuplicateString(msg);
180     } else {
181 	if (this->inactive_help_msg) delete[] this->inactive_help_msg;
182 	this->inactive_help_msg = NUL(char*);
183 	if ((!msg) || (!msg[0])) return ;
184 	this->inactive_help_msg = DuplicateString(msg);
185     }
186 
187     if (this->help_widget) {
188 	XtRemoveEventHandler (this->help_widget, EnterWindowMask|LeaveWindowMask,
189 	    True, (XtEventHandler)UIComponent_BubbleEH, (XtPointer)this);
190     }
191     this->help_widget = w;
192     if (this->help_widget) {
193 	XtAddEventHandler (this->help_widget, EnterWindowMask|LeaveWindowMask,
194 	    False, (XtEventHandler)UIComponent_BubbleEH, (XtPointer)this);
195     }
196 }
197 
198 
widgetDestroyed()199 void UIComponent::widgetDestroyed()
200 {
201     this->root = NUL(Widget);
202 }
203 
204 
getResources(const XtPointer resourceBase,const XtResourceList resourceList,const int numResources)205 void UIComponent::getResources(const XtPointer      resourceBase,
206 			       const XtResourceList resourceList,
207 			       const int            numResources)
208 {
209     Widget parent;
210 
211     ASSERT(resourceBase);
212     ASSERT(resourceList);
213     ASSERT(numResources > 0);
214 
215     ASSERT(this->root);
216     ASSERT(this->name);
217 
218     parent = XtParent(this->root);
219     //
220     // Use XtGetSubresources() to retrieve the resources
221     // for a component with a parent.
222     //
223     XtGetSubresources
224 	(parent ? parent : this->root,
225 	 resourceBase,
226 	 this->name,
227 	 this->getClassName(),
228 	 resourceList,
229 	 numResources,
230 	 NUL(ArgList),
231 	 0);
232 }
233 
234 
setDefaultResources(const Widget widget,const String * resourceSpec)235 void UIComponent::setDefaultResources(const Widget  widget,
236 				      const String* resourceSpec)
237 {
238     XrmDatabase resourceDatabase;
239     Display*    display;
240     int         i;
241     char        buffer[4096];
242     char	prepend[1024];
243 
244     ASSERT(widget);
245     ASSERT(resourceSpec);
246 
247     display          = XtDisplay(widget);
248     resourceDatabase = XrmGetStringDatabase("");
249 
250     String name,classname;
251     XtGetApplicationNameAndClass(display,&name,&classname);
252 
253     // Determine the base name of the application (i.e. ./dx -> dx)
254 #ifdef	DXD_NON_UNIX_DIR_SEPARATOR
255     char *p = this->name;
256     for (int j=strlen(this->name)-1; j>=0; j--) {
257 	if ((this->name[j] == '\\') || (this->name[j] == '/') || (this->name[j] == ':')) {
258 	    p = &this->name[j+1];
259 	    break;
260 	}
261     }
262 #else
263     char *p = strrchr(this->name,'/');
264     p = ( !p ? this->name : p+1);
265 #endif
266 
267     if (EqualString(name,p)) { 		// Global resources
268 	// Replace the resource name with the class name when the resources
269 	// are for the top level component.
270 	strcpy(prepend,classname);
271     } else {
272 	// Prepend the classname and the component's name
273 	sprintf(prepend,"%s*%s",classname,this->name);
274     }
275 
276 
277     ASSERT(STRLEN(prepend) < 1024);
278 
279     i = 0;
280     while(resourceSpec[i] != NUL(char*))
281     {
282 	sprintf(buffer, "%s%s", prepend, resourceSpec[i++]);
283 	ASSERT(STRLEN(buffer) < 4096);
284 	XrmPutLineResource(&resourceDatabase, buffer);
285     }
286 
287     //
288     // Merge the rosources into the Xt database with lowest precedence.
289     //
290     if (resourceDatabase)
291     {
292 #if defined XlibSpecificationRelease && XlibSpecificationRelease > 4
293         XrmDatabase db = XrmGetDatabase(display);
294         XrmCombineDatabase(resourceDatabase, &db, False);
295 #else
296 	XrmMergeDatabases(display->db, &resourceDatabase);
297 	display->db = resourceDatabase;
298 #endif
299     }
300 }
301 
302 
manage()303 void UIComponent::manage()
304 {
305     ASSERT(this->root);
306 
307     //
308     // Check to see that a destroy callback has been installed.
309     //
310     ASSERT(XtHasCallbacks(this->root, XmNdestroyCallback) == XtCallbackHasSome);
311 
312     //
313     // Manage the root widget.
314     //
315     XtManageChild(this->root);
316 }
317 
318 
isManaged()319 boolean UIComponent::isManaged()
320 {
321     if (!this->root)
322 	return FALSE;
323     return XtIsManaged(this->root);
324 }
325 
unmanage()326 void UIComponent::unmanage()
327 {
328     ASSERT(this->root);
329     XtUnmanageChild(this->root);
330 }
331 
332 
activate()333 void UIComponent::activate()
334 {
335     if (this->root)
336     {
337 	if (!theApplication->bubbleHelpEnabled())
338 	    XtSetSensitive(this->root, TRUE);
339 	else {
340 	    if(deactivated)
341 	    {
342 		if(xtt)
343 		    XtAugmentTranslations(this->root, xtt);
344 		XtVaSetValues(this->root, XmNforeground, this->fg, NULL);
345 		deactivated = FALSE;
346 	    }
347 	}
348     }
349     this->active = TRUE;
350     if (this->inactive_help_msg) {
351 	delete[] this->inactive_help_msg;
352 	this->inactive_help_msg = NUL(char*);
353     }
354 }
355 
356 
deactivate(const char * inactive_help)357 void UIComponent::deactivate(const char * inactive_help)
358 {
359     if (this->root)
360     {
361 	if (!theApplication->bubbleHelpEnabled())
362 	    XtSetSensitive(this->root, FALSE);
363 	else {
364 	    if(!deactivated)
365 	    {
366 		XtVaGetValues(this->root, XmNtranslations, &xtt, NULL);
367 		XtVaGetValues(this->root, XmNforeground, &this->fg, NULL);
368 		XtUninstallTranslations(this->root);
369 		XtVaSetValues(this->root,
370 		    RES_CONVERT(XmNforeground, "gray50"), NULL);
371 		deactivated = TRUE;
372 	    }
373 	}
374     }
375     this->active = FALSE;
376     if ((this->inactive_help_msg) || (inactive_help))
377 	this->setBubbleHelp (inactive_help, this->help_widget, FALSE);
378 }
379 
380 //
381 // Set the size of this component
382 //
setXYSize(int x,int y)383 void UIComponent::setXYSize(int x, int y)
384 {
385     this->setGeometry(UIComponent::UnspecifiedPosition,
386 			UIComponent::UnspecifiedPosition,
387 			x,y);
388 }
389 //
390 // Get the size of this component
391 //
getXYSize(int * x,int * y)392 void UIComponent::getXYSize(int *x, int *y)
393 {
394     int a, b;
395     this->getGeometry(&a, &b, x, y);
396 }
397 //
398 // Get the position of this component
399 //
getXYPosition(int * x,int * y)400 void UIComponent::getXYPosition(int *x, int *y)
401 {
402     int a, b;
403     this->getGeometry(x,y, &a, &b);
404 }
405 //
406 // Set the position of this component
407 //
setXYPosition(int x,int y)408 void UIComponent::setXYPosition(int x, int y)
409 {
410     this->setGeometry(x,y, UIComponent::UnspecifiedDimension,
411                            UIComponent::UnspecifiedDimension);
412 }
413 
414 //
415 // Get the size and dimensions.
416 // Return TRUE if all return values are valid.
417 //
getGeometry(int * x,int * y,int * width,int * height)418 boolean UIComponent::getGeometry(int *x, int *y, int *width, int *height)
419 {
420     Position a,b;
421     Dimension w,h;
422 
423     ASSERT(this->root);
424 
425     XtVaGetValues(this->root,
426         XmNx, &a,
427         XmNy, &b,
428         XmNwidth, &w,
429         XmNheight, &h,
430         NULL);
431 
432    *x = a;
433    *y = b;
434    *width = w;
435    *height = h;
436 
437    return TRUE;
438 }
439 //
440 // Set the size and dimensions.
441 //
setGeometry(int x,int y,int width,int height)442 void UIComponent::setGeometry(int x, int y, int width, int height)
443 {
444     ASSERT(this->root);
445     Arg wargs[4];
446     int     n = 0;
447 
448 #if 1
449     if (x != UIComponent::UnspecifiedPosition)
450 	{ XtSetArg(wargs[n],XmNx,x); n++; }
451     if (y != UIComponent::UnspecifiedPosition)
452 	{ XtSetArg(wargs[n],XmNy,y); n++; }
453     if (width != UIComponent::UnspecifiedDimension)
454 	{ XtSetArg(wargs[n],XmNwidth,width); n++; }
455     if (height != UIComponent::UnspecifiedDimension)
456 	{ XtSetArg(wargs[n],XmNheight,height); n++; }
457     if (n > 0)
458         XtSetValues(this->root,wargs,n);
459 #else
460     XtWidgetGeometry req;
461 
462     req.x = x; req.y = y; req.width = width; req.height = height;
463     req.request_mode = 0;
464     if (x != UIComponent::UnspecifiedPosition)  req.request_mode|= CWX;
465     if (y != UIComponent::UnspecifiedPosition)  req.request_mode|= CWY;
466     if (width != UIComponent::UnspecifiedDimension)  req.request_mode|= CWWidth;
467     if (height != UIComponent::UnspecifiedDimension)  req.request_mode|= CWHeight;
468     if (req.request_mode)
469 	XtMakeGeometryRequest (this->root, &req, NULL);
470 #endif
471 }
472 
473 
474 
installComponentHelpCallback(Widget widget)475 void UIComponent::installComponentHelpCallback(Widget widget)
476 {
477 
478     if (!widget)
479 	widget = this->root;
480     ASSERT(widget);
481 
482     if (XtHasCallbacks(widget, XmNhelpCallback) != XtCallbackNoList)
483     {
484 	// Ensure that only one copy of the help callback is installed.
485 	XtRemoveCallback
486 	    (widget,
487 	     XmNhelpCallback,
488 	     (XtCallbackProc)UIComponent_ComponentHelpCB,
489 	     (XtPointer)this);
490 	XtAddCallback
491 	    (widget,
492 	     XmNhelpCallback,
493 	     (XtCallbackProc)UIComponent_ComponentHelpCB,
494 	     (XtPointer)this);
495     }
496 }
497 
removeComponentHelpCallback(Widget widget)498 void UIComponent::removeComponentHelpCallback(Widget widget)
499 {
500 
501     if (!widget)
502 	widget = this->root;
503     ASSERT(widget);
504 
505     if (XtHasCallbacks(widget, XmNhelpCallback) != XtCallbackNoList)
506 	XtRemoveCallback
507 	    (widget,
508 	     XmNhelpCallback,
509 	     (XtCallbackProc)UIComponent_ComponentHelpCB,
510 	     (XtPointer)this);
511 }
512 
513 
getComponentHelpTopic()514 const char *UIComponent::getComponentHelpTopic()
515 {
516     return this->name;
517 }
componentHelp()518 void UIComponent::componentHelp()
519 {
520     theApplication->helpOn(this->getComponentHelpTopic());
521 }
setLocalData(void * data)522 void UIComponent::setLocalData(void *data)
523 {
524     ASSERT(this->root);
525     Arg arg[1];
526     XtSetArg(arg[0], XmNuserData, (XtPointer)data);
527     XtSetValues(this->root, arg,1);
528 }
529 
getLocalData()530 void *UIComponent::getLocalData()
531 {
532     void *data;
533     ASSERT(this->root);
534     Arg arg[1];
535     XtSetArg(arg[0], XmNuserData, (XtPointer)&data);
536     XtGetValues(this->root, arg,1);
537     return data;
538 }
539 
540 /*
541     ParseGeometryComment actually performs the Window placement and
542     Window sizing for shell windows such as the ImageWindow and
543     Control Panels. The network saves the placement as a normalized
544     vector of where and what size it was on the authors screen
545     and then tries to replicate that to fit the screen of others.
546     This becomes a real problem when Xinerama comes into play. For example,
547     the width/height ratio changes significantly and makes ImageWindows
548     very wide when reconstructing them if the Xinerama environment is
549     in place. For example DisplayWidth may be 2560 and Height may only
550     be 1024. But the original developer was on a system of 1280 x 1024.
551     The resulting image doesn't look anything like the authors.
552 
553     The fix for placement is to check for Xinerama and replicate on
554     the dominate screen. Then this also needs to be fixed in PrintGeometryComment
555     as well.
556 
557     It will not be possible to replicate the multiple screen geometry
558     unless the support for Xinerama is turned off. For example: two
559     identical screens side by side. If the user builds with the VPE on
560     the primary screen, and puts the Image Window on the secondary screen,
561     when the Editor is re-opened and executed, the Image Window will
562     appear on the primary screen. The only way to fix this would be
563     to append the screen number in the GeometryComment
564  */
565 
PrintGeometryComment(FILE * f,int xpos,int ypos,int xsize,int ysize,const char * tag,const char * indent)566 boolean UIComponent::PrintGeometryComment(FILE *f, int xpos, int ypos,
567                                 int xsize, int ysize, const char *tag,
568                                 const char *indent)
569 {
570     float norm_xsize, norm_ysize, norm_xpos, norm_ypos;
571 
572     if (!tag)
573         tag = "window";
574 
575     if (!indent)
576         indent = "";
577 
578 	Display *disp = theApplication->getDisplay();
579     int width = DisplayWidth(disp,0);
580     int height = DisplayHeight(disp,0);
581     int x=0, y=0;
582     int screen = -1;
583 
584  #if defined(HAVE_XINERAMA)
585 			// Do some Xinerama Magic to use the largest main screen.
586 			int dummy_a, dummy_b;
587 			int screens;
588 			XineramaScreenInfo   *screeninfo = NULL;
589 
590 			if ((XineramaQueryExtension (disp, &dummy_a, &dummy_b)) &&
591 				(screeninfo = XineramaQueryScreens(disp, &screens))) {
592 				// Xinerama Detected
593 
594 				if (XineramaIsActive(disp)) {
595 					width = 0; height = 0;
596 					int i = dummy_a;
597 					while ( i < screens ) {
598 						if(xpos > screeninfo[i].x_org &&
599 							xpos < screeninfo[i].x_org+screeninfo[i].width &&
600 							ypos > screeninfo[i].y_org &&
601 							ypos < screeninfo[i].y_org+screeninfo[i].height ) {
602 							screen = i;
603 							width = screeninfo[i].width;
604 							height = screeninfo[i].height;
605 							x = screeninfo[i].x_org;
606 							y = screeninfo[i].y_org;
607 						}
608 						i++;
609 					}
610 				}
611 			}
612 #endif
613 
614     norm_xsize = (float) xsize/width;
615     norm_ysize = (float) ysize/height;
616     norm_xpos  = (float) (xpos - x)/width;
617     norm_ypos  = (float) (ypos - y)/height;
618     if(screen != -1) {
619     	if (fprintf(f,
620           "%s// %s: position = (%6.4f,%6.4f), size = %6.4fx%6.4f, screen = %d\n",
621                 indent, tag, norm_xpos,norm_ypos,norm_xsize,norm_ysize,screen) < 0)
622         return FALSE;
623     } else {
624     	if (fprintf(f,
625           "%s// %s: position = (%6.4f,%6.4f), size = %6.4fx%6.4f\n",
626                 indent, tag, norm_xpos,norm_ypos,norm_xsize,norm_ysize) < 0)
627         return FALSE;
628     }
629 
630     return TRUE;
631 }
632 
ParseGeometryComment(const char * line,const char * file,int lineno,int * xpos,int * ypos,int * xsize,int * ysize,const char * tag)633 boolean UIComponent::ParseGeometryComment(const char *line, const char *file,
634                                 int lineno, int *xpos, int *ypos,
635                                 int *xsize, int *ysize, const char *tag)
636 {
637     char format[1024];
638     int items;
639     float norm_xsize, norm_ysize, norm_xpos, norm_ypos;
640     int screen = -1;
641     int display_xsize, display_ysize;
642 
643     if (!tag)
644         tag = "window";
645 
646     int taglen = STRLEN(tag);
647     sprintf(format," %s: position = (%%f,%%f), size = %%fx%%f, screen = %%d",tag);
648 
649     // 15 = strlen(" ") + strlen(": position = (")
650     if (!EqualSubstring(line,format,taglen+15))
651         return FALSE;
652 
653     items = sscanf(line,format,&norm_xpos,&norm_ypos,&norm_xsize,&norm_ysize,&screen);
654 
655     if (items == 4 || items == 5) {
656         // If the size when printed was UIComponent::UnspecifiedPosition
657         // then the normalized value will be greater than 3, so ignore it.
658         if ((norm_xsize < 3) && (norm_ysize < 3)) {
659 
660 			int x=0, y=0, width=0, height=0;
661  			Display *disp = theApplication->getDisplay();
662             width = DisplayWidth(disp,0);
663             height = DisplayHeight(disp,0);
664 
665  #if defined(HAVE_XINERAMA)
666 			// Do some Xinerama Magic to use the largest main screen.
667 			int dummy_a, dummy_b;
668 			int screens;
669 			XineramaScreenInfo   *screeninfo = NULL;
670 
671 			if ((XineramaQueryExtension (disp, &dummy_a, &dummy_b)) &&
672 				(screeninfo = XineramaQueryScreens(disp, &screens))) {
673 				// Xinerama Detected
674 		//fprintf(stderr, "screens = %d, screen = %d, \nline = %s\n", screens, screen, line);
675 				if (XineramaIsActive(disp)) {
676 					width = 0; height = 0;
677 					int i = dummy_a;
678 					if(screen != -1 && screen <= screens) {
679 						width = screeninfo[screen].width;
680 						height = screeninfo[screen].height;
681 						x = screeninfo[screen].x_org;
682 						y = screeninfo[screen].y_org;
683 					} else
684 					while ( i < screens ) {
685 						if(screeninfo[i].width > width) {
686 							width = screeninfo[i].width;
687 							height = screeninfo[i].height;
688 							x = screeninfo[i].x_org;
689 							y = screeninfo[i].y_org;
690 						}
691 						i++;
692 					}
693 				}
694 			}
695 #endif
696             *xpos  = (int) (width * norm_xpos  + .5 + x);
697             *ypos  = (int) (height * norm_ypos  + .5 + y);
698             *xsize = (int) (width * norm_xsize + .5);
699             *ysize = (int) (height * norm_ysize + .5);
700         } else {
701             *xpos = UIComponent::UnspecifiedPosition;
702             *ypos = UIComponent::UnspecifiedPosition;
703             *xsize = UIComponent::UnspecifiedDimension;
704             *ysize = UIComponent::UnspecifiedDimension;
705         }
706     } else {
707         WarningMessage("Bad comment found in file '%s' line %d (ignoring)",
708                                         file,lineno);
709     }
710 
711     return TRUE;
712 }
713 
714 extern "C" void
UIComponent_InstallBubbleTP(XtPointer cData,XtIntervalId * id)715 UIComponent_InstallBubbleTP (XtPointer cData, XtIntervalId *id)
716 {
717     UIComponent *uic = (UIComponent*)cData;
718     uic->showBubbleHelp();
719     UIComponent::BubbleTimer = 0;
720 }
721 
722 extern "C" void
UIComponent_BubbleEH(Widget w,XtPointer cData,XEvent * xev,Boolean * doit)723 UIComponent_BubbleEH (Widget w, XtPointer cData, XEvent* xev, Boolean* doit)
724 {
725     *doit = True;
726     UIComponent *uic = (UIComponent*)cData;
727     if (xev->type == EnterNotify) {
728 	if (UIComponent::BubbleTimer)
729 	    XtRemoveTimeOut (UIComponent::BubbleTimer);
730 	UIComponent::BubbleTimer =
731 	    XtAppAddTimeOut (theApplication->getApplicationContext(),
732 		(unsigned long)100, (XtTimerCallbackProc)UIComponent_InstallBubbleTP,
733 		(XtPointer)uic);
734 	//uic->showBubbleHelp();
735     } else if (xev->type == LeaveNotify) {
736 	if (UIComponent::BubbleTimer)
737 	    XtRemoveTimeOut (UIComponent::BubbleTimer);
738 	else
739 	    uic->eraseBubbleHelp();
740     }
741 }
742 
743 void
showBubbleHelp()744 UIComponent::showBubbleHelp()
745 {
746     if (!theApplication->bubbleHelpEnabled())  return ;
747 
748     Widget help_viewer = theApplication->getHelpViewer();
749     if (!help_viewer) return ;
750 
751     char *help_msg;
752     if ((this->inactive_help_msg) && (!this->active))
753 	help_msg = this->inactive_help_msg;
754     else
755 	help_msg = this->help_msg;
756     if (!help_msg) return ;
757 
758     if (XtIsSubclass(help_viewer, xmLabelWidgetClass)) {
759 	XmString xmstr = XmStringCreateLtoR (help_msg, "small_normal");
760 	XtVaSetValues (help_viewer, XmNlabelString, xmstr, NULL);
761 	XmStringFree(xmstr);
762     } else if (XtIsSubclass(help_viewer, xmTextWidgetClass)) {
763 	XtVaSetValues (help_viewer, XmNvalue, help_msg, NULL);
764     } else if (XtIsSubclass(help_viewer, xmTextFieldWidgetClass)) {
765 	XtVaSetValues (help_viewer, XmNvalue, help_msg, NULL);
766     } else ASSERT(0);
767 }
768 
769 void
eraseBubbleHelp()770 UIComponent::eraseBubbleHelp()
771 {
772     if (!theApplication->bubbleHelpEnabled())  return ;
773 
774     Widget help_viewer = theApplication->getHelpViewer();
775     if (!help_viewer) return ;
776 
777     if (XtIsSubclass(help_viewer, xmLabelWidgetClass)) {
778 	XmString xmstr = XmStringCreateLtoR ("", "small_normal");
779 	XtVaSetValues (help_viewer, XmNlabelString, xmstr, NULL);
780 	XmStringFree(xmstr);
781     } else if (XtIsSubclass(help_viewer, xmTextWidgetClass)) {
782 	XtVaSetValues (help_viewer, XmNvalue, "", NULL);
783     } else if (XtIsSubclass(help_viewer, xmTextFieldWidgetClass)) {
784 	XtVaSetValues (help_viewer, XmNvalue, "", NULL);
785     } else ASSERT(0);
786 }
787 
788