nautilus-bookmarks-window.c 27 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
/*
 * 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
19
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 21 22
 *
 * Authors: John Sullivan <sullivan@eazel.com>
 */
23

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

27
#include <config.h>
28 29

#include "nautilus-application.h"
30
#include "nautilus-bookmarks-window.h"
31
#include "nautilus-window.h"
32

33
#include <libnautilus-private/nautilus-entry.h>
34
#include <libnautilus-private/nautilus-global-preferences.h>
35

36
#include <gtk/gtk.h>
37
#include <glib/gi18n.h>
38
#include <gdk/gdkkeysyms.h>
39

40 41 42 43 44
/* 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. */
45 46 47 48 49 50 51

enum {
	BOOKMARK_LIST_COLUMN_ICON,
	BOOKMARK_LIST_COLUMN_NAME,
	BOOKMARK_LIST_COLUMN_BOOKMARK,
	BOOKMARK_LIST_NUM_COLUMNS
};
52

53 54
/* Larger size initially; user can stretch or shrink (but not shrink below min) */
#define BOOKMARKS_WINDOW_INITIAL_WIDTH	500
55
#define BOOKMARKS_WINDOW_INITIAL_HEIGHT	400
56

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
G_DEFINE_TYPE (NautilusBookmarksWindow, nautilus_bookmarks_window, GTK_TYPE_WINDOW)

enum {
	PROP_PARENT_WINDOW = 1,
	NUM_PROPERTIES
};

static GParamSpec* properties[NUM_PROPERTIES] = { NULL, };

struct NautilusBookmarksWindowPrivate {
	NautilusWindow *parent_window;

	NautilusBookmarkList *bookmarks;
	gulong bookmarks_changed_id;

	GtkTreeView *tree_view;
	GtkTreeSelection *selection;
	gulong row_activated_id;
	gulong button_press_id;
	gulong key_press_id;
	gulong selection_changed_id;

	GtkListStore *model;
	gulong row_changed_id;
	gulong row_deleted_id;

	GtkWidget *name_field;
	GtkWidget *uri_field;
	gulong name_changed_id;
	gulong uri_changed_id;
	gboolean text_changed;

	GtkWidget *remove_button;
	GtkWidget *up_button;
	GtkWidget *down_button;
};

94
static gboolean
95
get_selection_exists (NautilusBookmarksWindow *self)
96
{
97
	return gtk_tree_selection_get_selected (self->priv->selection, NULL, NULL);
98 99 100
}

static guint
101
get_selected_row (NautilusBookmarksWindow *self)
102 103 104 105 106 107
{
	GtkTreeIter       iter;
	GtkTreePath      *path;
	GtkTreeModel     *model;
	gint		 *indices, row;
	
108 109 110
	if (!gtk_tree_selection_get_selected (self->priv->selection, &model, &iter)) {
		g_assert_not_reached ();
	}
111 112 113 114 115 116 117 118 119
	
	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 NautilusBookmark *
120
get_selected_bookmark (NautilusBookmarksWindow *self)
121
{
122
	if (!get_selection_exists (self)) {
123
		return NULL;
124
	}
125

126
	if (nautilus_bookmark_list_length (self->priv->bookmarks) < 1) {
127
		return NULL;
128
	}
129

130 131
	return nautilus_bookmark_list_item_at (self->priv->bookmarks,
					       get_selected_row (self));
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
}

static int
nautilus_bookmarks_window_key_press_event_cb (GtkWindow *window, 
					      GdkEventKey *event, 
					      gpointer user_data)
{
	if (event->state & GDK_CONTROL_MASK && event->keyval == GDK_KEY_w) {
		gtk_widget_destroy (GTK_WIDGET (window));
		return TRUE;
	}

	return FALSE;
}

147 148 149
static GtkListStore *
create_bookmark_store (void)
{
150
	return gtk_list_store_new (BOOKMARK_LIST_NUM_COLUMNS,
151
				   G_TYPE_ICON,
152
				   G_TYPE_STRING,
153
				   G_TYPE_OBJECT);
154 155 156
}

static void
157
setup_empty_list (NautilusBookmarksWindow *self)
158
{
159
	GtkListStore *empty_model;
160 161
	GtkTreeIter iter;

162 163
	empty_model = create_bookmark_store ();
	gtk_list_store_append (empty_model, &iter);
164

165
	gtk_list_store_set (empty_model, &iter,
166 167
			    BOOKMARK_LIST_COLUMN_NAME, _("No bookmarks defined"),
			    -1);
168 169 170 171
	gtk_tree_view_set_model (self->priv->tree_view,
				 GTK_TREE_MODEL (empty_model));

	g_object_unref (empty_model);
172 173
}

174
static void
175
update_widgets_sensitivity (NautilusBookmarksWindow *self)
176 177 178 179
{
	NautilusBookmark *selected;
	int n_active;
	int index = -1;
180
	gboolean builtin;
181

182 183
	selected = get_selected_bookmark (self);
	n_active = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->priv->model), NULL);
