Converted README to markdown
[rox-filer.git] / ROX-Filer / src / bookmarks.c
blob33b91143f522542d78983bbc5b600bf8ee5c5ab3
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* bookmarks.c - handles the bookmarks menu */
22 #include "config.h"
24 #include <stdlib.h>
25 #include <gtk/gtk.h>
26 #include <string.h>
28 #include "global.h"
30 #include "bookmarks.h"
31 #include "choices.h"
32 #include "filer.h"
33 #include "xml.h"
34 #include "support.h"
35 #include "gui_support.h"
36 #include "main.h"
37 #include "mount.h"
38 #include "action.h"
39 #include "options.h"
40 #include "bind.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)
90 if (event->type == GDK_BUTTON_RELEASE ||
91 event->type == GDK_BUTTON_PRESS)
92 button = ((GdkEventButton *) event)->button;
93 gdk_event_free(event);
96 menu = GTK_MENU(bookmarks_build_menu(filer_window));
97 gtk_menu_popup(menu, NULL, NULL, position_menu, filer_window,
98 button, gtk_get_current_event_time());
101 /* Show the Edit Bookmarks dialog */
102 void bookmarks_edit(void)
104 GtkListStore *model;
105 GtkWidget *list, *hbox, *button, *swin;
106 GtkTreeSelection *selection;
107 GtkCellRenderer *cell;
108 xmlNode *node;
109 GtkTreeIter iter;
111 if (bookmarks_window)
113 gtk_window_present(GTK_WINDOW(bookmarks_window));
114 return;
117 update_bookmarks();
119 bookmarks_window = gtk_dialog_new();
120 number_of_windows++;
122 gtk_dialog_add_button(GTK_DIALOG(bookmarks_window),
123 GTK_STOCK_CLOSE, GTK_RESPONSE_OK);
125 g_signal_connect(bookmarks_window, "destroy",
126 G_CALLBACK(gtk_widget_destroyed), &bookmarks_window);
127 g_signal_connect(bookmarks_window, "destroy",
128 G_CALLBACK(one_less_window), NULL);
130 swin = gtk_scrolled_window_new(NULL, NULL);
131 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
132 GTK_SHADOW_IN);
133 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
134 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
135 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
136 swin, TRUE, TRUE, 0);
138 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
140 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
142 cell = gtk_cell_renderer_text_new();
143 g_signal_connect(G_OBJECT(cell), "edited",
144 G_CALLBACK(cell_edited), model);
145 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
146 g_object_set_data(G_OBJECT(cell), "column", GINT_TO_POINTER(0));
147 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
148 _("Path"), cell, "text", 0, NULL);
150 cell = gtk_cell_renderer_text_new();
151 g_signal_connect(G_OBJECT(cell), "edited",
152 G_CALLBACK(cell_edited), model);
153 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
154 g_object_set_data(G_OBJECT(cell), "column", GINT_TO_POINTER(1));
155 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
156 _("Title"), cell, "text", 1, NULL);
158 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), TRUE);
159 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
161 node = xmlDocGetRootElement(bookmarks->doc);
162 for (node = node->xmlChildrenNode; node; node = node->next)
164 GtkTreeIter iter;
165 gchar *mark, *title;
167 if (node->type != XML_ELEMENT_NODE)
168 continue;
169 if (strcmp(node->name, "bookmark") != 0)
170 continue;
172 mark = xmlNodeListGetString(bookmarks->doc,
173 node->xmlChildrenNode, 1);
174 if (!mark)
175 continue;
177 title=xmlGetProp(node, "title");
178 if(!title)
179 title=mark;
181 gtk_list_store_append(model, &iter);
182 gtk_list_store_set(model, &iter, 0, mark, 1, title, -1);
183 if(title!=mark)
184 xmlFree(title);
186 xmlFree(mark);
189 gtk_widget_set_size_request(list, 300, 300);
190 gtk_container_add(GTK_CONTAINER(swin), list);
192 hbox = gtk_hbutton_box_new();
193 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
194 hbox, FALSE, TRUE, 0);
195 gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
197 button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
198 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
199 g_signal_connect(button, "clicked", G_CALLBACK(edit_delete), list);
201 button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
202 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
203 g_signal_connect(button, "clicked", G_CALLBACK(reorder_up), list);
204 button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
205 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
206 g_signal_connect(button, "clicked", G_CALLBACK(reorder_down), list);
208 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
209 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
211 /* Select the first item, otherwise the first click starts edit
212 * mode, which is very confusing!
214 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
215 gtk_tree_selection_select_iter(selection, &iter);
217 g_signal_connect(bookmarks_window, "response",
218 G_CALLBACK(edit_response), model);
220 /* Allow directories to be dropped in */
222 GtkTargetEntry targets[] = { {"text/uri-list", 0, 0} };
223 gtk_drag_dest_set(bookmarks_window, GTK_DEST_DEFAULT_ALL,
224 targets, G_N_ELEMENTS(targets),
225 GDK_ACTION_COPY |GDK_ACTION_PRIVATE);
226 g_signal_connect(bookmarks_window, "drag-data-received",
227 G_CALLBACK(dir_dropped), list);
230 g_signal_connect_swapped(model, "row-changed",
231 G_CALLBACK(commit_edits), model);
232 g_signal_connect_swapped(model, "row-inserted",
233 G_CALLBACK(commit_edits), model);
234 g_signal_connect_swapped(model, "row-deleted",
235 G_CALLBACK(commit_edits), model);
236 g_signal_connect_swapped(model, "rows-reordered",
237 G_CALLBACK(commit_edits), model);
239 gtk_widget_show_all(bookmarks_window);
242 static void history_remove(const char *path)
244 GList *old;
246 old = g_hash_table_lookup(history_hash, path);
247 if (old)
249 g_hash_table_remove(history_hash, path);
251 if (history_tail == old)
252 history_tail = old->prev;
253 g_free(old->data);
254 history = g_list_delete_link(history, old);
256 history_free++;
260 /* Add this path to the global history of visited directories. If it
261 * already exists there, make it the most recent. If its parent exists
262 * already, remove the parent.
264 void bookmarks_add_history(const gchar *path)
266 char *new;
268 new = g_strdup(path);
269 ensure_utf8(&new);
271 if (!history_hash)
272 history_hash = g_hash_table_new(g_str_hash, g_str_equal);
274 history_remove(new);
277 char *parent;
278 parent = g_dirname(path);
279 history_remove(parent);
280 g_free(parent);
283 history = g_list_prepend(history, new);
284 if (!history_tail)
285 history_tail = history;
286 g_hash_table_insert(history_hash, new, history);
288 history_free--;
289 if (history_free == -1)
291 g_return_if_fail(history_tail != NULL);
292 history_remove((char *) history_tail->data);
296 void bookmarks_add_uri(const EscapedPath *uri)
298 char *path;
299 struct stat info;
301 path = get_local_path(uri);
303 if (!path)
305 delayed_error(_("Can't bookmark non-local resource '%s'\n"),
306 uri);
307 return;
310 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
311 bookmarks_add_dir(path);
312 else
313 delayed_error(_("'%s' isn't a directory"), path);
314 g_free(path);
317 /****************************************************************
318 * INTERNAL FUNCTIONS *
319 ****************************************************************/
321 /* Initialise the bookmarks document to be empty. Does not save. */
322 static void bookmarks_new(void)
324 if (bookmarks)
325 g_object_unref(G_OBJECT(bookmarks));
326 bookmarks = xml_new(NULL);
327 bookmarks->doc = xmlNewDoc("1.0");
328 xmlDocSetRootElement(bookmarks->doc,
329 xmlNewDocNode(bookmarks->doc, NULL, "bookmarks", NULL));
332 static void position_menu(GtkMenu *menu, gint *x, gint *y,
333 gboolean *push_in, gpointer data)
335 FilerWindow *filer_window = (FilerWindow *) data;
337 gdk_window_get_origin(GTK_WIDGET(filer_window->view)->window, x, y);
340 /* Makes sure that 'bookmarks' is up-to-date, reloading from file if it has
341 * changed. If no bookmarks were loaded and there is no file then initialise
342 * bookmarks to an empty document.
344 static void update_bookmarks()
346 gchar *path;
348 /* Update the bookmarks, if possible */
349 path = choices_find_xdg_path_load("Bookmarks.xml", PROJECT, SITE);
350 if (path)
352 XMLwrapper *wrapper;
353 wrapper = xml_cache_load(path);
354 if (wrapper)
356 if (bookmarks)
357 g_object_unref(bookmarks);
358 bookmarks = wrapper;
361 g_free(path);
364 if (!bookmarks)
365 bookmarks_new();
368 /* Return the node for the 'mark' bookmark */
369 static xmlNode *bookmark_find(const gchar *mark)
371 xmlNode *node;
373 update_bookmarks();
375 node = xmlDocGetRootElement(bookmarks->doc);
377 for (node = node->xmlChildrenNode; node; node = node->next)
379 gchar *path;
380 gboolean same;
382 if (node->type != XML_ELEMENT_NODE)
383 continue;
384 if (strcmp(node->name, "bookmark") != 0)
385 continue;
387 path = xmlNodeListGetString(bookmarks->doc,
388 node->xmlChildrenNode, 1);
389 if (!path)
390 continue;
392 same = strcmp(mark, path) == 0;
393 xmlFree(path);
395 if (same)
396 return node;
399 return NULL;
402 /* Save the bookmarks to a file */
403 static void bookmarks_save()
405 guchar *save_path;
407 save_path = choices_find_xdg_path_save("Bookmarks.xml", PROJECT, SITE,
408 TRUE);
409 if (save_path)
411 save_xml_file(bookmarks->doc, save_path);
412 g_free(save_path);
416 /* Add a bookmark if it doesn't already exist, and save the
417 * bookmarks.
419 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data)
421 FilerWindow *filer_window = (FilerWindow *) user_data;
423 bookmarks_add_dir(filer_window->sym_path);
426 static void bookmarks_add_dir(const guchar *dir)
428 xmlNode *bookmark;
430 if (bookmark_find(dir))
431 return;
433 bookmark = xmlNewTextChild(xmlDocGetRootElement(bookmarks->doc),
434 NULL, "bookmark", dir);
435 xmlSetProp(bookmark, "title", dir);
437 bookmarks_save();
439 if (bookmarks_window)
440 gtk_widget_destroy(bookmarks_window);
443 /* Called when a bookmark has been chosen */
444 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window)
446 const gchar *mark;
447 GtkLabel *label;
448 GdkEvent *event;
449 gboolean new_win=FALSE;
451 mark=g_object_get_data(G_OBJECT(item), "bookmark-path");
452 if(!mark) {
453 label = GTK_LABEL(GTK_BIN(item)->child);
454 mark = gtk_label_get_text(label);
457 event=gtk_get_current_event();
458 if(event)
460 if(event->type==GDK_BUTTON_PRESS ||
461 event->type==GDK_BUTTON_RELEASE)
463 GdkEventButton *button=(GdkEventButton *) event;
465 new_win=o_new_button_1.int_value?
466 button->button==1: button->button!=1;
468 gdk_event_free(event);
471 if (strcmp(mark, filer_window->sym_path) != 0)
473 if(new_win)
474 filer_opendir(mark, filer_window, NULL);
475 else
476 filer_change_to(filer_window, mark, NULL);
478 if (g_hash_table_lookup(fstab_mounts, filer_window->real_path) &&
479 !mount_is_mounted(filer_window->real_path, NULL, NULL))
481 GList *paths;
483 paths = g_list_prepend(NULL, filer_window->real_path);
484 action_mount(paths, FALSE, TRUE, -1);
485 g_list_free(paths);
489 static void edit_delete(GtkButton *button, GtkTreeView *view)
491 GtkTreeModel *model;
492 GtkListStore *list;
493 GtkTreeSelection *selection;
494 GtkTreeIter iter;
495 gboolean more, any = FALSE;
497 model = gtk_tree_view_get_model(view);
498 list = GTK_LIST_STORE(model);
500 selection = gtk_tree_view_get_selection(view);
502 more = gtk_tree_model_get_iter_first(model, &iter);
504 while (more)
506 GtkTreeIter old = iter;
508 more = gtk_tree_model_iter_next(model, &iter);
510 if (gtk_tree_selection_iter_is_selected(selection, &old))
512 any = TRUE;
513 gtk_list_store_remove(list, &old);
517 if (!any)
519 report_error(_("You should first select some rows to delete"));
520 return;
524 static void reorder(GtkTreeView *view, int dir)
526 GtkTreeModel *model;
527 GtkListStore *list;
528 GtkTreePath *cursor = NULL;
529 GtkTreeIter iter, old, new;
530 GValue mark = {0};
531 GValue title = {0};
532 gboolean ok;
534 g_return_if_fail(view != NULL);
535 g_return_if_fail(dir == 1 || dir == -1);
537 model = gtk_tree_view_get_model(view);
538 list = GTK_LIST_STORE(model);
540 gtk_tree_view_get_cursor(view, &cursor, NULL);
541 if (!cursor)
543 report_error(_("Put the cursor on an entry in the "
544 "list to move it"));
545 return;
548 gtk_tree_model_get_iter(model, &old, cursor);
549 if (dir > 0)
551 gtk_tree_path_next(cursor);
552 ok = gtk_tree_model_get_iter(model, &iter, cursor);
554 else
556 ok = gtk_tree_path_prev(cursor);
557 if (ok)
558 gtk_tree_model_get_iter(model, &iter, cursor);
560 if (!ok)
562 gtk_tree_path_free(cursor);
563 report_error(_("This item is already at the end"));
564 return;
567 gtk_tree_model_get_value(model, &old, 0, &mark);
568 gtk_tree_model_get_value(model, &old, 1, &title);
569 if (dir > 0)
570 gtk_list_store_insert_after(list, &new, &iter);
571 else
572 gtk_list_store_insert_before(list, &new, &iter);
573 gtk_list_store_set(list, &new, 0, g_value_get_string(&mark), -1);
574 gtk_list_store_set(list, &new, 1, g_value_get_string(&title), -1);
575 gtk_list_store_remove(list, &old);
577 g_value_unset(&mark);
578 g_value_unset(&title);
580 gtk_tree_view_set_cursor(view, cursor, 0, FALSE);
581 gtk_tree_path_free(cursor);
584 static void reorder_up(GtkButton *button, GtkTreeView *view)
586 reorder(view, -1);
589 static void reorder_down(GtkButton *button, GtkTreeView *view)
591 reorder(view, 1);
594 static gboolean dir_dropped(GtkWidget *window, GdkDragContext *context,
595 int x, int y,
596 GtkSelectionData *selection_data, guint info,
597 guint time, GtkTreeView *view)
599 GtkListStore *model;
600 GList *uris, *next;
602 if (!selection_data->data)
604 /* Timeout? */
605 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
606 return TRUE;
609 model = GTK_LIST_STORE(gtk_tree_view_get_model(view));
611 uris = uri_list_to_glist(selection_data->data);
613 for (next = uris; next; next = next->next)
615 guchar *path;
617 path = get_local_path((EscapedPath *) next->data);
619 if (path)
621 GtkTreeIter iter;
622 struct stat info;
624 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
626 gtk_list_store_append(model, &iter);
627 gtk_list_store_set(model, &iter, 0, path,
628 1, path, -1);
630 else
631 delayed_error(_("'%s' isn't a directory"),
632 path);
634 g_free(path);
636 else
637 delayed_error(_("Can't bookmark non-local directories "
638 "like '%s'"), (gchar *) next->data);
641 destroy_glist(&uris);
643 return TRUE;
646 static void commit_edits(GtkTreeModel *model)
648 GtkTreeIter iter;
650 bookmarks_new();
652 if (gtk_tree_model_get_iter_first(model, &iter))
654 GValue mark = {0}, title={0};
655 xmlNode *root = xmlDocGetRootElement(bookmarks->doc);
659 xmlNode *bookmark;
661 gtk_tree_model_get_value(model, &iter, 0, &mark);
662 bookmark = xmlNewTextChild(root, NULL, "bookmark",
663 g_value_get_string(&mark));
664 g_value_unset(&mark);
665 gtk_tree_model_get_value(model, &iter, 1, &title);
666 xmlSetProp(bookmark, "title",
667 g_value_get_string(&title));
668 g_value_unset(&title);
669 } while (gtk_tree_model_iter_next(model, &iter));
672 bookmarks_save();
675 static void edit_response(GtkWidget *window, gint response, GtkTreeModel *model)
677 commit_edits(model);
679 gtk_widget_destroy(window);
682 static void cell_edited(GtkCellRendererText *cell,
683 const gchar *path_string,
684 const gchar *new_text,
685 gpointer data)
687 GtkTreeModel *model = (GtkTreeModel *) data;
688 GtkTreePath *path;
689 GtkTreeIter iter;
690 gint col;
692 path = gtk_tree_path_new_from_string(path_string);
693 gtk_tree_model_get_iter(model, &iter, path);
694 gtk_tree_path_free(path);
695 col=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column"));
697 gtk_list_store_set(GTK_LIST_STORE(model), &iter, col, new_text, -1);
700 static void activate_edit(GtkMenuShell *item, gpointer data)
702 bookmarks_edit();
705 static gint cmp_dirname(gconstpointer a, gconstpointer b)
707 return g_utf8_collate(*(gchar **) a, *(gchar **) b);
710 static void free_path_for_item(GtkWidget *widget, gpointer udata)
712 gchar *path=(gchar *) udata;
713 g_free(path);
716 static GtkWidget *build_history_menu(FilerWindow *filer_window)
718 GtkWidget *menu;
719 GPtrArray *items;
720 GList *next;
721 int i;
723 menu = gtk_menu_new();
725 if (!history)
726 return menu;
728 g_return_val_if_fail(history_hash != NULL, menu);
729 g_return_val_if_fail(history_tail != NULL, menu);
731 items = g_ptr_array_new();
733 for (next = history; next; next = next->next)
734 g_ptr_array_add(items, next->data);
736 g_ptr_array_sort(items, cmp_dirname);
738 for (i = 0; i < items->len; i++)
740 GtkWidget *item;
741 const char *path = (char *) items->pdata[i];
742 gchar *copy;
744 item = gtk_menu_item_new_with_label(path);
746 copy=g_strdup(path);
747 g_object_set_data(G_OBJECT(item), "bookmark-path", copy);
748 g_signal_connect(item, "destroy",
749 G_CALLBACK(free_path_for_item), copy);
751 if (strcmp(path, filer_window->sym_path) == 0)
752 gtk_widget_set_sensitive(item, FALSE);
753 else
754 g_signal_connect(item, "activate",
755 G_CALLBACK(bookmarks_activate),
756 filer_window);
758 gtk_widget_show(item);
759 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
762 g_ptr_array_free(items, TRUE);
764 return menu;
767 /* Builds the bookmarks' menu. Done whenever the bookmarks icon has been
768 * clicked.
770 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window)
772 GtkWidget *menu;
773 GtkWidget *item;
774 xmlNode *node;
775 gboolean need_separator = TRUE;
777 menu = gtk_menu_new();
779 item = gtk_menu_item_new_with_label(_("Add New Bookmark"));
780 g_signal_connect(item, "activate",
781 G_CALLBACK(bookmarks_add), filer_window);
782 gtk_widget_show(item);
783 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
784 gtk_menu_shell_select_item(GTK_MENU_SHELL(menu), item);
786 item = gtk_menu_item_new_with_label(_("Edit Bookmarks"));
787 g_signal_connect(item, "activate", G_CALLBACK(activate_edit), NULL);
788 gtk_widget_show(item);
789 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
791 item = gtk_menu_item_new_with_label(_("Recently Visited"));
792 gtk_widget_show(item);
793 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
794 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
795 build_history_menu(filer_window));
797 /* Now add all the bookmarks to the menu */
799 update_bookmarks();
801 node = xmlDocGetRootElement(bookmarks->doc);
803 for (node = node->xmlChildrenNode; node; node = node->next)
805 gchar *mark, *title, *path;
807 if (node->type != XML_ELEMENT_NODE)
808 continue;
809 if (strcmp(node->name, "bookmark") != 0)
810 continue;
812 mark = xmlNodeListGetString(bookmarks->doc,
813 node->xmlChildrenNode, 1);
814 if (!mark)
815 continue;
816 path=g_strdup(mark);
818 title=xmlGetProp(node, "title");
819 if(!title)
820 title=mark;
822 item = gtk_menu_item_new_with_label(title);
824 g_object_set_data(G_OBJECT(item), "bookmark-path", path);
825 g_signal_connect(item, "destroy",
826 G_CALLBACK(free_path_for_item), path);
828 if(title!=mark)
829 xmlFree(title);
830 xmlFree(mark);
832 g_signal_connect(item, "activate",
833 G_CALLBACK(bookmarks_activate),
834 filer_window);
836 gtk_widget_show(item);
838 if (need_separator)
840 GtkWidget *sep;
841 sep = gtk_separator_menu_item_new();
842 gtk_widget_show(sep);
843 gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
844 need_separator = FALSE;
847 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
851 return menu;