Mercurial > geeqie.yaz
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 |