184
	if (selected != NULL) {
185
		index = get_selected_row (self);
186 187 188
		builtin = nautilus_bookmark_get_is_builtin (selected);
	} else {
		builtin = FALSE;
189 190 191
	}

	/* Set the sensitivity of widgets that require a selection */
192 193 194
	gtk_widget_set_sensitive (self->priv->remove_button, !builtin && index >= 0 && n_active > 1);
	gtk_widget_set_sensitive (self->priv->up_button, !builtin && index > 0);
	gtk_widget_set_sensitive (self->priv->down_button, !builtin && index >= 0 && index < n_active - 1);
195 196
	gtk_widget_set_sensitive (self->priv->name_field, selected != NULL);
	gtk_widget_set_sensitive (self->priv->uri_field, selected != NULL);
197 198
}

199 200 201 202
static void
on_selection_changed (GtkTreeSelection *treeselection,
		      gpointer user_data)
{
203
	NautilusBookmarksWindow *self = user_data;
204 205 206 207 208
	NautilusBookmark *selected;
	const char *name = NULL;
	char *entry_text = NULL;
	GFile *location;

209
	selected = get_selected_bookmark (self);
210 211 212 213 214 215 216 217

	if (selected) {
		name = nautilus_bookmark_get_name (selected);
		location = nautilus_bookmark_get_location (selected);
		entry_text = g_file_get_parse_name (location);

		g_object_unref (location);
	}
218

219
	update_widgets_sensitivity (self);
220

221 222
	g_signal_handler_block (self->priv->name_field, self->priv->name_changed_id);
	nautilus_entry_set_text (NAUTILUS_ENTRY (self->priv->name_field),
223
				 name ? name : "");
224
	g_signal_handler_unblock (self->priv->name_field, self->priv->name_changed_id);
225

226 227
	g_signal_handler_block (self->priv->uri_field, self->priv->uri_changed_id);
	nautilus_entry_set_text (NAUTILUS_ENTRY (self->priv->uri_field),
228
				 entry_text ? entry_text : "");
229
	g_signal_handler_unblock (self->priv->uri_field, self->priv->uri_changed_id);
230

231
	self->priv->text_changed = FALSE;
232 233 234 235

	g_free (entry_text);
}

236
static void
237 238
bookmarks_set_empty (NautilusBookmarksWindow *self,
		     gboolean empty)
239 240 241 242
{
	GtkTreeIter iter;

	if (empty) {
243 244
		setup_empty_list (self);
		gtk_widget_set_sensitive (GTK_WIDGET (self->priv->tree_view), FALSE);
245
	} else {
246 247
		gtk_tree_view_set_model (self->priv->tree_view, GTK_TREE_MODEL (self->priv->model));
		gtk_widget_set_sensitive (GTK_WIDGET (self->priv->tree_view), TRUE);
248

249 250 251
		if (nautilus_bookmark_list_length (self->priv->bookmarks) > 0 &&
		    !get_selection_exists (self)) {
			gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
252
						       &iter, NULL, 0);
253
			gtk_tree_selection_select_iter (self->priv->selection, &iter);
254 255 256
		}
	}

257
	on_selection_changed (self->priv->selection, self);
