1#include <string.h>
2#include "iconbrowserapp.h"
3#include "iconbrowserwin.h"
4#include "iconbrowsericon.h"
5#include "iconbrowsercontext.h"
6#include <gtk/gtk.h>
7
8
9struct _IconBrowserWindow
10{
11 GtkApplicationWindow parent;
12
13 GtkWidget *symbolic_radio;
14 GtkWidget *searchbar;
15 GListModel *icon_filter_model;
16 GListStore *icon_store;
17 GListModel *context_model;
18 GListStore *context_store;
19 GtkFilter *name_filter;
20 GtkFilter *search_mode_filter;
21 GtkWidget *details;
22 GtkWidget *image1;
23 GtkWidget *image2;
24 GtkWidget *image3;
25 GtkWidget *image4;
26 GtkWidget *image5;
27 GtkWidget *image6;
28 GtkWidget *image7;
29 GtkWidget *image8;
30 GtkWidget *label8;
31 GtkWidget *description;
32};
33
34struct _IconBrowserWindowClass
35{
36 GtkApplicationWindowClass parent_class;
37};
38
39G_DEFINE_TYPE(IconBrowserWindow, icon_browser_window, GTK_TYPE_APPLICATION_WINDOW);
40
41static GtkIconTheme *
42icon_browser_window_get_icon_theme (IconBrowserWindow *win)
43{
44 return gtk_icon_theme_get_for_display (display: gtk_widget_get_display (GTK_WIDGET (win)));
45}
46
47static void
48add_icon (IconBrowserWindow *win,
49 const char *name,
50 const char *description,
51 const char *context)
52{
53 GtkIconTheme *icon_theme = icon_browser_window_get_icon_theme (win);
54 char *regular_name;
55 char *symbolic_name;
56 IbIcon *icon;
57
58 regular_name = g_strdup (str: name);
59 if (!gtk_icon_theme_has_icon (self: icon_theme, icon_name: regular_name))
60 {
61 g_free (mem: regular_name);
62 regular_name = NULL;
63 }
64
65 symbolic_name = g_strconcat (string1: name, "-symbolic", NULL);
66 if (!gtk_icon_theme_has_icon (self: icon_theme, icon_name: symbolic_name))
67 {
68 g_free (mem: symbolic_name);
69 symbolic_name = NULL;
70 }
71
72 icon = ib_icon_new (regular_name, symbolic_name, description, context);
73 g_object_bind_property (source: win->symbolic_radio, source_property: "active",
74 target: icon, target_property: "use-symbolic",
75 flags: G_BINDING_DEFAULT);
76 g_list_store_append (store: win->icon_store, item: icon);
77 g_object_unref (object: icon);
78}
79
80static void
81add_context (IconBrowserWindow *win,
82 const char *id,
83 const char *name,
84 const char *description)
85{
86 IbContext *context;
87
88 context = ib_context_new (id, name, description);
89 g_list_store_append (store: win->context_store, item: context);
90 g_object_unref (object: context);
91}
92
93static void
94populate (IconBrowserWindow *win)
95{
96 GFile *file;
97 GKeyFile *kf;
98 char *data;
99 gsize length;
100 char **groups;
101 int i;
102
103 file = g_file_new_for_uri (uri: "resource:/org/gtk/iconbrowser/gtk/icon.list");
104 g_file_load_contents (file, NULL, contents: &data, length: &length, NULL, NULL);
105
106 kf = g_key_file_new ();
107 g_key_file_load_from_data (key_file: kf, data, length, flags: G_KEY_FILE_NONE, NULL);
108
109 groups = g_key_file_get_groups (key_file: kf, length: &length);
110 for (i = 0; i < length; i++)
111 {
112 const char *context;
113 const char *name;
114 const char *description;
115 char **keys;
116 gsize len;
117 int j;
118
119 context = groups[i];
120 name = g_key_file_get_string (key_file: kf, group_name: context, key: "Name", NULL);
121 description = g_key_file_get_string (key_file: kf, group_name: context, key: "Description", NULL);
122 add_context (win, id: context, name, description);
123
124 keys = g_key_file_get_keys (key_file: kf, group_name: context, length: &len, NULL);
125 for (j = 0; j < len; j++)
126 {
127 const char *key = keys[j];
128 const char *value;
129
130 if (strcmp (s1: key, s2: "Name") == 0 || strcmp (s1: key, s2: "Description") == 0)
131 continue;
132
133 value = g_key_file_get_string (key_file: kf, group_name: context, key, NULL);
134
135 add_icon (win, name: key, description: value, context);
136 }
137 g_strfreev (str_array: keys);
138 }
139 g_strfreev (str_array: groups);
140}
141
142static gboolean
143filter_by_icon_name (gpointer item,
144 gpointer data)
145{
146 return ib_icon_get_name (icon: IB_ICON (ptr: item)) != NULL;
147}
148
149static void
150symbolic_toggled (IconBrowserWindow *win)
151{
152 gtk_filter_changed (self: win->name_filter, change: GTK_FILTER_CHANGE_DIFFERENT);
153}
154
155static void
156copy_to_clipboard (GtkButton *button,
157 IconBrowserWindow *win)
158{
159 GdkClipboard *clipboard;
160
161 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (win));
162 gdk_clipboard_set_text (clipboard, text: gtk_window_get_title (GTK_WINDOW (win->details)));
163}
164
165static void
166set_image (GtkWidget *image, const char *name, int size)
167{
168 gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name: name);
169 gtk_image_set_pixel_size (GTK_IMAGE (image), pixel_size: size);
170}
171
172static void
173item_activated (GtkGridView *view,
174 guint position,
175 IconBrowserWindow *win)
176{
177 GListModel *model = G_LIST_MODEL (ptr: gtk_grid_view_get_model (self: view));
178 IbIcon *icon = g_list_model_get_item (list: model, position);
179 const char *name;
180 const char *description;
181 gboolean symbolic;
182
183 name = ib_icon_get_name (icon);
184 description = ib_icon_get_description (icon);
185 symbolic = ib_icon_get_use_symbolic (icon);
186
187 gtk_window_set_title (GTK_WINDOW (win->details), title: name);
188 set_image (image: win->image1, name, size: 8);
189 set_image (image: win->image2, name, size: 16);
190 set_image (image: win->image3, name, size: 18);
191 set_image (image: win->image4, name, size: 24);
192 set_image (image: win->image5, name, size: 32);
193 set_image (image: win->image6, name, size: 48);
194 set_image (image: win->image7, name, size: 64);
195 if (symbolic)
196 {
197 gtk_widget_show (widget: win->image8);
198 gtk_widget_show (widget: win->label8);
199 set_image (image: win->image8, name, size: 64);
200 }
201 else
202 {
203 gtk_widget_hide (widget: win->image8);
204 gtk_widget_hide (widget: win->label8);
205 }
206 if (description && description[0])
207 {
208 gtk_label_set_text (GTK_LABEL (win->description), str: description);
209 gtk_widget_show (widget: win->description);
210 }
211 else
212 {
213 gtk_widget_hide (widget: win->description);
214 }
215
216 gtk_window_present (GTK_WINDOW (win->details));
217
218 g_object_unref (object: icon);
219}
220
221static GdkPaintable *
222get_image_paintable (GtkImage *image)
223{
224 const char *icon_name;
225 GtkIconTheme *icon_theme;
226 GtkIconPaintable *icon;
227 int size;
228
229 switch (gtk_image_get_storage_type (image))
230 {
231 case GTK_IMAGE_PAINTABLE:
232 return g_object_ref (gtk_image_get_paintable (image));
233 case GTK_IMAGE_ICON_NAME:
234 icon_name = gtk_image_get_icon_name (image);
235 size = gtk_image_get_pixel_size (image);
236 icon_theme = gtk_icon_theme_get_for_display (display: gtk_widget_get_display (GTK_WIDGET (image)));
237 icon = gtk_icon_theme_lookup_icon (self: icon_theme,
238 icon_name,
239 NULL,
240 size, scale: 1,
241 direction: gtk_widget_get_direction (GTK_WIDGET (image)),
242 flags: 0);
243 if (icon == NULL)
244 {
245g_print (format: "no icon for %s\n", icon_name);
246 return NULL;
247 }
248 return GDK_PAINTABLE (ptr: icon);
249 case GTK_IMAGE_GICON:
250 case GTK_IMAGE_EMPTY:
251 default:
252 g_warning ("Image storage type %d not handled",
253 gtk_image_get_storage_type (image));
254 return NULL;
255 }
256}
257
258static void
259drag_begin (GtkDragSource *source,
260 GdkDrag *drag,
261 GtkWidget *widget)
262{
263 GdkPaintable *paintable;
264
265 paintable = get_image_paintable (GTK_IMAGE (widget));
266 if (paintable)
267 {
268 int w, h;
269
270 w = gdk_paintable_get_intrinsic_width (paintable);
271 h = gdk_paintable_get_intrinsic_height (paintable);
272 gtk_drag_source_set_icon (source, paintable, hot_x: w, hot_y: h);
273 g_object_unref (object: paintable);
274 }
275}
276
277static GdkContentProvider *
278drag_prepare_texture (GtkDragSource *source,
279 double x,
280 double y,
281 GtkWidget *widget)
282{
283 GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (widget));
284 GtkSnapshot *snapshot;
285 double width, height;
286 GskRenderNode *node;
287 GskRenderer *renderer;
288 GdkTexture *texture;
289 GdkContentProvider *ret;
290
291 if (!GDK_IS_PAINTABLE (ptr: paintable))
292 return NULL;
293
294 snapshot = gtk_snapshot_new ();
295 width = gdk_paintable_get_intrinsic_width (paintable);
296 height = gdk_paintable_get_intrinsic_height (paintable);
297 gdk_paintable_snapshot (paintable, snapshot, width, height);
298 node = gtk_snapshot_free_to_node (snapshot);
299
300 renderer = gtk_native_get_renderer (self: gtk_widget_get_native (widget));
301 texture = gsk_renderer_render_texture (renderer, root: node, viewport: &GRAPHENE_RECT_INIT (0, 0, width, height));
302
303 ret = gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, texture);
304
305 g_object_unref (object: texture);
306 gsk_render_node_unref (node);
307
308 return ret;
309}
310
311static GdkContentProvider *
312drag_prepare_file (GtkDragSource *source,
313 double x,
314 double y,
315 GtkWidget *widget)
316{
317 GdkContentProvider *content;
318 GtkIconTheme *icon_theme;
319 const char *name;
320 GtkIconPaintable *info;
321
322 name = gtk_image_get_icon_name (GTK_IMAGE (widget));
323 icon_theme = gtk_icon_theme_get_for_display (display: gtk_widget_get_display (widget));
324
325 info = gtk_icon_theme_lookup_icon (self: icon_theme,
326 icon_name: name,
327 NULL,
328 size: 32, scale: 1,
329 direction: gtk_widget_get_direction (widget),
330 flags: 0);
331 content = gdk_content_provider_new_typed (G_TYPE_FILE, gtk_icon_paintable_get_file (self: info));
332 g_object_unref (object: info);
333
334 return content;
335}
336
337static void
338setup_image_dnd (GtkWidget *image)
339{
340 GtkDragSource *source;
341
342 source = gtk_drag_source_new ();
343 g_signal_connect (source, "prepare", G_CALLBACK (drag_prepare_texture), image);
344 g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
345 gtk_widget_add_controller (widget: image, GTK_EVENT_CONTROLLER (source));
346}
347
348static void
349setup_scalable_image_dnd (GtkWidget *image)
350{
351 GtkDragSource *source;
352
353 source = gtk_drag_source_new ();
354 g_signal_connect (source, "prepare", G_CALLBACK (drag_prepare_file), image);
355 g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
356 gtk_widget_add_controller (widget: image, GTK_EVENT_CONTROLLER (source));
357}
358
359static void
360search_mode_toggled (GtkSearchBar *searchbar,
361 GParamSpec *pspec,
362 IconBrowserWindow *win)
363{
364 if (gtk_search_bar_get_search_mode (bar: searchbar))
365 gtk_single_selection_set_selected (self: GTK_SINGLE_SELECTION (ptr: win->context_model), GTK_INVALID_LIST_POSITION);
366 else if (gtk_single_selection_get_selected (self: GTK_SINGLE_SELECTION (ptr: win->context_model)) == GTK_INVALID_LIST_POSITION)
367 gtk_single_selection_set_selected (self: GTK_SINGLE_SELECTION (ptr: win->context_model), position: 0);
368
369 gtk_filter_changed (self: win->search_mode_filter, change: GTK_FILTER_CHANGE_DIFFERENT);
370}
371
372static void
373selected_name_changed (GtkSingleSelection *selection,
374 GParamSpec *pspec,
375 IconBrowserWindow *win)
376{
377 if (gtk_single_selection_get_selected (self: selection) != GTK_INVALID_LIST_POSITION)
378 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (win->searchbar), FALSE);
379}
380
381static void
382icon_browser_window_init (IconBrowserWindow *win)
383{
384 GtkFilter *filter;
385
386 gtk_widget_init_template (GTK_WIDGET (win));
387
388 setup_image_dnd (win->image1);
389 setup_image_dnd (win->image2);
390 setup_image_dnd (win->image3);
391 setup_image_dnd (win->image4);
392 setup_image_dnd (win->image5);
393 setup_image_dnd (win->image6);
394 setup_image_dnd (win->image7);
395 setup_scalable_image_dnd (win->image8);
396
397 gtk_window_set_transient_for (GTK_WINDOW (win->details), GTK_WINDOW (win));
398 gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (win->searchbar), GTK_WIDGET (win));
399
400 populate (win);
401
402 filter = gtk_filter_list_model_get_filter (self: GTK_FILTER_LIST_MODEL (ptr: win->icon_filter_model));
403
404 win->name_filter = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: filter_by_icon_name, NULL, NULL));
405
406 gtk_multi_filter_append (self: GTK_MULTI_FILTER (ptr: filter), g_object_ref (win->name_filter));
407
408 g_signal_connect (win->searchbar, "notify::search-mode-enabled", G_CALLBACK (search_mode_toggled), win);
409 g_signal_connect (win->context_model, "notify::selected", G_CALLBACK (selected_name_changed), win);
410}
411
412static void
413icon_browser_window_finalize (GObject *object)
414{
415 IconBrowserWindow *win = ICON_BROWSER_WINDOW (object);
416
417 g_clear_object (&win->name_filter);
418
419 G_OBJECT_CLASS (icon_browser_window_parent_class)->finalize (object);
420}
421
422static void
423icon_browser_window_class_init (IconBrowserWindowClass *class)
424{
425 GObjectClass *object_class = G_OBJECT_CLASS (class);
426
427 object_class->finalize = icon_browser_window_finalize;
428
429 g_type_ensure (IB_TYPE_ICON);
430 g_type_ensure (IB_TYPE_CONTEXT);
431
432 gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
433 resource_name: "/org/gtk/iconbrowser/gtk/window.ui");
434
435 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, symbolic_radio);
436 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, searchbar);
437 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, icon_store);
438 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, icon_filter_model);
439 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, context_model);
440 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, context_store);
441
442 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, details);
443 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image1);
444 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image2);
445 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image3);
446 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image4);
447 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image5);
448 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image6);
449 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image7);
450 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, image8);
451 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, label8);
452 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, description);
453 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, search_mode_filter);
454
455 gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), item_activated);
456 gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), copy_to_clipboard);
457 gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), symbolic_toggled);
458}
459
460IconBrowserWindow *
461icon_browser_window_new (IconBrowserApp *app)
462{
463 return g_object_new (ICON_BROWSER_WINDOW_TYPE, first_property_name: "application", app, NULL);
464}
465

source code of gtk/demos/icon-browser/iconbrowserwin.c