nautilus-bookmarks-window.c 32 KB
Newer Older
1 2
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Nautilus
 *
 * Copyright (C) 1999, 2000 Eazel, Inc.
 *
 * Nautilus 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.
 *
 * Nautilus 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 <sullivan@eazel.com>
 */
24

25 26
/* nautilus-bookmarks-window.c - implementation of bookmark-editing window.
 */
27

28
#include <config.h>
29
#include "nautilus-bookmarks-window.h"
30
#include "nautilus-window.h"
31
#include "nautilus-navigation-window.h"
Alexander Larsson's avatar
Alexander Larsson committed
32
#include <libnautilus-private/nautilus-undo.h>
33
#include <libnautilus-private/nautilus-global-preferences.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
34
#include <eel/eel-gtk-extensions.h>
Michael Meeks's avatar
Michael Meeks committed
35
#include <eel/eel-gnome-extensions.h>
36
#include <libnautilus-private/nautilus-undo-signal-handlers.h>
37
#include <gtk/gtk.h>
38
#include <gdk/gdkkeysyms.h>
39

40 41 42 43
/* Static variables to keep track of window state. If there were
 * more than one bookmark-editing window, these would be struct or
 * class fields. 
 */
44
static int		     bookmark_list_changed_signal_id;
45
static NautilusBookmarkList *bookmarks = NULL;
Johan Dahlin's avatar
Johan Dahlin committed
46 47
static GtkTreeView	    *bookmark_list_widget = NULL; /* awkward name to distinguish from NautilusBookmarkList */
static GtkListStore	    *bookmark_list_store = NULL;
48
static GtkListStore	    *bookmark_empty_list_store = NULL;
Johan Dahlin's avatar
Johan Dahlin committed
49 50
static GtkTreeSelection     *bookmark_selection = NULL;
static int                   selection_changed_id = 0;
51
static GtkWidget	    *name_field = NULL;
52
static int		     name_field_changed_signal_id;
53
static GtkWidget	    *remove_button = NULL;
54
static GtkWidget            *jump_button = NULL;
55
static gboolean		     text_changed = FALSE;
56
static gboolean		     name_text_changed = FALSE;
57
static GtkWidget	    *uri_field = NULL;
58
static int		     uri_field_changed_signal_id;
59 60
static int		     row_changed_signal_id;
static int		     row_deleted_signal_id;
61
static int                   row_activated_signal_id;
62
static int                   button_pressed_signal_id;
63
static int                   key_pressed_signal_id;
64
static int                   jump_button_signal_id;
65
static NautilusApplication  *application;
Johan Dahlin's avatar
Johan Dahlin committed
66

67
/* forward declarations */
68 69 70 71 72 73 74 75 76 77
static guint    get_selected_row                            (void);
static gboolean get_selection_exists                        (void);
static void     name_or_uri_field_activate                  (NautilusEntry        *entry);
static void     nautilus_bookmarks_window_restore_geometry  (GtkWidget            *window);
static void     on_bookmark_list_changed                    (NautilusBookmarkList *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);
78 79
static void     on_jump_button_clicked                      (GtkButton            *button,
							     gpointer              user_data);
80 81 82 83 84 85 86
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);
87 88 89 90
static void	on_row_activated			    (GtkTreeView	  *view,
							     GtkTreePath	  *path,
                                                             GtkTreeViewColumn    *column,
							     gpointer		   user_data);
91 92 93
static gboolean	on_button_pressed                           (GtkTreeView	  *view,
                                                             GdkEventButton       *event,
							     gpointer		   user_data);
94 95 96
static gboolean	on_key_pressed                              (GtkTreeView	  *view,
                                                             GdkEventKey          *event,
							     gpointer		   user_data);
Johan Dahlin's avatar
Johan Dahlin committed
97
static void     on_selection_changed                        (GtkTreeSelection     *treeselection,
98
							     gpointer              user_data);
Johan Dahlin's avatar
Johan Dahlin committed
99