258 259
}

260
static void
261
repopulate (NautilusBookmarksWindow *self)
262
{
263 264 265 266 267
	NautilusBookmark *selected;
	GtkListStore *store;
	GtkTreePath *path;
	GtkTreeRowReference *reference;
	guint index;
268

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
	selected = get_selected_bookmark (self);

	g_signal_handler_block (self->priv->selection, self->priv->selection_changed_id);
	g_signal_handler_block (self->priv->model, self->priv->row_deleted_id);
        g_signal_handler_block (self->priv->tree_view, self->priv->row_activated_id);
        g_signal_handler_block (self->priv->tree_view, self->priv->key_press_id);
        g_signal_handler_block (self->priv->tree_view, self->priv->button_press_id);

	gtk_list_store_clear (self->priv->model);

	g_signal_handler_unblock (self->priv->selection, self->priv->selection_changed_id);
	g_signal_handler_unblock (self->priv->model, self->priv->row_deleted_id);
        g_signal_handler_unblock (self->priv->tree_view, self->priv->row_activated_id);
        g_signal_handler_unblock (self->priv->tree_view, self->priv->key_press_id);
        g_signal_handler_unblock (self->priv->tree_view, self->priv->button_press_id);

285
	/* Fill the list in with the bookmark names. */
286
	g_signal_handler_block (self->priv->model, self->priv->row_changed_id);
287

288
	reference = NULL;
289
	store = self->priv->model;
290

291
	for (index = 0; index < nautilus_bookmark_list_length (self->priv->bookmarks); ++index) {
292 293 294 295
		NautilusBookmark *bookmark;
		const char       *bookmark_name;
		GIcon            *bookmark_icon;
		GtkTreeIter       iter;
296

297
		bookmark = nautilus_bookmark_list_item_at (self->priv->bookmarks, index);
298
		bookmark_name = nautilus_bookmark_get_name (bookmark);
299
		bookmark_icon = nautilus_bookmark_get_symbolic_icon (bookmark);
300

301 302 303 304 305 306
		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter, 
				    BOOKMARK_LIST_COLUMN_ICON, bookmark_icon,
				    BOOKMARK_LIST_COLUMN_NAME, bookmark_name,
				    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark,
				    -1);
307

308 309 310
		if (bookmark == selected) {
			/* save old selection */
			GtkTreePath *path;
311

312 313 314 315
			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);
		}
316

317 318
		g_object_unref (bookmark_icon);
	}
319

320
	g_signal_handler_unblock (self->priv->model, self->priv->row_changed_id);
321

322 323
	if (reference != NULL) {
		/* restore old selection */
324

325 326 327
		/* bookmarks_set_empty() will call the selection change handler,
 		 * so we block it here in case of selection change.
 		 */
328
		g_signal_handler_block (self->priv->selection, self->priv->selection_changed_id);
329

330 331
		g_assert (index != 0);
		g_assert (gtk_tree_row_reference_valid (reference));
332

333
		path = gtk_tree_row_reference_get_path (reference);
334
		gtk_tree_selection_select_path (self->priv->selection, path);
335 336
		gtk_tree_row_reference_free (reference);
		gtk_tree_path_free (path);
337

338
		g_signal_handler_unblock (self->priv->selection, self->priv->selection_changed_id);
339
	}
340

341
	bookmarks_set_empty (self, (index == 0));
342 343
}

344
static void
345 346
on_bookmark_list_changed (NautilusBookmarkList *bookmarks,
			  gpointer user_data)
347
{
348
	NautilusBookmarksWindow *self = user_data;
349 350

	/* maybe add logic here or in repopulate to save/restore selection */
351
	repopulate (self);
352 353 354 355 356 357
}

