blob: 05b6e2344a36cf13d028c5edc007ad5ee6e6be6e [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
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.
12//
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.
17//
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.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28#include <FL/Fl_Tooltip.H>
29#include <FL/fl_draw.H>
30#include <FL/Fl_Menu_Window.H>
31
32#include <stdio.h>
33#include <string.h> // strdup()
34
35float Fl_Tooltip::delay_ = 1.0f;
36float Fl_Tooltip::hoverdelay_ = 0.2f;
37Fl_Color Fl_Tooltip::color_ = fl_color_cube(FL_NUM_RED - 1,
38 FL_NUM_GREEN - 1,
39 FL_NUM_BLUE - 2);
40Fl_Color Fl_Tooltip::textcolor_ = FL_BLACK;
41Fl_Font Fl_Tooltip::font_ = FL_HELVETICA;
42Fl_Fontsize Fl_Tooltip::size_ = -1;
43
44#define MAX_WIDTH 400
45
46static const char* tip;
47/**
48 This widget creates a tooltip box window, with no caption.
49*/
50class Fl_TooltipBox : public Fl_Menu_Window {
51public:
52 /** Creates the box window */
53 Fl_TooltipBox() : Fl_Menu_Window(0, 0) {
54 set_override();
55 set_tooltip_window();
56 end();
57 }
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;
63
64 Fl_Menu_Window::show();
65 }
66};
67
68Fl_Widget* Fl_Tooltip::widget_ = 0;
69static Fl_TooltipBox *window = 0;
70static int Y,H;
71
72#ifdef __APPLE__
73// returns the unique tooltip window
74Fl_Window *Fl_Tooltip::current_window(void)
75{
76 return (Fl_Window*)window;
77}
78#endif
79
80void Fl_TooltipBox::layout() {
81 fl_font(Fl_Tooltip::font(), Fl_Tooltip::size());
82 int ww, hh;
83 ww = MAX_WIDTH;
84 fl_measure(tip, ww, hh, FL_ALIGN_LEFT|FL_ALIGN_WRAP|FL_ALIGN_INSIDE);
85 ww += 6; hh += 6;
86
87 // find position on the screen of the widget:
88 int ox = Fl::event_x_root();
89 int oy = Y + H+2;
90 for (Fl_Widget* p = Fl_Tooltip::current(); p; p = p->window()) {
91 oy += p->y();
92 }
93 int scr_x, scr_y, scr_w, scr_h;
94 Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h);
95 if (ox+ww > scr_x+scr_w) ox = scr_x+scr_w - ww;
96 if (ox < scr_x) ox = scr_x;
97 if (H > 30) {
98 oy = Fl::event_y_root()+13;
99 if (oy+hh > scr_y+scr_h) oy -= 23+hh;
100 } else {
101 if (oy+hh > scr_y+scr_h) oy -= (4+hh+H);
102 }
103 if (oy < scr_y) oy = scr_y;
104
105 resize(ox, oy, ww, hh);
106}
107
108void Fl_TooltipBox::draw() {
109 draw_box(FL_BORDER_BOX, 0, 0, w(), h(), Fl_Tooltip::color());
110 fl_color(Fl_Tooltip::textcolor());
111 fl_font(Fl_Tooltip::font(), Fl_Tooltip::size());
112 fl_draw(tip, 3, 3, w()-6, h()-6, Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_WRAP));
113}
114
115static char recent_tooltip;
116
117static void recent_timeout(void*) {
118#ifdef DEBUG
119 puts("recent_timeout();");
120#endif // DEBUG
121
122 recent_tooltip = 0;
123}
124
125static char recursion;
126
127static void tooltip_timeout(void*) {
128#ifdef DEBUG
129 puts("tooltip_timeout();");
130#endif // DEBUG
131
132 if (recursion) return;
133 recursion = 1;
134 if (!tip || !*tip) {
135 if (window) window->hide();
136 } else {
137 int condition = 1;
138#if !(defined(__APPLE__) || defined(WIN32))
139 condition = (Fl::grab() == NULL);
140#endif
141 if ( condition ) {
142 if (!window) window = new Fl_TooltipBox;
143 // this cast bypasses the normal Fl_Window label() code:
144 ((Fl_Widget*)window)->label(tip);
145 window->layout();
146 window->redraw();
147 // printf("tooltip_timeout: Showing window %p with tooltip \"%s\"...\n",
148 // window, tip ? tip : "(null)");
149 window->show();
150 }
151 }
152
153 Fl::remove_timeout(recent_timeout);
154 recent_tooltip = 1;
155 recursion = 0;
156}
157
158/**
159 This method is called when the mouse pointer enters a widget.
160 <P>If this widget or one of its parents has a tooltip, enter it. This
161 will do nothing if this is the current widget (even if the mouse moved
162 out so an exit() was done and then moved back in). If no tooltip can
163 be found do Fl_Tooltip::exit_(). If you don't want this behavior (for instance
164 if you want the tooltip to reappear when the mouse moves back in)
165 call the fancier enter_area() below.
166*/
167void Fl_Tooltip::enter_(Fl_Widget* w) {
168#ifdef DEBUG
169 printf("Fl_Tooltip::enter_(w=%p)\n", w);
170 printf(" window=%p\n", window);
171#endif // DEBUG
172
173 // find the enclosing group with a tooltip:
174 Fl_Widget* tw = w;
175 for (;;) {
176 if (!tw) {exit_(0); return;}
177 if (tw == widget_) return;
178 if (tw->tooltip()) break;
179 tw = tw->parent();
180 }
181 enter_area(w, 0, 0, w->w(), w->h(), tw->tooltip());
182}
183/**
184 Sets the current widget target.
185 Acts as though enter(widget) was done but does not pop up a
186 tooltip. This is useful to prevent a tooltip from reappearing when
187 a modal overlapping window is deleted. FLTK does this automatically
188 when you click the mouse button.
189*/
190void Fl_Tooltip::current(Fl_Widget* w) {
191#ifdef DEBUG
192 printf("Fl_Tooltip::current(w=%p)\n", w);
193#endif // DEBUG
194
195 exit_(0);
196 // find the enclosing group with a tooltip:
197 Fl_Widget* tw = w;
198 for (;;) {
199 if (!tw) return;
200 if (tw->tooltip()) break;
201 tw = tw->parent();
202 }
203 // act just like Fl_Tooltip::enter_() except we can remember a zero:
204 widget_ = w;
205}
206
207// Hide any visible tooltip.
208/** This method is called when the mouse pointer leaves a widget. */
209void Fl_Tooltip::exit_(Fl_Widget *w) {
210#ifdef DEBUG
211 printf("Fl_Tooltip::exit_(w=%p)\n", w);
212 printf(" widget=%p, window=%p\n", widget_, window);
213#endif // DEBUG
214
215 if (!widget_ || (w && w == window)) return;
216 widget_ = 0;
217 Fl::remove_timeout(tooltip_timeout);
218 Fl::remove_timeout(recent_timeout);
219 if (window && window->visible()) window->hide();
220 if (recent_tooltip) {
221 if (Fl::event_state() & FL_BUTTONS) recent_tooltip = 0;
222 else Fl::add_timeout(Fl_Tooltip::hoverdelay(), recent_timeout);
223 }
224}
225
226// Get ready to display a tooltip. The widget and the xywh box inside
227// it define an area the tooltip is for, this along with the current
228// mouse position places the tooltip (the mouse is assumed to point
229// inside or near the box).
230/**
231 You may be able to use this to provide tooltips for internal pieces
232 of your widget. Call this after setting Fl::belowmouse() to
233 your widget (because that calls the above enter() method). Then figure
234 out what thing the mouse is pointing at, and call this with the widget
235 (this pointer is used to remove the tooltip if the widget is deleted
236 or hidden, and to locate the tooltip), the rectangle surrounding the
237 area, relative to the top-left corner of the widget (used to calculate
238 where to put the tooltip), and the text of the tooltip (which must be
239 a pointer to static data as it is not copied).
240*/
241void Fl_Tooltip::enter_area(Fl_Widget* wid, int x,int y,int w,int h, const char* t)
242{
243 (void)x;
244 (void)w;
245
246#ifdef DEBUG
247 printf("Fl_Tooltip::enter_area(wid=%p, x=%d, y=%d, w=%d, h=%d, t=\"%s\")\n",
248 wid, x, y, w, h, t ? t : "(null)");
249 printf(" recursion=%d, window=%p\n", recursion, window);
250#endif // DEBUG
251
252 if (recursion) return;
253 if (!t || !*t || !enabled()) {
254 exit_(0);
255 return;
256 }
257 // do nothing if it is the same:
258 if (wid==widget_ /*&& x==X && y==Y && w==W && h==H*/ && t==tip) return;
259 Fl::remove_timeout(tooltip_timeout);
260 Fl::remove_timeout(recent_timeout);
261 // remember it:
262 widget_ = wid; Y = y; H = h; tip = t;
263 // popup the tooltip immediately if it was recently up:
264 if (recent_tooltip) {
265 if (window) window->hide();
266 Fl::add_timeout(Fl_Tooltip::hoverdelay(), tooltip_timeout);
267 } else if (Fl_Tooltip::delay() < .1) {
268#ifdef WIN32
269 // possible fix for the Windows titlebar, it seems to want the
270 // window to be destroyed, moving it messes up the parenting:
271 if (window && window->visible()) window->hide();
272#endif // WIN32
273 tooltip_timeout(0);
274 } else {
275 if (window && window->visible()) window->hide();
276 Fl::add_timeout(Fl_Tooltip::delay(), tooltip_timeout);
277 }
278
279#ifdef DEBUG
280 printf(" tip=\"%s\", window->shown()=%d\n", tip ? tip : "(null)",
281 window ? window->shown() : 0);
282#endif // DEBUG
283}
284
285void Fl_Tooltip::set_enter_exit_once_() {
286 static char beenhere = 0;
287 if (!beenhere) {
288 beenhere = 1;
289 Fl_Tooltip::enter = Fl_Tooltip::enter_;
290 Fl_Tooltip::exit = Fl_Tooltip::exit_;
291 }
292}
293
294/**
295 Sets the current tooltip text.
296
297 Sets a string of text to display in a popup tooltip window when the user
298 hovers the mouse over the widget. The string is <I>not</I> copied, so
299 make sure any formatted string is stored in a static, global,
300 or allocated buffer. If you want a copy made and managed for you,
301 use the copy_tooltip() method, which will manage the tooltip string
302 automatically.
303
304 If no tooltip is set, the tooltip of the parent is inherited. Setting a
305 tooltip for a group and setting no tooltip for a child will show the
306 group's tooltip instead. To avoid this behavior, you can set the child's
307 tooltip to an empty string ("").
308 \param[in] text New tooltip text (no copy is made)
309 \see copy_tooltip(const char*), tooltip()
310*/
311void Fl_Widget::tooltip(const char *text) {
312 Fl_Tooltip::set_enter_exit_once_();
313 if (flags() & COPIED_TOOLTIP) {
314 // reassigning a copied tooltip remains the same copied tooltip
315 if (tooltip_ == text) return;
316 free((void*)(tooltip_)); // free maintained copy
317 clear_flag(COPIED_TOOLTIP); // disable copy flag (WE don't make copies)
318 }
319 tooltip_ = text;
320}
321
322/**
323 Sets the current tooltip text.
324 Unlike tooltip(), this method allocates a copy of the tooltip
325 string instead of using the original string pointer.
326
327 The internal copy will automatically be freed whenever you assign
328 a new tooltip or when the widget is destroyed.
329
330 If no tooltip is set, the tooltip of the parent is inherited. Setting a
331 tooltip for a group and setting no tooltip for a child will show the
332 group's tooltip instead. To avoid this behavior, you can set the child's
333 tooltip to an empty string ("").
334 \param[in] text New tooltip text (an internal copy is made and managed)
335 \see tooltip(const char*), tooltip()
336*/
337void Fl_Widget::copy_tooltip(const char *text) {
338 Fl_Tooltip::set_enter_exit_once_();
339 if (flags() & COPIED_TOOLTIP) free((void *)(tooltip_));
340 if (text) {
341 set_flag(COPIED_TOOLTIP);
342 tooltip_ = strdup(text);
343 } else {
344 clear_flag(COPIED_TOOLTIP);
345 tooltip_ = (char *)0;
346 }
347}
348
349//
350// End of "$Id: Fl_Tooltip.cxx 8788 2011-06-08 14:35:22Z matt $".
351//