100 101 102 103 104 105 106 107 108 109 110 111 112 113
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);
114
static void	open_selected_bookmark 			    (gpointer   user_data, GdkScreen *screen);
115
static void	update_bookmark_from_text		    (void);
116

117 118 119 120 121
/* 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
   nautilus_bookmark_list_insert_item() makes a copy of the bookmark, so we
   have to fetch the new copy and update our pointer. */
122 123
#define BOOKMARK_LIST_COLUMN_ICON		0
#define BOOKMARK_LIST_COLUMN_NAME		1
124
#define BOOKMARK_LIST_COLUMN_BOOKMARK		2
125 126
#define BOOKMARK_LIST_COLUMN_STYLE		3
#define BOOKMARK_LIST_COLUMN_COUNT		4
127

128 129 130 131 132 133 134 135 136 137
/* 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

138 139 140 141 142
static void
nautilus_bookmarks_window_response_callback (GtkDialog *dialog,
					     int response_id,
					     gpointer callback_data)
{
143 144 145
	if (response_id == GTK_RESPONSE_HELP) {
		GError *error = NULL;

146 147 148
		gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
			      "ghelp:user-guide#gosnautilus-36",
			      gtk_get_current_event_time (), &error);
149

150 151 152 153 154
		if (error) {
			GtkWidget *err_dialog;
			err_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
							     GTK_DIALOG_DESTROY_WITH_PARENT,
							     GTK_MESSAGE_ERROR,
155
							     GTK_BUTTONS_OK,
156
							     _("There was an error displaying help: \n%s"),
157 158 159 160 161 162 163 164 165
							     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);
		}
166
	} else if (response_id == GTK_RESPONSE_CLOSE) {
167
		gtk_widget_hide (GTK_WIDGET (dialog));
168
        }
169 170
}

171 172 173 174
static GtkListStore *
create_bookmark_store (void)
{
	return gtk_list_store_new (BOOKMARK_LIST_COLUMN_COUNT,
175
				   G_TYPE_ICON,
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
				   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 (nautilus_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);
}

220 221 222 223
static void
edit_bookmarks_dialog_reset_signals (gpointer data,
				     GObject *obj)
{
Cosimo Cecchi's avatar
Cosimo Cecchi committed
224
	g_signal_handler_disconnect (jump_button,
225
				     jump_button_signal_id);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
226
	g_signal_handler_disconnect (bookmark_list_widget,
227 228 229 230 231 232 233 234 235
				     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);
}

236 237 238 239
/**
 * create_bookmarks_window:
 * 
 * Create a new bookmark-editing window. 
240
 * @list: The NautilusBookmarkList that this window will edit.
241 242 243
 *
 * Return value: A pointer to the new window.
 **/
244
GtkWindow *
245
create_bookmarks_window (NautilusBookmarkList *list, GObject *undo_manager_source)
246
{
Johan Dahlin's avatar
Johan Dahlin committed
247 248 249
	GtkWidget         *window;
	GtkTreeViewColumn *col;
	GtkCellRenderer   *rend;
250
	GtkBuilder        *builder;
251 252 253

	bookmarks = list;

254 255 256 257
	builder = gtk_builder_new ();
	if (!gtk_builder_add_from_file (builder,
					UIDIR  "/nautilus-bookmarks-window.ui",
					NULL)) {
258 259 260
		return NULL;
	}

261 262 263 264 265
	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");

266
	application = nautilus_application_dup_singleton ();
267

268
	set_up_close_accelerator (window);
269
	nautilus_undo_share_undo_manager (G_OBJECT (window), undo_manager_source);
270

271
	gtk_window_set_wmclass (GTK_WINDOW (window), "bookmarks", "Nautilus");
272
	nautilus_bookmarks_window_restore_geometry (window);
273 274 275

	g_object_weak_ref (G_OBJECT (undo_manager_source), edit_bookmarks_dialog_reset_signals, 
			   undo_manager_source);
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
276
	
277
	bookmark_list_widget = GTK_TREE_VIEW (gtk_builder_get_object (builder, "bookmark_tree_view"));
278

Johan Dahlin's avatar
Johan Dahlin committed
279 280 281
	rend = gtk_cell_renderer_pixbuf_new ();
	col = gtk_tree_view_column_new_with_attributes ("Icon", 
							rend,
282
							"gicon", 
Johan Dahlin's avatar
Johan Dahlin committed
283 284 285 286 287 288
							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),
					      NAUTILUS_ICON_SIZE_SMALLER);
