1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * GtkQueryTips: Query onscreen widgets for their tooltips
5 * Copyright (C) 1998 Tim Janik
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.
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.
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.
22 #include "gtktipsquery.h"
23 #include "gtksignal.h"
24 #include "gtktooltips.h"
29 /* --- arguments --- */
44 SIGNAL_WIDGET_ENTERED,
45 SIGNAL_WIDGET_SELECTED,
48 typedef void (*SignalWidgetEntered) (GtkObject *object,
50 const gchar *tip_text,
51 const gchar *tip_private,
53 typedef gint (*SignalWidgetSelected) (GtkObject *object,
55 const gchar *tip_text,
56 const gchar *tip_private,
57 GdkEventButton *event,
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,
67 static void gtk_tips_query_set_arg (GtkTipsQuery *tips_query,
70 static void gtk_tips_query_get_arg (GtkTipsQuery *tips_query,
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,
77 const gchar *tip_text,
78 const gchar *tip_private);
81 /* --- variables --- */
82 static GtkLabelClass *parent_class = NULL;
83 static guint tips_query_signals[SIGNAL_LAST] = { 0 };
86 /* --- functions --- */
88 gtk_tips_query_get_type (void)
90 static guint tips_query_type = 0;
94 GtkTypeInfo tips_query_info =
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,
105 tips_query_type = gtk_type_unique (gtk_label_get_type (), &tips_query_info);
108 return tips_query_type;
112 gtk_tips_query_marshal_widget_entered (GtkObject *object,
117 SignalWidgetEntered sfunc = (SignalWidgetEntered) func;
120 (GtkWidget*) GTK_VALUE_OBJECT (args[0]),
121 GTK_VALUE_STRING (args[1]),
122 GTK_VALUE_STRING (args[2]),
127 gtk_tips_query_marshal_widget_selected (GtkObject *object,
134 SignalWidgetSelected sfunc = (SignalWidgetSelected) func;
135 return_val = GTK_RETLOC_BOOL (args[4]);
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]),
146 gtk_tips_query_class_init (GtkTipsQueryClass *class)
148 GtkObjectClass *object_class;
149 GtkWidgetClass *widget_class;
151 object_class = (GtkObjectClass*) class;
152 widget_class = (GtkWidgetClass*) class;
154 parent_class = gtk_type_class (gtk_label_get_type ());
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);
161 tips_query_signals[SIGNAL_START_QUERY] =
162 gtk_signal_new ("start_query",
165 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, start_query),
166 gtk_signal_default_marshaller,
168 tips_query_signals[SIGNAL_STOP_QUERY] =
169 gtk_signal_new ("stop_query",
172 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, stop_query),
173 gtk_signal_default_marshaller,
175 tips_query_signals[SIGNAL_WIDGET_ENTERED] =
176 gtk_signal_new ("widget_entered",
179 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_entered),
180 gtk_tips_query_marshal_widget_entered,
185 tips_query_signals[SIGNAL_WIDGET_SELECTED] =
186 gtk_signal_new ("widget_selected",
189 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_selected),
190 gtk_tips_query_marshal_widget_selected,
196 gtk_object_class_add_signals (object_class, tips_query_signals, SIGNAL_LAST);
198 object_class->destroy = gtk_tips_query_destroy;
199 widget_class->event = gtk_tips_query_event;
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;
208 gtk_tips_query_init (GtkTipsQuery *tips_query)
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;
218 gtk_label_set (GTK_LABEL (tips_query), tips_query->label_inactive);
222 gtk_tips_query_set_arg (GtkTipsQuery *tips_query,
228 case ARG_EMIT_ALWAYS:
229 tips_query->emit_always = (GTK_VALUE_BOOL (*arg) != FALSE);
232 gtk_tips_query_set_caller (tips_query, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
234 case ARG_LABEL_INACTIVE:
235 gtk_tips_query_set_labels (tips_query, GTK_VALUE_STRING (*arg), tips_query->label_no_tip);
237 case ARG_LABEL_NO_TIP:
238 gtk_tips_query_set_labels (tips_query, tips_query->label_inactive, GTK_VALUE_STRING (*arg));
246 gtk_tips_query_get_arg (GtkTipsQuery *tips_query,
252 case ARG_EMIT_ALWAYS:
253 GTK_VALUE_BOOL (*arg) = tips_query->emit_always;
256 GTK_VALUE_OBJECT (*arg) = (GtkObject*) tips_query->caller;
258 case ARG_LABEL_INACTIVE:
259 GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_inactive);
261 case ARG_LABEL_NO_TIP:
262 GTK_VALUE_STRING (*arg) = g_strdup (tips_query->label_no_tip);
265 arg->type = GTK_TYPE_INVALID;
271 gtk_tips_query_destroy (GtkObject *object)
273 GtkTipsQuery *tips_query;
275 g_return_if_fail (object != NULL);
276 g_return_if_fail (GTK_IS_TIPS_QUERY (object));
278 tips_query = GTK_TIPS_QUERY (object);
280 if (tips_query->in_query)
281 gtk_tips_query_stop_query (tips_query);
283 gtk_tips_query_set_caller (tips_query, NULL);
285 if (GTK_OBJECT_CLASS (parent_class)->destroy)
286 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
290 gtk_tips_query_new (void)
292 GtkTipsQuery *tips_query;
294 tips_query = gtk_type_new (gtk_tips_query_get_type ());
296 return GTK_WIDGET (tips_query);
300 gtk_tips_query_set_labels (GtkTipsQuery *tips_query,
301 const gchar *label_inactive,
302 const gchar *label_no_tip)
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);
311 old = tips_query->label_inactive;
312 tips_query->label_inactive = g_strdup (label_inactive);
314 old = tips_query->label_no_tip;
315 tips_query->label_no_tip = g_strdup (label_no_tip);
320 gtk_tips_query_set_caller (GtkTipsQuery *tips_query,
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);
327 g_return_if_fail (GTK_IS_WIDGET (caller));
330 gtk_widget_ref (caller);
332 if (tips_query->caller)
333 gtk_widget_unref (tips_query->caller);
335 tips_query->caller = caller;
339 gtk_tips_query_start_query (GtkTipsQuery *tips_query)
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));
346 tips_query->in_query = TRUE;
347 gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_START_QUERY]);
351 gtk_tips_query_stop_query (GtkTipsQuery *tips_query)
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);
357 gtk_signal_emit (GTK_OBJECT (tips_query), tips_query_signals[SIGNAL_STOP_QUERY]);
358 tips_query->in_query = FALSE;
362 gtk_tips_query_real_start_query (GtkTipsQuery *tips_query)
366 g_return_if_fail (tips_query != NULL);
367 g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
369 tips_query->query_cursor = gdk_cursor_new (GDK_QUESTION_ARROW);
370 failure = gdk_pointer_grab (GTK_WIDGET (tips_query)->window,
372 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
373 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
375 tips_query->query_cursor,
379 gdk_cursor_destroy (tips_query->query_cursor);
380 tips_query->query_cursor = NULL;
382 gtk_grab_add (GTK_WIDGET (tips_query));
386 gtk_tips_query_real_stop_query (GtkTipsQuery *tips_query)
388 g_return_if_fail (tips_query != NULL);
389 g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
391 gtk_grab_remove (GTK_WIDGET (tips_query));
392 if (tips_query->query_cursor)
394 gdk_pointer_ungrab (GDK_CURRENT_TIME);
395 gdk_cursor_destroy (tips_query->query_cursor);
396 tips_query->query_cursor = NULL;
398 if (tips_query->last_crossed)
400 gtk_widget_unref (tips_query->last_crossed);
401 tips_query->last_crossed = NULL;
404 gtk_label_set (GTK_LABEL (tips_query), tips_query->label_inactive);
408 gtk_tips_query_widget_entered (GtkTipsQuery *tips_query,
410 const gchar *tip_text,
411 const gchar *tip_private)
413 g_return_if_fail (tips_query != NULL);
414 g_return_if_fail (GTK_IS_TIPS_QUERY (tips_query));
417 tip_text = tips_query->label_no_tip;
419 if (!g_str_equal (GTK_LABEL (tips_query)->label, (gchar*) tip_text))
420 gtk_label_set (GTK_LABEL (tips_query), tip_text);
424 gtk_tips_query_emit_widget_entered (GtkTipsQuery *tips_query,
427 GtkTooltipsData *tdata;
429 if (widget == (GtkWidget*) tips_query)
433 tdata = gtk_tooltips_data_get (widget);
437 if (!widget && tips_query->last_crossed)
439 gtk_signal_emit (GTK_OBJECT (tips_query),
440 tips_query_signals[SIGNAL_WIDGET_ENTERED],
444 gtk_widget_unref (tips_query->last_crossed);
445 tips_query->last_crossed = NULL;
447 else if (widget && widget != tips_query->last_crossed)
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],
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;
463 gtk_tips_query_event (GtkWidget *widget,
466 GtkTipsQuery *tips_query;
467 GtkWidget *event_widget;
468 gboolean event_handled;
470 g_return_val_if_fail (widget != NULL, FALSE);
471 g_return_val_if_fail (GTK_IS_TIPS_QUERY (widget), FALSE);
473 tips_query = GTK_TIPS_QUERY (widget);
474 if (!tips_query->in_query)
476 if (GTK_WIDGET_CLASS (parent_class)->event)
477 return GTK_WIDGET_CLASS (parent_class)->event (widget, event);
482 event_widget = gtk_get_event_widget (event);
484 event_handled = FALSE;
487 GdkWindow *pointer_window;
489 case GDK_LEAVE_NOTIFY:
491 pointer_window = gdk_window_get_pointer (event_widget->window, NULL, NULL, NULL);
493 pointer_window = NULL;
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;
501 case GDK_ENTER_NOTIFY:
502 gtk_tips_query_emit_widget_entered (tips_query, event_widget);
503 event_handled = TRUE;
506 case GDK_BUTTON_PRESS:
507 case GDK_BUTTON_RELEASE:
510 if (event_widget == (GtkWidget*) tips_query ||
511 event_widget == tips_query->caller)
512 gtk_tips_query_stop_query (tips_query);
516 GtkTooltipsData *tdata;
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],
524 tdata ? tdata->tip_text : NULL,
525 tdata ? tdata->tip_private : NULL,
530 gtk_tips_query_stop_query (tips_query);
533 event_handled = TRUE;
540 return event_handled;