themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_Tooltip.cxx
blob1af62baa9a51950402ce72d0ea274235dac86550
1 //
2 // "$Id: Fl_Tooltip.cxx 8788 2011-06-08 14:35:22Z matt $"
3 //
4 // Tooltip source file for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2011 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <FL/Fl_Tooltip.H>
29 #include <FL/fl_draw.H>
30 #include <FL/Fl_Menu_Window.H>
32 #include <stdio.h>
33 #include <string.h> // strdup()
35 float Fl_Tooltip::delay_ = 1.0f;
36 float Fl_Tooltip::hoverdelay_ = 0.2f;
37 Fl_Color Fl_Tooltip::color_ = fl_color_cube(FL_NUM_RED - 1,
38 FL_NUM_GREEN - 1,
39 FL_NUM_BLUE - 2);
40 Fl_Color Fl_Tooltip::textcolor_ = FL_BLACK;
41 Fl_Font Fl_Tooltip::font_ = FL_HELVETICA;
42 Fl_Fontsize Fl_Tooltip::size_ = -1;
44 #define MAX_WIDTH 400
46 static const char* tip;
47 /**
48 This widget creates a tooltip box window, with no caption.
50 class Fl_TooltipBox : public Fl_Menu_Window {
51 public:
52 /** Creates the box window */
53 Fl_TooltipBox() : Fl_Menu_Window(0, 0) {
54 set_override();
55 set_tooltip_window();
56 end();
58 void draw();
59 void layout();
60 /** Shows the tooltip windows only if a tooltip text is available. */
61 void show() {
62 if (!tip) return;
64 Fl_Menu_Window::show();
68 Fl_Widget* Fl_Tooltip::widget_ = 0;
69 static Fl_TooltipBox *window = 0;
70 static int Y,H;
72 #ifdef __APPLE__
73 // returns the unique tooltip window
74 Fl_Window *Fl_Tooltip::current_window(void)
76 return (Fl_Window*)window;
78 #endif
81 static void show_tooltip_func ( const char *s )
83 if (!window) window = new Fl_TooltipBox;
84 // this cast bypasses the normal Fl_Window label() code:
85 ((Fl_Widget*)window)->label(s);
86 window->layout();
87 window->redraw();
88 // printf("tooltip_timeout: Showing window %p with tooltip \"%s\"...\n",
89 // window, tip ? tip : "(null)");
90 window->show();
93 static void hide_tooltip_func ( void )
95 if (window) window->hide();
98 static void ensure_callbacks ( void )
100 if ( ! fl_hide_tooltip )
101 fl_hide_tooltip = hide_tooltip_func;
102 if ( ! fl_show_tooltip )
103 fl_show_tooltip = show_tooltip_func;
106 void Fl_TooltipBox::layout() {
107 fl_font(Fl_Tooltip::font(), Fl_Tooltip::size());
108 int ww, hh;
109 ww = MAX_WIDTH;
110 fl_measure(tip, ww, hh, FL_ALIGN_LEFT|FL_ALIGN_WRAP|FL_ALIGN_INSIDE);
111 ww += 6; hh += 6;
113 // find position on the screen of the widget:
114 int ox = Fl::event_x_root();
115 int oy = Y + H+2;
116 for (Fl_Widget* p = Fl_Tooltip::current(); p; p = p->window()) {
117 oy += p->y();
119 int scr_x, scr_y, scr_w, scr_h;
120 Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h);
121 if (ox+ww > scr_x+scr_w) ox = scr_x+scr_w - ww;
122 if (ox < scr_x) ox = scr_x;
123 if (H > 30) {
124 oy = Fl::event_y_root()+13;
125 if (oy+hh > scr_y+scr_h) oy -= 23+hh;
126 } else {
127 if (oy+hh > scr_y+scr_h) oy -= (4+hh+H);
129 if (oy < scr_y) oy = scr_y;
131 resize(ox, oy, ww, hh);
134 void Fl_TooltipBox::draw() {
135 draw_box(FL_BORDER_BOX, 0, 0, w(), h(), Fl_Tooltip::color());
136 fl_color(Fl_Tooltip::textcolor());
137 fl_font(Fl_Tooltip::font(), Fl_Tooltip::size());
138 fl_draw(tip, 3, 3, w()-6, h()-6, Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_WRAP));
141 static char recent_tooltip;
143 static void recent_timeout(void*) {
144 #ifdef DEBUG
145 puts("recent_timeout();");
146 #endif // DEBUG
148 recent_tooltip = 0;
151 static char recursion;
153 void (*fl_show_tooltip)( const char *s ) = 0;
154 void (*fl_hide_tooltip)( void ) = 0;
156 static void tooltip_timeout(void*) {
158 ensure_callbacks();
160 #ifdef DEBUG
161 puts("tooltip_timeout();");
162 #endif // DEBUG
164 if (recursion) return;
165 recursion = 1;
166 if (!tip || !*tip) {
167 fl_hide_tooltip();
168 } else {
169 int condition = 1;
170 #if !(defined(__APPLE__) || defined(WIN32))
171 condition = (Fl::grab() == NULL);
172 #endif
173 if ( condition )
174 fl_show_tooltip( tip );
177 Fl::remove_timeout(recent_timeout);
178 recent_tooltip = 1;
179 recursion = 0;
183 This method is called when the mouse pointer enters a widget.
184 <P>If this widget or one of its parents has a tooltip, enter it. This
185 will do nothing if this is the current widget (even if the mouse moved
186 out so an exit() was done and then moved back in). If no tooltip can
187 be found do Fl_Tooltip::exit_(). If you don't want this behavior (for instance
188 if you want the tooltip to reappear when the mouse moves back in)
189 call the fancier enter_area() below.
191 void Fl_Tooltip::enter_(Fl_Widget* w) {
192 #ifdef DEBUG
193 printf("Fl_Tooltip::enter_(w=%p)\n", w);
194 printf(" window=%p\n", window);
195 #endif // DEBUG
197 // find the enclosing group with a tooltip:
198 Fl_Widget* tw = w;
199 for (;;) {
200 if (!tw) {exit_(0); return;}
201 if (tw == widget_) return;
202 if (tw->tooltip()) break;
203 tw = tw->parent();
205 enter_area(w, 0, 0, w->w(), w->h(), tw->tooltip());
207 /**
208 Sets the current widget target.
209 Acts as though enter(widget) was done but does not pop up a
210 tooltip. This is useful to prevent a tooltip from reappearing when
211 a modal overlapping window is deleted. FLTK does this automatically
212 when you click the mouse button.
214 void Fl_Tooltip::current(Fl_Widget* w) {
215 #ifdef DEBUG
216 printf("Fl_Tooltip::current(w=%p)\n", w);
217 #endif // DEBUG
219 exit_(0);
220 // find the enclosing group with a tooltip:
221 Fl_Widget* tw = w;
222 for (;;) {
223 if (!tw) return;
224 if (tw->tooltip()) break;
225 tw = tw->parent();
227 // act just like Fl_Tooltip::enter_() except we can remember a zero:
228 widget_ = w;
231 // Hide any visible tooltip.
232 /** This method is called when the mouse pointer leaves a widget. */
233 void Fl_Tooltip::exit_(Fl_Widget *w) {
234 #ifdef DEBUG
235 printf("Fl_Tooltip::exit_(w=%p)\n", w);
236 printf(" widget=%p, window=%p\n", widget_, window);
237 #endif // DEBUG
239 ensure_callbacks();
241 if (!widget_ || (w && w == window)) return;
242 widget_ = 0;
243 Fl::remove_timeout(tooltip_timeout);
244 Fl::remove_timeout(recent_timeout);
245 fl_hide_tooltip();
246 if (recent_tooltip) {
247 if (Fl::event_state() & FL_BUTTONS) recent_tooltip = 0;
248 else Fl::add_timeout(Fl_Tooltip::hoverdelay(), recent_timeout);
252 // Get ready to display a tooltip. The widget and the xywh box inside
253 // it define an area the tooltip is for, this along with the current
254 // mouse position places the tooltip (the mouse is assumed to point
255 // inside or near the box).
257 You may be able to use this to provide tooltips for internal pieces
258 of your widget. Call this after setting Fl::belowmouse() to
259 your widget (because that calls the above enter() method). Then figure
260 out what thing the mouse is pointing at, and call this with the widget
261 (this pointer is used to remove the tooltip if the widget is deleted
262 or hidden, and to locate the tooltip), the rectangle surrounding the
263 area, relative to the top-left corner of the widget (used to calculate
264 where to put the tooltip), and the text of the tooltip (which must be
265 a pointer to static data as it is not copied).
267 void Fl_Tooltip::enter_area(Fl_Widget* wid, int x,int y,int w,int h, const char* t)
269 (void)x;
270 (void)w;
272 ensure_callbacks();
274 #ifdef DEBUG
275 printf("Fl_Tooltip::enter_area(wid=%p, x=%d, y=%d, w=%d, h=%d, t=\"%s\")\n",
276 wid, x, y, w, h, t ? t : "(null)");
277 printf(" recursion=%d, window=%p\n", recursion, window);
278 #endif // DEBUG
280 if (recursion) return;
281 if (!t || !*t || !enabled()) {
282 exit_(0);
283 return;
285 // do nothing if it is the same:
286 if (wid==widget_ /*&& x==X && y==Y && w==W && h==H*/ && t==tip) return;
287 Fl::remove_timeout(tooltip_timeout);
288 Fl::remove_timeout(recent_timeout);
289 // remember it:
290 widget_ = wid; Y = y; H = h; tip = t;
291 // popup the tooltip immediately if it was recently up:
292 if (recent_tooltip) {
293 fl_hide_tooltip();
294 Fl::add_timeout(Fl_Tooltip::hoverdelay(), tooltip_timeout);
295 } else if (Fl_Tooltip::delay() < .1) {
296 #ifdef WIN32
297 // possible fix for the Windows titlebar, it seems to want the
298 // window to be destroyed, moving it messes up the parenting:
299 fl_hide_tooltip();
300 #endif // WIN32
301 tooltip_timeout(0);
302 } else {
303 fl_hide_tooltip();
304 Fl::add_timeout(Fl_Tooltip::delay(), tooltip_timeout);
307 #ifdef DEBUG
308 printf(" tip=\"%s\", window->shown()=%d\n", tip ? tip : "(null)",
309 window ? window->shown() : 0);
310 #endif // DEBUG
313 void Fl_Tooltip::set_enter_exit_once_() {
314 static char beenhere = 0;
315 if (!beenhere) {
316 beenhere = 1;
317 Fl_Tooltip::enter = Fl_Tooltip::enter_;
318 Fl_Tooltip::exit = Fl_Tooltip::exit_;
323 Sets the current tooltip text.
325 Sets a string of text to display in a popup tooltip window when the user
326 hovers the mouse over the widget. The string is <I>not</I> copied, so
327 make sure any formatted string is stored in a static, global,
328 or allocated buffer. If you want a copy made and managed for you,
329 use the copy_tooltip() method, which will manage the tooltip string
330 automatically.
332 If no tooltip is set, the tooltip of the parent is inherited. Setting a
333 tooltip for a group and setting no tooltip for a child will show the
334 group's tooltip instead. To avoid this behavior, you can set the child's
335 tooltip to an empty string ("").
336 \param[in] text New tooltip text (no copy is made)
337 \see copy_tooltip(const char*), tooltip()
339 void Fl_Widget::tooltip(const char *text) {
340 Fl_Tooltip::set_enter_exit_once_();
341 if (flags() & COPIED_TOOLTIP) {
342 // reassigning a copied tooltip remains the same copied tooltip
343 if (tooltip_ == text) return;
344 free((void*)(tooltip_)); // free maintained copy
345 clear_flag(COPIED_TOOLTIP); // disable copy flag (WE don't make copies)
347 tooltip_ = text;
351 Sets the current tooltip text.
352 Unlike tooltip(), this method allocates a copy of the tooltip
353 string instead of using the original string pointer.
355 The internal copy will automatically be freed whenever you assign
356 a new tooltip or when the widget is destroyed.
358 If no tooltip is set, the tooltip of the parent is inherited. Setting a
359 tooltip for a group and setting no tooltip for a child will show the
360 group's tooltip instead. To avoid this behavior, you can set the child's
361 tooltip to an empty string ("").
362 \param[in] text New tooltip text (an internal copy is made and managed)
363 \see tooltip(const char*), tooltip()
365 void Fl_Widget::copy_tooltip(const char *text) {
366 Fl_Tooltip::set_enter_exit_once_();
367 if (flags() & COPIED_TOOLTIP) free((void *)(tooltip_));
368 if (text) {
369 set_flag(COPIED_TOOLTIP);
370 tooltip_ = strdup(text);
371 } else {
372 clear_flag(COPIED_TOOLTIP);
373 tooltip_ = (char *)0;
378 // End of "$Id: Fl_Tooltip.cxx 8788 2011-06-08 14:35:22Z matt $".