summaryrefslogtreecommitdiff
path: root/src/caja-bookmarks-window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/caja-bookmarks-window.c')
-rw-r--r--src/caja-bookmarks-window.c1112
1 files changed, 1112 insertions, 0 deletions
diff --git a/src/caja-bookmarks-window.c b/src/caja-bookmarks-window.c
new file mode 100644
index 00000000..afd87425
--- /dev/null
+++ b/src/caja-bookmarks-window.c
@@ -0,0 +1,1112 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/*
+ * Caja
+ *
+ * Copyright (C) 1999, 2000 Eazel, Inc.
+ *
+ * Caja is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Caja is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors: John Sullivan <[email protected]>
+ */
+
+/* caja-bookmarks-window.c - implementation of bookmark-editing window.
+ */
+
+#include <config.h>
+#include "caja-bookmarks-window.h"
+#include "caja-window.h"
+#include "caja-navigation-window.h"
+#include "caja-spatial-window.h"
+#include <libcaja-private/caja-undo.h>
+#include <libcaja-private/caja-global-preferences.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-mate-extensions.h>
+#include <libcaja-private/caja-undo-signal-handlers.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+/* Static variables to keep track of window state. If there were
+ * more than one bookmark-editing window, these would be struct or
+ * class fields.
+ */
+static int bookmark_list_changed_signal_id;
+static CajaBookmarkList *bookmarks = NULL;
+static GtkTreeView *bookmark_list_widget = NULL; /* awkward name to distinguish from CajaBookmarkList */
+static GtkListStore *bookmark_list_store = NULL;
+static GtkListStore *bookmark_empty_list_store = NULL;
+static GtkTreeSelection *bookmark_selection = NULL;
+static int selection_changed_id = 0;
+static GtkWidget *name_field = NULL;
+static int name_field_changed_signal_id;
+static GtkWidget *remove_button = NULL;
+static GtkWidget *jump_button = NULL;
+static gboolean text_changed = FALSE;
+static gboolean name_text_changed = FALSE;
+static GtkWidget *uri_field = NULL;
+static int uri_field_changed_signal_id;
+static int row_changed_signal_id;
+static int row_deleted_signal_id;
+static int row_activated_signal_id;
+static int button_pressed_signal_id;
+static int key_pressed_signal_id;
+static int jump_button_signal_id;
+static CajaApplication *application;
+static gboolean parent_is_browser_window;
+
+/* forward declarations */
+static guint get_selected_row (void);
+static gboolean get_selection_exists (void);
+static void name_or_uri_field_activate (CajaEntry *entry);
+static void caja_bookmarks_window_restore_geometry (GtkWidget *window);
+static void on_bookmark_list_changed (CajaBookmarkList *list,
+ gpointer user_data);
+static void on_name_field_changed (GtkEditable *editable,
+ gpointer user_data);
+static void on_remove_button_clicked (GtkButton *button,
+ gpointer user_data);
+static void on_jump_button_clicked (GtkButton *button,
+ gpointer user_data);
+static void on_row_changed (GtkListStore *store,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data);
+static void on_row_deleted (GtkListStore *store,
+ GtkTreePath *path,
+ gpointer user_data);
+static void on_row_activated (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data);
+static gboolean on_button_pressed (GtkTreeView *view,
+ GdkEventButton *event,
+ gpointer user_data);
+static gboolean on_key_pressed (GtkTreeView *view,
+ GdkEventKey *event,
+ gpointer user_data);
+static void on_selection_changed (GtkTreeSelection *treeselection,
+ gpointer user_data);
+
+static gboolean on_text_field_focus_out_event (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data);
+static void on_uri_field_changed (GtkEditable *editable,
+ gpointer user_data);
+static gboolean on_window_delete_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data);
+static void on_window_hide_event (GtkWidget *widget,
+ gpointer user_data);
+static void on_window_destroy_event (GtkWidget *widget,
+ gpointer user_data);
+static void repopulate (void);
+static void set_up_close_accelerator (GtkWidget *window);
+static void open_selected_bookmark (gpointer user_data, GdkScreen *screen);
+static void update_bookmark_from_text (void);
+
+/* We store a pointer to the bookmark in a column so when an item is moved
+ with DnD we know which item it is. However we have to be careful to keep
+ this in sync with the actual bookmark. Note that
+ caja_bookmark_list_insert_item() makes a copy of the bookmark, so we
+ have to fetch the new copy and update our pointer. */
+#define BOOKMARK_LIST_COLUMN_ICON 0
+#define BOOKMARK_LIST_COLUMN_NAME 1
+#define BOOKMARK_LIST_COLUMN_BOOKMARK 2
+#define BOOKMARK_LIST_COLUMN_STYLE 3
+#define BOOKMARK_LIST_COLUMN_COUNT 4
+
+/* layout constants */
+
+/* Keep window from shrinking down ridiculously small; numbers are somewhat arbitrary */
+#define BOOKMARKS_WINDOW_MIN_WIDTH 300
+#define BOOKMARKS_WINDOW_MIN_HEIGHT 100
+
+/* Larger size initially; user can stretch or shrink (but not shrink below min) */
+#define BOOKMARKS_WINDOW_INITIAL_WIDTH 500
+#define BOOKMARKS_WINDOW_INITIAL_HEIGHT 200
+
+static void
+caja_bookmarks_window_response_callback (GtkDialog *dialog,
+ int response_id,
+ gpointer callback_data)
+{
+ if (response_id == GTK_RESPONSE_HELP)
+ {
+ GError *error = NULL;
+
+ gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
+ "ghelp:user-guide#goscaja-36",
+ gtk_get_current_event_time (), &error);
+
+ if (error)
+ {
+ GtkWidget *err_dialog;
+ err_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("There was an error displaying help: \n%s"),
+ error->message);
+
+ g_signal_connect (G_OBJECT (err_dialog),
+ "response", G_CALLBACK (gtk_widget_destroy),
+ NULL);
+ gtk_window_set_resizable (GTK_WINDOW (err_dialog), FALSE);
+ gtk_widget_show (err_dialog);
+ g_error_free (error);
+ }
+ }
+ else if (response_id == GTK_RESPONSE_CLOSE)
+ {
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ }
+}
+
+static GtkListStore *
+create_bookmark_store (void)
+{
+ return gtk_list_store_new (BOOKMARK_LIST_COLUMN_COUNT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ PANGO_TYPE_STYLE);
+}
+
+static void
+setup_empty_list (void)
+{
+ GtkTreeIter iter;
+
+ bookmark_empty_list_store = create_bookmark_store ();
+ gtk_list_store_append (bookmark_empty_list_store, &iter);
+
+ gtk_list_store_set (bookmark_empty_list_store, &iter,
+ BOOKMARK_LIST_COLUMN_NAME, _("No bookmarks defined"),
+ BOOKMARK_LIST_COLUMN_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+}
+
+static void
+bookmarks_set_empty (gboolean empty)
+{
+ GtkTreeIter iter;
+
+ if (empty)
+ {
+ gtk_tree_view_set_model (bookmark_list_widget,
+ GTK_TREE_MODEL (bookmark_empty_list_store));
+ gtk_widget_set_sensitive (GTK_WIDGET (bookmark_list_widget), FALSE);
+ }
+ else
+ {
+ gtk_tree_view_set_model (bookmark_list_widget,
+ GTK_TREE_MODEL (bookmark_list_store));
+ gtk_widget_set_sensitive (GTK_WIDGET (bookmark_list_widget), TRUE);
+
+ if (caja_bookmark_list_length (bookmarks) > 0 &&
+ !get_selection_exists ())
+ {
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (bookmark_list_store),
+ &iter, NULL, 0);
+ gtk_tree_selection_select_iter (bookmark_selection, &iter);
+ }
+ }
+
+ on_selection_changed (bookmark_selection, NULL);
+}
+
+static void
+edit_bookmarks_dialog_reset_signals (gpointer data,
+ GObject *obj)
+{
+ g_signal_handler_disconnect (GTK_OBJECT (jump_button),
+ jump_button_signal_id);
+ g_signal_handler_disconnect (GTK_OBJECT (bookmark_list_widget),
+ row_activated_signal_id);
+ jump_button_signal_id =
+ g_signal_connect (jump_button, "clicked",
+ G_CALLBACK (on_jump_button_clicked), NULL);
+ row_activated_signal_id =
+ g_signal_connect (bookmark_list_widget, "row_activated",
+ G_CALLBACK (on_row_activated), NULL);
+}
+
+/**
+ * create_bookmarks_window:
+ *
+ * Create a new bookmark-editing window.
+ * @list: The CajaBookmarkList that this window will edit.
+ *
+ * Return value: A pointer to the new window.
+ **/
+GtkWindow *
+create_bookmarks_window (CajaBookmarkList *list, GObject *undo_manager_source)
+{
+ GtkWidget *window;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *rend;
+ GtkBuilder *builder;
+
+ bookmarks = list;
+
+ builder = gtk_builder_new ();
+ if (!gtk_builder_add_from_file (builder,
+ UIDIR "/caja-bookmarks-window.ui",
+ NULL))
+ {
+ return NULL;
+ }
+
+ window = (GtkWidget *)gtk_builder_get_object (builder, "bookmarks_dialog");
+ bookmark_list_widget = (GtkTreeView *)gtk_builder_get_object (builder, "bookmark_tree_view");
+ remove_button = (GtkWidget *)gtk_builder_get_object (builder, "bookmark_delete_button");
+ jump_button = (GtkWidget *)gtk_builder_get_object (builder, "bookmark_jump_button");
+
+ application = CAJA_WINDOW (undo_manager_source)->application;
+
+ if (CAJA_IS_NAVIGATION_WINDOW (undo_manager_source))
+ {
+ parent_is_browser_window = TRUE;
+ }
+ else
+ {
+ parent_is_browser_window = FALSE;
+ }
+
+ set_up_close_accelerator (window);
+ caja_undo_share_undo_manager (G_OBJECT (window), undo_manager_source);
+
+ gtk_window_set_wmclass (GTK_WINDOW (window), "bookmarks", "Caja");
+ caja_bookmarks_window_restore_geometry (window);
+
+ g_object_weak_ref (G_OBJECT (undo_manager_source), edit_bookmarks_dialog_reset_signals,
+ undo_manager_source);
+
+ bookmark_list_widget = GTK_TREE_VIEW (gtk_builder_get_object (builder, "bookmark_tree_view"));
+
+ rend = gtk_cell_renderer_pixbuf_new ();
+ col = gtk_tree_view_column_new_with_attributes ("Icon",
+ rend,
+ "pixbuf",
+ BOOKMARK_LIST_COLUMN_ICON,
+ NULL);
+ gtk_tree_view_append_column (bookmark_list_widget,
+ GTK_TREE_VIEW_COLUMN (col));
+ gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (col),
+ CAJA_ICON_SIZE_SMALLER);
+
+ rend = gtk_cell_renderer_text_new ();
+ g_object_set (rend,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "ellipsize-set", TRUE,
+ NULL);
+
+ col = gtk_tree_view_column_new_with_attributes ("Icon",
+ rend,
+ "text",
+ BOOKMARK_LIST_COLUMN_NAME,
+ "style",
+ BOOKMARK_LIST_COLUMN_STYLE,
+ NULL);
+ gtk_tree_view_append_column (bookmark_list_widget,
+ GTK_TREE_VIEW_COLUMN (col));
+
+ bookmark_list_store = create_bookmark_store ();
+ setup_empty_list ();
+ gtk_tree_view_set_model (bookmark_list_widget,
+ GTK_TREE_MODEL (bookmark_empty_list_store));
+
+ bookmark_selection =
+ GTK_TREE_SELECTION (gtk_tree_view_get_selection (bookmark_list_widget));
+
+ name_field = caja_entry_new ();
+
+ gtk_widget_show (name_field);
+ gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_name_placeholder")),
+ name_field, TRUE, TRUE, 0);
+ caja_undo_editable_set_undo_key (GTK_EDITABLE (name_field), TRUE);
+
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (gtk_builder_get_object (builder, "bookmark_name_label")),
+ name_field);
+
+ uri_field = caja_entry_new ();
+ gtk_widget_show (uri_field);
+ gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_location_placeholder")),
+ uri_field, TRUE, TRUE, 0);
+ caja_undo_editable_set_undo_key (GTK_EDITABLE (uri_field), TRUE);
+
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (gtk_builder_get_object (builder, "bookmark_location_label")),
+ uri_field);
+
+ bookmark_list_changed_signal_id =
+ g_signal_connect (bookmarks, "contents_changed",
+ G_CALLBACK (on_bookmark_list_changed), NULL);
+ row_changed_signal_id =
+ g_signal_connect (bookmark_list_store, "row_changed",
+ G_CALLBACK (on_row_changed), NULL);
+ row_deleted_signal_id =
+ g_signal_connect (bookmark_list_store, "row_deleted",
+ G_CALLBACK (on_row_deleted), NULL);
+ row_activated_signal_id =
+ g_signal_connect (bookmark_list_widget, "row_activated",
+ G_CALLBACK (on_row_activated), undo_manager_source);
+ button_pressed_signal_id =
+ g_signal_connect (bookmark_list_widget, "button_press_event",
+ G_CALLBACK (on_button_pressed), NULL);
+ key_pressed_signal_id =
+ g_signal_connect (bookmark_list_widget, "key_press_event",
+ G_CALLBACK (on_key_pressed), NULL);
+ selection_changed_id =
+ g_signal_connect (bookmark_selection, "changed",
+ G_CALLBACK (on_selection_changed), NULL);
+
+ g_signal_connect (window, "delete_event",
+ G_CALLBACK (on_window_delete_event), NULL);
+ g_signal_connect (window, "hide",
+ G_CALLBACK (on_window_hide_event), NULL);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (on_window_destroy_event), NULL);
+ g_signal_connect (window, "response",
+ G_CALLBACK (caja_bookmarks_window_response_callback), NULL);
+
+ name_field_changed_signal_id =
+ g_signal_connect (name_field, "changed",
+ G_CALLBACK (on_name_field_changed), NULL);
+
+ g_signal_connect (name_field, "focus_out_event",
+ G_CALLBACK (on_text_field_focus_out_event), NULL);
+ g_signal_connect (name_field, "activate",
+ G_CALLBACK (name_or_uri_field_activate), NULL);
+
+ uri_field_changed_signal_id =
+ g_signal_connect (uri_field, "changed",
+ G_CALLBACK (on_uri_field_changed), NULL);
+
+ g_signal_connect (uri_field, "focus_out_event",
+ G_CALLBACK (on_text_field_focus_out_event), NULL);
+ g_signal_connect (uri_field, "activate",
+ G_CALLBACK (name_or_uri_field_activate), NULL);
+ g_signal_connect (remove_button, "clicked",
+ G_CALLBACK (on_remove_button_clicked), NULL);
+ jump_button_signal_id =
+ g_signal_connect (jump_button, "clicked",
+ G_CALLBACK (on_jump_button_clicked), undo_manager_source);
+
+ gtk_tree_selection_set_mode (bookmark_selection, GTK_SELECTION_BROWSE);
+
+ /* Fill in list widget with bookmarks, must be after signals are wired up. */
+ repopulate();
+
+ g_object_unref (builder);
+
+ return GTK_WINDOW (window);
+}
+
+void
+edit_bookmarks_dialog_set_signals (GObject *undo_manager_source)
+{
+
+ g_signal_handler_disconnect (GTK_OBJECT (jump_button),
+ jump_button_signal_id);
+ g_signal_handler_disconnect (GTK_OBJECT (bookmark_list_widget),
+ row_activated_signal_id);
+
+ jump_button_signal_id =
+ g_signal_connect (jump_button, "clicked",
+ G_CALLBACK (on_jump_button_clicked), undo_manager_source);
+ row_activated_signal_id =
+ g_signal_connect (bookmark_list_widget, "row_activated",
+ G_CALLBACK (on_row_activated), undo_manager_source);
+
+ g_object_weak_ref (G_OBJECT (undo_manager_source), edit_bookmarks_dialog_reset_signals,
+ undo_manager_source);
+}
+
+static CajaBookmark *
+get_selected_bookmark (void)
+{
+ g_return_val_if_fail(CAJA_IS_BOOKMARK_LIST(bookmarks), NULL);
+
+ if (!get_selection_exists())
+ return NULL;
+
+ if (caja_bookmark_list_length (bookmarks) < 1)
+ return NULL;
+
+ return caja_bookmark_list_item_at(bookmarks, get_selected_row ());
+}
+
+static guint
+get_selected_row (void)
+{
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkTreeModel *model;
+ gint *indices, row;
+
+ g_assert (get_selection_exists());
+
+ model = GTK_TREE_MODEL (bookmark_list_store);
+ gtk_tree_selection_get_selected (bookmark_selection,
+ &model,
+ &iter);
+
+ path = gtk_tree_model_get_path (model, &iter);
+ indices = gtk_tree_path_get_indices (path);
+ row = indices[0];
+ gtk_tree_path_free (path);
+ return row;
+}
+
+static gboolean
+get_selection_exists (void)
+{
+ return gtk_tree_selection_get_selected (bookmark_selection, NULL, NULL);
+}
+
+static void
+caja_bookmarks_window_restore_geometry (GtkWidget *window)
+{
+ const char *window_geometry;
+
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (CAJA_IS_BOOKMARK_LIST (bookmarks));
+
+ window_geometry = caja_bookmark_list_get_window_geometry (bookmarks);
+
+ if (window_geometry != NULL)
+ {
+ eel_gtk_window_set_initial_geometry_from_string
+ (GTK_WINDOW (window), window_geometry,
+ BOOKMARKS_WINDOW_MIN_WIDTH, BOOKMARKS_WINDOW_MIN_HEIGHT, FALSE);
+
+ }
+ else
+ {
+ /* use default since there was no stored geometry */
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ BOOKMARKS_WINDOW_INITIAL_WIDTH,
+ BOOKMARKS_WINDOW_INITIAL_HEIGHT);
+
+ /* Let window manager handle default position if no position stored */
+ }
+}
+
+/**
+ * caja_bookmarks_window_save_geometry:
+ *
+ * Save window size & position to disk.
+ * @window: The bookmarks window whose geometry should be saved.
+ **/
+void
+caja_bookmarks_window_save_geometry (GtkWindow *window)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (CAJA_IS_BOOKMARK_LIST (bookmarks));
+
+ /* Don't bother if window is already closed */
+ if (gtk_widget_get_visible (GTK_WIDGET (window)))
+ {
+ char *geometry_string;
+
+ geometry_string = eel_gtk_window_get_geometry_string (window);
+
+ caja_bookmark_list_set_window_geometry (bookmarks, geometry_string);
+ g_free (geometry_string);
+ }
+}
+
+static void
+on_bookmark_list_changed (CajaBookmarkList *bookmarks, gpointer data)
+{
+ g_return_if_fail (CAJA_IS_BOOKMARK_LIST (bookmarks));
+
+ /* maybe add logic here or in repopulate to save/restore selection */
+ repopulate ();
+}
+
+static void
+on_name_field_changed (GtkEditable *editable,
+ gpointer user_data)
+{
+ GtkTreeIter iter;
+ g_return_if_fail(GTK_IS_TREE_VIEW(bookmark_list_widget));
+ g_return_if_fail(GTK_IS_ENTRY(name_field));
+
+ if (!get_selection_exists())
+ return;
+
+ /* Update text displayed in list instantly. Also remember that
+ * user has changed text so we update real bookmark later.
+ */
+ gtk_tree_selection_get_selected (bookmark_selection,
+ NULL,
+ &iter);
+
+ gtk_list_store_set (bookmark_list_store,
+ &iter, BOOKMARK_LIST_COLUMN_NAME,
+ gtk_entry_get_text (GTK_ENTRY (name_field)),
+ -1);
+ text_changed = TRUE;
+ name_text_changed = TRUE;
+}
+
+static void
+open_selected_bookmark (gpointer user_data, GdkScreen *screen)
+{
+ CajaBookmark *selected;
+ CajaWindow *window;
+ GFile *location;
+
+ selected = get_selected_bookmark ();
+
+ if (!selected)
+ {
+ return;
+ }
+
+ location = caja_bookmark_get_location (selected);
+ if (location == NULL)
+ {
+ return;
+ }
+
+ if (CAJA_IS_NAVIGATION_WINDOW (user_data))
+ {
+ caja_window_go_to (CAJA_WINDOW (user_data), location);
+ }
+ else if (CAJA_IS_SPATIAL_WINDOW (user_data))
+ {
+ window = caja_application_present_spatial_window (application,
+ NULL,
+ NULL,
+ location,
+ screen);
+ }
+ else /* window that opened bookmarks window has been closed */
+ {
+ if (parent_is_browser_window || eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER))
+ {
+ window = caja_application_create_navigation_window (application,
+ NULL,
+ screen);
+ caja_window_go_to (window, location);
+ }
+ else
+ {
+ window = caja_application_present_spatial_window (application,
+ NULL,
+ NULL,
+ location,
+ screen);
+ }
+ }
+
+ g_object_unref (location);
+}
+
+static void
+on_jump_button_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ GdkScreen *screen;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (button));
+ open_selected_bookmark (user_data, screen);
+}
+
+static void
+bookmarks_delete_bookmark (void)
+{
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gint *indices, row, rows;
+
+ g_assert (GTK_IS_TREE_VIEW (bookmark_list_widget));
+
+ if (!gtk_tree_selection_get_selected (bookmark_selection, NULL, &iter))
+ return;
+
+ /* Remove the selected item from the list store. on_row_deleted() will
+ remove it from the bookmark list. */
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (bookmark_list_store),
+ &iter);
+ indices = gtk_tree_path_get_indices (path);
+ row = indices[0];
+ gtk_tree_path_free (path);
+
+ gtk_list_store_remove (bookmark_list_store, &iter);
+
+ /* Try to select the same row, or the last one in the list. */
+ rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (bookmark_list_store), NULL);
+ if (row >= rows)
+ row = rows - 1;
+
+ if (row < 0)
+ {
+ bookmarks_set_empty (TRUE);
+ }
+ else
+ {
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (bookmark_list_store),
+ &iter, NULL, row);
+ gtk_tree_selection_select_iter (bookmark_selection, &iter);
+ }
+}
+
+static void
+on_remove_button_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ bookmarks_delete_bookmark ();
+}
+
+
+/* This is a bit of a kludge to get DnD to work. We check if the row in the
+ GtkListStore matches the one in the bookmark list. If it doesn't, we assume
+ the bookmark has just been dragged here and we insert it into the bookmark
+ list. */
+static void
+on_row_changed (GtkListStore *store,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ CajaBookmark *bookmark = NULL, *bookmark_in_list;
+ gint *indices, row;
+ gboolean insert_bookmark = TRUE;
+
+ store = bookmark_list_store;
+
+ indices = gtk_tree_path_get_indices (path);
+ row = indices[0];
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ BOOKMARK_LIST_COLUMN_BOOKMARK, &bookmark,
+ -1);
+
+ /* If the bookmark in the list doesn't match the changed one, it must
+ have been dragged here, so we insert it into the list. */
+ if (row < (gint) caja_bookmark_list_length (bookmarks))
+ {
+ bookmark_in_list = caja_bookmark_list_item_at (bookmarks,
+ row);
+ if (bookmark_in_list == bookmark)
+ insert_bookmark = FALSE;
+ }
+
+ if (insert_bookmark)
+ {
+ g_signal_handler_block (bookmarks,
+ bookmark_list_changed_signal_id);
+ caja_bookmark_list_insert_item (bookmarks, bookmark, row);
+ g_signal_handler_unblock (bookmarks,
+ bookmark_list_changed_signal_id);
+
+ /* The bookmark will be copied when inserted into the list, so
+ we have to update the pointer in the list store. */
+ bookmark = caja_bookmark_list_item_at (bookmarks, row);
+ g_signal_handler_block (store, row_changed_signal_id);
+ gtk_list_store_set (store, iter,
+ BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark,
+ -1);
+ g_signal_handler_unblock (store, row_changed_signal_id);
+ }
+}
+
+/* The update_bookmark_from_text() calls in the
+ * on_button_pressed() and on_key_pressed() handlers
+ * of the tree view are a hack.
+ *
+ * The purpose is to track selection changes to the view
+ * and write the text fields back before the selection
+ * actually changed.
+ *
+ * Note that the focus-out event of the text entries is emitted
+ * after the selection changed, else this would not not be neccessary.
+ */
+
+static gboolean
+on_button_pressed (GtkTreeView *view,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ update_bookmark_from_text ();
+
+ return FALSE;
+}
+
+static gboolean
+on_key_pressed (GtkTreeView *view,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ if (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete)
+ {
+ bookmarks_delete_bookmark ();
+ return TRUE;
+ }
+
+ update_bookmark_from_text ();
+
+ return FALSE;
+}
+
+static void
+on_row_activated (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ GdkScreen *screen;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (view));
+ open_selected_bookmark (user_data, screen);
+}
+
+static void
+on_row_deleted (GtkListStore *store,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ gint *indices, row;
+
+ indices = gtk_tree_path_get_indices (path);
+ row = indices[0];
+
+ g_signal_handler_block (bookmarks, bookmark_list_changed_signal_id);
+ caja_bookmark_list_delete_item_at (bookmarks, row);
+ g_signal_handler_unblock (bookmarks, bookmark_list_changed_signal_id);
+}
+
+static void
+on_selection_changed (GtkTreeSelection *treeselection,
+ gpointer user_data)
+{
+ CajaBookmark *selected;
+ char *name = NULL, *entry_text = NULL;
+ GFile *location;
+
+ g_assert (GTK_IS_ENTRY (name_field));
+ g_assert (GTK_IS_ENTRY (uri_field));
+
+ selected = get_selected_bookmark ();
+
+ if (selected)
+ {
+ name = caja_bookmark_get_name (selected);
+ location = caja_bookmark_get_location (selected);
+ entry_text = g_file_get_parse_name (location);
+
+ g_object_unref (location);
+ }
+
+ /* Set the sensitivity of widgets that require a selection */
+ gtk_widget_set_sensitive (remove_button, selected != NULL);
+ gtk_widget_set_sensitive (jump_button, selected != NULL);
+ gtk_widget_set_sensitive (name_field, selected != NULL);
+ gtk_widget_set_sensitive (uri_field, selected != NULL);
+
+ g_signal_handler_block (name_field, name_field_changed_signal_id);
+ caja_entry_set_text (CAJA_ENTRY (name_field),
+ name ? name : "");
+ g_signal_handler_unblock (name_field, name_field_changed_signal_id);
+
+ g_signal_handler_block (uri_field, uri_field_changed_signal_id);
+ caja_entry_set_text (CAJA_ENTRY (uri_field),
+ entry_text ? entry_text : "");
+ g_signal_handler_unblock (uri_field, uri_field_changed_signal_id);
+
+ text_changed = FALSE;
+ name_text_changed = FALSE;
+
+ g_free (name);
+ g_free (entry_text);
+}
+
+
+static void
+update_bookmark_from_text (void)
+{
+ if (text_changed)
+ {
+ CajaBookmark *bookmark, *bookmark_in_list;
+ char *name;
+ GdkPixbuf *pixbuf;
+ guint selected_row;
+ GtkTreeIter iter;
+ GFile *location;
+
+ g_assert (GTK_IS_ENTRY (name_field));
+ g_assert (GTK_IS_ENTRY (uri_field));
+
+ if (gtk_entry_get_text_length (GTK_ENTRY (uri_field)) == 0)
+ {
+ return;
+ }
+
+ location = g_file_parse_name
+ (gtk_entry_get_text (GTK_ENTRY (uri_field)));
+
+ bookmark = caja_bookmark_new (location, gtk_entry_get_text (GTK_ENTRY (name_field)),
+ name_text_changed, NULL);
+
+ g_object_unref (location);
+
+ selected_row = get_selected_row ();
+
+ /* turn off list updating 'cuz otherwise the list-reordering code runs
+ * after repopulate(), thus reordering the correctly-ordered list.
+ */
+ g_signal_handler_block (bookmarks,
+ bookmark_list_changed_signal_id);
+ caja_bookmark_list_delete_item_at (bookmarks, selected_row);
+ caja_bookmark_list_insert_item (bookmarks, bookmark, selected_row);
+ g_signal_handler_unblock (bookmarks,
+ bookmark_list_changed_signal_id);
+ g_object_unref (bookmark);
+
+ /* We also have to update the bookmark pointer in the list
+ store. */
+ gtk_tree_selection_get_selected (bookmark_selection,
+ NULL, &iter);
+ g_signal_handler_block (bookmark_list_store,
+ row_changed_signal_id);
+
+ bookmark_in_list = caja_bookmark_list_item_at (bookmarks,
+ selected_row);
+
+ name = caja_bookmark_get_name (bookmark_in_list);
+
+ pixbuf = caja_bookmark_get_pixbuf (bookmark_in_list, GTK_ICON_SIZE_MENU);
+
+ gtk_list_store_set (bookmark_list_store, &iter,
+ BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark_in_list,
+ BOOKMARK_LIST_COLUMN_NAME, name,
+ BOOKMARK_LIST_COLUMN_ICON, pixbuf,
+ -1);
+ g_signal_handler_unblock (bookmark_list_store,
+ row_changed_signal_id);
+
+ g_object_unref (pixbuf);
+ g_free (name);
+ }
+}
+
+static gboolean
+on_text_field_focus_out_event (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data)
+{
+ g_assert (CAJA_IS_ENTRY (widget));
+
+ update_bookmark_from_text ();
+ return FALSE;
+}
+
+static void
+name_or_uri_field_activate (CajaEntry *entry)
+{
+ g_assert (CAJA_IS_ENTRY (entry));
+
+ update_bookmark_from_text ();
+ caja_entry_select_all_at_idle (entry);
+}
+
+static void
+on_uri_field_changed (GtkEditable *editable,
+ gpointer user_data)
+{
+ /* Remember that user has changed text so we
+ * update real bookmark later.
+ */
+ text_changed = TRUE;
+}
+
+static gboolean
+on_window_delete_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ gtk_widget_hide (widget);
+ return TRUE;
+}
+
+static gboolean
+restore_geometry (gpointer data)
+{
+ g_assert (GTK_IS_WINDOW (data));
+
+ caja_bookmarks_window_restore_geometry (GTK_WIDGET (data));
+
+ /* Don't call this again */
+ return FALSE;
+}
+
+static void
+on_window_hide_event (GtkWidget *widget,
+ gpointer user_data)
+{
+ caja_bookmarks_window_save_geometry (GTK_WINDOW (widget));
+
+ /* Disable undo for entry widgets */
+ caja_undo_unregister (G_OBJECT (name_field));
+ caja_undo_unregister (G_OBJECT (uri_field));
+
+ /* restore_geometry only works after window is hidden */
+ g_idle_add (restore_geometry, widget);
+}
+
+static void
+on_window_destroy_event (GtkWidget *widget,
+ gpointer user_data)
+{
+ g_object_unref (bookmark_list_store);
+ g_object_unref (bookmark_empty_list_store);
+ g_source_remove_by_user_data (widget);
+}
+
+static void
+repopulate (void)
+{
+ CajaBookmark *selected;
+ GtkListStore *store;
+ GtkTreePath *path;
+ GtkTreeRowReference *reference;
+ guint index;
+
+ g_assert (GTK_IS_TREE_VIEW (bookmark_list_widget));
+ g_assert (CAJA_IS_BOOKMARK_LIST (bookmarks));
+
+ store = GTK_LIST_STORE (bookmark_list_store);
+
+ selected = get_selected_bookmark ();
+
+ g_signal_handler_block (bookmark_selection,
+ selection_changed_id);
+ g_signal_handler_block (bookmark_list_store,
+ row_deleted_signal_id);
+ g_signal_handler_block (bookmark_list_widget,
+ row_activated_signal_id);
+ g_signal_handler_block (bookmark_list_widget,
+ key_pressed_signal_id);
+ g_signal_handler_block (bookmark_list_widget,
+ button_pressed_signal_id);
+
+ gtk_list_store_clear (store);
+
+ g_signal_handler_unblock (bookmark_list_widget,
+ row_activated_signal_id);
+ g_signal_handler_unblock (bookmark_list_widget,
+ key_pressed_signal_id);
+ g_signal_handler_unblock (bookmark_list_widget,
+ button_pressed_signal_id);
+ g_signal_handler_unblock (bookmark_list_store,
+ row_deleted_signal_id);
+ g_signal_handler_unblock (bookmark_selection,
+ selection_changed_id);
+
+ /* Fill the list in with the bookmark names. */
+ g_signal_handler_block (store, row_changed_signal_id);
+
+ reference = NULL;
+
+ for (index = 0; index < caja_bookmark_list_length (bookmarks); ++index)
+ {
+ CajaBookmark *bookmark;
+ char *bookmark_name;
+ GdkPixbuf *bookmark_pixbuf;
+ GtkTreeIter iter;
+
+ bookmark = caja_bookmark_list_item_at (bookmarks, index);
+ bookmark_name = caja_bookmark_get_name (bookmark);
+ bookmark_pixbuf = caja_bookmark_get_pixbuf (bookmark, GTK_ICON_SIZE_MENU);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ BOOKMARK_LIST_COLUMN_ICON, bookmark_pixbuf,
+ BOOKMARK_LIST_COLUMN_NAME, bookmark_name,
+ BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark,
+ BOOKMARK_LIST_COLUMN_STYLE, PANGO_STYLE_NORMAL,
+ -1);
+
+ if (bookmark == selected)
+ {
+ /* save old selection */
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+ reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path);
+ gtk_tree_path_free (path);
+ }
+
+ g_free (bookmark_name);
+ g_object_unref (bookmark_pixbuf);
+
+ }
+ g_signal_handler_unblock (store, row_changed_signal_id);
+
+ if (reference != NULL)
+ {
+ /* restore old selection */
+
+ /* bookmarks_set_empty() will call the selection change handler,
+ * so we block it here in case of selection change.
+ */
+ g_signal_handler_block (bookmark_selection, selection_changed_id);
+
+ g_assert (index != 0);
+ g_assert (gtk_tree_row_reference_valid (reference));
+
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_selection_select_path (bookmark_selection, path);
+ gtk_tree_row_reference_free (reference);
+ gtk_tree_path_free (path);
+
+ g_signal_handler_unblock (bookmark_selection, selection_changed_id);
+ }
+
+ bookmarks_set_empty (index == 0);
+}
+
+static int
+handle_close_accelerator (GtkWindow *window,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ g_assert (GTK_IS_WINDOW (window));
+ g_assert (event != NULL);
+ g_assert (user_data == NULL);
+
+ if (eel_gtk_window_event_is_close_accelerator (window, event))
+ {
+ gtk_widget_hide (GTK_WIDGET (window));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+set_up_close_accelerator (GtkWidget *window)
+{
+ /* Note that we don't call eel_gtk_window_set_up_close_accelerator
+ * here because we have to handle saving geometry before hiding the
+ * window.
+ */
+ g_signal_connect (window, "key_press_event",
+ G_CALLBACK (handle_close_accelerator), NULL);
+}