1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
33 #include "gtkwidget.h"
34 #include "gtkwindow.h"
35 #include "gtksignal.h"
37 #include "gtktooltips.h"
40 #define DEFAULT_DELAY 500 /* Default delay in ms */
41 #define STICKY_DELAY 0 /* Delay before popping up next tip
44 #define STICKY_REVERT_DELAY 1000 /* Delay before sticky tooltips revert
48 static void gtk_tooltips_class_init (GtkTooltipsClass *klass);
49 static void gtk_tooltips_init (GtkTooltips *tooltips);
50 static void gtk_tooltips_destroy (GtkObject *object);
52 static gint gtk_tooltips_event_handler (GtkWidget *widget,
54 static void gtk_tooltips_widget_unmap (GtkWidget *widget,
56 static void gtk_tooltips_widget_remove (GtkWidget *widget,
58 static void gtk_tooltips_set_active_widget (GtkTooltips *tooltips,
60 static gint gtk_tooltips_timeout (gpointer data);
62 static gint gtk_tooltips_paint_window (GtkTooltips *tooltips);
63 static void gtk_tooltips_draw_tips (GtkTooltips *tooltips);
65 static GtkObjectClass *parent_class;
66 static const gchar *tooltips_data_key = "_GtkTooltipsData";
69 gtk_tooltips_get_type (void)
71 static GtkType tooltips_type = 0;
75 static const GtkTypeInfo tooltips_info =
79 sizeof (GtkTooltipsClass),
80 (GtkClassInitFunc) gtk_tooltips_class_init,
81 (GtkObjectInitFunc) gtk_tooltips_init,
82 /* reserved_1 */ NULL,
83 /* reserved_2 */ NULL,
84 (GtkClassInitFunc) NULL,
87 tooltips_type = gtk_type_unique (GTK_TYPE_OBJECT, &tooltips_info);
94 gtk_tooltips_class_init (GtkTooltipsClass *class)
96 GtkObjectClass *object_class;
98 object_class = (GtkObjectClass*) class;
99 parent_class = gtk_type_class (GTK_TYPE_OBJECT);
101 object_class->destroy = gtk_tooltips_destroy;
105 gtk_tooltips_init (GtkTooltips *tooltips)
107 tooltips->tip_window = NULL;
108 tooltips->active_tips_data = NULL;
109 tooltips->tips_data_list = NULL;
111 tooltips->delay = DEFAULT_DELAY;
112 tooltips->enabled = TRUE;
113 tooltips->timer_tag = 0;
114 tooltips->use_sticky_delay = FALSE;
115 tooltips->last_popdown.tv_sec = -1;
116 tooltips->last_popdown.tv_usec = -1;
120 gtk_tooltips_new (void)
122 return gtk_type_new (GTK_TYPE_TOOLTIPS);
126 gtk_tooltips_destroy_data (GtkTooltipsData *tooltipsdata)
128 g_free (tooltipsdata->tip_text);
129 g_free (tooltipsdata->tip_private);
130 gtk_signal_disconnect_by_data (GTK_OBJECT (tooltipsdata->widget),
131 (gpointer) tooltipsdata);
132 gtk_object_remove_data (GTK_OBJECT (tooltipsdata->widget), tooltips_data_key);
133 gtk_widget_unref (tooltipsdata->widget);
134 g_free (tooltipsdata);
138 gtk_tooltips_destroy (GtkObject *object)
140 GtkTooltips *tooltips = GTK_TOOLTIPS (object);
142 GtkTooltipsData *tooltipsdata;
144 g_return_if_fail (tooltips != NULL);
146 if (tooltips->timer_tag)
148 gtk_timeout_remove (tooltips->timer_tag);
149 tooltips->timer_tag = 0;
152 if (tooltips->tips_data_list != NULL)
154 current = g_list_first (tooltips->tips_data_list);
155 while (current != NULL)
157 tooltipsdata = (GtkTooltipsData*) current->data;
158 current = current->next;
159 gtk_tooltips_widget_remove (tooltipsdata->widget, tooltipsdata);
163 if (tooltips->tip_window)
164 gtk_widget_destroy (tooltips->tip_window);
168 gtk_tooltips_force_window (GtkTooltips *tooltips)
170 g_return_if_fail (tooltips != NULL);
171 g_return_if_fail (GTK_IS_TOOLTIPS (tooltips));
173 if (!tooltips->tip_window)
175 tooltips->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
176 gtk_widget_set_app_paintable (tooltips->tip_window, TRUE);
177 gtk_window_set_policy (GTK_WINDOW (tooltips->tip_window), FALSE, FALSE, TRUE);
178 gtk_widget_set_name (tooltips->tip_window, "gtk-tooltips");
179 gtk_container_set_border_width (GTK_CONTAINER (tooltips->tip_window), 4);
181 gtk_signal_connect_object (GTK_OBJECT (tooltips->tip_window),
183 GTK_SIGNAL_FUNC (gtk_tooltips_paint_window),
184 GTK_OBJECT (tooltips));
186 tooltips->tip_label = gtk_label_new (NULL);
187 gtk_label_set_line_wrap (GTK_LABEL (tooltips->tip_label), TRUE);
188 gtk_misc_set_alignment (GTK_MISC (tooltips->tip_label), 0.5, 0.5);
189 gtk_widget_show (tooltips->tip_label);
191 gtk_container_add (GTK_CONTAINER (tooltips->tip_window), tooltips->tip_label);
193 gtk_signal_connect (GTK_OBJECT (tooltips->tip_window),
195 GTK_SIGNAL_FUNC (gtk_widget_destroyed),
196 &tooltips->tip_window);
201 gtk_tooltips_enable (GtkTooltips *tooltips)
203 g_return_if_fail (tooltips != NULL);
205 tooltips->enabled = TRUE;
209 gtk_tooltips_disable (GtkTooltips *tooltips)
211 g_return_if_fail (tooltips != NULL);
213 gtk_tooltips_set_active_widget (tooltips, NULL);
215 tooltips->enabled = FALSE;
219 gtk_tooltips_set_delay (GtkTooltips *tooltips,
222 g_return_if_fail (tooltips != NULL);
224 tooltips->delay = delay;
228 gtk_tooltips_data_get (GtkWidget *widget)
230 g_return_val_if_fail (widget != NULL, NULL);
232 return gtk_object_get_data ((GtkObject*) widget, tooltips_data_key);
236 gtk_tooltips_set_tip (GtkTooltips *tooltips,
238 const gchar *tip_text,
239 const gchar *tip_private)
241 GtkTooltipsData *tooltipsdata;
243 g_return_if_fail (tooltips != NULL);
244 g_return_if_fail (GTK_IS_TOOLTIPS (tooltips));
245 g_return_if_fail (widget != NULL);
247 tooltipsdata = gtk_tooltips_data_get (widget);
249 gtk_tooltips_widget_remove (tooltipsdata->widget, tooltipsdata);
254 tooltipsdata = g_new0 (GtkTooltipsData, 1);
256 if (tooltipsdata != NULL)
258 tooltipsdata->tooltips = tooltips;
259 tooltipsdata->widget = widget;
260 gtk_widget_ref (widget);
262 tooltipsdata->tip_text = g_strdup (tip_text);
263 tooltipsdata->tip_private = g_strdup (tip_private);
265 tooltips->tips_data_list = g_list_append (tooltips->tips_data_list,
267 gtk_signal_connect_after(GTK_OBJECT (widget), "event",
268 (GtkSignalFunc) gtk_tooltips_event_handler,
269 (gpointer) tooltipsdata);
271 gtk_object_set_data (GTK_OBJECT (widget), tooltips_data_key,
272 (gpointer) tooltipsdata);
274 gtk_signal_connect (GTK_OBJECT (widget), "unmap",
275 (GtkSignalFunc) gtk_tooltips_widget_unmap,
276 (gpointer) tooltipsdata);
278 gtk_signal_connect (GTK_OBJECT (widget), "unrealize",
279 (GtkSignalFunc) gtk_tooltips_widget_unmap,
280 (gpointer) tooltipsdata);
282 gtk_signal_connect (GTK_OBJECT (widget), "destroy",
283 (GtkSignalFunc) gtk_tooltips_widget_remove,
284 (gpointer) tooltipsdata);
289 gtk_tooltips_paint_window (GtkTooltips *tooltips)
291 gtk_paint_flat_box (tooltips->tip_window->style, tooltips->tip_window->window,
292 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
293 NULL, GTK_WIDGET(tooltips->tip_window), "tooltip",
300 gtk_tooltips_draw_tips (GtkTooltips * tooltips)
302 GtkRequisition requisition;
305 gint x, y, w, h, scr_w, scr_h;
306 GtkTooltipsData *data;
308 if (!tooltips->tip_window)
309 gtk_tooltips_force_window (tooltips);
310 else if (GTK_WIDGET_VISIBLE (tooltips->tip_window))
312 gtk_widget_hide (tooltips->tip_window);
313 g_get_current_time (&tooltips->last_popdown);
316 gtk_widget_ensure_style (tooltips->tip_window);
317 style = tooltips->tip_window->style;
319 widget = tooltips->active_tips_data->widget;
321 scr_w = gdk_screen_width ();
322 scr_h = gdk_screen_height ();
324 data = tooltips->active_tips_data;
326 gtk_label_set_text (GTK_LABEL (tooltips->tip_label), data->tip_text);
328 gtk_widget_size_request (tooltips->tip_window, &requisition);
329 w = requisition.width;
330 h = requisition.height;
332 gdk_window_get_pointer (NULL, &x, NULL, NULL);
333 gdk_window_get_origin (widget->window, NULL, &y);
334 if (GTK_WIDGET_NO_WINDOW (widget))
335 y += widget->allocation.y;
340 x -= (x + w) - scr_w;
344 if ((y + h + widget->allocation.height + 4) > scr_h)
347 y = y + widget->allocation.height + 4;
349 gtk_widget_set_uposition (tooltips->tip_window, x, y);
350 gtk_widget_show (tooltips->tip_window);
354 gtk_tooltips_timeout (gpointer data)
356 GtkTooltips *tooltips = (GtkTooltips *) data;
358 GDK_THREADS_ENTER ();
360 if (tooltips->active_tips_data != NULL &&
361 GTK_WIDGET_DRAWABLE (tooltips->active_tips_data->widget))
362 gtk_tooltips_draw_tips (tooltips);
364 GDK_THREADS_LEAVE ();
370 gtk_tooltips_set_active_widget (GtkTooltips *tooltips,
373 if (tooltips->tip_window)
375 if (GTK_WIDGET_VISIBLE (tooltips->tip_window))
376 g_get_current_time (&tooltips->last_popdown);
377 gtk_widget_hide (tooltips->tip_window);
379 if (tooltips->timer_tag)
381 gtk_timeout_remove (tooltips->timer_tag);
382 tooltips->timer_tag = 0;
385 tooltips->active_tips_data = NULL;
391 for (list = tooltips->tips_data_list; list; list = list->next)
393 GtkTooltipsData *tooltipsdata;
395 tooltipsdata = list->data;
397 if (tooltipsdata->widget == widget &&
398 GTK_WIDGET_DRAWABLE (widget))
400 tooltips->active_tips_data = tooltipsdata;
407 tooltips->use_sticky_delay = FALSE;
412 gtk_tooltips_recently_shown (GtkTooltips *tooltips)
417 g_get_current_time (&now);
418 msec = (now.tv_sec - tooltips->last_popdown.tv_sec) * 1000 +
419 (now.tv_usec - tooltips->last_popdown.tv_usec) / 1000;
420 return (msec < STICKY_REVERT_DELAY);
424 gtk_tooltips_event_handler (GtkWidget *widget,
427 GtkTooltips *tooltips;
428 GtkTooltipsData *old_tips_data;
429 GtkWidget *event_widget;
431 if ((event->type == GDK_LEAVE_NOTIFY || event->type == GDK_ENTER_NOTIFY) &&
432 event->crossing.detail == GDK_NOTIFY_INFERIOR)
435 event_widget = gtk_get_event_widget (event);
436 if (event_widget != widget)
439 old_tips_data = gtk_tooltips_data_get (widget);
440 tooltips = old_tips_data->tooltips;
444 case GDK_MOTION_NOTIFY:
449 case GDK_ENTER_NOTIFY:
450 old_tips_data = tooltips->active_tips_data;
451 if (tooltips->enabled &&
452 (!old_tips_data || old_tips_data->widget != widget))
456 gtk_tooltips_set_active_widget (tooltips, widget);
458 if (tooltips->use_sticky_delay &&
459 gtk_tooltips_recently_shown (tooltips))
460 delay = STICKY_DELAY;
462 delay = tooltips->delay;
463 tooltips->timer_tag = gtk_timeout_add (delay,
464 gtk_tooltips_timeout,
465 (gpointer) tooltips);
469 case GDK_LEAVE_NOTIFY:
471 gboolean use_sticky_delay;
473 use_sticky_delay = tooltips->tip_window &&
474 GTK_WIDGET_VISIBLE (tooltips->tip_window);
475 gtk_tooltips_set_active_widget (tooltips, NULL);
476 tooltips->use_sticky_delay = use_sticky_delay;
481 gtk_tooltips_set_active_widget (tooltips, NULL);
490 gtk_tooltips_widget_unmap (GtkWidget *widget,
493 GtkTooltipsData *tooltipsdata = (GtkTooltipsData *)data;
494 GtkTooltips *tooltips = tooltipsdata->tooltips;
496 if (tooltips->active_tips_data &&
497 (tooltips->active_tips_data->widget == widget))
498 gtk_tooltips_set_active_widget (tooltips, NULL);
502 gtk_tooltips_widget_remove (GtkWidget *widget,
505 GtkTooltipsData *tooltipsdata = (GtkTooltipsData*) data;
506 GtkTooltips *tooltips = tooltipsdata->tooltips;
508 gtk_tooltips_widget_unmap (widget, data);
509 tooltips->tips_data_list = g_list_remove (tooltips->tips_data_list,
511 gtk_tooltips_destroy_data (tooltipsdata);
515 _gtk_tooltips_show_tip (GtkWidget *widget)
517 /* Showing the tip from the keyboard */
519 /* FIXME this function is completely broken right now,
520 * popdown doesn't occur when it should.
523 GtkTooltipsData *tooltipsdata;
525 tooltipsdata = gtk_tooltips_data_get (widget);
527 if (tooltipsdata == NULL)
530 gtk_tooltips_set_active_widget (tooltipsdata->tooltips,
533 gtk_tooltips_timeout (tooltipsdata->tooltips);