comparison src/bar_info.c @ 9:d907d608745f

Sync to GQview 1.5.9 release. ######## DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS! This CVS is never up to date with current development and is provided solely for reference purposes, please use the latest official release package when making any changes or translation updates. ########
author gqview
date Sat, 26 Feb 2005 00:13:35 +0000
parents
children 6281cc38e5ca
comparison
equal deleted inserted replaced
8:e0d0593d519e 9:d907d608745f
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
1118 pref_toolbar_button(tbar, GTK_STOCK_INDEX, NULL,
1119 _("Edit favorite keywords list."),
1120 G_CALLBACK(bar_keyword_edit_cb), bd);
1121 pref_toolbar_spacer(tbar);
1122 bd->button_set_add = pref_toolbar_button(tbar, GTK_STOCK_ADD, NULL,
1123 _("Add keywords to selected files"),
1124 G_CALLBACK(bar_info_set_add), bd);
1125 bd->button_set_replace = pref_toolbar_button(tbar, GTK_STOCK_CONVERT, NULL,
1126 _("Add keywords to selected files, replacing the existing ones."),
1127 G_CALLBACK(bar_info_set_replace), bd);
1128 pref_toolbar_spacer(tbar);
1129 bd->button_save = pref_toolbar_button(tbar, GTK_STOCK_SAVE, NULL,
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