289

Johan Dahlin's avatar
Johan Dahlin committed
290
	rend = gtk_cell_renderer_text_new ();
291 292 293 294 295
	g_object_set (rend,
		      "ellipsize", PANGO_ELLIPSIZE_END,
		      "ellipsize-set", TRUE,
		      NULL);

Johan Dahlin's avatar
Johan Dahlin committed
296 297 298 299
	col = gtk_tree_view_column_new_with_attributes ("Icon", 
							rend,
							"text", 
							BOOKMARK_LIST_COLUMN_NAME,
300 301
							"style",
							BOOKMARK_LIST_COLUMN_STYLE,
Johan Dahlin's avatar
Johan Dahlin committed
302 303 304 305
							NULL);
	gtk_tree_view_append_column (bookmark_list_widget,
				     GTK_TREE_VIEW_COLUMN (col));
	
306 307
	bookmark_list_store = create_bookmark_store ();
	setup_empty_list ();
Johan Dahlin's avatar
Johan Dahlin committed
308
	gtk_tree_view_set_model (bookmark_list_widget,
309
				 GTK_TREE_MODEL (bookmark_empty_list_store));
Johan Dahlin's avatar
Johan Dahlin committed
310 311 312
	
	bookmark_selection =
		GTK_TREE_SELECTION (gtk_tree_view_get_selection (bookmark_list_widget));
313

314
	name_field = nautilus_entry_new ();
315
	
316
	gtk_widget_show (name_field);
317
	gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_name_placeholder")),
318
			    name_field, TRUE, TRUE, 0);
319
	
320
	gtk_label_set_mnemonic_widget (
321
		GTK_LABEL (gtk_builder_get_object (builder, "bookmark_name_label")),
322
		name_field);
323

324
	uri_field = nautilus_entry_new ();
325
	gtk_widget_show (uri_field);
326
	gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_location_placeholder")),
327
			    uri_field, TRUE, TRUE, 0);
328

329
	gtk_label_set_mnemonic_widget (
330
		GTK_LABEL (gtk_builder_get_object (builder, "bookmark_location_label")),
331
		uri_field);
332

333
	bookmark_list_changed_signal_id =
334
		g_signal_connect (bookmarks, "changed",
335
				  G_CALLBACK (on_bookmark_list_changed), NULL);
336 337 338 339 340 341
	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);
342 343 344
        row_activated_signal_id =
                g_signal_connect (bookmark_list_widget, "row_activated",
                                  G_CALLBACK (on_row_activated), undo_manager_source);
345 346 347
        button_pressed_signal_id =
                g_signal_connect (bookmark_list_widget, "button_press_event",
                                  G_CALLBACK (on_button_pressed), NULL);
348 349 350
        key_pressed_signal_id =
                g_signal_connect (bookmark_list_widget, "key_press_event",
                                  G_CALLBACK (on_key_pressed), NULL);
Johan Dahlin's avatar
Johan Dahlin committed
351 352 353
	selection_changed_id =
		g_signal_connect (bookmark_selection, "changed",
				  G_CALLBACK (on_selection_changed), NULL);	
354

355
	g_signal_connect (window, "delete_event",
356
			  G_CALLBACK (on_window_delete_event), NULL);
357
	g_signal_connect (window, "hide",
358
			  G_CALLBACK (on_window_hide_event), NULL);                    	    
359
	g_signal_connect (window, "destroy",
360 361 362 363 364
			  G_CALLBACK (on_window_destroy_event), NULL);
	g_signal_connect (window, "response",
			  G_CALLBACK (nautilus_bookmarks_window_response_callback), NULL);

	name_field_changed_signal_id =
365
		g_signal_connect (name_field, "changed",
366
				  G_CALLBACK (on_name_field_changed), NULL);
