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
9 struct _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
34 struct _IconBrowserWindowClass
35 {
36 GtkApplicationWindowClass parent_class;
37 };
38
39 G_DEFINE_TYPE(IconBrowserWindow, icon_browser_window, GTK_TYPE_APPLICATION_WINDOW);
40
41 static GtkIconTheme *
icon_browser_window_get_icon_theme(IconBrowserWindow * win)42 icon_browser_window_get_icon_theme (IconBrowserWindow *win)
43 {
44 return gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (win)));
45 }
46
47 static void
add_icon(IconBrowserWindow * win,const char * name,const char * description,const char * context)48 add_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 (name);
59 if (!gtk_icon_theme_has_icon (icon_theme, regular_name))
60 {
61 g_free (regular_name);
62 regular_name = NULL;
63 }
64
65 symbolic_name = g_strconcat (name, "-symbolic", NULL);
66 if (!gtk_icon_theme_has_icon (icon_theme, symbolic_name))
67 {
68 g_free (symbolic_name);
69 symbolic_name = NULL;
70 }
71
72 icon = ib_icon_new (regular_name, symbolic_name, description, context);
73 g_object_bind_property (win->symbolic_radio, "active",
74 icon, "use-symbolic",
75 G_BINDING_DEFAULT);
76 g_list_store_append (win->icon_store, icon);
77 g_object_unref (icon);
78 }
79
80 static void
add_context(IconBrowserWindow * win,const char * id,const char * name,const char * description)81 add_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 (win->context_store, context);
90 g_object_unref (context);
91 }
92
93 static void
populate(IconBrowserWindow * win)94 populate (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 ("resource:/org/gtk/iconbrowser/gtk/icon.list");
104 g_file_load_contents (file, NULL, &data, &length, NULL, NULL);
105
106 kf = g_key_file_new ();
107 g_key_file_load_from_data (kf, data, length, G_KEY_FILE_NONE, NULL);
108
109 groups = g_key_file_get_groups (kf, &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 (kf, context, "Name", NULL);
121 description = g_key_file_get_string (kf, context, "Description", NULL);
122 add_context (win, context, name, description);
123
124 keys = g_key_file_get_keys (kf, context, &len, NULL);
125 for (j = 0; j < len; j++)
126 {
127 const char *key = keys[j];
128 const char *value;
129
130 if (strcmp (key, "Name") == 0 || strcmp (key, "Description") == 0)
131 continue;
132
133 value = g_key_file_get_string (kf, context, key, NULL);
134
135 add_icon (win, key, value, context);
136 }
137 g_strfreev (keys);
138 }
139 g_strfreev (groups);
140 }
141
142 static gboolean
filter_by_icon_name(gpointer item,gpointer data)143 filter_by_icon_name (gpointer item,
144 gpointer data)
145 {
146 return ib_icon_get_name (IB_ICON (item)) != NULL;
147 }
148
149 static void
symbolic_toggled(IconBrowserWindow * win)150 symbolic_toggled (IconBrowserWindow *win)
151 {
152 gtk_filter_changed (win->name_filter, GTK_FILTER_CHANGE_DIFFERENT);
153 }
154
155 static void
copy_to_clipboard(GtkButton * button,IconBrowserWindow * win)156 copy_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, gtk_window_get_title (GTK_WINDOW (win->details)));
163 }
164
165 static void
set_image(GtkWidget * image,const char * name,int size)166 set_image (GtkWidget *image, const char *name, int size)
167 {
168 gtk_image_set_from_icon_name (GTK_IMAGE (image), name);
169 gtk_image_set_pixel_size (GTK_IMAGE (image), size);
170 }
171
172 static void
item_activated(GtkGridView * view,guint position,IconBrowserWindow * win)173 item_activated (GtkGridView *view,
174 guint position,
175 IconBrowserWindow *win)
176 {
177 GListModel *model = G_LIST_MODEL (gtk_grid_view_get_model (view));
178 IbIcon *icon = g_list_model_get_item (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), name);
188 set_image (win->image1, name, 8);
189 set_image (win->image2, name, 16);
190 set_image (win->image3, name, 18);
191 set_image (win->image4, name, 24);
192 set_image (win->image5, name, 32);
193 set_image (win->image6, name, 48);
194 set_image (win->image7, name, 64);
195 if (symbolic)
196 {
197 gtk_widget_show (win->image8);
198 gtk_widget_show (win->label8);
199 set_image (win->image8, name, 64);
200 }
201 else
202 {
203 gtk_widget_hide (win->image8);
204 gtk_widget_hide (win->label8);
205 }
206 if (description && description[0])
207 {
208 gtk_label_set_text (GTK_LABEL (win->description), description);
209 gtk_widget_show (win->description);
210 }
211 else
212 {
213 gtk_widget_hide (win->description);
214 }
215
216 gtk_window_present (GTK_WINDOW (win->details));
217
218 g_object_unref (icon);
219 }
220
221 static GdkPaintable *
get_image_paintable(GtkImage * image)222 get_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 (gtk_widget_get_display (GTK_WIDGET (image)));
237 icon = gtk_icon_theme_lookup_icon (icon_theme,
238 icon_name,
239 NULL,
240 size, 1,
241 gtk_widget_get_direction (GTK_WIDGET (image)),
242 0);
243 if (icon == NULL)
244 {
245 g_print ("no icon for %s\n", icon_name);
246 return NULL;
247 }
248 return GDK_PAINTABLE (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
258 static void
drag_begin(GtkDragSource * source,GdkDrag * drag,GtkWidget * widget)259 drag_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, w, h);
273 g_object_unref (paintable);
274 }
275 }
276
277 static GdkContentProvider *
drag_prepare_texture(GtkDragSource * source,double x,double y,GtkWidget * widget)278 drag_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 (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 (gtk_widget_get_native (widget));
301 texture = gsk_renderer_render_texture (renderer, node, &GRAPHENE_RECT_INIT (0, 0, width, height));
302
303 ret = gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, texture);
304
305 g_object_unref (texture);
306 gsk_render_node_unref (node);
307
308 return ret;
309 }
310
311 static GdkContentProvider *
drag_prepare_file(GtkDragSource * source,double x,double y,GtkWidget * widget)312 drag_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 (gtk_widget_get_display (widget));
324
325 info = gtk_icon_theme_lookup_icon (icon_theme,
326 name,
327 NULL,
328 32, 1,
329 gtk_widget_get_direction (widget),
330 0);
331 content = gdk_content_provider_new_typed (G_TYPE_FILE, gtk_icon_paintable_get_file (info));
332 g_object_unref (info);
333
334 return content;
335 }
336
337 static void
setup_image_dnd(GtkWidget * image)338 setup_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 (image, GTK_EVENT_CONTROLLER (source));
346 }
347
348 static void
setup_scalable_image_dnd(GtkWidget * image)349 setup_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 (image, GTK_EVENT_CONTROLLER (source));
357 }
358
359 static void
search_mode_toggled(GtkSearchBar * searchbar,GParamSpec * pspec,IconBrowserWindow * win)360 search_mode_toggled (GtkSearchBar *searchbar,
361 GParamSpec *pspec,
362 IconBrowserWindow *win)
363 {
364 if (gtk_search_bar_get_search_mode (searchbar))
365 gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (win->context_model), GTK_INVALID_LIST_POSITION);
366 else if (gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (win->context_model)) == GTK_INVALID_LIST_POSITION)
367 gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (win->context_model), 0);
368
369 gtk_filter_changed (win->search_mode_filter, GTK_FILTER_CHANGE_DIFFERENT);
370 }
371
372 static void
selected_name_changed(GtkSingleSelection * selection,GParamSpec * pspec,IconBrowserWindow * win)373 selected_name_changed (GtkSingleSelection *selection,
374 GParamSpec *pspec,
375 IconBrowserWindow *win)
376 {
377 if (gtk_single_selection_get_selected (selection) != GTK_INVALID_LIST_POSITION)
378 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (win->searchbar), FALSE);
379 }
380
381 static void
icon_browser_window_init(IconBrowserWindow * win)382 icon_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 (GTK_FILTER_LIST_MODEL (win->icon_filter_model));
403
404 win->name_filter = GTK_FILTER (gtk_custom_filter_new (filter_by_icon_name, NULL, NULL));
405
406 gtk_multi_filter_append (GTK_MULTI_FILTER (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
412 static void
icon_browser_window_finalize(GObject * object)413 icon_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
422 static void
icon_browser_window_class_init(IconBrowserWindowClass * class)423 icon_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 "/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
460 IconBrowserWindow *
icon_browser_window_new(IconBrowserApp * app)461 icon_browser_window_new (IconBrowserApp *app)
462 {
463 return g_object_new (ICON_BROWSER_WINDOW_TYPE, "application", app, NULL);
464 }
465