Updated Spanish translation
[anjuta-git-plugin.git] / plugins / document-manager / editor-tooltips.c
blob8a93ecc326298764b1b20ea13a3de5876683d4b2
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 */
28 #include <gtk/gtk.h>
29 #if !GTK_CHECK_VERSION (2,12,0)
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
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
48 * if we are sticky
50 #define STICKY_REVERT_DELAY 1000 /* Delay before sticky tooltips revert
51 * to normal
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,
59 GdkEvent * event);
60 static void editor_tooltips_widget_unmap (GtkWidget * widget,
61 gpointer data);
62 static void editor_tooltips_widget_remove (GtkWidget * widget,
63 gpointer data);
64 static void editor_tooltips_set_active_widget (EditorTooltips * tooltips,
65 GtkWidget * widget);
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";
77 GType
78 editor_tooltips_get_type (void)
80 static GType tooltips_type = 0;
82 if (!tooltips_type) {
83 static const GTypeInfo tooltips_info = {
84 sizeof (EditorTooltipsClass),
85 NULL, /* base_init */
86 NULL, /* base_finalize */
87 (GClassInitFunc) editor_tooltips_class_init,
88 NULL, /* class_finalize */
89 NULL, /* class_data */
90 sizeof (EditorTooltips),
91 0, /* n_preallocs */
92 (GInstanceInitFunc) editor_tooltips_init,
95 tooltips_type =
96 g_type_register_static (GTK_TYPE_OBJECT,
97 "EditorTooltips",
98 &tooltips_info, 0);
101 return tooltips_type;
104 static void
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;
116 static void
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;
131 EditorTooltips *
132 editor_tooltips_new (void)
134 return g_object_new (EDITOR_TYPE_TOOLTIPS, NULL);
137 static void
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,
145 tooltipsdata);
146 g_signal_handlers_disconnect_by_func (tooltipsdata->widget,
147 editor_tooltips_widget_unmap,
148 tooltipsdata);
149 g_signal_handlers_disconnect_by_func (tooltipsdata->widget,
150 editor_tooltips_widget_remove,
151 tooltipsdata);
153 g_object_set_data (G_OBJECT (tooltipsdata->widget),
154 tooltips_data_key, NULL);
155 g_object_unref (tooltipsdata->widget);
156 g_free (tooltipsdata);
159 static void
160 tip_window_display_closed (GdkDisplay * display,
161 gboolean was_error, EditorTooltips * tooltips)
163 editor_tooltips_unset_tip_window (tooltips);
166 static void
167 disconnect_tip_window_display_closed (EditorTooltips * tooltips)
169 g_signal_handlers_disconnect_by_func (gtk_widget_get_display
170 (tooltips->tip_window),
171 (gpointer)
172 tip_window_display_closed,
173 tooltips);
176 static void
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;
187 static void
188 editor_tooltips_destroy (GtkObject * object)
190 EditorTooltips *tooltips = EDITOR_TOOLTIPS (object);
191 GList *current;
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,
208 tooltipsdata);
212 editor_tooltips_unset_tip_window (tooltips);
215 static void
216 editor_tooltips_update_screen (EditorTooltips * tooltips,
217 gboolean new_window)
219 gboolean screen_changed = FALSE;
221 if (tooltips->active_tips_data &&
222 tooltips->active_tips_data->widget) {
223 GdkScreen *screen =
224 gtk_widget_get_screen (tooltips->active_tips_data->
225 widget);
227 screen_changed =
228 (screen !=
229 gtk_widget_get_screen (tooltips->tip_window));
231 if (screen_changed) {
232 if (!new_window)
233 disconnect_tip_window_display_closed
234 (tooltips);
236 gtk_window_set_screen (GTK_WINDOW
237 (tooltips->tip_window),
238 screen);
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),
246 tooltips);
250 void
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,
266 "expose_event",
267 G_CALLBACK
268 (editor_tooltips_paint_window),
269 tooltips);
271 tooltips->tip_label = gtk_label_new (NULL);
272 gtk_label_set_line_wrap (GTK_LABEL (tooltips->tip_label),
273 TRUE);
274 gtk_misc_set_alignment (GTK_MISC (tooltips->tip_label),
275 0.5, 0.5);
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,
282 "destroy",
283 G_CALLBACK (gtk_widget_destroyed),
284 &tooltips->tip_window);
288 void
289 editor_tooltips_enable (EditorTooltips * tooltips)
291 g_return_if_fail (tooltips != NULL);
293 tooltips->enabled = TRUE;
296 void
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;
306 EditorTooltipsData *
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);
314 void
315 editor_tooltips_set_tip (EditorTooltips * tooltips,
316 GtkWidget * widget,
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);
326 if (!tip_text) {
327 if (tooltipsdata)
328 editor_tooltips_widget_remove (tooltipsdata->widget,
329 tooltipsdata);
330 return;
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);
344 } else {
345 DEBUG_PRINT ("tooltip data aws not present. Setting now" );
346 g_object_ref (widget);
348 if (tooltipsdata)
349 editor_tooltips_widget_remove (tooltipsdata->widget,
350 tooltipsdata);
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",
363 G_CALLBACK
364 (editor_tooltips_event_handler),
365 tooltipsdata);
367 g_object_set_data (G_OBJECT (widget), tooltips_data_key,
368 tooltipsdata);
370 g_signal_connect (widget, "unmap",
371 G_CALLBACK (editor_tooltips_widget_unmap),
372 tooltipsdata);
374 g_signal_connect (widget, "unrealize",
375 G_CALLBACK (editor_tooltips_widget_unmap),
376 tooltipsdata);
378 g_signal_connect (widget, "destroy",
379 G_CALLBACK
380 (editor_tooltips_widget_remove),
381 tooltipsdata);
385 static gint
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",
392 0, 0, -1, -1);
394 return FALSE;
397 static void
398 editor_tooltips_draw_tips (EditorTooltips * tooltips)
400 GtkRequisition requisition;
401 GtkWidget *widget;
402 GtkStyle *style;
403 gint x, y, w, h, scr_w, scr_h;
404 EditorTooltipsData *data;
405 gboolean keyboard_mode;
406 GdkScreen *screen;
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),
429 data->tip_text);
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;
443 if (!keyboard_mode)
444 gdk_window_get_pointer (gdk_screen_get_root_window
445 (screen), &x, NULL, NULL);
447 x -= (w / 2 + 4);
449 if ((x + w) > scr_w)
450 x -= (x + w) - scr_w;
451 else if (x < 0)
452 x = 0;
454 if ((y + h + widget->allocation.height + 4) > scr_h)
455 y = y - h - 4;
456 else
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);
463 static gint
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 ();
476 return FALSE;
479 static void
480 editor_tooltips_set_active_widget (EditorTooltips * tooltips,
481 GtkWidget * widget)
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;
495 if (widget) {
496 GList *list;
498 for (list = tooltips->tips_data_list; list;
499 list = list->next) {
500 EditorTooltipsData *tooltipsdata;
502 tooltipsdata = list->data;
504 if (tooltipsdata->widget == widget &&
505 GTK_WIDGET_DRAWABLE (widget)) {
506 tooltips->active_tips_data = tooltipsdata;
507 break;
510 } else {
511 tooltips->use_sticky_delay = FALSE;
515 static void
516 editor_tooltips_show_tip (GtkWidget * widget)
518 EditorTooltipsData *tooltipsdata;
520 tooltipsdata = editor_tooltips_data_get (widget);
522 if (tooltipsdata &&
523 (!tooltipsdata->tooltips->active_tips_data ||
524 tooltipsdata->tooltips->active_tips_data->widget != widget)) {
525 editor_tooltips_set_active_widget (tooltipsdata->tooltips,
526 widget);
527 editor_tooltips_draw_tips (tooltipsdata->tooltips);
531 static void
532 editor_tooltips_hide_tip (GtkWidget * widget)
534 EditorTooltipsData *tooltipsdata;
536 tooltipsdata = editor_tooltips_data_get (widget);
538 if (tooltipsdata &&
539 (tooltipsdata->tooltips->active_tips_data &&
540 tooltipsdata->tooltips->active_tips_data->widget == widget))
541 editor_tooltips_set_active_widget (tooltipsdata->tooltips,
542 NULL);
545 static gboolean
546 editor_tooltips_recently_shown (EditorTooltips * tooltips)
548 GTimeVal now;
549 glong msec;
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);
557 static gboolean
558 get_keyboard_mode (GtkWidget * widget)
560 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
561 if (GTK_IS_WINDOW (toplevel))
562 return
563 GPOINTER_TO_UINT (g_object_get_data
564 (G_OBJECT (toplevel),
565 "gtk-tooltips-keyboard-mode"));
566 else
567 return FALSE;
570 static void
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));
581 if (focus)
582 editor_tooltips_show_tip (focus);
586 static void
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;
592 if (focus)
593 editor_tooltips_hide_tip (focus);
595 g_object_set_data (G_OBJECT (toplevel),
596 "gtk-tooltips-keyboard-mode",
597 GUINT_TO_POINTER (FALSE));
601 static void
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)) {
609 guint delay;
611 editor_tooltips_set_active_widget (tooltips, widget);
613 if (tooltips->use_sticky_delay &&
614 editor_tooltips_recently_shown (tooltips))
615 delay = STICKY_DELAY;
616 else
617 delay = tooltips->delay;
618 tooltips->timer_tag = g_timeout_add (delay,
619 editor_tooltips_timeout,
620 (gpointer)
621 tooltips);
625 static void
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)
636 return;
638 old_tips_data = editor_tooltips_data_get (widget);
639 tooltips = old_tips_data->tooltips;
641 if (keyboard_mode) {
642 switch (event->type) {
643 case GDK_FOCUS_CHANGE:
644 if (event->focus_change.in)
645 editor_tooltips_show_tip (widget);
646 else
647 editor_tooltips_hide_tip (widget);
648 break;
649 default:
650 break;
652 } else {
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)
657 return;
660 switch (event->type) {
661 case GDK_EXPOSE:
662 /* do nothing */
663 break;
664 case GDK_ENTER_NOTIFY:
665 if (!
666 (GTK_IS_MENU_ITEM (widget)
667 && GTK_MENU_ITEM (widget)->submenu))
668 editor_tooltips_start_delay (tooltips,
669 widget);
670 break;
672 case GDK_LEAVE_NOTIFY:
674 gboolean use_sticky_delay;
676 use_sticky_delay = tooltips->tip_window &&
677 GTK_WIDGET_VISIBLE (tooltips->
678 tip_window);
679 editor_tooltips_set_active_widget (tooltips,
680 NULL);
681 tooltips->use_sticky_delay =
682 use_sticky_delay;
684 break;
685 #if 0
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,
696 GTK_LEAVE_PENDING);
697 editor_tooltips_set_active_widget (tooltips,
698 NULL);
699 editor_tooltips_start_delay (tooltips,
700 widget);
701 break;
703 break; /* ignore */
704 #endif
705 case GDK_BUTTON_PRESS:
706 case GDK_BUTTON_RELEASE:
707 case GDK_KEY_PRESS:
708 case GDK_KEY_RELEASE:
709 case GDK_PROXIMITY_IN:
710 case GDK_SCROLL:
711 editor_tooltips_set_active_widget (tooltips, NULL);
712 break;
713 default:
714 break;
719 static void
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);
730 static void
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,
738 tooltipsdata);
739 editor_tooltips_destroy_data (tooltipsdata);
742 void
743 _editor_tooltips_toggle_keyboard_mode (GtkWidget * widget)
745 if (get_keyboard_mode (widget))
746 stop_keyboard_mode (widget);
747 else
748 start_keyboard_mode (widget);
751 #endif /* !GTK_CHECK_VERSION (2,12,0) */