Advanced exif window: make all columns sortable and resizable.
[geeqie.git] / src / advanced_exif.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2009 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "advanced_exif.h"
16
17 #include "exif.h"
18 #include "metadata.h"
19 #include "filedata.h"
20 #include "history_list.h"
21 #include "misc.h"
22 #include "ui_misc.h"
23 #include "window.h"
24 #include "dnd.h"
25
26 /* FIXME: not needed when bar_exif.c is improved */
27 #include "bar_exif.h"
28
29 #include <math.h>
30
31 #define ADVANCED_EXIF_DATA_COLUMN_WIDTH 200
32
33 /*
34  *-------------------------------------------------------------------
35  * EXIF window
36  *-------------------------------------------------------------------
37  */
38
39 typedef struct _ExifWin ExifWin;
40 struct _ExifWin
41 {
42         GtkWidget *window;
43         GtkWidget *vbox;
44         GtkWidget *scrolled;
45         GtkWidget *listview;
46
47         FileData *fd;
48 };
49
50 enum {
51         EXIF_ADVCOL_ENABLED = 0,
52         EXIF_ADVCOL_TAG,
53         EXIF_ADVCOL_NAME,
54         EXIF_ADVCOL_VALUE,
55         EXIF_ADVCOL_FORMAT,
56         EXIF_ADVCOL_ELEMENTS,
57         EXIF_ADVCOL_DESCRIPTION,
58         EXIF_ADVCOL_COUNT
59 };
60
61 static gint advanced_exif_row_enabled(const gchar *name)
62 {
63         GList *list;
64
65         if (!name) return FALSE;
66
67         list = history_list_get_by_key("exif_extras");
68         while (list)
69                 {
70                 if (strcmp(name, (gchar *)(list->data)) == 0) return TRUE;
71                 list = list->next;
72         }
73
74         return FALSE;
75 }
76
77 static void advanced_exif_update(ExifWin *ew)
78 {
79         ExifData *exif;
80
81         GtkListStore *store;
82         GtkTreeIter iter;
83         ExifData *exif_original;
84         ExifItem *item;
85
86         exif = exif_read_fd(ew->fd);
87         
88         gtk_widget_set_sensitive(ew->scrolled, !!exif);
89
90         if (!exif) return;
91         
92         exif_original = exif_get_original(exif);
93
94         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ew->listview)));
95         gtk_list_store_clear(store);
96
97         item = exif_get_first_item(exif_original);
98         while (item)
99                 {
100                 gchar *tag;
101                 gchar *tag_name;
102                 gchar *text;
103                 gchar *utf8_text;
104                 const gchar *format;
105                 gchar *elements;
106                 gchar *description;
107
108                 tag = g_strdup_printf("0x%04x", exif_item_get_tag_id(item));
109                 tag_name = exif_item_get_tag_name(item);
110                 format = exif_item_get_format_name(item, TRUE);
111                 text = exif_item_get_data_as_text(item);
112                 utf8_text = utf8_validate_or_convert(text);
113                 g_free(text);
114                 elements = g_strdup_printf("%d", exif_item_get_elements(item));
115                 description = exif_item_get_description(item);
116                 if (!description || *description == '\0') 
117                         {
118                         g_free(description);
119                         description = g_strdup(tag_name);
120                         }
121
122                 gtk_list_store_append(store, &iter);
123                 gtk_list_store_set(store, &iter,
124                                 EXIF_ADVCOL_ENABLED, advanced_exif_row_enabled(tag_name),
125                                 EXIF_ADVCOL_TAG, tag,
126                                 EXIF_ADVCOL_NAME, tag_name,
127                                 EXIF_ADVCOL_VALUE, utf8_text,
128                                 EXIF_ADVCOL_FORMAT, format,
129                                 EXIF_ADVCOL_ELEMENTS, elements,
130                                 EXIF_ADVCOL_DESCRIPTION, description, -1);
131                 g_free(tag);
132                 g_free(utf8_text);
133                 g_free(elements);
134                 g_free(description);
135                 g_free(tag_name);
136                 item = exif_get_next_item(exif_original);
137                 }
138         exif_free_fd(ew->fd, exif);
139
140 }
141
142 static void advanced_exif_clear(ExifWin *ew)
143 {
144         GtkListStore *store;
145
146         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ew->listview)));
147         gtk_list_store_clear(store);
148 }
149
150 void advanced_exif_set_fd(GtkWidget *window, FileData *fd)
151 {
152         ExifWin *ew;
153
154         ew = g_object_get_data(G_OBJECT(window), "advanced_exif_data");
155         if (!ew) return;
156
157         /* store this, advanced view toggle needs to reload data */
158         file_data_unref(ew->fd);
159         ew->fd = file_data_ref(fd);
160
161         advanced_exif_clear(ew);
162         advanced_exif_update(ew);
163 }
164
165 #if 0
166 static void advanced_exif_row_toggled_cb(GtkCellRendererToggle *toggle, const gchar *path, gpointer data)
167 {
168         GtkWidget *listview = data;
169         GtkTreeModel *store;
170         GtkTreeIter iter;
171         GtkTreePath *tpath;
172         gchar *name = NULL;
173         gboolean active;
174
175         store = gtk_tree_view_get_model(GTK_TREE_VIEW(listview));
176
177         tpath = gtk_tree_path_new_from_string(path);
178         gtk_tree_model_get_iter(store, &iter, tpath);
179         gtk_tree_path_free(tpath);
180
181         gtk_tree_model_get(store, &iter, EXIF_ADVCOL_ENABLED, &active,
182                                          EXIF_ADVCOL_NAME, &name, -1);
183         active = (!active);
184
185         if (active &&
186             g_list_length(history_list_get_by_key("exif_extras")) >= EXIF_BAR_CUSTOM_COUNT)
187                 {
188                 active = FALSE;
189                 }
190
191         gtk_list_store_set(GTK_LIST_STORE(store), &iter, EXIF_ADVCOL_ENABLED, active, -1);
192
193         if (active)
194                 {
195                 history_list_add_to_key("exif_extras", name, EXIF_BAR_CUSTOM_COUNT);
196                 }
197         else
198                 {
199                 history_list_item_change("exif_extras", name, NULL);
200                 }
201
202         g_free(name);
203 }
204 #endif 
205
206 #if 0
207 static void advanced_exif_add_column_check(GtkWidget *listview, const gchar *title, gint n)
208 {
209         GtkTreeViewColumn *column;
210         GtkCellRenderer *renderer;
211
212         column = gtk_tree_view_column_new();
213         gtk_tree_view_column_set_title(column, title);
214         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
215
216         renderer = gtk_cell_renderer_toggle_new();
217         gtk_tree_view_column_pack_start(column, renderer, TRUE);
218         gtk_tree_view_column_add_attribute(column, renderer, "active", n);
219         gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
220
221         g_signal_connect(G_OBJECT(renderer), "toggled",
222                          G_CALLBACK(advanced_exif_row_toggled_cb), listview);
223 }
224 #endif
225
226 static GtkTargetEntry advanced_exif_drag_types[] = {
227         { "text/plain", 0, TARGET_TEXT_PLAIN }
228 };
229 static gint n_exif_drag_types = 1;
230
231
232 static void advanced_exif_dnd_get(GtkWidget *listview, GdkDragContext *context,
233                                      GtkSelectionData *selection_data, guint info,
234                                      guint time, gpointer data)
235 {
236         ExifWin *ew = data;
237         GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview)); 
238         GtkTreeIter iter;
239
240         if (gtk_tree_selection_get_selected(sel, NULL, &iter)) 
241                 {
242                 GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(listview));
243                 gchar *key;
244
245                 gtk_tree_model_get(store, &iter, EXIF_ADVCOL_NAME, &key, -1);
246                 gtk_selection_data_set_text(selection_data, key, -1);
247                 printf("%s\n",key);
248                 g_free(key);
249                 }
250
251 }
252
253
254 static void advanced_exif_dnd_begin(GtkWidget *listview, GdkDragContext *context, gpointer data)
255 {
256         ExifWin *ew = data;
257         GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview)); 
258         GtkTreeIter iter;
259
260         if (gtk_tree_selection_get_selected(sel, NULL, &iter)) 
261                 {
262                 GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(listview));
263                 gchar *key;
264
265                 gtk_tree_model_get(store, &iter, EXIF_ADVCOL_NAME, &key, -1);
266
267                 dnd_set_drag_label(listview, context, key);
268                 g_free(key);
269                 }
270 }
271
272
273
274 static void advanced_exif_add_column(GtkWidget *listview, const gchar *title, gint n, gint sizable)
275 {
276         GtkTreeViewColumn *column;
277         GtkCellRenderer *renderer;
278
279         column = gtk_tree_view_column_new();
280         gtk_tree_view_column_set_title(column, title);
281
282         if (sizable)
283                 {
284                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
285                 gtk_tree_view_column_set_fixed_width(column, ADVANCED_EXIF_DATA_COLUMN_WIDTH);
286                 }
287         else
288                 {
289                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
290                 }
291         
292         gtk_tree_view_column_set_resizable(column, TRUE);
293         gtk_tree_view_column_set_sort_column_id(column, n);
294
295         renderer = gtk_cell_renderer_text_new();
296         gtk_tree_view_column_pack_start(column, renderer, TRUE);
297         gtk_tree_view_column_add_attribute(column, renderer, "text", n);
298         gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
299 }
300
301 void advanced_exif_close(GtkWidget *window)
302 {
303         ExifWin *ew;
304
305         ew = g_object_get_data(G_OBJECT(window), "advanced_exif_data");
306         if (!ew) return;
307
308         gtk_widget_destroy(ew->vbox);
309 }
310
311 static void advanced_exif_destroy(GtkWidget *widget, gpointer data)
312 {
313         ExifWin *ew = data;
314         file_data_unref(ew->fd);
315         g_free(ew);
316 }
317
318 static gint advanced_exif_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
319 {
320         gint n = GPOINTER_TO_INT(data);
321         gint ret = 0;
322
323         switch (n)
324                 {
325                 case EXIF_ADVCOL_DESCRIPTION:
326                 case EXIF_ADVCOL_VALUE:
327                 case EXIF_ADVCOL_NAME:
328                 case EXIF_ADVCOL_TAG:
329                 case EXIF_ADVCOL_FORMAT:
330                 case EXIF_ADVCOL_ELEMENTS:
331                         {
332                         gchar *s1, *s2;
333
334                         gtk_tree_model_get(model, a, n, &s1, -1);
335                         gtk_tree_model_get(model, b, n, &s2, -1);
336
337                         if (!s1 || !s2)
338                                 {
339                                 if (!s1 && !s2) break;
340                                 ret = s1 ? 1 : -1;
341                                 }
342                         else
343                                 {
344                                 ret = g_utf8_collate(s1, s2);
345                                 }
346
347                         g_free(s1);
348                         g_free(s2);
349                         }
350                         break;
351
352                 default:
353                         g_return_val_if_reached(0);
354                 }
355
356         return ret;
357 }
358
359 GtkWidget *advanced_exif_new(void)
360 {
361         ExifWin *ew;
362         GtkListStore *store;
363         GdkGeometry geometry;
364         GtkTreeSortable *sortable;
365         gint n;
366
367         ew = g_new0(ExifWin, 1);
368
369         ew->window = window_new(GTK_WINDOW_TOPLEVEL, "view", NULL, NULL, _("Metadata"));
370
371         geometry.min_width = 900;
372         geometry.min_height = 600;
373         gtk_window_set_geometry_hints(GTK_WINDOW(ew->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
374
375         gtk_window_set_resizable(GTK_WINDOW(ew->window), TRUE);
376
377         g_object_set_data(G_OBJECT(ew->window), "advanced_exif_data", ew);
378         g_signal_connect_after(G_OBJECT(ew->window), "destroy",
379                                G_CALLBACK(advanced_exif_destroy), ew);
380
381         ew->vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
382         gtk_container_add(GTK_CONTAINER(ew->window), ew->vbox);
383         gtk_widget_show(ew->vbox);
384
385
386         store = gtk_list_store_new(7, G_TYPE_BOOLEAN,
387                                       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
388                                       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
389
390         /* set up sorting */
391         sortable = GTK_TREE_SORTABLE(store);
392         for (n = EXIF_ADVCOL_DESCRIPTION; n <= EXIF_ADVCOL_ELEMENTS; n++)
393                 gtk_tree_sortable_set_sort_func(sortable, n, advanced_exif_sort_cb,
394                                                 GINT_TO_POINTER(n), NULL);
395
396         /* set initial sort order */
397         gtk_tree_sortable_set_sort_column_id(sortable, EXIF_ADVCOL_NAME, GTK_SORT_ASCENDING);
398
399         ew->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
400         g_object_unref(store);
401
402         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(ew->listview), TRUE);
403         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ew->listview), TRUE);
404
405         advanced_exif_add_column(ew->listview, _("Description"), EXIF_ADVCOL_DESCRIPTION, FALSE);
406         advanced_exif_add_column(ew->listview, _("Value"), EXIF_ADVCOL_VALUE, TRUE);
407         advanced_exif_add_column(ew->listview, _("Name"), EXIF_ADVCOL_NAME, FALSE);
408         advanced_exif_add_column(ew->listview, _("Tag"), EXIF_ADVCOL_TAG, FALSE);
409         advanced_exif_add_column(ew->listview, _("Format"), EXIF_ADVCOL_FORMAT, FALSE);
410         advanced_exif_add_column(ew->listview, _("Elements"), EXIF_ADVCOL_ELEMENTS, FALSE);
411         
412
413         gtk_drag_source_set(ew->listview,
414                            GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
415                            advanced_exif_drag_types, n_exif_drag_types,
416                            GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
417
418         g_signal_connect(G_OBJECT(ew->listview), "drag_data_get",
419                          G_CALLBACK(advanced_exif_dnd_get), ew);
420
421         g_signal_connect(G_OBJECT(ew->listview), "drag_begin",
422                          G_CALLBACK(advanced_exif_dnd_begin), ew);
423
424         ew->scrolled = gtk_scrolled_window_new(NULL, NULL);
425         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ew->scrolled), GTK_SHADOW_IN);
426         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ew->scrolled),
427                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
428         gtk_box_pack_start(GTK_BOX(ew->vbox), ew->scrolled, TRUE, TRUE, 0);
429         gtk_container_add(GTK_CONTAINER(ew->scrolled), ew->listview);
430         gtk_widget_show(ew->listview);
431         gtk_widget_show(ew->scrolled);
432
433         gtk_widget_show(ew->window);
434         return ew->window;
435 }
436 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */