config file format changed to XML
[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_indent(outstr, indent);
486         g_string_append_printf(outstr, "<pane_keywords\n");
487         indent++;
488         WRITE_CHAR(*pkd, pane.title);
489         WRITE_BOOL(*pkd, pane.expanded);
490         WRITE_CHAR(*pkd, key);
491         indent--;
492         write_indent(outstr, indent);
493         g_string_append_printf(outstr, "/>\n");
494 }
495
496 gint bar_pane_keywords_event(GtkWidget *bar, GdkEvent *event)
497 {
498         PaneKeywordsData *pkd;
499
500         pkd = g_object_get_data(G_OBJECT(bar), "pane_data");
501         if (!pkd) return FALSE;
502
503         if (GTK_WIDGET_HAS_FOCUS(pkd->keyword_view)) return gtk_widget_event(pkd->keyword_view, event);
504
505         return FALSE;
506 }
507
508 static void bar_pane_keywords_keyword_set(PaneKeywordsData *pkd, const gchar *keyword, gint active)
509 {
510         GList *list;
511         gint found;
512
513         if (!keyword) return;
514
515         list = keyword_list_pull(pkd->keyword_view);
516         found = find_string_in_list(list, keyword);
517
518         if (active != found)
519                 {
520                 if (found)
521                         {
522                         GList *work = list;
523
524                         while (work)
525                                 {
526                                 gchar *key = work->data;
527                                 work = work->next;
528
529                                 if (key && keyword && strcmp(key, keyword) == 0)
530                                         {
531                                         list = g_list_remove(list, key);
532                                         g_free(key);
533                                         }
534                                 }
535                         }
536                 else
537                         {
538                         list = g_list_append(list, g_strdup(keyword));
539                         }
540
541                 keyword_list_push(pkd->keyword_view, list);
542                 }
543
544         string_list_free(list);
545 }
546
547 static void bar_pane_keywords_keyword_toggle(GtkCellRendererToggle *toggle, const gchar *path, gpointer data)
548 {
549         PaneKeywordsData *pkd = data;
550         GtkTreeModel *store;
551         GtkTreeIter iter;
552         GtkTreePath *tpath;
553         gchar *key = NULL;
554         gboolean active;
555
556         store = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
557
558         tpath = gtk_tree_path_new_from_string(path);
559         gtk_tree_model_get_iter(store, &iter, tpath);
560         gtk_tree_path_free(tpath);
561
562         gtk_tree_model_get(store, &iter, KEYWORD_COLUMN_TOGGLE, &active,
563                                          KEYWORD_COLUMN_TEXT, &key, -1);
564         active = (!active);
565         gtk_list_store_set(GTK_LIST_STORE(store), &iter, KEYWORD_COLUMN_TOGGLE, active, -1);
566
567         bar_pane_keywords_keyword_set(pkd, key, active);
568         g_free(key);
569 }
570
571 static void bar_pane_keywords_set_selection(PaneKeywordsData *pkd, gboolean append)
572 {
573         GList *keywords = NULL;
574         GList *list = NULL;
575         GList *work;
576
577         if (!pkd->pane.list_func) return;
578
579         keywords = keyword_list_pull(pkd->keyword_view);
580
581         list = pkd->pane.list_func(pkd->pane.list_data);
582         work = list;
583         while (work)
584                 {
585                 FileData *fd = work->data;
586                 work = work->next;
587
588                 if (append)
589                         {
590                         metadata_append_list(fd, KEYWORD_KEY, keywords);
591                         }
592                 else
593                         {
594                         metadata_write_list(fd, KEYWORD_KEY, keywords);
595                         }
596                 }
597
598         filelist_free(list);
599         string_list_free(keywords);
600 }
601
602 static void bar_pane_keywords_sel_add_cb(GtkWidget *button, gpointer data)
603 {
604         PaneKeywordsData *pkd = data;
605
606         bar_pane_keywords_set_selection(pkd, TRUE);
607 }
608
609 static void bar_pane_keywords_sel_replace_cb(GtkWidget *button, gpointer data)
610 {
611         PaneKeywordsData *pkd = data;
612
613         bar_pane_keywords_set_selection(pkd, FALSE);
614 }
615
616 static void bar_pane_keywords_populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer data)
617 {
618         PaneKeywordsData *pkd = data;
619
620         menu_item_add_divider(GTK_WIDGET(menu));
621         menu_item_add_stock(GTK_WIDGET(menu), _("Add keywords to selected files"), GTK_STOCK_ADD, G_CALLBACK(bar_pane_keywords_sel_add_cb), pkd);
622         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);
623 }
624
625
626 static void bar_pane_keywords_notify_cb(FileData *fd, NotifyType type, gpointer data)
627 {
628         PaneKeywordsData *pkd = data;
629         if (fd == pkd->fd) bar_pane_keywords_update(pkd);
630 }
631
632 static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data)
633 {
634         PaneKeywordsData *pkd = data;
635
636         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
637         bar_pane_keywords_write(pkd);
638         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
639 }
640
641 static void bar_pane_keywords_mark_edited (GtkCellRendererText *cell, const gchar *path, const gchar *text, gpointer data)
642 {
643         PaneKeywordsData *pkd = data;
644         GtkTreeModel *store;
645         GtkTreeIter iter;
646         GtkTreePath *tpath;
647         gchar *key = NULL;
648         gint i;
649         FileDataGetMarkFunc get_mark_func;
650         FileDataSetMarkFunc set_mark_func;
651         gpointer mark_func_data;
652
653         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
654
655         store = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
656
657         tpath = gtk_tree_path_new_from_string(path);
658         gtk_tree_model_get_iter(store, &iter, tpath);
659         gtk_tree_path_free(tpath);
660
661         gtk_tree_model_get(store, &iter, KEYWORD_COLUMN_TEXT, &key, -1);
662
663         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
664                 {
665                 file_data_get_registered_mark_func(i, &get_mark_func, &set_mark_func, &mark_func_data);
666                 if (get_mark_func == meta_data_get_keyword_mark && strcmp(mark_func_data, key) == 0) 
667                         {
668                         g_free(mark_func_data);
669                         file_data_register_mark_func(i, NULL, NULL, NULL);
670                         }
671                 }
672
673         if (sscanf(text, " %d ", &i) &&i >=1 && i <= FILEDATA_MARKS_SIZE)
674                 {
675                 i--;
676                 file_data_get_registered_mark_func(i, &get_mark_func, &set_mark_func, &mark_func_data);
677                 if (get_mark_func == meta_data_get_keyword_mark && mark_func_data) g_free(mark_func_data); 
678                 file_data_register_mark_func(i, meta_data_get_keyword_mark, meta_data_set_keyword_mark, g_strdup(key));
679                 }
680
681         g_free(key);
682
683         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
684         bar_pane_keywords_update(pkd);
685 }
686
687 void bar_pane_keywords_close(GtkWidget *bar)
688 {
689         PaneKeywordsData *pkd;
690
691         pkd = g_object_get_data(G_OBJECT(bar), "pane_data");
692         if (!pkd) return;
693
694         gtk_widget_destroy(pkd->widget);
695 }
696
697 static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data)
698 {
699         PaneKeywordsData *pkd = data;
700
701         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
702
703         file_data_unref(pkd->fd);
704         g_free(pkd->key);
705
706         g_free(pkd);
707 }
708
709 static GtkTreeModel *create_marks_list(void)
710 {
711         GtkListStore *model;
712         GtkTreeIter iter;
713         gint i;
714
715         /* create list store */
716         model = gtk_list_store_new(1, G_TYPE_STRING);
717         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
718                 {
719                 gchar str[10];
720                 g_sprintf(str, " %d ", i + 1);
721                 gtk_list_store_append (model, &iter);
722                 gtk_list_store_set(model, &iter, 0, str, -1);
723                 }
724         gtk_list_store_append (model, &iter);
725         gtk_list_store_set(model, &iter, 0, " ... ", -1);
726         return GTK_TREE_MODEL(model);
727 }
728
729 GtkWidget *bar_pane_keywords_new(const gchar *title, const gchar *key, gboolean expanded)
730 {
731         PaneKeywordsData *pkd;
732         GtkWidget *hbox;
733         GtkWidget *scrolled;
734         GtkTextBuffer *buffer;
735         GtkListStore *store;
736         GtkTreeViewColumn *column;
737         GtkCellRenderer *renderer;
738
739         pkd = g_new0(PaneKeywordsData, 1);
740
741         pkd->pane.pane_set_fd = bar_pane_keywords_set_fd;
742         pkd->pane.pane_event = bar_pane_keywords_event;
743         pkd->pane.pane_write_config = bar_pane_keywords_write_config;
744         pkd->pane.title = g_strdup(title);
745         pkd->pane.expanded = expanded;
746
747         pkd->key = g_strdup(key);
748         
749
750         hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
751
752         pkd->widget = hbox;
753         g_object_set_data(G_OBJECT(pkd->widget), "pane_data", pkd);
754         g_signal_connect(G_OBJECT(pkd->widget), "destroy",
755                          G_CALLBACK(bar_pane_keywords_destroy), pkd);
756         gtk_widget_show(hbox);
757
758         scrolled = gtk_scrolled_window_new(NULL, NULL);
759         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
760         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
761                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
762         gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
763         gtk_widget_show(scrolled);
764
765         pkd->keyword_view = gtk_text_view_new();
766         gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_view);
767         g_signal_connect(G_OBJECT(pkd->keyword_view), "populate-popup",
768                          G_CALLBACK(bar_pane_keywords_populate_popup_cb), pkd);
769         gtk_widget_show(pkd->keyword_view);
770
771         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
772         g_signal_connect(G_OBJECT(buffer), "changed",
773                          G_CALLBACK(bar_pane_keywords_changed), pkd);
774
775         scrolled = gtk_scrolled_window_new(NULL, NULL);
776         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
777         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
778                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
779         gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
780         gtk_widget_show(scrolled);
781
782         store = gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
783         pkd->keyword_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
784         g_object_unref(store);
785         
786         gtk_widget_set_size_request(pkd->keyword_treeview, -1, 400);
787
788         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pkd->keyword_treeview), FALSE);
789
790         gtk_tree_view_set_search_column(GTK_TREE_VIEW(pkd->keyword_treeview), KEYWORD_COLUMN_TEXT);
791
792         column = gtk_tree_view_column_new();
793         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
794
795         renderer = gtk_cell_renderer_toggle_new();
796         gtk_tree_view_column_pack_start(column, renderer, FALSE);
797         gtk_tree_view_column_add_attribute(column, renderer, "active", KEYWORD_COLUMN_TOGGLE);
798         g_signal_connect(G_OBJECT(renderer), "toggled",
799                          G_CALLBACK(bar_pane_keywords_keyword_toggle), pkd);
800
801         renderer = gtk_cell_renderer_text_new();
802         gtk_tree_view_column_pack_start(column, renderer, TRUE);
803         gtk_tree_view_column_add_attribute(column, renderer, "text", KEYWORD_COLUMN_TEXT);
804
805         gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
806
807         column = gtk_tree_view_column_new();
808         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
809
810         renderer = gtk_cell_renderer_combo_new();
811         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE,
812                                          "model", create_marks_list(),
813                                          "text-column", 0,
814                                          "has-entry", FALSE,
815                                          NULL);
816
817         gtk_tree_view_column_pack_start(column, renderer, TRUE);
818         gtk_tree_view_column_add_attribute(column, renderer, "text", KEYWORD_COLUMN_MARK);
819         g_signal_connect (renderer, "edited",
820                           G_CALLBACK (bar_pane_keywords_mark_edited), pkd);
821         gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
822
823
824         gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_treeview);
825         gtk_widget_show(pkd->keyword_treeview);
826
827         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
828
829         return pkd->widget;
830 }
831
832 GtkWidget *bar_pane_keywords_new_from_config(const gchar **attribute_names, const gchar **attribute_values)
833 {
834         gchar *title = g_strdup(_("NoName"));
835         gchar *key = g_strdup(COMMENT_KEY);
836         gboolean expanded = TRUE;
837
838         while (*attribute_names)
839                 {
840                 const gchar *option = *attribute_names++;
841                 const gchar *value = *attribute_values++;
842
843                 READ_CHAR_FULL("pane.title", title);
844                 READ_CHAR_FULL("key", key);
845                 READ_BOOL_FULL("pane.expanded", expanded);
846                 
847
848                 DEBUG_1("unknown attribute %s = %s", option, value);
849                 }
850         
851         return bar_pane_keywords_new(title, key, expanded);
852 }
853
854 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */