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