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