static void
on_name_field_changed (GtkEditable *editable,
		       gpointer     user_data)
{
358
	NautilusBookmarksWindow *self = user_data;
Johan Dahlin's avatar
Johan Dahlin committed
359
	GtkTreeIter   iter;
360

361
	if (!get_selection_exists(self)) {
362
		return;
363
	}
364 365 366 367

	/* Update text displayed in list instantly. Also remember that 
	 * user has changed text so we update real bookmark later. 
	 */
368 369
	gtk_tree_selection_get_selected (self->priv->selection, NULL, &iter);
	gtk_list_store_set (self->priv->model,
Johan Dahlin's avatar
Johan Dahlin committed
370
			    &iter, BOOKMARK_LIST_COLUMN_NAME, 
371
			    gtk_entry_get_text (GTK_ENTRY (self->priv->name_field)),
Johan Dahlin's avatar
Johan Dahlin committed
372
			    -1);
373

374
	self->priv->text_changed = TRUE;
375 376
}

377
static void
378
bookmarks_delete_bookmark (NautilusBookmarksWindow *self)
379
{
Johan Dahlin's avatar
Johan Dahlin committed
380
	GtkTreeIter iter;
381 382
	GtkTreePath *path;
	gint *indices, row, rows;
Johan Dahlin's avatar
Johan Dahlin committed
383
	
384
	if (!gtk_tree_selection_get_selected (self->priv->selection, NULL, &iter)) {
Johan Dahlin's avatar
Johan Dahlin committed
385
		return;
386
	}
387

388 389
	/* Remove the selected item from the list store. on_row_deleted() will
	   remove it from the bookmark list. */
390
	path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->priv->model), &iter);
391 392 393 394
	indices = gtk_tree_path_get_indices (path);
	row = indices[0];
	gtk_tree_path_free (path);

395
	gtk_list_store_remove (self->priv->model, &iter);
Johan Dahlin's avatar
Johan Dahlin committed
396

397
	/* Try to select the same row, or the last one in the list. */
398 399
	rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->priv->model), NULL);
	if (row >= rows) {
400
		row = rows - 1;
401
	}
Johan Dahlin's avatar
Johan Dahlin committed
402

403
	if (row < 0) {
404
		bookmarks_set_empty (self, TRUE);
405
	} else {
406
		gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
407
					       &iter, NULL, row);
408
		gtk_tree_selection_select_iter (self->priv->selection, &iter);
409
	}
Johan Dahlin's avatar
Johan Dahlin committed
410
}
411

412 413 414 415
static void
on_remove_button_clicked (GtkButton *button,
                          gpointer   user_data)
{
416 417
	NautilusBookmarksWindow *self = user_data;
        bookmarks_delete_bookmark (self);
418 419
}

420 421 422 423
static void
on_up_button_clicked (GtkButton *button,
		      gpointer   user_data)
{
424
	NautilusBookmarksWindow *self = user_data;
425 426 427
	guint row;
	GtkTreeIter iter;

428 429 430
	row = get_selected_row (self);
	nautilus_bookmark_list_move_item (self->priv->bookmarks, row, row - 1);
	gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
431
				       &iter, NULL, row - 1);
432
	gtk_tree_selection_select_iter (self->priv->selection, &iter);
433 434 435 436 437 438
}

static void
on_down_button_clicked (GtkButton *button,
			gpointer   user_data)
{
439
	NautilusBookmarksWindow *self = user_data;
440 441 442
	guint row;
	GtkTreeIter iter;

443 444 445
	row = get_selected_row (self);
	nautilus_bookmark_list_move_item (self->priv->bookmarks, row, row + 1);
	gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->priv->model),
446
				       &iter, NULL, row + 1);
447
	gtk_tree_selection_select_iter (self->priv->selection, &iter);
448
}
449

450 451 452 453
/* 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. */
454
static void
455 456 457 458
on_row_changed (GtkListStore *store,
		GtkTreePath *path,
		GtkTreeIter *iter,
		gpointer user_data)
459
{
460
	NautilusBookmarksWindow *self = user_data;
461 462 463 464 465 466 467 468 469
	NautilusBookmark *bookmark = NULL, *bookmark_in_list;
	gint *indices, row;
	gboolean insert_bookmark = TRUE;

	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);
470

471 472
	/* 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. */
