]> Pileus Git - ~andy/gtk/blob - gtk/gtktipsquery.c
Don't define HAVE_DIMM_H if MSC, as you have to get the Platform SDK to
[~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
23 /*
24  * Modified by the GTK+ Team and others 1997-1999.  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   gtk_object_add_arg_type ("GtkTipsQuery::emit_always", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EMIT_ALWAYS);
121   gtk_object_add_arg_type ("GtkTipsQuery::caller", GTK_TYPE_WIDGET, GTK_ARG_READWRITE, ARG_CALLER);
122   gtk_object_add_arg_type ("GtkTipsQuery::label_inactive", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL_INACTIVE);
123   gtk_object_add_arg_type ("GtkTipsQuery::label_no_tip", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL_NO_TIP);
124
125   tips_query_signals[SIGNAL_START_QUERY] =
126     gtk_signal_new ("start_query",
127                     GTK_RUN_FIRST,
128                     object_class->type,
129                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, start_query),
130                     gtk_marshal_NONE__NONE,
131                     GTK_TYPE_NONE, 0);
132   tips_query_signals[SIGNAL_STOP_QUERY] =
133     gtk_signal_new ("stop_query",
134                     GTK_RUN_FIRST,
135                     object_class->type,
136                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, stop_query),
137                     gtk_marshal_NONE__NONE,
138                     GTK_TYPE_NONE, 0);
139   tips_query_signals[SIGNAL_WIDGET_ENTERED] =
140     gtk_signal_new ("widget_entered",
141                     GTK_RUN_LAST,
142                     object_class->type,
143                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_entered),
144                     gtk_marshal_NONE__POINTER_STRING_STRING,
145                     GTK_TYPE_NONE, 3,
146                     GTK_TYPE_WIDGET,
147                     GTK_TYPE_STRING,
148                     GTK_TYPE_STRING);
149   tips_query_signals[SIGNAL_WIDGET_SELECTED] =
150     gtk_signal_new ("widget_selected",
151                     GTK_RUN_LAST,
152                     object_class->type,
153                     GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_selected),
154                     gtk_marshal_BOOL__POINTER_STRING_STRING_POINTER,
155                     GTK_TYPE_BOOL, 4,
156                     GTK_TYPE_WIDGET,
157                     GTK_TYPE_STRING,
158                     GTK_TYPE_STRING,
159                     GTK_TYPE_GDK_EVENT);
160   gtk_object_class_add_signals (object_class, tips_query_signals, SIGNAL_LAST);
161
162   object_class->set_arg = gtk_tips_query_set_arg;
163   object_class->get_arg = gtk_tips_query_get_arg;
164   object_class->destroy = gtk_tips_query_destroy;
165
166   widget_class->event = gtk_tips_query_event;
167
168   class->start_query = gtk_tips_query_real_start_query;
169   class->stop_query = gtk_tips_query_real_stop_query;
170   class->widget_entered = gtk_tips_query_widget_entered;
171   class->widget_selected = NULL;
172 }
173
174 static void
175 gtk_tips_query_init (GtkTipsQuery *tips_query)
176 {
177   tips_query->emit_always = FALSE;
178   tips_query->in_query = FALSE;
179   tips_query->label_inactive = g_strdup ("");
180   tips_query->label_no_tip = g_strdup (_("--- No Tip ---"));
181   tips_query->caller = NULL;
182   tips_query->last_crossed = NULL;
183   tips_query->query_cursor = NULL;
184
185   gtk_label_set_text (GTK_LABEL (tips_query), tips_query->label_inactive);
186 }
187
188 static void
189 gtk_tips_query_set_arg (GtkObject              *object,
190                         GtkArg                 *arg,
191                         guint                   arg_id)
192 {
193   GtkTipsQuery *tips_query;
194
195   tips_query = GTK_TIPS_QUERY (object);
196
197   switch (arg_id)
198     {
199     case ARG_EMIT_ALWAYS:
200       tips_query->emit_always = (GTK_VALUE_BOOL (*arg) != FALSE);
201       break;
202     case ARG_CALLER:
203       gtk_tips_query_set_caller (tips_query, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
204       break;
205     case ARG_LABEL_INACTIVE:
206       gtk_tips_query_set_labels (tips_query, GTK_VALUE_STRING (*arg), tips_query->label_no_tip);
207       break;
208     case ARG_LABEL_NO_TIP:
209       gtk_tips_query_set_labels (tips_query, tips_query->label_inactive, GTK_VALUE_STRING (*arg));
210       break;
211     default:
212       break;
213     }
214 }
215
216 static void
217 gtk_tips_query_get_arg (GtkObject             *object,
218                         GtkArg                *arg,
219                         guint                  arg_id)
220 {
221   GtkTipsQuery *tips_query;
222
223   tips_query = GTK_TIPS_QUERY (object);
224
225   switch (arg_id)
226     {
227     case ARG_EMIT_ALWAYS:
228       GTK_VALUE_BOOL (*arg) = tips_query->emit_always;
229       break;
230     case ARG_CALLER:
231       GTK_VALUE_OBJECT (*arg) = (GtkObject*) tips_query->caller;
232       break;
233     case ARG_LABEL_INACTIVE:
234       GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_inactive);
235       break;
236     case ARG_LABEL_NO_TIP:
237       GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_no_tip);
238       break;
239     default:
240       arg->type = GTK_TYPE_INVALID;
241       break;
242     }
243 }
244
245 static void
246 gtk_tips_query_destroy (GtkObject       *object)
247 {
248   GtkTipsQuery *tips_query;
249
250   g_return_if_fail (object != NULL);
251   g_return_if_fail (GTK_IS_TIPS_QUERY (object));
252
253   tips_query = GTK_TIPS_QUERY (object);
254
255   if (tips_query->in_query)
256     gtk_tips_query_stop_query (tips_query);
257
258   gtk_tips_query_set_caller (tips_query, NULL);
259
260   if (GTK_OBJECT_CLASS (parent_class)->destroy)
261     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
262 }
263
264 GtkWidget*
265 gtk_tips_query_new (void)
266 {
267   GtkTipsQuery *tips_query;
268
269   tips_query = gtk_type_new (gtk_tips_query_get_type ());
270
271   return GTK_WIDGET (tips_query);
272 }
273
274 void
275 gtk_tips_query_set_labels (GtkTipsQuery   *tips_query,
276                            const gchar    *label_inactive,
277                            const gchar    *label_no_tip)
278 {
279   gchar *old;
280
281   g_return_if_fail (tips_query != NULL);
282   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
283   g_return_if_fail (label_inactive != NULL);
284   g_return_if_fail (label_no_tip != NULL);
285
286   old = tips_query->label_inactive;
287   tips_query->label_inactive = g_strdup (label_inactive);
288   g_free (old);
289   old = tips_query->label_no_tip;
290   tips_query->label_no_tip = g_strdup (label_no_tip);
291   g_free (old);
292 }
293
294 void
295 gtk_tips_query_set_caller (GtkTipsQuery   *tips_query,
296                            GtkWidget       *caller)
297 {
298   g_return_if_fail (tips_query != NULL);
299   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
300   g_return_if_fail (tips_query->in_query == FALSE);
301   if (caller)
302     g_return_if_fail (GTK_IS_WIDGET (caller));
303
304   if (caller)
305     gtk_widget_ref (caller);
306
307   if (tips_query->caller)
308     gtk_widget_unref (tips_query->caller);
309
310   tips_query->caller = caller;
311 }
312
313 void
314 gtk_tips_query_start_query (GtkTipsQuery *tips_query)
315 {
316   g_return_if_fail (tips_query != NULL);
317   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
318   g_return_if_fail (tips_query->in_query == FALSE);
319   g_return_if_fail (GTK_WIDGET_REALIZED (tips_query));
320
321   tips_query->in_query = TRUE;
322   gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_START_QUERY]);
323 }
324
325 void
326 gtk_tips_query_stop_query (GtkTipsQuery *tips_query)
327 {
328   g_return_if_fail (tips_query != NULL);
329   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
330   g_return_if_fail (tips_query->in_query == TRUE);
331
332   gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_STOP_QUERY]);
333   tips_query->in_query = FALSE;
334 }
335
336 static void
337 gtk_tips_query_real_start_query (GtkTipsQuery *tips_query)
338 {
339   gint failure;
340   
341   g_return_if_fail (tips_query != NULL);
342   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
343   
344   tips_query->query_cursor = gdk_cursor_new (GDK_QUESTION_ARROW);
345   failure = gdk_pointer_grab (GTK_WIDGET (tips_query)->window,
346                               TRUE,
347                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
348                               GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
349                               NULL,
350                               tips_query->query_cursor,
351                               GDK_CURRENT_TIME);
352   if (failure)
353     {
354       gdk_cursor_destroy (tips_query->query_cursor);
355       tips_query->query_cursor = NULL;
356     }
357   gtk_grab_add (GTK_WIDGET (tips_query));
358 }
359
360 static void
361 gtk_tips_query_real_stop_query (GtkTipsQuery *tips_query)
362 {
363   g_return_if_fail (tips_query != NULL);
364   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
365   
366   gtk_grab_remove (GTK_WIDGET (tips_query));
367   if (tips_query->query_cursor)
368     {
369       gdk_pointer_ungrab (GDK_CURRENT_TIME);
370       gdk_cursor_destroy (tips_query->query_cursor);
371       tips_query->query_cursor = NULL;
372     }
373   if (tips_query->last_crossed)
374     {
375       gtk_widget_unref (tips_query->last_crossed);
376       tips_query->last_crossed = NULL;
377     }
378   
379   gtk_label_set_text (GTK_LABEL (tips_query), tips_query->label_inactive);
380 }
381
382 static void
383 gtk_tips_query_widget_entered (GtkTipsQuery   *tips_query,
384                                GtkWidget      *widget,
385                                const gchar    *tip_text,
386                                const gchar    *tip_private)
387 {
388   g_return_if_fail (tips_query != NULL);
389   g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
390
391   if (!tip_text)
392     tip_text = tips_query->label_no_tip;
393
394   if (!g_str_equal (GTK_LABEL (tips_query)->label, (gchar*) tip_text))
395     gtk_label_set_text (GTK_LABEL (tips_query), tip_text);
396 }
397
398 static void
399 gtk_tips_query_emit_widget_entered (GtkTipsQuery *tips_query,
400                                     GtkWidget    *widget)
401 {
402   GtkTooltipsData *tdata;
403
404   if (widget == (GtkWidget*) tips_query)
405     widget = NULL;
406
407   if (widget)
408     tdata = gtk_tooltips_data_get (widget);
409   else
410     tdata = NULL;
411
412   if (!widget && tips_query->last_crossed)
413     {
414       gtk_signal_emit (GTK_OBJECT (tips_query),
415                        tips_query_signals[SIGNAL_WIDGET_ENTERED],
416                        NULL,
417                        NULL,
418                        NULL);
419       gtk_widget_unref (tips_query->last_crossed);
420       tips_query->last_crossed = NULL;
421     }
422   else if (widget && widget != tips_query->last_crossed)
423     {
424       gtk_widget_ref (widget);
425       if (tdata || tips_query->emit_always)
426           gtk_signal_emit (GTK_OBJECT (tips_query),
427                            tips_query_signals[SIGNAL_WIDGET_ENTERED],
428                            widget,
429                            tdata ? tdata->tip_text : NULL,
430                            tdata ? tdata->tip_private : NULL);
431       if (tips_query->last_crossed)
432         gtk_widget_unref (tips_query->last_crossed);
433       tips_query->last_crossed = widget;
434     }
435 }
436
437 static gint
438 gtk_tips_query_event (GtkWidget        *widget,
439                       GdkEvent         *event)
440 {
441   GtkTipsQuery *tips_query;
442   GtkWidget *event_widget;
443   gboolean event_handled;
444   
445   g_return_val_if_fail (widget != NULL, FALSE);
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 }