simplified config writing
[geeqie.git] / src / bar_keywords.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2009 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include <glib/gprintf.h>
14
15 #include "main.h"
16 #include "bar_keywords.h"
17
18 #include "filedata.h"
19 #include "history_list.h"
20 #include "metadata.h"
21 #include "misc.h"
22 #include "ui_fileops.h"
23 #include "ui_misc.h"
24 #include "ui_utildlg.h"
25 #include "utilops.h"
26 #include "bar.h"
27 #include "ui_menu.h"
28 #include "rcfile.h"
29
30 static const gchar *keyword_favorite_defaults[] = {
31         N_("Favorite"),
32         N_("Todo"),
33         N_("People"),
34         N_("Places"),
35         N_("Art"),
36         N_("Nature"),
37         N_("Possessions"),
38         NULL
39 };
40
41
42 static void bar_pane_keywords_keyword_update_all(void);
43 static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data);
44
45 /*
46  *-------------------------------------------------------------------
47  * keyword / comment utils
48  *-------------------------------------------------------------------
49  */
50
51
52 GList *keyword_list_pull(GtkWidget *text_widget)
53 {
54         GList *list;
55         gchar *text;
56
57         text = text_widget_text_pull(text_widget);
58         list = string_to_keywords_list(text);
59
60         g_free(text);
61
62         return list;
63 }
64
65 static void keyword_list_push(GtkWidget *textview, GList *list)
66 {
67         GtkTextBuffer *buffer;
68         GtkTextIter start, end;
69
70         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
71         gtk_text_buffer_get_bounds(buffer, &start, &end);
72         gtk_text_buffer_delete(buffer, &start, &end);
73
74         while (list)
75                 {
76                 const gchar *word = list->data;
77                 GtkTextIter iter;
78
79                 gtk_text_buffer_get_end_iter(buffer, &iter);
80                 if (word) gtk_text_buffer_insert(buffer, &iter, word, -1);
81                 gtk_text_buffer_get_end_iter(buffer, &iter);
82                 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
83
84                 list = list->next;
85                 }
86 }
87
88
89 /*
90  *-------------------------------------------------------------------
91  * keyword list dialog
92  *-------------------------------------------------------------------
93  */
94
95 #define KEYWORD_DIALOG_WIDTH  200
96 #define KEYWORD_DIALOG_HEIGHT 250
97
98 typedef struct _KeywordDlg KeywordDlg;
99 struct _KeywordDlg
100 {
101         GenericDialog *gd;
102         GtkWidget *treeview;
103 };
104
105 static KeywordDlg *keyword_dialog = NULL;
106
107
108 static void keyword_dialog_cancel_cb(GenericDialog *gd, gpointer data)
109 {
110         g_free(keyword_dialog);
111         keyword_dialog = NULL;
112 }
113
114 static void keyword_dialog_ok_cb(GenericDialog *gd, gpointer data)
115 {
116         KeywordDlg *kd = data;
117         GtkTreeModel *store;
118         GtkTreeIter iter;
119         gint valid;
120
121         history_list_free_key("keywords");
122
123         store = gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview));
124         valid = gtk_tree_model_get_iter_first(store, &iter);
125         while (valid)
126                 {
127                 gchar *key;
128
129                 gtk_tree_model_get(store, &iter, 0, &key, -1);
130                 valid = gtk_tree_model_iter_next(store, &iter);
131
132                 history_list_add_to_key("keywords", key, 0);
133                 }
134
135         keyword_dialog_cancel_cb(gd, data);
136
137         bar_pane_keywords_keyword_update_all();
138 }
139
140 static void keyword_dialog_add_cb(GtkWidget *button, gpointer data)
141 {
142         KeywordDlg *kd = data;
143         GtkTreeSelection *selection;
144         GtkTreeModel *store;
145         GtkTreeIter sibling;
146         GtkTreeIter iter;
147         GtkTreePath *tpath;
148
149         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(kd->treeview));
150         if (gtk_tree_selection_get_selected(selection, &store, &sibling))
151                 {
152                 gtk_list_store_insert_before(GTK_LIST_STORE(store), &iter, &sibling);
153                 }
154         else
155                 {
156                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview));
157                 gtk_list_store_append(GTK_LIST_STORE(store), &iter);
158                 }
159
160         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 1, TRUE, -1);
161
162         tpath = gtk_tree_model_get_path(store, &iter);
163         gtk_tree_view_set_cursor(GTK_TREE_VIEW(kd->treeview), tpath,
164                                  gtk_tree_view_get_column(GTK_TREE_VIEW(kd->treeview), 0), TRUE);
165         gtk_tree_path_free(tpath);
166 }
167
168 static void keyword_dialog_remove_cb(GtkWidget *button, gpointer data)
169 {
170         KeywordDlg *kd = data;
171         GtkTreeSelection *selection;
172         GtkTreeModel *store;
173         GtkTreeIter iter;
174         GtkTreeIter next;
175         GtkTreePath *tpath;
176
177         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(kd->treeview));
178         if (!gtk_tree_selection_get_selected(selection, &store, &iter)) return;
179
180         tpath = NULL;
181         next = iter;
182         if (gtk_tree_model_iter_next(store, &next))
183                 {
184                 tpath = gtk_tree_model_get_path(store, &next);
185                 }
186         else
187                 {
188                 tpath = gtk_tree_model_get_path(store, &iter);
189                 if (!gtk_tree_path_prev(tpath))
190                         {
191                         gtk_tree_path_free(tpath);
192                         tpath = NULL;
193                         }
194                 }
195         if (tpath)
196                 {
197                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(kd->treeview), tpath,
198                                          gtk_tree_view_get_column(GTK_TREE_VIEW(kd->treeview), 0), FALSE);
199                 gtk_tree_path_free(tpath);
200                 }
201
202         gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
203 }
204
205 static void keyword_dialog_edit_cb(GtkCellRendererText *renderer, const gchar *path,
206                                    const gchar *new_text, gpointer data)
207 {
208         KeywordDlg *kd = data;
209         GtkTreeModel *store;
210         GtkTreeIter iter;
211         GtkTreePath *tpath;
212
213         if (!new_text || strlen(new_text) == 0) return;
214
215         store = gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview));
216
217         tpath = gtk_tree_path_new_from_string(path);
218         gtk_tree_model_get_iter(store, &iter, tpath);
219         gtk_tree_path_free(tpath);
220
221         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 0, new_text, -1);
222 }
223
224 static void keyword_dialog_populate(KeywordDlg *kd)
225 {
226         GtkListStore *store;
227         GList *list;
228
229         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(kd->treeview)));
230         gtk_list_store_clear(store);
231
232         list = history_list_get_by_key("keywords");
233         list = g_list_last(list);
234         while (list)
235                 {
236                 GtkTreeIter iter;
237
238                 gtk_list_store_append(store, &iter);
239                 gtk_list_store_set(store, &iter, 0, list->data,
240                                                  1, TRUE, -1);
241
242                 list = list->prev;
243                 }
244 }
245
246 static void keyword_dialog_show(void)
247 {
248         GtkWidget *scrolled;
249         GtkListStore *store;
250         GtkTreeViewColumn *column;
251         GtkCellRenderer *renderer;
252         GtkWidget *hbox;
253         GtkWidget *button;
254
255         if (keyword_dialog)
256                 {
257                 gtk_window_present(GTK_WINDOW(keyword_dialog->gd->dialog));
258                 return;
259                 }
260
261         keyword_dialog = g_new0(KeywordDlg, 1);
262
263         keyword_dialog->gd = generic_dialog_new(_("Keyword Presets"),
264                                                 "keyword_presets", NULL, TRUE,
265                                                 keyword_dialog_cancel_cb, keyword_dialog);
266         generic_dialog_add_message(keyword_dialog->gd, NULL, _("Favorite keywords list"), NULL);
267
268         generic_dialog_add_button(keyword_dialog->gd, GTK_STOCK_OK, NULL,
269                                  keyword_dialog_ok_cb, TRUE);
270
271         scrolled = gtk_scrolled_window_new(NULL, NULL);
272         gtk_widget_set_size_request(scrolled, KEYWORD_DIALOG_WIDTH, KEYWORD_DIALOG_HEIGHT);
273         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
274         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
275                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
276         gtk_box_pack_start(GTK_BOX(keyword_dialog->gd->vbox), scrolled, TRUE, TRUE, 5);
277         gtk_widget_show(scrolled);
278
279         store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN);
280         keyword_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
281         g_object_unref(store);
282
283         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(keyword_dialog->treeview), FALSE);
284         gtk_tree_view_set_search_column(GTK_TREE_VIEW(keyword_dialog->treeview), 0);
285         gtk_tree_view_set_reorderable(GTK_TREE_VIEW(keyword_dialog->treeview), TRUE);
286
287         column = gtk_tree_view_column_new();
288         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
289         renderer = gtk_cell_renderer_text_new();
290         g_signal_connect(G_OBJECT(renderer), "edited",
291                          G_CALLBACK(keyword_dialog_edit_cb), keyword_dialog);
292         gtk_tree_view_column_pack_start(column, renderer, TRUE);
293         gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
294         gtk_tree_view_column_add_attribute(column, renderer, "editable", 1);
295         gtk_tree_view_append_column(GTK_TREE_VIEW(keyword_dialog->treeview), column);
296
297         gtk_container_add(GTK_CONTAINER(scrolled), keyword_dialog->treeview);
298         gtk_widget_show(keyword_dialog->treeview);
299
300         hbox = gtk_hbox_new(FALSE, 5);
301         gtk_box_pack_start(GTK_BOX(keyword_dialog->gd->vbox), hbox, FALSE, FALSE, 0);
302         gtk_widget_show(hbox);
303
304         button = gtk_button_new_from_stock(GTK_STOCK_ADD);
305         g_signal_connect(G_OBJECT(button), "clicked",
306                          G_CALLBACK(keyword_dialog_add_cb), keyword_dialog);
307         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
308         gtk_widget_show(button);
309
310         button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
311         g_signal_connect(G_OBJECT(button), "clicked",
312                          G_CALLBACK(keyword_dialog_remove_cb), keyword_dialog);
313         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
314         gtk_widget_show(button);
315
316         keyword_dialog_populate(keyword_dialog);
317
318         gtk_widget_show(keyword_dialog->gd->dialog);
319 }
320
321
322 static void bar_keyword_edit_cb(GtkWidget *button, gpointer data)
323 {
324         keyword_dialog_show();
325 }
326
327
328 /*
329  *-------------------------------------------------------------------
330  * info bar
331  *-------------------------------------------------------------------
332  */
333
334 enum {
335         KEYWORD_COLUMN_TOGGLE = 0,
336         KEYWORD_COLUMN_TEXT,
337         KEYWORD_COLUMN_MARK
338 };
339
340 typedef struct _PaneKeywordsData PaneKeywordsData;
341 struct _PaneKeywordsData
342 {
343         PaneData pane;
344         GtkWidget *widget;
345
346         GtkWidget *keyword_view;
347         GtkWidget *keyword_treeview;
348
349         FileData *fd;
350         gchar *key;
351 };
352
353
354 static GList *bar_list = NULL;
355
356
357 static void bar_pane_keywords_write(PaneKeywordsData *pkd)
358 {
359         GList *list;
360
361         if (!pkd->fd) return;
362
363         list = keyword_list_pull(pkd->keyword_view);
364
365         metadata_write_list(pkd->fd, KEYWORD_KEY, list);
366
367         string_list_free(list);
368 }
369
370 static gchar *bar_pane_keywords_get_mark_text(const gchar *key)
371 {
372         gint i;
373         static gchar buf[10];
374         
375         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
376                 {
377                 FileDataGetMarkFunc get_mark_func;
378                 FileDataSetMarkFunc set_mark_func;
379                 gpointer data;
380                 file_data_get_registered_mark_func(i, &get_mark_func, &set_mark_func, &data);
381                 if (get_mark_func == meta_data_get_keyword_mark && strcmp(data, key) == 0) 
382                         {
383                         g_sprintf(buf, " %d ", i + 1);
384                         return buf;
385                         }
386                 }
387         return " ... ";
388 }
389
390 static void bar_keyword_list_sync(PaneKeywordsData *pkd, GList *keywords)
391 {
392         GList *list;
393         GtkListStore *store;
394         GtkTreeIter iter;
395
396         list = history_list_get_by_key("keywords");
397         if (!list)
398                 {
399                 /* blank? set up a few example defaults */
400
401                 gint i = 0;
402
403                 while (keyword_favorite_defaults[i] != NULL)
404                         {
405                         history_list_add_to_key("keywords", _(keyword_favorite_defaults[i]), 0);
406                         i++;
407                         }
408
409                 list = history_list_get_by_key("keywords");
410                 }
411
412         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview)));
413
414         gtk_list_store_clear(store);
415
416         list = g_list_last(list);
417         while (list)
418                 {
419                 gchar *key = list->data;
420
421                 gtk_list_store_append(store, &iter);
422                 gtk_list_store_set(store, &iter, KEYWORD_COLUMN_TOGGLE, find_string_in_list(keywords, key),
423                                                  KEYWORD_COLUMN_TEXT, key,
424                                                  KEYWORD_COLUMN_MARK, bar_pane_keywords_get_mark_text(key), -1);
425
426                 list = list->prev;
427                 }
428 }
429
430 static void bar_pane_keywords_keyword_update_all(void)
431 {
432         GList *work;
433
434         work = bar_list;
435         while (work)
436                 {
437                 PaneKeywordsData *pkd;
438                 GList *keywords;
439
440                 pkd = work->data;
441                 work = work->next;
442
443                 keywords = keyword_list_pull(pkd->keyword_view);
444                 bar_keyword_list_sync(pkd, keywords);
445                 string_list_free(keywords);
446                 }
447 }
448
449 static void bar_pane_keywords_update(PaneKeywordsData *pkd)
450 {
451         GList *keywords = NULL;
452         GtkTextBuffer *keyword_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
453
454         g_signal_handlers_block_by_func(keyword_buffer, bar_pane_keywords_changed, pkd);
455
456         keywords = metadata_read_list(pkd->fd, KEYWORD_KEY, METADATA_PLAIN);
457         keyword_list_push(pkd->keyword_view, keywords);
458         bar_keyword_list_sync(pkd, keywords);
459         string_list_free(keywords);
460         
461         g_signal_handlers_unblock_by_func(keyword_buffer, bar_pane_keywords_changed, pkd);
462
463 }
464
465 void bar_pane_keywords_set_fd(GtkWidget *pane, FileData *fd)
466 {
467         PaneKeywordsData *pkd;
468
469         pkd = g_object_get_data(G_OBJECT(pane), "pane_data");
470         if (!pkd) return;
471
472         file_data_unref(pkd->fd);
473         pkd->fd = file_data_ref(fd);
474
475         bar_pane_keywords_update(pkd);
476 }
477
478 static void bar_pane_keywords_write_config(GtkWidget *pane, GString *outstr, gint indent)
479 {
480         PaneKeywordsData *pkd;
481
482         pkd = g_object_get_data(G_OBJECT(pane), "pane_data");
483         if (!pkd) return;
484
485         WRITE_STRING("<pane_keywords\n");
486         indent++;
487         WRITE_CHAR(*pkd, pane.title);
488         WRITE_BOOL(*pkd, pane.expanded);
489         WRITE_CHAR(*pkd, key);
490         indent--;
491         WRITE_STRING("/>\n");
492 }
493
494 gint bar_pane_keywords_event(GtkWidget *bar, GdkEvent *event)
495 {
496         PaneKeywordsData *pkd;
497
498         pkd = g_object_get_data(G_OBJECT(bar), "pane_data");
499         if (!pkd) return FALSE;
500
501         if (GTK_WIDGET_HAS_FOCUS(pkd->keyword_view)) return gtk_widget_event(pkd->keyword_view, event);
502
503         return FALSE;
504 }
505
506 static void bar_pane_keywords_keyword_set(PaneKeywordsData *pkd, const gchar *keyword, gint active)
507 {
508         GList *list;
509         gint found;
510
511         if (!keyword) return;
512
513         list = keyword_list_pull(pkd->keyword_view);
514         found = find_string_in_list(list, keyword);
515
516         if (active != found)
517                 {
518                 if (found)
519                         {
520                         GList *work = list;
521
522                         while (work)
523                                 {
524                                 gchar *key = work->data;
525                                 work = work->next;
526
527                                 if (key && keyword && strcmp(key, keyword) == 0)
528                                         {
529                                         list = g_list_remove(list, key);
530                                         g_free(key);
531                                         }
532                                 }
533                         }
534                 else
535                         {
536                         list = g_list_append(list, g_strdup(keyword));
537                         }
538
539                 keyword_list_push(pkd->keyword_view, list);
540                 }
541
542         string_list_free(list);
543 }
544
545 static void bar_pane_keywords_keyword_toggle(GtkCellRendererToggle *toggle, const gchar *path, gpointer data)
546 {
547         PaneKeywordsData *pkd = data;
548         GtkTreeModel *store;
549         GtkTreeIter iter;
550         GtkTreePath *tpath;
551         gchar *key = NULL;
552         gboolean active;
553
554         store = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
555
556         tpath = gtk_tree_path_new_from_string(path);
557         gtk_tree_model_get_iter(store, &iter, tpath);
558         gtk_tree_path_free(tpath);
559
560         gtk_tree_model_get(store, &iter, KEYWORD_COLUMN_TOGGLE, &active,
561                                          KEYWORD_COLUMN_TEXT, &key, -1);
562         active = (!active);
563         gtk_list_store_set(GTK_LIST_STORE(store), &iter, KEYWORD_COLUMN_TOGGLE, active, -1);
564
565         bar_pane_keywords_keyword_set(pkd, key, active);
566         g_free(key);
567 }
568
569 static void bar_pane_keywords_set_selection(PaneKeywordsData *pkd, gboolean append)
570 {
571         GList *keywords = NULL;
572         GList *list = NULL;
573         GList *work;
574
575         if (!pkd->pane.list_func) return;
576
577         keywords = keyword_list_pull(pkd->keyword_view);
578
579         list = pkd->pane.list_func(pkd->pane.list_data);
580         work = list;
581         while (work)
582                 {
583                 FileData *fd = work->data;
584                 work = work->next;
585
586                 if (append)
587                         {
588                         metadata_append_list(fd, KEYWORD_KEY, keywords);
589                         }
590                 else
591                         {
592                         metadata_write_list(fd, KEYWORD_KEY, keywords);
593                         }
594                 }
595
596         filelist_free(list);
597         string_list_free(keywords);
598 }
599
600 static void bar_pane_keywords_sel_add_cb(GtkWidget *button, gpointer data)
601 {
602         PaneKeywordsData *pkd = data;
603
604         bar_pane_keywords_set_selection(pkd, TRUE);
605 }
606
607 static void bar_pane_keywords_sel_replace_cb(GtkWidget *button, gpointer data)
608 {
609         PaneKeywordsData *pkd = data;
610
611         bar_pane_keywords_set_selection(pkd, FALSE);
612 }
613
614 static void bar_pane_keywords_populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer data)
615 {
616         PaneKeywordsData *pkd = data;
617
618         menu_item_add_divider(GTK_WIDGET(menu));
619         menu_item_add_stock(GTK_WIDGET(menu), _("Add keywords to selected files"), GTK_STOCK_ADD, G_CALLBACK(bar_pane_keywords_sel_add_cb), pkd);
620         menu_item_add_stock(GTK_WIDGET(menu), _("Replace existing keywords in selected files"), GTK_STOCK_CONVERT, G_CALLBACK(bar_pane_keywords_sel_replace_cb), pkd);
621 }
622
623
624 static void bar_pane_keywords_notify_cb(FileData *fd, NotifyType type, gpointer data)
625 {
626         PaneKeywordsData *pkd = data;
627         if (fd == pkd->fd) bar_pane_keywords_update(pkd);
628 }
629
630 static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data)
631 {
632         PaneKeywordsData *pkd = data;
633
634         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
635         bar_pane_keywords_write(pkd);
636         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
637 }
638
639 static void bar_pane_keywords_mark_edited (GtkCellRendererText *cell, const gchar *path, const gchar *text, gpointer data)
640 {
641         PaneKeywordsData *pkd = data;
642         GtkTreeModel *store;
643         GtkTreeIter iter;
644         GtkTreePath *tpath;
645         gchar *key = NULL;
646         gint i;
647         FileDataGetMarkFunc get_mark_func;
648         FileDataSetMarkFunc set_mark_func;
649         gpointer mark_func_data;
650
651         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
652
653         store = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
654
655         tpath = gtk_tree_path_new_from_string(path);
656         gtk_tree_model_get_iter(store, &iter, tpath);
657         gtk_tree_path_free(tpath);
658
659         gtk_tree_model_get(store, &iter, KEYWORD_COLUMN_TEXT, &key, -1);
660
661         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
662                 {
663                 file_data_get_registered_mark_func(i, &get_mark_func, &set_mark_func, &mark_func_data);
664                 if (get_mark_func == meta_data_get_keyword_mark && strcmp(mark_func_data, key) == 0) 
665                         {
666                         g_free(mark_func_data);
667                         file_data_register_mark_func(i, NULL, NULL, NULL);
668                         }
669                 }
670
671         if (sscanf(text, " %d ", &i) &&i >=1 && i <= FILEDATA_MARKS_SIZE)
672                 {
673                 i--;
674                 file_data_get_registered_mark_func(i, &get_mark_func, &set_mark_func, &mark_func_data);
675                 if (get_mark_func == meta_data_get_keyword_mark && mark_func_data) g_free(mark_func_data); 
676                 file_data_register_mark_func(i, meta_data_get_keyword_mark, meta_data_set_keyword_mark, g_strdup(key));
677                 }
678
679         g_free(key);
680
681         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
682         bar_pane_keywords_update(pkd);
683 }
684
685 void bar_pane_keywords_close(GtkWidget *bar)
686 {
687         PaneKeywordsData *pkd;
688
689         pkd = g_object_get_data(G_OBJECT(bar), "pane_data");
690         if (!pkd) return;
691
692         gtk_widget_destroy(pkd->widget);
693 }
694
695 static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data)
696 {
697         PaneKeywordsData *pkd = data;
698
699         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
700
701         file_data_unref(pkd->fd);
702         g_free(pkd->key);
703
704         g_free(pkd);
705 }
706
707 static GtkTreeModel *create_marks_list(void)
708 {
709         GtkListStore *model;
710         GtkTreeIter iter;
711         gint i;
712
713         /* create list store */
714         model = gtk_list_store_new(1, G_TYPE_STRING);
715         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
716                 {
717                 gchar str[10];
718                 g_sprintf(str, " %d ", i + 1);
719                 gtk_list_store_append (model, &iter);
720                 gtk_list_store_set(model, &iter, 0, str, -1);
721                 }
722         gtk_list_store_append (model, &iter);
723         gtk_list_store_set(model, &iter, 0, " ... ", -1);
724         return GTK_TREE_MODEL(model);
725 }
726
727 GtkWidget *bar_pane_keywords_new(const gchar *title, const gchar *key, gboolean expanded)
728 {
729         PaneKeywordsData *pkd;
730         GtkWidget *hbox;
731         GtkWidget *scrolled;
732         GtkTextBuffer *buffer;
733         GtkListStore *store;
734         GtkTreeViewColumn *column;
735         GtkCellRenderer *renderer;
736
737         pkd = g_new0(PaneKeywordsData, 1);
738
739         pkd->pane.pane_set_fd = bar_pane_keywords_set_fd;
740         pkd->pane.pane_event = bar_pane_keywords_event;
741         pkd->pane.pane_write_config = bar_pane_keywords_write_config;
742         pkd->pane.title = g_strdup(title);
743         pkd->pane.expanded = expanded;
744
745         pkd->key = g_strdup(key);
746         
747
748         hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
749
750         pkd->widget = hbox;
751         g_object_set_data(G_OBJECT(pkd->widget), "pane_data", pkd);
752         g_signal_connect(G_OBJECT(pkd->widget), "destroy",
753                          G_CALLBACK(bar_pane_keywords_destroy), pkd);
754         gtk_widget_show(hbox);
755
756         scrolled = gtk_scrolled_window_new(NULL, NULL);
757         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
758         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
759                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
760         gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
761         gtk_widget_show(scrolled);
762
763         pkd->keyword_view = gtk_text_view_new();
764         gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_view);
765         g_signal_connect(G_OBJECT(pkd->keyword_view), "populate-popup",
766                          G_CALLBACK(bar_pane_keywords_populate_popup_cb), pkd);
767         gtk_widget_show(pkd->keyword_view);
768
769         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
770         g_signal_connect(G_OBJECT(buffer), "changed",
771                          G_CALLBACK(bar_pane_keywords_changed), pkd);
772
773         scrolled = gtk_scrolled_window_new(NULL, NULL);
774         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
775         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
776                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
777         gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
778         gtk_widget_show(scrolled);
779
780         store = gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
781         pkd->keyword_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
782         g_object_unref(store);
783         
784         gtk_widget_set_size_request(pkd->keyword_treeview, -1, 400);
785
786         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pkd->keyword_treeview), FALSE);
787
788         gtk_tree_view_set_search_column(GTK_TREE_VIEW(pkd->keyword_treeview), KEYWORD_COLUMN_TEXT);
789
790         column = gtk_tree_view_column_new();
791         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
792
793         renderer = gtk_cell_renderer_toggle_new();
794         gtk_tree_view_column_pack_start(column, renderer, FALSE);
795         gtk_tree_view_column_add_attribute(column, renderer, "active", KEYWORD_COLUMN_TOGGLE);
796         g_signal_connect(G_OBJECT(renderer), "toggled",
797                          G_CALLBACK(bar_pane_keywords_keyword_toggle), pkd);
798
799         renderer = gtk_cell_renderer_text_new();
800         gtk_tree_view_column_pack_start(column, renderer, TRUE);
801         gtk_tree_view_column_add_attribute(column, renderer, "text", KEYWORD_COLUMN_TEXT);
802
803         gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
804
805         column = gtk_tree_view_column_new();
806         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
807
808         renderer = gtk_cell_renderer_combo_new();
809         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE,
810                                          "model", create_marks_list(),
811                                          "text-column", 0,
812                                          "has-entry", FALSE,
813                                          NULL);
814
815         gtk_tree_view_column_pack_start(column, renderer, TRUE);
816         gtk_tree_view_column_add_attribute(column, renderer, "text", KEYWORD_COLUMN_MARK);
817         g_signal_connect (renderer, "edited",
818                           G_CALLBACK (bar_pane_keywords_mark_edited), pkd);
819         gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
820
821
822         gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_treeview);
823         gtk_widget_show(pkd->keyword_treeview);
824
825         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
826
827         return pkd->widget;
828 }
829
830 GtkWidget *bar_pane_keywords_new_from_config(const gchar **attribute_names, const gchar **attribute_values)
831 {
832         gchar *title = g_strdup(_("NoName"));
833         gchar *key = g_strdup(COMMENT_KEY);
834         gboolean expanded = TRUE;
835
836         while (*attribute_names)
837                 {
838                 const gchar *option = *attribute_names++;
839                 const gchar *value = *attribute_values++;
840
841                 READ_CHAR_FULL("pane.title", title);
842                 READ_CHAR_FULL("key", key);
843                 READ_BOOL_FULL("pane.expanded", expanded);
844                 
845
846                 DEBUG_1("unknown attribute %s = %s", option, value);
847                 }
848         
849         return bar_pane_keywords_new(title, key, expanded);
850 }
851
852 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */