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