367
                      		    
368
	g_signal_connect (name_field, "focus_out_event",
369
			  G_CALLBACK (on_text_field_focus_out_event), NULL);                            
370
	g_signal_connect (name_field, "activate",
371
			  G_CALLBACK (name_or_uri_field_activate), NULL);
372

373
	uri_field_changed_signal_id = 
374
		g_signal_connect (uri_field, "changed",
375
				  G_CALLBACK (on_uri_field_changed), NULL);
376
                      		    
377
	g_signal_connect (uri_field, "focus_out_event",
378
			  G_CALLBACK (on_text_field_focus_out_event), NULL);
379
	g_signal_connect (uri_field, "activate",
380
			  G_CALLBACK (name_or_uri_field_activate), NULL);
381
	g_signal_connect (remove_button, "clicked",
382
			  G_CALLBACK (on_remove_button_clicked), NULL);
383 384 385
	jump_button_signal_id = 
		g_signal_connect (jump_button, "clicked",
				  G_CALLBACK (on_jump_button_clicked), undo_manager_source);
386

387
	gtk_tree_selection_set_mode (bookmark_selection, GTK_SELECTION_BROWSE);
Johan Dahlin's avatar
Johan Dahlin committed
388
	
389 390 391
	/* Fill in list widget with bookmarks, must be after signals are wired up. */
	repopulate();

392
	g_object_unref (builder);
393
	
394
	return GTK_WINDOW (window);
395 396
}

397 398 399 400
void
edit_bookmarks_dialog_set_signals (GObject *undo_manager_source)
{

Cosimo Cecchi's avatar
Cosimo Cecchi committed
401
	g_signal_handler_disconnect (jump_button,
402
				     jump_button_signal_id);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
403
	g_signal_handler_disconnect (bookmark_list_widget,
404 405 406 407 408 409 410 411 412 413 414 415 416
				     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);
}

417
static NautilusBookmark *
418
get_selected_bookmark (void)
419
{
420
	g_return_val_if_fail(NAUTILUS_IS_BOOKMARK_LIST(bookmarks), NULL);
421

422 423 424
	if (!get_selection_exists())
		return NULL;

425 426 427
	if (nautilus_bookmark_list_length (bookmarks) < 1)
		return NULL;

428
	return nautilus_bookmark_list_item_at(bookmarks, get_selected_row ());
429 430 431
}

static guint
432
get_selected_row (void)
433
{
Johan Dahlin's avatar
Johan Dahlin committed
434 435 436
	GtkTreeIter       iter;
	GtkTreePath      *path;
	GtkTreeModel     *model;
437
	gint		 *indices, row;
Johan Dahlin's avatar
Johan Dahlin committed
438
	
439
	g_assert (get_selection_exists());
Johan Dahlin's avatar
Johan Dahlin committed
440 441 442 443 444 445 446
	
	model = GTK_TREE_MODEL (bookmark_list_store);
	gtk_tree_selection_get_selected (bookmark_selection,
					 &model,
					 &iter);
	
	path = gtk_tree_model_get_path (model, &iter);
447 448 449 450
	indices = gtk_tree_path_get_indices (path);
	row = indices[0];
	gtk_tree_path_free (path);
	return row;
451 452 453
}

static gboolean
454
get_selection_exists (void)
455
{
Johan Dahlin's avatar
Johan Dahlin committed
456
	return gtk_tree_selection_get_selected (bookmark_selection, NULL, NULL);
457 458
}

459 460 461
static void
nautilus_bookmarks_window_restore_geometry (GtkWidget *window)
{
462
	const char *window_geometry;
463 464
	
	g_return_if_fail (GTK_IS_WINDOW (window));
465
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
466

467
	window_geometry = nautilus_bookmark_list_get_window_geometry (bookmarks);
468

469
	if (window_geometry != NULL) {	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
470
		eel_gtk_window_set_initial_geometry_from_string 
471
			(GTK_WINDOW (window), window_geometry, 
472
			 BOOKMARKS_WINDOW_MIN_WIDTH, BOOKMARKS_WINDOW_MIN_HEIGHT, FALSE);
473

474 475 476 477 478
	} 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);
