1 /*
2 LiteClue.c - LiteClue widget
3 	See LiteClue documentation
4 	Version 1.4
5 
6 Copyright 1996 COMPUTER GENERATION, INC.,
7 
8 The software is provided "as is", without warranty of any kind, express
9 or implied, including but not limited to the warranties of
10 merchantability, fitness for a particular purpose and noninfringement.
11 In no event shall Computer Generation, inc. nor the author be liable for
12 any claim, damages or other liability, whether in an action of contract,
13 tort or otherwise, arising from, out of or in connection with the
14 software or the use or other dealings in the software.
15 
16 Permission to use, copy, modify, and distribute this software and its
17 documentation for any purpose and without fee is hereby granted,
18 provided that the above copyright notice appear in all copies and that
19 both that copyright notice and this permission notice appear in
20 supporting documentation.
21 
22 Author:
23 Gary Aviv
24 Computer Generation, Inc.,
25 gary@compgen.com
26 www.compgen.com/widgets
27 
28 Thanks to Contributers:
29 J Satchell, Eric Marttila
30 */
31 /* Revision History:
32 $Log: LiteClue.c,v $
33 Revision 1.16  1998/09/07 14:06:19  gary
34 Added const to prototype of XcgLiteClueAddWidget at request from user
35 
36 Revision 1.15  1998/07/30 16:05:16  gary
37 Add NO_FONT_SET to usr FontStruct rather than FontSet
38 
39 Revision 1.14  1998/01/06 15:30:33  gary
40 If font specified by resource can not be converted, use fixed
41 font as fallback. If no font at all can be converted, prevent
42 crash, just disable widget entirely.
43 
44 Revision 1.13  1997/07/07 14:55:04  gary
45 Cancel timeouts when XcgLiteClueDeleteWidget is called to prevent
46 errant timeout event on deleted widget.
47 
48 Revision 1.12  1997/06/20 20:09:09  gary
49 Add XcgLiteClueDispatchEvent to enable clues for insensitive widgets.
50 
51 Revision 1.11  1997/06/15 14:10:24  gary
52 Add XcgLiteClueDispatchEvent to enable clues for insensitive widgets.
53 
54 Revision 1.10  1997/04/14 13:02:33  gary
55 Attempt to fix problem when we get multiple enter events bu no leave event.
56 
57 Revision 1.9  1997/03/10 14:42:41  gary
58 Attempt to fix problem when we get multiple enter events bu no leave event.
59 Add C++ wrapper to allow linking with C++ programs. (In HView.h)
60 
61 Revision 1.8  1997/01/17 13:44:14  gary
62 Support of cancelWaitPeriod resource: this is a period from the point
63 a help popdown occurs in which the normal waitPeriod is suspended
64 for the next popup
65 
66 Revision 1.7  1996/12/16 22:35:38  gary
67 Fix double entry problem
68 
69 Revision 1.6  1996/11/18 14:52:21  gary
70 remove some compile warnings pointed out by a user
71 
72 Revision 1.5  1996/11/12 20:56:43  gary
73 remove some compile warnings
74 
75 Revision 1.4  1996/10/20  13:38:16  gary
76 Version 1.2 freeze
77 
78 Revision 1.3  1996/10/19 16:16:30  gary
79 Compile warning removed with cast
80 
81 Revision 1.2  1996/10/19 16:07:38  gary
82 a) R4 back compatibility
83 b) Delay before pop up of help, waitPeriod resource (def 500 ms).
84 	Thanks to J Satchell for this.
85 c) Button press in watched widget pops down help
86 
87 Revision 1.1  1996/10/18 23:14:58  gary
88 Initial
89 
90 
91 $log
92 Added const to prototype of XcgLiteClueAddWidget at request from user
93 $log
94 */
95 #include <unistd.h>
96 #include <signal.h>
97 /* #include <Xm/XmP.h> */
98 #include <X11/IntrinsicP.h>
99 #include <X11/StringDefs.h>
100 
101 #include "LiteClueP.h"
102 
103 #include <stdio.h>
104 
105 #define CheckWidgetClass(routine) \
106 	if (XtClass(w) != xcgLiteClueWidgetClass) \
107 		wrong_widget(routine)
108 
109 
110 static Boolean setValues( Widget _current, Widget _request, Widget _new, ArgList args, Cardinal * num_args);
111 static void Initialize(Widget treq, Widget tnew, ArgList args, Cardinal *num_args);
112 struct liteClue_context_str * alloc_liteClue_context(void);
113 static void popdown_timeout_event( XtPointer client_data, XtIntervalId *id);
114 
115 /* keep information about each widget we are keeping track of */
116 struct liteClue_context_str
117 {
118 	ListThread next;	/* next in list */
119 	Widget watched_w;	/* the widget we are watching */
120 	XcgLiteClueWidget cw;	/* pointer back to the liteClue widget */
121 	Position  abs_x, abs_y;
122 	Boolean sensitive;	/* if False, liteClue is suppressed */
123 	char * text;		/* text to display */
124 	short text_size;	/* its size */
125 };
126 
127 void free_widget_context(XcgLiteClueWidget cw, struct liteClue_context_str * obj);
128 /*
129 Widget resources: eg to set LiteClue box background:
130  *XcgLiteClue.background: yellow
131 
132 */
133 #define offset(field) XtOffsetOf(LiteClueRec, field)
134 static XtResource LC_resources[] =
135 {
136 	{XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
137 		offset(liteClue.foreground), XtRString, "black"},
138 #if XtSpecificationRelease < 5 || defined(NO_FONT_SET)
139 	{XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
140 		offset(liteClue.fontset), XtRString,
141           "-adobe-new century schoolbook-bold-r-normal-*-14-*-*-*-*-*-*-*"},
142 #else
143 	{XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
144 		offset(liteClue.fontset), XtRString, "-adobe-new century schoolbook-bold-r-normal-*-12-*"},
145 #endif
146 	{XgcNwaitPeriod, XgcCWaitPeriod, XtRInt , sizeof(int),
147 		offset(liteClue.waitPeriod),XtRString, "500" },
148 
149 	{XgcNwaitPeriod2, XgcCWaitPeriod, XtRInt , sizeof(int),
150 		offset(liteClue.waitPeriod2),XtRString, "2000" },
151 
152 	{XgcNcancelWaitPeriod, XgcCCancelWaitPeriod, XtRInt , sizeof(int),
153 		offset(liteClue.cancelWaitPeriod),XtRString, "2000" },
154 };
155 
156 #undef offset
157 
158 #if 0
159 static XtActionsRec actions[] =
160 }; /* actions */
161 #endif
162 
163 
164 LiteClueClassRec xcgLiteClueClassRec =
165 {
166     {
167 	(WidgetClass)&overrideShellClassRec,	/* superclass */
168 	"XcgLiteClue",				/* class_name */
169 	(Cardinal)sizeof(LiteClueRec),		/* widget size */
170 	NULL,					/* class_init */
171 	(XtWidgetClassProc)NULL,		/* class_part_init */
172 	(XtEnum)FALSE,				/* class_inited */
173 	(XtInitProc)Initialize,			/* initialize */
174 	(XtArgsProc)NULL,			/* init_hook */
175 	XtInheritRealize,			/* realize */
176 	(XtActionList)0,			/* actions */
177 	(Cardinal)0,			/* num_actions */
178 	(XtResourceList)LC_resources,		/* resources */
179 	(Cardinal)XtNumber(LC_resources),		/* num_resources */
180 	NULLQUARK,				/* xrm_class */
181 	TRUE,					/* compress_motion */
182 	(XtEnum)FALSE,				/* compress_exposur */
183 	TRUE,					/* compress enterleave */
184 	FALSE,					/* visibility_interest */
185 	(XtWidgetProc)NULL,			/* destroy */
186 	XtInheritResize,
187 	XtInheritExpose,			/* expose, */
188 	(XtSetValuesFunc)setValues,		/* set_values */
189 	(XtArgsFunc)NULL,			/* set_values_hook */
190 	XtInheritSetValuesAlmost,		/* set_values_almost */
191 	(XtArgsProc)NULL,			/* get_values_hook */
192 	XtInheritAcceptFocus,		/* accept_focus */
193 	XtVersion,				/* version */
194 	(XtPointer)NULL,			/* callback_private */
195 	XtInheritTranslations,
196 	XtInheritQueryGeometry,			/* query_geometry */
197 	XtInheritDisplayAccelerator,		/* display_accelerator */
198 	(XtPointer)0,			/* extension */
199     },
200     { /*** composite-Class ***/
201 	XtInheritGeometryManager,	/* geometry_manager   	*/
202 	XtInheritChangeManaged,	/* change_managed		*/
203 	XtInheritInsertChild,	/* insert_child		*/
204 	XtInheritDeleteChild,	/* delete_child		*/
205 	NULL	/* extension		*/
206     },
207 	{ /* Shell */
208 	(XtPointer) NULL,       /* pointer to extension record      */
209 	},
210 	/* Override Shell */
211 	{
212 	0,
213 	},
214 	/* LiteClue */
215 	{
216 	0,
217 	},
218 };
219 
220 WidgetClass xcgLiteClueWidgetClass = (WidgetClass) & xcgLiteClueClassRec;
221 
222 /* doubly linked list processing */
223 
224 /*
225 	 initialize header - both pointers point to it
226 */
227 static void xcgListInit(ListThread *newbuf)
228 {
229 	newbuf->back = newbuf;
230 	newbuf->forw = newbuf;
231 }
232 
233 
234 /*
235 	 insert newbuf before posbuf
236 */
237 static void xcgListInsertBefore(ListThread *newlist, ListThread *poslist)
238 {
239 	ListThread *prevbuf;
240 
241 	prevbuf = poslist->back;
242 
243 	poslist->back = newlist;
244 	newlist->forw = poslist;
245 	newlist->back = prevbuf;
246 	prevbuf->forw = newlist;
247 }
248 
249 
250 /*
251 	remove rembuf from queue
252 */
253 static ListThread * xcgListRemove(ListThread *rembuf)
254 {
255 	ListThread *prevbuf, *nextbuf;
256 
257 	prevbuf = rembuf->back;
258 	nextbuf = rembuf->forw;
259 
260 	prevbuf->forw = nextbuf;
261 	nextbuf->back = prevbuf;
262 
263 	rembuf->back = (ListThread *) NULL;	/* for safety to cause trap if ..*/
264 	rembuf->forw = (ListThread *) NULL;	/* .. mistakenly refed */
265 	return rembuf;
266 }
267 
268 /*
269 The font_information is derived
270 */
271 
272 #if XtSpecificationRelease < 5 || defined(NO_FONT_SET)
273 
274 /* R4 and below code */
275 /*
276 Return XFontSet for passed font_string.
277 return status
278 */
279 static int string_to_FontSet (XcgLiteClueWidget cw, char * font_string, XFontStruct ** out)
280 {
281 	Boolean sts;
282 	XrmValue from;
283 	XrmValue to;
284 
285 	to.size = sizeof(out);
286 	to.addr = (void *) out;
287 	from.size = strlen(from.addr = font_string );
288 	sts = XtConvertAndStore((Widget) cw, XtRString, &from, XtRFontStruct, &to);
289 	return sts;
290 }
291 
292 static void compute_font_info(XcgLiteClueWidget cw)
293 {
294 	int direction_return;
295 	int font_ascent_return, font_descent_return;
296 	XCharStruct oret;
297 	if (!cw->liteClue.fontset)
298 		string_to_FontSet(cw, "fixed", &cw->liteClue.fontset) ;
299 	if (!cw->liteClue.fontset)
300 	{
301 		fprintf(stderr,"LiteClue: can not find resource font nor fallback fixed font\n");
302 		return;
303 	}
304 	XTextExtents( cw->liteClue.fontset, "1", 1,
305 		&direction_return,
306 		&font_ascent_return, &font_descent_return, &oret);
307 
308 	cw->liteClue.font_baseline = oret.ascent;	/* y offset from top to baseline,
309 			don't know why this is returned as negative */
310 	cw->liteClue.font_width = oret.width;	/* the width and height of the object */
311 	cw->liteClue.font_height = oret.ascent+oret.descent;
312 }
313 
314 #else
315 /*
316 Return XFontSet for passed font_string.
317 return status
318 */
319 static int string_to_FontSet (XcgLiteClueWidget cw, char * font_string, XFontSet * out)
320 {
321 	Boolean sts;
322 	XrmValue from;
323 	XrmValue to;
324 
325 	to.size = sizeof(out);
326 	to.addr = (void *) out;
327 	from.size = strlen(from.addr = font_string );
328 	sts = XtConvertAndStore((Widget) cw, XtRString, &from, XtRFontSet, &to);
329 	return sts;
330 }
331 
332 /* R5 and above code */
333 static void compute_font_info(XcgLiteClueWidget cw)
334 {
335 	XRectangle ink;
336 	XRectangle logical;
337 
338 	if (!cw->liteClue.fontset)
339 		string_to_FontSet(cw, "fixed", &cw->liteClue.fontset) ;
340 	if (!cw->liteClue.fontset)
341 	{
342 		fprintf(stderr,"LiteClue: can not find resource font nor fallback fixed font\n");
343 		return;
344 	}
345 	XmbTextExtents(cw->liteClue.fontset, "1", 1,&ink, &logical);
346 
347 	cw->liteClue.font_baseline = -logical.y;	/* y offset from top to baseline,
348 			don't know why this is returned as negative */
349 	cw->liteClue.font_width = logical.width;	/* the width and height of the object */
350 	cw->liteClue.font_height = logical.height;
351 }
352 #endif
353 
354 /*
355  Creates the various graphic contexts we will need
356 */
357 static void create_GC(XcgLiteClueWidget cw )
358 {
359 	XtGCMask valuemask;
360 	XGCValues myXGCV;
361 
362 
363 	valuemask = GCForeground | GCBackground | GCFillStyle ;
364 	myXGCV.foreground = cw->liteClue.foreground;
365 	myXGCV.background = cw->core.background_pixel;
366 	myXGCV.fill_style = FillSolid;
367 
368 #if XtSpecificationRelease < 5	|| defined(NO_FONT_SET)
369 	valuemask |= GCFont ;
370 	myXGCV.font = cw->liteClue.fontset->fid;
371 #endif	/* end R4 hack */
372 
373 	if (cw->liteClue.text_GC )
374 		XtReleaseGC((Widget) cw, cw->liteClue.text_GC );
375 	cw->liteClue.text_GC = XtGetGC((Widget)cw, valuemask, &myXGCV);
376 }
377 
378 
379 /* a routine to halt execution and force
380 a core dump for debugging analysis
381 when a public routine is called with the wrong class of widget
382 */
383 static void wrong_widget(char * routine)
384 {
385 	int mypid = getpid();
386 	fprintf(stderr, "Wrong class of widget passed to %s\n", routine);
387 	fflush(stderr);
388 	kill(mypid, SIGABRT);
389 }
390 
391 /*
392 Find the target in the widget list. Return context pointer if found,
393 NULL if not
394 */
395 static struct liteClue_context_str * find_watched_widget(XcgLiteClueWidget cw,
396 	Widget target)
397 {
398 	struct liteClue_context_str * obj;
399 
400 	for (obj = (struct liteClue_context_str *) cw->liteClue.widget_list.forw;
401 		obj != (struct liteClue_context_str *) & cw->liteClue.widget_list;
402 		obj = (struct liteClue_context_str *)obj->next.forw )
403 	{
404 		if (target == obj->watched_w)
405 			return obj;
406 	}
407 	return NULL;
408 }
409 
410 /*
411 	allocate and initialize a widget context
412 */
413 struct liteClue_context_str * alloc_liteClue_context(void)
414 {
415 	struct liteClue_context_str * out;
416 	out = (struct liteClue_context_str *) XtMalloc(sizeof(struct liteClue_context_str));
417 	memset(out, 0, sizeof(struct liteClue_context_str));
418 	xcgListInit(&out->next);
419 	return out ;
420 }
421 
422 /*
423 	allocate, initialize and link a liteClue context to the list
424 */
425 static struct liteClue_context_str * alloc_link_liteClue_context(XcgLiteClueWidget cw )
426 {
427 	struct liteClue_context_str * out = alloc_liteClue_context();
428 
429 	/* link as new last */
430 	xcgListInsertBefore(&out->next, &cw->liteClue.widget_list);
431 	out->cw = cw;	/* initialize this emeber - its always the same */
432 	return out;
433 }
434 
435 /*
436 	free a widget context
437 */
438 void free_widget_context(XcgLiteClueWidget cw, struct liteClue_context_str * obj)
439 {
440 	xcgListRemove((ListThread *)obj);
441 	/* free up all things object points to */
442 	obj->sensitive = False;
443 	if (obj->text )
444 		XtFree(obj->text);
445 	XtFree((char *) obj);
446 }
447 
448 
449 /* -------------------- Widget Methods ---------------------- */
450 /* Initialize method */
451 static void Initialize(Widget treq, Widget tnew, ArgList args,
452 Cardinal *num_args)
453 {
454 	XcgLiteClueWidget cw = (XcgLiteClueWidget) tnew;
455 
456 
457 	cw->liteClue.text_GC = NULL;
458 	cw->liteClue.HelpIsUp = False;
459 	cw->liteClue.HelpPopDownTime = 0;
460 	cw->liteClue.interval_id = (XtIntervalId)0;
461 	cw->liteClue.interval_id2 = (XtIntervalId)0;
462 	xcgListInit(&cw->liteClue.widget_list);	/* initialize empty list */
463 	compute_font_info(cw);
464 	create_GC(cw );
465 }
466 
467 static Boolean setValues( Widget _current, Widget _request, Widget _new, ArgList args, Cardinal * num_args)
468 {
469 	XcgLiteClueWidget cw_new = (XcgLiteClueWidget) _new;
470 	XcgLiteClueWidget cw_cur = (XcgLiteClueWidget) _current;
471 
472 	/* values of cw_new->liteClue.cancelWaitPeriod and
473 	   cw_new->liteClue.waitPeriod are accepted without checking */
474 
475 	if (cw_new->liteClue.foreground != cw_cur->liteClue.foreground
476 	||  cw_new->core.background_pixel != cw_cur->core.background_pixel )
477 	{
478 		create_GC(cw_new);
479 	}
480 
481 	return FALSE;
482 }
483 
484 /* ----------------- Event handlers ------------------------*/
485 
486 /* At this point the help may be popup
487 */
488 static void timeout_event( XtPointer client_data, XtIntervalId *id)
489 {
490 #define BorderPix 2
491 	struct liteClue_context_str * obj = (struct liteClue_context_str *) client_data;
492 	XcgLiteClueWidget cw = obj->cw;
493 
494 	XRectangle ink;
495 	XRectangle logical;
496 	Widget w;
497 	Window rwin, cld;
498 	int rx, ry, wx, wy, width, height;
499 	unsigned int mask;
500 	Display *dpy;
501 	int scr;
502 
503 	if (cw->liteClue.interval_id == (XtIntervalId)0)
504 		return;	/* timeout was removed but callback happened anyway */
505 	cw->liteClue.interval_id = (XtIntervalId)0;
506 	if (obj->sensitive == False)
507 		return;
508 
509 	w = obj->watched_w;
510 
511 #if XtSpecificationRelease < 5	|| defined(NO_FONT_SET)
512 	{
513 	int direction_return;
514 	int font_ascent_return, font_descent_return;
515 	XCharStruct oret;
516 	XTextExtents( cw->liteClue.fontset ,obj->text , obj->text_size,
517 		&direction_return,
518 		&font_ascent_return, &font_descent_return, &oret);
519 	logical.width = oret.width;
520 	}
521 #else
522 	XmbTextExtents(cw->liteClue.fontset, obj->text , obj->text_size ,&ink, &logical);
523 #endif
524 
525 	dpy = XtDisplay((Widget) cw);
526 	XQueryPointer(dpy, DefaultRootWindow(dpy),
527 			&rwin, &cld, &rx, &ry, &wx, &wy, &mask);
528 	scr = DefaultScreen(dpy);
529 	rx += 3;
530 	ry += 15;
531 	wx = DisplayWidth(dpy, scr);
532 	wy = DisplayHeight(dpy, scr);
533 	width  = 2*BorderPix +logical.width;
534 	height = 2*BorderPix + cw->liteClue.font_height;
535 
536 	if(rx+width > wx)
537 		rx = wx - width;
538 	if(ry+height > wy)
539 		ry = wy - height;
540 
541 	XtResizeWidget((Widget) cw, width, height, cw->core.border_width );
542 
543 	XtMoveWidget((Widget) cw, rx, ry);
544 	XtPopup((Widget) cw, XtGrabNone);
545 	cw->liteClue.HelpIsUp = True;
546 
547 	cw->liteClue.interval_id2 = XtAppAddTimeOut(
548 			XtWidgetToApplicationContext(w),
549 			cw->liteClue.waitPeriod2,
550 			popdown_timeout_event, client_data);
551 
552 #if XtSpecificationRelease < 5	|| defined(NO_FONT_SET)
553 	XDrawImageString(XtDisplay((Widget) cw), XtWindow((Widget) cw),
554 		cw->liteClue.text_GC , BorderPix,
555 		BorderPix + cw->liteClue.font_baseline, obj->text , obj->text_size);
556 #else
557 	XmbDrawImageString(XtDisplay((Widget) cw), XtWindow((Widget) cw),
558 		cw->liteClue.fontset,
559 		cw->liteClue.text_GC , BorderPix,
560 		BorderPix + cw->liteClue.font_baseline, obj->text , obj->text_size);
561 #endif
562 }
563 
564 /*
565 Pointer enters watched widget, set a timer at which time it will
566 popup the help
567 */
568 static void Enter_event(Widget w, XtPointer client_data, XEvent * xevent, Boolean * continue_to_dispatch )
569 {
570 	struct liteClue_context_str * obj = (struct liteClue_context_str *) client_data;
571 	XcgLiteClueWidget cw = obj->cw;
572 	XEnterWindowEvent * event = & xevent->xcrossing;
573 	int current_waitPeriod ;
574 
575 	if (obj->sensitive == False || !cw->liteClue.fontset)
576 		return;
577 	/* check for two enters in a row - happens when widget is
578 	   exposed under a pop-up */
579 	if (cw->liteClue.interval_id != (XtIntervalId)0)
580 		return;
581 	if(event->mode != NotifyNormal)
582 		return;
583 
584 	/* if a help was recently popped down, don't delay in poping up
585 	   help for next watched widget
586 	*/
587 	if ((event->time -  cw->liteClue.HelpPopDownTime) >
588 			cw->liteClue.cancelWaitPeriod )
589 		current_waitPeriod = cw->liteClue.waitPeriod,timeout_event;
590 	else
591 		current_waitPeriod = 0;
592 
593 	cw->liteClue.interval_id = XtAppAddTimeOut(
594 			XtWidgetToApplicationContext(w),
595 			current_waitPeriod, timeout_event, client_data);
596 }
597 
598 /*
599 Remove timer, if its pending. Then popdown help.
600 */
601 static void Leave_event(Widget w, XtPointer client_data, XEvent * xevent, Boolean * continue_to_dispatch )
602 {
603 	XEnterWindowEvent *event = NULL;
604 	struct liteClue_context_str * obj = (struct liteClue_context_str *) client_data;
605 	XcgLiteClueWidget cw = obj->cw;
606 
607 	if(xevent)
608 	 event = & xevent->xcrossing;
609 
610 	if (cw->liteClue.interval_id != (XtIntervalId)0)
611 	{
612 		XtRemoveTimeOut(cw->liteClue.interval_id);
613 		cw->liteClue.interval_id= (XtIntervalId)0;
614 	}
615 
616 	if (cw->liteClue.interval_id2 != (XtIntervalId)0)
617 	{
618 		XtRemoveTimeOut(cw->liteClue.interval_id2);
619 		cw->liteClue.interval_id2= (XtIntervalId)0;
620 	}
621 
622 	if (obj->sensitive == False)
623 		return;
624 	if (cw->liteClue.HelpIsUp)
625 	{
626 		XtPopdown((Widget) cw);
627 		cw->liteClue.HelpIsUp = False;
628 		if(xevent)
629 		 cw->liteClue.HelpPopDownTime = event->time;
630 		else
631 		 cw->liteClue.HelpPopDownTime = 0;
632 	}
633 }
634 
635 static void popdown_timeout_event( XtPointer client_data, XtIntervalId *id)
636 {
637  Boolean dummy;
638 
639  Leave_event(NULL, client_data, NULL, &dummy);
640 }
641 
642 /* ---------------- Widget API ---------------------------- */
643 
644 /*
645 ;+
646 XcgLiteClueAddWidget -- Add a widget to be watched. LiteClue will be given for this widget
647 
648 Func:	A widget will be added to the LiteClue watched list. Clues are given for
649 	sensitive watched widgets when the pointer enters its window. If the
650 	widget is already watched, the passed text replaces its current clue
651 	text. If text is null, the widget is still added, if it is not already
652 	in the list, but no clue will appear. Text may be specified with
653 	XcgLiteClueAddWidget in a subsequent call. When text is null and the
654 	widget is already in the list, its text is not changed. When a widget
655 	will is added to the watched list, it automatically becomes sensitive.
656 	Otherwise, its sensitivity is not changed. A watched widget which is not
657 	sensitive retains its context but clues are suppressed.
658 	None of this affects the behaviour of the watched widget itself.
659 	LiteClue monitors enter and leave events of the watched widget's
660 	window passively.
661 
662 Input:	w - LiteClue widget
663 	watch - the widget to give liteClues for
664 	text - pointer to liteClue text. (May be NULL)
665 	size - size of text. May be zero
666 		in which case a strlen will be done.
667 	option - option mask, future use, zero for now.
668 Output:
669 
670 Return:
671 
672 ;-
673 */
674 void XcgLiteClueAddWidget(Widget w, Widget watch, const char * text, int size, int option )
675 {
676 #	define ROUTINE "XcgLiteClueAddWidget"
677 	XcgLiteClueWidget cw = (XcgLiteClueWidget) w;
678 	struct liteClue_context_str * obj;
679 	Boolean exists = False;
680 
681 	CheckWidgetClass(ROUTINE);	/* make sure we are called with a LiteClue widget */
682 
683 	obj = find_watched_widget(cw, watch);
684 	if (obj)
685 	{
686 		exists = True;
687 		if (text)
688 		{
689 			if(obj->text)
690 				XtFree(obj->text);
691 			obj->text = NULL;
692 		}
693 	}
694 	else
695 	{
696 		obj = alloc_link_liteClue_context(cw );
697 		obj->watched_w = watch;
698 	}
699 	if (text && !(obj->text))
700 	{
701 		if (!size)
702 			size = strlen(text);
703 		obj->text = XtMalloc(size+1);
704 		memcpy(obj->text, text, size);
705 		obj->text[size] = 0;
706 		obj->text_size = size;
707 	}
708 	if (!exists)	/* was created */
709 	{
710 		XtAddEventHandler(watch, EnterWindowMask, False,
711 			Enter_event, (XtPointer) obj);
712 		XtAddEventHandler(watch, LeaveWindowMask|ButtonPressMask,
713 			False, Leave_event, (XtPointer) obj);
714 
715 		obj->sensitive = True;
716 	}
717 
718 #	undef ROUTINE
719 }
720 
721 
722 /*
723 ;+
724 XcgLiteClueDeleteWidget -- Delete a widget that is watched.
725 
726 Func:	A widget is deleted from the watched list and its resources are
727 	freed. LiteClue is no longer given for the widget.
728 	If the widget is not watched, nothing is done.
729 
730 Input:	w - LiteClue widget
731 	watch - the widget to delete
732 Output:
733 
734 Return:
735 
736 ;-
737 */
738 void XcgLiteClueDeleteWidget(Widget w, Widget watch)
739 {
740 #	define ROUTINE "XcgLiteClueDeleteWidget"
741 	XcgLiteClueWidget cw = (XcgLiteClueWidget) w;
742 	struct liteClue_context_str * obj;
743 
744 	CheckWidgetClass(ROUTINE);	/* make sure we are called with a LiteClue widget */
745 	obj = find_watched_widget(cw, watch);
746 	if (obj)
747 	{
748 		XtRemoveEventHandler(watch, EnterWindowMask, False,
749 			Enter_event, (XtPointer) obj);
750 		XtRemoveEventHandler(watch, LeaveWindowMask|ButtonPressMask,
751 			False, Leave_event, (XtPointer) obj);
752 		if (cw->liteClue.interval_id != (XtIntervalId)0)
753 		{
754 			XtRemoveTimeOut(cw->liteClue.interval_id);
755 			cw->liteClue.interval_id= (XtIntervalId)0;
756 		}
757 		free_widget_context(cw, obj);
758 	}
759 
760 #	undef ROUTINE
761 }
762 
763 
764 /*
765 ;+
766 XcgLiteClueSetSensitive -- Enable/disable sensitivity for watched widget.
767 
768 Func:	When a watched widget is sensitive, a clue is poped up when the pointer
769 	enters its window. When a watched widget is insensitive, the widget is
770 	retained in the watched list but no clue is poped. The sensitivity of a
771 	watched widget relative to clues is set or reset by this function. The
772 	Xt sensitivity of the watched widget is not altered by this function.
773 
774 Input:	w - LiteClue widget
775 	watch - the widget to make sensitive or insensitive or NULL
776 		to change all watched widgets
777 	sensitive - True or False
778 Output:
779 
780 Return:
781 
782 ;-
783 */
784 void XcgLiteClueSetSensitive(Widget w, Widget watch, Boolean sensitive)
785 {
786 #	define ROUTINE "XcgLiteClueSetSensitive"
787 	XcgLiteClueWidget cw = (XcgLiteClueWidget) w;
788 	struct liteClue_context_str * obj;
789 
790 	CheckWidgetClass(ROUTINE);	/* make sure we are called with a LiteClue widget */
791 	if (watch)
792 	{
793 		obj = find_watched_widget(cw, watch);
794 		if (obj)
795 		{
796 			obj->sensitive = sensitive;
797 			return;
798 		}
799 		else
800 			return;
801 	}
802 
803 	/* do them all */
804 	for (obj = (struct liteClue_context_str *) cw->liteClue.widget_list.forw;
805 		obj != (struct liteClue_context_str *) & cw->liteClue.widget_list;
806 		obj = (struct liteClue_context_str *)obj->next.forw )
807 	{
808 		obj->sensitive = sensitive;
809 	}
810 
811 #	undef ROUTINE
812 }
813 
814 /*
815 ;+
816 XcgLiteClueGetSensitive -- Get sensitivity mode for watched widget.
817 
818 Func:	When a watched widget is sensitive, a clue is poped up when the pointer
819 	enters its window. When a watched widget is insensitive, the widget is
820 	retained in the watched list but no clue is poped. The sensitivity state
821 	of a watched widget relative to clues is returned by this function. The
822 	Xt sensitivity of a widget is a totally independent concept.
823 
824 Input:	w - LiteClue widget
825 	watch - the widget for which to get sensitivity state. If NULL
826 		first watched widget is used. If there are no watched widgets,
827 		False is returned.
828 Output:
829 
830 Return:	sensitive - True or False
831 
832 ;-
833 */
834 Boolean XcgLiteClueGetSensitive(Widget w, Widget watch)
835 {
836 #	define ROUTINE "XcgLiteClueGetSensitive"
837 
838 	XcgLiteClueWidget cw = (XcgLiteClueWidget) w;
839 	struct liteClue_context_str * obj;
840 
841 	CheckWidgetClass(ROUTINE);	/* make sure we are called with a LiteClue widget */
842 	if (watch)
843 	{
844 		obj = find_watched_widget(cw, watch);
845 		if (obj)
846 			return obj->sensitive;
847 		else
848 			return False;
849 	}
850 	/* do the first one */
851 	obj = (struct liteClue_context_str *) cw->liteClue.widget_list.forw;
852 	if (obj != (struct liteClue_context_str *) & cw->liteClue.widget_list)
853 		return obj->sensitive;
854 	else
855 		return False;
856 
857 #	undef ROUTINE
858 }
859 
860 
861 /*
862 ;+
863 XcgLiteClueDispatchEvent -- Dispatch event from main X event loop
864 
865 Func:	This function may be used to enable clues for insensitive
866 	watched widgets. Normally, XtAppMainLoop (which calls
867 	XtDispatchEvent) will not deliver EnterNotify and LeaveNotify
868 	events to widgets that are not sensitive (XtSetSensitive). This
869 	prevents clues from poping up for these widgets. To bypass this
870 	limitation, you can break out XtAppMainLoop and add a call to
871 	XcgLiteClueDispatchEvent ass follows:
872 
873 	MyXtAppMainLoop(XtAppContext app)
874 	{
875 	    XEvent event;
876 
877 	    for (;;) {
878 	        XtAppNextEvent(app, &event);
879 		XcgLiteClueDispatchEvent(w, event) ;
880 	        XtDispatchEvent(&event);
881 	    }
882 	}
883 
884 Input:	w - LiteClue widget
885 	event - received event, normally from call to XtAppNextEvent.
886 
887 Output: void
888 
889 Return:	True - event was dispatched to non-sensitive watched widget.
890 	False - not a EnterNotify or LeaveNotify event or window in
891 		event is not a non-sensitive watched widget.
892 
893 ;-
894 */
895 Boolean XcgLiteClueDispatchEvent(Widget w, XEvent  *event)
896 {
897 #	define ROUTINE "XcgLiteClueDispatchEvent"
898 
899 	XcgLiteClueWidget cw = (XcgLiteClueWidget) w;
900 	struct liteClue_context_str * obj;
901 	Boolean continue_to_dispatch;
902 
903 	if (event->type != EnterNotify && event->type != LeaveNotify)
904 		return False;
905 	CheckWidgetClass(ROUTINE);	/* make sure we are called with a LiteClue widget */
906 
907 	/* scan list */
908 	for (obj = (struct liteClue_context_str *) cw->liteClue.widget_list.forw;
909 		obj != (struct liteClue_context_str *) & cw->liteClue.widget_list;
910 		obj = (struct liteClue_context_str *)obj->next.forw )
911 	{
912 		if ((XtWindow(obj->watched_w) != event->xany.window)
913 		||  (XtIsSensitive(obj->watched_w)) )
914 			continue;
915 		/* found one */
916 		if (event->type == EnterNotify )
917 			Enter_event(obj->watched_w, (XtPointer)obj, event,  &continue_to_dispatch);
918 		else
919 			Leave_event(obj->watched_w, (XtPointer)obj, event,  &continue_to_dispatch);
920 		return True;
921 	}
922 	return False;
923 
924 #	undef ROUTINE
925 }
926 
927