473 474
	if (row < (gint) nautilus_bookmark_list_length (self->priv->bookmarks)) {
		bookmark_in_list = nautilus_bookmark_list_item_at (self->priv->bookmarks, row);
475 476 477
		if (bookmark_in_list == bookmark)
			insert_bookmark = FALSE;
	}
478

479
	if (insert_bookmark) {
480 481 482 483 484 485
		g_signal_handler_block (self->priv->bookmarks,
					self->priv->bookmarks_changed_id);
		nautilus_bookmark_list_insert_item (self->priv->bookmarks,
						    bookmark, row);
		g_signal_handler_unblock (self->priv->bookmarks,
					  self->priv->bookmarks_changed_id);
486 487 488

		/* The bookmark will be copied when inserted into the list, so
		   we have to update the pointer in the list store. */
489 490
		bookmark = nautilus_bookmark_list_item_at (self->priv->bookmarks, row);
		g_signal_handler_block (store, self->priv->row_changed_id);
491 492 493
		gtk_list_store_set (store, iter,
				    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark,
				    -1);
494
		g_signal_handler_unblock (store, self->priv->row_changed_id);
495 496 497
	}
}

498
static void
499
update_bookmark_from_text (NautilusBookmarksWindow *self)
500 501 502 503 504 505 506 507
{
	NautilusBookmark *bookmark, *bookmark_in_list;
	const char *name;
	GIcon *icon;
	guint selected_row;
	GtkTreeIter iter;
	GFile *location;

508 509
	if (!self->priv->text_changed ||
	    gtk_entry_get_text_length (GTK_ENTRY (self->priv->uri_field)) == 0) {
510 511 512 513
		return;
	}

	location = g_file_parse_name 
514 515
		(gtk_entry_get_text (GTK_ENTRY (self->priv->uri_field)));

516
	bookmark = nautilus_bookmark_new (location,
517 518
					  gtk_entry_get_text (GTK_ENTRY (self->priv->name_field)));

519 520
	g_object_unref (location);

521
	selected_row = get_selected_row (self);
522 523 524 525

	/* turn off list updating 'cuz otherwise the list-reordering code runs
	 * after repopulate(), thus reordering the correctly-ordered list.
	 */
526 527 528 529
	g_signal_handler_block (self->priv->bookmarks, self->priv->bookmarks_changed_id);
	nautilus_bookmark_list_delete_item_at (self->priv->bookmarks, selected_row);
	nautilus_bookmark_list_insert_item (self->priv->bookmarks, bookmark, selected_row);
	g_signal_handler_unblock (self->priv->bookmarks, self->priv->bookmarks_changed_id);
530 531 532 533
	g_object_unref (bookmark);

	/* We also have to update the bookmark pointer in the list
	   store. */
534 535
	gtk_tree_selection_get_selected (self->priv->selection, NULL, &iter);
	g_signal_handler_block (self->priv->model, self->priv->row_changed_id);
536

537
	bookmark_in_list = nautilus_bookmark_list_item_at (self->priv->bookmarks, selected_row);
538
	name = nautilus_bookmark_get_name (bookmark_in_list);
539
	icon = nautilus_bookmark_get_symbolic_icon (bookmark_in_list);
540

541
	gtk_list_store_set (self->priv->model, &iter,
542 543 544 545 546
			    BOOKMARK_LIST_COLUMN_BOOKMARK, bookmark_in_list,
			    BOOKMARK_LIST_COLUMN_NAME, name,
			    BOOKMARK_LIST_COLUMN_ICON, icon,
			    -1);

547
	g_signal_handler_unblock (self->priv->model, self->priv->row_changed_id);
548 549 550
	g_object_unref (icon);
}

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
/* 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)
{
568 569
	NautilusBookmarksWindow *self = user_data;
	update_bookmark_from_text (self);
570 571 572 573

	return FALSE;
}

574 575 576 577 578
static gboolean
on_key_pressed (GtkTreeView *view,
                GdkEventKey *event,
                gpointer user_data)
{
579 580
	NautilusBookmarksWindow *self = user_data;

581
        if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) {
582
                bookmarks_delete_bookmark (self);
583 584 585
                return TRUE;
        }

586
	update_bookmark_from_text (self);
587

588 589 590
        return FALSE;
}

591 592 593 594 595 596
static void
on_row_activated (GtkTreeView       *view,
                  GtkTreePath       *path,
                  GtkTreeViewColumn *column,
                  gpointer           user_data)
{
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
	NautilusBookmarksWindow *self = user_data;
	NautilusBookmark *selected;
	GFile *location;

	selected = get_selected_bookmark (self);

	if (!selected) {
		return;
	}

	location = nautilus_bookmark_get_location (selected);
	if (location == NULL) {
		return;
	}

	nautilus_window_go_to (self->priv->parent_window, location);
	g_object_unref (location);
614 615
}

616 617 618 619 620
static void
on_row_deleted (GtkListStore *store,
		GtkTreePath *path,
		gpointer user_data)
{
621
	NautilusBookmarksWindow *self = user_data;
622 623 624 625 626
	gint *indices, row;

	indices = gtk_tree_path_get_indices (path);
	row = indices[0];

627 628 629
	g_signal_handler_block (self->priv->bookmarks, self->priv->bookmarks_changed_id);
	nautilus_bookmark_list_delete_item_at (self->priv->bookmarks, row);
	g_signal_handler_unblock (self->priv->bookmarks, self->priv->bookmarks_changed_id);
630 631
}

632 633 634 635 636
static gboolean
on_text_field_focus_out_event (GtkWidget *widget,
			       GdkEventFocus *event,
			       gpointer user_data)
{
637 638
	NautilusBookmarksWindow *self = user_data;
	update_bookmark_from_text (self);
639

640 641 642
	return FALSE;
}

643
static void
644 645
name_or_uri_field_activate (NautilusEntry *entry,
			    gpointer user_data)
646
{
647
	NautilusBookmarksWindow *self = user_data;
648

649
	update_bookmark_from_text (self);
650 651
	nautilus_entry_select_all_at_idle (entry);
}
652 653 654

static void
on_uri_field_changed (GtkEditable *editable,
655
		      gpointer user_data)
656
{
657 658
	NautilusBookmarksWindow *self = user_data;

659 660 661
	/* Remember that user has changed text so we 
	 * update real bookmark later. 
	 */