479

480 481
		/* Let window manager handle default position if no position stored */
	}
482 483 484 485 486 487 488 489 490
}

/**
 * nautilus_bookmarks_window_save_geometry:
 * 
 * Save window size & position to disk.
 * @window: The bookmarks window whose geometry should be saved.
 **/
void
491
nautilus_bookmarks_window_save_geometry (GtkWindow *window)
492 493
{
	g_return_if_fail (GTK_IS_WINDOW (window));
494
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
495 496

	/* Don't bother if window is already closed */
497
	if (gtk_widget_get_visible (GTK_WIDGET (window))) {
498
		char *geometry_string;
499
		
500 501
		geometry_string = eel_gtk_window_get_geometry_string (window);

502
		nautilus_bookmark_list_set_window_geometry (bookmarks, geometry_string);
503 504 505
		g_free (geometry_string);
	}
}
506 507

static void
508
on_bookmark_list_changed (NautilusBookmarkList *bookmarks, gpointer data)
509
{
510
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
511 512

	/* maybe add logic here or in repopulate to save/restore selection */
513
	repopulate ();
514 515 516 517 518 519
}

static void
on_name_field_changed (GtkEditable *editable,
		       gpointer     user_data)
{
Johan Dahlin's avatar
Johan Dahlin committed
520 521
	GtkTreeIter   iter;
	g_return_if_fail(GTK_IS_TREE_VIEW(bookmark_list_widget));
522
	g_return_if_fail(GTK_IS_ENTRY(name_field));
523 524 525

	if (!get_selection_exists())
		return;
526 527 528 529

	/* Update text displayed in list instantly. Also remember that 
	 * user has changed text so we update real bookmark later. 
	 */
Johan Dahlin's avatar
Johan Dahlin committed
530 531 532 533 534 535 536 537
	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);
538
	text_changed = TRUE;
539
	name_text_changed = TRUE;
540 541
}

542
static void
543
open_selected_bookmark (gpointer user_data, GdkScreen *screen)
544
{
545
	NautilusBookmark *selected;
546
	NautilusWindow *window;
Alexander Larsson's avatar
Alexander Larsson committed
547
	GFile *location;
548 549
	
	selected = get_selected_bookmark ();
550

551 552 553
	if (!selected) {
		return;
	}
554

Alexander Larsson's avatar
Alexander Larsson committed
555 556
	location = nautilus_bookmark_get_location (selected);
	if (location == NULL) { 
557 558
		return;
	}
559

560
	window = user_data;
561 562
	nautilus_window_go_to (window, location);

Alexander Larsson's avatar
Alexander Larsson committed
563
	g_object_unref (location);
564 565
}

566 567 568 569
static void
on_jump_button_clicked (GtkButton *button,
                        gpointer   user_data)
{
570 571 572 573
	GdkScreen *screen;

	screen = gtk_widget_get_screen (GTK_WIDGET (button));
	open_selected_bookmark (user_data, screen);
574
}
575 576

