]> Pileus Git - ~andy/gtk/blob - gtk/gtktipsquery.c
6a19278166acd30f2b028c258918e1e4c0b0d233
[~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_LAST,
188                     object_class->type,
189                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_selected),
190                     gtk_tips_query_marshal_widget_selected,
191                     GTK_TYPE_BOOL, 4,
192                     GTK_TYPE_WIDGET,
193                     GTK_TYPE_STRING,
194                     GTK_TYPE_STRING,
195                     GTK_TYPE_GDK_EVENT);
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       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 }