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