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