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