be more verbose on parse errors
[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 #include "dnd.h"
31
32
33 static void bar_pane_keywords_keyword_update_all(void);
34 static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data);
35
36 /*
37  *-------------------------------------------------------------------
38  * keyword / comment utils
39  *-------------------------------------------------------------------
40  */
41
42
43 GList *keyword_list_pull(GtkWidget *text_widget)
44 {
45         GList *list;
46         gchar *text;
47
48         text = text_widget_text_pull(text_widget);
49         list = string_to_keywords_list(text);
50
51         g_free(text);
52
53         return list;
54 }
55
56 /* the "changed" signal should be blocked before calling this */
57 static void keyword_list_push(GtkWidget *textview, GList *list)
58 {
59         GtkTextBuffer *buffer;
60         GtkTextIter start, end;
61
62         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
63         gtk_text_buffer_get_bounds(buffer, &start, &end);
64         gtk_text_buffer_delete(buffer, &start, &end);
65
66         while (list)
67                 {
68                 const gchar *word = list->data;
69                 GtkTextIter iter;
70
71                 gtk_text_buffer_get_end_iter(buffer, &iter);
72                 if (word) gtk_text_buffer_insert(buffer, &iter, word, -1);
73                 gtk_text_buffer_get_end_iter(buffer, &iter);
74                 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
75
76                 list = list->next;
77                 }
78 }
79
80
81 /*
82  *-------------------------------------------------------------------
83  * info bar
84  *-------------------------------------------------------------------
85  */
86
87
88 enum {
89         FILTER_KEYWORD_COLUMN_TOGGLE = 0,
90         FILTER_KEYWORD_COLUMN_MARK,
91         FILTER_KEYWORD_COLUMN_NAME,
92         FILTER_KEYWORD_COLUMN_IS_KEYWORD,
93         FILTER_KEYWORD_COLUMN_COUNT
94 };
95
96 static GType filter_keyword_column_types[] = {G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN};
97
98 typedef struct _PaneKeywordsData PaneKeywordsData;
99 struct _PaneKeywordsData
100 {
101         PaneData pane;
102         GtkWidget *widget;
103
104         GtkWidget *keyword_view;
105         GtkWidget *keyword_treeview;
106
107         GtkTreePath *click_tpath;
108
109         gboolean expand_checked;
110         gboolean collapse_unchecked;
111         gboolean hide_unchecked;
112
113         gint idle_id;   
114         FileData *fd;
115         gchar *key;
116 };
117
118 typedef struct _ConfDialogData ConfDialogData;
119 struct _ConfDialogData
120 {
121         PaneKeywordsData *pkd;
122         GtkTreePath *click_tpath;
123         
124         /* dialog parts */
125         GenericDialog *gd;
126         GtkWidget *edit_widget;
127         gboolean is_keyword;
128         
129         gboolean edit_existing;
130 };
131
132 static GList *bar_list = NULL;
133
134
135 static void bar_pane_keywords_write(PaneKeywordsData *pkd)
136 {
137         GList *list;
138
139         if (!pkd->fd) return;
140
141         list = keyword_list_pull(pkd->keyword_view);
142
143         metadata_write_list(pkd->fd, KEYWORD_KEY, list);
144
145         string_list_free(list);
146 }
147
148 gboolean bar_keyword_tree_expand_if_set_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
149 {
150         PaneKeywordsData *pkd = data;
151         gboolean set;
152
153         gtk_tree_model_get(model, iter, FILTER_KEYWORD_COLUMN_TOGGLE, &set, -1);
154         
155         if (set && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(pkd->keyword_treeview), path))
156                 {
157                 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(pkd->keyword_treeview), path);
158                 }
159         return FALSE;
160 }
161
162 gboolean bar_keyword_tree_collapse_if_unset_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
163 {
164         PaneKeywordsData *pkd = data;
165         gboolean set;
166
167         gtk_tree_model_get(model, iter, FILTER_KEYWORD_COLUMN_TOGGLE, &set, -1);
168         
169         if (!set && gtk_tree_view_row_expanded(GTK_TREE_VIEW(pkd->keyword_treeview), path))
170                 {
171                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(pkd->keyword_treeview), path);
172                 }
173         return FALSE;
174 }
175
176 static void bar_keyword_tree_sync(PaneKeywordsData *pkd)
177 {
178         GtkTreeModel *model;
179
180         GtkTreeModel *keyword_tree;
181         GList *keywords;
182
183         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
184         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
185
186         keywords = keyword_list_pull(pkd->keyword_view);
187         keyword_show_set_in(GTK_TREE_STORE(keyword_tree), model, keywords);
188         if (pkd->hide_unchecked) keyword_hide_unset_in(GTK_TREE_STORE(keyword_tree), model, keywords);
189         string_list_free(keywords);
190
191         gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(model));
192
193         if (pkd->expand_checked) gtk_tree_model_foreach(model, bar_keyword_tree_expand_if_set_cb, pkd);
194         if (pkd->collapse_unchecked) gtk_tree_model_foreach(model, bar_keyword_tree_collapse_if_unset_cb, pkd);
195 }
196
197 static void bar_pane_keywords_keyword_update_all(void)
198 {
199         GList *work;
200
201         work = bar_list;
202         while (work)
203                 {
204                 PaneKeywordsData *pkd;
205 //              GList *keywords;
206
207                 pkd = work->data;
208                 work = work->next;
209
210                 bar_keyword_tree_sync(pkd);
211                 }
212 }
213
214 static void bar_pane_keywords_update(PaneKeywordsData *pkd)
215 {
216         GList *keywords = NULL;
217         GtkTextBuffer *keyword_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
218
219         g_signal_handlers_block_by_func(keyword_buffer, bar_pane_keywords_changed, pkd);
220
221         keywords = metadata_read_list(pkd->fd, KEYWORD_KEY, METADATA_PLAIN);
222         keyword_list_push(pkd->keyword_view, keywords);
223         bar_keyword_tree_sync(pkd);
224         string_list_free(keywords);
225         
226         g_signal_handlers_unblock_by_func(keyword_buffer, bar_pane_keywords_changed, pkd);
227
228 }
229
230 void bar_pane_keywords_set_fd(GtkWidget *pane, FileData *fd)
231 {
232         PaneKeywordsData *pkd;
233
234         pkd = g_object_get_data(G_OBJECT(pane), "pane_data");
235         if (!pkd) return;
236
237         file_data_unref(pkd->fd);
238         pkd->fd = file_data_ref(fd);
239
240         bar_pane_keywords_update(pkd);
241 }
242
243 static void bar_pane_keywords_write_config(GtkWidget *pane, GString *outstr, gint indent)
244 {
245         PaneKeywordsData *pkd;
246
247         pkd = g_object_get_data(G_OBJECT(pane), "pane_data");
248         if (!pkd) return;
249
250         WRITE_NL(); WRITE_STRING("<pane_keywords ");
251         write_char_option(outstr, indent, "pane.title", gtk_label_get_text(GTK_LABEL(pkd->pane.title)));
252         WRITE_BOOL(*pkd, pane.expanded);
253         WRITE_CHAR(*pkd, key);
254         WRITE_STRING("/>");
255 }
256
257 gint bar_pane_keywords_event(GtkWidget *bar, GdkEvent *event)
258 {
259         PaneKeywordsData *pkd;
260
261         pkd = g_object_get_data(G_OBJECT(bar), "pane_data");
262         if (!pkd) return FALSE;
263
264         if (GTK_WIDGET_HAS_FOCUS(pkd->keyword_view)) return gtk_widget_event(pkd->keyword_view, event);
265
266         return FALSE;
267 }
268
269 static void bar_pane_keywords_keyword_toggle(GtkCellRendererToggle *toggle, const gchar *path, gpointer data)
270 {
271         PaneKeywordsData *pkd = data;
272         GtkTreeModel *model;
273         GtkTreeIter iter;
274         GtkTreePath *tpath;
275         gboolean active;
276         GList *list;
277         GtkTreeIter child_iter;
278         GtkTreeModel *keyword_tree;
279         
280         GtkTextBuffer *keyword_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
281
282         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
283
284         tpath = gtk_tree_path_new_from_string(path);
285         gtk_tree_model_get_iter(model, &iter, tpath);
286         gtk_tree_path_free(tpath);
287
288         gtk_tree_model_get(model, &iter, FILTER_KEYWORD_COLUMN_TOGGLE, &active, -1);
289         active = (!active);
290
291
292         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
293         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &child_iter, &iter);
294
295         list = keyword_list_pull(pkd->keyword_view);
296         if (active) 
297                 keyword_tree_set(keyword_tree, &child_iter, &list);
298         else
299                 keyword_tree_reset(keyword_tree, &child_iter, &list);
300         
301         g_signal_handlers_block_by_func(keyword_buffer, bar_pane_keywords_changed, pkd);
302         keyword_list_push(pkd->keyword_view, list);
303         string_list_free(list);
304         g_signal_handlers_unblock_by_func(keyword_buffer, bar_pane_keywords_changed, pkd);
305
306         /* call this just once in the end */
307         bar_pane_keywords_changed(keyword_buffer, pkd);
308         /*
309           bar_pane_keywords_change calls bar_keyword_tree_sync, no need to do it again
310         bar_keyword_tree_sync(pkd);
311         */
312 }
313
314 void bar_pane_keywords_filter_modify(GtkTreeModel *model, GtkTreeIter *iter, GValue *value, gint column, gpointer data)
315 {
316         PaneKeywordsData *pkd = data;
317         GtkTreeModel *keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
318         GtkTreeIter child_iter;
319
320         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &child_iter, iter);
321         
322         memset(value, 0, sizeof (GValue));
323
324         switch (column)
325                 {
326                 case FILTER_KEYWORD_COLUMN_TOGGLE:
327                         {
328                         GList *keywords = keyword_list_pull(pkd->keyword_view);
329                         gboolean set = keyword_tree_is_set(keyword_tree, &child_iter, keywords);
330                         string_list_free(keywords);
331                         
332                         g_value_init(value, G_TYPE_BOOLEAN);
333                         g_value_set_boolean(value, set);
334                         break;
335                         }
336                 case FILTER_KEYWORD_COLUMN_MARK:
337                         gtk_tree_model_get_value(keyword_tree, &child_iter, KEYWORD_COLUMN_MARK, value);
338                         break;
339                 case FILTER_KEYWORD_COLUMN_NAME:
340                         gtk_tree_model_get_value(keyword_tree, &child_iter, KEYWORD_COLUMN_NAME, value);
341                         break;
342                 case FILTER_KEYWORD_COLUMN_IS_KEYWORD:
343                         gtk_tree_model_get_value(keyword_tree, &child_iter, KEYWORD_COLUMN_IS_KEYWORD, value);
344                         break;
345                 }
346         return;
347
348 }
349
350 gboolean bar_pane_keywords_filter_visible(GtkTreeModel *keyword_tree, GtkTreeIter *iter, gpointer data)
351 {
352         GtkTreeModel *filter = data;
353         
354         return !keyword_is_hidden_in(keyword_tree, iter, filter);
355 }
356
357 static void bar_pane_keywords_set_selection(PaneKeywordsData *pkd, gboolean append)
358 {
359         GList *keywords = NULL;
360         GList *list = NULL;
361         GList *work;
362
363         keywords = keyword_list_pull(pkd->keyword_view);
364
365         list = layout_selection_list(pkd->pane.lw);
366         work = list;
367         while (work)
368                 {
369                 FileData *fd = work->data;
370                 work = work->next;
371
372                 if (append)
373                         {
374                         metadata_append_list(fd, KEYWORD_KEY, keywords);
375                         }
376                 else
377                         {
378                         metadata_write_list(fd, KEYWORD_KEY, keywords);
379                         }
380                 }
381
382         filelist_free(list);
383         string_list_free(keywords);
384 }
385
386 static void bar_pane_keywords_sel_add_cb(GtkWidget *button, gpointer data)
387 {
388         PaneKeywordsData *pkd = data;
389
390         bar_pane_keywords_set_selection(pkd, TRUE);
391 }
392
393 static void bar_pane_keywords_sel_replace_cb(GtkWidget *button, gpointer data)
394 {
395         PaneKeywordsData *pkd = data;
396
397         bar_pane_keywords_set_selection(pkd, FALSE);
398 }
399
400 static void bar_pane_keywords_populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer data)
401 {
402         PaneKeywordsData *pkd = data;
403
404         menu_item_add_divider(GTK_WIDGET(menu));
405         menu_item_add_stock(GTK_WIDGET(menu), _("Add keywords to selected files"), GTK_STOCK_ADD, G_CALLBACK(bar_pane_keywords_sel_add_cb), pkd);
406         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);
407 }
408
409
410 static void bar_pane_keywords_notify_cb(FileData *fd, NotifyType type, gpointer data)
411 {
412         PaneKeywordsData *pkd = data;
413         if ((type & (NOTIFY_REREAD | NOTIFY_CHANGE | NOTIFY_METADATA)) && fd == pkd->fd) bar_pane_keywords_update(pkd);
414 }
415
416 static gboolean bar_pane_keywords_changed_idle_cb(gpointer data)
417 {
418         PaneKeywordsData *pkd = data;
419
420         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
421         bar_pane_keywords_write(pkd);
422         bar_keyword_tree_sync(pkd);
423         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
424         pkd->idle_id = -1;
425         return FALSE;
426 }
427
428 static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data)
429 {
430         PaneKeywordsData *pkd = data;
431
432         if (pkd->idle_id != -1) return;
433         /* higher prio than redraw */
434         pkd->idle_id = g_idle_add_full(G_PRIORITY_HIGH_IDLE, bar_pane_keywords_changed_idle_cb, pkd, NULL);
435 }
436
437
438 /*
439  *-------------------------------------------------------------------
440  * dnd
441  *-------------------------------------------------------------------
442  */
443
444
445 static GtkTargetEntry bar_pane_keywords_drag_types[] = {
446         { TARGET_APP_KEYWORD_PATH_STRING, GTK_TARGET_SAME_WIDGET, TARGET_APP_KEYWORD_PATH },
447         { "text/plain", 0, TARGET_TEXT_PLAIN }
448 };
449 static gint n_keywords_drag_types = 2;
450
451
452 static GtkTargetEntry bar_pane_keywords_drop_types[] = {
453         { TARGET_APP_KEYWORD_PATH_STRING, GTK_TARGET_SAME_WIDGET, TARGET_APP_KEYWORD_PATH },
454         { "text/plain", 0, TARGET_TEXT_PLAIN }
455 };
456 static gint n_keywords_drop_types = 2;
457
458
459 static void bar_pane_keywords_dnd_get(GtkWidget *tree_view, GdkDragContext *context,
460                                      GtkSelectionData *selection_data, guint info,
461                                      guint time, gpointer data)
462 {
463         GtkTreeIter iter;
464         GtkTreeModel *model;
465         GtkTreeIter child_iter;
466         GtkTreeModel *keyword_tree;
467
468         GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 
469
470         if (!gtk_tree_selection_get_selected(sel, &model, &iter)) return;
471
472         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
473         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &child_iter, &iter);
474
475         switch (info)
476                 {
477                 case TARGET_APP_KEYWORD_PATH:
478                         {
479                         GList *path = keyword_tree_get_path(keyword_tree, &child_iter);
480                         gtk_selection_data_set(selection_data, selection_data->target,
481                                                8, (gpointer) &path, sizeof(path));
482                         break;
483                         }
484
485                 case TARGET_TEXT_PLAIN:
486                 default:
487                         {
488                         gchar *name = keyword_get_name(keyword_tree, &child_iter);
489                         gtk_selection_data_set_text(selection_data, name, -1);
490                         g_free(name);
491                         }
492                         break;
493                 }
494 }
495
496 static void bar_pane_keywords_dnd_begin(GtkWidget *tree_view, GdkDragContext *context, gpointer data)
497 {
498         GtkTreeIter iter;
499         GtkTreeModel *model;
500         GtkTreeIter child_iter;
501         GtkTreeModel *keyword_tree;
502         gchar *name;
503
504         GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); 
505
506         if (!gtk_tree_selection_get_selected(sel, &model, &iter)) return;
507
508         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
509         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &child_iter, &iter);
510
511         name = keyword_get_name(keyword_tree, &child_iter);
512
513         dnd_set_drag_label(tree_view, context, name);
514         g_free(name);
515
516 }
517
518 static void bar_pane_keywords_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
519 {
520 }
521
522
523 static gboolean bar_pane_keywords_dnd_can_move(GtkTreeModel *keyword_tree, GtkTreeIter *src_kw_iter, GtkTreeIter *dest_kw_iter)
524 {
525         gchar *src_name;
526         GtkTreeIter parent;
527         
528         if (dest_kw_iter && keyword_same_parent(keyword_tree, src_kw_iter, dest_kw_iter)) 
529                 {
530                 return TRUE; /* reordering of siblings is ok */
531                 }
532         if (!dest_kw_iter && !gtk_tree_model_iter_parent(keyword_tree, &parent, src_kw_iter))
533                 {
534                 return TRUE; /* reordering of top-level siblings is ok */
535                 }
536
537         src_name = keyword_get_name(keyword_tree, src_kw_iter);
538         if (keyword_exists(keyword_tree, NULL, dest_kw_iter, src_name, FALSE))
539                 {
540                 g_free(src_name);
541                 return FALSE;
542         }
543         g_free(src_name);
544         return TRUE;
545 }
546
547 static gboolean bar_pane_keywords_dnd_skip_existing(GtkTreeModel *keyword_tree, GtkTreeIter *dest_kw_iter, GList **keywords)
548 {
549         /* we have to find at least one keyword that does not already exist as a sibling of dest_kw_iter */
550         GList *work = *keywords;
551         while (work)
552                 {
553                 gchar *keyword = work->data;
554                 if (keyword_exists(keyword_tree, NULL, dest_kw_iter, keyword, FALSE))
555                         {
556                         GList *next = work->next;
557                         g_free(keyword);
558                         *keywords = g_list_delete_link(*keywords, work);
559                         work = next;
560                         }
561                 else
562                         {
563                         work = work->next;
564                         }
565                 }
566         return !!*keywords;
567 }
568
569 static void bar_pane_keywords_dnd_receive(GtkWidget *tree_view, GdkDragContext *context,
570                                           gint x, gint y,
571                                           GtkSelectionData *selection_data, guint info,
572                                           guint time, gpointer data)
573 {
574         PaneKeywordsData *pkd = data;
575         GtkTreePath *tpath = NULL;
576         GtkTreeViewDropPosition pos;
577         GtkTreeModel *model;
578
579         GtkTreeModel *keyword_tree;
580         gboolean src_valid = FALSE;
581         GList *new_keywords = NULL;
582         GList *work;
583
584         /* iterators for keyword_tree */
585         GtkTreeIter src_kw_iter;
586         GtkTreeIter dest_kw_iter;
587         GtkTreeIter new_kw_iter;
588
589         g_signal_stop_emission_by_name(tree_view, "drag_data_received");
590
591         model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
592         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
593
594         gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(tree_view), x, y, &tpath, &pos);
595         gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(tree_view), NULL, pos);
596
597         switch (info)
598                 {
599                 case TARGET_APP_KEYWORD_PATH:
600                         {
601                         GList *path = *(gpointer *)selection_data->data;
602                         src_valid = keyword_tree_get_iter(keyword_tree, &src_kw_iter, path);
603                         string_list_free(path);
604                         break;
605                         }
606                 default:
607                         new_keywords = string_to_keywords_list((gchar *)selection_data->data);
608                         break;
609                 }
610
611         if (tpath)
612                 {
613                 GtkTreeIter dest_iter;
614                 gtk_tree_model_get_iter(model, &dest_iter, tpath);
615                 gtk_tree_path_free(tpath);
616                 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &dest_kw_iter, &dest_iter);
617
618                 if (src_valid && gtk_tree_store_is_ancestor(GTK_TREE_STORE(keyword_tree), &src_kw_iter, &dest_kw_iter))
619                         {
620                         /* can't move to it's own child */
621                         return;
622                         }
623
624                 if (src_valid && keyword_compare(keyword_tree, &src_kw_iter, &dest_kw_iter) == 0)
625                         {
626                         /* can't move to itself */
627                         return;
628                         }
629
630                 if ((pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) &&
631                     !gtk_tree_model_iter_has_child(keyword_tree, &dest_kw_iter))
632                         {
633                         /* the node has no children, all keywords can be added */
634                         gtk_tree_store_append(GTK_TREE_STORE(keyword_tree), &new_kw_iter, &dest_kw_iter);
635                         }
636                 else
637                         {
638                         if (src_valid && !bar_pane_keywords_dnd_can_move(keyword_tree, &src_kw_iter, &dest_kw_iter))
639                                 {
640                                 /* the keyword can't be moved if the same name already exist */
641                                 return;
642                                 }
643                         if (new_keywords && !bar_pane_keywords_dnd_skip_existing(keyword_tree, &dest_kw_iter, &new_keywords))
644                                 {
645                                 /* the keywords can't be added if the same name already exist */
646                                 return;
647                                 }
648                                 
649                         switch (pos)
650                                 {
651                                 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
652                                 case GTK_TREE_VIEW_DROP_BEFORE:
653                                         gtk_tree_store_insert_before(GTK_TREE_STORE(keyword_tree), &new_kw_iter, NULL, &dest_kw_iter);
654                                         break;
655                                 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
656                                 case GTK_TREE_VIEW_DROP_AFTER:
657                                         gtk_tree_store_insert_after(GTK_TREE_STORE(keyword_tree), &new_kw_iter, NULL, &dest_kw_iter);
658                                         break;
659                                 }
660                         }
661                         
662                 }
663         else
664                 {
665                 if (src_valid && !bar_pane_keywords_dnd_can_move(keyword_tree, &src_kw_iter, NULL))
666                         {
667                         /* the keyword can't be moved if the same name already exist */
668                         return;
669                         }
670                 if (new_keywords && !bar_pane_keywords_dnd_skip_existing(keyword_tree, NULL, &new_keywords))
671                         {
672                         /* the keywords can't be added if the same name already exist */
673                         return;
674                         }
675                 gtk_tree_store_append(GTK_TREE_STORE(keyword_tree), &new_kw_iter, NULL);
676                 }
677                 
678                 
679         if (src_valid)
680                 {
681                 keyword_move_recursive(GTK_TREE_STORE(keyword_tree), &new_kw_iter, &src_kw_iter);
682                 }
683         
684         work = new_keywords;
685         while (work)
686                 {
687                 gchar *keyword = work->data;
688                 keyword_set(GTK_TREE_STORE(keyword_tree), &new_kw_iter, keyword, TRUE);
689                 work = work->next;
690
691                 if (work)
692                         {
693                         GtkTreeIter add;
694                         gtk_tree_store_insert_after(GTK_TREE_STORE(keyword_tree), &add, NULL, &new_kw_iter);
695                         new_kw_iter = add;
696                         }
697                 }
698         string_list_free(new_keywords);
699         bar_keyword_tree_sync(pkd);
700 }
701
702 static gint bar_pane_keywords_dnd_motion(GtkWidget *tree_view, GdkDragContext *context,
703                                         gint x, gint y, guint time, gpointer data)
704 {
705         GtkTreePath *tpath = NULL;
706         GtkTreeViewDropPosition pos;
707         gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(tree_view), x, y, &tpath, &pos);
708         if (tpath)
709                 {
710                 GtkTreeModel *model;
711                 GtkTreeIter dest_iter;
712                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
713                 gtk_tree_model_get_iter(model, &dest_iter, tpath);
714                 if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE && gtk_tree_model_iter_has_child(model, &dest_iter))
715                         pos = GTK_TREE_VIEW_DROP_BEFORE;
716                 
717                 if (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER && gtk_tree_model_iter_has_child(model, &dest_iter))
718                         pos = GTK_TREE_VIEW_DROP_AFTER;
719                 }
720
721         gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(tree_view), tpath, pos);
722         gtk_tree_path_free(tpath);
723         
724         if (tree_view == gtk_drag_get_source_widget(context))
725                 gdk_drag_status(context, GDK_ACTION_MOVE, time);
726         else
727                 gdk_drag_status(context, GDK_ACTION_COPY, time);
728         
729         return TRUE;
730 }
731
732 /*
733  *-------------------------------------------------------------------
734  * edit dialog
735  *-------------------------------------------------------------------
736  */
737
738 static void bar_pane_keywords_edit_destroy_cb(GtkWidget *widget, gpointer data)
739 {
740         ConfDialogData *cdd = data;
741         gtk_tree_path_free(cdd->click_tpath);
742         g_free(cdd);
743 }
744
745
746 static void bar_pane_keywords_edit_cancel_cb(GenericDialog *gd, gpointer data)
747 {
748 }
749
750
751 static void bar_pane_keywords_edit_ok_cb(GenericDialog *gd, gpointer data)
752 {
753         ConfDialogData *cdd = data;
754         PaneKeywordsData *pkd = cdd->pkd;
755         GtkTreeModel *model;
756
757         GtkTreeModel *keyword_tree;
758         GtkTreeIter kw_iter;
759         
760         gboolean have_dest = FALSE;
761         
762         GList *keywords;
763
764         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
765         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
766         
767         if (cdd->click_tpath)
768                 {
769                 GtkTreeIter iter;
770                 if (gtk_tree_model_get_iter(model, &iter, cdd->click_tpath))
771                         {
772                         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &kw_iter, &iter);
773                         have_dest = TRUE;
774                         }
775                 }
776         
777         if (cdd->edit_existing && !have_dest) return;
778         
779         keywords = keyword_list_pull(cdd->edit_widget);
780         
781         if (cdd->edit_existing)
782                 {
783                 if (keywords && keywords->data && /* there should be one keyword */
784                     !keyword_exists(keyword_tree, NULL, &kw_iter, keywords->data, TRUE))
785                         {
786                         keyword_set(GTK_TREE_STORE(keyword_tree), &kw_iter, keywords->data, cdd->is_keyword);
787                         }
788                 }
789         else
790                 {
791                 GList *work = keywords;
792
793                 while (work)
794                         {
795                         GtkTreeIter add;
796                         if (keyword_exists(keyword_tree, NULL, have_dest ? &kw_iter : NULL, work->data, FALSE))
797                                 {
798                                 work = work->next;
799                                 continue;
800                                 }
801                         if (have_dest)
802                                 {
803                                 gtk_tree_store_insert_after(GTK_TREE_STORE(keyword_tree), &add, NULL, &kw_iter);
804                                 }
805                         else
806                                 {
807                                 gtk_tree_store_append(GTK_TREE_STORE(keyword_tree), &add, NULL);
808                                 have_dest = TRUE;
809                                 }
810                         kw_iter = add;
811                         keyword_set(GTK_TREE_STORE(keyword_tree), &kw_iter, work->data, cdd->is_keyword);
812                         work = work->next;
813                         }
814                 }
815         string_list_free(keywords);
816 }
817
818 static void bar_pane_keywords_conf_set_helper(GtkWidget *widget, gpointer data)
819 {
820         ConfDialogData *cdd = data;
821         cdd->is_keyword = FALSE;
822 }
823
824 static void bar_pane_keywords_conf_set_kw(GtkWidget *widget, gpointer data)
825 {
826         ConfDialogData *cdd = data;
827         cdd->is_keyword = TRUE;
828 }
829
830
831
832 static void bar_pane_keywords_edit_dialog(PaneKeywordsData *pkd, gboolean edit_existing)
833 {
834         ConfDialogData *cdd;
835         GenericDialog *gd;
836         GtkWidget *table;
837         GtkWidget *group;
838         GtkWidget *button;
839         
840         gchar *name = NULL;
841         gboolean is_keyword = TRUE;
842         
843
844         if (edit_existing && pkd->click_tpath)
845                 {
846                 GtkTreeModel *model;
847                 GtkTreeIter iter;
848                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
849
850                 if (gtk_tree_model_get_iter(model, &iter, pkd->click_tpath))
851                         {
852                         gtk_tree_model_get(model, &iter, FILTER_KEYWORD_COLUMN_NAME, &name,
853                                                          FILTER_KEYWORD_COLUMN_IS_KEYWORD, &is_keyword, -1);
854                         }
855                 else
856                         {
857                         return;
858                         }
859                 }
860                 
861         if (edit_existing && !name) return;
862         
863
864         cdd = g_new0(ConfDialogData, 1);
865         cdd->pkd =pkd;
866         cdd->click_tpath = pkd->click_tpath;
867         pkd->click_tpath = NULL;
868         cdd->is_keyword = is_keyword;
869         cdd->edit_existing = edit_existing;
870
871         cdd->gd = gd = generic_dialog_new(name ? _("Edit keyword") : _("Add keywords"), "keyword_edit",
872                                 pkd->widget, TRUE,
873                                 bar_pane_keywords_edit_cancel_cb, cdd);
874         g_signal_connect(G_OBJECT(gd->dialog), "destroy",
875                          G_CALLBACK(bar_pane_keywords_edit_destroy_cb), cdd);
876
877
878         generic_dialog_add_message(gd, NULL, name ? _("Configure keyword") : _("Add keyword"), NULL);
879
880         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
881                                   bar_pane_keywords_edit_ok_cb, TRUE);
882
883         table = pref_table_new(gd->vbox, 3, 1, FALSE, TRUE);
884         pref_table_label(table, 0, 0, _("Keyword:"), 1.0);
885         cdd->edit_widget = gtk_entry_new();
886         gtk_widget_set_size_request(cdd->edit_widget, 300, -1);
887         if (name) gtk_entry_set_text(GTK_ENTRY(cdd->edit_widget), name);
888         gtk_table_attach_defaults(GTK_TABLE(table), cdd->edit_widget, 1, 2, 0, 1);
889         /* here could eventually be a text view instead of entry */
890         generic_dialog_attach_default(gd, cdd->edit_widget);
891         gtk_widget_show(cdd->edit_widget);
892
893         group = pref_group_new(gd->vbox, FALSE, _("Keyword type:"), GTK_ORIENTATION_VERTICAL);
894
895         button = pref_radiobutton_new(group, NULL, _("Active keyword"),
896                                       (cdd->is_keyword),
897                                       G_CALLBACK(bar_pane_keywords_conf_set_kw), cdd);
898         button = pref_radiobutton_new(group, button, _("Helper"),
899                                       (!cdd->is_keyword),
900                                       G_CALLBACK(bar_pane_keywords_conf_set_helper), cdd);
901         g_free(name);
902
903         gtk_widget_show(gd->dialog);
904 }
905
906
907
908
909 /*
910  *-------------------------------------------------------------------
911  * popup menu
912  *-------------------------------------------------------------------
913  */
914
915 static void bar_pane_keywords_edit_dialog_cb(GtkWidget *menu_widget, gpointer data)
916 {
917         PaneKeywordsData *pkd = data;
918         bar_pane_keywords_edit_dialog(pkd, TRUE);
919 }
920
921 static void bar_pane_keywords_add_dialog_cb(GtkWidget *menu_widget, gpointer data)
922 {
923         PaneKeywordsData *pkd = data;
924         bar_pane_keywords_edit_dialog(pkd, FALSE);
925 }
926
927 static void bar_pane_keywords_connect_mark_cb(GtkWidget *menu_widget, gpointer data)
928 {
929         PaneKeywordsData *pkd = data;
930
931         GtkTreeModel *model;
932         GtkTreeIter iter;
933
934         GtkTreeModel *keyword_tree;
935         GtkTreeIter kw_iter;
936
937         gint mark = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_widget), "mark")) - 1;
938
939         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
940         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
941         
942         if (!pkd->click_tpath) return;
943         if (!gtk_tree_model_get_iter(model, &iter, pkd->click_tpath)) return;
944
945         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &kw_iter, &iter);
946
947         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
948
949         meta_data_connect_mark_with_keyword(keyword_tree, &kw_iter, mark);
950
951         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
952 //      bar_pane_keywords_update(pkd);
953 }
954
955
956 static void bar_pane_keywords_delete_cb(GtkWidget *menu_widget, gpointer data)
957 {
958         PaneKeywordsData *pkd = data;
959         GtkTreeModel *model;
960         GtkTreeIter iter;
961
962         GtkTreeModel *keyword_tree;
963         GtkTreeIter kw_iter;
964
965         if (!pkd->click_tpath) return;
966
967         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
968         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
969
970         if (!gtk_tree_model_get_iter(model, &iter, pkd->click_tpath)) return;
971         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &kw_iter, &iter);
972         
973         keyword_delete(GTK_TREE_STORE(keyword_tree), &kw_iter);
974 }
975
976 static void bar_pane_keywords_hide_cb(GtkWidget *menu_widget, gpointer data)
977 {
978         PaneKeywordsData *pkd = data;
979         GtkTreeModel *model;
980         GtkTreeIter iter;
981
982         GtkTreeModel *keyword_tree;
983         GtkTreeIter kw_iter;
984
985         if (!pkd->click_tpath) return;
986
987         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
988         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
989
990         if (!gtk_tree_model_get_iter(model, &iter, pkd->click_tpath)) return;
991         gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &kw_iter, &iter);
992         
993         keyword_hide_in(GTK_TREE_STORE(keyword_tree), &kw_iter, model);
994 }
995
996 static void bar_pane_keywords_show_all_cb(GtkWidget *menu_widget, gpointer data)
997 {
998         PaneKeywordsData *pkd = data;
999         GtkTreeModel *model;
1000
1001         GtkTreeModel *keyword_tree;
1002
1003         pkd->hide_unchecked = FALSE;
1004
1005         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
1006         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
1007
1008         keyword_show_all_in(GTK_TREE_STORE(keyword_tree), model);
1009         
1010         if (!pkd->collapse_unchecked) gtk_tree_view_expand_all(GTK_TREE_VIEW(pkd->keyword_treeview));
1011         bar_keyword_tree_sync(pkd);
1012 }
1013
1014 static void bar_pane_keywords_expand_checked_cb(GtkWidget *menu_widget, gpointer data)
1015 {
1016         PaneKeywordsData *pkd = data;
1017         GtkTreeModel *model;
1018
1019         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
1020         gtk_tree_model_foreach(model, bar_keyword_tree_expand_if_set_cb, pkd);
1021 }
1022
1023 static void bar_pane_keywords_collapse_unchecked_cb(GtkWidget *menu_widget, gpointer data)
1024 {
1025         PaneKeywordsData *pkd = data;
1026         GtkTreeModel *model;
1027
1028         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
1029         gtk_tree_model_foreach(model, bar_keyword_tree_collapse_if_unset_cb, pkd);
1030 }
1031
1032 static void bar_pane_keywords_hide_unchecked_cb(GtkWidget *menu_widget, gpointer data)
1033 {
1034         PaneKeywordsData *pkd = data;
1035         GtkTreeModel *model;
1036
1037         GtkTreeModel *keyword_tree;
1038         GList *keywords;
1039         
1040         model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
1041         keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
1042
1043         keywords = keyword_list_pull(pkd->keyword_view);
1044         keyword_hide_unset_in(GTK_TREE_STORE(keyword_tree), model, keywords);
1045         string_list_free(keywords);
1046         bar_keyword_tree_sync(pkd);
1047 }
1048
1049 static void bar_pane_keywords_expand_checked_toggle_cb(GtkWidget *menu_widget, gpointer data)
1050 {
1051         PaneKeywordsData *pkd = data;
1052         pkd->expand_checked = !pkd->expand_checked;
1053         bar_keyword_tree_sync(pkd);
1054 }
1055
1056 static void bar_pane_keywords_collapse_unchecked_toggle_cb(GtkWidget *menu_widget, gpointer data)
1057 {
1058         PaneKeywordsData *pkd = data;
1059         pkd->collapse_unchecked = !pkd->collapse_unchecked;
1060         bar_keyword_tree_sync(pkd);
1061 }
1062
1063 static void bar_pane_keywords_hide_unchecked_toggle_cb(GtkWidget *menu_widget, gpointer data)
1064 {
1065         PaneKeywordsData *pkd = data;
1066         pkd->hide_unchecked = !pkd->hide_unchecked;
1067         bar_keyword_tree_sync(pkd);
1068 }
1069
1070 static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pkd, gint x, gint y)
1071 {
1072         GtkWidget *menu;
1073         GtkWidget *item;
1074         GtkWidget *submenu;
1075         GtkTreeViewDropPosition pos;
1076         
1077         if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath);
1078         pkd->click_tpath = NULL;
1079         gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(pkd->keyword_treeview), x, y, &pkd->click_tpath, &pos);
1080
1081         menu = popup_menu_short_lived();
1082
1083         if (pkd->click_tpath)
1084                 {
1085                 /* for the entry */
1086                 gchar *text;
1087                 gchar *mark;
1088                 gint i;
1089                 
1090                 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
1091                 
1092                 GtkTreeIter iter;
1093                 gtk_tree_model_get_iter(model, &iter, pkd->click_tpath);
1094                 gchar *name;
1095                 
1096                 gtk_tree_model_get(model, &iter, FILTER_KEYWORD_COLUMN_NAME, &name,
1097                                                  FILTER_KEYWORD_COLUMN_MARK, &mark, -1);
1098                 
1099                 text = g_strdup_printf(_("Hide \"%s\""), name);
1100                 menu_item_add_stock(menu, text, GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_hide_cb), pkd);
1101                 g_free(text);
1102                 
1103                 submenu = gtk_menu_new();
1104                 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1105                         {
1106                         text = g_strdup_printf(_("Mark %d"), i + 1);
1107                         item = menu_item_add(submenu, text, G_CALLBACK(bar_pane_keywords_connect_mark_cb), pkd);
1108                         g_object_set_data(G_OBJECT(item), "mark", GINT_TO_POINTER(i + 1));
1109                         g_free(text);
1110                         }
1111                 text = g_strdup_printf(_("Connect \"%s\" to mark"), name);
1112                 item = menu_item_add(menu, text, NULL, NULL);
1113                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1114                 g_free(text);
1115
1116                 menu_item_add_divider(menu);
1117
1118                 text = g_strdup_printf(_("Edit \"%s\""), name);
1119                 menu_item_add_stock(menu, text, GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_edit_dialog_cb), pkd);
1120                 g_free(text);
1121                 text = g_strdup_printf(_("Delete \"%s\""), name);
1122                 menu_item_add_stock(menu, text, GTK_STOCK_DELETE, G_CALLBACK(bar_pane_keywords_delete_cb), pkd);
1123                 g_free(text);
1124
1125                 
1126                 if (mark && mark[0])
1127                         {
1128                         text = g_strdup_printf(_("Disconnect \"%s\" from mark %s"), name, mark);
1129                         menu_item_add_stock(menu, text, GTK_STOCK_DELETE, G_CALLBACK(bar_pane_keywords_connect_mark_cb), pkd);
1130                         g_free(text);
1131                         }
1132
1133                 menu_item_add_divider(menu);
1134                 g_free(mark);
1135                 g_free(name);
1136                 }
1137         /* for the pane */
1138
1139
1140         menu_item_add(menu, _("Expand checked"), G_CALLBACK(bar_pane_keywords_expand_checked_cb), pkd);
1141         menu_item_add(menu, _("Collapse unchecked"), G_CALLBACK(bar_pane_keywords_collapse_unchecked_cb), pkd);
1142         menu_item_add(menu, _("Hide unchecked"), G_CALLBACK(bar_pane_keywords_hide_unchecked_cb), pkd);
1143         menu_item_add(menu, _("Show all"), G_CALLBACK(bar_pane_keywords_show_all_cb), pkd);
1144
1145         submenu = gtk_menu_new();
1146         item = menu_item_add(menu, _("On any change"), NULL, NULL);
1147         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1148
1149         menu_item_add_check(submenu, _("Expand checked"), pkd->expand_checked, G_CALLBACK(bar_pane_keywords_expand_checked_toggle_cb), pkd);
1150         menu_item_add_check(submenu, _("Collapse unchecked"), pkd->collapse_unchecked, G_CALLBACK(bar_pane_keywords_collapse_unchecked_toggle_cb), pkd);
1151         menu_item_add_check(submenu, _("Hide unchecked"), pkd->hide_unchecked, G_CALLBACK(bar_pane_keywords_hide_unchecked_toggle_cb), pkd);
1152
1153         menu_item_add_divider(menu);
1154
1155         menu_item_add_stock(menu, _("Add keyword"), GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_add_dialog_cb), pkd);
1156         
1157         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
1158 }
1159
1160
1161 static gboolean bar_pane_keywords_menu_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) 
1162
1163         PaneKeywordsData *pkd = data;
1164         if (bevent->button == MOUSE_BUTTON_RIGHT)
1165                 {
1166                 bar_pane_keywords_menu_popup(widget, pkd, bevent->x, bevent->y);
1167                 return TRUE;
1168                 }
1169         return FALSE;
1170
1171
1172 /*
1173  *-------------------------------------------------------------------
1174  * init
1175  *-------------------------------------------------------------------
1176  */
1177
1178 void bar_pane_keywords_close(GtkWidget *bar)
1179 {
1180         PaneKeywordsData *pkd;
1181
1182         pkd = g_object_get_data(G_OBJECT(bar), "pane_data");
1183         if (!pkd) return;
1184
1185         gtk_widget_destroy(pkd->widget);
1186 }
1187
1188 static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data)
1189 {
1190         PaneKeywordsData *pkd = data;
1191
1192         if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath);
1193         if (pkd->idle_id != -1) g_source_remove(pkd->idle_id);
1194         file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
1195
1196         file_data_unref(pkd->fd);
1197         g_free(pkd->key);
1198
1199         g_free(pkd);
1200 }
1201
1202
1203 GtkWidget *bar_pane_keywords_new(const gchar *title, const gchar *key, gboolean expanded)
1204 {
1205         PaneKeywordsData *pkd;
1206         GtkWidget *hbox;
1207         GtkWidget *scrolled;
1208         GtkTextBuffer *buffer;
1209         GtkTreeModel *store;
1210         GtkTreeViewColumn *column;
1211         GtkCellRenderer *renderer;
1212
1213         pkd = g_new0(PaneKeywordsData, 1);
1214
1215         pkd->pane.pane_set_fd = bar_pane_keywords_set_fd;
1216         pkd->pane.pane_event = bar_pane_keywords_event;
1217         pkd->pane.pane_write_config = bar_pane_keywords_write_config;
1218         pkd->pane.title = bar_pane_expander_title(title);
1219
1220         pkd->pane.expanded = expanded;
1221
1222         pkd->key = g_strdup(key);
1223         
1224         pkd->expand_checked = TRUE;
1225         
1226         pkd->idle_id = -1;
1227
1228         hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
1229
1230         pkd->widget = hbox;
1231         g_object_set_data(G_OBJECT(pkd->widget), "pane_data", pkd);
1232         g_signal_connect(G_OBJECT(pkd->widget), "destroy",
1233                          G_CALLBACK(bar_pane_keywords_destroy), pkd);
1234         gtk_widget_show(hbox);
1235
1236         scrolled = gtk_scrolled_window_new(NULL, NULL);
1237         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
1238         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1239                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1240         gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
1241         gtk_widget_show(scrolled);
1242
1243         pkd->keyword_view = gtk_text_view_new();
1244         gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_view);
1245         g_signal_connect(G_OBJECT(pkd->keyword_view), "populate-popup",
1246                          G_CALLBACK(bar_pane_keywords_populate_popup_cb), pkd);
1247         gtk_widget_show(pkd->keyword_view);
1248
1249         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
1250         g_signal_connect(G_OBJECT(buffer), "changed",
1251                          G_CALLBACK(bar_pane_keywords_changed), pkd);
1252
1253         scrolled = gtk_scrolled_window_new(NULL, NULL);
1254         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
1255         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1256                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1257         gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
1258         gtk_widget_show(scrolled);
1259
1260
1261         if (!keyword_tree) keyword_tree_new_default();
1262
1263         store = gtk_tree_model_filter_new(GTK_TREE_MODEL(keyword_tree), NULL);
1264
1265         gtk_tree_model_filter_set_modify_func(GTK_TREE_MODEL_FILTER(store),
1266                                               FILTER_KEYWORD_COLUMN_COUNT,
1267                                               filter_keyword_column_types,
1268                                               bar_pane_keywords_filter_modify,
1269                                               pkd,
1270                                               NULL);
1271         gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(store),
1272                                                bar_pane_keywords_filter_visible,
1273                                                store,
1274                                                NULL);
1275
1276         pkd->keyword_treeview = gtk_tree_view_new_with_model(store);
1277         g_object_unref(store);
1278         
1279         gtk_widget_set_size_request(pkd->keyword_treeview, -1, 400);
1280
1281         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pkd->keyword_treeview), FALSE);
1282
1283 //      gtk_tree_view_set_search_column(GTK_TREE_VIEW(pkd->keyword_treeview), FILTER_KEYWORD_COLUMN_);
1284
1285         column = gtk_tree_view_column_new();
1286         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1287
1288         renderer = gtk_cell_renderer_text_new();
1289         gtk_tree_view_column_pack_start(column, renderer, TRUE);
1290
1291         gtk_tree_view_column_add_attribute(column, renderer, "text", FILTER_KEYWORD_COLUMN_MARK);
1292
1293         gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
1294
1295         column = gtk_tree_view_column_new();
1296         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1297         renderer = gtk_cell_renderer_toggle_new();
1298         gtk_tree_view_column_pack_start(column, renderer, FALSE);
1299         gtk_tree_view_column_add_attribute(column, renderer, "active", FILTER_KEYWORD_COLUMN_TOGGLE);
1300         gtk_tree_view_column_add_attribute(column, renderer, "visible", FILTER_KEYWORD_COLUMN_IS_KEYWORD);
1301         g_signal_connect(G_OBJECT(renderer), "toggled",
1302                          G_CALLBACK(bar_pane_keywords_keyword_toggle), pkd);
1303
1304         renderer = gtk_cell_renderer_text_new();
1305         gtk_tree_view_column_pack_start(column, renderer, TRUE);
1306         gtk_tree_view_column_add_attribute(column, renderer, "text", FILTER_KEYWORD_COLUMN_NAME);
1307
1308         gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
1309         gtk_tree_view_set_expander_column(GTK_TREE_VIEW(pkd->keyword_treeview), column);
1310
1311         gtk_drag_source_set(pkd->keyword_treeview,
1312                             GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
1313                             bar_pane_keywords_drag_types, n_keywords_drag_types,
1314                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
1315
1316         g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_data_get",
1317                          G_CALLBACK(bar_pane_keywords_dnd_get), pkd);
1318
1319         g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_begin",
1320                          G_CALLBACK(bar_pane_keywords_dnd_begin), pkd);
1321         g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_end",
1322                          G_CALLBACK(bar_pane_keywords_dnd_end), pkd);
1323
1324         gtk_drag_dest_set(pkd->keyword_treeview,
1325                           GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
1326                           bar_pane_keywords_drop_types, n_keywords_drop_types,
1327                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
1328                           
1329         g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_data_received",
1330                          G_CALLBACK(bar_pane_keywords_dnd_receive), pkd);
1331
1332         g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_motion",
1333                          G_CALLBACK(bar_pane_keywords_dnd_motion), pkd);
1334
1335         g_signal_connect(G_OBJECT(pkd->keyword_treeview), "button_press_event", 
1336                          G_CALLBACK(bar_pane_keywords_menu_cb), pkd);
1337         
1338         gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_treeview);
1339         gtk_widget_show(pkd->keyword_treeview);
1340
1341         file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW);
1342
1343         return pkd->widget;
1344 }
1345
1346 GtkWidget *bar_pane_keywords_new_from_config(const gchar **attribute_names, const gchar **attribute_values)
1347 {
1348         gchar *title = g_strdup(_("NoName"));
1349         gchar *key = g_strdup(COMMENT_KEY);
1350         gboolean expanded = TRUE;
1351
1352         while (*attribute_names)
1353                 {
1354                 const gchar *option = *attribute_names++;
1355                 const gchar *value = *attribute_values++;
1356
1357                 if (READ_CHAR_FULL("pane.title", title)) continue;
1358                 if (READ_CHAR_FULL("key", key)) continue;
1359                 if (READ_BOOL_FULL("pane.expanded", expanded)) continue;
1360                 
1361
1362                 log_printf("unknown attribute %s = %s\n", option, value);
1363                 }
1364         
1365         return bar_pane_keywords_new(title, key, expanded);
1366 }
1367
1368 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */