1 | /* testtooltips.c: Test application for GTK >= 2.12 tooltips code |
2 | * |
3 | * Copyright (C) 2006-2007 Imendio AB |
4 | * Contact: Kristian Rietveld <kris@imendio.com> |
5 | * |
6 | * This work is provided "as is"; redistribution and modification |
7 | * in whole or in part, in any medium, physical or electronic is |
8 | * permitted without restriction. |
9 | * |
10 | * This work is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
13 | * |
14 | * In no event shall the authors or contributors be liable for any |
15 | * direct, indirect, incidental, special, exemplary, or consequential |
16 | * damages (including, but not limited to, procurement of substitute |
17 | * goods or services; loss of use, data, or profits; or business |
18 | * interruption) however caused and on any theory of liability, whether |
19 | * in contract, strict liability, or tort (including negligence or |
20 | * otherwise) arising in any way out of the use of this software, even |
21 | * if advised of the possibility of such damage. |
22 | */ |
23 | |
24 | #include <gtk/gtk.h> |
25 | |
26 | typedef struct _MyTooltip MyTooltip; |
27 | typedef struct _MyTooltipClass MyTooltipClass; |
28 | |
29 | |
30 | struct _MyTooltip |
31 | { |
32 | GtkWidget parent_instance; |
33 | }; |
34 | |
35 | struct _MyTooltipClass |
36 | { |
37 | GtkWidgetClass parent_class; |
38 | }; |
39 | |
40 | static GType my_tooltip_get_type (void); |
41 | G_DEFINE_TYPE (MyTooltip, my_tooltip, GTK_TYPE_WIDGET) |
42 | |
43 | static void |
44 | my_tooltip_init (MyTooltip *tt) |
45 | { |
46 | GtkWidget *label = gtk_label_new (str: "Some text in a tooltip" ); |
47 | |
48 | gtk_widget_set_parent (widget: label, GTK_WIDGET (tt)); |
49 | |
50 | gtk_widget_add_css_class (GTK_WIDGET (tt), css_class: "background" ); |
51 | } |
52 | |
53 | static void |
54 | my_tooltip_class_init (MyTooltipClass *tt_class) |
55 | { |
56 | gtk_widget_class_set_layout_manager_type (GTK_WIDGET_CLASS (tt_class), GTK_TYPE_BIN_LAYOUT); |
57 | gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (tt_class), name: "tooltip" ); |
58 | } |
59 | |
60 | static gboolean |
61 | query_tooltip_cb (GtkWidget *widget, |
62 | int x, |
63 | int y, |
64 | gboolean keyboard_tip, |
65 | GtkTooltip *tooltip, |
66 | gpointer data) |
67 | { |
68 | gtk_tooltip_set_markup (tooltip, markup: gtk_button_get_label (GTK_BUTTON (widget))); |
69 | gtk_tooltip_set_icon_from_icon_name (tooltip, icon_name: "edit-delete" ); |
70 | |
71 | return TRUE; |
72 | } |
73 | |
74 | static gboolean |
75 | query_tooltip_text_view_cb (GtkWidget *widget, |
76 | int x, |
77 | int y, |
78 | gboolean keyboard_tip, |
79 | GtkTooltip *tooltip, |
80 | gpointer data) |
81 | { |
82 | GtkTextTag *tag = data; |
83 | GtkTextIter iter; |
84 | GtkTextView *text_view = GTK_TEXT_VIEW (widget); |
85 | GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view); |
86 | |
87 | if (keyboard_tip) |
88 | { |
89 | int offset; |
90 | |
91 | g_object_get (object: buffer, first_property_name: "cursor-position" , &offset, NULL); |
92 | gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: offset); |
93 | } |
94 | else |
95 | { |
96 | int bx, by, trailing; |
97 | |
98 | gtk_text_view_window_to_buffer_coords (text_view, win: GTK_TEXT_WINDOW_TEXT, |
99 | window_x: x, window_y: y, buffer_x: &bx, buffer_y: &by); |
100 | gtk_text_view_get_iter_at_position (text_view, iter: &iter, trailing: &trailing, x: bx, y: by); |
101 | } |
102 | |
103 | if (gtk_text_iter_has_tag (iter: &iter, tag)) |
104 | gtk_tooltip_set_text (tooltip, text: "Tooltip on text tag" ); |
105 | else |
106 | return FALSE; |
107 | |
108 | return TRUE; |
109 | } |
110 | |
111 | static gboolean |
112 | query_tooltip_tree_view_cb (GtkWidget *widget, |
113 | int x, |
114 | int y, |
115 | gboolean keyboard_tip, |
116 | GtkTooltip *tooltip, |
117 | gpointer data) |
118 | { |
119 | GtkTreeIter iter; |
120 | GtkTreeView *tree_view = GTK_TREE_VIEW (widget); |
121 | GtkTreeModel *model = gtk_tree_view_get_model (tree_view); |
122 | GtkTreePath *path = NULL; |
123 | char *tmp; |
124 | char *pathstring; |
125 | |
126 | char buffer[512]; |
127 | |
128 | if (!gtk_tree_view_get_tooltip_context (tree_view, x, y, |
129 | keyboard_tip, |
130 | model: &model, path: &path, iter: &iter)) |
131 | return FALSE; |
132 | |
133 | gtk_tree_model_get (tree_model: model, iter: &iter, 0, &tmp, -1); |
134 | pathstring = gtk_tree_path_to_string (path); |
135 | |
136 | g_snprintf (string: buffer, n: 511, format: "<b>Path %s:</b> %s" , pathstring, tmp); |
137 | gtk_tooltip_set_markup (tooltip, markup: buffer); |
138 | |
139 | gtk_tree_view_set_tooltip_row (tree_view, tooltip, path); |
140 | |
141 | gtk_tree_path_free (path); |
142 | g_free (mem: pathstring); |
143 | g_free (mem: tmp); |
144 | |
145 | return TRUE; |
146 | } |
147 | |
148 | static GtkTreeModel * |
149 | create_model (void) |
150 | { |
151 | GtkTreeStore *store; |
152 | GtkTreeIter iter; |
153 | |
154 | store = gtk_tree_store_new (n_columns: 1, G_TYPE_STRING); |
155 | |
156 | /* A tree store with some random words ... */ |
157 | gtk_tree_store_insert_with_values (tree_store: store, iter: &iter, NULL, position: 0, |
158 | 0, "File Manager" , -1); |
159 | gtk_tree_store_insert_with_values (tree_store: store, iter: &iter, NULL, position: 0, |
160 | 0, "Gossip" , -1); |
161 | gtk_tree_store_insert_with_values (tree_store: store, iter: &iter, NULL, position: 0, |
162 | 0, "System Settings" , -1); |
163 | gtk_tree_store_insert_with_values (tree_store: store, iter: &iter, NULL, position: 0, |
164 | 0, "The GIMP" , -1); |
165 | gtk_tree_store_insert_with_values (tree_store: store, iter: &iter, NULL, position: 0, |
166 | 0, "Terminal" , -1); |
167 | gtk_tree_store_insert_with_values (tree_store: store, iter: &iter, NULL, position: 0, |
168 | 0, "Word Processor" , -1); |
169 | |
170 | return GTK_TREE_MODEL (store); |
171 | } |
172 | |
173 | static void |
174 | selection_changed_cb (GtkTreeSelection *selection, |
175 | GtkWidget *tree_view) |
176 | { |
177 | gtk_widget_trigger_tooltip_query (widget: tree_view); |
178 | } |
179 | |
180 | static struct Rectangle |
181 | { |
182 | int x; |
183 | int y; |
184 | float r; |
185 | float g; |
186 | float b; |
187 | const char *tooltip; |
188 | } |
189 | rectangles[] = |
190 | { |
191 | { 10, 10, 0.0, 0.0, 0.9, "Blue box!" }, |
192 | { 200, 170, 1.0, 0.0, 0.0, "Red thing" }, |
193 | { 100, 50, 0.8, 0.8, 0.0, "Yellow thing" } |
194 | }; |
195 | |
196 | static gboolean |
197 | query_tooltip_drawing_area_cb (GtkWidget *widget, |
198 | int x, |
199 | int y, |
200 | gboolean keyboard_tip, |
201 | GtkTooltip *tooltip, |
202 | gpointer data) |
203 | { |
204 | int i; |
205 | |
206 | if (keyboard_tip) |
207 | return FALSE; |
208 | |
209 | for (i = 0; i < G_N_ELEMENTS (rectangles); i++) |
210 | { |
211 | struct Rectangle *r = &rectangles[i]; |
212 | |
213 | if (r->x < x && x < r->x + 50 |
214 | && r->y < y && y < r->y + 50) |
215 | { |
216 | gtk_tooltip_set_markup (tooltip, markup: r->tooltip); |
217 | return TRUE; |
218 | } |
219 | } |
220 | |
221 | return FALSE; |
222 | } |
223 | |
224 | static void |
225 | drawing_area_draw (GtkDrawingArea *drawing_area, |
226 | cairo_t *cr, |
227 | int width, |
228 | int height, |
229 | gpointer data) |
230 | { |
231 | int i; |
232 | |
233 | cairo_set_source_rgb (cr, red: 1.0, green: 1.0, blue: 1.0); |
234 | cairo_paint (cr); |
235 | |
236 | for (i = 0; i < G_N_ELEMENTS (rectangles); i++) |
237 | { |
238 | struct Rectangle *r = &rectangles[i]; |
239 | |
240 | cairo_rectangle (cr, x: r->x, y: r->y, width: 50, height: 50); |
241 | cairo_set_source_rgb (cr, red: r->r, green: r->g, blue: r->b); |
242 | cairo_stroke (cr); |
243 | |
244 | cairo_rectangle (cr, x: r->x, y: r->y, width: 50, height: 50); |
245 | cairo_set_source_rgba (cr, red: r->r, green: r->g, blue: r->b, alpha: 0.5); |
246 | cairo_fill (cr); |
247 | } |
248 | } |
249 | |
250 | static gboolean |
251 | query_tooltip_label_cb (GtkWidget *widget, |
252 | int x, |
253 | int y, |
254 | gboolean keyboard_tip, |
255 | GtkTooltip *tooltip, |
256 | gpointer data) |
257 | { |
258 | GtkWidget *custom = data; |
259 | |
260 | gtk_tooltip_set_custom (tooltip, custom_widget: custom); |
261 | |
262 | return TRUE; |
263 | } |
264 | |
265 | static void |
266 | quit_cb (GtkWidget *widget, |
267 | gpointer data) |
268 | { |
269 | gboolean *done = data; |
270 | |
271 | *done = TRUE; |
272 | |
273 | g_main_context_wakeup (NULL); |
274 | } |
275 | |
276 | int |
277 | main (int argc, char *argv[]) |
278 | { |
279 | GtkWidget *window; |
280 | GtkWidget *box; |
281 | GtkWidget *drawing_area; |
282 | GtkWidget *button; |
283 | GtkWidget *tooltip; |
284 | GtkWidget *popover; |
285 | GtkWidget *box2; |
286 | GtkWidget *custom; |
287 | |
288 | GtkWidget *tree_view; |
289 | GtkTreeViewColumn *column; |
290 | |
291 | GtkWidget *text_view; |
292 | GtkTextBuffer *buffer; |
293 | GtkTextIter iter; |
294 | GtkTextTag *tag; |
295 | |
296 | const char *text, *markup; |
297 | gboolean done = FALSE; |
298 | |
299 | gtk_init (); |
300 | |
301 | window = gtk_window_new (); |
302 | gtk_window_set_title (GTK_WINDOW (window), title: "Tooltips test" ); |
303 | g_signal_connect (window, "destroy" , G_CALLBACK (quit_cb), &done); |
304 | |
305 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 3); |
306 | gtk_window_set_child (GTK_WINDOW (window), child: box); |
307 | |
308 | tooltip = g_object_new (object_type: my_tooltip_get_type (), NULL); |
309 | gtk_widget_set_margin_top (widget: tooltip, margin: 20); |
310 | gtk_widget_set_margin_bottom (widget: tooltip, margin: 20); |
311 | gtk_widget_set_halign (widget: tooltip, align: GTK_ALIGN_CENTER); |
312 | gtk_box_append (GTK_BOX (box), child: tooltip); |
313 | |
314 | |
315 | /* A check button using the tooltip-markup property */ |
316 | button = gtk_check_button_new_with_label (label: "This one uses the tooltip-markup property" ); |
317 | gtk_widget_set_tooltip_text (widget: button, text: "Hello, I am a static tooltip." ); |
318 | gtk_box_append (GTK_BOX (box), child: button); |
319 | |
320 | text = gtk_widget_get_tooltip_text (widget: button); |
321 | markup = gtk_widget_get_tooltip_markup (widget: button); |
322 | g_assert_true (g_str_equal ("Hello, I am a static tooltip." , text)); |
323 | g_assert_true (g_str_equal ("Hello, I am a static tooltip." , markup)); |
324 | |
325 | /* A check button using the query-tooltip signal */ |
326 | button = gtk_check_button_new_with_label (label: "I use the query-tooltip signal" ); |
327 | g_object_set (object: button, first_property_name: "has-tooltip" , TRUE, NULL); |
328 | g_signal_connect (button, "query-tooltip" , |
329 | G_CALLBACK (query_tooltip_cb), NULL); |
330 | gtk_box_append (GTK_BOX (box), child: button); |
331 | |
332 | /* A label */ |
333 | button = gtk_label_new (str: "I am just a label" ); |
334 | gtk_label_set_selectable (GTK_LABEL (button), FALSE); |
335 | gtk_widget_set_tooltip_text (widget: button, text: "Label & and tooltip" ); |
336 | gtk_box_append (GTK_BOX (box), child: button); |
337 | |
338 | text = gtk_widget_get_tooltip_text (widget: button); |
339 | markup = gtk_widget_get_tooltip_markup (widget: button); |
340 | g_assert_true (g_str_equal ("Label & and tooltip" , text)); |
341 | g_assert_true (g_str_equal ("Label & and tooltip" , markup)); |
342 | |
343 | /* A selectable label */ |
344 | button = gtk_label_new (str: "I am a selectable label" ); |
345 | gtk_label_set_selectable (GTK_LABEL (button), TRUE); |
346 | gtk_widget_set_tooltip_markup (widget: button, markup: "<b>Another</b> Label tooltip" ); |
347 | gtk_box_append (GTK_BOX (box), child: button); |
348 | |
349 | text = gtk_widget_get_tooltip_text (widget: button); |
350 | markup = gtk_widget_get_tooltip_markup (widget: button); |
351 | g_assert_true (g_str_equal ("Another Label tooltip" , text)); |
352 | g_assert_true (g_str_equal ("<b>Another</b> Label tooltip" , markup)); |
353 | |
354 | /* An insensitive button */ |
355 | button = gtk_button_new_with_label (label: "This one is insensitive" ); |
356 | gtk_widget_set_sensitive (widget: button, FALSE); |
357 | g_object_set (object: button, first_property_name: "tooltip-text" , "Insensitive!" , NULL); |
358 | gtk_box_append (GTK_BOX (box), child: button); |
359 | |
360 | /* Testcases from Kris without a tree view don't exist. */ |
361 | tree_view = gtk_tree_view_new_with_model (model: create_model ()); |
362 | gtk_widget_set_size_request (widget: tree_view, width: 200, height: 240); |
363 | |
364 | gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view), |
365 | position: 0, title: "Test" , |
366 | cell: gtk_cell_renderer_text_new (), |
367 | "text" , 0, |
368 | NULL); |
369 | |
370 | g_object_set (object: tree_view, first_property_name: "has-tooltip" , TRUE, NULL); |
371 | g_signal_connect (tree_view, "query-tooltip" , |
372 | G_CALLBACK (query_tooltip_tree_view_cb), NULL); |
373 | g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)), |
374 | "changed" , G_CALLBACK (selection_changed_cb), tree_view); |
375 | |
376 | /* Set a tooltip on the column */ |
377 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), n: 0); |
378 | gtk_tree_view_column_set_clickable (tree_column: column, TRUE); |
379 | g_object_set (object: gtk_tree_view_column_get_button (tree_column: column), first_property_name: "tooltip-text" , "Header" , NULL); |
380 | |
381 | gtk_box_append (GTK_BOX (box), child: tree_view); |
382 | |
383 | /* And a text view for Matthias */ |
384 | buffer = gtk_text_buffer_new (NULL); |
385 | |
386 | gtk_text_buffer_get_end_iter (buffer, iter: &iter); |
387 | gtk_text_buffer_insert (buffer, iter: &iter, text: "Hello, the text " , len: -1); |
388 | |
389 | tag = gtk_text_buffer_create_tag (buffer, tag_name: "bold" , NULL); |
390 | g_object_set (object: tag, first_property_name: "weight" , PANGO_WEIGHT_BOLD, NULL); |
391 | |
392 | gtk_text_buffer_get_end_iter (buffer, iter: &iter); |
393 | gtk_text_buffer_insert_with_tags (buffer, iter: &iter, text: "in bold" , len: -1, first_tag: tag, NULL); |
394 | |
395 | gtk_text_buffer_get_end_iter (buffer, iter: &iter); |
396 | gtk_text_buffer_insert (buffer, iter: &iter, text: " has a tooltip!" , len: -1); |
397 | |
398 | text_view = gtk_text_view_new_with_buffer (buffer); |
399 | gtk_widget_set_size_request (widget: text_view, width: 200, height: 50); |
400 | |
401 | g_object_set (object: text_view, first_property_name: "has-tooltip" , TRUE, NULL); |
402 | g_signal_connect (text_view, "query-tooltip" , |
403 | G_CALLBACK (query_tooltip_text_view_cb), tag); |
404 | |
405 | gtk_box_append (GTK_BOX (box), child: text_view); |
406 | |
407 | /* Drawing area */ |
408 | drawing_area = gtk_drawing_area_new (); |
409 | gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (drawing_area), width: 320); |
410 | gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (drawing_area), height: 240); |
411 | gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), |
412 | draw_func: drawing_area_draw, NULL, NULL); |
413 | g_object_set (object: drawing_area, first_property_name: "has-tooltip" , TRUE, NULL); |
414 | g_signal_connect (drawing_area, "query-tooltip" , |
415 | G_CALLBACK (query_tooltip_drawing_area_cb), NULL); |
416 | gtk_box_append (GTK_BOX (box), child: drawing_area); |
417 | |
418 | button = gtk_menu_button_new (); |
419 | gtk_widget_set_halign (widget: button, align: GTK_ALIGN_CENTER); |
420 | gtk_menu_button_set_label (GTK_MENU_BUTTON (button), label: "Custom tooltip I" ); |
421 | gtk_box_append (GTK_BOX (box), child: button); |
422 | popover = gtk_popover_new (); |
423 | gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover); |
424 | box2 = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
425 | gtk_popover_set_child (GTK_POPOVER (popover), child: box2); |
426 | |
427 | button = gtk_label_new (str: "Hidden here" ); |
428 | custom = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 5); |
429 | gtk_box_append (GTK_BOX (custom), child: gtk_label_new (str: "See, custom" )); |
430 | gtk_box_append (GTK_BOX (custom), child: g_object_new (GTK_TYPE_SPINNER, first_property_name: "spinning" , TRUE, NULL)); |
431 | g_object_ref_sink (custom); |
432 | g_object_set (object: button, first_property_name: "has-tooltip" , TRUE, NULL); |
433 | gtk_box_append (GTK_BOX (box2), child: button); |
434 | g_signal_connect (button, "query-tooltip" , |
435 | G_CALLBACK (query_tooltip_label_cb), custom); |
436 | |
437 | button = gtk_label_new (str: "Custom tooltip II" ); |
438 | custom = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 5); |
439 | gtk_box_append (GTK_BOX (custom), child: gtk_label_new (str: "See, custom too" )); |
440 | gtk_box_append (GTK_BOX (custom), child: g_object_new (GTK_TYPE_SPINNER, first_property_name: "spinning" , TRUE, NULL)); |
441 | g_object_ref_sink (custom); |
442 | g_object_set (object: button, first_property_name: "has-tooltip" , TRUE, NULL); |
443 | g_signal_connect (button, "query-tooltip" , |
444 | G_CALLBACK (query_tooltip_label_cb), custom); |
445 | gtk_box_append (GTK_BOX (box), child: button); |
446 | |
447 | /* Done! */ |
448 | gtk_widget_show (widget: window); |
449 | |
450 | while (!done) |
451 | g_main_context_iteration (NULL, TRUE); |
452 | |
453 | return 0; |
454 | } |
455 | |