1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "config.h"
25 
26 #include <X11/Xlib.h>
27 
28 #include "E.h"
29 #include "aclass.h"
30 #include "conf.h"
31 #include "dialog.h"
32 #include "emodule.h"
33 #include "eobj.h"
34 #include "iclass.h"
35 #include "list.h"
36 #include "settings.h"
37 #include "tclass.h"
38 #include "timers.h"
39 #include "tooltips.h"
40 #include "xwin.h"
41 
42 static              LIST_HEAD(tt_list);
43 static Timer       *tt_timer = NULL;
44 
45 static struct {
46    char                enable;
47    char                showroottooltip;
48    int                 delay;	/* milliseconds */
49 } Conf_tooltips;
50 
51 static struct {
52    int                 inhibit;
53    char                root_motion_mask_set;
54    CB_GetAclass       *ac_func;
55    void               *ac_data;
56 } Mode_tooltips;
57 
58 struct _tooltip {
59    dlist_t             list;
60    const char         *name;
61    ImageClass         *iclass[5];
62    TextClass          *tclass;
63    int                 dist;
64    Win                 iwin;
65    EObj               *win[5];
66    ImageClass         *tooltippic;
67 };
68 
69 #define TTWIN win[4]
70 #define TTICL iclass[4]
71 
72 static void
TooltipRealize(ToolTip * tt)73 TooltipRealize(ToolTip * tt)
74 {
75    int                 i, wh;
76    EObj               *eo;
77 
78    for (i = 0; i < 5; i++)
79      {
80 	if (!tt->iclass[i])
81 	   continue;
82 
83 	wh = (i + 1) * 8;
84 	eo = EobjWindowCreate(EOBJ_TYPE_MISC, -50, -100, wh, wh, 1, tt->name);
85 	eo->fade = eo->shadow = 1;
86 	EobjChangeOpacity(eo, OpacityFromPercent(Conf.opacity.tooltips));
87 	tt->win[i] = eo;
88      }
89    tt->iwin = ECreateWindow(EobjGetWin(tt->TTWIN), 0, 0, 1, 1, 0);
90 }
91 
92 static ToolTip     *
TooltipCreate(const char * name,const char * ic0,const char * ic1,const char * ic2,const char * ic3,const char * ic4,const char * tclass,int dist,const char * tooltippic)93 TooltipCreate(const char *name, const char *ic0, const char *ic1,
94 	      const char *ic2, const char *ic3, const char *ic4,
95 	      const char *tclass, int dist, const char *tooltippic)
96 {
97    ToolTip            *tt;
98    ImageClass         *ic;
99 
100    if (!ic0 || !tclass)
101       return NULL;
102 
103    ic = ImageclassAlloc(ic0, 0);
104    if (!ic)
105       return NULL;
106 
107    tt = ECALLOC(ToolTip, 1);
108    if (!tt)
109       return NULL;
110 
111    tt->name = Estrdup(name);
112    tt->iclass[0] = ImageclassAlloc(ic1, 0);
113    tt->iclass[1] = ImageclassAlloc(ic2, 0);
114    tt->iclass[2] = ImageclassAlloc(ic3, 0);
115    tt->iclass[3] = ImageclassAlloc(ic4, 0);
116    tt->iclass[4] = ic;
117    tt->tclass = TextclassAlloc(tclass, 1);
118    tt->tooltippic = ImageclassAlloc(tooltippic, 0);
119 
120    tt->dist = dist;
121 
122    LIST_PREPEND(ToolTip, &tt_list, tt);
123 
124    return tt;
125 }
126 
127 #if 0				/* Not used */
128 static void
129 TooltipDestroy(ToolTip * tt)
130 {
131    if (!tt)
132       return;
133 
134    if (tt->ref_count > 0)
135      {
136 	DialogOK("ToolTip Error!", _("%u references remain"), tt->ref_count);
137      }
138 }
139 #endif
140 
141 int
TooltipConfigLoad(FILE * fs)142 TooltipConfigLoad(FILE * fs)
143 {
144    int                 err = 0;
145    char                s[FILEPATH_LEN_MAX];
146    char                s2[FILEPATH_LEN_MAX];
147    char                name[64];
148    char                iclass[64];
149    char                bubble1[64], bubble2[64], bubble3[64], bubble4[64];
150    char                tclass[64];
151    char                tooltiphelppic[64];
152    int                 i1;
153    int                 distance = 0;
154 
155    name[0] = iclass[0] = tclass[0] = '\0';
156    bubble1[0] = bubble2[0] = bubble3[0] = bubble4[0] = '\0';
157    tooltiphelppic[0] = '\0';
158 
159    while (GetLine(s, sizeof(s), fs))
160      {
161 	i1 = ConfigParseline1(s, s2, NULL, NULL);
162 	switch (i1)
163 	  {
164 	  case CONFIG_CLOSE:
165 	     if (iclass[0] && tclass[0] && name[0])
166 		TooltipCreate(name, iclass, bubble1, bubble2,
167 			      bubble3, bubble4, tclass, distance,
168 			      tooltiphelppic);
169 	     goto done;
170 
171 	  case CONFIG_CLASSNAME:
172 	     if (TooltipFind(s2))
173 	       {
174 		  SkipTillEnd(fs);
175 		  goto done;
176 	       }
177 	     STRCPY(name, s2);
178 	     break;
179 	  case CONFIG_IMAGECLASS:
180 	     STRCPY(iclass, s2);
181 	     break;
182 	  case TOOLTIP_BUBBLE1:
183 	     STRCPY(bubble1, s2);
184 	     break;
185 	  case TOOLTIP_BUBBLE2:
186 	     STRCPY(bubble2, s2);
187 	     break;
188 	  case TOOLTIP_BUBBLE3:
189 	     STRCPY(bubble3, s2);
190 	     break;
191 	  case TOOLTIP_BUBBLE4:
192 	     STRCPY(bubble4, s2);
193 	     break;
194 	  case CONFIG_TEXT:
195 	     STRCPY(tclass, s2);
196 	     break;
197 	  case TOOLTIP_DISTANCE:
198 	     distance = atoi(s2);
199 	     break;
200 	  case TOOLTIP_HELP_PIC:
201 	     STRCPY(tooltiphelppic, s2);
202 	     break;
203 	  default:
204 	     ConfigParseError("ToolTip", s);
205 	     break;
206 	  }
207      }
208    err = -1;
209 
210  done:
211    return err;
212 }
213 
214 static ImageClass  *
TooltipCreateIclass(const char * name,const char * file,int * pw,int * ph)215 TooltipCreateIclass(const char *name, const char *file, int *pw, int *ph)
216 {
217    ImageClass         *ic;
218    EImage             *im;
219    int                 w, h;
220 
221    ic = ImageclassFind(name, 0);
222    if (!ic)
223       ic = ImageclassCreateSimple(name, file);
224    im = ImageclassGetImage(ic, 0, 0, 0);
225 
226    if (im)
227      {
228 	EImageGetSize(im, &w, &h);
229 	if (*pw < w)
230 	   *pw = w;
231 	if (*ph < h)
232 	   *ph = h;
233      }
234 
235    return ic;
236 }
237 
238 static void
TooltipIclassPaste(ToolTip * tt,const char * ic_name,int x,int y,int * px)239 TooltipIclassPaste(ToolTip * tt, const char *ic_name, int x, int y, int *px)
240 {
241    ImageClass         *ic;
242    EImage             *im;
243    int                 w, h;
244 
245    ic = ImageclassFind(ic_name, 0);
246    im = ImageclassGetImage(ic, 0, 0, 0);
247    if (!ic || !im)
248       return;
249 
250    EImageGetSize(im, &w, &h);
251    EImageRenderOnDrawable(im, EobjGetWin(tt->TTWIN), NoXID, EIMAGE_BLEND, x, y,
252 			  w, h);
253 
254    *px = x + w;
255 }
256 
257 void
TooltipShow(ToolTip * tt,const char * text,ActionClass * ac,int x,int y)258 TooltipShow(ToolTip * tt, const char *text, ActionClass * ac, int x, int y)
259 {
260    int                 i, w, h, ix, iy, iw, ih, dx, dy, xx, yy;
261    int                 ww, hh, adx, ady, dist;
262    int                 headline_h = 0, headline_w = 0, icons_width =
263       0, labels_width = 0, double_w = 0;
264    EImage             *im;
265    int                *heights = NULL;
266    ImageClass         *ic;
267    EImageBorder       *pad;
268    int                 cols[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
269    int                 num, modifiers;
270    Action             *aa;
271    const char         *tts;
272    EObj               *eo;
273 
274    if (!tt || Mode.mode != MODE_NONE)
275       return;
276 
277    if (!tt->TTWIN)
278      {
279 	TooltipRealize(tt);
280 	if (!tt->TTWIN)
281 	   return;
282      }
283 
284    /* if we get an actionclass, look for tooltip action texts */
285    h = 0;
286    if (ac)
287      {
288 	num = ActionclassGetActionCount(ac);
289 	heights = EMALLOC(int, num);
290 
291 	for (i = 0; i < num; i++)
292 	  {
293 	     int                 temp_w, temp_h;
294 
295 	     temp_w = 0;
296 	     temp_h = 0;
297 
298 	     aa = ActionclassGetAction(ac, i);
299 	     if (!aa)
300 		continue;
301 
302 	     tts = ActionGetTooltipString(aa);
303 	     if (!tts)
304 		continue;
305 	     tts = _(tts);
306 
307 	     TextSize(tt->tclass, 0, 0, STATE_NORMAL, tts, &temp_w, &temp_h,
308 		      17);
309 	     if (temp_w > labels_width)
310 		labels_width = temp_w;
311 
312 	     if (ActionGetEvent(aa) == EVENT_DOUBLE_DOWN)
313 	       {
314 		  TextSize(tt->tclass, 0, 0, STATE_NORMAL, "2x", &double_w,
315 			   &temp_h, 17);
316 		  if (cols[0] < double_w)
317 		     cols[0] = double_w;
318 	       }
319 
320 	     if (ActionGetAnybutton(aa))
321 	       {
322 		  TooltipCreateIclass("TOOLTIP_MOUSEBUTTON_ANY",
323 				      "pix/mouse_any.png", &cols[1], &temp_h);
324 	       }
325 	     else
326 		switch (ActionGetButton(aa))
327 		  {
328 		  case 1:
329 		     TooltipCreateIclass("TOOLTIP_MOUSEBUTTON_1",
330 					 "pix/mouse_1.png", &cols[1], &temp_h);
331 		     break;
332 		  case 2:
333 		     TooltipCreateIclass("TOOLTIP_MOUSEBUTTON_2",
334 					 "pix/mouse_2.png", &cols[1], &temp_h);
335 		     break;
336 		  case 3:
337 		     TooltipCreateIclass("TOOLTIP_MOUSEBUTTON_3",
338 					 "pix/mouse_3.png", &cols[1], &temp_h);
339 		     break;
340 		  case 4:
341 		     TooltipCreateIclass("TOOLTIP_MOUSEBUTTON_4",
342 					 "pix/mouse_4.png", &cols[1], &temp_h);
343 		     break;
344 		  case 5:
345 		     TooltipCreateIclass("TOOLTIP_MOUSEBUTTON_5",
346 					 "pix/mouse_5.png", &cols[1], &temp_h);
347 		     break;
348 		  case 0:
349 		  default:
350 		     break;
351 		  }
352 
353 	     modifiers = ActionGetModifiers(aa);
354 	     if (modifiers)
355 	       {
356 		  if (modifiers & ShiftMask)
357 		     TooltipCreateIclass("TOOLTIP_KEY_SHIFT",
358 					 "pix/key_shift.png",
359 					 &cols[2], &temp_h);
360 		  if (modifiers & LockMask)
361 		     TooltipCreateIclass("TOOLTIP_KEY_LOCK",
362 					 "pix/key_lock.png", &cols[3], &temp_h);
363 		  if (modifiers & ControlMask)
364 		     TooltipCreateIclass("TOOLTIP_KEY_CTRL",
365 					 "pix/key_ctrl.png", &cols[4], &temp_h);
366 		  if (modifiers & Mod1Mask)
367 		     TooltipCreateIclass("TOOLTIP_KEY_MOD1",
368 					 "pix/key_mod1.png", &cols[5], &temp_h);
369 		  if (modifiers & Mod2Mask)
370 		     TooltipCreateIclass("TOOLTIP_KEY_MOD2",
371 					 "pix/key_mod2.png", &cols[6], &temp_h);
372 		  if (modifiers & Mod3Mask)
373 		     TooltipCreateIclass("TOOLTIP_KEY_MOD3",
374 					 "pix/key_mod3.png", &cols[7], &temp_h);
375 		  if (modifiers & Mod4Mask)
376 		     TooltipCreateIclass("TOOLTIP_KEY_MOD4",
377 					 "pix/key_mod4.png", &cols[8], &temp_h);
378 		  if (modifiers & Mod5Mask)
379 		     TooltipCreateIclass("TOOLTIP_KEY_MOD5",
380 					 "pix/key_mod5.png", &cols[9], &temp_h);
381 	       }
382 
383 	     temp_w = cols[0] + cols[1] + cols[2] + cols[3] + cols[4] +
384 		cols[5] + cols[6] + cols[7] + cols[8] + cols[9];
385 
386 	     if (temp_w > icons_width)
387 		icons_width = temp_w;
388 	     heights[i] = temp_h;
389 	     h += temp_h;
390 	  }
391      }
392 
393    TextSize(tt->tclass, 0, 0, STATE_NORMAL, text, &headline_w, &headline_h, 17);
394    if (headline_w < icons_width + labels_width)
395       w = icons_width + labels_width;
396    else
397       w = headline_w;
398    h += headline_h;
399 
400    ic = tt->TTICL;
401    pad = ImageclassGetPadding(ic);
402    iw = 0;
403    ih = 0;
404    if (tt->tooltippic)
405      {
406 	im = ImageclassGetImage(tt->tooltippic, 0, 0, 0);
407 	if (im)
408 	  {
409 	     EImageGetSize(im, &iw, &ih);
410 	     EImageFree(im);
411 	  }
412 	w += iw;
413 	if (h < ih)
414 	   h = ih;
415      }
416    w += pad->left + pad->right;
417    h += pad->top + pad->bottom;
418 
419    if ((tt->tooltippic) && (iw > 0) && (ih > 0))
420      {
421 	ix = pad->left;
422 	iy = (h - ih) / 2;
423 	EMoveResizeWindow(tt->iwin, ix, iy, iw, ih);
424 	EMapWindow(tt->iwin);
425 	ImageclassApply(tt->tooltippic, tt->iwin, 0, 0, STATE_NORMAL);
426      }
427    else
428       EUnmapWindow(tt->iwin);
429 
430    dx = x - WinGetW(VROOT) / 2;
431    dy = y - WinGetH(VROOT) / 2;
432 
433    if ((dy == 0) && (dx == 0))
434       dy = -1;
435 
436    adx = dx;
437    if (adx < 0)
438       adx = -adx;
439    ady = dy;
440    if (ady < 0)
441       ady = -ady;
442    if (adx < ady)
443       /*   +-------+   */
444       /*   |\#####/|   */
445       /*   | \###/ |   */
446       /*   |  \#/  |   */
447       /*   |  /#\  |   */
448       /*   | /###\ |   */
449       /*   |/#####\|   */
450       /*   +-------+   */
451      {
452 	if (dy == 0)
453 	  {
454 	     dy = 1;
455 	     ady = 1;
456 	  }
457 	dist = tt->dist;
458 	ady /= dy;
459 
460 	if (tt->win[0])
461 	  {
462 	     yy = y - ((ady * 10 * dist) / 100);
463 	     xx = x - (dist * 10 * dx) / (100 * WinGetW(VROOT) / 2);
464 	     EobjMove(tt->win[0], xx - 4, yy - 4);
465 	  }
466 
467 	if (tt->win[1])
468 	  {
469 	     yy = y - ((ady * 30 * dist) / 100);
470 	     xx = x - (dist * 30 * dx) / (100 * WinGetW(VROOT) / 2);
471 	     EobjMove(tt->win[1], xx - 8, yy - 8);
472 	  }
473 
474 	if (tt->win[2])
475 	  {
476 	     yy = y - ((ady * 50 * dist) / 100);
477 	     xx = x - (dist * 50 * dx) / (100 * WinGetW(VROOT) / 2);
478 	     EobjMove(tt->win[2], xx - 12, yy - 12);
479 	  }
480 
481 	if (tt->win[3])
482 	  {
483 	     yy = y - ((ady * 80 * dist) / 100);
484 	     xx = x - (dist * 80 * dx) / (100 * WinGetW(VROOT) / 2);
485 	     EobjMove(tt->win[3], xx - 16, yy - 16);
486 	  }
487 
488 	yy = y - ((ady * 100 * dist) / 100);
489 	xx = x - (dist * 100 * dx) / (100 * WinGetW(VROOT) / 2);
490 	if (ady < 0)
491 	   hh = 0;
492 	else
493 	   hh = h;
494 	ww = (w / 2) + ((dx * w) / (WinGetW(VROOT) / 2));
495      }
496    else
497       /*   +-------+   */
498       /*   |\     /|   */
499       /*   |#\   /#|   */
500       /*   |##\ /##|   */
501       /*   |##/ \##|   */
502       /*   |#/   \#|   */
503       /*   |/     \|   */
504       /*   +-------+   */
505      {
506 	if (dx == 0)
507 	  {
508 	     dx = 1;
509 	     adx = 1;
510 	  }
511 	dist = tt->dist;
512 	adx /= dx;
513 
514 	if (tt->win[0])
515 	  {
516 	     xx = x - ((adx * 10 * dist) / 100);
517 	     yy = y - (dist * 10 * dy) / (100 * WinGetH(VROOT) / 2);
518 	     EobjMove(tt->win[0], xx - 4, yy - 4);
519 	  }
520 
521 	if (tt->win[1])
522 	  {
523 	     xx = x - ((adx * 30 * dist) / 100);
524 	     yy = y - (dist * 30 * dy) / (100 * WinGetH(VROOT) / 2);
525 	     EobjMove(tt->win[1], xx - 8, yy - 8);
526 	  }
527 
528 	if (tt->win[2])
529 	  {
530 	     xx = x - ((adx * 50 * dist) / 100);
531 	     yy = y - (dist * 50 * dy) / (100 * WinGetH(VROOT) / 2);
532 	     EobjMove(tt->win[2], xx - 12, yy - 12);
533 	  }
534 
535 	if (tt->win[3])
536 	  {
537 	     xx = x - ((adx * 80 * dist) / 100);
538 	     yy = y - (dist * 80 * dy) / (100 * WinGetH(VROOT) / 2);
539 	     EobjMove(tt->win[3], xx - 16, yy - 16);
540 	  }
541 
542 	xx = x - ((adx * 100 * dist) / 100);
543 	yy = y - (dist * 100 * dy) / (100 * WinGetH(VROOT) / 2);
544 	if (adx < 0)
545 	   ww = 0;
546 	else
547 	   ww = w;
548 	hh = (h / 2) + ((dy * h) / (WinGetH(VROOT) / 2));
549      }
550 
551    EobjMoveResize(tt->TTWIN, xx - ww, yy - hh, w, h);
552 
553    for (i = 0; i < 5; i++)
554      {
555 	eo = tt->win[i];
556 	if (!eo)
557 	   continue;
558 	ImageclassApply(tt->iclass[i], EobjGetWin(eo), 0, 0, STATE_NORMAL);
559 	EobjShapeUpdate(eo, 0);
560 	EobjMap(eo, 0);
561      }
562 
563    xx = pad->left + iw;
564 
565    /* draw the ordinary tooltip text */
566    TextDraw(tt->tclass, EobjGetWin(tt->TTWIN), NoXID, 0, 0, STATE_NORMAL, text,
567 	    xx, pad->top, headline_w, headline_h, 17, 512);
568 
569    /* draw the icons and labels, if any */
570    if (ac)
571      {
572 	num = ActionclassGetActionCount(ac);
573 	y = pad->top + headline_h;
574 	xx = pad->left + double_w;
575 
576 	for (i = 0; i < num; i++)
577 	  {
578 	     x = xx + iw;
579 
580 	     aa = ActionclassGetAction(ac, i);
581 	     if (!aa)
582 		continue;
583 
584 	     tts = ActionGetTooltipString(aa);
585 	     if (!tts)
586 		continue;
587 	     tts = _(tts);
588 
589 	     if (ActionGetEvent(aa) == EVENT_DOUBLE_DOWN)
590 	       {
591 		  TextDraw(tt->tclass, EobjGetWin(tt->TTWIN), NoXID, 0, 0,
592 			   STATE_NORMAL, "2x", xx + iw - double_w, y, double_w,
593 			   heights[i], 17, 0);
594 	       }
595 
596 	     if (ActionGetAnybutton(aa))
597 	       {
598 		  TooltipIclassPaste(tt, "TOOLTIP_MOUSEBUTTON_ANY", x, y, &x);
599 	       }
600 	     else
601 		switch (ActionGetButton(aa))
602 		  {
603 		  case 1:
604 		     TooltipIclassPaste(tt, "TOOLTIP_MOUSEBUTTON_1", x, y, &x);
605 		     break;
606 		  case 2:
607 		     TooltipIclassPaste(tt, "TOOLTIP_MOUSEBUTTON_2", x, y, &x);
608 		     break;
609 		  case 3:
610 		     TooltipIclassPaste(tt, "TOOLTIP_MOUSEBUTTON_3", x, y, &x);
611 		     break;
612 		  case 4:
613 		     TooltipIclassPaste(tt, "TOOLTIP_MOUSEBUTTON_4", x, y, &x);
614 		     break;
615 		  case 5:
616 		     TooltipIclassPaste(tt, "TOOLTIP_MOUSEBUTTON_5", x, y, &x);
617 		     break;
618 		  default:
619 		     break;
620 		  }
621 
622 	     modifiers = ActionGetModifiers(aa);
623 	     if (modifiers)
624 	       {
625 		  if (modifiers & ShiftMask)
626 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_SHIFT", x, y, &x);
627 		  if (modifiers & LockMask)
628 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_LOCK", x, y, &x);
629 		  if (modifiers & ControlMask)
630 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_CTRL", x, y, &x);
631 		  if (modifiers & Mod1Mask)
632 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_MOD1", x, y, &x);
633 		  if (modifiers & Mod2Mask)
634 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_MOD2", x, y, &x);
635 		  if (modifiers & Mod3Mask)
636 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_MOD3", x, y, &x);
637 		  if (modifiers & Mod4Mask)
638 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_MOD4", x, y, &x);
639 		  if (modifiers & Mod5Mask)
640 		     TooltipIclassPaste(tt, "TOOLTIP_KEY_MOD5", x, y, &x);
641 	       }
642 
643 	     TextDraw(tt->tclass, EobjGetWin(tt->TTWIN), NoXID, 0, 0,
644 		      STATE_NORMAL, tts, pad->left + icons_width + iw, y,
645 		      labels_width, heights[i], 17, 0);
646 	     y += heights[i];
647 
648 	  }
649      }
650 
651    Efree(heights);
652 }
653 
654 void
TooltipHide(ToolTip * tt)655 TooltipHide(ToolTip * tt)
656 {
657    int                 i;
658 
659    if (!tt || !tt->TTWIN || !tt->TTWIN->shown)
660       return;
661 
662    for (i = 4; i >= 0; i--)
663       if (tt->win[i])
664 	 EobjUnmap(tt->win[i]);
665 }
666 
667 static int
_TooltipMatchName(const void * data,const void * match)668 _TooltipMatchName(const void *data, const void *match)
669 {
670    return strcmp(((const ToolTip *)data)->name, (const char *)match);
671 }
672 
673 ToolTip            *
TooltipFind(const char * name)674 TooltipFind(const char *name)
675 {
676    return LIST_FIND(ToolTip, &tt_list, _TooltipMatchName, name);
677 }
678 
679 /*
680  * Tooltips
681  */
682 
683 void
TooltipsHide(void)684 TooltipsHide(void)
685 {
686    ToolTip            *tt;
687 
688    TooltipsSetPending(0, NULL, NULL);
689 
690    LIST_FOR_EACH(ToolTip, &tt_list, tt) TooltipHide(tt);
691 }
692 
693 void
TooltipsEnable(int enable)694 TooltipsEnable(int enable)
695 {
696    if (enable)
697      {
698 	if (Mode_tooltips.inhibit > 0)
699 	   Mode_tooltips.inhibit--;
700      }
701    else
702      {
703 	Mode_tooltips.inhibit++;
704 	TooltipsHide();
705      }
706 }
707 
708 static ToolTip     *ttip = NULL;
709 
710 static int
ToolTipTimeout(void * data __UNUSED__)711 ToolTipTimeout(void *data __UNUSED__)
712 {
713    int                 x, y;
714    unsigned int        mask;
715    ActionClass        *ac;
716    const char         *tts;
717 
718    if (!ttip)
719       ttip = TooltipFind("DEFAULT");
720    if (!ttip)
721       goto done;
722 
723    /* In the case of multiple screens, check to make sure
724     * the root window is still where the mouse is... */
725    if (!EQueryPointer(NULL, &x, &y, NULL, &mask))
726       goto done;
727 
728    /* In case this is a virtual root */
729    if (x < 0 || y < 0 || x >= WinGetW(VROOT) || y >= WinGetH(VROOT))
730       goto done;
731 
732    /* dont pop up tooltip is mouse button down */
733    if (mask &
734        (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))
735       goto done;
736 
737    if (!Mode_tooltips.ac_func)
738       goto done;
739    ac = Mode_tooltips.ac_func(Mode_tooltips.ac_data);
740    if (!ac)
741       goto done;
742 
743    tts = ActionclassGetTooltipString(ac);
744    if (!tts)
745       goto done;
746 
747    TooltipShow(ttip, _(tts), ac, x, y);
748 
749  done:
750    tt_timer = NULL;
751    return 0;
752 }
753 
754 /*
755  * We want this on
756  * ButtonPress, ButtonRelease, MotionNotify, EnterNotify, LeaveNotify
757  */
758 void
TooltipsSetPending(int type,CB_GetAclass * func,void * data)759 TooltipsSetPending(int type, CB_GetAclass * func, void *data)
760 {
761    if (!Mode_tooltips.ac_func && !func)
762       return;
763 
764    Mode_tooltips.ac_func = func;
765    Mode_tooltips.ac_data = data;
766 
767    TooltipHide(ttip);
768 
769    TIMER_DEL(tt_timer);
770 
771    if (Conf_tooltips.showroottooltip)
772      {
773 	if (!Mode_tooltips.root_motion_mask_set)
774 	  {
775 	     Mode_tooltips.root_motion_mask_set = 1;
776 	     ESelectInputChange(VROOT, PointerMotionMask, 0);
777 	  }
778      }
779    else
780      {
781 	if (Mode_tooltips.root_motion_mask_set)
782 	  {
783 	     Mode_tooltips.root_motion_mask_set = 0;
784 	     ESelectInputChange(VROOT, 0, PointerMotionMask);
785 	  }
786      }
787 
788    if (!func)
789       return;
790    if (Mode_tooltips.inhibit || !Conf_tooltips.enable)
791       return;
792    if (type && !Conf_tooltips.showroottooltip)
793       return;
794 
795    TIMER_ADD(tt_timer, Conf_tooltips.delay, ToolTipTimeout, NULL);
796 }
797 
798 /*
799  * Tooltips Module
800  */
801 
802 static void
TooltipsSighan(int sig,void * prm __UNUSED__)803 TooltipsSighan(int sig, void *prm __UNUSED__)
804 {
805    switch (sig)
806      {
807      case ESIGNAL_INIT:
808 	memset(&Mode_tooltips, 0, sizeof(Mode_tooltips));
809 	break;
810      case ESIGNAL_AREA_SWITCH_START:
811      case ESIGNAL_DESK_SWITCH_START:
812      case ESIGNAL_EWIN_CHANGE:
813 	TooltipsHide();
814 	break;
815      }
816 }
817 
818 #if ENABLE_DIALOGS
819 /*
820  * Configuration dialog
821  */
822 static char         tmp_tooltips;
823 static int          tmp_tooltiptime;
824 static char         tmp_roottip;
825 
826 static void
_DlgApplyTooltips(Dialog * d __UNUSED__,int val __UNUSED__,void * data __UNUSED__)827 _DlgApplyTooltips(Dialog * d __UNUSED__, int val __UNUSED__,
828 		  void *data __UNUSED__)
829 {
830    Conf_tooltips.enable = tmp_tooltips;
831    Conf_tooltips.delay = tmp_tooltiptime * 10;
832    Conf_tooltips.showroottooltip = tmp_roottip;
833 
834    autosave();
835 }
836 
837 static void
_DlgFillTooltips(Dialog * d __UNUSED__,DItem * table,void * data __UNUSED__)838 _DlgFillTooltips(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
839 {
840    DItem              *di;
841 
842    tmp_tooltips = Conf_tooltips.enable;
843    tmp_tooltiptime = Conf_tooltips.delay / 10;
844    tmp_roottip = Conf_tooltips.showroottooltip;
845 
846    DialogItemTableSetOptions(table, 2, 0, 0, 0);
847 
848    di = DialogAddItem(table, DITEM_CHECKBUTTON);
849    DialogItemSetColSpan(di, 2);
850    DialogItemSetText(di, _("Display Tooltips"));
851    DialogItemCheckButtonSetPtr(di, &tmp_tooltips);
852 
853    di = DialogAddItem(table, DITEM_CHECKBUTTON);
854    DialogItemSetColSpan(di, 2);
855    DialogItemSetText(di, _("Display Root Window Tips"));
856    DialogItemCheckButtonSetPtr(di, &tmp_roottip);
857 
858    di = DialogAddItem(table, DITEM_TEXT);
859    DialogItemSetAlign(di, 0, 512);
860    DialogItemSetText(di, _("Tooltip Delay:"));
861 
862    di = DialogAddItem(table, DITEM_SLIDER);
863    DialogItemSliderSetBounds(di, 0, 300);
864    DialogItemSliderSetUnits(di, 10);
865    DialogItemSliderSetJump(di, 25);
866    DialogItemSliderSetValPtr(di, &tmp_tooltiptime);
867 }
868 
869 const DialogDef     DlgTooltips = {
870    "CONFIGURE_TOOLTIPS",
871    N_("Tooltips"), N_("Tooltip Settings"),
872    0,
873    SOUND_SETTINGS_TOOLTIPS,
874    "pix/tips.png",
875    N_("Enlightenment Tooltip\n" "Settings Dialog"),
876    _DlgFillTooltips,
877    DLG_OAC, _DlgApplyTooltips, NULL
878 };
879 #endif /* ENABLE_DIALOGS */
880 
881 static const CfgItem TooltipsCfgItems[] = {
882    CFG_ITEM_BOOL(Conf_tooltips, enable, 1),
883    CFG_ITEM_BOOL(Conf_tooltips, showroottooltip, 1),
884    CFG_ITEM_INT(Conf_tooltips, delay, 1500),
885 };
886 
887 /*
888  * Module descriptor
889  */
890 extern const EModule ModTooltips;
891 
892 const EModule       ModTooltips = {
893    "tooltips", "tt",
894    TooltipsSighan,
895    {0, NULL},
896    MOD_ITEMS(TooltipsCfgItems)
897 };
898