1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkQueryTips: Query onscreen widgets for their tooltips
5  * Copyright (C) 1998 Tim Janik
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 
23 /*
24  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GTK+ Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28  */
29 
30 #undef GTK_DISABLE_DEPRECATED
31 
32 #include "config.h"
33 #include "gtktipsquery.h"
34 #include "gtksignal.h"
35 #include "gtktooltips.h"
36 #include "gtkmain.h"
37 #include "gtkmarshalers.h"
38 #include "gtkintl.h"
39 
40 #include "gtkalias.h"
41 
42 
43 
44 /* --- arguments --- */
45 enum {
46   ARG_0,
47   ARG_EMIT_ALWAYS,
48   ARG_CALLER,
49   ARG_LABEL_INACTIVE,
50   ARG_LABEL_NO_TIP
51 };
52 
53 
54 /* --- signals --- */
55 enum
56 {
57   SIGNAL_START_QUERY,
58   SIGNAL_STOP_QUERY,
59   SIGNAL_WIDGET_ENTERED,
60   SIGNAL_WIDGET_SELECTED,
61   SIGNAL_LAST
62 };
63 
64 /* --- prototypes --- */
65 static void	gtk_tips_query_class_init	(GtkTipsQueryClass	*class);
66 static void	gtk_tips_query_init		(GtkTipsQuery		*tips_query);
67 static void	gtk_tips_query_destroy		(GtkObject		*object);
68 static gint	gtk_tips_query_event		(GtkWidget		*widget,
69 						 GdkEvent		*event);
70 static void	gtk_tips_query_set_arg		(GtkObject              *object,
71 						 GtkArg			*arg,
72 						 guint			 arg_id);
73 static void	gtk_tips_query_get_arg		(GtkObject              *object,
74 						 GtkArg			*arg,
75 						 guint			arg_id);
76 static void	gtk_tips_query_real_start_query	(GtkTipsQuery		*tips_query);
77 static void	gtk_tips_query_real_stop_query	(GtkTipsQuery		*tips_query);
78 static void	gtk_tips_query_widget_entered	(GtkTipsQuery		*tips_query,
79 						 GtkWidget		*widget,
80 						 const gchar		*tip_text,
81 						 const gchar		*tip_private);
82 
83 
84 /* --- variables --- */
85 static GtkLabelClass	*parent_class = NULL;
86 static guint		 tips_query_signals[SIGNAL_LAST] = { 0 };
87 
88 
89 /* --- functions --- */
90 GtkType
gtk_tips_query_get_type(void)91 gtk_tips_query_get_type (void)
92 {
93   static GtkType tips_query_type = 0;
94 
95   if (!tips_query_type)
96     {
97       static const GtkTypeInfo tips_query_info =
98       {
99 	"GtkTipsQuery",
100 	sizeof (GtkTipsQuery),
101 	sizeof (GtkTipsQueryClass),
102 	(GtkClassInitFunc) gtk_tips_query_class_init,
103 	(GtkObjectInitFunc) gtk_tips_query_init,
104         /* reserved_1 */ NULL,
105 	/* reserved_2 */ NULL,
106 	(GtkClassInitFunc) NULL,
107       };
108 
109       I_("GtkTipsQuery");
110       tips_query_type = gtk_type_unique (gtk_label_get_type (), &tips_query_info);
111     }
112 
113   return tips_query_type;
114 }
115 
116 static void
gtk_tips_query_class_init(GtkTipsQueryClass * class)117 gtk_tips_query_class_init (GtkTipsQueryClass *class)
118 {
119   GtkObjectClass *object_class;
120   GtkWidgetClass *widget_class;
121 
122   object_class = (GtkObjectClass*) class;
123   widget_class = (GtkWidgetClass*) class;
124 
125   parent_class = gtk_type_class (gtk_label_get_type ());
126 
127 
128   object_class->set_arg = gtk_tips_query_set_arg;
129   object_class->get_arg = gtk_tips_query_get_arg;
130   object_class->destroy = gtk_tips_query_destroy;
131 
132   widget_class->event = gtk_tips_query_event;
133 
134   class->start_query = gtk_tips_query_real_start_query;
135   class->stop_query = gtk_tips_query_real_stop_query;
136   class->widget_entered = gtk_tips_query_widget_entered;
137   class->widget_selected = NULL;
138 
139   gtk_object_add_arg_type ("GtkTipsQuery::emit-always", GTK_TYPE_BOOL, GTK_ARG_READWRITE | G_PARAM_STATIC_NAME, ARG_EMIT_ALWAYS);
140   gtk_object_add_arg_type ("GtkTipsQuery::caller", GTK_TYPE_WIDGET, GTK_ARG_READWRITE | G_PARAM_STATIC_NAME, ARG_CALLER);
141   gtk_object_add_arg_type ("GtkTipsQuery::label-inactive", GTK_TYPE_STRING, GTK_ARG_READWRITE | G_PARAM_STATIC_NAME, ARG_LABEL_INACTIVE);
142   gtk_object_add_arg_type ("GtkTipsQuery::label-no-tip", GTK_TYPE_STRING, GTK_ARG_READWRITE | G_PARAM_STATIC_NAME, ARG_LABEL_NO_TIP);
143 
144   tips_query_signals[SIGNAL_START_QUERY] =
145     gtk_signal_new (I_("start-query"),
146 		    GTK_RUN_FIRST,
147 		    GTK_CLASS_TYPE (object_class),
148 		    GTK_SIGNAL_OFFSET (GtkTipsQueryClass, start_query),
149 		    _gtk_marshal_VOID__VOID,
150 		    GTK_TYPE_NONE, 0);
151   tips_query_signals[SIGNAL_STOP_QUERY] =
152     gtk_signal_new (I_("stop-query"),
153 		    GTK_RUN_FIRST,
154 		    GTK_CLASS_TYPE (object_class),
155 		    GTK_SIGNAL_OFFSET (GtkTipsQueryClass, stop_query),
156 		    _gtk_marshal_VOID__VOID,
157 		    GTK_TYPE_NONE, 0);
158   tips_query_signals[SIGNAL_WIDGET_ENTERED] =
159     gtk_signal_new (I_("widget-entered"),
160 		    GTK_RUN_LAST,
161 		    GTK_CLASS_TYPE (object_class),
162 		    GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_entered),
163 		    _gtk_marshal_VOID__OBJECT_STRING_STRING,
164 		    GTK_TYPE_NONE, 3,
165 		    GTK_TYPE_WIDGET,
166 		    GTK_TYPE_STRING,
167 		    GTK_TYPE_STRING);
168   tips_query_signals[SIGNAL_WIDGET_SELECTED] =
169     g_signal_new (I_("widget-selected"),
170                   G_TYPE_FROM_CLASS(object_class),
171                   G_SIGNAL_RUN_LAST,
172                   G_STRUCT_OFFSET(GtkTipsQueryClass, widget_selected),
173                   _gtk_boolean_handled_accumulator, NULL,
174                   _gtk_marshal_BOOLEAN__OBJECT_STRING_STRING_BOXED,
175                   G_TYPE_BOOLEAN, 4,
176                   GTK_TYPE_WIDGET,
177                   G_TYPE_STRING,
178                   G_TYPE_STRING,
179                   GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
180 }
181 
182 static void
gtk_tips_query_init(GtkTipsQuery * tips_query)183 gtk_tips_query_init (GtkTipsQuery *tips_query)
184 {
185   tips_query->emit_always = FALSE;
186   tips_query->in_query = FALSE;
187   tips_query->label_inactive = g_strdup ("");
188   tips_query->label_no_tip = g_strdup (_("--- No Tip ---"));
189   tips_query->caller = NULL;
190   tips_query->last_crossed = NULL;
191   tips_query->query_cursor = NULL;
192 
193   gtk_label_set_text (GTK_LABEL (tips_query), tips_query->label_inactive);
194 }
195 
196 static void
gtk_tips_query_set_arg(GtkObject * object,GtkArg * arg,guint arg_id)197 gtk_tips_query_set_arg (GtkObject              *object,
198 			GtkArg                 *arg,
199 			guint                   arg_id)
200 {
201   GtkTipsQuery *tips_query;
202 
203   tips_query = GTK_TIPS_QUERY (object);
204 
205   switch (arg_id)
206     {
207     case ARG_EMIT_ALWAYS:
208       tips_query->emit_always = (GTK_VALUE_BOOL (*arg) != FALSE);
209       break;
210     case ARG_CALLER:
211       gtk_tips_query_set_caller (tips_query, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
212       break;
213     case ARG_LABEL_INACTIVE:
214       gtk_tips_query_set_labels (tips_query, GTK_VALUE_STRING (*arg), tips_query->label_no_tip);
215       break;
216     case ARG_LABEL_NO_TIP:
217       gtk_tips_query_set_labels (tips_query, tips_query->label_inactive, GTK_VALUE_STRING (*arg));
218       break;
219     default:
220       break;
221     }
222 }
223 
224 static void
gtk_tips_query_get_arg(GtkObject * object,GtkArg * arg,guint arg_id)225 gtk_tips_query_get_arg (GtkObject             *object,
226 			GtkArg                *arg,
227 			guint                  arg_id)
228 {
229   GtkTipsQuery *tips_query;
230 
231   tips_query = GTK_TIPS_QUERY (object);
232 
233   switch (arg_id)
234     {
235     case ARG_EMIT_ALWAYS:
236       GTK_VALUE_BOOL (*arg) = tips_query->emit_always;
237       break;
238     case ARG_CALLER:
239       GTK_VALUE_OBJECT (*arg) = (GtkObject*) tips_query->caller;
240       break;
241     case ARG_LABEL_INACTIVE:
242       GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_inactive);
243       break;
244     case ARG_LABEL_NO_TIP:
245       GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_no_tip);
246       break;
247     default:
248       arg->type = GTK_TYPE_INVALID;
249       break;
250     }
251 }
252 
253 static void
gtk_tips_query_destroy(GtkObject * object)254 gtk_tips_query_destroy (GtkObject	*object)
255 {
256   GtkTipsQuery *tips_query = GTK_TIPS_QUERY (object);
257 
258   if (tips_query->in_query)
259     gtk_tips_query_stop_query (tips_query);
260 
261   gtk_tips_query_set_caller (tips_query, NULL);
262 
263   g_free (tips_query->label_inactive);
264   tips_query->label_inactive = NULL;
265   g_free (tips_query->label_no_tip);
266   tips_query->label_no_tip = NULL;
267 
268   GTK_OBJECT_CLASS (parent_class)->destroy (object);
269 }
270 
271 GtkWidget*
gtk_tips_query_new(void)272 gtk_tips_query_new (void)
273 {
274   GtkTipsQuery *tips_query;
275 
276   tips_query = gtk_type_new (gtk_tips_query_get_type ());
277 
278   return GTK_WIDGET (tips_query);
279 }
280 
281 void
gtk_tips_query_set_labels(GtkTipsQuery * tips_query,const gchar * label_inactive,const gchar * label_no_tip)282 gtk_tips_query_set_labels (GtkTipsQuery   *tips_query,
283 			   const gchar	  *label_inactive,
284 			   const gchar	  *label_no_tip)
285 {
286   gchar *old;
287 
288   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
289   g_return_if_fail (label_inactive != NULL);
290   g_return_if_fail (label_no_tip != NULL);
291 
292   old = tips_query->label_inactive;
293   tips_query->label_inactive = g_strdup (label_inactive);
294   g_free (old);
295   old = tips_query->label_no_tip;
296   tips_query->label_no_tip = g_strdup (label_no_tip);
297   g_free (old);
298 }
299 
300 void
gtk_tips_query_set_caller(GtkTipsQuery * tips_query,GtkWidget * caller)301 gtk_tips_query_set_caller (GtkTipsQuery   *tips_query,
302 			   GtkWidget	   *caller)
303 {
304   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
305   g_return_if_fail (tips_query->in_query == FALSE);
306   if (caller)
307     g_return_if_fail (GTK_IS_WIDGET (caller));
308 
309   if (caller)
310     g_object_ref (caller);
311 
312   if (tips_query->caller)
313     g_object_unref (tips_query->caller);
314 
315   tips_query->caller = caller;
316 }
317 
318 void
gtk_tips_query_start_query(GtkTipsQuery * tips_query)319 gtk_tips_query_start_query (GtkTipsQuery *tips_query)
320 {
321   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
322   g_return_if_fail (tips_query->in_query == FALSE);
323   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tips_query)));
324 
325   tips_query->in_query = TRUE;
326   gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_START_QUERY]);
327 }
328 
329 void
gtk_tips_query_stop_query(GtkTipsQuery * tips_query)330 gtk_tips_query_stop_query (GtkTipsQuery *tips_query)
331 {
332   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
333   g_return_if_fail (tips_query->in_query == TRUE);
334 
335   gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_STOP_QUERY]);
336   tips_query->in_query = FALSE;
337 }
338 
339 static void
gtk_tips_query_real_start_query(GtkTipsQuery * tips_query)340 gtk_tips_query_real_start_query (GtkTipsQuery *tips_query)
341 {
342   gint failure;
343 
344   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
345 
346   tips_query->query_cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (tips_query)),
347 							 GDK_QUESTION_ARROW);
348   failure = gdk_pointer_grab (GTK_WIDGET (tips_query)->window,
349 			      TRUE,
350 			      GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
351 			      GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
352 			      NULL,
353 			      tips_query->query_cursor,
354 			      GDK_CURRENT_TIME);
355   if (failure)
356     {
357       gdk_cursor_unref (tips_query->query_cursor);
358       tips_query->query_cursor = NULL;
359     }
360   gtk_grab_add (GTK_WIDGET (tips_query));
361 }
362 
363 static void
gtk_tips_query_real_stop_query(GtkTipsQuery * tips_query)364 gtk_tips_query_real_stop_query (GtkTipsQuery *tips_query)
365 {
366   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
367 
368   gtk_grab_remove (GTK_WIDGET (tips_query));
369   if (tips_query->query_cursor)
370     {
371       gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (tips_query)),
372 				  GDK_CURRENT_TIME);
373       gdk_cursor_unref (tips_query->query_cursor);
374       tips_query->query_cursor = NULL;
375     }
376   if (tips_query->last_crossed)
377     {
378       g_object_unref (tips_query->last_crossed);
379       tips_query->last_crossed = NULL;
380     }
381 
382   gtk_label_set_text (GTK_LABEL (tips_query), tips_query->label_inactive);
383 }
384 
385 static void
gtk_tips_query_widget_entered(GtkTipsQuery * tips_query,GtkWidget * widget,const gchar * tip_text,const gchar * tip_private)386 gtk_tips_query_widget_entered (GtkTipsQuery   *tips_query,
387 			       GtkWidget      *widget,
388 			       const gchar    *tip_text,
389 			       const gchar    *tip_private)
390 {
391   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
392 
393   if (!tip_text)
394     tip_text = tips_query->label_no_tip;
395 
396   if (!g_str_equal (GTK_LABEL (tips_query)->label, (gchar*) tip_text))
397     gtk_label_set_text (GTK_LABEL (tips_query), tip_text);
398 }
399 
400 static void
gtk_tips_query_emit_widget_entered(GtkTipsQuery * tips_query,GtkWidget * widget)401 gtk_tips_query_emit_widget_entered (GtkTipsQuery *tips_query,
402 				    GtkWidget	 *widget)
403 {
404   GtkTooltipsData *tdata;
405 
406   if (widget == (GtkWidget*) tips_query)
407     widget = NULL;
408 
409   if (widget)
410     tdata = gtk_tooltips_data_get (widget);
411   else
412     tdata = NULL;
413 
414   if (!widget && tips_query->last_crossed)
415     {
416       gtk_signal_emit (GTK_OBJECT (tips_query),
417 		       tips_query_signals[SIGNAL_WIDGET_ENTERED],
418 		       NULL,
419 		       NULL,
420 		       NULL);
421       g_object_unref (tips_query->last_crossed);
422       tips_query->last_crossed = NULL;
423     }
424   else if (widget && widget != tips_query->last_crossed)
425     {
426       g_object_ref (widget);
427       if (tdata || tips_query->emit_always)
428 	  gtk_signal_emit (GTK_OBJECT (tips_query),
429 			   tips_query_signals[SIGNAL_WIDGET_ENTERED],
430 			   widget,
431 			   tdata ? tdata->tip_text : NULL,
432 			   tdata ? tdata->tip_private : NULL);
433       if (tips_query->last_crossed)
434 	g_object_unref (tips_query->last_crossed);
435       tips_query->last_crossed = widget;
436     }
437 }
438 
439 static gint
gtk_tips_query_event(GtkWidget * widget,GdkEvent * event)440 gtk_tips_query_event (GtkWidget	       *widget,
441 		      GdkEvent	       *event)
442 {
443   GtkTipsQuery *tips_query;
444   GtkWidget *event_widget;
445   gboolean event_handled;
446 
447   g_return_val_if_fail (GTK_IS_TIPS_QUERY (widget), FALSE);
448 
449   tips_query = GTK_TIPS_QUERY (widget);
450   if (!tips_query->in_query)
451     {
452       if (GTK_WIDGET_CLASS (parent_class)->event)
453 	return GTK_WIDGET_CLASS (parent_class)->event (widget, event);
454       else
455 	return FALSE;
456     }
457 
458   event_widget = gtk_get_event_widget (event);
459 
460   event_handled = FALSE;
461   switch (event->type)
462     {
463       GdkWindow *pointer_window;
464 
465     case  GDK_LEAVE_NOTIFY:
466       if (event_widget)
467 	pointer_window = gdk_window_get_pointer (event_widget->window, NULL, NULL, NULL);
468       else
469 	pointer_window = NULL;
470       event_widget = NULL;
471       if (pointer_window)
472 	{
473 	  gpointer event_widget_ptr;
474 	  gdk_window_get_user_data (pointer_window, &event_widget_ptr);
475 	  event_widget = event_widget_ptr;
476 	}
477       gtk_tips_query_emit_widget_entered (tips_query, event_widget);
478       event_handled = TRUE;
479       break;
480 
481     case  GDK_ENTER_NOTIFY:
482       gtk_tips_query_emit_widget_entered (tips_query, event_widget);
483       event_handled = TRUE;
484       break;
485 
486     case  GDK_BUTTON_PRESS:
487     case  GDK_BUTTON_RELEASE:
488       if (event_widget)
489 	{
490 	  if (event_widget == (GtkWidget*) tips_query ||
491 	      event_widget == tips_query->caller)
492 	    gtk_tips_query_stop_query (tips_query);
493 	  else
494 	    {
495 	      gint stop;
496 	      GtkTooltipsData *tdata;
497 
498 	      stop = TRUE;
499 	      tdata = gtk_tooltips_data_get (event_widget);
500 	      if (tdata || tips_query->emit_always)
501 		gtk_signal_emit (GTK_OBJECT (tips_query),
502 				 tips_query_signals[SIGNAL_WIDGET_SELECTED],
503 				 event_widget,
504 				 tdata ? tdata->tip_text : NULL,
505 				 tdata ? tdata->tip_private : NULL,
506 				 event,
507 				 &stop);
508 
509 	      if (stop)
510 		gtk_tips_query_stop_query (tips_query);
511 	    }
512 	}
513       event_handled = TRUE;
514       break;
515 
516     default:
517       break;
518     }
519 
520   return event_handled;
521 }
522 
523 #define __GTK_TIPS_QUERY_C__
524 #include "gtkaliasdef.c"
525