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