r2228: Made 'Automatic' an icon size, rather than a separate option.
[rox-filer.git] / ROX-Filer / src / bookmarks.c
blob62705b3a3533809b93515bc1b79e2ce0c5ba64b0
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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>
29 #include "global.h"
31 #include "bookmarks.h"
32 #include "choices.h"
33 #include "filer.h"
34 #include "xml.h"
35 #include "support.h"
36 #include "gui_support.h"
38 static XMLwrapper *bookmarks = NULL;
39 static GtkWidget *bookmarks_window = NULL;
41 /* Static prototypes */
42 static void update_bookmarks(void);
43 static xmlNode *bookmark_find(const gchar *mark);
44 static void bookmarks_save(void);
45 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data);
46 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window);
47 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window);
48 static void position_menu(GtkMenu *menu, gint *x, gint *y,
49 gboolean *push_in, gpointer data);
50 static void cell_edited(GtkCellRendererText *cell,
51 const gchar *path_string,
52 const gchar *new_text,
53 gpointer data);
54 static void reorder_up(GtkButton *button, GtkTreeView *view);
55 static void reorder_down(GtkButton *button, GtkTreeView *view);
56 static void edit_response(GtkWidget *window, gint response,
57 GtkTreeModel *model);
58 static void edit_delete(GtkButton *button, GtkTreeView *view);
61 /****************************************************************
62 * EXTERNAL INTERFACE *
63 ****************************************************************/
65 /* Shows the bookmarks menu */
66 void bookmarks_show_menu(FilerWindow *filer_window)
68 GdkEvent *event;
69 GtkMenu *menu;
70 int button = 0;
72 event = gtk_get_current_event();
73 if (event->type == GDK_BUTTON_RELEASE ||
74 event->type == GDK_BUTTON_PRESS)
75 button = ((GdkEventButton *) event)->button;
77 menu = GTK_MENU(bookmarks_build_menu(filer_window));
78 gtk_menu_popup(menu, NULL, NULL, position_menu, filer_window,
79 button, gtk_get_current_event_time());
82 /* Show the Edit Bookmarks dialog */
83 void bookmarks_edit(void)
85 GtkListStore *model;
86 GtkWidget *list, *frame, *hbox, *button;
87 GtkTreeSelection *selection;
88 GtkCellRenderer *cell;
89 xmlNode *node;
91 if (bookmarks_window)
93 gtk_window_present(GTK_WINDOW(bookmarks_window));
94 return;
97 update_bookmarks();
99 bookmarks_window = gtk_dialog_new();
101 gtk_dialog_add_button(GTK_DIALOG(bookmarks_window),
102 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
103 gtk_dialog_add_button(GTK_DIALOG(bookmarks_window),
104 GTK_STOCK_OK, GTK_RESPONSE_OK);
106 g_signal_connect(bookmarks_window, "destroy",
107 G_CALLBACK(gtk_widget_destroyed), &bookmarks_window);
109 frame = gtk_frame_new(NULL);
110 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
111 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
112 frame, TRUE, TRUE, 0);
114 model = gtk_list_store_new(1, G_TYPE_STRING);
116 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
118 cell = gtk_cell_renderer_text_new();
119 g_signal_connect(G_OBJECT(cell), "edited",
120 G_CALLBACK(cell_edited), model);
121 g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
122 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1,
123 NULL, cell, "text", 0, NULL);
124 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), TRUE);
125 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
127 node = xmlDocGetRootElement(bookmarks->doc);
128 for (node = node->xmlChildrenNode; node; node = node->next)
130 GtkTreeIter iter;
131 guchar *mark;
133 if (node->type != XML_ELEMENT_NODE)
134 continue;
135 if (strcmp(node->name, "bookmark") != 0)
136 continue;
138 mark = xmlNodeListGetString(bookmarks->doc,
139 node->xmlChildrenNode, 1);
140 if (!mark)
141 continue;
143 gtk_list_store_append(model, &iter);
144 gtk_list_store_set(model, &iter, 0, mark, -1);
146 xmlFree(mark);
149 gtk_widget_set_size_request(list, 300, 100);
150 gtk_container_add(GTK_CONTAINER(frame), list);
152 hbox = gtk_hbutton_box_new();
153 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bookmarks_window)->vbox),
154 hbox, FALSE, TRUE, 0);
155 gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
157 button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
158 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
159 g_signal_connect(button, "clicked", G_CALLBACK(edit_delete), list);
161 button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
162 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
163 g_signal_connect(button, "clicked", G_CALLBACK(reorder_up), list);
164 button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
165 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
166 g_signal_connect(button, "clicked", G_CALLBACK(reorder_down), list);
168 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
169 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
171 g_signal_connect(bookmarks_window, "response",
172 G_CALLBACK(edit_response), model);
174 gtk_widget_show_all(bookmarks_window);
178 /****************************************************************
179 * INTERNAL FUNCTIONS *
180 ****************************************************************/
182 /* Initialise the bookmarks document to be empty. Does not save. */
183 static void bookmarks_new(void)
185 if (bookmarks)
186 g_object_unref(G_OBJECT(bookmarks));
187 bookmarks = xml_new(NULL);
188 bookmarks->doc = xmlNewDoc("1.0");
189 xmlDocSetRootElement(bookmarks->doc,
190 xmlNewDocNode(bookmarks->doc, NULL, "bookmarks", NULL));
193 static void position_menu(GtkMenu *menu, gint *x, gint *y,
194 gboolean *push_in, gpointer data)
196 FilerWindow *filer_window = (FilerWindow *) data;
198 gdk_window_get_origin(GTK_WIDGET(filer_window->view)->window, x, y);
201 /* Makes sure that 'bookmarks' is up-to-date, reloading from file if it has
202 * changed. If no bookmarks were loaded and there is no file then initialise
203 * bookmarks to an empty document.
205 static void update_bookmarks()
207 gchar *path;
209 /* Update the bookmarks, if possible */
210 path = choices_find_path_load("Bookmarks.xml", PROJECT);
211 if (path)
213 XMLwrapper *wrapper;
214 wrapper = xml_cache_load(path);
215 if (wrapper)
217 if (bookmarks)
218 g_object_unref(bookmarks);
219 bookmarks = wrapper;
222 g_free(path);
225 if (!bookmarks)
226 bookmarks_new();
229 /* Return the node for the 'mark' bookmark */
230 static xmlNode *bookmark_find(const gchar *mark)
232 xmlNode *node;
234 update_bookmarks();
236 node = xmlDocGetRootElement(bookmarks->doc);
238 for (node = node->xmlChildrenNode; node; node = node->next)
240 gchar *path;
241 gboolean same;
243 if (node->type != XML_ELEMENT_NODE)
244 continue;
245 if (strcmp(node->name, "bookmark") != 0)
246 continue;
248 path = xmlNodeListGetString(bookmarks->doc,
249 node->xmlChildrenNode, 1);
250 if (!path)
251 continue;
253 same = strcmp(mark, path) == 0;
254 xmlFree(path);
256 if (same)
257 return node;
260 return NULL;
263 /* Save the bookmarks to a file */
264 static void bookmarks_save()
266 guchar *save_path;
268 save_path = choices_find_path_save("Bookmarks.xml", PROJECT, TRUE);
269 if (save_path)
271 save_xml_file(bookmarks->doc, save_path);
272 g_free(save_path);
275 if (bookmarks_window)
276 gtk_widget_destroy(bookmarks_window);
279 /* Add a bookmark if it doesn't already exist, and save the
280 * bookmarks.
282 static void bookmarks_add(GtkMenuItem *menuitem, gpointer user_data)
284 FilerWindow *filer_window = (FilerWindow *) user_data;
285 xmlNode *bookmark;
286 guchar *name;
288 name = filer_window->sym_path;
289 if (bookmark_find(name))
290 return;
292 bookmark = xmlNewChild(xmlDocGetRootElement(bookmarks->doc),
293 NULL, "bookmark", name);
295 bookmarks_save();
298 /* Called when a bookmark has been selected (right or left click) */
299 static void bookmarks_activate(GtkMenuShell *item, FilerWindow *filer_window)
301 const gchar *mark;
302 GtkLabel *label;
304 label = GTK_LABEL(GTK_BIN(item)->child);
305 mark = gtk_label_get_text(label);
307 if (strcmp(mark, filer_window->sym_path) != 0)
308 filer_change_to(filer_window, mark, NULL);
311 static void edit_delete(GtkButton *button, GtkTreeView *view)
313 GtkTreeModel *model;
314 GtkListStore *list;
315 GtkTreeSelection *selection;
316 GtkTreeIter iter;
317 gboolean more, any = FALSE;
319 model = gtk_tree_view_get_model(view);
320 list = GTK_LIST_STORE(model);
322 selection = gtk_tree_view_get_selection(view);
324 more = gtk_tree_model_get_iter_first(model, &iter);
326 while (more)
328 GtkTreeIter old = iter;
330 more = gtk_tree_model_iter_next(model, &iter);
332 if (gtk_tree_selection_iter_is_selected(selection, &old))
334 any = TRUE;
335 gtk_list_store_remove(list, &old);
339 if (!any)
341 report_error(_("You should first select some rows to delete"));
342 return;
346 static void reorder(GtkTreeView *view, int dir)
348 GtkTreeModel *model;
349 GtkListStore *list;
350 GtkTreePath *cursor = NULL;
351 GtkTreeIter iter, old, new;
352 GValue value = {0};
353 gboolean ok;
355 g_return_if_fail(view != NULL);
356 g_return_if_fail(dir == 1 || dir == -1);
358 model = gtk_tree_view_get_model(view);
359 list = GTK_LIST_STORE(model);
361 gtk_tree_view_get_cursor(view, &cursor, NULL);
362 if (!cursor)
364 report_error(_("Put the cursor on an entry in the "
365 "list to move it"));
366 return;
369 gtk_tree_model_get_iter(model, &old, cursor);
370 if (dir > 0)
372 gtk_tree_path_next(cursor);
373 ok = gtk_tree_model_get_iter(model, &iter, cursor);
375 else
377 ok = gtk_tree_path_prev(cursor);
378 if (ok)
379 gtk_tree_model_get_iter(model, &iter, cursor);
381 if (!ok)
383 gtk_tree_path_free(cursor);
384 report_error(_("This item is already at the end"));
385 return;
388 gtk_tree_model_get_value(model, &old, 0, &value);
389 if (dir > 0)
390 gtk_list_store_insert_after(list, &new, &iter);
391 else
392 gtk_list_store_insert_before(list, &new, &iter);
393 gtk_list_store_set(list, &new, 0, g_value_get_string(&value), -1);
394 gtk_list_store_remove(list, &old);
396 g_value_unset(&value);
398 gtk_tree_view_set_cursor(view, cursor, 0, FALSE);
399 gtk_tree_path_free(cursor);
402 static void reorder_up(GtkButton *button, GtkTreeView *view)
404 reorder(view, -1);
407 static void reorder_down(GtkButton *button, GtkTreeView *view)
409 reorder(view, 1);
412 static void edit_response(GtkWidget *window, gint response, GtkTreeModel *model)
414 GtkTreeIter iter;
416 if (response != GTK_RESPONSE_OK)
418 gtk_widget_destroy(window);
419 return;
422 bookmarks_new();
424 if (gtk_tree_model_get_iter_first(model, &iter))
426 GValue value = {0};
427 xmlNode *root = xmlDocGetRootElement(bookmarks->doc);
431 xmlNode *bookmark;
433 gtk_tree_model_get_value(model, &iter, 0, &value);
434 bookmark = xmlNewChild(root, NULL, "bookmark",
435 g_value_get_string(&value));
436 g_value_unset(&value);
437 } while (gtk_tree_model_iter_next(model, &iter));
440 bookmarks_save();
442 gtk_widget_destroy(window);
445 static void cell_edited(GtkCellRendererText *cell,
446 const gchar *path_string,
447 const gchar *new_text,
448 gpointer data)
450 GtkTreeModel *model = (GtkTreeModel *) data;
451 GtkTreePath *path;
452 GtkTreeIter iter;
454 path = gtk_tree_path_new_from_string(path_string);
455 gtk_tree_model_get_iter(model, &iter, path);
456 gtk_tree_path_free(path);
458 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, new_text, -1);
461 static void activate_edit(GtkMenuShell *item, gpointer data)
463 bookmarks_edit();
466 /* Builds the bookmarks' menu. Done whenever the bookmarks icon has been
467 * clicked.
469 static GtkWidget *bookmarks_build_menu(FilerWindow *filer_window)
471 GtkWidget *menu;
472 GtkWidget *item;
473 xmlNode *node;
474 gboolean need_separator = TRUE;
476 menu = gtk_menu_new();
478 item = gtk_menu_item_new_with_label(_("Add new bookmark"));
479 g_signal_connect(item, "activate",
480 G_CALLBACK(bookmarks_add), filer_window);
481 gtk_widget_show(item);
482 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
483 gtk_menu_shell_select_item(GTK_MENU_SHELL(menu), item);
485 item = gtk_menu_item_new_with_label(_("Edit bookmarks"));
486 g_signal_connect(item, "activate", G_CALLBACK(activate_edit), NULL);
487 gtk_widget_show(item);
488 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
490 /* Now add all the bookmarks to the menu */
492 update_bookmarks();
494 node = xmlDocGetRootElement(bookmarks->doc);
496 for (node = node->xmlChildrenNode; node; node = node->next)
498 gchar *mark;
500 if (node->type != XML_ELEMENT_NODE)
501 continue;
502 if (strcmp(node->name, "bookmark") != 0)
503 continue;
505 mark = xmlNodeListGetString(bookmarks->doc,
506 node->xmlChildrenNode, 1);
507 if (!mark)
508 continue;
509 item = gtk_menu_item_new_with_label(mark);
510 xmlFree(mark);
512 g_signal_connect(item, "activate",
513 G_CALLBACK(bookmarks_activate),
514 filer_window);
516 gtk_widget_show(item);
518 if (need_separator)
520 GtkWidget *sep;
521 sep = gtk_separator_menu_item_new();
522 gtk_widget_show(sep);
523 gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
524 need_separator = FALSE;
527 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
531 return menu;