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