Mercurial > geeqie.yaz
annotate src/bar_info.c @ 101:847e4bc6b54c
Tue Nov 7 15:35:59 2006 John Ellis <johne@verizon.net>
* exif.c: Use new format_raw_exif_offset() function to find Exif in
raw files.
* filelist.c: Add orf and pef to displayed file types.
* format_canon.h, format_fuji.h: Update to new #define format.
* format_fuji.c: Use same offset for Exif as the jpeg image as the
Exif is always embedded in the jpeg and assuming offset of 12 is
just broken.
* format_nikon.h: Update to new #define format, and add pentax here
as finding the jpeg will be same code.
* format_olympus.[ch]: Support Olympus raw files with embedded jpegs,
not all raw files will have a jpeg, but all appear to have Exif tags.
* format_raw.[ch]: Add new camera types, and add a debugging facility
to easily list all tags within tiff files (see format_raw.h to enable).
author | gqview |
---|---|
date | Tue, 07 Nov 2006 21:00:50 +0000 |
parents | 6281cc38e5ca |
children | 71e1ebee420e |
rev | line source |
---|---|
9 | 1 /* |
2 * GQview | |
3 * (C) 2004 John Ellis | |
4 * | |
5 * Author: John Ellis | |
6 * | |
7 * This software is released under the GNU General Public License (GNU GPL). | |
8 * Please read the included file COPYING for more information. | |
9 * This software comes with no warranty of any kind, use at your own risk! | |
10 */ | |
11 | |
12 | |
13 #include "gqview.h" | |
14 #include "bar_info.h" | |
15 | |
16 #include "cache.h" | |
17 #include "filelist.h" | |
18 #include "info.h" | |
19 #include "utilops.h" | |
20 #include "ui_bookmark.h" | |
21 #include "ui_fileops.h" | |
22 #include "ui_misc.h" | |
23 #include "ui_utildlg.h" | |
24 | |
25 | |
26 #define BAR_KEYWORD_AUTOSAVE_TIME 10000 | |
27 | |
28 | |
29 static const gchar *keyword_favorite_defaults[] = { | |
30 N_("Favorite"), | |
31 N_("Todo"), | |
32 N_("People"), | |
33 N_("Places"), | |
34 N_("Art"), | |
35 N_("Nature"), | |
36 N_("Possessions"), | |
37 NULL | |
38 }; | |
39 | |
40 | |
41 static void bar_info_keyword_update_all(void); | |
42 | |
43 | |
44 /* | |
45 *------------------------------------------------------------------- | |
46 * keyword / comment utils | |
47 *------------------------------------------------------------------- | |
48 */ | |
49 | |
50 gint comment_write(const gchar *path, GList *keywords, const gchar *comment) | |
51 { | |
52 FILE *f; | |
53 | |
54 f = fopen(path, "w"); | |
55 if (!f) return FALSE; | |
56 | |
57 fprintf(f, "#GQview comment (%s)\n\n", VERSION); | |
58 | |
59 fprintf(f, "[keywords]\n"); | |
60 while (keywords) | |
61 { | |
62 const gchar *word = keywords->data; | |
63 keywords = keywords->next; | |
64 | |
65 fprintf(f, "%s\n", word); | |
66 } | |
67 fprintf(f, "\n"); | |
68 | |
69 fprintf(f, "[comment]\n"); | |
70 fprintf(f, "%s\n", (comment) ? comment : ""); | |
71 | |
72 fprintf(f, "#end\n"); | |
73 | |
74 fclose(f); | |
75 | |
76 return TRUE; | |
77 } | |
78 | |
79 gint comment_cache_write(const gchar *path, GList *keywords, const gchar *comment) | |
80 { | |
81 gchar *comment_path; | |
82 gint success = FALSE; | |
83 | |
84 /* If an existing metadata file exists, we will try writing to | |
85 * it's location regardless of the user's preference. | |
86 */ | |
87 comment_path = cache_find_location(CACHE_TYPE_METADATA, path); | |
88 if (comment_path && !access_file(comment_path, W_OK)) | |
89 { | |
90 g_free(comment_path); | |
91 comment_path = NULL; | |
92 } | |
93 | |
94 if (!comment_path) | |
95 { | |
96 gchar *comment_dir; | |
97 mode_t mode = 0755; | |
98 | |
99 comment_dir = cache_get_location(CACHE_TYPE_METADATA, path, FALSE, &mode); | |
100 if (cache_ensure_dir_exists(comment_dir, mode)) | |
101 { | |
102 comment_path = g_strconcat(comment_dir, "/", filename_from_path(path), | |
103 GQVIEW_CACHE_EXT_METADATA, NULL); | |
104 } | |
105 g_free(comment_dir); | |
106 } | |
107 | |
108 if (comment_path) | |
109 { | |
110 gchar *comment_pathl; | |
111 | |
112 if (debug) printf("Saving comment: %s\n", comment_path); | |
113 | |
114 comment_pathl = path_from_utf8(comment_path); | |
115 | |
116 success = comment_write(comment_pathl, keywords, comment); | |
117 | |
118 g_free(comment_pathl); | |
119 g_free(comment_path); | |
120 } | |
121 | |
122 return success; | |
123 } | |
124 | |
125 gint comment_read(const gchar *path, GList **keywords, gchar **comment) | |
126 { | |
127 FILE *f; | |
128 gchar s_buf[1024]; | |
129 gchar *key = NULL; | |
130 GList *list = NULL; | |
131 GString *comment_build = NULL; | |
132 | |
133 f = fopen(path, "r"); | |
134 if (!f) return FALSE; | |
135 | |
136 while (fgets(s_buf,sizeof(s_buf), f)) | |
137 { | |
138 if (s_buf[0]=='#') continue; | |
139 if (s_buf[0]=='[') | |
140 { | |
141 gint c = 0; | |
142 gchar *ptr = s_buf + 1; | |
143 | |
144 while(ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++; | |
145 | |
146 g_free(key); | |
147 key = g_strndup(ptr, c); | |
148 } | |
149 else if (key) | |
150 { | |
151 gint newline = FALSE; | |
152 gchar *ptr = s_buf; | |
153 | |
154 while (*ptr != '\n' && *ptr != '\0') ptr++; | |
155 if (*ptr == '\n') | |
156 { | |
157 *ptr = '\0'; | |
158 newline = TRUE; | |
159 } | |
160 | |
161 if (strcasecmp(key, "keywords") == 0) | |
162 { | |
163 if (strlen(s_buf) > 0) list = g_list_prepend(list, g_strdup(s_buf)); | |
164 } | |
165 else if (strcasecmp(key, "comment") == 0) | |
166 { | |
167 if (!comment_build) comment_build = g_string_new(""); | |
168 g_string_append(comment_build, s_buf); | |
169 if (strlen(s_buf) > 0 && newline) g_string_append_c(comment_build, '\n'); | |
170 } | |
171 } | |
172 } | |
173 | |
174 fclose(f); | |
175 g_free(key); | |
176 | |
177 *keywords = g_list_reverse(list); | |
178 if (comment_build) | |
179 { | |
180 if (comment) *comment = g_strdup(comment_build->str); | |
181 g_string_free(comment_build, TRUE); | |
182 } | |
183 | |
184 return TRUE; | |
185 } | |
186 | |
187 gint comment_cache_read(const gchar *path, GList **keywords, gchar **comment) | |
188 { | |
189 gchar *comment_path; | |
190 gchar *comment_pathl; | |
191 gint success = FALSE; | |
192 | |
193 comment_path = cache_find_location(CACHE_TYPE_METADATA, path); | |
194 if (!comment_path) return FALSE; | |
195 | |
196 comment_pathl = path_from_utf8(comment_path); | |
197 | |
198 success = comment_read(comment_pathl, keywords, comment); | |
199 | |
200 g_free(comment_pathl); | |
201 g_free(comment_path); | |
202 | |
203 return success; | |
204 } | |
205 | |
206 static gchar *comment_pull(GtkWidget *textview) | |
207 { | |
208 GtkTextBuffer *buffer; | |
209 GtkTextIter start, end; | |
210 | |
211 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); | |
212 gtk_text_buffer_get_bounds(buffer, &start, &end); | |
213 | |
214 return gtk_text_buffer_get_text(buffer, &start, &end, FALSE); | |
215 } | |
216 | |
217 GList *keyword_list_pull(GtkWidget *text_widget) | |
218 { | |
219 GList *list = NULL; | |
220 gchar *text; | |
221 gchar *ptr; | |
222 | |
223 if (GTK_IS_TEXT_VIEW(text_widget)) | |
224 { | |
225 text = comment_pull(text_widget); | |
226 } | |
227 else if (GTK_IS_ENTRY(text_widget)) | |
228 { | |
229 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(text_widget))); | |
230 } | |
231 else | |
232 { | |
233 return NULL; | |
234 } | |
235 | |
236 ptr = text; | |
237 while (*ptr != '\0') | |
238 { | |
239 gchar *begin; | |
240 gint l = 0; | |
241 | |
242 while (*ptr == ' ' || *ptr == ',' || *ptr == '\n' || *ptr == '\r' || *ptr == '\b') ptr++; | |
243 begin = ptr; | |
244 if (*ptr != '\0') | |
245 { | |
246 while (*ptr != ' ' && *ptr != ',' && | |
247 *ptr != '\n' && *ptr != '\r' && *ptr != '\b' && | |
248 *ptr != '\0') | |
249 { | |
250 ptr++; | |
251 l++; | |
252 } | |
253 } | |
254 | |
255 if (l > 0) list = g_list_append(list, g_strndup(begin, l)); | |
256 } | |
257 | |
258 g_free(text); | |
259 | |
260 return list; | |
261 } | |
262 | |
263 void keyword_list_push(GtkWidget *textview, GList *list) | |
264 { | |
265 GtkTextBuffer *buffer; | |
266 GtkTextIter start, end; | |
267 | |
268 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); | |
269 gtk_text_buffer_get_bounds(buffer, &start, &end); | |
270 gtk_text_buffer_delete (buffer, &start, &end); | |
271 | |
272 while (list) | |
273 { | |
274 const gchar *word = list->data; | |
275 GtkTextIter iter; | |
276 | |
277 gtk_text_buffer_get_end_iter(buffer, &iter); | |
278 if (word) gtk_text_buffer_insert(buffer, &iter, word, -1); | |
279 gtk_text_buffer_get_end_iter(buffer, &iter); | |
280 gtk_text_buffer_insert(buffer, &iter, "\n", -1); | |
281 | |
282 list = list->next; | |
283 } | |
284 } | |
285 | |
286 static void metadata_set_keywords(const gchar *path, GList *list, gint add) | |
287 { | |
288 gchar *comment = NULL; | |
289 GList *keywords = NULL; | |
290 GList *save_list = NULL; | |
291 | |
292 comment_cache_read(path, &keywords, &comment); | |
293 | |
294 if (add) | |
295 { | |
296 GList *work; | |
297 | |
298 work = list; | |
299 while (work) | |
300 { | |
301 gchar *key; | |
302 GList *p; | |
303 | |
304 key = work->data; | |
305 work = work->next; | |
306 | |
307 p = keywords; | |
308 while (p && key) | |
309 { | |
310 gchar *needle = p->data; | |
311 p = p->next; | |
312 | |
313 if (strcmp(needle, key) == 0) key = NULL; | |
314 } | |
315 | |
316 if (key) keywords = g_list_append(keywords, g_strdup(key)); | |
317 } | |
318 save_list = keywords; | |
319 } | |
320 else | |
321 { | |
322 save_list = list; | |
323 } | |
324 | |
325 comment_cache_write(path, save_list, comment); | |
326 | |
327 path_list_free(keywords); | |
328 g_free(comment); | |
329 } | |
330 | |
331 /* | |
332 *------------------------------------------------------------------- | |
333 * keyword list dialog | |
334 *------------------------------------------------------------------- | |
335 */ | |
336 | |
337 #define KEYWORD_DIALOG_WIDTH 200 | |
338 #define KEYWORD_DIALOG_HEIGHT 250 | |
339 | |
340 typedef struct _KeywordDlg KeywordDlg; | |
341 struct _KeywordDlg | |
342 { | |
343 GenericDialog *gd; | |
344 GtkWidget *treeview; | |
345 }; | |
346 | |
347 static KeywordDlg *keyword_dialog = NULL; | |
348 | |
349 | |
350 static void keyword_dialog_cancel_cb(GenericDialog *gd, gpointer data) | |
351 { | |
352 g_free(keyword_dialog); | |
353 keyword_dialog = NULL; | |
354 } | |
355 | |
356 static void keyword_dialog_ok_cb(GenericDialog *gd, gpointer data) | |
357 { | |
358 KeywordDlg *kd = data; | |
359 GtkTreeModel *store; | |
360 GtkTreeIter iter; | |
361 gint valid; | |
362 | |
363 history_list_free_key("keywords"); | |
364 | |
365 store = gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview)); | |
366 valid = gtk_tree_model_get_iter_first(store, &iter); | |
367 while (valid) | |
368 { | |
369 gchar *key; | |
370 | |
371 gtk_tree_model_get(store, &iter, 0, &key, -1); | |
372 valid = gtk_tree_model_iter_next(store, &iter); | |
373 | |
374 history_list_add_to_key("keywords", key, 0); | |
375 } | |
376 | |
377 keyword_dialog_cancel_cb(gd, data); | |
378 | |
379 bar_info_keyword_update_all(); | |
380 } | |
381 | |
382 static void keyword_dialog_add_cb(GtkWidget *button, gpointer data) | |
383 { | |
384 KeywordDlg *kd = data; | |
385 GtkTreeSelection *selection; | |
386 GtkTreeModel *store; | |
387 GtkTreeIter sibling; | |
388 GtkTreeIter iter; | |
389 GtkTreePath *tpath; | |
390 | |
391 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(kd->treeview)); | |
392 if (gtk_tree_selection_get_selected(selection, &store, &sibling)) | |
393 { | |
394 gtk_list_store_insert_before(GTK_LIST_STORE(store), &iter, &sibling); | |
395 } | |
396 else | |
397 { | |
398 store = gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview)); | |
399 gtk_list_store_append(GTK_LIST_STORE(store), &iter); | |
400 } | |
401 | |
402 gtk_list_store_set(GTK_LIST_STORE(store), &iter, 1, TRUE, -1); | |
403 | |
404 tpath = gtk_tree_model_get_path(store, &iter); | |
405 gtk_tree_view_set_cursor(GTK_TREE_VIEW(kd->treeview), tpath, | |
406 gtk_tree_view_get_column(GTK_TREE_VIEW(kd->treeview), 0), TRUE); | |
407 gtk_tree_path_free(tpath); | |
408 } | |
409 | |
410 static void keyword_dialog_remove_cb(GtkWidget *button, gpointer data) | |
411 { | |
412 KeywordDlg *kd = data; | |
413 GtkTreeSelection *selection; | |
414 GtkTreeModel *store; | |
415 GtkTreeIter iter; | |
416 GtkTreeIter next; | |
417 GtkTreePath *tpath; | |
418 | |
419 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(kd->treeview)); | |
420 if (!gtk_tree_selection_get_selected(selection, &store, &iter)) return; | |
421 | |
422 tpath = NULL; | |
423 next = iter; | |
424 if (gtk_tree_model_iter_next(store, &next)) | |
425 { | |
426 tpath = gtk_tree_model_get_path(store, &next); | |
427 } | |
428 else | |
429 { | |
430 tpath = gtk_tree_model_get_path(store, &iter); | |
431 if (!gtk_tree_path_prev(tpath)) | |
432 { | |
433 gtk_tree_path_free(tpath); | |
434 tpath = NULL; | |
435 } | |
436 } | |
437 if (tpath) | |
438 { | |
439 gtk_tree_view_set_cursor(GTK_TREE_VIEW(kd->treeview), tpath, | |
440 gtk_tree_view_get_column(GTK_TREE_VIEW(kd->treeview), 0), FALSE); | |
441 gtk_tree_path_free(tpath); | |
442 } | |
443 | |
444 gtk_list_store_remove(GTK_LIST_STORE(store), &iter); | |
445 } | |
446 | |
447 static void keyword_dialog_edit_cb(GtkCellRendererText *renderer, const gchar *path, | |
448 const gchar *new_text, gpointer data) | |
449 { | |
450 KeywordDlg *kd = data; | |
451 GtkTreeModel *store; | |
452 GtkTreeIter iter; | |
453 GtkTreePath *tpath; | |
454 | |
455 if (!new_text || strlen(new_text) == 0) return; | |
456 | |
457 store = gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview)); | |
458 | |
459 tpath = gtk_tree_path_new_from_string(path); | |
460 gtk_tree_model_get_iter(store, &iter, tpath); | |
461 gtk_tree_path_free(tpath); | |
462 | |
463 gtk_list_store_set(GTK_LIST_STORE(store), &iter, 0, new_text, -1); | |
464 } | |
465 | |
466 static void keyword_dialog_populate(KeywordDlg *kd) | |
467 { | |
468 GtkListStore *store; | |
469 GList *list; | |
470 | |
471 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview))); | |
472 gtk_list_store_clear(store); | |
473 | |
474 list = history_list_get_by_key("keywords"); | |
475 list = g_list_last(list); | |
476 while (list) | |
477 { | |
478 GtkTreeIter iter; | |
479 | |
480 gtk_list_store_append(store, &iter); | |
481 gtk_list_store_set(store, &iter, 0, list->data, | |
482 1, TRUE, -1); | |
483 | |
484 list = list->prev; | |
485 } | |
486 } | |
487 | |
488 static void keyword_dialog_show(void) | |
489 { | |
490 GtkWidget *scrolled; | |
491 GtkListStore *store; | |
492 GtkTreeViewColumn *column; | |
493 GtkCellRenderer *renderer; | |
494 GtkWidget *hbox; | |
495 GtkWidget *button; | |
496 | |
497 if (keyword_dialog) | |
498 { | |
499 gtk_window_present(GTK_WINDOW(keyword_dialog->gd->dialog)); | |
500 return; | |
501 } | |
502 | |
503 keyword_dialog = g_new0(KeywordDlg, 1); | |
504 | |
505 keyword_dialog->gd = generic_dialog_new(_("Keyword Presets"), | |
506 "GQview", "keyword_presets", NULL, TRUE, | |
507 keyword_dialog_cancel_cb, keyword_dialog); | |
508 generic_dialog_add_message(keyword_dialog->gd, NULL, _("Favorite keywords list"), NULL); | |
509 | |
510 generic_dialog_add_button(keyword_dialog->gd, GTK_STOCK_OK, NULL, | |
511 keyword_dialog_ok_cb, TRUE); | |
512 | |
513 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
514 gtk_widget_set_size_request(scrolled, KEYWORD_DIALOG_WIDTH, KEYWORD_DIALOG_HEIGHT); | |
515 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
516 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
517 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
518 gtk_box_pack_start(GTK_BOX(keyword_dialog->gd->vbox), scrolled, TRUE, TRUE, 5); | |
519 gtk_widget_show(scrolled); | |
520 | |
521 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN); | |
522 keyword_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); | |
523 g_object_unref(store); | |
524 | |
525 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(keyword_dialog->treeview), FALSE); | |
526 gtk_tree_view_set_search_column(GTK_TREE_VIEW(keyword_dialog->treeview), 0); | |
527 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(keyword_dialog->treeview), TRUE); | |
528 | |
529 column = gtk_tree_view_column_new(); | |
530 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); | |
531 renderer = gtk_cell_renderer_text_new(); | |
532 g_signal_connect(G_OBJECT(renderer), "edited", | |
533 G_CALLBACK(keyword_dialog_edit_cb), keyword_dialog); | |
534 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
535 gtk_tree_view_column_add_attribute(column, renderer, "text", 0); | |
536 gtk_tree_view_column_add_attribute(column, renderer, "editable", 1); | |
537 gtk_tree_view_append_column(GTK_TREE_VIEW(keyword_dialog->treeview), column); | |
538 | |
539 gtk_container_add(GTK_CONTAINER(scrolled), keyword_dialog->treeview); | |
540 gtk_widget_show(keyword_dialog->treeview); | |
541 | |
542 hbox = gtk_hbox_new(FALSE, 5); | |
543 gtk_box_pack_start(GTK_BOX(keyword_dialog->gd->vbox), hbox, FALSE, FALSE, 0); | |
544 gtk_widget_show(hbox); | |
545 | |
546 button = gtk_button_new_from_stock(GTK_STOCK_ADD); | |
547 g_signal_connect(G_OBJECT(button), "clicked", | |
548 G_CALLBACK(keyword_dialog_add_cb), keyword_dialog); | |
549 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); | |
550 gtk_widget_show(button); | |
551 | |
552 button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); | |
553 g_signal_connect(G_OBJECT(button), "clicked", | |
554 G_CALLBACK(keyword_dialog_remove_cb), keyword_dialog); | |
555 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); | |
556 gtk_widget_show(button); | |
557 | |
558 keyword_dialog_populate(keyword_dialog); | |
559 | |
560 gtk_widget_show(keyword_dialog->gd->dialog); | |
561 } | |
562 | |
563 | |
564 static void bar_keyword_edit_cb(GtkWidget *button, gpointer data) | |
565 { | |
566 keyword_dialog_show(); | |
567 } | |
568 | |
569 | |
570 /* | |
571 *------------------------------------------------------------------- | |
572 * info bar | |
573 *------------------------------------------------------------------- | |
574 */ | |
575 | |
576 typedef enum { | |
577 BAR_SORT_COPY, | |
578 BAR_SORT_MOVE, | |
579 BAR_SORT_LINK | |
580 } SortActionType; | |
581 | |
582 enum { | |
583 KEYWORD_COLUMN_TOGGLE = 0, | |
584 KEYWORD_COLUMN_TEXT | |
585 }; | |
586 | |
587 typedef struct _BarInfoData BarInfoData; | |
588 struct _BarInfoData | |
589 { | |
590 GtkWidget *vbox; | |
591 GtkWidget *group_box; | |
592 GtkWidget *label_file_name; | |
593 GtkWidget *label_file_time; | |
594 | |
595 GtkWidget *keyword_view; | |
596 GtkWidget *keyword_treeview; | |
597 | |
598 GtkWidget *comment_view; | |
599 | |
600 GtkWidget *button_save; | |
601 GtkWidget *button_set_add; | |
602 GtkWidget *button_set_replace; | |
603 | |
604 gchar *path; | |
605 | |
606 gint changed; | |
607 gint save_timeout_id; | |
608 | |
609 GList *(*list_func)(gpointer); | |
610 gpointer list_data; | |
611 }; | |
612 | |
613 | |
614 static GList *bar_list = NULL; | |
615 | |
616 | |
617 static void bar_info_write(BarInfoData *bd) | |
618 { | |
619 GList *list; | |
620 gchar *comment; | |
621 | |
622 if (!bd->path) return; | |
623 | |
624 list = keyword_list_pull(bd->keyword_view); | |
625 comment = comment_pull(bd->comment_view); | |
626 | |
627 comment_cache_write(bd->path, list, comment); | |
628 | |
629 path_list_free(list); | |
630 g_free(comment); | |
631 | |
632 bd->changed = FALSE; | |
633 gtk_widget_set_sensitive(bd->button_save, FALSE); | |
634 } | |
635 | |
636 static gint bar_info_autosave(gpointer data) | |
637 { | |
638 BarInfoData *bd = data; | |
639 | |
640 bar_info_write(bd); | |
641 | |
642 bd->save_timeout_id = -1; | |
643 | |
644 return FALSE; | |
645 } | |
646 | |
647 static void bar_info_save_update(BarInfoData *bd, gint enable) | |
648 { | |
649 if (bd->save_timeout_id != -1) | |
650 { | |
651 g_source_remove(bd->save_timeout_id); | |
652 bd->save_timeout_id = -1; | |
653 } | |
654 if (enable) | |
655 { | |
656 bd->save_timeout_id = g_timeout_add(BAR_KEYWORD_AUTOSAVE_TIME, bar_info_autosave, bd); | |
657 } | |
658 } | |
659 | |
660 static gint bar_keyword_list_find(GList *list, const gchar *keyword) | |
661 { | |
662 while (list) | |
663 { | |
664 gchar *haystack = list->data; | |
665 | |
666 if (haystack && keyword && strcmp(haystack, keyword) == 0) return TRUE; | |
667 | |
668 list = list->next; | |
669 } | |
670 | |
671 return FALSE; | |
672 } | |
673 | |
674 static void bar_keyword_list_sync(BarInfoData *bd, GList *keywords) | |
675 { | |
676 GList *list; | |
677 GtkListStore *store; | |
678 GtkTreeIter iter; | |
679 | |
680 list = history_list_get_by_key("keywords"); | |
681 if (!list) | |
682 { | |
683 /* blank? set up a few example defaults */ | |
684 | |
685 gint i = 0; | |
686 | |
687 while (keyword_favorite_defaults[i] != NULL) | |
688 { | |
689 history_list_add_to_key("keywords", _(keyword_favorite_defaults[i]), 0); | |
690 i++; | |
691 } | |
692 | |
693 list = history_list_get_by_key("keywords"); | |
694 } | |
695 | |
696 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(bd->keyword_treeview))); | |
697 | |
698 gtk_list_store_clear(store); | |
699 | |
700 list = g_list_last(list); | |
701 while (list) | |
702 { | |
703 gchar *key = list->data; | |
704 | |
705 gtk_list_store_append(store, &iter); | |
706 gtk_list_store_set(store, &iter, KEYWORD_COLUMN_TOGGLE, bar_keyword_list_find(keywords, key), | |
707 KEYWORD_COLUMN_TEXT, key, -1); | |
708 | |
709 list = list->prev; | |
710 } | |
711 } | |
712 | |
713 static void bar_info_keyword_update_all(void) | |
714 { | |
715 GList *work; | |
716 | |
717 work = bar_list; | |
718 while (work) | |
719 { | |
720 BarInfoData *bd; | |
721 GList *keywords; | |
722 | |
723 bd = work->data; | |
724 work = work->next; | |
725 | |
726 keywords = keyword_list_pull(bd->keyword_view); | |
727 bar_keyword_list_sync(bd, keywords); | |
728 path_list_free(keywords); | |
729 } | |
730 } | |
731 | |
732 static void bar_info_update(BarInfoData *bd) | |
733 { | |
734 GList *keywords = NULL; | |
735 gchar *comment = NULL; | |
736 | |
737 if (bd->label_file_name) | |
738 { | |
739 gtk_label_set_text(GTK_LABEL(bd->label_file_name), (bd->path) ? filename_from_path(bd->path) : ""); | |
740 } | |
741 if (bd->label_file_time) | |
742 { | |
743 gtk_label_set_text(GTK_LABEL(bd->label_file_time), (bd->path) ? text_from_time(filetime(bd->path)) : ""); | |
744 } | |
745 | |
746 if (comment_cache_read(bd->path, &keywords, &comment)) | |
747 { | |
748 keyword_list_push(bd->keyword_view, keywords); | |
749 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(bd->comment_view)), | |
750 (comment) ? comment : "", -1); | |
751 | |
752 bar_keyword_list_sync(bd, keywords); | |
753 | |
754 path_list_free(keywords); | |
755 g_free(comment); | |
756 } | |
757 else | |
758 { | |
759 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(bd->keyword_view)), "", -1); | |
760 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(bd->comment_view)), "", -1); | |
761 | |
762 bar_keyword_list_sync(bd, NULL); | |
763 } | |
764 | |
765 bar_info_save_update(bd, FALSE); | |
766 bd->changed = FALSE; | |
767 gtk_widget_set_sensitive(bd->button_save, FALSE); | |
768 | |
769 gtk_widget_set_sensitive(bd->group_box, (bd->path != NULL)); | |
770 } | |
771 | |
772 void bar_info_set(GtkWidget *bar, const gchar *path) | |
773 { | |
774 BarInfoData *bd; | |
775 | |
776 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
777 if (!bd) return; | |
778 | |
779 if (bd->changed) bar_info_write(bd); | |
780 | |
781 g_free(bd->path); | |
782 bd->path = g_strdup(path); | |
783 | |
784 bar_info_update(bd); | |
785 } | |
786 | |
787 void bar_info_maint_renamed(GtkWidget *bar, const gchar *path) | |
788 { | |
789 BarInfoData *bd; | |
790 | |
791 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
792 if (!bd) return; | |
793 | |
794 g_free(bd->path); | |
795 bd->path = g_strdup(path); | |
796 | |
797 if (bd->label_file_name) | |
798 { | |
799 gtk_label_set_text(GTK_LABEL(bd->label_file_name), (bd->path) ? filename_from_path(bd->path) : ""); | |
800 } | |
801 } | |
802 | |
803 gint bar_info_event(GtkWidget *bar, GdkEvent *event) | |
804 { | |
805 BarInfoData *bd; | |
806 | |
807 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
808 if (!bd) return FALSE; | |
809 | |
810 if (GTK_WIDGET_HAS_FOCUS(bd->keyword_view)) return gtk_widget_event(bd->keyword_view, event); | |
811 if (GTK_WIDGET_HAS_FOCUS(bd->comment_view)) return gtk_widget_event(bd->comment_view, event); | |
812 | |
813 return FALSE; | |
814 } | |
815 | |
816 static void bar_info_keyword_set(BarInfoData *bd, const gchar *keyword, gint active) | |
817 { | |
818 GList *list; | |
819 gint found; | |
820 | |
821 if (!keyword) return; | |
822 | |
823 list = keyword_list_pull(bd->keyword_view); | |
824 found = bar_keyword_list_find(list, keyword); | |
825 | |
826 if (active != found) | |
827 { | |
828 if (found) | |
829 { | |
830 GList *work = list; | |
831 | |
832 while (work) | |
833 { | |
834 gchar *key = work->data; | |
835 work = work->next; | |
836 | |
837 if (key && keyword && strcmp(key, keyword) == 0) | |
838 { | |
839 list = g_list_remove(list, key); | |
840 g_free(key); | |
841 } | |
842 } | |
843 } | |
844 else | |
845 { | |
846 list = g_list_append(list, g_strdup(keyword)); | |
847 } | |
848 | |
849 keyword_list_push(bd->keyword_view, list); | |
850 } | |
851 | |
852 path_list_free(list); | |
853 } | |
854 | |
855 static void bar_info_keyword_toggle(GtkCellRendererToggle *toggle, const gchar *path, gpointer data) | |
856 { | |
857 BarInfoData *bd = data; | |
858 GtkTreeModel *store; | |
859 GtkTreeIter iter; | |
860 GtkTreePath *tpath; | |
861 gchar *key = NULL; | |
862 gboolean active; | |
863 | |
864 store = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->keyword_treeview)); | |
865 | |
866 tpath = gtk_tree_path_new_from_string(path); | |
867 gtk_tree_model_get_iter(store, &iter, tpath); | |
868 gtk_tree_path_free(tpath); | |
869 | |
870 gtk_tree_model_get(store, &iter, KEYWORD_COLUMN_TOGGLE, &active, | |
871 KEYWORD_COLUMN_TEXT, &key, -1); | |
872 active = (!active); | |
873 gtk_list_store_set(GTK_LIST_STORE(store), &iter, KEYWORD_COLUMN_TOGGLE, active, -1); | |
874 | |
875 bar_info_keyword_set(bd, key, active); | |
876 g_free(key); | |
877 } | |
878 | |
879 static void bar_info_save(GtkWidget *button, gpointer data) | |
880 { | |
881 BarInfoData *bd = data; | |
882 | |
883 bar_info_save_update(bd, FALSE); | |
884 bar_info_write(bd); | |
885 } | |
886 | |
887 static void bar_info_set_selection(BarInfoData *bd, gint add) | |
888 { | |
889 GList *keywords; | |
890 GList *list = NULL; | |
891 GList *work; | |
892 | |
893 if (!bd->list_func) return; | |
894 | |
895 keywords = keyword_list_pull(bd->keyword_view); | |
896 if (!keywords && add) return; | |
897 | |
898 list = bd->list_func(bd->list_data); | |
899 work = list; | |
900 while (work) | |
901 { | |
902 const gchar *path = work->data; | |
903 work = work->next; | |
904 | |
905 metadata_set_keywords(path, keywords, add); | |
906 } | |
907 | |
908 path_list_free(list); | |
909 path_list_free(keywords); | |
910 } | |
911 | |
912 static void bar_info_set_add(GtkWidget *button, gpointer data) | |
913 { | |
914 BarInfoData *bd = data; | |
915 | |
916 bar_info_set_selection(bd, TRUE); | |
917 } | |
918 | |
919 static void bar_info_set_replace(GtkWidget *button, gpointer data) | |
920 { | |
921 BarInfoData *bd = data; | |
922 | |
923 bar_info_set_selection(bd, FALSE); | |
924 } | |
925 | |
926 static void bar_info_changed(GtkTextBuffer *buffer, gpointer data) | |
927 { | |
928 BarInfoData *bd = data; | |
929 | |
930 bd->changed = TRUE; | |
931 gtk_widget_set_sensitive(bd->button_save, TRUE); | |
932 | |
933 bar_info_save_update(bd, TRUE); | |
934 } | |
935 | |
936 void bar_info_close(GtkWidget *bar) | |
937 { | |
938 BarInfoData *bd; | |
939 | |
940 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
941 if (!bd) return; | |
942 | |
943 gtk_widget_destroy(bd->vbox); | |
944 } | |
945 | |
946 static void bar_info_destroy(GtkWidget *widget, gpointer data) | |
947 { | |
948 BarInfoData *bd = data; | |
949 | |
950 if (bd->changed) bar_info_write(bd); | |
951 bar_info_save_update(bd, FALSE); | |
952 | |
953 bar_list = g_list_remove(bar_list, bd); | |
954 | |
955 g_free(bd->path); | |
956 | |
957 g_free(bd); | |
958 } | |
959 | |
960 GtkWidget *bar_info_new(const gchar *path, gint metadata_only, GtkWidget *bounding_widget) | |
961 { | |
962 BarInfoData *bd; | |
963 GtkWidget *box; | |
964 GtkWidget *hbox; | |
965 GtkWidget *table; | |
966 GtkWidget *scrolled; | |
967 GtkTextBuffer *buffer; | |
968 GtkWidget *label; | |
969 GtkWidget *tbar; | |
970 GtkListStore *store; | |
971 GtkTreeViewColumn *column; | |
972 GtkCellRenderer *renderer; | |
973 | |
974 bd = g_new0(BarInfoData, 1); | |
975 | |
976 bd->list_func = NULL; | |
977 bd->list_data = NULL; | |
978 | |
979 bd->vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP); | |
980 g_object_set_data(G_OBJECT(bd->vbox), "bar_info_data", bd); | |
981 g_signal_connect(G_OBJECT(bd->vbox), "destroy", | |
982 G_CALLBACK(bar_info_destroy), bd); | |
983 | |
984 if (!metadata_only) | |
985 { | |
986 hbox = pref_box_new(bd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP); | |
987 | |
988 label = sizer_new(bd->vbox, bounding_widget, SIZER_POS_LEFT); | |
989 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | |
990 gtk_widget_show(label); | |
991 | |
992 label = gtk_label_new(_("Keywords")); | |
993 pref_label_bold(label, TRUE, FALSE); | |
994 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); | |
995 gtk_widget_show(label); | |
996 } | |
997 | |
998 bd->group_box = pref_box_new(bd->vbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); | |
999 | |
1000 if (!metadata_only) | |
1001 { | |
1002 GtkWidget *table; | |
1003 | |
1004 table = pref_table_new(bd->group_box, 2, 2, FALSE, FALSE); | |
1005 | |
1006 bd->label_file_name = table_add_line(table, 0, 0, _("Filename:"), NULL); | |
1007 bd->label_file_time = table_add_line(table, 0, 1, _("File date:"), NULL); | |
1008 } | |
1009 else | |
1010 { | |
1011 bd->label_file_name = NULL; | |
1012 bd->label_file_time = NULL; | |
1013 } | |
1014 | |
1015 table = gtk_table_new(3, 1, TRUE); | |
1016 gtk_table_set_row_spacings(GTK_TABLE(table), PREF_PAD_GAP); | |
1017 gtk_box_pack_start(GTK_BOX(bd->group_box), table, TRUE, TRUE, 0); | |
1018 gtk_widget_show(table); | |
1019 | |
1020 /* keyword entry */ | |
1021 | |
1022 box = gtk_vbox_new(FALSE, 0); | |
1023 gtk_table_attach(GTK_TABLE(table), box, 0, 1, 0, 2, | |
1024 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); | |
1025 gtk_widget_show(box); | |
1026 | |
1027 label = pref_label_new(box, _("Keywords:")); | |
1028 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); | |
1029 pref_label_bold(label, TRUE, FALSE); | |
1030 | |
1031 hbox = pref_box_new(box, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP); | |
1032 | |
1033 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
1034 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
1035 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
1036 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
1037 gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0); | |
1038 gtk_widget_show(scrolled); | |
1039 | |
1040 bd->keyword_view = gtk_text_view_new(); | |
1041 gtk_container_add(GTK_CONTAINER(scrolled), bd->keyword_view); | |
1042 gtk_widget_show(bd->keyword_view); | |
1043 | |
1044 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bd->keyword_view)); | |
1045 g_signal_connect(G_OBJECT(buffer), "changed", | |
1046 G_CALLBACK(bar_info_changed), bd); | |
1047 | |
1048 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
1049 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
1050 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
1051 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
1052 gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0); | |
1053 gtk_widget_show(scrolled); | |
1054 | |
1055 store = gtk_list_store_new(2, G_TYPE_BOOLEAN, G_TYPE_STRING); | |
1056 bd->keyword_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); | |
1057 g_object_unref(store); | |
1058 | |
1059 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bd->keyword_treeview), FALSE); | |
1060 | |
1061 if (metadata_only) | |
1062 { | |
1063 gtk_tree_view_set_search_column(GTK_TREE_VIEW(bd->keyword_treeview), KEYWORD_COLUMN_TEXT); | |
1064 } | |
1065 else | |
1066 { | |
1067 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(bd->keyword_treeview), FALSE); | |
1068 } | |
1069 | |
1070 column = gtk_tree_view_column_new(); | |
1071 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); | |
1072 | |
1073 renderer = gtk_cell_renderer_toggle_new(); | |
1074 gtk_tree_view_column_pack_start(column, renderer, FALSE); | |
1075 gtk_tree_view_column_add_attribute(column, renderer, "active", KEYWORD_COLUMN_TOGGLE); | |
1076 g_signal_connect(G_OBJECT(renderer), "toggled", | |
1077 G_CALLBACK(bar_info_keyword_toggle), bd); | |
1078 | |
1079 renderer = gtk_cell_renderer_text_new(); | |
1080 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
1081 gtk_tree_view_column_add_attribute(column, renderer, "text", KEYWORD_COLUMN_TEXT); | |
1082 | |
1083 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->keyword_treeview), column); | |
1084 | |
1085 gtk_container_add(GTK_CONTAINER(scrolled), bd->keyword_treeview); | |
1086 gtk_widget_show(bd->keyword_treeview); | |
1087 | |
1088 /* comment entry */ | |
1089 | |
1090 box = gtk_vbox_new(FALSE, 0); | |
1091 gtk_table_attach(GTK_TABLE(table), box, 0, 1, 2, 3, | |
1092 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); | |
1093 gtk_widget_show(box); | |
1094 | |
1095 label = pref_label_new(box, _("Comment:")); | |
1096 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); | |
1097 pref_label_bold(label, TRUE, FALSE); | |
1098 | |
1099 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
1100 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
1101 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
1102 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
1103 gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0); | |
1104 gtk_widget_show(scrolled); | |
1105 | |
1106 bd->comment_view = gtk_text_view_new(); | |
1107 gtk_container_add(GTK_CONTAINER(scrolled), bd->comment_view); | |
1108 gtk_widget_show(bd->comment_view); | |
1109 | |
1110 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bd->comment_view)); | |
1111 g_signal_connect(G_OBJECT(buffer), "changed", | |
1112 G_CALLBACK(bar_info_changed), bd); | |
1113 | |
1114 /* toolbar */ | |
1115 | |
1116 tbar = pref_toolbar_new(bd->group_box, GTK_TOOLBAR_ICONS); | |
1117 | |
41
6281cc38e5ca
Wed Apr 27 15:17:57 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1118 pref_toolbar_button(tbar, GTK_STOCK_INDEX, NULL, FALSE, |
9 | 1119 _("Edit favorite keywords list."), |
1120 G_CALLBACK(bar_keyword_edit_cb), bd); | |
1121 pref_toolbar_spacer(tbar); | |
41
6281cc38e5ca
Wed Apr 27 15:17:57 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1122 bd->button_set_add = pref_toolbar_button(tbar, GTK_STOCK_ADD, NULL, FALSE, |
9 | 1123 _("Add keywords to selected files"), |
1124 G_CALLBACK(bar_info_set_add), bd); | |
41
6281cc38e5ca
Wed Apr 27 15:17:57 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1125 bd->button_set_replace = pref_toolbar_button(tbar, GTK_STOCK_CONVERT, NULL, FALSE, |
9 | 1126 _("Add keywords to selected files, replacing the existing ones."), |
1127 G_CALLBACK(bar_info_set_replace), bd); | |
1128 pref_toolbar_spacer(tbar); | |
41
6281cc38e5ca
Wed Apr 27 15:17:57 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
1129 bd->button_save = pref_toolbar_button(tbar, GTK_STOCK_SAVE, NULL, FALSE, |
9 | 1130 _("Save comment now"), |
1131 G_CALLBACK(bar_info_save), bd); | |
1132 | |
1133 bd->save_timeout_id = -1; | |
1134 | |
1135 bd->path = g_strdup(path); | |
1136 bar_info_update(bd); | |
1137 | |
1138 bar_info_selection(bd->vbox, 0); | |
1139 | |
1140 bar_list = g_list_append(bar_list, bd); | |
1141 | |
1142 return bd->vbox; | |
1143 } | |
1144 | |
1145 void bar_info_set_selection_func(GtkWidget *bar, GList *(*list_func)(gpointer data), gpointer data) | |
1146 { | |
1147 BarInfoData *bd; | |
1148 | |
1149 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
1150 if (!bd) return; | |
1151 | |
1152 bd->list_func = list_func; | |
1153 bd->list_data = data; | |
1154 } | |
1155 | |
1156 void bar_info_selection(GtkWidget *bar, gint count) | |
1157 { | |
1158 BarInfoData *bd; | |
1159 gint enable; | |
1160 | |
1161 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
1162 if (!bd) return; | |
1163 | |
1164 enable = (count > 0 && bd->list_func != NULL); | |
1165 | |
1166 gtk_widget_set_sensitive(bd->button_set_add, enable); | |
1167 gtk_widget_set_sensitive(bd->button_set_replace, enable); | |
1168 } | |
1169 | |
1170 void bar_info_size_request(GtkWidget *bar, gint width) | |
1171 { | |
1172 BarInfoData *bd; | |
1173 | |
1174 bd = g_object_get_data(G_OBJECT(bar), "bar_info_data"); | |
1175 if (!bd) return; | |
1176 | |
1177 if (bd->label_file_name) | |
1178 { | |
1179 gtk_widget_set_size_request(bd->vbox, width, -1); | |
1180 } | |
1181 } | |
1182 |