662
	self->priv->text_changed = TRUE;
663 664
}

665 666
static void
nautilus_bookmarks_window_dispose (GObject *object)
667
{
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
	NautilusBookmarksWindow *self = NAUTILUS_BOOKMARKS_WINDOW (object);

	if (self->priv->bookmarks_changed_id != 0) {
		g_signal_handler_disconnect (self->priv->bookmarks,
					     self->priv->bookmarks_changed_id);
		self->priv->bookmarks_changed_id = 0;
	}

	g_clear_object (&self->priv->model);

	G_OBJECT_CLASS (nautilus_bookmarks_window_parent_class)->dispose (object);
}

static void
nautilus_bookmarks_window_constructed (GObject *object)
{
	NautilusBookmarksWindow *self = NAUTILUS_BOOKMARKS_WINDOW (object);
	GtkBuilder *builder;
	GError *error = NULL;
	GtkWindow *window;
	GtkWidget *content;
689
	GtkTreeViewColumn *col;
690
	GtkCellRenderer *rend;
691

692
	G_OBJECT_CLASS (nautilus_bookmarks_window_parent_class)->constructed (object);
693

694 695 696
	builder = gtk_builder_new ();
	if (!gtk_builder_add_from_resource (builder,
					    "/org/gnome/nautilus/nautilus-bookmarks-window.ui",
697 698
					    &error)) {
		g_object_unref (builder);
699

700 701 702 703
		g_critical ("Can't load UI description for the bookmarks editor: %s", error->message);
		g_error_free (error);
		return;
	}
704

705 706
	window = GTK_WINDOW (object);
	gtk_window_set_title (window, _("Bookmarks"));
707 708 709 710
	gtk_window_set_default_size (window, 
				     BOOKMARKS_WINDOW_INITIAL_WIDTH, 
				     BOOKMARKS_WINDOW_INITIAL_HEIGHT);
	gtk_window_set_application (window,
711
				    gtk_window_get_application (GTK_WINDOW (self->priv->parent_window)));
712

713
	gtk_window_set_destroy_with_parent (window, TRUE);
714
	gtk_window_set_transient_for (window, GTK_WINDOW (self->priv->parent_window));
715
	gtk_window_set_position (window, GTK_WIN_POS_CENTER_ON_PARENT);
716
	gtk_container_set_border_width (GTK_CONTAINER (window), 6);
717 718 719

	g_signal_connect (window, "key-press-event",
			  G_CALLBACK (nautilus_bookmarks_window_key_press_event_cb), NULL);
720

721 722 723 724 725 726 727
	content = GTK_WIDGET (gtk_builder_get_object (builder, "bookmarks_window_content"));
	gtk_container_add (GTK_CONTAINER (window), content);

	/* tree view */
	self->priv->tree_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "bookmark_tree_view"));
	self->priv->selection = gtk_tree_view_get_selection (self->priv->tree_view);
	gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_BROWSE);
