1 /*
2  * Copyright © 2009-2018 Siyan Panayotov <contact@siyanpanayotov.com>
3  *
4  * This file is part of Viewnior.
5  *
6  * Viewnior is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Viewnior is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Viewnior.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <libintl.h>
21 #include <glib/gi18n.h>
22 #define _(String) gettext (String)
23 
24 #include "vnr-properties-dialog.h"
25 #include "vnr-file.h"
26 #include "vnr-tools.h"
27 #include "uni-exiv2.hpp"
28 
29 G_DEFINE_TYPE (VnrPropertiesDialog, vnr_properties_dialog, GTK_TYPE_DIALOG);
30 
31 static void vnr_properties_dialog_update_metadata(VnrPropertiesDialog *dialog);
32 
33 static gboolean
key_press_cb(GtkWidget * widget,GdkEventKey * event,gpointer user_data)34 key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
35 {
36     if(event->keyval == GDK_KEY_Escape)
37     {
38         gtk_widget_hide(widget);
39         return TRUE;
40     }
41     else
42         return FALSE;
43 }
44 
45 static void
get_file_info(gchar * filename,goffset * size,const gchar ** type)46 get_file_info(gchar *filename, goffset *size, const gchar **type)
47 {
48     GFile *file;
49     GFileInfo *fileinfo;
50 
51     file = g_file_new_for_path(filename);
52     fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE","
53                                   G_FILE_ATTRIBUTE_STANDARD_SIZE,
54                                   0, NULL, NULL);
55 
56     if (fileinfo == NULL)
57     {
58         return;
59     }
60 
61     *size = g_file_info_get_size(fileinfo);
62     *type = g_strdup(g_file_info_get_content_type(fileinfo));
63 
64     g_object_unref(file);
65     g_object_unref(fileinfo);
66 }
67 
68 static void
set_new_pixbuf(VnrPropertiesDialog * dialog,GdkPixbuf * original)69 set_new_pixbuf(VnrPropertiesDialog *dialog, GdkPixbuf* original)
70 {
71     if(dialog->thumbnail != NULL)
72     {
73         g_object_unref(dialog->thumbnail);
74         dialog->thumbnail = NULL;
75     }
76 
77     if(original == NULL)
78     {
79         gtk_image_set_from_stock(GTK_IMAGE(dialog->image), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_DIALOG);
80         return;
81     }
82 
83     int width, height;
84 
85     width = gdk_pixbuf_get_width (original);
86     height = gdk_pixbuf_get_height (original);
87 
88     vnr_tools_fit_to_size(&height, &width, 100,100);
89 
90     dialog->thumbnail = gdk_pixbuf_scale_simple (original, width, height,
91                                                  GDK_INTERP_NEAREST);
92 }
93 
94 static void
vnr_properties_dialog_class_init(VnrPropertiesDialogClass * klass)95 vnr_properties_dialog_class_init (VnrPropertiesDialogClass * klass) {}
96 
97 GtkWidget *
vnr_properties_dialog_new(VnrWindow * vnr_win,GtkAction * next_action,GtkAction * prev_action)98 vnr_properties_dialog_new (VnrWindow *vnr_win, GtkAction *next_action, GtkAction *prev_action)
99 {
100     VnrPropertiesDialog *dialog;
101 
102     dialog = g_object_new (VNR_TYPE_PROPERTIES_DIALOG, NULL);
103 
104     dialog->thumbnail = NULL;
105     dialog->vnr_win = vnr_win;
106 
107 	gtk_activatable_set_related_action (GTK_ACTIVATABLE(dialog->next_button), next_action);
108 
109     gtk_activatable_set_related_action (GTK_ACTIVATABLE(dialog->prev_button), prev_action);
110 
111     gtk_button_set_label (GTK_BUTTON(dialog->next_button), _("_Next"));
112     gtk_button_set_label (GTK_BUTTON(dialog->prev_button), _("_Previous"));
113     gtk_widget_grab_focus (dialog->close_button);
114 
115     return (GtkWidget *) dialog;
116 }
117 
118 
119 static void
vnr_properties_dialog_init(VnrPropertiesDialog * dialog)120 vnr_properties_dialog_init (VnrPropertiesDialog * dialog)
121 {
122     GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
123     GtkWidget *action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
124     GtkWidget *temp_box;
125     GtkWidget *temp_label;
126 
127     gtk_window_set_title(GTK_WINDOW(dialog), _("Image Properties"));
128 
129     /* VBox containing the Location labels */
130     temp_box = gtk_vbox_new(FALSE,0);
131     gtk_container_set_border_width (GTK_CONTAINER(temp_box), 10);
132     gtk_box_pack_start (GTK_BOX(content_area), temp_box, FALSE,FALSE,0);
133 
134     temp_label = gtk_label_new(NULL);
135     gtk_label_set_markup(GTK_LABEL(temp_label), _("<b>Location:</b>"));
136     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
137     gtk_box_pack_start (GTK_BOX (temp_box), temp_label, FALSE,FALSE,0);
138 
139     dialog->location_label = gtk_label_new(NULL);
140     gtk_misc_set_alignment (GTK_MISC(dialog->location_label), 0, 0);
141     gtk_label_set_selectable (GTK_LABEL(dialog->location_label), TRUE);
142     gtk_label_set_ellipsize (GTK_LABEL(dialog->location_label), PANGO_ELLIPSIZE_END);
143     gtk_box_pack_start (GTK_BOX (temp_box), dialog->location_label, FALSE,FALSE,0);
144 
145     /* VBox containing the image and meta data */
146     dialog->layout = gtk_vbox_new(FALSE,10);
147     gtk_container_set_border_width (GTK_CONTAINER(dialog->layout), 10);
148     gtk_box_pack_start ( GTK_BOX (content_area) , dialog->layout, FALSE,FALSE,0);
149 
150     /* HBox containing the image and the two columns with labels */
151     dialog->image_layout = gtk_hbox_new(FALSE,10);
152     gtk_container_set_border_width (GTK_CONTAINER(dialog->image_layout), 10);
153     gtk_box_pack_start ( GTK_BOX (dialog->layout) , dialog->image_layout, FALSE,FALSE,0);
154 
155     /* The frame around the image */
156     temp_box = gtk_frame_new(NULL);
157     gtk_widget_set_size_request (temp_box, 105, 105);
158     gtk_box_pack_start (GTK_BOX (dialog->image_layout), temp_box, FALSE,FALSE,0);
159 
160     dialog->image = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_DIALOG);
161     gtk_container_add (GTK_CONTAINER (temp_box), dialog->image);
162 
163 
164     /* Buttons */
165     dialog->prev_button = gtk_button_new();
166     gtk_button_set_image (GTK_BUTTON(dialog->prev_button),
167                           gtk_image_new_from_stock(GTK_STOCK_GO_BACK, GTK_ICON_SIZE_BUTTON));
168     gtk_container_add (GTK_CONTAINER (action_area), dialog->prev_button);
169 
170     dialog->next_button = gtk_button_new();
171     gtk_button_set_image (GTK_BUTTON(dialog->next_button),
172                           gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON));
173     gtk_container_add (GTK_CONTAINER (action_area), dialog->next_button);
174 
175     dialog->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
176     gtk_container_add (GTK_CONTAINER (action_area), dialog->close_button);
177 
178 
179     /* Image Data Labels */
180     temp_box = gtk_vbox_new(FALSE,0);
181     gtk_box_pack_start (GTK_BOX (dialog->image_layout), temp_box, FALSE,FALSE,0);
182 
183     temp_label = gtk_label_new(NULL);
184     gtk_label_set_markup(GTK_LABEL(temp_label), _("<b>Name:</b>"));
185     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
186     gtk_box_pack_start (GTK_BOX (temp_box), temp_label, FALSE,FALSE,0);
187     temp_label = gtk_label_new(NULL);
188     gtk_label_set_markup(GTK_LABEL(temp_label), _("<b>Type:</b>"));
189     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
190     gtk_box_pack_start (GTK_BOX (temp_box), temp_label, FALSE,FALSE,0);
191     temp_label = gtk_label_new(NULL);
192     gtk_label_set_markup(GTK_LABEL(temp_label), _("<b>Size:</b>"));
193     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
194     gtk_box_pack_start (GTK_BOX (temp_box), temp_label, FALSE,FALSE,0);
195     temp_label = gtk_label_new(NULL);
196     gtk_label_set_markup(GTK_LABEL(temp_label), _("<b>Width:</b>"));
197     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
198     gtk_box_pack_start (GTK_BOX (temp_box), temp_label, FALSE,FALSE,0);
199     temp_label = gtk_label_new(NULL);
200     gtk_label_set_markup(GTK_LABEL(temp_label), _("<b>Height:</b>"));
201     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
202     gtk_box_pack_start (GTK_BOX (temp_box), temp_label, FALSE,FALSE,0);
203 
204 
205     temp_box = gtk_vbox_new(FALSE,0);
206     gtk_box_pack_start (GTK_BOX (dialog->image_layout), temp_box, FALSE,FALSE,0);
207 
208     dialog->name_label = gtk_label_new(NULL);
209     gtk_label_set_selectable (GTK_LABEL(dialog->name_label), TRUE);
210     gtk_misc_set_alignment (GTK_MISC(dialog->name_label), 0, 0);
211     gtk_box_pack_start (GTK_BOX (temp_box), dialog->name_label, FALSE,FALSE,0);
212     dialog->type_label = gtk_label_new(NULL);
213     gtk_label_set_selectable (GTK_LABEL(dialog->type_label), TRUE);
214     gtk_misc_set_alignment (GTK_MISC(dialog->type_label), 0, 0);
215     gtk_box_pack_start (GTK_BOX (temp_box), dialog->type_label, FALSE,FALSE,0);
216     dialog->size_label = gtk_label_new(NULL);
217     gtk_label_set_selectable (GTK_LABEL(dialog->size_label), TRUE);
218     gtk_misc_set_alignment (GTK_MISC(dialog->size_label), 0, 0);
219     gtk_box_pack_start (GTK_BOX (temp_box), dialog->size_label, FALSE,FALSE,0);
220     dialog->width_label = gtk_label_new(NULL);
221     gtk_label_set_selectable (GTK_LABEL(dialog->width_label), TRUE);
222     gtk_misc_set_alignment (GTK_MISC(dialog->width_label), 0, 0);
223     gtk_box_pack_start (GTK_BOX (temp_box), dialog->width_label, FALSE,FALSE,0);
224     dialog->height_label = gtk_label_new(NULL);
225     gtk_label_set_selectable (GTK_LABEL(dialog->height_label), TRUE);
226     gtk_misc_set_alignment (GTK_MISC(dialog->height_label), 0, 0);
227     gtk_box_pack_start (GTK_BOX (temp_box), dialog->height_label, FALSE,FALSE,0);
228 
229     /* Metadata Labels */
230 
231     temp_box = gtk_hbox_new(FALSE,10);
232     gtk_box_pack_start (GTK_BOX (dialog->layout), temp_box, FALSE,FALSE,0);
233 
234     dialog->meta_names_box = gtk_vbox_new(FALSE,0);
235     gtk_box_pack_start (GTK_BOX (temp_box), dialog->meta_names_box, FALSE,FALSE,0);
236 
237     dialog->meta_values_box = gtk_vbox_new(FALSE,0);
238     gtk_box_pack_start (GTK_BOX (temp_box), dialog->meta_values_box, FALSE,FALSE,0);
239 
240     /* Events and rest */
241 
242     g_signal_connect(G_OBJECT(dialog), "delete-event",
243                      G_CALLBACK(gtk_widget_hide_on_delete), NULL);
244     g_signal_connect_swapped(G_OBJECT(dialog->close_button), "clicked",
245                              G_CALLBACK(gtk_widget_hide_on_delete), dialog);
246 
247     g_signal_connect(G_OBJECT(dialog), "key-press-event", G_CALLBACK(key_press_cb), NULL);
248 
249     gtk_widget_show_all(content_area);
250     gtk_widget_show_all(action_area);
251 }
252 
253 void
vnr_properties_dialog_update(VnrPropertiesDialog * dialog)254 vnr_properties_dialog_update(VnrPropertiesDialog *dialog)
255 {
256     const gchar *filetype = NULL;
257     goffset filesize = 0;
258     gchar *filetype_desc = NULL;
259     gchar *filesize_str = NULL;
260 
261     get_file_info ((gchar*)VNR_FILE(dialog->vnr_win->file_list->data)->path,
262                    &filesize, &filetype);
263 
264     if(filetype == NULL && filesize == 0)
265     {
266         vnr_properties_dialog_clear(dialog);
267         return;
268     }
269 
270     vnr_properties_dialog_update_image(dialog);
271     vnr_properties_dialog_update_metadata(dialog);
272 
273     filesize_str = g_format_size (filesize);
274 
275     filetype_desc = g_content_type_get_description (filetype);
276 
277     gtk_label_set_text(GTK_LABEL(dialog->name_label),
278                        (gchar*)VNR_FILE(dialog->vnr_win->file_list->data)->display_name);
279 
280     gtk_label_set_text(GTK_LABEL(dialog->location_label),
281                        (gchar*)VNR_FILE(dialog->vnr_win->file_list->data)->path);
282 
283     gtk_label_set_text(GTK_LABEL(dialog->type_label), filetype_desc);
284     gtk_label_set_text(GTK_LABEL(dialog->size_label), filesize_str);
285 
286     g_free(filesize_str);
287     g_free((gchar*)filetype);
288     g_free(filetype_desc);
289 }
290 
291 static void
vnr_properties_dialog_clear_metadata(VnrPropertiesDialog * dialog)292 vnr_properties_dialog_clear_metadata(VnrPropertiesDialog *dialog)
293 {
294     GList *children;
295     GList *iter;
296 
297     children = gtk_container_get_children(GTK_CONTAINER(dialog->meta_values_box));
298     for(iter = children; iter != NULL; iter = g_list_next(iter)) {
299         gtk_widget_destroy(GTK_WIDGET(iter->data));
300     }
301     g_list_free(children);
302 
303     children = gtk_container_get_children(GTK_CONTAINER(dialog->meta_names_box));
304     for(iter = children; iter != NULL; iter = g_list_next(iter)) {
305         gtk_widget_destroy(GTK_WIDGET(iter->data));
306     }
307     g_list_free(children);
308 }
309 
310 static void
vnr_cb_add_metadata(const char * label,const char * value,void * user_data)311 vnr_cb_add_metadata(const char *label, const char *value, void *user_data) {
312     VnrPropertiesDialog *dialog = VNR_PROPERTIES_DIALOG(user_data);
313     GtkWidget *temp_label;
314     gchar *formatted_label;
315 
316     // value
317     temp_label = gtk_label_new(NULL);
318     gtk_label_set_text(GTK_LABEL(temp_label), value);
319     gtk_label_set_selectable (GTK_LABEL(temp_label), TRUE);
320     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
321     gtk_box_pack_start (GTK_BOX(dialog->meta_values_box), temp_label, FALSE,FALSE,0);
322 
323     gtk_widget_show(temp_label);
324 
325     // label
326     formatted_label = g_strdup_printf("<b>%s:</b>", label);
327 
328     temp_label = gtk_label_new(NULL);
329     gtk_label_set_markup(GTK_LABEL(temp_label), formatted_label);
330     gtk_misc_set_alignment (GTK_MISC(temp_label), 0, 0);
331     gtk_box_pack_start (GTK_BOX(dialog->meta_names_box), temp_label, FALSE,FALSE,0);
332 
333     g_free(formatted_label);
334     gtk_widget_show(temp_label);
335 }
336 
337 static void
vnr_properties_dialog_update_metadata(VnrPropertiesDialog * dialog)338 vnr_properties_dialog_update_metadata(VnrPropertiesDialog *dialog)
339 {
340     vnr_properties_dialog_clear_metadata(dialog);
341 
342     uni_read_exiv2_map(
343         VNR_FILE(dialog->vnr_win->file_list->data)->path,
344         vnr_cb_add_metadata,
345         (void*)dialog);
346 }
347 
348 void
vnr_properties_dialog_update_image(VnrPropertiesDialog * dialog)349 vnr_properties_dialog_update_image(VnrPropertiesDialog *dialog)
350 {
351     gchar *width_str, *height_str;
352 
353     set_new_pixbuf(dialog, uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(dialog->vnr_win->view)));
354     gtk_image_set_from_pixbuf (GTK_IMAGE(dialog->image) , dialog->thumbnail);
355 
356     width_str = g_strdup_printf("%i px", dialog->vnr_win->current_image_width);
357     height_str = g_strdup_printf("%i px", dialog->vnr_win->current_image_height);
358 
359     gtk_label_set_text(GTK_LABEL(dialog->width_label), width_str);
360     gtk_label_set_text(GTK_LABEL(dialog->height_label), height_str);
361 
362     g_free(width_str);
363     g_free(height_str);
364 }
365 
366 void
vnr_properties_dialog_clear(VnrPropertiesDialog * dialog)367 vnr_properties_dialog_clear(VnrPropertiesDialog *dialog)
368 {
369     set_new_pixbuf(dialog, NULL);
370     vnr_properties_dialog_clear_metadata(dialog);
371 
372     gtk_label_set_text(GTK_LABEL(dialog->name_label), _("None"));
373     gtk_label_set_text(GTK_LABEL(dialog->location_label), _("None"));
374     gtk_label_set_text(GTK_LABEL(dialog->type_label), _("None"));
375     gtk_label_set_text(GTK_LABEL(dialog->size_label), _("None"));
376     gtk_label_set_text(GTK_LABEL(dialog->width_label), _("None"));
377     gtk_label_set_text(GTK_LABEL(dialog->height_label), _("None"));
378 }
379 
380 void
vnr_properties_dialog_show(VnrPropertiesDialog * dialog)381 vnr_properties_dialog_show (VnrPropertiesDialog *dialog)
382 {
383     vnr_properties_dialog_update(dialog);
384     gtk_window_present(GTK_WINDOW(dialog));
385 }
386