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