728

729
	rend = gtk_cell_renderer_pixbuf_new ();
730
	g_object_set (rend, "follow-state", TRUE, NULL);
731 732 733 734 735
	col = gtk_tree_view_column_new_with_attributes ("Icon", 
							rend,
							"gicon", 
							BOOKMARK_LIST_COLUMN_ICON,
							NULL);
736
	gtk_tree_view_append_column (self->priv->tree_view,
737 738 739
				     GTK_TREE_VIEW_COLUMN (col));
	gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (col),
					      NAUTILUS_ICON_SIZE_SMALLER);
740

741 742 743 744 745
	rend = gtk_cell_renderer_text_new ();
	g_object_set (rend,
		      "ellipsize", PANGO_ELLIPSIZE_END,
		      "ellipsize-set", TRUE,
		      NULL);
746

747 748 749 750 751
	col = gtk_tree_view_column_new_with_attributes ("Icon", 
							rend,
							"text", 
							BOOKMARK_LIST_COLUMN_NAME,
							NULL);
752
	gtk_tree_view_append_column (self->priv->tree_view,
753
				     GTK_TREE_VIEW_COLUMN (col));
754

755 756 757 758 759 760
	self->priv->model = create_bookmark_store ();
	setup_empty_list (self);

	/* name entry */
	self->priv->name_field = nautilus_entry_new ();
	gtk_widget_show (self->priv->name_field);
761
	gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_name_placeholder")),
762
			    self->priv->name_field, TRUE, TRUE, 0);
Johan Dahlin's avatar
Johan Dahlin committed
763
	
764 765
	gtk_label_set_mnemonic_widget (
		GTK_LABEL (gtk_builder_get_object (builder, "bookmark_name_label")),
766
		self->priv->name_field);
767

768 769 770
	/* URI entry */
	self->priv->uri_field = nautilus_entry_new ();
	gtk_widget_show (self->priv->uri_field);
771
	gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (builder, "bookmark_location_placeholder")),
772
			    self->priv->uri_field, TRUE, TRUE, 0);
773

774 775
	gtk_label_set_mnemonic_widget (
		GTK_LABEL (gtk_builder_get_object (builder, "bookmark_location_label")),
776 777 778 779 780 781
		self->priv->uri_field);

	/* buttons */
	self->priv->remove_button = GTK_WIDGET (gtk_builder_get_object (builder, "bookmark_remove_button"));
	self->priv->up_button = GTK_WIDGET (gtk_builder_get_object (builder, "bookmark_up_button"));
	self->priv->down_button = GTK_WIDGET (gtk_builder_get_object (builder, "bookmark_down_button"));
782

