r3768: Updated years.
[rox-filer.git] / ROX-Filer / src / bookmarks.c
blob2928e6ceb376dec8547388d3507a165dffa2d10f
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* bookmarks.c - handles the bookmarks menu */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <gtk/gtk.h>
28 #include <string.h>
30 #include "global.h"
32 #include "bookmarks.h"
33 #include "choices.h"
34 #include "filer.h"
35 #include "xml.h"
36 #include "support.h"
37 #include "gui_support.h"
38 #include "main.h"
39 #include "mount.h"
40 #include "action.h"
42 static GList *history = NULL; /* Most recent first */
43 static GList *history_tail = NULL; /* Oldest item */
44 static GHashTable *history_hash = NULL; /* Path -> GList link */
45 static gint history_free = 30; /* Space left in history */
47 static XMLwrapper *bookmarks = NULL;
48 static GtkWidget *bookmarks_window = NULL;
50 /* Static prototypes */
51 static void update_bookmarks(void);
52 static xmlNode *bookmark_find(const gchar *mark);
53 static void bookmarks_save(void);
54 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data);
55 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window);
56 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window);
57 static void position_menu(GtkMenu *menu, gint *x, gint *y,
58 gboolean *push_in, gpointer data);
59 static void cell_edited(GtkCellRendererText *cell,
60 const gchar *path_string,
61 const gchar *new_text,
62 gpointer data);
63 static void reorder_up(GtkButton *button, GtkTreeView *view);
64 static void reorder_down(GtkButton *button, GtkTreeView *view);
65 static void edit_response(GtkWidget *window, gint response,
66 GtkTreeModel *model);
67 static void edit_delete(GtkButton *button, GtkTreeView *view);
68 static gboolean dir_dropped(GtkWidget *window, GdkDragContext *context,
69 int x, int y,
70 GtkSelectionData *selection_data, guint info,
71 guint time, GtkTreeView *view);
72 static void bookmarks_add_dir(const guchar *dir);
73 static void commit_edits(GtkTreeModel *model);
76 /****************************************************************
77 * EXTERNAL INTERFACE *
78 ****************************************************************/
80 /* Shows the bookmarks menu */
81 void bookmarks_show_menu(FilerWindow *filer_window)
83 GdkEvent *event;
84 GtkMenu *menu;
85 int button = 0;
87 event = gtk_get_current_event();
88 if (event->type == GDK_BUTTON_RELEASE ||
89 event->type == GDK_BUTTON_PRESS)
90 button = ((GdkEventButton *) event)->button;
92 menu = GTK_MENU(bookmarks_build_menu(filer_window));
93 gtk_menu_popup(menu, NULL, NULL, position_menu, filer_window,
94 button, gtk_get_current_event_time());
97 /* Show the Edit Bookmarks dialog */
98 void bookmarks_edit(void)
100 GtkListStore *model;
101 GtkWidget *list, *hbox, *button, *swin;
102 GtkTreeSelection *selection;
103 GtkCellRenderer *cell;
104 xmlNode *node;
105 GtkTreeIter iter;
107 if (bookmarks_window)
109 gtk_window_present(GTK_WINDOW(bookmarks_window));
110 return;
113 update_bookmarks();
115 bookmarks_window = gtk_dialog_new();
116 number_of_windows++;
118 gtk_dialog_add_button(GTK_DIALOG(bookmarks_window),
119 GTK_STOCK_CLOSE, GTK_RESPONSE_OK);
121 g_signal_connect(bookmarks_window, "destroy",
122 G_CALLBACK(gtk_widget_destroyed), &bookmarks_window);
123 g_signal_connect(bookmarks_window, "destroy",
124 G_CALLBACK(one_less_window), NULL);
126 swin = gtk_scrolled_window_new(NULL, NULL);
127 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
128 GTK_SHADOW_IN);
129 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
130 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
131 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
132 swin, TRUE, TRUE, 0);
134 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
136 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
138 cell = gtk_cell_renderer_text_new();
139 g_signal_connect(G_OBJECT(cell), "edited",
140 G_CALLBACK(cell_edited), model);
141 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
142 g_object_set_data(G_OBJECT(cell), "column", GINT_TO_POINTER(0));
143 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
144 _("Path"), cell, "text", 0, NULL);
146 cell = gtk_cell_renderer_text_new();
147 g_signal_connect(G_OBJECT(cell), "edited",
148 G_CALLBACK(cell_edited), model);
149 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
150 g_object_set_data(G_OBJECT(cell), "column", GINT_TO_POINTER(1));
151 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
152 _("Title"), cell, "text", 1, NULL);
154 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), TRUE);
155 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
157 node = xmlDocGetRootElement(bookmarks->doc);
158 for (node = node->xmlChildrenNode; node; node = node->next)
160 GtkTreeIter iter;
161 gchar *mark, *title;
163 if (node->type != XML_ELEMENT_NODE)
164 continue;
165 if (strcmp(node->name, "bookmark") != 0)
166 continue;
168 mark = xmlNodeListGetString(bookmarks->doc,
169 node->xmlChildrenNode, 1);
170 if (!mark)
171 continue;
173 title=xmlGetProp(node, "title");
174 if(!title)
175 title=mark;
177 gtk_list_store_append(model, &iter);
178 gtk_list_store_set(model, &iter, 0, mark, 1, title, -1);
179 if(title!=mark)
180 xmlFree(title);
182 xmlFree(mark);
185 gtk_widget_set_size_request(list, 300, 300);
186 gtk_container_add(GTK_CONTAINER(swin), list);
188 hbox = gtk_hbutton_box_new();
189 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
190 hbox, FALSE, TRUE, 0);
191 gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
193 button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
194 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
195 g_signal_connect(button, "clicked", G_CALLBACK(edit_delete), list);
197 button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
198 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
199 g_signal_connect(button, "clicked", G_CALLBACK(reorder_up), list);
200 button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
201 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
202 g_signal_connect(button, "clicked", G_CALLBACK(reorder_down), list);
204 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
205 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
207 /* Select the first item, otherwise the first click starts edit
208 * mode, which is very confusing!
210 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
211 gtk_tree_selection_select_iter(selection, &iter);
213 g_signal_connect(bookmarks_window, "response",
214 G_CALLBACK(edit_response), model);
216 /* Allow directories to be dropped in */
218 GtkTargetEntry targets[] = { {"text/uri-list", 0, 0} };
219 gtk_drag_dest_set(bookmarks_window, GTK_DEST_DEFAULT_ALL,
220 targets, G_N_ELEMENTS(targets),
221 GDK_ACTION_COPY |GDK_ACTION_PRIVATE);
222 g_signal_connect(bookmarks_window, "drag-data-received",
223 G_CALLBACK(dir_dropped), list);
226 g_signal_connect_swapped(model, "row-changed",
227 G_CALLBACK(commit_edits), model);
228 g_signal_connect_swapped(model, "row-inserted",
229 G_CALLBACK(commit_edits), model);
230 g_signal_connect_swapped(model, "row-deleted",
231 G_CALLBACK(commit_edits), model);
232 g_signal_connect_swapped(model, "rows-reordered",
233 G_CALLBACK(commit_edits), model);
235 gtk_widget_show_all(bookmarks_window);
238 static void history_remove(const char *path)
240 GList *old;
242 old = g_hash_table_lookup(history_hash, path);
243 if (old)
245 g_hash_table_remove(history_hash, path);
247 if (history_tail == old)
248 history_tail = old->prev;
249 g_free(old->data);
250 history = g_list_delete_link(history, old);
252 history_free++;
256 /* Add this path to the global history of visited directories. If it
257 * already exists there, make it the most recent. If its parent exists
258 * already, remove the parent.
260 void bookmarks_add_history(const gchar *path)
262 char *new;
264 new = g_strdup(path);
265 ensure_utf8(&new);
267 if (!history_hash)
268 history_hash = g_hash_table_new(g_str_hash, g_str_equal);
270 history_remove(new);
273 char *parent;
274 parent = g_dirname(path);
275 history_remove(parent);
276 g_free(parent);
279 history = g_list_prepend(history, new);
280 if (!history_tail)
281 history_tail = history;
282 g_hash_table_insert(history_hash, new, history);
284 history_free--;
285 if (history_free == -1)
287 g_return_if_fail(history_tail != NULL);
288 history_remove((char *) history_tail->data);
292 void bookmarks_add_uri(const EscapedPath *uri)
294 char *path;
295 struct stat info;
297 path = get_local_path(uri);
299 if (!path)
301 delayed_error(_("Can't bookmark non-local resource '%s'\n"),
302 uri);
303 return;
306 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
307 bookmarks_add_dir(path);
308 else
309 delayed_error(_("'%s' isn't a directory"), path);
310 g_free(path);
313 /****************************************************************
314 * INTERNAL FUNCTIONS *
315 ****************************************************************/
317 /* Initialise the bookmarks document to be empty. Does not save. */
318 static void bookmarks_new(void)
320 if (bookmarks)
321 g_object_unref(G_OBJECT(bookmarks));
322 bookmarks = xml_new(NULL);
323 bookmarks->doc = xmlNewDoc("1.0");
324 xmlDocSetRootElement(bookmarks->doc,
325 xmlNewDocNode(bookmarks->doc, NULL, "bookmarks", NULL));
328 static void position_menu(GtkMenu *menu, gint *x, gint *y,
329 gboolean *push_in, gpointer data)
331 FilerWindow *filer_window = (FilerWindow *) data;
333 gdk_window_get_origin(GTK_WIDGET(filer_window->view)->window, x, y);
336 /* Makes sure that 'bookmarks' is up-to-date, reloading from file if it has
337 * changed. If no bookmarks were loaded and there is no file then initialise
338 * bookmarks to an empty document.
340 static void update_bookmarks()
342 gchar *path;
344 /* Update the bookmarks, if possible */
345 path = choices_find_path_load("Bookmarks.xml", PROJECT);
346 if (path)
348 XMLwrapper *wrapper;
349 wrapper = xml_cache_load(path);
350 if (wrapper)
352 if (bookmarks)
353 g_object_unref(bookmarks);
354 bookmarks = wrapper;
357 g_free(path);
360 if (!bookmarks)
361 bookmarks_new();
364 /* Return the node for the 'mark' bookmark */
365 static xmlNode *bookmark_find(const gchar *mark)
367 xmlNode *node;
369 update_bookmarks();
371 node = xmlDocGetRootElement(bookmarks->doc);
373 for (node = node->xmlChildrenNode; node; node = node->next)
375 gchar *path;
376 gboolean same;
378 if (node->type != XML_ELEMENT_NODE)
379 continue;
380 if (strcmp(node->name, "bookmark") != 0)
381 continue;
383 path = xmlNodeListGetString(bookmarks->doc,
384 node->xmlChildrenNode, 1);
385 if (!path)
386 continue;
388 same = strcmp(mark, path) == 0;
389 xmlFree(path);
391 if (same)
392 return node;
395 return NULL;
398 /* Save the bookmarks to a file */
399 static void bookmarks_save()
401 guchar *save_path;
403 save_path = choices_find_path_save("Bookmarks.xml", PROJECT, TRUE);
404 if (save_path)
406 save_xml_file(bookmarks->doc, save_path);
407 g_free(save_path);
411 /* Add a bookmark if it doesn't already exist, and save the
412 * bookmarks.
414 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data)
416 FilerWindow *filer_window = (FilerWindow *) user_data;
418 bookmarks_add_dir(filer_window->sym_path);
421 static void bookmarks_add_dir(const guchar *dir)
423 xmlNode *bookmark;
425 if (bookmark_find(dir))
426 return;
428 bookmark = xmlNewTextChild(xmlDocGetRootElement(bookmarks->doc),
429 NULL, "bookmark", dir);
430 xmlSetProp(bookmark, "title", dir);
432 bookmarks_save();
434 if (bookmarks_window)
435 gtk_widget_destroy(bookmarks_window);
438 /* Called when a bookmark has been chosen */
439 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window)
441 const gchar *mark;
442 GtkLabel *label;
444 mark=g_object_get_data(G_OBJECT(item), "bookmark-path");
445 if(!mark) {
446 label = GTK_LABEL(GTK_BIN(item)->child);
447 mark = gtk_label_get_text(label);
450 if (strcmp(mark, filer_window->sym_path) != 0)
451 filer_change_to(filer_window, mark, NULL);
452 if (g_hash_table_lookup(fstab_mounts, filer_window->real_path) &&
453 !mount_is_mounted(filer_window->real_path, NULL, NULL))
455 GList *paths;
457 paths = g_list_prepend(NULL, filer_window->real_path);
458 action_mount(paths, FALSE, -1);
459 g_list_free(paths);
463 static void edit_delete(GtkButton *button, GtkTreeView *view)
465 GtkTreeModel *model;
466 GtkListStore *list;
467 GtkTreeSelection *selection;
468 GtkTreeIter iter;
469 gboolean more, any = FALSE;
471 model = gtk_tree_view_get_model(view);
472 list = GTK_LIST_STORE(model);
474 selection = gtk_tree_view_get_selection(view);
476 more = gtk_tree_model_get_iter_first(model, &iter);
478 while (more)
480 GtkTreeIter old = iter;
482 more = gtk_tree_model_iter_next(model, &iter);
484 if (gtk_tree_selection_iter_is_selected(selection, &old))
486 any = TRUE;
487 gtk_list_store_remove(list, &old);
491 if (!any)
493 report_error(_("You should first select some rows to delete"));
494 return;
498 static void reorder(GtkTreeView *view, int dir)
500 GtkTreeModel *model;
501 GtkListStore *list;
502 GtkTreePath *cursor = NULL;
503 GtkTreeIter iter, old, new;
504 GValue mark = {0};
505 GValue title = {0};
506 gboolean ok;
508 g_return_if_fail(view != NULL);
509 g_return_if_fail(dir == 1 || dir == -1);
511 model = gtk_tree_view_get_model(view);
512 list = GTK_LIST_STORE(model);
514 gtk_tree_view_get_cursor(view, &cursor, NULL);
515 if (!cursor)
517 report_error(_("Put the cursor on an entry in the "
518 "list to move it"));
519 return;
522 gtk_tree_model_get_iter(model, &old, cursor);
523 if (dir > 0)
525 gtk_tree_path_next(cursor);
526 ok = gtk_tree_model_get_iter(model, &iter, cursor);
528 else
530 ok = gtk_tree_path_prev(cursor);
531 if (ok)
532 gtk_tree_model_get_iter(model, &iter, cursor);
534 if (!ok)
536 gtk_tree_path_free(cursor);
537 report_error(_("This item is already at the end"));
538 return;
541 gtk_tree_model_get_value(model, &old, 0, &mark);
542 gtk_tree_model_get_value(model, &old, 1, &title);
543 if (dir > 0)
544 gtk_list_store_insert_after(list, &new, &iter);
545 else
546 gtk_list_store_insert_before(list, &new, &iter);
547 gtk_list_store_set(list, &new, 0, g_value_get_string(&mark), -1);
548 gtk_list_store_set(list, &new, 1, g_value_get_string(&title), -1);
549 gtk_list_store_remove(list, &old);
551 g_value_unset(&mark);
552 g_value_unset(&title);
554 gtk_tree_view_set_cursor(view, cursor, 0, FALSE);
555 gtk_tree_path_free(cursor);
558 static void reorder_up(GtkButton *button, GtkTreeView *view)
560 reorder(view, -1);
563 static void reorder_down(GtkButton *button, GtkTreeView *view)
565 reorder(view, 1);
568 static gboolean dir_dropped(GtkWidget *window, GdkDragContext *context,
569 int x, int y,
570 GtkSelectionData *selection_data, guint info,
571 guint time, GtkTreeView *view)
573 GtkListStore *model;
574 GList *uris, *next;
576 if (!selection_data->data)
578 /* Timeout? */
579 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
580 return TRUE;
583 model = GTK_LIST_STORE(gtk_tree_view_get_model(view));
585 uris = uri_list_to_glist(selection_data->data);
587 for (next = uris; next; next = next->next)
589 guchar *path;
591 path = get_local_path((EscapedPath *) next->data);
593 if (path)
595 GtkTreeIter iter;
596 struct stat info;
598 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
600 gtk_list_store_append(model, &iter);
601 gtk_list_store_set(model, &iter, 0, path,
602 1, path, -1);
604 else
605 delayed_error(_("'%s' isn't a directory"),
606 path);
608 g_free(path);
610 else
611 delayed_error(_("Can't bookmark non-local directories "
612 "like '%s'"), (gchar *) next->data);
615 destroy_glist(&uris);
617 return TRUE;
620 static void commit_edits(GtkTreeModel *model)
622 GtkTreeIter iter;
624 bookmarks_new();
626 if (gtk_tree_model_get_iter_first(model, &iter))
628 GValue mark = {0}, title={0};
629 xmlNode *root = xmlDocGetRootElement(bookmarks->doc);
633 xmlNode *bookmark;
635 gtk_tree_model_get_value(model, &iter, 0, &mark);
636 bookmark = xmlNewTextChild(root, NULL, "bookmark",
637 g_value_get_string(&mark));
638 g_value_unset(&mark);
639 gtk_tree_model_get_value(model, &iter, 1, &title);
640 xmlSetProp(bookmark, "title",
641 g_value_get_string(&title));
642 g_value_unset(&title);
643 } while (gtk_tree_model_iter_next(model, &iter));
646 bookmarks_save();
649 static void edit_response(GtkWidget *window, gint response, GtkTreeModel *model)
651 commit_edits(model);
653 gtk_widget_destroy(window);
656 static void cell_edited(GtkCellRendererText *cell,
657 const gchar *path_string,
658 const gchar *new_text,
659 gpointer data)
661 GtkTreeModel *model = (GtkTreeModel *) data;
662 GtkTreePath *path;
663 GtkTreeIter iter;
664 gint col;
666 path = gtk_tree_path_new_from_string(path_string);
667 gtk_tree_model_get_iter(model, &iter, path);
668 gtk_tree_path_free(path);
669 col=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column"));
671 gtk_list_store_set(GTK_LIST_STORE(model), &iter, col, new_text, -1);
674 static void activate_edit(GtkMenuShell *item, gpointer data)
676 bookmarks_edit();
679 static gint cmp_dirname(gconstpointer a, gconstpointer b)
681 return g_utf8_collate(*(gchar **) a, *(gchar **) b);
684 static void free_path_for_item(GtkWidget *widget, gpointer udata)
686 gchar *path=(gchar *) udata;
687 g_free(path);
690 static GtkWidget *build_history_menu(FilerWindow *filer_window)
692 GtkWidget *menu;
693 GPtrArray *items;
694 GList *next;
695 int i;
697 menu = gtk_menu_new();
699 if (!history)
700 return menu;
702 g_return_val_if_fail(history_hash != NULL, menu);
703 g_return_val_if_fail(history_tail != NULL, menu);
705 items = g_ptr_array_new();
707 for (next = history; next; next = next->next)
708 g_ptr_array_add(items, next->data);
710 g_ptr_array_sort(items, cmp_dirname);
712 for (i = 0; i < items->len; i++)
714 GtkWidget *item;
715 const char *path = (char *) items->pdata[i];
716 gchar *copy;
718 item = gtk_menu_item_new_with_label(path);
720 copy=g_strdup(path);
721 g_object_set_data(G_OBJECT(item), "bookmark-path", copy);
722 g_signal_connect(item, "destroy",
723 G_CALLBACK(free_path_for_item), copy);
725 if (strcmp(path, filer_window->sym_path) == 0)
726 gtk_widget_set_sensitive(item, FALSE);
727 else
728 g_signal_connect(item, "activate",
729 G_CALLBACK(bookmarks_activate),
730 filer_window);
732 gtk_widget_show(item);
733 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
736 g_ptr_array_free(items, TRUE);
738 return menu;
741 /* Builds the bookmarks' menu. Done whenever the bookmarks icon has been
742 * clicked.
744 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window)
746 GtkWidget *menu;
747 GtkWidget *item;
748 xmlNode *node;
749 gboolean need_separator = TRUE;
751 menu = gtk_menu_new();
753 item = gtk_menu_item_new_with_label(_("Add New Bookmark"));
754 g_signal_connect(item, "activate",
755 G_CALLBACK(bookmarks_add), filer_window);
756 gtk_widget_show(item);
757 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
758 gtk_menu_shell_select_item(GTK_MENU_SHELL(menu), item);
760 item = gtk_menu_item_new_with_label(_("Edit Bookmarks"));
761 g_signal_connect(item, "activate", G_CALLBACK(activate_edit), NULL);
762 gtk_widget_show(item);
763 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
765 item = gtk_menu_item_new_with_label(_("Recently Visited"));
766 gtk_widget_show(item);
767 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
768 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
769 build_history_menu(filer_window));
771 /* Now add all the bookmarks to the menu */
773 update_bookmarks();
775 node = xmlDocGetRootElement(bookmarks->doc);
777 for (node = node->xmlChildrenNode; node; node = node->next)
779 gchar *mark, *title, *path;
781 if (node->type != XML_ELEMENT_NODE)
782 continue;
783 if (strcmp(node->name, "bookmark") != 0)
784 continue;
786 mark = xmlNodeListGetString(bookmarks->doc,
787 node->xmlChildrenNode, 1);
788 if (!mark)
789 continue;
790 path=g_strdup(mark);
792 title=xmlGetProp(node, "title");
793 if(!title)
794 title=mark;
796 item = gtk_menu_item_new_with_label(title);
798 g_object_set_data(G_OBJECT(item), "bookmark-path", path);
799 g_signal_connect(item, "destroy",
800 G_CALLBACK(free_path_for_item), path);
802 if(title!=mark)
803 xmlFree(title);
804 xmlFree(mark);
806 g_signal_connect(item, "activate",
807 G_CALLBACK(bookmarks_activate),
808 filer_window);
810 gtk_widget_show(item);
812 if (need_separator)
814 GtkWidget *sep;
815 sep = gtk_separator_menu_item_new();
816 gtk_widget_show(sep);
817 gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
818 need_separator = FALSE;
821 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
825 return menu;