2 // "$Id: Fl_Tooltip.cxx 8788 2011-06-08 14:35:22Z matt $"
4 // Tooltip source file for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2011 by Bill Spitzak and others.
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
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>
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,
40 Fl_Color
Fl_Tooltip::textcolor_
= FL_BLACK
;
41 Fl_Font
Fl_Tooltip::font_
= FL_HELVETICA
;
42 Fl_Fontsize
Fl_Tooltip::size_
= -1;
46 static const char* tip
;
48 This widget creates a tooltip box window, with no caption.
50 class Fl_TooltipBox
: public Fl_Menu_Window
{
52 /** Creates the box window */
53 Fl_TooltipBox() : Fl_Menu_Window(0, 0) {
60 /** Shows the tooltip windows only if a tooltip text is available. */
64 Fl_Menu_Window::show();
68 Fl_Widget
* Fl_Tooltip::widget_
= 0;
69 static Fl_TooltipBox
*window
= 0;
73 // returns the unique tooltip window
74 Fl_Window
*Fl_Tooltip::current_window(void)
76 return (Fl_Window
*)window
;
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
);
88 // printf("tooltip_timeout: Showing window %p with tooltip \"%s\"...\n",
89 // window, tip ? tip : "(null)");
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());
110 fl_measure(tip
, ww
, hh
, FL_ALIGN_LEFT
|FL_ALIGN_WRAP
|FL_ALIGN_INSIDE
);
113 // find position on the screen of the widget:
114 int ox
= Fl::event_x_root();
116 for (Fl_Widget
* p
= Fl_Tooltip::current(); p
; p
= p
->window()) {
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
;
124 oy
= Fl::event_y_root()+13;
125 if (oy
+hh
> scr_y
+scr_h
) oy
-= 23+hh
;
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*) {
145 puts("recent_timeout();");
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*) {
161 puts("tooltip_timeout();");
164 if (recursion
) return;
170 #if !(defined(__APPLE__) || defined(WIN32))
171 condition
= (Fl::grab() == NULL
);
174 fl_show_tooltip( tip
);
177 Fl::remove_timeout(recent_timeout
);
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
) {
193 printf("Fl_Tooltip::enter_(w=%p)\n", w
);
194 printf(" window=%p\n", window
);
197 // find the enclosing group with a tooltip:
200 if (!tw
) {exit_(0); return;}
201 if (tw
== widget_
) return;
202 if (tw
->tooltip()) break;
205 enter_area(w
, 0, 0, w
->w(), w
->h(), tw
->tooltip());
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
) {
216 printf("Fl_Tooltip::current(w=%p)\n", w
);
220 // find the enclosing group with a tooltip:
224 if (tw
->tooltip()) break;
227 // act just like Fl_Tooltip::enter_() except we can remember a zero:
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
) {
235 printf("Fl_Tooltip::exit_(w=%p)\n", w
);
236 printf(" widget=%p, window=%p\n", widget_
, window
);
241 if (!widget_
|| (w
&& w
== window
)) return;
243 Fl::remove_timeout(tooltip_timeout
);
244 Fl::remove_timeout(recent_timeout
);
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
)
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
);
280 if (recursion
) return;
281 if (!t
|| !*t
|| !enabled()) {
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
);
290 widget_
= wid
; Y
= y
; H
= h
; tip
= t
;
291 // popup the tooltip immediately if it was recently up:
292 if (recent_tooltip
) {
294 Fl::add_timeout(Fl_Tooltip::hoverdelay(), tooltip_timeout
);
295 } else if (Fl_Tooltip::delay() < .1) {
297 // possible fix for the Windows titlebar, it seems to want the
298 // window to be destroyed, moving it messes up the parenting:
304 Fl::add_timeout(Fl_Tooltip::delay(), tooltip_timeout
);
308 printf(" tip=\"%s\", window->shown()=%d\n", tip
? tip
: "(null)",
309 window
? window
->shown() : 0);
313 void Fl_Tooltip::set_enter_exit_once_() {
314 static char beenhere
= 0;
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
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)
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_
));
369 set_flag(COPIED_TOOLTIP
);
370 tooltip_
= strdup(text
);
372 clear_flag(COPIED_TOOLTIP
);
373 tooltip_
= (char *)0;
378 // End of "$Id: Fl_Tooltip.cxx 8788 2011-06-08 14:35:22Z matt $".