r4275: If we can't find libc.so.6 for xattr support, try libc.so in case we only...
[rox-filer.git] / ROX-Filer / src / bookmarks.c
blob1e7e9fb3cf1efdc838652da72f674c784c495ed4
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"
41 #include "options.h"
42 #include "bind.h"
44 static GList *history = NULL; /* Most recent first */
45 static GList *history_tail = NULL; /* Oldest item */
46 static GHashTable *history_hash = NULL; /* Path -> GList link */
47 static gint history_free = 30; /* Space left in history */
49 static XMLwrapper *bookmarks = NULL;
50 static GtkWidget *bookmarks_window = NULL;
52 /* Static prototypes */
53 static void update_bookmarks(void);
54 static xmlNode *bookmark_find(const gchar *mark);
55 static void bookmarks_save(void);
56 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data);
57 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window);
58 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window);
59 static void position_menu(GtkMenu *menu, gint *x, gint *y,
60 gboolean *push_in, gpointer data);
61 static void cell_edited(GtkCellRendererText *cell,
62 const gchar *path_string,
63 const gchar *new_text,
64 gpointer data);
65 static void reorder_up(GtkButton *button, GtkTreeView *view);
66 static void reorder_down(GtkButton *button, GtkTreeView *view);
67 static void edit_response(GtkWidget *window, gint response,
68 GtkTreeModel *model);
69 static void edit_delete(GtkButton *button, GtkTreeView *view);
70 static gboolean dir_dropped(GtkWidget *window, GdkDragContext *context,
71 int x, int y,
72 GtkSelectionData *selection_data, guint info,
73 guint time, GtkTreeView *view);
74 static void bookmarks_add_dir(const guchar *dir);
75 static void commit_edits(GtkTreeModel *model);
78 /****************************************************************
79 * EXTERNAL INTERFACE *
80 ****************************************************************/
82 /* Shows the bookmarks menu */
83 void bookmarks_show_menu(FilerWindow *filer_window)
85 GdkEvent *event;
86 GtkMenu *menu;
87 int button = 0;
89 event = gtk_get_current_event();
90 if (event->type == GDK_BUTTON_RELEASE ||
91 event->type == GDK_BUTTON_PRESS)
92 button = ((GdkEventButton *) event)->button;
93 gdk_event_free(event);
95 menu = GTK_MENU(bookmarks_build_menu(filer_window));
96 gtk_menu_popup(menu, NULL, NULL, position_menu, filer_window,
97 button, gtk_get_current_event_time());
100 /* Show the Edit Bookmarks dialog */
101 void bookmarks_edit(void)
103 GtkListStore *model;
104 GtkWidget *list, *hbox, *button, *swin;
105 GtkTreeSelection *selection;
106 GtkCellRenderer *cell;
107 xmlNode *node;
108 GtkTreeIter iter;
110 if (bookmarks_window)
112 gtk_window_present(GTK_WINDOW(bookmarks_window));
113 return;
116 update_bookmarks();
118 bookmarks_window = gtk_dialog_new();
119 number_of_windows++;
121 gtk_dialog_add_button(GTK_DIALOG(bookmarks_window),
122 GTK_STOCK_CLOSE, GTK_RESPONSE_OK);
124 g_signal_connect(bookmarks_window, "destroy",
125 G_CALLBACK(gtk_widget_destroyed), &bookmarks_window);
126 g_signal_connect(bookmarks_window, "destroy",
127 G_CALLBACK(one_less_window), NULL);
129 swin = gtk_scrolled_window_new(NULL, NULL);
130 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
131 GTK_SHADOW_IN);
132 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
133 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
134 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
135 swin, TRUE, TRUE, 0);
137 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
139 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
141 cell = gtk_cell_renderer_text_new();
142 g_signal_connect(G_OBJECT(cell), "edited",
143 G_CALLBACK(cell_edited), model);
144 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
145 g_object_set_data(G_OBJECT(cell), "column", GINT_TO_POINTER(0));
146 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
147 _("Path"), cell, "text", 0, NULL);
149 cell = gtk_cell_renderer_text_new();
150 g_signal_connect(G_OBJECT(cell), "edited",
151 G_CALLBACK(cell_edited), model);
152 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
153 g_object_set_data(G_OBJECT(cell), "column", GINT_TO_POINTER(1));
154 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
155 _("Title"), cell, "text", 1, NULL);
157 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), TRUE);
158 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
160 node = xmlDocGetRootElement(bookmarks->doc);
161 for (node = node->xmlChildrenNode; node; node = node->next)
163 GtkTreeIter iter;
164 gchar *mark, *title;
166 if (node->type != XML_ELEMENT_NODE)
167 continue;
168 if (strcmp(node->name, "bookmark") != 0)
169 continue;
171 mark = xmlNodeListGetString(bookmarks->doc,
172 node->xmlChildrenNode, 1);
173 if (!mark)
174 continue;
176 title=xmlGetProp(node, "title");
177 if(!title)
178 title=mark;
180 gtk_list_store_append(model, &iter);
181 gtk_list_store_set(model, &iter, 0, mark, 1, title, -1);
182 if(title!=mark)
183 xmlFree(title);
185 xmlFree(mark);
188 gtk_widget_set_size_request(list, 300, 300);
189 gtk_container_add(GTK_CONTAINER(swin), list);
191 hbox = gtk_hbutton_box_new();
192 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
193 hbox, FALSE, TRUE, 0);
194 gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
196 button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
197 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
198 g_signal_connect(button, "clicked", G_CALLBACK(edit_delete), list);
200 button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
201 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
202 g_signal_connect(button, "clicked", G_CALLBACK(reorder_up), list);
203 button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
204 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
205 g_signal_connect(button, "clicked", G_CALLBACK(reorder_down), list);
207 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
208 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
210 /* Select the first item, otherwise the first click starts edit
211 * mode, which is very confusing!
213 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
214 gtk_tree_selection_select_iter(selection, &iter);
216 g_signal_connect(bookmarks_window, "response",
217 G_CALLBACK(edit_response), model);
219 /* Allow directories to be dropped in */
221 GtkTargetEntry targets[] = { {"text/uri-list", 0, 0} };
222 gtk_drag_dest_set(bookmarks_window, GTK_DEST_DEFAULT_ALL,
223 targets, G_N_ELEMENTS(targets),
224 GDK_ACTION_COPY |GDK_ACTION_PRIVATE);
225 g_signal_connect(bookmarks_window, "drag-data-received",
226 G_CALLBACK(dir_dropped), list);
229 g_signal_connect_swapped(model, "row-changed",
230 G_CALLBACK(commit_edits), model);
231 g_signal_connect_swapped(model, "row-inserted",
232 G_CALLBACK(commit_edits), model);
233 g_signal_connect_swapped(model, "row-deleted",
234 G_CALLBACK(commit_edits), model);
235 g_signal_connect_swapped(model, "rows-reordered",
236 G_CALLBACK(commit_edits), model);
238 gtk_widget_show_all(bookmarks_window);
241 static void history_remove(const char *path)
243 GList *old;
245 old = g_hash_table_lookup(history_hash, path);
246 if (old)
248 g_hash_table_remove(history_hash, path);
250 if (history_tail == old)
251 history_tail = old->prev;
252 g_free(old->data);
253 history = g_list_delete_link(history, old);
255 history_free++;
259 /* Add this path to the global history of visited directories. If it
260 * already exists there, make it the most recent. If its parent exists
261 * already, remove the parent.
263 void bookmarks_add_history(const gchar *path)
265 char *new;
267 new = g_strdup(path);
268 ensure_utf8(&new);
270 if (!history_hash)
271 history_hash = g_hash_table_new(g_str_hash, g_str_equal);
273 history_remove(new);
276 char *parent;
277 parent = g_dirname(path);
278 history_remove(parent);
279 g_free(parent);
282 history = g_list_prepend(history, new);
283 if (!history_tail)
284 history_tail = history;
285 g_hash_table_insert(history_hash, new, history);
287 history_free--;
288 if (history_free == -1)
290 g_return_if_fail(history_tail != NULL);
291 history_remove((char *) history_tail->data);
295 void bookmarks_add_uri(const EscapedPath *uri)
297 char *path;
298 struct stat info;
300 path = get_local_path(uri);
302 if (!path)
304 delayed_error(_("Can't bookmark non-local resource '%s'\n"),
305 uri);
306 return;
309 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
310 bookmarks_add_dir(path);
311 else
312 delayed_error(_("'%s' isn't a directory"), path);
313 g_free(path);
316 /****************************************************************
317 * INTERNAL FUNCTIONS *
318 ****************************************************************/
320 /* Initialise the bookmarks document to be empty. Does not save. */
321 static void bookmarks_new(void)
323 if (bookmarks)
324 g_object_unref(G_OBJECT(bookmarks));
325 bookmarks = xml_new(NULL);
326 bookmarks->doc = xmlNewDoc("1.0");
327 xmlDocSetRootElement(bookmarks->doc,
328 xmlNewDocNode(bookmarks->doc, NULL, "bookmarks", NULL));
331 static void position_menu(GtkMenu *menu, gint *x, gint *y,
332 gboolean *push_in, gpointer data)
334 FilerWindow *filer_window = (FilerWindow *) data;
336 gdk_window_get_origin(GTK_WIDGET(filer_window->view)->window, x, y);
339 /* Makes sure that 'bookmarks' is up-to-date, reloading from file if it has
340 * changed. If no bookmarks were loaded and there is no file then initialise
341 * bookmarks to an empty document.
343 static void update_bookmarks()
345 gchar *path;
347 /* Update the bookmarks, if possible */
348 path = choices_find_xdg_path_load("Bookmarks.xml", PROJECT, SITE);
349 if (path)
351 XMLwrapper *wrapper;
352 wrapper = xml_cache_load(path);
353 if (wrapper)
355 if (bookmarks)
356 g_object_unref(bookmarks);
357 bookmarks = wrapper;
360 g_free(path);
363 if (!bookmarks)
364 bookmarks_new();
367 /* Return the node for the 'mark' bookmark */
368 static xmlNode *bookmark_find(const gchar *mark)
370 xmlNode *node;
372 update_bookmarks();
374 node = xmlDocGetRootElement(bookmarks->doc);
376 for (node = node->xmlChildrenNode; node; node = node->next)
378 gchar *path;
379 gboolean same;
381 if (node->type != XML_ELEMENT_NODE)
382 continue;
383 if (strcmp(node->name, "bookmark") != 0)
384 continue;
386 path = xmlNodeListGetString(bookmarks->doc,
387 node->xmlChildrenNode, 1);
388 if (!path)
389 continue;
391 same = strcmp(mark, path) == 0;
392 xmlFree(path);
394 if (same)
395 return node;
398 return NULL;
401 /* Save the bookmarks to a file */
402 static void bookmarks_save()
404 guchar *save_path;
406 save_path = choices_find_xdg_path_save("Bookmarks.xml", PROJECT, SITE,
407 TRUE);
408 if (save_path)
410 save_xml_file(bookmarks->doc, save_path);
411 g_free(save_path);
415 /* Add a bookmark if it doesn't already exist, and save the
416 * bookmarks.
418 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data)
420 FilerWindow *filer_window = (FilerWindow *) user_data;
422 bookmarks_add_dir(filer_window->sym_path);
425 static void bookmarks_add_dir(const guchar *dir)
427 xmlNode *bookmark;
429 if (bookmark_find(dir))
430 return;
432 bookmark = xmlNewTextChild(xmlDocGetRootElement(bookmarks->doc),
433 NULL, "bookmark", dir);
434 xmlSetProp(bookmark, "title", dir);
436 bookmarks_save();
438 if (bookmarks_window)
439 gtk_widget_destroy(bookmarks_window);
442 /* Called when a bookmark has been chosen */
443 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window)
445 const gchar *mark;
446 GtkLabel *label;
447 GdkEvent *event;
448 gboolean new_win=FALSE;
450 mark=g_object_get_data(G_OBJECT(item), "bookmark-path");
451 if(!mark) {
452 label = GTK_LABEL(GTK_BIN(item)->child);
453 mark = gtk_label_get_text(label);
456 event=gtk_get_current_event();
457 if(event)
459 if(event->type==GDK_BUTTON_PRESS ||
460 event->type==GDK_BUTTON_RELEASE)
462 GdkEventButton *button=(GdkEventButton *) event;
464 new_win=o_new_button_1.int_value?
465 button->button==1: button->button!=1;
467 gdk_event_free(event);
470 if (strcmp(mark, filer_window->sym_path) != 0)
472 if(new_win)
473 filer_opendir(mark, filer_window, NULL);
474 else
475 filer_change_to(filer_window, mark, NULL);
477 if (g_hash_table_lookup(fstab_mounts, filer_window->real_path) &&
478 !mount_is_mounted(filer_window->real_path, NULL, NULL))
480 GList *paths;
482 paths = g_list_prepend(NULL, filer_window->real_path);
483 action_mount(paths, FALSE, TRUE, -1);
484 g_list_free(paths);
488 static void edit_delete(GtkButton *button, GtkTreeView *view)
490 GtkTreeModel *model;
491 GtkListStore *list;
492 GtkTreeSelection *selection;
493 GtkTreeIter iter;
494 gboolean more, any = FALSE;
496 model = gtk_tree_view_get_model(view);
497 list = GTK_LIST_STORE(model);
499 selection = gtk_tree_view_get_selection(view);
501 more = gtk_tree_model_get_iter_first(model, &iter);
503 while (more)
505 GtkTreeIter old = iter;
507 more = gtk_tree_model_iter_next(model, &iter);
509 if (gtk_tree_selection_iter_is_selected(selection, &old))
511 any = TRUE;
512 gtk_list_store_remove(list, &old);
516 if (!any)
518 report_error(_("You should first select some rows to delete"));
519 return;
523 static void reorder(GtkTreeView *view, int dir)
525 GtkTreeModel *model;
526 GtkListStore *list;
527 GtkTreePath *cursor = NULL;
528 GtkTreeIter iter, old, new;
529 GValue mark = {0};
530 GValue title = {0};
531 gboolean ok;
533 g_return_if_fail(view != NULL);
534 g_return_if_fail(dir == 1 || dir == -1);
536 model = gtk_tree_view_get_model(view);
537 list = GTK_LIST_STORE(model);
539 gtk_tree_view_get_cursor(view, &cursor, NULL);
540 if (!cursor)
542 report_error(_("Put the cursor on an entry in the "
543 "list to move it"));
544 return;
547 gtk_tree_model_get_iter(model, &old, cursor);
548 if (dir > 0)
550 gtk_tree_path_next(cursor);
551 ok = gtk_tree_model_get_iter(model, &iter, cursor);
553 else
555 ok = gtk_tree_path_prev(cursor);
556 if (ok)
557 gtk_tree_model_get_iter(model, &iter, cursor);
559 if (!ok)
561 gtk_tree_path_free(cursor);
562 report_error(_("This item is already at the end"));
563 return;
566 gtk_tree_model_get_value(model, &old, 0, &mark);
567 gtk_tree_model_get_value(model, &old, 1, &title);
568 if (dir > 0)
569 gtk_list_store_insert_after(list, &new, &iter);
570 else
571 gtk_list_store_insert_before(list, &new, &iter);
572 gtk_list_store_set(list, &new, 0, g_value_get_string(&mark), -1);
573 gtk_list_store_set(list, &new, 1, g_value_get_string(&title), -1);
574 gtk_list_store_remove(list, &old);
576 g_value_unset(&mark);
577 g_value_unset(&title);
579 gtk_tree_view_set_cursor(view, cursor, 0, FALSE);
580 gtk_tree_path_free(cursor);
583 static void reorder_up(GtkButton *button, GtkTreeView *view)
585 reorder(view, -1);
588 static void reorder_down(GtkButton *button, GtkTreeView *view)
590 reorder(view, 1);
593 static gboolean dir_dropped(GtkWidget *window, GdkDragContext *context,
594 int x, int y,
595 GtkSelectionData *selection_data, guint info,
596 guint time, GtkTreeView *view)
598 GtkListStore *model;
599 GList *uris, *next;
601 if (!selection_data->data)
603 /* Timeout? */
604 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
605 return TRUE;
608 model = GTK_LIST_STORE(gtk_tree_view_get_model(view));
610 uris = uri_list_to_glist(selection_data->data);
612 for (next = uris; next; next = next->next)
614 guchar *path;
616 path = get_local_path((EscapedPath *) next->data);
618 if (path)
620 GtkTreeIter iter;
621 struct stat info;
623 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
625 gtk_list_store_append(model, &iter);
626 gtk_list_store_set(model, &iter, 0, path,
627 1, path, -1);
629 else
630 delayed_error(_("'%s' isn't a directory"),
631 path);
633 g_free(path);
635 else
636 delayed_error(_("Can't bookmark non-local directories "
637 "like '%s'"), (gchar *) next->data);
640 destroy_glist(&uris);
642 return TRUE;
645 static void commit_edits(GtkTreeModel *model)
647 GtkTreeIter iter;
649 bookmarks_new();
651 if (gtk_tree_model_get_iter_first(model, &iter))
653 GValue mark = {0}, title={0};
654 xmlNode *root = xmlDocGetRootElement(bookmarks->doc);
658 xmlNode *bookmark;
660 gtk_tree_model_get_value(model, &iter, 0, &mark);
661 bookmark = xmlNewTextChild(root, NULL, "bookmark",
662 g_value_get_string(&mark));
663 g_value_unset(&mark);
664 gtk_tree_model_get_value(model, &iter, 1, &title);
665 xmlSetProp(bookmark, "title",
666 g_value_get_string(&title));
667 g_value_unset(&title);
668 } while (gtk_tree_model_iter_next(model, &iter));
671 bookmarks_save();
674 static void edit_response(GtkWidget *window, gint response, GtkTreeModel *model)
676 commit_edits(model);
678 gtk_widget_destroy(window);
681 static void cell_edited(GtkCellRendererText *cell,
682 const gchar *path_string,
683 const gchar *new_text,
684 gpointer data)
686 GtkTreeModel *model = (GtkTreeModel *) data;
687 GtkTreePath *path;
688 GtkTreeIter iter;
689 gint col;
691 path = gtk_tree_path_new_from_string(path_string);
692 gtk_tree_model_get_iter(model, &iter, path);
693 gtk_tree_path_free(path);
694 col=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column"));
696 gtk_list_store_set(GTK_LIST_STORE(model), &iter, col, new_text, -1);
699 static void activate_edit(GtkMenuShell *item, gpointer data)
701 bookmarks_edit();
704 static gint cmp_dirname(gconstpointer a, gconstpointer b)
706 return g_utf8_collate(*(gchar **) a, *(gchar **) b);
709 static void free_path_for_item(GtkWidget *widget, gpointer udata)
711 gchar *path=(gchar *) udata;
712 g_free(path);
715 static GtkWidget *build_history_menu(FilerWindow *filer_window)
717 GtkWidget *menu;
718 GPtrArray *items;
719 GList *next;
720 int i;
722 menu = gtk_menu_new();
724 if (!history)
725 return menu;
727 g_return_val_if_fail(history_hash != NULL, menu);
728 g_return_val_if_fail(history_tail != NULL, menu);
730 items = g_ptr_array_new();
732 for (next = history; next; next = next->next)
733 g_ptr_array_add(items, next->data);
735 g_ptr_array_sort(items, cmp_dirname);
737 for (i = 0; i < items->len; i++)
739 GtkWidget *item;
740 const char *path = (char *) items->pdata[i];
741 gchar *copy;
743 item = gtk_menu_item_new_with_label(path);
745 copy=g_strdup(path);
746 g_object_set_data(G_OBJECT(item), "bookmark-path", copy);
747 g_signal_connect(item, "destroy",
748 G_CALLBACK(free_path_for_item), copy);
750 if (strcmp(path, filer_window->sym_path) == 0)
751 gtk_widget_set_sensitive(item, FALSE);
752 else
753 g_signal_connect(item, "activate",
754 G_CALLBACK(bookmarks_activate),
755 filer_window);
757 gtk_widget_show(item);
758 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
761 g_ptr_array_free(items, TRUE);
763 return menu;
766 /* Builds the bookmarks' menu. Done whenever the bookmarks icon has been
767 * clicked.
769 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window)
771 GtkWidget *menu;
772 GtkWidget *item;
773 xmlNode *node;
774 gboolean need_separator = TRUE;
776 menu = gtk_menu_new();
778 item = gtk_menu_item_new_with_label(_("Add New Bookmark"));
779 g_signal_connect(item, "activate",
780 G_CALLBACK(bookmarks_add), filer_window);
781 gtk_widget_show(item);
782 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
783 gtk_menu_shell_select_item(GTK_MENU_SHELL(menu), item);
785 item = gtk_menu_item_new_with_label(_("Edit Bookmarks"));
786 g_signal_connect(item, "activate", G_CALLBACK(activate_edit), NULL);
787 gtk_widget_show(item);
788 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
790 item = gtk_menu_item_new_with_label(_("Recently Visited"));
791 gtk_widget_show(item);
792 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
793 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
794 build_history_menu(filer_window));
796 /* Now add all the bookmarks to the menu */
798 update_bookmarks();
800 node = xmlDocGetRootElement(bookmarks->doc);
802 for (node = node->xmlChildrenNode; node; node = node->next)
804 gchar *mark, *title, *path;
806 if (node->type != XML_ELEMENT_NODE)
807 continue;
808 if (strcmp(node->name, "bookmark") != 0)
809 continue;
811 mark = xmlNodeListGetString(bookmarks->doc,
812 node->xmlChildrenNode, 1);
813 if (!mark)
814 continue;
815 path=g_strdup(mark);
817 title=xmlGetProp(node, "title");
818 if(!title)
819 title=mark;
821 item = gtk_menu_item_new_with_label(title);
823 g_object_set_data(G_OBJECT(item), "bookmark-path", path);
824 g_signal_connect(item, "destroy",
825 G_CALLBACK(free_path_for_item), path);
827 if(title!=mark)
828 xmlFree(title);
829 xmlFree(mark);
831 g_signal_connect(item, "activate",
832 G_CALLBACK(bookmarks_activate),
833 filer_window);
835 gtk_widget_show(item);
837 if (need_separator)
839 GtkWidget *sep;
840 sep = gtk_separator_menu_item_new();
841 gtk_widget_show(sep);
842 gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
843 need_separator = FALSE;
846 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
850 return menu;