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