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