]> Pileus Git - ~andy/gtk/blob - gtk/gtktipsquery.c
Changed LGPL address for FSF in all .h and .c files
[~andy/gtk] / gtk / gtktipsquery.c
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 Library 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  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library 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 #include        "gtktipsquery.h"
23 #include        "gtksignal.h"
24 #include        "gtktooltips.h"
25 #include        "gtkmain.h"
26
27
28
29 /* --- arguments --- */
30 enum {
31   ARG_0,
32   ARG_EMIT_ALWAYS,
33   ARG_CALLER,
34   ARG_LABEL_INACTIVE,
35   ARG_LABEL_NO_TIP
36 };
37
38
39 /* --- signals --- */
40 enum
41 {
42   SIGNAL_START_QUERY,
43   SIGNAL_STOP_QUERY,
44   SIGNAL_WIDGET_ENTERED,
45   SIGNAL_WIDGET_SELECTED,
46   SIGNAL_LAST
47 };
48 typedef void    (*SignalWidgetEntered)          (GtkObject      *object,
49                                                  GtkWidget      *widget,
50                                                  const gchar    *tip_text,
51                                                  const gchar    *tip_private,
52                                                  gpointer        func_data);
53 typedef gint    (*SignalWidgetSelected)         (GtkObject      *object,
54                                                  GtkWidget      *widget,
55                                                  const gchar    *tip_text,
56                                                  const gchar    *tip_private,
57                                                  GdkEventButton *event,
58                                                  gpointer        func_data);
59
60
61 /* --- prototypes --- */
62 static void     gtk_tips_query_class_init       (GtkTipsQueryClass      *class);
63 static void     gtk_tips_query_init             (GtkTipsQuery           *tips_query);
64 static void     gtk_tips_query_destroy          (GtkObject              *object);
65 static gint     gtk_tips_query_event            (GtkWidget              *widget,
66                                                  GdkEvent               *event);
67 static void     gtk_tips_query_set_arg          (GtkTipsQuery           *tips_query,
68                                                  GtkArg                 *arg,
69                                                  guint                   arg_id);
70 static void     gtk_tips_query_get_arg          (GtkTipsQuery           *tips_query,
71                                                  GtkArg                 *arg,
72                                                  guint                  arg_id);
73 static void     gtk_tips_query_real_start_query (GtkTipsQuery           *tips_query);
74 static void     gtk_tips_query_real_stop_query  (GtkTipsQuery           *tips_query);
75 static void     gtk_tips_query_widget_entered   (GtkTipsQuery           *tips_query,
76                                                  GtkWidget              *widget,
77                                                  const gchar            *tip_text,
78                                                  const gchar            *tip_private);
79
80
81 /* --- variables --- */
82 static GtkLabelClass    *parent_class = NULL;
83 static guint             tips_query_signals[SIGNAL_LAST] = { 0 };
84
85
86 /* --- functions --- */
87 guint
88 gtk_tips_query_get_type (void)
89 {
90   static guint tips_query_type = 0;
91
92   if (!tips_query_type)
93     {
94       GtkTypeInfo tips_query_info =
95       {
96         "GtkTipsQuery",
97         sizeof (GtkTipsQuery),
98         sizeof (GtkTipsQueryClass),
99         (GtkClassInitFunc) gtk_tips_query_class_init,
100         (GtkObjectInitFunc) gtk_tips_query_init,
101         (GtkArgSetFunc) gtk_tips_query_set_arg,
102         (GtkArgGetFunc) gtk_tips_query_get_arg,
103       };
104
105       tips_query_type = gtk_type_unique (gtk_label_get_type (), &tips_query_info);
106     }
107
108   return tips_query_type;
109 }
110
111 static void
112 gtk_tips_query_marshal_widget_entered (GtkObject      *object,
113                                        GtkSignalFunc  func,
114                                        gpointer       func_data,
115                                        GtkArg         *args)
116 {
117   SignalWidgetEntered sfunc = (SignalWidgetEntered) func;
118
119   (* sfunc) (object,
120              (GtkWidget*) GTK_VALUE_OBJECT (args[0]),
121              GTK_VALUE_STRING (args[1]),
122              GTK_VALUE_STRING (args[2]),
123              func_data);
124 }
125
126 static void
127 gtk_tips_query_marshal_widget_selected (GtkObject      *object,
128                                         GtkSignalFunc  func,
129                                         gpointer       func_data,
130                                         GtkArg         *args)
131 {
132   gint *return_val;
133
134   SignalWidgetSelected sfunc = (SignalWidgetSelected) func;
135   return_val = GTK_RETLOC_BOOL (args[4]);
136   
137   *return_val = (* sfunc) (object,
138                            (GtkWidget*) GTK_VALUE_OBJECT (args[0]),
139                            GTK_VALUE_STRING (args[1]),
140                            GTK_VALUE_STRING (args[2]),
141                            GTK_VALUE_BOXED (args[3]),
142                            func_data);
143 }
144
145 static void
146 gtk_tips_query_class_init (GtkTipsQueryClass *class)
147 {
148   GtkObjectClass *object_class;
149   GtkWidgetClass *widget_class;
150
151   object_class = (GtkObjectClass*) class;
152   widget_class = (GtkWidgetClass*) class;
153
154   parent_class = gtk_type_class (gtk_label_get_type ());
155
156   gtk_object_add_arg_type ("GtkTipsQuery::emit_always", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EMIT_ALWAYS);
157   gtk_object_add_arg_type ("GtkTipsQuery::caller", GTK_TYPE_WIDGET, GTK_ARG_READWRITE, ARG_CALLER);
158   gtk_object_add_arg_type ("GtkTipsQuery::label_inactive", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL_INACTIVE);
159   gtk_object_add_arg_type ("GtkTipsQuery::label_no_tip", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL_NO_TIP);
160
161   tips_query_signals[SIGNAL_START_QUERY] =
162     gtk_signal_new ("start_query",
163                     GTK_RUN_FIRST,
164                     object_class->type,
165                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, start_query),
166                     gtk_signal_default_marshaller,
167                     GTK_TYPE_NONE, 0);
168   tips_query_signals[SIGNAL_STOP_QUERY] =
169     gtk_signal_new ("stop_query",
170                     GTK_RUN_FIRST,
171                     object_class->type,
172                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, stop_query),
173                     gtk_signal_default_marshaller,
174                     GTK_TYPE_NONE, 0);
175   tips_query_signals[SIGNAL_WIDGET_ENTERED] =
176     gtk_signal_new ("widget_entered",
177                     GTK_RUN_LAST,
178                     object_class->type,
179                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_entered),
180                     gtk_tips_query_marshal_widget_entered,
181                     GTK_TYPE_NONE, 3,
182                     GTK_TYPE_WIDGET,
183                     GTK_TYPE_STRING,
184                     GTK_TYPE_STRING);
185   tips_query_signals[SIGNAL_WIDGET_SELECTED] =
186     gtk_signal_new ("widget_selected",
187                     GTK_RUN_FIRST,
188                     object_class->type,
189                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_selected),
190                     gtk_tips_query_marshal_widget_selected,
191                     GTK_TYPE_INT, 4,
192                     GTK_TYPE_WIDGET,
193                     GTK_TYPE_STRING,
194                     GTK_TYPE_STRING,
195                     GTK_TYPE_BOXED);
196   gtk_object_class_add_signals (object_class, tips_query_signals, SIGNAL_LAST);
197
198   object_class->destroy = gtk_tips_query_destroy;
199   widget_class->event = gtk_tips_query_event;
200
201   class->start_query = gtk_tips_query_real_start_query;
202   class->stop_query = gtk_tips_query_real_stop_query;
203   class->widget_entered = gtk_tips_query_widget_entered;
204   class->widget_selected = NULL;
205 }
206
207 static void
208 gtk_tips_query_init (GtkTipsQuery *tips_query)
209 {
210   tips_query->emit_always = FALSE;
211   tips_query->in_query = FALSE;
212   tips_query->label_inactive = g_strdup ("");
213   tips_query->label_no_tip = g_strdup ("--- No Tip ---");
214   tips_query->caller = NULL;
215   tips_query->last_crossed = NULL;
216   tips_query->query_cursor = NULL;
217
218   gtk_label_set (GTK_LABEL (tips_query), tips_query->label_inactive);
219 }
220
221 static void
222 gtk_tips_query_set_arg (GtkTipsQuery           *tips_query,
223                         GtkArg                 *arg,
224                         guint                   arg_id)
225 {
226   switch (arg_id)
227     {
228     case ARG_EMIT_ALWAYS:
229       tips_query->emit_always = (GTK_VALUE_BOOL (*arg) != FALSE);
230       break;
231     case ARG_CALLER:
232       gtk_tips_query_set_caller (tips_query, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
233       break;
234     case ARG_LABEL_INACTIVE:
235       gtk_tips_query_set_labels (tips_query, GTK_VALUE_STRING (*arg), tips_query->label_no_tip);
236       break;
237     case ARG_LABEL_NO_TIP:
238       gtk_tips_query_set_labels (tips_query, tips_query->label_inactive, GTK_VALUE_STRING (*arg));
239       break;
240     default:
241       arg->type = GTK_TYPE_INVALID;
242       break;
243     }
244 }
245
246 static void
247 gtk_tips_query_get_arg (GtkTipsQuery           *tips_query,
248                         GtkArg                 *arg,
249                         guint                  arg_id)
250 {
251   switch (arg_id)
252     {
253     case ARG_EMIT_ALWAYS:
254       GTK_VALUE_BOOL (*arg) = tips_query->emit_always;
255       break;
256     case ARG_CALLER:
257       GTK_VALUE_OBJECT (*arg) = (GtkObject*) tips_query->caller;
258       break;
259     case ARG_LABEL_INACTIVE:
260       GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_inactive);
261       break;
262     case ARG_LABEL_NO_TIP:
263       GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_no_tip);
264       break;
265     default:
266       arg->type = GTK_TYPE_INVALID;
267       break;
268     }
269 }
270
271 static void
272 gtk_tips_query_destroy (GtkObject       *object)
273 {
274   GtkTipsQuery *tips_query;
275
276   g_return_if_fail (object != NULL);
277   g_return_if_fail (GTK_IS_TIPS_QUERY (object));
278
279   tips_query = GTK_TIPS_QUERY (object);
280
281   if (tips_query->in_query)
282     gtk_tips_query_stop_query (tips_query);
283
284   gtk_tips_query_set_caller (tips_query, NULL);
285
286   if (GTK_OBJECT_CLASS (parent_class)->destroy)
287     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
288 }
289
290 GtkWidget*
291 gtk_tips_query_new (void)
292 {
293   GtkTipsQuery *tips_query;
294
295   tips_query = gtk_type_new (gtk_tips_query_get_type ());
296
297   return GTK_WIDGET (tips_query);
298 }
299
300 void
301 gtk_tips_query_set_labels (GtkTipsQuery   *tips_query,
302                            const gchar    *label_inactive,
303                            const gchar    *label_no_tip)
304 {
305   gchar *old;
306
307   g_return_if_fail (tips_query != NULL);
308   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
309   g_return_if_fail (label_inactive != NULL);
310   g_return_if_fail (label_no_tip != NULL);
311
312   old = tips_query->label_inactive;
313   tips_query->label_inactive = g_strdup (label_inactive);
314   g_free (old);
315   old = tips_query->label_no_tip;
316   tips_query->label_no_tip = g_strdup (label_no_tip);
317   g_free (old);
318 }
319
320 void
321 gtk_tips_query_set_caller (GtkTipsQuery   *tips_query,
322                            GtkWidget       *caller)
323 {
324   g_return_if_fail (tips_query != NULL);
325   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
326   g_return_if_fail (tips_query->in_query == FALSE);
327   if (caller)
328     g_return_if_fail (GTK_IS_WIDGET (caller));
329
330   if (caller)
331     gtk_widget_ref (caller);
332
333   if (tips_query->caller)
334     gtk_widget_unref (tips_query->caller);
335
336   tips_query->caller = caller;
337 }
338
339 void
340 gtk_tips_query_start_query (GtkTipsQuery *tips_query)
341 {
342   g_return_if_fail (tips_query != NULL);
343   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
344   g_return_if_fail (tips_query->in_query == FALSE);
345   g_return_if_fail (GTK_WIDGET_REALIZED (tips_query));
346
347   tips_query->in_query = TRUE;
348   gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_START_QUERY]);
349 }
350
351 void
352 gtk_tips_query_stop_query (GtkTipsQuery *tips_query)
353 {
354   g_return_if_fail (tips_query != NULL);
355   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
356   g_return_if_fail (tips_query->in_query == TRUE);
357
358   gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_STOP_QUERY]);
359   tips_query->in_query = FALSE;
360 }
361
362 static void
363 gtk_tips_query_real_start_query (GtkTipsQuery *tips_query)
364 {
365   gint failure;
366   
367   g_return_if_fail (tips_query != NULL);
368   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
369   
370   tips_query->query_cursor = gdk_cursor_new (GDK_QUESTION_ARROW);
371   failure = gdk_pointer_grab (GTK_WIDGET (tips_query)->window,
372                               TRUE,
373                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
374                               GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
375                               NULL,
376                               tips_query->query_cursor,
377                               GDK_CURRENT_TIME);
378   if (failure)
379     {
380       gdk_cursor_destroy (tips_query->query_cursor);
381       tips_query->query_cursor = NULL;
382     }
383   gtk_grab_add (GTK_WIDGET (tips_query));
384 }
385
386 static void
387 gtk_tips_query_real_stop_query (GtkTipsQuery *tips_query)
388 {
389   g_return_if_fail (tips_query != NULL);
390   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
391   
392   gtk_grab_remove (GTK_WIDGET (tips_query));
393   if (tips_query->query_cursor)
394     {
395       gdk_pointer_ungrab (GDK_CURRENT_TIME);
396       gdk_cursor_destroy (tips_query->query_cursor);
397       tips_query->query_cursor = NULL;
398     }
399   if (tips_query->last_crossed)
400     {
401       gtk_widget_unref (tips_query->last_crossed);
402       tips_query->last_crossed = NULL;
403     }
404   
405   gtk_label_set (GTK_LABEL (tips_query), tips_query->label_inactive);
406 }
407
408 static void
409 gtk_tips_query_widget_entered (GtkTipsQuery   *tips_query,
410                                GtkWidget      *widget,
411                                const gchar    *tip_text,
412                                const gchar    *tip_private)
413 {
414   g_return_if_fail (tips_query != NULL);
415   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
416
417   if (!tip_text)
418     tip_text = tips_query->label_no_tip;
419
420   if (!g_str_equal (GTK_LABEL (tips_query)->label, (gchar*) tip_text))
421     gtk_label_set (GTK_LABEL (tips_query), tip_text);
422 }
423
424 static void
425 gtk_tips_query_emit_widget_entered (GtkTipsQuery *tips_query,
426                                     GtkWidget    *widget)
427 {
428   GtkTooltipsData *tdata;
429
430   if (widget == (GtkWidget*) tips_query)
431     widget = NULL;
432
433   if (widget)
434     tdata = gtk_tooltips_data_get (widget);
435   else
436     tdata = NULL;
437
438   if (!widget && tips_query->last_crossed)
439     {
440       gtk_signal_emit (GTK_OBJECT (tips_query),
441                        tips_query_signals[SIGNAL_WIDGET_ENTERED],
442                        NULL,
443                        NULL,
444                        NULL);
445       gtk_widget_unref (tips_query->last_crossed);
446       tips_query->last_crossed = NULL;
447     }
448   else if (widget && widget != tips_query->last_crossed)
449     {
450       gtk_widget_ref (widget);
451       if (tdata || tips_query->emit_always)
452           gtk_signal_emit (GTK_OBJECT (tips_query),
453                            tips_query_signals[SIGNAL_WIDGET_ENTERED],
454                            widget,
455                            tdata ? tdata->tip_text : NULL,
456                            tdata ? tdata->tip_private : NULL);
457       if (tips_query->last_crossed)
458         gtk_widget_unref (tips_query->last_crossed);
459       tips_query->last_crossed = widget;
460     }
461 }
462
463 static gint
464 gtk_tips_query_event (GtkWidget        *widget,
465                       GdkEvent         *event)
466 {
467   GtkTipsQuery *tips_query;
468   GtkWidget *event_widget;
469   gboolean event_handled;
470   
471   g_return_val_if_fail (widget != NULL, FALSE);
472   g_return_val_if_fail (GTK_IS_TIPS_QUERY (widget), FALSE);
473
474   tips_query = GTK_TIPS_QUERY (widget);
475   if (!tips_query->in_query)
476     {
477       if (GTK_WIDGET_CLASS (parent_class)->event)
478         return GTK_WIDGET_CLASS (parent_class)->event (widget, event);
479       else
480         return FALSE;
481     }
482
483   event_widget = gtk_get_event_widget (event);
484
485   event_handled = FALSE;
486   switch (event->type)
487     {
488       GdkWindow *pointer_window;
489       
490     case  GDK_LEAVE_NOTIFY:
491       if (event_widget)
492         pointer_window = gdk_window_get_pointer (event_widget->window, NULL, NULL, NULL);
493       else
494         pointer_window = NULL;
495       event_widget = NULL;
496       if (pointer_window)
497         gdk_window_get_user_data (pointer_window, (gpointer*) &event_widget);
498       gtk_tips_query_emit_widget_entered (tips_query, event_widget);
499       event_handled = TRUE;
500       break;
501
502     case  GDK_ENTER_NOTIFY:
503       gtk_tips_query_emit_widget_entered (tips_query, event_widget);
504       event_handled = TRUE;
505       break;
506
507     case  GDK_BUTTON_PRESS:
508     case  GDK_BUTTON_RELEASE:
509       if (event_widget)
510         {
511           if (event_widget == (GtkWidget*) tips_query ||
512               event_widget == tips_query->caller)
513             gtk_tips_query_stop_query (tips_query);
514           else
515             {
516               gint stop;
517               GtkTooltipsData *tdata;
518               
519               stop = TRUE;
520               tdata = gtk_tooltips_data_get (event_widget);
521               if (tdata || tips_query->emit_always)
522                 gtk_signal_emit (GTK_OBJECT (tips_query),
523                                  tips_query_signals[SIGNAL_WIDGET_SELECTED],
524                                  event_widget,
525                                  tdata ? tdata->tip_text : NULL,
526                                  tdata ? tdata->tip_private : NULL,
527                                  event,
528                                  &stop);
529               
530               if (stop)
531                 gtk_tips_query_stop_query (tips_query);
532             }
533         }
534       event_handled = TRUE;
535       break;
536
537     default:
538       break;
539     }
540
541   return event_handled;
542 }