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