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 Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtktipsquery.h"
22 #include "gtksignal.h"
23 #include "gtktooltips.h"
28 /* --- arguments --- */
43 SIGNAL_WIDGET_ENTERED,
44 SIGNAL_WIDGET_SELECTED,
47 typedef void (*SignalWidgetEntered) (GtkObject *object,
49 const gchar *tip_text,
50 const gchar *tip_private,
52 typedef gint (*SignalWidgetSelected) (GtkObject *object,
54 const gchar *tip_text,
55 const gchar *tip_private,
56 GdkEventButton *event,
60 /* --- prototypes --- */
61 static void gtk_tips_query_class_init (GtkTipsQueryClass *class);
62 static void gtk_tips_query_init (GtkTipsQuery *tips_query);
63 static void gtk_tips_query_destroy (GtkObject *object);
64 static gint gtk_tips_query_event (GtkWidget *widget,
66 static void gtk_tips_query_set_arg (GtkTipsQuery *tips_query,
69 static void gtk_tips_query_get_arg (GtkTipsQuery *tips_query,
72 static void gtk_tips_query_real_start_query (GtkTipsQuery *tips_query);
73 static void gtk_tips_query_real_stop_query (GtkTipsQuery *tips_query);
74 static void gtk_tips_query_widget_entered (GtkTipsQuery *tips_query,
76 const gchar *tip_text,
77 const gchar *tip_private);
80 /* --- variables --- */
81 static GtkLabelClass *parent_class = NULL;
82 static guint tips_query_signals[SIGNAL_LAST] = { 0 };
85 /* --- functions --- */
87 gtk_tips_query_get_type (void)
89 static guint tips_query_type = 0;
93 GtkTypeInfo tips_query_info =
96 sizeof (GtkTipsQuery),
97 sizeof (GtkTipsQueryClass),
98 (GtkClassInitFunc) gtk_tips_query_class_init,
99 (GtkObjectInitFunc) gtk_tips_query_init,
100 (GtkArgSetFunc) gtk_tips_query_set_arg,
101 (GtkArgGetFunc) gtk_tips_query_get_arg,
104 tips_query_type = gtk_type_unique (gtk_label_get_type (), &tips_query_info);
107 return tips_query_type;
111 gtk_tips_query_marshal_widget_entered (GtkObject *object,
116 SignalWidgetEntered sfunc = (SignalWidgetEntered) func;
119 (GtkWidget*) GTK_VALUE_OBJECT (args[0]),
120 GTK_VALUE_STRING (args[1]),
121 GTK_VALUE_STRING (args[2]),
126 gtk_tips_query_marshal_widget_selected (GtkObject *object,
133 SignalWidgetSelected sfunc = (SignalWidgetSelected) func;
134 return_val = GTK_RETLOC_BOOL (args[4]);
136 *return_val = (* sfunc) (object,
137 (GtkWidget*) GTK_VALUE_OBJECT (args[0]),
138 GTK_VALUE_STRING (args[1]),
139 GTK_VALUE_STRING (args[2]),
140 GTK_VALUE_BOXED (args[3]),
145 gtk_tips_query_class_init (GtkTipsQueryClass *class)
147 GtkObjectClass *object_class;
148 GtkWidgetClass *widget_class;
150 object_class = (GtkObjectClass*) class;
151 widget_class = (GtkWidgetClass*) class;
153 parent_class = gtk_type_class (gtk_label_get_type ());
155 gtk_object_add_arg_type ("GtkTipsQuery::emit_always", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EMIT_ALWAYS);
156 gtk_object_add_arg_type ("GtkTipsQuery::caller", GTK_TYPE_WIDGET, GTK_ARG_READWRITE, ARG_CALLER);
157 gtk_object_add_arg_type ("GtkTipsQuery::label_inactive", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL_INACTIVE);
158 gtk_object_add_arg_type ("GtkTipsQuery::label_no_tip", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL_NO_TIP);
160 tips_query_signals[SIGNAL_START_QUERY] =
161 gtk_signal_new ("start_query",
164 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, start_query),
165 gtk_signal_default_marshaller,
167 tips_query_signals[SIGNAL_STOP_QUERY] =
168 gtk_signal_new ("stop_query",
171 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, stop_query),
172 gtk_signal_default_marshaller,
174 tips_query_signals[SIGNAL_WIDGET_ENTERED] =
175 gtk_signal_new ("widget_entered",
178 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_entered),
179 gtk_tips_query_marshal_widget_entered,
184 tips_query_signals[SIGNAL_WIDGET_SELECTED] =
185 gtk_signal_new ("widget_selected",
188 GTK_SIGNAL_OFFSET (GtkTipsQueryClass, widget_selected),
189 gtk_tips_query_marshal_widget_selected,
195 gtk_object_class_add_signals (object_class, tips_query_signals, SIGNAL_LAST);
197 object_class->destroy = gtk_tips_query_destroy;
198 widget_class->event = gtk_tips_query_event;
200 class->start_query = gtk_tips_query_real_start_query;
201 class->stop_query = gtk_tips_query_real_stop_query;
202 class->widget_entered = gtk_tips_query_widget_entered;
203 class->widget_selected = NULL;
207 gtk_tips_query_init (GtkTipsQuery *tips_query)
209 tips_query->emit_always = FALSE;
210 tips_query->in_query = FALSE;
211 tips_query->label_inactive = g_strdup ("");
212 tips_query->label_no_tip = g_strdup ("--- No Tip ---");
213 tips_query->caller = NULL;
214 tips_query->last_crossed = NULL;
215 tips_query->query_cursor = NULL;
217 gtk_label_set (GTK_LABEL (tips_query), tips_query->label_inactive);
221 gtk_tips_query_set_arg (GtkTipsQuery *tips_query,
227 case ARG_EMIT_ALWAYS:
228 tips_query->emit_always = (GTK_VALUE_BOOL (*arg) != FALSE);
231 gtk_tips_query_set_caller (tips_query, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
233 case ARG_LABEL_INACTIVE:
234 gtk_tips_query_set_labels (tips_query, GTK_VALUE_STRING (*arg), tips_query->label_no_tip);
236 case ARG_LABEL_NO_TIP:
237 gtk_tips_query_set_labels (tips_query, tips_query->label_inactive, GTK_VALUE_STRING (*arg));
240 arg->type = GTK_TYPE_INVALID;
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;