static void
577
bookmarks_delete_bookmark (void)
578
{
Johan Dahlin's avatar
Johan Dahlin committed
579
	GtkTreeIter iter;
580 581
	GtkTreePath *path;
	gint *indices, row, rows;
Johan Dahlin's avatar
Johan Dahlin committed
582 583 584

	g_assert (GTK_IS_TREE_VIEW (bookmark_list_widget));
	
585
	if (!gtk_tree_selection_get_selected (bookmark_selection, NULL, &iter))
Johan Dahlin's avatar
Johan Dahlin committed
586
		return;
587

588 589 590 591 592 593 594 595
	/* 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);

Johan Dahlin's avatar
Johan Dahlin committed
596 597
	gtk_list_store_remove (bookmark_list_store, &iter);

598 599 600 601
	/* 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;
Johan Dahlin's avatar
Johan Dahlin committed
602

603 604 605 606 607 608 609
	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);
	}
Johan Dahlin's avatar
Johan Dahlin committed
610
}
611

612 613 614 615 616 617 618 619
static void
on_remove_button_clicked (GtkButton *button,
                          gpointer   user_data)
{
        bookmarks_delete_bookmark ();
}


620 621 622 623
/* 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. */
624
static void
625 626 627 628
on_row_changed (GtkListStore *store,
		GtkTreePath *path,
		GtkTreeIter *iter,
		gpointer user_data)
629
{
630 631 632 633
	NautilusBookmark *bookmark = NULL, *bookmark_in_list;
	gint *indices, row;
	gboolean insert_bookmark = TRUE;

634 635
	store = bookmark_list_store;

636 637 638 639 640
	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);
641

642 643 644 645 646 647 648 649
	/* 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) nautilus_bookmark_list_length (bookmarks)) {
		bookmark_in_list = nautilus_bookmark_list_item_at (bookmarks,
								   row);
		if (bookmark_in_list == bookmark)
			insert_bookmark = FALSE;
	}
650

651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
	if (insert_bookmark) {
		g_signal_handler_block (bookmarks,
					bookmark_list_changed_signal_id);
		nautilus_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 = nautilus_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);
	}
}

669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
/* 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;
}

691 692 693 694 695
static gboolean
on_key_pressed (GtkTreeView *view,
                GdkEventKey *event,
                gpointer user_data)
{
696
        if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) {
697 698 699 700
                bookmarks_delete_bookmark ();
                return TRUE;
        }

701 702
	update_bookmark_from_text ();

703 704 705
        return FALSE;
}

706 707 708 709 710 711
static void
on_row_activated (GtkTreeView       *view,
                  GtkTreePath       *path,
                  GtkTreeViewColumn *column,
                  gpointer           user_data)
{
712 713 714 715
	GdkScreen *screen;

	screen = gtk_widget_get_screen (GTK_WIDGET (view));
	open_selected_bookmark (user_data, screen);
716 717
}

718 719 720 721 722 723 724 725 726 727 728 729 730
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);
	nautilus_bookmark_list_delete_item_at (bookmarks, row);
	g_signal_handler_unblock (bookmarks, bookmark_list_changed_signal_id);
731 732 733
}

static void
Johan Dahlin's avatar
Johan Dahlin committed
734 735
on_selection_changed (GtkTreeSelection *treeselection,
		      gpointer user_data)
736
{
737
	NautilusBookmark *selected;
738 739
	const char *name = NULL;
	char *entry_text = NULL;
740 741
	GFile *location;

742 743
	g_assert (GTK_IS_ENTRY (name_field));
	g_assert (GTK_IS_ENTRY (uri_field));
744

745
	selected = get_selected_bookmark ();
746 747 748

	if (selected) {
		name = nautilus_bookmark_get_name (selected);
749 750 751 752
		location = nautilus_bookmark_get_location (selected);
		entry_text = g_file_get_parse_name (location);

		g_object_unref (location);
753
	}
754
	
755 756
	/* Set the sensitivity of widgets that require a selection */
	gtk_widget_set_sensitive (remove_button, selected != NULL);
757
        gtk_widget_set_sensitive (jump_button, selected != NULL);
758 759 760 761 762 763 764 765 766 767
	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);
	nautilus_entry_set_text (NAUTILUS_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);
	nautilus_entry_set_text (NAUTILUS_ENTRY (uri_field),
768
				 entry_text ? entry_text : "");
769 770 771
	g_signal_handler_unblock (uri_field, uri_field_changed_signal_id);

	text_changed = FALSE;
772
	name_text_changed = FALSE;
773

774
	g_free (entry_text);
775 776 777
}


