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., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, 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/.
27 /* gtk 2.12 tooltips API can replace all this */
29 #if !GTK_CHECK_VERSION (2,12,0)
35 #include <gtk/gtklabel.h>
36 #include <gtk/gtkmain.h>
37 #include <gtk/gtkmenuitem.h>
38 #include <gtk/gtkwidget.h>
39 #include <gtk/gtkwindow.h>
40 #include <gtk/gtkstyle.h>
42 #include "editor-tooltips.h"
43 #include <libanjuta/anjuta-debug.h>
46 #define DEFAULT_DELAY 500 /* Default delay in ms */
47 #define STICKY_DELAY 0 /* Delay before popping up next tip
50 #define STICKY_REVERT_DELAY 1000 /* Delay before sticky tooltips revert
54 static void editor_tooltips_class_init (EditorTooltipsClass
* klass
);
55 static void editor_tooltips_init (EditorTooltips
* tooltips
);
56 static void editor_tooltips_destroy (GtkObject
* object
);
58 static void editor_tooltips_event_handler (GtkWidget
* widget
,
60 static void editor_tooltips_widget_unmap (GtkWidget
* widget
,
62 static void editor_tooltips_widget_remove (GtkWidget
* widget
,
64 static void editor_tooltips_set_active_widget (EditorTooltips
* tooltips
,
66 static gint
editor_tooltips_timeout (gpointer data
);
68 static gint
editor_tooltips_paint_window (EditorTooltips
* tooltips
);
69 static void editor_tooltips_draw_tips (EditorTooltips
* tooltips
);
70 static void editor_tooltips_unset_tip_window (EditorTooltips
* tooltips
);
72 static gboolean
get_keyboard_mode (GtkWidget
* widget
);
74 static GtkObjectClass
*parent_class
;
75 static const gchar
*tooltips_data_key
= "_EditorTooltipsData";
78 editor_tooltips_get_type (void)
80 static GType tooltips_type
= 0;
83 static const GTypeInfo tooltips_info
= {
84 sizeof (EditorTooltipsClass
),
86 NULL
, /* base_finalize */
87 (GClassInitFunc
) editor_tooltips_class_init
,
88 NULL
, /* class_finalize */
89 NULL
, /* class_data */
90 sizeof (EditorTooltips
),
92 (GInstanceInitFunc
) editor_tooltips_init
,
96 g_type_register_static (GTK_TYPE_OBJECT
,
101 return tooltips_type
;
105 editor_tooltips_class_init (EditorTooltipsClass
* klass
)
107 GtkObjectClass
*object_class
;
109 object_class
= GTK_OBJECT_CLASS (klass
);
111 parent_class
= g_type_class_peek_parent (klass
);
113 object_class
->destroy
= editor_tooltips_destroy
;
117 editor_tooltips_init (EditorTooltips
* tooltips
)
119 tooltips
->tip_window
= NULL
;
120 tooltips
->active_tips_data
= NULL
;
121 tooltips
->tips_data_list
= NULL
;
123 tooltips
->delay
= DEFAULT_DELAY
;
124 tooltips
->enabled
= TRUE
;
125 tooltips
->timer_tag
= 0;
126 tooltips
->use_sticky_delay
= FALSE
;
127 tooltips
->last_popdown
.tv_sec
= -1;
128 tooltips
->last_popdown
.tv_usec
= -1;
132 editor_tooltips_new (void)
134 return g_object_new (EDITOR_TYPE_TOOLTIPS
, NULL
);
138 editor_tooltips_destroy_data (EditorTooltipsData
* tooltipsdata
)
140 g_free (tooltipsdata
->tip_text
);
141 g_free (tooltipsdata
->tip_private
);
143 g_signal_handlers_disconnect_by_func (tooltipsdata
->widget
,
144 editor_tooltips_event_handler
,
146 g_signal_handlers_disconnect_by_func (tooltipsdata
->widget
,
147 editor_tooltips_widget_unmap
,
149 g_signal_handlers_disconnect_by_func (tooltipsdata
->widget
,
150 editor_tooltips_widget_remove
,
153 g_object_set_data (G_OBJECT (tooltipsdata
->widget
),
154 tooltips_data_key
, NULL
);
155 g_object_unref (tooltipsdata
->widget
);
156 g_free (tooltipsdata
);
160 tip_window_display_closed (GdkDisplay
* display
,
161 gboolean was_error
, EditorTooltips
* tooltips
)
163 editor_tooltips_unset_tip_window (tooltips
);
167 disconnect_tip_window_display_closed (EditorTooltips
* tooltips
)
169 g_signal_handlers_disconnect_by_func (gtk_widget_get_display
170 (tooltips
->tip_window
),
172 tip_window_display_closed
,
177 editor_tooltips_unset_tip_window (EditorTooltips
* tooltips
)
179 if (tooltips
->tip_window
) {
180 disconnect_tip_window_display_closed (tooltips
);
182 gtk_widget_destroy (tooltips
->tip_window
);
183 tooltips
->tip_window
= NULL
;
188 editor_tooltips_destroy (GtkObject
* object
)
190 EditorTooltips
*tooltips
= EDITOR_TOOLTIPS (object
);
192 EditorTooltipsData
*tooltipsdata
;
194 g_return_if_fail (tooltips
!= NULL
);
196 DEBUG_PRINT ("destroying tooltips...");
197 if (tooltips
->timer_tag
) {
198 g_source_remove (tooltips
->timer_tag
);
199 tooltips
->timer_tag
= 0;
202 if (tooltips
->tips_data_list
!= NULL
) {
203 current
= g_list_first (tooltips
->tips_data_list
);
204 while (current
!= NULL
) {
205 tooltipsdata
= (EditorTooltipsData
*) current
->data
;
206 current
= current
->next
;
207 editor_tooltips_widget_remove (tooltipsdata
->widget
,
212 editor_tooltips_unset_tip_window (tooltips
);
216 editor_tooltips_update_screen (EditorTooltips
* tooltips
,
219 gboolean screen_changed
= FALSE
;
221 if (tooltips
->active_tips_data
&&
222 tooltips
->active_tips_data
->widget
) {
224 gtk_widget_get_screen (tooltips
->active_tips_data
->
229 gtk_widget_get_screen (tooltips
->tip_window
));
231 if (screen_changed
) {
233 disconnect_tip_window_display_closed
236 gtk_window_set_screen (GTK_WINDOW
237 (tooltips
->tip_window
),
242 if (screen_changed
|| new_window
)
243 g_signal_connect (gtk_widget_get_display
244 (tooltips
->tip_window
), "closed",
245 G_CALLBACK (tip_window_display_closed
),
251 editor_tooltips_force_window (EditorTooltips
* tooltips
)
253 g_return_if_fail (EDITOR_IS_TOOLTIPS (tooltips
));
255 if (!tooltips
->tip_window
) {
256 tooltips
->tip_window
= gtk_window_new (GTK_WINDOW_POPUP
);
257 editor_tooltips_update_screen (tooltips
, TRUE
);
258 gtk_widget_set_app_paintable (tooltips
->tip_window
, TRUE
);
259 gtk_window_set_resizable (GTK_WINDOW
260 (tooltips
->tip_window
), FALSE
);
261 gtk_widget_set_name (tooltips
->tip_window
, "gtk-tooltips");
262 gtk_container_set_border_width (GTK_CONTAINER
263 (tooltips
->tip_window
), 4);
265 g_signal_connect_swapped (tooltips
->tip_window
,
268 (editor_tooltips_paint_window
),
271 tooltips
->tip_label
= gtk_label_new (NULL
);
272 gtk_label_set_line_wrap (GTK_LABEL (tooltips
->tip_label
),
274 gtk_misc_set_alignment (GTK_MISC (tooltips
->tip_label
),
276 gtk_widget_show (tooltips
->tip_label
);
278 gtk_container_add (GTK_CONTAINER (tooltips
->tip_window
),
279 tooltips
->tip_label
);
281 g_signal_connect (tooltips
->tip_window
,
283 G_CALLBACK (gtk_widget_destroyed
),
284 &tooltips
->tip_window
);
289 editor_tooltips_enable (EditorTooltips
* tooltips
)
291 g_return_if_fail (tooltips
!= NULL
);
293 tooltips
->enabled
= TRUE
;
297 editor_tooltips_disable (EditorTooltips
* tooltips
)
299 g_return_if_fail (tooltips
!= NULL
);
301 editor_tooltips_set_active_widget (tooltips
, NULL
);
303 tooltips
->enabled
= FALSE
;
307 editor_tooltips_data_get (GtkWidget
* widget
)
309 g_return_val_if_fail (widget
!= NULL
, NULL
);
311 return g_object_get_data (G_OBJECT (widget
), tooltips_data_key
);
315 editor_tooltips_set_tip (EditorTooltips
* tooltips
,
317 const gchar
* tip_text
, const gchar
* tip_private
)
319 EditorTooltipsData
*tooltipsdata
;
321 g_return_if_fail (EDITOR_IS_TOOLTIPS (tooltips
));
322 g_return_if_fail (widget
!= NULL
);
324 tooltipsdata
= editor_tooltips_data_get (widget
);
328 editor_tooltips_widget_remove (tooltipsdata
->widget
,
333 if (tooltips
->active_tips_data
334 && tooltips
->active_tips_data
->widget
== widget
335 && GTK_WIDGET_DRAWABLE (tooltips
->active_tips_data
->widget
)) {
337 g_free (tooltipsdata
->tip_text
);
338 g_free (tooltipsdata
->tip_private
);
340 tooltipsdata
->tip_text
= g_strdup (tip_text
);
341 tooltipsdata
->tip_private
= g_strdup (tip_private
);
343 editor_tooltips_draw_tips (tooltips
);
345 DEBUG_PRINT ("tooltip data aws not present. Setting now" );
346 g_object_ref (widget
);
349 editor_tooltips_widget_remove (tooltipsdata
->widget
,
352 tooltipsdata
= g_new0 (EditorTooltipsData
, 1);
354 tooltipsdata
->tooltips
= tooltips
;
355 tooltipsdata
->widget
= widget
;
357 tooltipsdata
->tip_text
= g_strdup (tip_text
);
358 tooltipsdata
->tip_private
= g_strdup (tip_private
);
360 tooltips
->tips_data_list
=
361 g_list_append (tooltips
->tips_data_list
, tooltipsdata
);
362 g_signal_connect_after (widget
, "event-after",
364 (editor_tooltips_event_handler
),
367 g_object_set_data (G_OBJECT (widget
), tooltips_data_key
,
370 g_signal_connect (widget
, "unmap",
371 G_CALLBACK (editor_tooltips_widget_unmap
),
374 g_signal_connect (widget
, "unrealize",
375 G_CALLBACK (editor_tooltips_widget_unmap
),
378 g_signal_connect (widget
, "destroy",
380 (editor_tooltips_widget_remove
),
386 editor_tooltips_paint_window (EditorTooltips
* tooltips
)
388 gtk_paint_flat_box (tooltips
->tip_window
->style
,
389 tooltips
->tip_window
->window
, GTK_STATE_NORMAL
,
390 GTK_SHADOW_OUT
, NULL
,
391 GTK_WIDGET (tooltips
->tip_window
), "tooltip",
398 editor_tooltips_draw_tips (EditorTooltips
* tooltips
)
400 GtkRequisition requisition
;
403 gint x
, y
, w
, h
, scr_w
, scr_h
;
404 EditorTooltipsData
*data
;
405 gboolean keyboard_mode
;
408 if (!tooltips
->tip_window
)
409 editor_tooltips_force_window (tooltips
);
410 else if (GTK_WIDGET_VISIBLE (tooltips
->tip_window
))
411 g_get_current_time (&tooltips
->last_popdown
);
413 gtk_widget_ensure_style (tooltips
->tip_window
);
414 style
= tooltips
->tip_window
->style
;
416 widget
= tooltips
->active_tips_data
->widget
;
418 keyboard_mode
= get_keyboard_mode (widget
);
420 editor_tooltips_update_screen (tooltips
, FALSE
);
422 screen
= gtk_widget_get_screen (widget
);
423 scr_w
= gdk_screen_get_width (screen
);
424 scr_h
= gdk_screen_get_height (screen
);
426 data
= tooltips
->active_tips_data
;
428 gtk_label_set_markup (GTK_LABEL (tooltips
->tip_label
),
431 gtk_widget_size_request (tooltips
->tip_window
, &requisition
);
432 w
= requisition
.width
;
433 h
= requisition
.height
;
435 gdk_window_get_origin (widget
->window
, &x
, &y
);
436 if (GTK_WIDGET_NO_WINDOW (widget
)) {
437 x
+= widget
->allocation
.x
;
438 y
+= widget
->allocation
.y
;
441 x
+= widget
->allocation
.width
/ 2;
444 gdk_window_get_pointer (gdk_screen_get_root_window
445 (screen
), &x
, NULL
, NULL
);
450 x
-= (x
+ w
) - scr_w
;
454 if ((y
+ h
+ widget
->allocation
.height
+ 4) > scr_h
)
457 y
= y
+ widget
->allocation
.height
+ 4;
459 gtk_window_move (GTK_WINDOW (tooltips
->tip_window
), x
, y
);
460 gtk_widget_show (tooltips
->tip_window
);
464 editor_tooltips_timeout (gpointer data
)
466 EditorTooltips
*tooltips
= EDITOR_TOOLTIPS (data
);
468 GDK_THREADS_ENTER ();
470 if (tooltips
->active_tips_data
!= NULL
&&
471 GTK_WIDGET_DRAWABLE (tooltips
->active_tips_data
->widget
))
472 editor_tooltips_draw_tips (tooltips
);
474 GDK_THREADS_LEAVE ();
480 editor_tooltips_set_active_widget (EditorTooltips
* tooltips
,
483 if (tooltips
->tip_window
) {
484 if (GTK_WIDGET_VISIBLE (tooltips
->tip_window
))
485 g_get_current_time (&tooltips
->last_popdown
);
486 gtk_widget_hide (tooltips
->tip_window
);
488 if (tooltips
->timer_tag
) {
489 g_source_remove (tooltips
->timer_tag
);
490 tooltips
->timer_tag
= 0;
493 tooltips
->active_tips_data
= NULL
;
498 for (list
= tooltips
->tips_data_list
; list
;
500 EditorTooltipsData
*tooltipsdata
;
502 tooltipsdata
= list
->data
;
504 if (tooltipsdata
->widget
== widget
&&
505 GTK_WIDGET_DRAWABLE (widget
)) {
506 tooltips
->active_tips_data
= tooltipsdata
;
511 tooltips
->use_sticky_delay
= FALSE
;
516 editor_tooltips_show_tip (GtkWidget
* widget
)
518 EditorTooltipsData
*tooltipsdata
;
520 tooltipsdata
= editor_tooltips_data_get (widget
);
523 (!tooltipsdata
->tooltips
->active_tips_data
||
524 tooltipsdata
->tooltips
->active_tips_data
->widget
!= widget
)) {
525 editor_tooltips_set_active_widget (tooltipsdata
->tooltips
,
527 editor_tooltips_draw_tips (tooltipsdata
->tooltips
);
532 editor_tooltips_hide_tip (GtkWidget
* widget
)
534 EditorTooltipsData
*tooltipsdata
;
536 tooltipsdata
= editor_tooltips_data_get (widget
);
539 (tooltipsdata
->tooltips
->active_tips_data
&&
540 tooltipsdata
->tooltips
->active_tips_data
->widget
== widget
))
541 editor_tooltips_set_active_widget (tooltipsdata
->tooltips
,
546 editor_tooltips_recently_shown (EditorTooltips
* tooltips
)
551 g_get_current_time (&now
);
552 msec
= (now
.tv_sec
- tooltips
->last_popdown
.tv_sec
) * 1000 +
553 (now
.tv_usec
- tooltips
->last_popdown
.tv_usec
) / 1000;
554 return (msec
< STICKY_REVERT_DELAY
);
558 get_keyboard_mode (GtkWidget
* widget
)
560 GtkWidget
*toplevel
= gtk_widget_get_toplevel (widget
);
561 if (GTK_IS_WINDOW (toplevel
))
563 GPOINTER_TO_UINT (g_object_get_data
564 (G_OBJECT (toplevel
),
565 "gtk-tooltips-keyboard-mode"));
571 start_keyboard_mode (GtkWidget
* widget
)
573 GtkWidget
*toplevel
= gtk_widget_get_toplevel (widget
);
574 if (GTK_IS_WINDOW (toplevel
)) {
575 GtkWidget
*focus
= GTK_WINDOW (toplevel
)->focus_widget
;
577 g_object_set_data (G_OBJECT (toplevel
),
578 "gtk-tooltips-keyboard-mode",
579 GUINT_TO_POINTER (TRUE
));
582 editor_tooltips_show_tip (focus
);
587 stop_keyboard_mode (GtkWidget
* widget
)
589 GtkWidget
*toplevel
= gtk_widget_get_toplevel (widget
);
590 if (GTK_IS_WINDOW (toplevel
)) {
591 GtkWidget
*focus
= GTK_WINDOW (toplevel
)->focus_widget
;
593 editor_tooltips_hide_tip (focus
);
595 g_object_set_data (G_OBJECT (toplevel
),
596 "gtk-tooltips-keyboard-mode",
597 GUINT_TO_POINTER (FALSE
));
602 editor_tooltips_start_delay (EditorTooltips
* tooltips
, GtkWidget
* widget
)
604 EditorTooltipsData
*old_tips_data
;
606 old_tips_data
= tooltips
->active_tips_data
;
607 if (tooltips
->enabled
&&
608 (!old_tips_data
|| old_tips_data
->widget
!= widget
)) {
611 editor_tooltips_set_active_widget (tooltips
, widget
);
613 if (tooltips
->use_sticky_delay
&&
614 editor_tooltips_recently_shown (tooltips
))
615 delay
= STICKY_DELAY
;
617 delay
= tooltips
->delay
;
618 tooltips
->timer_tag
= g_timeout_add (delay
,
619 editor_tooltips_timeout
,
626 editor_tooltips_event_handler (GtkWidget
* widget
, GdkEvent
* event
)
628 EditorTooltips
*tooltips
;
629 EditorTooltipsData
*old_tips_data
;
630 GtkWidget
*event_widget
;
631 gboolean keyboard_mode
= get_keyboard_mode (widget
);
633 if ((event
->type
== GDK_LEAVE_NOTIFY
634 || event
->type
== GDK_ENTER_NOTIFY
)
635 && event
->crossing
.detail
== GDK_NOTIFY_INFERIOR
)
638 old_tips_data
= editor_tooltips_data_get (widget
);
639 tooltips
= old_tips_data
->tooltips
;
642 switch (event
->type
) {
643 case GDK_FOCUS_CHANGE
:
644 if (event
->focus_change
.in
)
645 editor_tooltips_show_tip (widget
);
647 editor_tooltips_hide_tip (widget
);
653 if (event
->type
!= GDK_KEY_PRESS
654 && event
->type
!= GDK_KEY_RELEASE
) {
655 event_widget
= gtk_get_event_widget (event
);
656 if (event_widget
!= widget
)
660 switch (event
->type
) {
664 case GDK_ENTER_NOTIFY
:
666 (GTK_IS_MENU_ITEM (widget
)
667 && GTK_MENU_ITEM (widget
)->submenu
))
668 editor_tooltips_start_delay (tooltips
,
672 case GDK_LEAVE_NOTIFY
:
674 gboolean use_sticky_delay
;
676 use_sticky_delay
= tooltips
->tip_window
&&
677 GTK_WIDGET_VISIBLE (tooltips
->
679 editor_tooltips_set_active_widget (tooltips
,
681 tooltips
->use_sticky_delay
=
686 case GDK_MOTION_NOTIFY
:
688 /* Handle menu items specially ... pend popup for each motion
689 * on other widgets, we ignore motion.
691 if (GTK_IS_MENU_ITEM (widget
)
692 && !GTK_MENU_ITEM (widget
)->submenu
) {
693 /* Completely evil hack to make sure we get the LEAVE_NOTIFY
695 GTK_PRIVATE_SET_FLAG (widget
,
697 editor_tooltips_set_active_widget (tooltips
,
699 editor_tooltips_start_delay (tooltips
,
705 case GDK_BUTTON_PRESS
:
706 case GDK_BUTTON_RELEASE
:
708 case GDK_KEY_RELEASE
:
709 case GDK_PROXIMITY_IN
:
711 editor_tooltips_set_active_widget (tooltips
, NULL
);
720 editor_tooltips_widget_unmap (GtkWidget
* widget
, gpointer data
)
722 EditorTooltipsData
*tooltipsdata
= (EditorTooltipsData
*) data
;
723 EditorTooltips
*tooltips
= tooltipsdata
->tooltips
;
725 if (tooltips
->active_tips_data
&&
726 (tooltips
->active_tips_data
->widget
== widget
))
727 editor_tooltips_set_active_widget (tooltips
, NULL
);
731 editor_tooltips_widget_remove (GtkWidget
* widget
, gpointer data
)
733 EditorTooltipsData
*tooltipsdata
= (EditorTooltipsData
*) data
;
734 EditorTooltips
*tooltips
= tooltipsdata
->tooltips
;
736 editor_tooltips_widget_unmap (widget
, data
);
737 tooltips
->tips_data_list
= g_list_remove (tooltips
->tips_data_list
,
739 editor_tooltips_destroy_data (tooltipsdata
);
743 _editor_tooltips_toggle_keyboard_mode (GtkWidget
* widget
)
745 if (get_keyboard_mode (widget
))
746 stop_keyboard_mode (widget
);
748 start_keyboard_mode (widget
);
751 #endif /* !GTK_CHECK_VERSION (2,12,0) */