nautilus-3.6.3/src/nautilus-bookmarks-window.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 
  3 /*
  4  * Nautilus
  5  *
  6  * Copyright (C) 1999, 2000 Eazel, Inc.
  7  *
  8  * Nautilus is free software; you can redistribute it and/or
  9  * modify it under the terms of the GNU General Public License as
 10  * published by the Free Software Foundation; either version 2 of the
 11  * License, or (at your option) any later version.
 12  *
 13  * Nautilus is distributed in the hope that it will be useful,
 14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16  * General Public License for more details.
 17  *
 18  * You should have received a copy of the GNU General Public License
 19  * along with this program; if not, write to the Free Software
 20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 21  *
 22  * Authors: John Sullivan <sullivan@eazel.com>
 23  */
 24 
 25 /* nautilus-bookmarks-window.c - implementation of bookmark-editing window.
 26  */
 27 
 28 #include <config.h>
 29 
 30 #include "nautilus-application.h"
 31 #include "nautilus-bookmarks-window.h"
 32 #include "nautilus-window.h"
 33 
 34 #include <libnautilus-private/nautilus-entry.h>
 35 #include <libnautilus-private/nautilus-global-preferences.h>
 36 
 37 #include <gtk/gtk.h>
 38 #include <glib/gi18n.h>
 39 #include <gdk/gdkkeysyms.h>
 40 
 41 /* We store a pointer to the bookmark in a column so when an item is moved
 42    with DnD we know which item it is. However we have to be careful to keep
 43    this in sync with the actual bookmark. Note that
 44    nautilus_bookmark_list_insert_item() makes a copy of the bookmark, so we
 45    have to fetch the new copy and update our pointer. */
 46 
 47 enum {
 48 	BOOKMARK_LIST_COLUMN_ICON,
 49 	BOOKMARK_LIST_COLUMN_NAME,
 50 	BOOKMARK_LIST_COLUMN_BOOKMARK,
 51 	BOOKMARK_LIST_COLUMN_STYLE,
 52 	BOOKMARK_LIST_NUM_COLUMNS
 53 };
 54 
 55 /* Larger size initially; user can stretch or shrink (but not shrink below min) */
 56 #define BOOKMARKS_WINDOW_INITIAL_WIDTH	500
 57 #define BOOKMARKS_WINDOW_INITIAL_HEIGHT	400
 58 
 59 G_DEFINE_TYPE (NautilusBookmarksWindow, nautilus_bookmarks_window, GTK_TYPE_WINDOW)
 60 
 61 enum {
 62 	PROP_PARENT_WINDOW = 1,
 63 	NUM_PROPERTIES
 64 };
 65 
 66 static GParamSpec* properties[NUM_PROPERTIES] = { NULL, };
 67 
 68 struct NautilusBookmarksWindowPrivate {
 69 	NautilusWindow *parent_window;
 70 
 71 	NautilusBookmarkList *bookmarks;
 72 	gulong bookmarks_changed_id;
 73 
 74 	GtkTreeView *tree_view;
 75 	GtkTreeSelection *selection;
 76 	gulong row_activated_id;
 77 	gulong button_press_id;
 78 	gulong key_press_id;
 79 	gulong selection_changed_id;
 80 
 81 	GtkListStore *model;
 82 	gulong row_changed_id;
 83 	gulong row_deleted_id;
 84 
 85 	GtkWidget *name_field;
 86 	GtkWidget *uri_field;
 87 	gulong name_changed_id;
 88 	gulong uri_changed_id;
 89 	gboolean text_changed;
 90 	gboolean name_text_changed;
 91 
 92 	GtkWidget *remove_button;
 93 	GtkWidget *up_button;
 94 	GtkWidget *down_button;
 95 };
 96 
 97 static gboolean
 98 get_selection_exists (NautilusBookmarksWindow *self)
 99 {
100 	return gtk_tree_selection_get_selected (self->priv->selection, NULL, NULL);
101 }
102 
103 static guint
104 get_selected_row (NautilusBookmarksWindow *self)
105 {
106 	GtkTreeIter       iter;
107 	GtkTreePath      *path;
108 	GtkTreeModel     *model;
109 	gint		 *indices, row;
110 	
111 	if (!gtk_tree_selection_get_selected (self->priv->selection, &model, &iter)) {
112 		g_assert_not_reached ();
113 	}
114 	
115 	path = gtk_tree_model_get_path (model, &iter);
116 	indices = gtk_tree_path_get_indices (path);
117 	row = indices[0];
118 	gtk_tree_path_free (path);
119 	return row;
120 }
121 
122 static NautilusBookmark *
123 get_selected_bookmark (NautilusBookmarksWindow *self)
124 {
125 	if (!get_selection_exists (self)) {
126 		return NULL;
127 	}
128 
129 	if (nautilus_bookmark_list_length (self->priv->bookmarks) < 1) {
130 		return NULL;
131 	}
132 
133 	return nautilus_bookmark_list_item_at (self->priv->bookmarks,
134 					       get_selected_row (self));
135 }
136 
137 static int
138 nautilus_bookmarks_window_key_press_event_cb (GtkWindow *window, 
139 					      GdkEventKey *event, 
140 					      gpointer user_data)
141 {
142 	if (event->state & GDK_CONTROL_MASK && event->keyval == GDK_KEY_w) {
143 		gtk_widget_destroy (GTK_WIDGET (window));
144 		return TRUE;
145 	}
146 
147 	return FALSE;
148 }
149 
150 static GtkListStore *
151 create_bookmark_store (void)
152 {
153 	return gtk_list_store_new (BOOKMARK_LIST_NUM_COLUMNS,
154 				   G_TYPE_ICON,
155 				   G_TYPE_STRING,
156 				   G_TYPE_OBJECT,
157 				   PANGO_TYPE_STYLE);
158 }
159 
160 static void
161 setup_empty_list (NautilusBookmarksWindow *self)
162 {
163 	GtkListStore *empty_model;
164 	GtkTreeIter iter;
165 
166 	empty_model = create_bookmark_store ();
167 	gtk_list_store_append (empty_model, &iter);
168 
169 	gtk_list_store_set (empty_model, &iter,
170 			    BOOKMARK_LIST_COLUMN_NAME, _("No bookmarks defined"),
171 			    BOOKMARK_LIST_COLUMN_STYLE, PANGO_STYLE_ITALIC,
172 			    -1);
173 	gtk_tree_view_set_model (self->priv->tree_view,
174 				 GTK_TREE_MODEL (empty_model));
175 
176 	g_object_unref (empty_model);
177 }
178 
179 static void
180 update_widgets_sensitivity (NautilusBookmarksWindow *self)
181 {
182 	NautilusBookmark *selected;
183 	int n_active;
184 	int index = -1;
185 
186 	selected = get_selected_bookmark (self);
187 	n_active = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->priv->model), NULL);
188 	if (selected != NULL) {
189 		index = get_selected_row (self);
190 	}
191 
192 	/* Set the sensitivity of widgets that require a selection */
193 	gtk_widget_set_sensitive (self->priv->remove_button, index >= 0 && n_active > 1);
194 	gtk_widget_set_sensitive (self->priv->up_button, index > 0);
195 	gtk_widget_set_sensitive (self->priv->down_button, index >= 0 && index < n_active - 1);
196 	gtk_widget_set_sensitive (self->priv->name_field, selected != NULL);
197 	gtk_widget_set_sensitive (self->priv->uri_field, selected != NULL);
198 }
199 
200 static void
201 on_selection_changed (GtkTreeSelection *treeselection,
202 		      gpointer user_data)
203 {
204 	NautilusBookmarksWindow *self = user_data;
205 	NautilusBookmark *selected;
206 	const char *name = NULL;
207 	char *entry_text = NULL;
208 	GFile *location;
209 
210 	selected = get_selected_bookmark (self);
211 
212 	if (selected) {
213 		name = nautilus_bookmark_get_name (selected);
214 		location = nautilus_bookmark_get_location (selected);
215 		entry_text = g_file_get_parse_name (location);
216 
217 		g_object_unref (location);
218 	}
219 
220 	update_widgets_sensitivity (self);
221 
222 	g_signal_handler_block (self->priv->name_field, self->priv->name_changed_id);
223 	nautilus_entry_set_text (NAUTILUS_ENTRY (self->priv->name_field),
224 				 name ? name : "");
225 	g_signal_handler_unblock (self->priv->name_field, self->priv->name_changed_id);
226 
227 	g_signal_handler_block (self->priv->uri_field, self->priv->uri_changed_id);
228 	nautilus_entry_set_text (NAUTILUS_ENTRY (self->priv->uri_field),
229 				 entry_text ? entry_text : "");
230 	g_signal_handler_unblock (self->priv->uri_field, self->priv->uri_changed_id);
231 
232 	self->priv->text_changed = FALSE;
233 	self->priv->name_text_changed = FALSE;
234 
235 	g_free (entry_text);
236 }
237 
238 static void
239 bookmarks_set_empty (NautilusBookmarksWindow *self,
240 		     gboolean empty)
241 {
242 	GtkTreeIter iter;
243 
244 	if (empty) {
245 		setup_empty_list (self);
246 		gtk_widget_set_sensitive (GTK_WIDGET (self->priv->tree_view), FALSE);
247 	} else {
248 		gtk_tree_view_set_model (self->priv->tree_view, GTK_TREE_MODEL (self->priv->model));
249 		gtk_widget_set_sensitive (GTK_WIDGET (self->priv->tree_view), TRUE);
250 
251 		if (nautilus_bookmark_list_length (self->priv->bookmarks) > 0 &&
252 		    !get_selection_exists (self)) {
253 			gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
254 						       &iter, NULL, 0);
255 			gtk_tree_selection_select_iter (self->priv->selection, &iter);
256 		}
257 	}
258 
259 	on_selection_changed (self->priv->selection, self);
260 }
261 
262 static void
263 repopulate (NautilusBookmarksWindow *self)
264 {
265 	NautilusBookmark *selected;
266 	GtkListStore *store;
267 	GtkTreePath *path;
268 	GtkTreeRowReference *reference;
269 	guint index;
270 
271 	selected = get_selected_bookmark (self);
272 
273 	g_signal_handler_block (self->priv->selection, self->priv->selection_changed_id);
274 	g_signal_handler_block (self->priv->model, self->priv->row_deleted_id);
275         g_signal_handler_block (self->priv->tree_view, self->priv->row_activated_id);
276         g_signal_handler_block (self->priv->tree_view, self->priv->key_press_id);
277         g_signal_handler_block (self->priv->tree_view, self->priv->button_press_id);
278 
279 	gtk_list_store_clear (self->priv->model);
280 
281 	g_signal_handler_unblock (self->priv->selection, self->priv->selection_changed_id);
282 	g_signal_handler_unblock (self->priv->model, self->priv->row_deleted_id);
283         g_signal_handler_unblock (self->priv->tree_view, self->priv->row_activated_id);
284         g_signal_handler_unblock (self->priv->tree_view, self->priv->key_press_id);
285         g_signal_handler_unblock (self->priv->tree_view, self->priv->button_press_id);
286 
287 	/* Fill the list in with the bookmark names. */
288 	g_signal_handler_block (self->priv->model, self->priv->row_changed_id);
289 
290 	reference = NULL;
291 	store = self->priv->model;
292 
293 	for (index = 0; index < nautilus_bookmark_list_length (self->priv->bookmarks); ++index) {
294 		NautilusBookmark *bookmark;
295 		const char       *bookmark_name;
296 		GIcon            *bookmark_icon;
297 		GtkTreeIter       iter;
298 
299 		bookmark = nautilus_bookmark_list_item_at (self->priv->bookmarks, index);
300 		bookmark_name = nautilus_bookmark_get_name (bookmark);
301 		bookmark_icon = nautilus_bookmark_get_symbolic_icon (bookmark);
302 
303 		gtk_list_store_append (store, &iter);
304 		gtk_list_store_set (store, &iter, 
305 				    BOOKMARK_LIST_COLUMN_ICON, bookmark_icon,
306 				    BOOKMARK_LIST_COLUMN_NAME, bookmark_name,
307 				    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark,
308 				    BOOKMARK_LIST_COLUMN_STYLE, PANGO_STYLE_NORMAL,
309 				    -1);
310 
311 		if (bookmark == selected) {
312 			/* save old selection */
313 			GtkTreePath *path;
314 
315 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
316 			reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path);
317 			gtk_tree_path_free (path);
318 		}
319 
320 		g_object_unref (bookmark_icon);
321 	}
322 
323 	g_signal_handler_unblock (self->priv->model, self->priv->row_changed_id);
324 
325 	if (reference != NULL) {
326 		/* restore old selection */
327 
328 		/* bookmarks_set_empty() will call the selection change handler,
329  		 * so we block it here in case of selection change.
330  		 */
331 		g_signal_handler_block (self->priv->selection, self->priv->selection_changed_id);
332 
333 		g_assert (index != 0);
334 		g_assert (gtk_tree_row_reference_valid (reference));
335 
336 		path = gtk_tree_row_reference_get_path (reference);
337 		gtk_tree_selection_select_path (self->priv->selection, path);
338 		gtk_tree_row_reference_free (reference);
339 		gtk_tree_path_free (path);
340 
341 		g_signal_handler_unblock (self->priv->selection, self->priv->selection_changed_id);
342 	}
343 
344 	bookmarks_set_empty (self, (index == 0));
345 }
346 
347 static void
348 on_bookmark_list_changed (NautilusBookmarkList *bookmarks,
349 			  gpointer user_data)
350 {
351 	NautilusBookmarksWindow *self = user_data;
352 
353 	/* maybe add logic here or in repopulate to save/restore selection */
354 	repopulate (self);
355 }
356 
357 static void
358 on_name_field_changed (GtkEditable *editable,
359 		       gpointer     user_data)
360 {
361 	NautilusBookmarksWindow *self = user_data;
362 	GtkTreeIter   iter;
363 
364 	if (!get_selection_exists(self)) {
365 		return;
366 	}
367 
368 	/* Update text displayed in list instantly. Also remember that 
369 	 * user has changed text so we update real bookmark later. 
370 	 */
371 	gtk_tree_selection_get_selected (self->priv->selection, NULL, &iter);
372 	gtk_list_store_set (self->priv->model,
373 			    &iter, BOOKMARK_LIST_COLUMN_NAME, 
374 			    gtk_entry_get_text (GTK_ENTRY (self->priv->name_field)),
375 			    -1);
376 
377 	self->priv->text_changed = TRUE;
378 	self->priv->name_text_changed = TRUE;
379 }
380 
381 static void
382 bookmarks_delete_bookmark (NautilusBookmarksWindow *self)
383 {
384 	GtkTreeIter iter;
385 	GtkTreePath *path;
386 	gint *indices, row, rows;
387 	
388 	if (!gtk_tree_selection_get_selected (self->priv->selection, NULL, &iter)) {
389 		return;
390 	}
391 
392 	/* Remove the selected item from the list store. on_row_deleted() will
393 	   remove it from the bookmark list. */
394 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->priv->model), &iter);
395 	indices = gtk_tree_path_get_indices (path);
396 	row = indices[0];
397 	gtk_tree_path_free (path);
398 
399 	gtk_list_store_remove (self->priv->model, &iter);
400 
401 	/* Try to select the same row, or the last one in the list. */
402 	rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->priv->model), NULL);
403 	if (row >= rows) {
404 		row = rows - 1;
405 	}
406 
407 	if (row < 0) {
408 		bookmarks_set_empty (self, TRUE);
409 	} else {
410 		gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
411 					       &iter, NULL, row);
412 		gtk_tree_selection_select_iter (self->priv->selection, &iter);
413 	}
414 }
415 
416 static void
417 on_remove_button_clicked (GtkButton *button,
418                           gpointer   user_data)
419 {
420 	NautilusBookmarksWindow *self = user_data;
421         bookmarks_delete_bookmark (self);
422 }
423 
424 static void
425 on_up_button_clicked (GtkButton *button,
426 		      gpointer   user_data)
427 {
428 	NautilusBookmarksWindow *self = user_data;
429 	guint row;
430 	GtkTreeIter iter;
431 
432 	row = get_selected_row (self);
433 	nautilus_bookmark_list_move_item (self->priv->bookmarks, row, row - 1);
434 	gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
435 				       &iter, NULL, row - 1);
436 	gtk_tree_selection_select_iter (self->priv->selection, &iter);
437 }
438 
439 static void
440 on_down_button_clicked (GtkButton *button,
441 			gpointer   user_data)
442 {
443 	NautilusBookmarksWindow *self = user_data;
444 	guint row;
445 	GtkTreeIter iter;
446 
447 	row = get_selected_row (self);
448 	nautilus_bookmark_list_move_item (self->priv->bookmarks, row, row + 1);
449 	gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
450 				       &iter, NULL, row + 1);
451 	gtk_tree_selection_select_iter (self->priv->selection, &iter);
452 }
453 
454 /* This is a bit of a kludge to get DnD to work. We check if the row in the
455    GtkListStore matches the one in the bookmark list. If it doesn't, we assume
456    the bookmark has just been dragged here and we insert it into the bookmark
457    list. */
458 static void
459 on_row_changed (GtkListStore *store,
460 		GtkTreePath *path,
461 		GtkTreeIter *iter,
462 		gpointer user_data)
463 {
464 	NautilusBookmarksWindow *self = user_data;
465 	NautilusBookmark *bookmark = NULL, *bookmark_in_list;
466 	gint *indices, row;
467 	gboolean insert_bookmark = TRUE;
468 
469 	indices = gtk_tree_path_get_indices (path);
470 	row = indices[0];
471 	gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
472 			    BOOKMARK_LIST_COLUMN_BOOKMARK, &bookmark,
473 			    -1);
474 
475 	/* If the bookmark in the list doesn't match the changed one, it must
476 	   have been dragged here, so we insert it into the list. */
477 	if (row < (gint) nautilus_bookmark_list_length (self->priv->bookmarks)) {
478 		bookmark_in_list = nautilus_bookmark_list_item_at (self->priv->bookmarks, row);
479 		if (bookmark_in_list == bookmark)
480 			insert_bookmark = FALSE;
481 	}
482 
483 	if (insert_bookmark) {
484 		g_signal_handler_block (self->priv->bookmarks,
485 					self->priv->bookmarks_changed_id);
486 		nautilus_bookmark_list_insert_item (self->priv->bookmarks,
487 						    bookmark, row);
488 		g_signal_handler_unblock (self->priv->bookmarks,
489 					  self->priv->bookmarks_changed_id);
490 
491 		/* The bookmark will be copied when inserted into the list, so
492 		   we have to update the pointer in the list store. */
493 		bookmark = nautilus_bookmark_list_item_at (self->priv->bookmarks, row);
494 		g_signal_handler_block (store, self->priv->row_changed_id);
495 		gtk_list_store_set (store, iter,
496 				    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark,
497 				    -1);
498 		g_signal_handler_unblock (store, self->priv->row_changed_id);
499 	}
500 }
501 
502 static void
503 update_bookmark_from_text (NautilusBookmarksWindow *self)
504 {
505 	NautilusBookmark *bookmark, *bookmark_in_list;
506 	const char *name;
507 	GIcon *icon;
508 	guint selected_row;
509 	GtkTreeIter iter;
510 	GFile *location;
511 
512 	if (!self->priv->text_changed ||
513 	    gtk_entry_get_text_length (GTK_ENTRY (self->priv->uri_field)) == 0) {
514 		return;
515 	}
516 
517 	location = g_file_parse_name 
518 		(gtk_entry_get_text (GTK_ENTRY (self->priv->uri_field)));
519 
520 	bookmark = nautilus_bookmark_new (location,
521 					  self->priv->name_text_changed ?
522 					  gtk_entry_get_text (GTK_ENTRY (self->priv->name_field)) : NULL);
523 	g_object_unref (location);
524 
525 	selected_row = get_selected_row (self);
526 
527 	/* turn off list updating 'cuz otherwise the list-reordering code runs
528 	 * after repopulate(), thus reordering the correctly-ordered list.
529 	 */
530 	g_signal_handler_block (self->priv->bookmarks, self->priv->bookmarks_changed_id);
531 	nautilus_bookmark_list_delete_item_at (self->priv->bookmarks, selected_row);
532 	nautilus_bookmark_list_insert_item (self->priv->bookmarks, bookmark, selected_row);
533 	g_signal_handler_unblock (self->priv->bookmarks, self->priv->bookmarks_changed_id);
534 	g_object_unref (bookmark);
535 
536 	/* We also have to update the bookmark pointer in the list
537 	   store. */
538 	gtk_tree_selection_get_selected (self->priv->selection, NULL, &iter);
539 	g_signal_handler_block (self->priv->model, self->priv->row_changed_id);
540 
541 	bookmark_in_list = nautilus_bookmark_list_item_at (self->priv->bookmarks, selected_row);
542 	name = nautilus_bookmark_get_name (bookmark_in_list);
543 	icon = nautilus_bookmark_get_symbolic_icon (bookmark_in_list);
544 
545 	gtk_list_store_set (self->priv->model, &iter,
546 			    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark_in_list,
547 			    BOOKMARK_LIST_COLUMN_NAME, name,
548 			    BOOKMARK_LIST_COLUMN_ICON, icon,
549 			    -1);
550 
551 	g_signal_handler_unblock (self->priv->model, self->priv->row_changed_id);
552 	g_object_unref (icon);
553 }
554 
555 /* The update_bookmark_from_text() calls in the
556  * on_button_pressed() and on_key_pressed() handlers
557  * of the tree view are a hack.
558  *
559  * The purpose is to track selection changes to the view
560  * and write the text fields back before the selection
561  * actually changed.
562  *
563  * Note that the focus-out event of the text entries is emitted
564  * after the selection changed, else this would not not be neccessary.
565  */
566 
567 static gboolean
568 on_button_pressed (GtkTreeView *view,
569 		   GdkEventButton *event,
570 		   gpointer user_data)
571 {
572 	NautilusBookmarksWindow *self = user_data;
573 	update_bookmark_from_text (self);
574 
575 	return FALSE;
576 }
577 
578 static gboolean
579 on_key_pressed (GtkTreeView *view,
580                 GdkEventKey *event,
581                 gpointer user_data)
582 {
583 	NautilusBookmarksWindow *self = user_data;
584 
585         if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) {
586                 bookmarks_delete_bookmark (self);
587                 return TRUE;
588         }
589 
590 	update_bookmark_from_text (self);
591 
592         return FALSE;
593 }
594 
595 static void
596 on_row_activated (GtkTreeView       *view,
597                   GtkTreePath       *path,
598                   GtkTreeViewColumn *column,
599                   gpointer           user_data)
600 {
601 	NautilusBookmarksWindow *self = user_data;
602 	NautilusBookmark *selected;
603 	GFile *location;
604 
605 	selected = get_selected_bookmark (self);
606 
607 	if (!selected) {
608 		return;
609 	}
610 
611 	location = nautilus_bookmark_get_location (selected);
612 	if (location == NULL) {
613 		return;
614 	}
615 
616 	nautilus_window_go_to (self->priv->parent_window, location);
617 	g_object_unref (location);
618 }
619 
620 static void
621 on_row_deleted (GtkListStore *store,
622 		GtkTreePath *path,
623 		gpointer user_data)
624 {
625 	NautilusBookmarksWindow *self = user_data;
626 	gint *indices, row;
627 
628 	indices = gtk_tree_path_get_indices (path);
629 	row = indices[0];
630 
631 	g_signal_handler_block (self->priv->bookmarks, self->priv->bookmarks_changed_id);
632 	nautilus_bookmark_list_delete_item_at (self->priv->bookmarks, row);
633 	g_signal_handler_unblock (self->priv->bookmarks, self->priv->bookmarks_changed_id);
634 }
635 
636 static gboolean
637 on_text_field_focus_out_event (GtkWidget *widget,
638 			       GdkEventFocus *event,
639 			       gpointer user_data)
640 {
641 	NautilusBookmarksWindow *self = user_data;
642 	update_bookmark_from_text (self);
643 
644 	return FALSE;
645 }
646 
647 static void
648 name_or_uri_field_activate (NautilusEntry *entry,
649 			    gpointer user_data)
650 {
651 	NautilusBookmarksWindow *self = user_data;
652 
653 	update_bookmark_from_text (self);
654 	nautilus_entry_select_all_at_idle (entry);
655 }
656 
657 static void
658 on_uri_field_changed (GtkEditable *editable,
659 		      gpointer user_data)
660 {
661 	NautilusBookmarksWindow *self = user_data;
662 
663 	/* Remember that user has changed text so we 
664 	 * update real bookmark later. 
665 	 */
666 	self->priv->text_changed = TRUE;
667 }
668 
669 static void
670 nautilus_bookmarks_window_dispose (GObject *object)
671 {
672 	NautilusBookmarksWindow *self = NAUTILUS_BOOKMARKS_WINDOW (object);
673 
674 	if (self->priv->bookmarks_changed_id != 0) {
675 		g_signal_handler_disconnect (self->priv->bookmarks,
676 					     self->priv->bookmarks_changed_id);
677 		self->priv->bookmarks_changed_id = 0;
678 	}
679 
680 	g_clear_object (&self->priv->model);
681 
682 	G_OBJECT_CLASS (nautilus_bookmarks_window_parent_class)->dispose (object);
683 }
684 
685 static void
686 nautilus_bookmarks_window_constructed (GObject *object)
687 {
688 	NautilusBookmarksWindow *self = NAUTILUS_BOOKMARKS_WINDOW (object);
689 	GtkBuilder *builder;
690 	GError *error = NULL;
691 	GtkWindow *window;
692 	GtkWidget *content;
693 	GtkTreeViewColumn *col;
694 	GtkCellRenderer *rend;
695 
696 	G_OBJECT_CLASS (nautilus_bookmarks_window_parent_class)->constructed (object);
697 
698 	builder = gtk_builder_new ();
699 	if (!gtk_builder_add_from_resource (builder,
700 					    "/org/gnome/nautilus/nautilus-bookmarks-window.ui",
701 					    &error)) {
702 		g_object_unref (builder);
703 
704 		g_critical ("Can't load UI description for the bookmarks editor: %s", error->message);
705 		g_error_free (error);
706 		return;
707 	}
708 
709 	window = GTK_WINDOW (object);
710 	gtk_window_set_title (window, _("Bookmarks"));
711 	gtk_window_set_default_size (window, 
712 				     BOOKMARKS_WINDOW_INITIAL_WIDTH, 
713 				     BOOKMARKS_WINDOW_INITIAL_HEIGHT);
714 	gtk_window_set_application (window,
715 				    gtk_window_get_application (GTK_WINDOW (self->priv->parent_window)));
716 
717 	gtk_window_set_destroy_with_parent (window, TRUE);
718 	gtk_window_set_transient_for (window, GTK_WINDOW (self->priv->parent_window));
719 	gtk_window_set_position (window, GTK_WIN_POS_CENTER_ON_PARENT);
720 	gtk_container_set_border_width (GTK_CONTAINER (window), 6);
721 
722 	g_signal_connect (window, "key-press-event",
723 			  G_CALLBACK (nautilus_bookmarks_window_key_press_event_cb), NULL);
724 
725 	content = GTK_WIDGET (gtk_builder_get_object (builder, "bookmarks_window_content"));
726 	gtk_container_add (GTK_CONTAINER (window), content);
727 
728 	/* tree view */
729 	self->priv->tree_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "bookmark_tree_view"));
730 	self->priv->selection = gtk_tree_view_get_selection (self->priv->tree_view);
731 	gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_BROWSE);
732 
733 	rend = gtk_cell_renderer_pixbuf_new ();
734 	g_object_set (rend, "follow-state", TRUE, NULL);
735 	col = gtk_tree_view_column_new_with_attributes ("Icon", 
736 							rend,
737 							"gicon", 
738 							BOOKMARK_LIST_COLUMN_ICON,
739 							NULL);
740 	gtk_tree_view_append_column (self->priv->tree_view,
741 				     GTK_TREE_VIEW_COLUMN (col));
742 	gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (col),
743 					      NAUTILUS_ICON_SIZE_SMALLER);
744 
745 	rend = gtk_cell_renderer_text_new ();
746 	g_object_set (rend,
747 		      "ellipsize", PANGO_ELLIPSIZE_END,
748 		      "ellipsize-set", TRUE,
749 		      NULL);
750 
751 	col = gtk_tree_view_column_new_with_attributes ("Icon", 
752 							rend,
753 							"text", 
754 							BOOKMARK_LIST_COLUMN_NAME,
755 							"style",
756 							BOOKMARK_LIST_COLUMN_STYLE,
757 							NULL);
758 	gtk_tree_view_append_column (self->priv->tree_view,
759 				     GTK_TREE_VIEW_COLUMN (col));
760 
761 	self->priv->model = create_bookmark_store ();
762 	setup_empty_list (self);
763 
764 	/* name entry */
765 	self->priv->name_field = nautilus_entry_new ();
766 	gtk_widget_show (self->priv->name_field);
767 	gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_name_placeholder")),
768 			    self->priv->name_field, TRUE, TRUE, 0);
769 	
770 	gtk_label_set_mnemonic_widget (
771 		GTK_LABEL (gtk_builder_get_object (builder, "bookmark_name_label")),
772 		self->priv->name_field);
773 
774 	/* URI entry */
775 	self->priv->uri_field = nautilus_entry_new ();
776 	gtk_widget_show (self->priv->uri_field);
777 	gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_location_placeholder")),
778 			    self->priv->uri_field, TRUE, TRUE, 0);
779 
780 	gtk_label_set_mnemonic_widget (
781 		GTK_LABEL (gtk_builder_get_object (builder, "bookmark_location_label")),
782 		self->priv->uri_field);
783 
784 	/* buttons */
785 	self->priv->remove_button = GTK_WIDGET (gtk_builder_get_object (builder, "bookmark_remove_button"));
786 	self->priv->up_button = GTK_WIDGET (gtk_builder_get_object (builder, "bookmark_up_button"));
787 	self->priv->down_button = GTK_WIDGET (gtk_builder_get_object (builder, "bookmark_down_button"));
788 
789 	g_object_unref (builder);
790 
791 	/* setup bookmarks list and signals */
792 	self->priv->bookmarks = nautilus_application_get_bookmarks
793 		(NAUTILUS_APPLICATION (g_application_get_default ()));
794 	self->priv->bookmarks_changed_id =
795 		g_signal_connect (self->priv->bookmarks, "changed",
796 				  G_CALLBACK (on_bookmark_list_changed), self);
797 
798 	self->priv->row_changed_id =
799 		g_signal_connect (self->priv->model, "row-changed",
800 				  G_CALLBACK (on_row_changed), self);
801 	self->priv->row_deleted_id =
802 		g_signal_connect (self->priv->model, "row-deleted",
803 				  G_CALLBACK (on_row_deleted), self);
804 
805         self->priv->row_activated_id =
806                 g_signal_connect (self->priv->tree_view, "row-activated",
807                                   G_CALLBACK (on_row_activated), self);
808         self->priv->button_press_id =
809                 g_signal_connect (self->priv->tree_view, "button-press-event",
810                                   G_CALLBACK (on_button_pressed), self);
811         self->priv->key_press_id =
812                 g_signal_connect (self->priv->tree_view, "key-press-event",
813                                   G_CALLBACK (on_key_pressed), self);
814 	self->priv->selection_changed_id =
815 		g_signal_connect (self->priv->selection, "changed",
816 				  G_CALLBACK (on_selection_changed), self);
817 
818 	self->priv->name_changed_id =
819 		g_signal_connect (self->priv->name_field, "changed",
820 				  G_CALLBACK (on_name_field_changed), self);
821 	g_signal_connect (self->priv->name_field, "focus_out_event",
822 			  G_CALLBACK (on_text_field_focus_out_event), self);
823 	g_signal_connect (self->priv->name_field, "activate",
824 			  G_CALLBACK (name_or_uri_field_activate), self);
825 
826 	self->priv->uri_changed_id =
827 		g_signal_connect (self->priv->uri_field, "changed",
828 				  G_CALLBACK (on_uri_field_changed), self);
829 	g_signal_connect (self->priv->uri_field, "focus_out_event",
830 			  G_CALLBACK (on_text_field_focus_out_event), self);
831 	g_signal_connect (self->priv->uri_field, "activate",
832 			  G_CALLBACK (name_or_uri_field_activate), self);
833 
834 	g_signal_connect (self->priv->remove_button, "clicked",
835 			  G_CALLBACK (on_remove_button_clicked), self);
836 	g_signal_connect (self->priv->up_button, "clicked",
837 			  G_CALLBACK (on_up_button_clicked), self);
838 	g_signal_connect (self->priv->down_button, "clicked",
839 			  G_CALLBACK (on_down_button_clicked), self);
840 	
841 	/* Fill in list widget with bookmarks, must be after signals are wired up. */
842 	repopulate (self);
843 }
844 
845 static void
846 nautilus_bookmarks_window_set_property (GObject *object,
847 					guint arg_id,
848 					const GValue *value,
849 					GParamSpec *pspec)
850 {
851 	NautilusBookmarksWindow *self = NAUTILUS_BOOKMARKS_WINDOW (object);
852 
853 	switch (arg_id) {
854 	case PROP_PARENT_WINDOW:
855 		self->priv->parent_window = g_value_get_object (value);
856 		break;
857 	default:
858 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, pspec);
859 		break;
860 	}
861 }
862 
863 static void
864 nautilus_bookmarks_window_init (NautilusBookmarksWindow *self)
865 {
866 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_BOOKMARKS_WINDOW,
867 						  NautilusBookmarksWindowPrivate);
868 }
869 
870 static void
871 nautilus_bookmarks_window_class_init (NautilusBookmarksWindowClass *klass)
872 {
873 	GObjectClass *oclass = G_OBJECT_CLASS (klass);
874 
875 	oclass->set_property = nautilus_bookmarks_window_set_property;
876 	oclass->dispose = nautilus_bookmarks_window_dispose;
877 	oclass->constructed = nautilus_bookmarks_window_constructed;
878 
879 	properties[PROP_PARENT_WINDOW] =
880 		g_param_spec_object ("parent-window",
881 				     "The NautilusWindow",
882 				     "The parent NautilusWindow",
883 				     NAUTILUS_TYPE_WINDOW,
884 				     G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
885 
886 	g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
887 	g_type_class_add_private (klass, sizeof (NautilusBookmarksWindowPrivate));
888 }
889 
890 /**
891  * nautilus_bookmarks_window_new:
892  * 
893  * Create a new bookmark-editing window. 
894  * @parent_window: The parent NautilusWindow.
895  *
896  * Return value: A pointer to the new window.
897  **/
898 GtkWindow *
899 nautilus_bookmarks_window_new (NautilusWindow *parent_window)
900 {
901 	return g_object_new (NAUTILUS_TYPE_BOOKMARKS_WINDOW,
902 			     "parent-window", parent_window,
903 			     NULL);
904 }