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