1 /*
2  * File: tipwin.cc
3  *
4  * Copyright 2012 Jorge Arellano Cid <jcid@dillo.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * The tipwin idea was derived from the Fl_Slider example [1]
12  * by Greg Ercolano, which is in public domain.
13  *
14  * [1] http://seriss.com/people/erco/fltk/#SliderTooltip
15  */
16 
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <FL/fl_draw.H>
22 #include <FL/Fl.H>
23 #include <FL/Fl_Group.H>
24 #include <FL/Fl_Menu_Window.H>
25 #include <FL/Fl_Tooltip.H>
26 #include <FL/Fl_Button.H>
27 
28 #include "prefs.h"
29 #include "tipwin.hh"
30 
31 /*
32  * Forward declarations
33  */
34 static void show_timeout(void*);
35 static void recent_timeout(void*);
36 
37 /*
38  * Custom tooltip window
39  */
TipWin()40 TipWin::TipWin() : Fl_Menu_Window(1, 1)     // will autosize
41 {
42    bgcolor = fl_color_cube(FL_NUM_RED - 1, FL_NUM_GREEN - 1, FL_NUM_BLUE - 2);
43    recent = 0;
44    strcpy(tip, "");
45    cur_widget = NULL;
46    set_override(); // no border
47    end();
48 }
49 
draw()50 void TipWin::draw()
51 {
52    draw_box(FL_BORDER_BOX, 0, 0, w(), h(), bgcolor);
53    fl_color(FL_BLACK);
54    fl_font(labelfont(), labelsize());
55    fl_draw(tip, 3, 3, w() - 6, h() - 6,
56            //Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_WRAP));
57            Fl_Align(FL_ALIGN_LEFT));
58 }
59 
value(const char * s)60 void TipWin::value(const char *s) {
61    // Recalc size of window
62    snprintf(tip, sizeof(tip) - 1, "%s", s);
63    fl_font(labelfont(), labelsize());
64    int W = w(), H = h();
65    W = 0;
66    fl_measure(tip, W, H, 0);
67    W += 8; H += 8;
68    size(W, H);
69    redraw();
70 }
71 
do_show(void * wid)72 void TipWin::do_show(void *wid) {
73    cur_widget = wid;  // Keep track of requesting widget
74    if (prefs.show_ui_tooltip) {
75       Fl::add_timeout(recent ? 0.2f : 0.8f, show_timeout);
76    }
77 }
78 
do_hide()79 void TipWin::do_hide() {
80    Fl::remove_timeout(show_timeout);
81    if (shown()) {
82       hide();
83       recent = 1;
84       Fl::add_timeout(0.8f, recent_timeout);
85    }
86 }
87 
recent_tooltip(int val)88 void TipWin::recent_tooltip(int val) {
89    recent = val;
90 }
91 
92 //--------------------------------------------------------------------------
93 
my_tipwin(void)94 TipWin *my_tipwin(void)
95 {
96    static TipWin *tw = NULL;
97 
98    if (!tw) {
99       Fl_Group *save = Fl_Group::current();    // save current widget..
100       tw = new TipWin();                       // ..because this trashes it
101       tw->hide();                              // start hidden
102       Fl_Group::current(save);                 // ..then back to previous.
103    }
104    return tw;
105 }
106 
show_timeout(void *)107 static void show_timeout(void*) {
108   // if offscreen, move tip ABOVE mouse instead
109   int scr_x, scr_y, scr_w, scr_h;
110   Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h);
111   int ty = Fl::event_y_root() + 20;
112   if (ty + my_tipwin()->h() > scr_h)
113      ty = Fl::event_y_root() - 20 - my_tipwin()->h();
114   if (ty < 0) ty = 0;
115 
116   my_tipwin()->position(Fl::event_x_root(), ty);
117   my_tipwin()->show();
118   my_tipwin()->recent_tooltip(0);
119 }
120 
recent_timeout(void *)121 static void recent_timeout(void*) {
122   my_tipwin()->recent_tooltip(0);
123 }
124 
125 
126 //---------------------------------------------------------------------------
127 
128 /*
129  * A Button sharing a custom tooltip window
130  */
TipWinButton(int x,int y,int w,int h,const char * l)131 TipWinButton::TipWinButton(int x, int y, int w, int h, const char *l) :
132     Fl_Button(x, y, w, h, l)
133 {
134    tipwin = my_tipwin();
135    mytooltip = strdup("empty");
136 }
137 
~TipWinButton(void)138 TipWinButton::~TipWinButton(void)
139 {
140    tipwin->cancel(this); // cancel tooltip if shown
141    free(mytooltip);
142 }
143 
handle(int e)144 int TipWinButton::handle(int e)
145 {
146    switch (e) {
147    case FL_ENTER:
148       tipwin->value(mytooltip);
149       tipwin->do_show(this);
150       break;
151    case FL_PUSH:            // push mouse
152    case FL_RELEASE:         // release mouse
153    case FL_HIDE:            // widget goes away
154    case FL_LEAVE:           // leave focus
155       tipwin->do_hide();
156       break;
157    }
158    return (Fl_Button::handle(e));
159 }
160 
set_tooltip(const char * s)161 void TipWinButton::set_tooltip(const char *s)
162 {
163    free(mytooltip);
164    mytooltip = strdup(s);
165 }
166 
167 
168 //---------------------------------------------------------------------------
169 
170 /*
171  * A Light Button sharing a custom tooltip window
172  */
CustButton(int x,int y,int w,int h,const char * l)173 CustButton::CustButton(int x, int y, int w, int h, const char *l) :
174    TipWinButton(x,y,w,h,l)
175 {
176    norm_color = color();
177    light_color = PREFS_UI_BUTTON_HIGHLIGHT_COLOR;
178 }
179 
handle(int e)180 int CustButton::handle(int e)
181 {
182    if (active()) {
183       if (e == FL_ENTER) {
184          color(light_color);
185          redraw();
186       } else if (e == FL_LEAVE || e == FL_RELEASE || e == FL_HIDE) {
187          color(norm_color);
188          redraw();
189       }
190    } else if (e == FL_DEACTIVATE && color() != norm_color) {
191       color(norm_color);
192       redraw();
193    }
194    return TipWinButton::handle(e);
195 }
196 
hl_color(Fl_Color col)197 void CustButton::hl_color(Fl_Color col)
198 {
199    light_color = col;
200 }
201 
202 
203 //---------------------------------------------------------------------------
204 
205 /*
206  * An Input with custom tooltip window
207  */
TipWinInput(int x,int y,int w,int h,const char * l)208 TipWinInput::TipWinInput (int x, int y, int w, int h, const char *l) :
209    Fl_Input(x,y,w,h,l)
210 {
211    tipwin = my_tipwin();
212    mytooltip = strdup("empty");
213 }
214 
~TipWinInput(void)215 TipWinInput::~TipWinInput(void)
216 {
217    tipwin->cancel(this); // cancel tooltip if shown
218    free(mytooltip);
219 }
220 
handle(int e)221 int TipWinInput::handle(int e)
222 {
223    switch (e) {
224    case FL_ENTER:
225       tipwin->value(mytooltip);
226       tipwin->do_show(this);
227       break;
228    case FL_PUSH:            // push mouse
229    case FL_RELEASE:         // release mouse
230    case FL_HIDE:            // widget goes away
231    case FL_LEAVE:           // leave focus
232    case FL_KEYBOARD:        // key press
233       tipwin->do_hide();
234       break;
235    }
236    return (Fl_Input::handle(e));
237 }
238 
set_tooltip(const char * s)239 void TipWinInput::set_tooltip(const char *s)
240 {
241    free(mytooltip);
242    mytooltip = strdup(s);
243 }
244 
245