778
static void
779
update_bookmark_from_text (void)
780
{
781
	if (text_changed) {
782
		NautilusBookmark *bookmark, *bookmark_in_list;
783 784
		const char *name;
		GIcon *icon;
785
		guint selected_row;
786
		GtkTreeIter iter;
Alexander Larsson's avatar
Alexander Larsson committed
787
		GFile *location;
788

789 790
		g_assert (GTK_IS_ENTRY (name_field));
		g_assert (GTK_IS_ENTRY (uri_field));
791

792 793 794 795
		if (gtk_entry_get_text_length (GTK_ENTRY (uri_field)) == 0) {
			return;
		}

796 797
		location = g_file_parse_name 
			(gtk_entry_get_text (GTK_ENTRY (uri_field)));
Alexander Larsson's avatar
Alexander Larsson committed
798
		
799 800 801
		bookmark = nautilus_bookmark_new (location,
						  name_text_changed ? gtk_entry_get_text (GTK_ENTRY (name_field)) : NULL,
						  NULL);
Alexander Larsson's avatar
Alexander Larsson committed
802 803
		
		g_object_unref (location);
804

805
		selected_row = get_selected_row ();
806 807 808 809

		/* turn off list updating 'cuz otherwise the list-reordering code runs
		 * after repopulate(), thus reordering the correctly-ordered list.
		 */
810 811
		g_signal_handler_block (bookmarks, 
					bookmark_list_changed_signal_id);
812 813
		nautilus_bookmark_list_delete_item_at (bookmarks, selected_row);
		nautilus_bookmark_list_insert_item (bookmarks, bookmark, selected_row);
814 815
		g_signal_handler_unblock (bookmarks, 
					  bookmark_list_changed_signal_id);
816
		g_object_unref (bookmark);
817 818 819 820 821 822 823

		/* 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);
824

825 826
		bookmark_in_list = nautilus_bookmark_list_item_at (bookmarks,
								   selected_row);
827 828

		name = nautilus_bookmark_get_name (bookmark_in_list);
829
		icon = nautilus_bookmark_get_icon (bookmark_in_list);
830

831 832
		gtk_list_store_set (bookmark_list_store, &iter,
				    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark_in_list,
833
				    BOOKMARK_LIST_COLUMN_NAME, name,
834
				    BOOKMARK_LIST_COLUMN_ICON, icon,
835 836 837
				    -1);
		g_signal_handler_unblock (bookmark_list_store,
					  row_changed_signal_id);
838

839
		g_object_unref (icon);
840
	}
841 842 843 844 845 846 847 848 849 850
}

static gboolean
on_text_field_focus_out_event (GtkWidget *widget,
			       GdkEventFocus *event,
			       gpointer user_data)
{
	g_assert (NAUTILUS_IS_ENTRY (widget));

	update_bookmark_from_text ();
851 852 853
	return FALSE;
}

854 855 856 857 858 859 860 861
static void
name_or_uri_field_activate (NautilusEntry *entry)
{
	g_assert (NAUTILUS_IS_ENTRY (entry));

	update_bookmark_from_text ();
	nautilus_entry_select_all_at_idle (entry);
}
862 863 864

static void
on_uri_field_changed (GtkEditable *editable,
865
		      gpointer user_data)
866 867 868 869 870 871 872 873 874
{
	/* Remember that user has changed text so we 
	 * update real bookmark later. 
	 */
	text_changed = TRUE;
}

static gboolean
on_window_delete_event (GtkWidget *widget,
875 876
			GdkEvent *event,
			gpointer user_data)
877
{
878
	gtk_widget_hide (widget);
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
	return TRUE;
}

static gboolean
restore_geometry (gpointer data)
{
	g_assert (GTK_IS_WINDOW (data));

	nautilus_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)
896
{
897
	nautilus_bookmarks_window_save_geometry (GTK_WINDOW (widget));
898

Darin Adler's avatar
Darin Adler committed
899
	/* Disable undo for entry widgets */
900 901
	nautilus_undo_unregister (G_OBJECT (name_field));
	nautilus_undo_unregister (G_OBJECT (uri_field));
902

903
	/* restore_geometry only works after window is hidden */
904
	g_idle_add (restore_geometry, widget);
905
}
906

907 908 909 910
static void
on_window_destroy_event (GtkWidget *widget,
		      	 gpointer user_data)
{
911 912
	g_object_unref (bookmark_list_store);
	g_object_unref (bookmark_empty_list_store);