783
	g_object_unref (builder);
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814

	/* setup bookmarks list and signals */
	self->priv->bookmarks = nautilus_application_get_bookmarks
		(NAUTILUS_APPLICATION (g_application_get_default ()));
	self->priv->bookmarks_changed_id =
		g_signal_connect (self->priv->bookmarks, "changed",
				  G_CALLBACK (on_bookmark_list_changed), self);

	self->priv->row_changed_id =
		g_signal_connect (self->priv->model, "row-changed",
				  G_CALLBACK (on_row_changed), self);
	self->priv->row_deleted_id =
		g_signal_connect (self->priv->model, "row-deleted",
				  G_CALLBACK (on_row_deleted), self);

        self->priv->row_activated_id =
                g_signal_connect (self->priv->tree_view, "row-activated",
                                  G_CALLBACK (on_row_activated), self);
        self->priv->button_press_id =
                g_signal_connect (self->priv->tree_view, "button-press-event",
                                  G_CALLBACK (on_button_pressed), self);
        self->priv->key_press_id =
                g_signal_connect (self->priv->tree_view, "key-press-event",
                                  G_CALLBACK (on_key_pressed), self);
	self->priv->selection_changed_id =
		g_signal_connect (self->priv->selection, "changed",
				  G_CALLBACK (on_selection_changed), self);

	self->priv->name_changed_id =
		g_signal_connect (self->priv->name_field, "changed",
				  G_CALLBACK (on_name_field_changed), self);
815
	g_signal_connect (self->priv->name_field, "focus-out-event",
816 817 818 819 820 821 822
			  G_CALLBACK (on_text_field_focus_out_event), self);
	g_signal_connect (self->priv->name_field, "activate",
			  G_CALLBACK (name_or_uri_field_activate), self);

	self->priv->uri_changed_id =
		g_signal_connect (self->priv->uri_field, "changed",
				  G_CALLBACK (on_uri_field_changed), self);
823
	g_signal_connect (self->priv->uri_field, "focus-out-event",
824 825 826 827 828 829 830 831 832 833
			  G_CALLBACK (on_text_field_focus_out_event), self);
	g_signal_connect (self->priv->uri_field, "activate",
			  G_CALLBACK (name_or_uri_field_activate), self);

	g_signal_connect (self->priv->remove_button, "clicked",
			  G_CALLBACK (on_remove_button_clicked), self);
	g_signal_connect (self->priv->up_button, "clicked",
			  G_CALLBACK (on_up_button_clicked), self);
	g_signal_connect (self->priv->down_button, "clicked",
			  G_CALLBACK (on_down_button_clicked), self);
834
	
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
	/* Fill in list widget with bookmarks, must be after signals are wired up. */
	repopulate (self);
}

static void
nautilus_bookmarks_window_set_property (GObject *object,
					guint arg_id,
					const GValue *value,
					GParamSpec *pspec)
{
	NautilusBookmarksWindow *self = NAUTILUS_BOOKMARKS_WINDOW (object);

	switch (arg_id) {
	case PROP_PARENT_WINDOW:
		self->priv->parent_window = g_value_get_object (value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, pspec);
		break;
	}
}

static void
nautilus_bookmarks_window_init (NautilusBookmarksWindow *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_BOOKMARKS_WINDOW,
						  NautilusBookmarksWindowPrivate);
}

static void
nautilus_bookmarks_window_class_init (NautilusBookmarksWindowClass *klass)
{
	GObjectClass *oclass = G_OBJECT_CLASS (klass);

	oclass->set_property = nautilus_bookmarks_window_set_property;
	oclass->dispose = nautilus_bookmarks_window_dispose;
	oclass->constructed = nautilus_bookmarks_window_constructed;

	properties[PROP_PARENT_WINDOW] =
		g_param_spec_object ("parent-window",
				     "The NautilusWindow",
				     "The parent NautilusWindow",
				     NAUTILUS_TYPE_WINDOW,
				     G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);

	g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
	g_type_class_add_private (klass, sizeof (NautilusBookmarksWindowPrivate));
}

/**
 * nautilus_bookmarks_window_new:
 * 
 * Create a new bookmark-editing window. 
 * @parent_window: The parent NautilusWindow.
 *
 * Return value: A pointer to the new window.
 **/
GtkWindow *
nautilus_bookmarks_window_new (NautilusWindow *parent_window)
{
	return g_object_new (NAUTILUS_TYPE_BOOKMARKS_WINDOW,
			     "parent-window", parent_window,
			     NULL);
898
}