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