1/* testiconview.c
2 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <gtk/gtk.h>
19#include <glib/gstdio.h>
20#include <sys/types.h>
21#include <string.h>
22
23#define NUMBER_OF_ITEMS 10
24#define SOME_ITEMS 100
25#define MANY_ITEMS 10000
26
27static void
28fill_model (GtkTreeModel *model)
29{
30 GdkPixbuf *pixbuf;
31 int i;
32 char *str, *str2;
33 GtkTreeIter iter;
34 GtkListStore *store = GTK_LIST_STORE (model);
35 gint32 size;
36
37 pixbuf = gdk_pixbuf_new_from_file (filename: "gnome-textfile.png", NULL);
38
39 i = 0;
40
41 gtk_list_store_prepend (list_store: store, iter: &iter);
42
43 gtk_list_store_set (list_store: store, iter: &iter,
44 0, pixbuf,
45 1, "Really really\nreally really loooooooooong item name",
46 2, 0,
47 3, "This is a <b>Test</b> of <i>markup</i>",
48 4, TRUE,
49 -1);
50
51 while (i < NUMBER_OF_ITEMS - 1)
52 {
53 GdkPixbuf *pb;
54 size = g_random_int_range (begin: 20, end: 70);
55 pb = gdk_pixbuf_scale_simple (src: pixbuf, dest_width: size, dest_height: size, interp_type: GDK_INTERP_NEAREST);
56
57 str = g_strdup_printf (format: "Icon %d", i);
58 str2 = g_strdup_printf (format: "Icon <b>%d</b>", i);
59 gtk_list_store_prepend (list_store: store, iter: &iter);
60 gtk_list_store_set (list_store: store, iter: &iter,
61 0, pb,
62 1, str,
63 2, i,
64 3, str2,
65 4, TRUE,
66 -1);
67 g_free (mem: str);
68 g_free (mem: str2);
69 i++;
70 }
71
72 // gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 2, GTK_SORT_ASCENDING);
73}
74
75static GtkTreeModel *
76create_model (void)
77{
78 GtkListStore *store;
79
80 store = gtk_list_store_new (n_columns: 5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
81
82 return GTK_TREE_MODEL (store);
83}
84
85
86static void
87foreach_selected_remove (GtkWidget *button, GtkIconView *icon_list)
88{
89 GtkTreeIter iter;
90 GtkTreeModel *model;
91
92 GList *list, *selected;
93
94 selected = gtk_icon_view_get_selected_items (icon_view: icon_list);
95 model = gtk_icon_view_get_model (icon_view: icon_list);
96
97 for (list = selected; list; list = list->next)
98 {
99 GtkTreePath *path = list->data;
100
101 gtk_tree_model_get_iter (tree_model: model, iter: &iter, path);
102 gtk_list_store_remove (GTK_LIST_STORE (model), iter: &iter);
103
104 gtk_tree_path_free (path);
105 }
106
107 g_list_free (list: selected);
108}
109
110
111static void
112swap_rows (GtkWidget *button, GtkIconView *icon_list)
113{
114 GtkTreeIter iter, iter2;
115 GtkTreeModel *model;
116
117 model = gtk_icon_view_get_model (icon_view: icon_list);
118 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), sort_column_id: -2, order: GTK_SORT_ASCENDING);
119
120 gtk_tree_model_get_iter_first (tree_model: model, iter: &iter);
121 iter2 = iter;
122 gtk_tree_model_iter_next (tree_model: model, iter: &iter2);
123 gtk_list_store_swap (GTK_LIST_STORE (model), a: &iter, b: &iter2);
124}
125
126static void
127add_n_items (GtkIconView *icon_list, int n)
128{
129 static int count = NUMBER_OF_ITEMS;
130
131 GtkTreeIter iter;
132 GtkListStore *store;
133 GdkPixbuf *pixbuf;
134 char *str, *str2;
135 int i;
136
137 store = GTK_LIST_STORE (gtk_icon_view_get_model (icon_list));
138 pixbuf = gdk_pixbuf_new_from_file (filename: "gnome-textfile.png", NULL);
139
140
141 for (i = 0; i < n; i++)
142 {
143 str = g_strdup_printf (format: "Icon %d", count);
144 str2 = g_strdup_printf (format: "Icon <b>%d</b>", count);
145 gtk_list_store_prepend (list_store: store, iter: &iter);
146 gtk_list_store_set (list_store: store, iter: &iter,
147 0, pixbuf,
148 1, str,
149 2, i,
150 3, str2,
151 -1);
152 g_free (mem: str);
153 g_free (mem: str2);
154 count++;
155 }
156}
157
158static void
159add_some (GtkWidget *button, GtkIconView *icon_list)
160{
161 add_n_items (icon_list, SOME_ITEMS);
162}
163
164static void
165add_many (GtkWidget *button, GtkIconView *icon_list)
166{
167 add_n_items (icon_list, MANY_ITEMS);
168}
169
170static void
171add_large (GtkWidget *button, GtkIconView *icon_list)
172{
173 GtkListStore *store;
174 GtkTreeIter iter;
175
176 GdkPixbuf *pixbuf, *pb;
177 char *str;
178
179 store = GTK_LIST_STORE (gtk_icon_view_get_model (icon_list));
180 pixbuf = gdk_pixbuf_new_from_file (filename: "gnome-textfile.png", NULL);
181
182 pb = gdk_pixbuf_scale_simple (src: pixbuf,
183 dest_width: 2 * gdk_pixbuf_get_width (pixbuf),
184 dest_height: 2 * gdk_pixbuf_get_height (pixbuf),
185 interp_type: GDK_INTERP_BILINEAR);
186
187 str = g_strdup_printf (format: "Some really long text");
188 gtk_list_store_append (list_store: store, iter: &iter);
189 gtk_list_store_set (list_store: store, iter: &iter,
190 0, pb,
191 1, str,
192 2, 0,
193 3, str,
194 -1);
195 g_object_unref (object: pb);
196 g_free (mem: str);
197
198 pb = gdk_pixbuf_scale_simple (src: pixbuf,
199 dest_width: 3 * gdk_pixbuf_get_width (pixbuf),
200 dest_height: 3 * gdk_pixbuf_get_height (pixbuf),
201 interp_type: GDK_INTERP_BILINEAR);
202
203 str = g_strdup (str: "see how long text behaves when placed underneath "
204 "an oversized icon which would allow for long lines");
205 gtk_list_store_append (list_store: store, iter: &iter);
206 gtk_list_store_set (list_store: store, iter: &iter,
207 0, pb,
208 1, str,
209 2, 1,
210 3, str,
211 -1);
212 g_object_unref (object: pb);
213 g_free (mem: str);
214
215 pb = gdk_pixbuf_scale_simple (src: pixbuf,
216 dest_width: 3 * gdk_pixbuf_get_width (pixbuf),
217 dest_height: 3 * gdk_pixbuf_get_height (pixbuf),
218 interp_type: GDK_INTERP_BILINEAR);
219
220 str = g_strdup (str: "short text");
221 gtk_list_store_append (list_store: store, iter: &iter);
222 gtk_list_store_set (list_store: store, iter: &iter,
223 0, pb,
224 1, str,
225 2, 2,
226 3, str,
227 -1);
228 g_object_unref (object: pb);
229 g_free (mem: str);
230
231 g_object_unref (object: pixbuf);
232}
233
234static void
235select_all (GtkWidget *button, GtkIconView *icon_list)
236{
237 gtk_icon_view_select_all (icon_view: icon_list);
238}
239
240static void
241select_nonexisting (GtkWidget *button, GtkIconView *icon_list)
242{
243 GtkTreePath *path = gtk_tree_path_new_from_indices (first_index: 999999, -1);
244 gtk_icon_view_select_path (icon_view: icon_list, path);
245 gtk_tree_path_free (path);
246}
247
248static void
249unselect_all (GtkWidget *button, GtkIconView *icon_list)
250{
251 gtk_icon_view_unselect_all (icon_view: icon_list);
252}
253
254static void
255selection_changed (GtkIconView *icon_list)
256{
257 g_print (format: "Selection changed!\n");
258}
259
260typedef struct {
261 GtkIconView *icon_list;
262 GtkTreePath *path;
263} ItemData;
264
265static void
266free_item_data (ItemData *data)
267{
268 gtk_tree_path_free (path: data->path);
269 g_free (mem: data);
270}
271
272static void
273item_activated (GtkIconView *icon_view,
274 GtkTreePath *path)
275{
276 GtkTreeIter iter;
277 GtkTreeModel *model;
278 char *text;
279
280 model = gtk_icon_view_get_model (icon_view);
281 gtk_tree_model_get_iter (tree_model: model, iter: &iter, path);
282
283 gtk_tree_model_get (tree_model: model, iter: &iter, 1, &text, -1);
284 g_print (format: "Item activated, text is %s\n", text);
285 g_free (mem: text);
286
287}
288
289static void
290toggled (GtkCellRendererToggle *cell,
291 char *path_string,
292 gpointer data)
293{
294 GtkTreeModel *model = GTK_TREE_MODEL (data);
295 GtkTreeIter iter;
296 GtkTreePath *path = gtk_tree_path_new_from_string (path: path_string);
297 gboolean value;
298
299 gtk_tree_model_get_iter (tree_model: model, iter: &iter, path);
300 gtk_tree_model_get (tree_model: model, iter: &iter, 4, &value, -1);
301
302 value = !value;
303 gtk_list_store_set (GTK_LIST_STORE (model), iter: &iter, 4, value, -1);
304
305 gtk_tree_path_free (path);
306}
307
308static void
309edited (GtkCellRendererText *cell,
310 char *path_string,
311 char *new_text,
312 gpointer data)
313{
314 GtkTreeModel *model = GTK_TREE_MODEL (data);
315 GtkTreeIter iter;
316 GtkTreePath *path = gtk_tree_path_new_from_string (path: path_string);
317
318 gtk_tree_model_get_iter (tree_model: model, iter: &iter, path);
319 gtk_list_store_set (GTK_LIST_STORE (model), iter: &iter, 1, new_text, -1);
320
321 gtk_tree_path_free (path);
322}
323
324static void
325item_cb (GtkWidget *menuitem,
326 ItemData *data)
327{
328 item_activated (icon_view: data->icon_list, path: data->path);
329}
330
331static void
332do_popup_menu (GtkWidget *icon_list,
333 GtkTreePath *path)
334{
335 GtkIconView *icon_view = GTK_ICON_VIEW (icon_list);
336 GtkWidget *menu;
337 GtkWidget *item;
338 ItemData *data;
339
340 if (!path)
341 return;
342
343 menu = gtk_popover_new ();
344 gtk_widget_set_parent (widget: menu, parent: icon_list);
345
346 data = g_new0 (ItemData, 1);
347 data->icon_list = icon_view;
348 data->path = path;
349 g_object_set_data_full (G_OBJECT (menu), key: "item-path", data, destroy: (GDestroyNotify)free_item_data);
350
351 item = gtk_button_new_with_label (label: "Activate");
352 gtk_popover_set_child (GTK_POPOVER (menu), child: item);
353 g_signal_connect (item, "clicked", G_CALLBACK (item_cb), data);
354
355 gtk_popover_popup (GTK_POPOVER (menu));
356}
357
358static void
359press_handler (GtkGestureClick *gesture,
360 guint n_press,
361 double x,
362 double y,
363 GtkWidget *widget)
364{
365 GtkTreePath *path = NULL;
366
367 /* Ignore double-clicks and triple-clicks */
368 if (n_press > 1)
369 return;
370
371 path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (widget), x, y);
372 do_popup_menu (icon_list: widget, path);
373}
374
375static gboolean
376popup_menu_handler (GtkWidget *widget)
377{
378 GtkTreePath *path = NULL;
379 GList *list;
380
381 list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (widget));
382
383 if (list)
384 {
385 path = (GtkTreePath*)list->data;
386 g_list_free_full (list, free_func: (GDestroyNotify) gtk_tree_path_free);
387 }
388
389 do_popup_menu (icon_list: widget, path);
390 return TRUE;
391}
392
393int
394main (int argc, char **argv)
395{
396 GtkWidget *paned, *tv;
397 GtkWidget *window, *icon_list, *scrolled_window;
398 GtkWidget *vbox, *bbox;
399 GtkWidget *button;
400 GtkTreeModel *model;
401 GtkCellRenderer *cell;
402 GtkTreeViewColumn *tvc;
403 GdkContentFormats *targets;
404 GtkGesture *gesture;
405
406#ifdef GTK_SRCDIR
407 g_chdir (GTK_SRCDIR);
408#endif
409
410 gtk_init ();
411
412 /* to test rtl layout, set RTL=1 in the environment */
413 if (g_getenv (variable: "RTL"))
414 gtk_widget_set_default_direction (dir: GTK_TEXT_DIR_RTL);
415
416 window = gtk_window_new ();
417 gtk_window_set_default_size (GTK_WINDOW (window), width: 700, height: 400);
418
419 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
420 gtk_window_set_child (GTK_WINDOW (window), child: vbox);
421
422 paned = gtk_paned_new (orientation: GTK_ORIENTATION_HORIZONTAL);
423 gtk_widget_set_vexpand (widget: paned, TRUE);
424 gtk_box_append (GTK_BOX (vbox), child: paned);
425
426 icon_list = gtk_icon_view_new ();
427 gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_list), mode: GTK_SELECTION_MULTIPLE);
428
429 tv = gtk_tree_view_new ();
430 tvc = gtk_tree_view_column_new ();
431 gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column: tvc);
432
433 gesture = gtk_gesture_click_new ();
434 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
435 GDK_BUTTON_SECONDARY);
436 g_signal_connect (gesture, "pressed",
437 G_CALLBACK (press_handler), icon_list);
438 gtk_widget_add_controller (widget: icon_list, GTK_EVENT_CONTROLLER (gesture));
439
440 g_signal_connect (icon_list, "selection_changed",
441 G_CALLBACK (selection_changed), NULL);
442 g_signal_connect (icon_list, "popup_menu",
443 G_CALLBACK (popup_menu_handler), NULL);
444
445 g_signal_connect (icon_list, "item_activated",
446 G_CALLBACK (item_activated), NULL);
447
448 model = create_model ();
449 gtk_icon_view_set_model (GTK_ICON_VIEW (icon_list), model);
450 gtk_tree_view_set_model (GTK_TREE_VIEW (tv), model);
451 fill_model (model);
452
453#if 0
454
455 gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (icon_list), 0);
456 gtk_icon_view_set_text_column (GTK_ICON_VIEW (icon_list), 1);
457
458#else
459
460 cell = gtk_cell_renderer_toggle_new ();
461 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_list), cell, FALSE);
462 g_object_set (object: cell, first_property_name: "activatable", TRUE, NULL);
463 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_list),
464 cell, "active", 4, NULL);
465 g_signal_connect (cell, "toggled", G_CALLBACK (toggled), model);
466
467 cell = gtk_cell_renderer_pixbuf_new ();
468 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_list), cell, FALSE);
469 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_list),
470 cell, "pixbuf", 0, NULL);
471
472 cell = gtk_cell_renderer_text_new ();
473 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_list), cell, FALSE);
474 g_object_set (object: cell,
475 first_property_name: "editable", TRUE,
476 "xalign", 0.5,
477 "wrap-mode", PANGO_WRAP_WORD_CHAR,
478 "wrap-width", 100,
479 NULL);
480 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_list),
481 cell, "text", 1, NULL);
482 g_signal_connect (cell, "edited", G_CALLBACK (edited), model);
483
484 /* now the tree view... */
485 cell = gtk_cell_renderer_toggle_new ();
486 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tvc), cell, FALSE);
487 g_object_set (object: cell, first_property_name: "activatable", TRUE, NULL);
488 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (tvc),
489 cell, "active", 4, NULL);
490 g_signal_connect (cell, "toggled", G_CALLBACK (toggled), model);
491
492 cell = gtk_cell_renderer_pixbuf_new ();
493 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tvc), cell, FALSE);
494 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (tvc),
495 cell, "pixbuf", 0, NULL);
496
497 cell = gtk_cell_renderer_text_new ();
498 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tvc), cell, FALSE);
499 g_object_set (object: cell, first_property_name: "editable", TRUE, NULL);
500 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (tvc),
501 cell, "text", 1, NULL);
502 g_signal_connect (cell, "edited", G_CALLBACK (edited), model);
503#endif
504 /* Allow DND between the icon view and the tree view */
505
506 targets = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA);
507 gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (icon_list),
508 start_button_mask: GDK_BUTTON1_MASK,
509 formats: targets,
510 actions: GDK_ACTION_MOVE);
511 gtk_icon_view_enable_model_drag_dest (GTK_ICON_VIEW (icon_list),
512 formats: targets,
513 actions: GDK_ACTION_MOVE);
514
515 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tv),
516 start_button_mask: GDK_BUTTON1_MASK,
517 formats: targets,
518 actions: GDK_ACTION_MOVE);
519 gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (tv),
520 formats: targets,
521 actions: GDK_ACTION_MOVE);
522 gdk_content_formats_unref (formats: targets);
523
524 scrolled_window = gtk_scrolled_window_new ();
525 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), child: icon_list);
526 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
527 hscrollbar_policy: GTK_POLICY_AUTOMATIC, vscrollbar_policy: GTK_POLICY_AUTOMATIC);
528
529 gtk_paned_set_start_child (GTK_PANED (paned), child: scrolled_window);
530
531 scrolled_window = gtk_scrolled_window_new ();
532 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), child: tv);
533 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
534 hscrollbar_policy: GTK_POLICY_AUTOMATIC, vscrollbar_policy: GTK_POLICY_AUTOMATIC);
535
536 gtk_paned_set_end_child (GTK_PANED (paned), child: scrolled_window);
537
538 bbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
539 gtk_widget_set_halign (widget: bbox, align: GTK_ALIGN_START);
540 gtk_box_append (GTK_BOX (vbox), child: bbox);
541
542 button = gtk_button_new_with_label (label: "Add some");
543 g_signal_connect (button, "clicked", G_CALLBACK (add_some), icon_list);
544 gtk_box_append (GTK_BOX (bbox), child: button);
545
546 button = gtk_button_new_with_label (label: "Add many");
547 g_signal_connect (button, "clicked", G_CALLBACK (add_many), icon_list);
548 gtk_box_append (GTK_BOX (bbox), child: button);
549
550 button = gtk_button_new_with_label (label: "Add large");
551 g_signal_connect (button, "clicked", G_CALLBACK (add_large), icon_list);
552 gtk_box_append (GTK_BOX (bbox), child: button);
553
554 button = gtk_button_new_with_label (label: "Remove selected");
555 g_signal_connect (button, "clicked", G_CALLBACK (foreach_selected_remove), icon_list);
556 gtk_box_append (GTK_BOX (bbox), child: button);
557
558 button = gtk_button_new_with_label (label: "Swap");
559 g_signal_connect (button, "clicked", G_CALLBACK (swap_rows), icon_list);
560 gtk_box_append (GTK_BOX (bbox), child: button);
561
562 bbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
563 gtk_widget_set_halign (widget: bbox, align: GTK_ALIGN_START);
564 gtk_box_append (GTK_BOX (vbox), child: bbox);
565
566 button = gtk_button_new_with_label (label: "Select all");
567 g_signal_connect (button, "clicked", G_CALLBACK (select_all), icon_list);
568 gtk_box_append (GTK_BOX (bbox), child: button);
569
570 button = gtk_button_new_with_label (label: "Unselect all");
571 g_signal_connect (button, "clicked", G_CALLBACK (unselect_all), icon_list);
572 gtk_box_append (GTK_BOX (bbox), child: button);
573
574 button = gtk_button_new_with_label (label: "Select nonexisting");
575 g_signal_connect (button, "clicked", G_CALLBACK (select_nonexisting), icon_list);
576 gtk_box_append (GTK_BOX (bbox), child: button);
577
578 icon_list = gtk_icon_view_new ();
579
580 scrolled_window = gtk_scrolled_window_new ();
581 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), child: icon_list);
582 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
583 hscrollbar_policy: GTK_POLICY_AUTOMATIC, vscrollbar_policy: GTK_POLICY_AUTOMATIC);
584 gtk_paned_set_end_child (GTK_PANED (paned), child: scrolled_window);
585
586 gtk_widget_show (widget: window);
587
588 while (TRUE)
589 g_main_context_iteration (NULL, TRUE);
590
591 return 0;
592}
593

source code of gtk/tests/testiconview.c