nautilus-bookmark-list.c 17.5 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-bookmark-list.c - implementation of centralized list of bookmarks.
 */
26

27
#include <config.h>
28 29
#include "nautilus-bookmark-list.h"

Alexander Larsson's avatar
Alexander Larsson committed
30 31
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-file.h>
32
#include <libnautilus-private/nautilus-icon-names.h>
33

34
#include <gio/gio.h>
35
#include <string.h>
36

37
#define MAX_BOOKMARK_LENGTH 80
38 39
#define LOAD_JOB 1
#define SAVE_JOB 2
40

41
enum {
42
	CHANGED,
43 44 45
	LAST_SIGNAL
};

46 47
static guint signals[LAST_SIGNAL];

48
/* forward declarations */
49

50 51
static void        nautilus_bookmark_list_load_file     (NautilusBookmarkList *bookmarks);
static void        nautilus_bookmark_list_save_file     (NautilusBookmarkList *bookmarks);
Alexander Larsson's avatar
Alexander Larsson committed
52

53
G_DEFINE_TYPE(NautilusBookmarkList, nautilus_bookmark_list, G_TYPE_OBJECT)
54 55 56 57 58

static NautilusBookmark *
new_bookmark_from_uri (const char *uri, const char *label)
{
	NautilusBookmark *new_bookmark;
Alexander Larsson's avatar
Alexander Larsson committed
59
	GFile *location;
60

Alexander Larsson's avatar
Alexander Larsson committed
61 62 63 64 65 66
	location = NULL;
	if (uri) {
		location = g_file_new_for_uri (uri);
	}
	
	new_bookmark = NULL;
67

68
	if (location) {
69
		new_bookmark = nautilus_bookmark_new (location, label);
70
		g_object_unref (location);
71
	}
72

Alexander Larsson's avatar
Alexander Larsson committed
73
	return new_bookmark;
74 75
}

Alexander Larsson's avatar
Alexander Larsson committed
76
static GFile *
77
nautilus_bookmark_list_get_legacy_file (void)
78
{
Alexander Larsson's avatar
Alexander Larsson committed
79 80 81 82 83 84 85 86 87 88 89
	char *filename;
	GFile *file;

	filename = g_build_filename (g_get_home_dir (),
				     ".gtk-bookmarks",
				     NULL);
	file = g_file_new_for_path (filename);

	g_free (filename);

	return file;
90
}
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
static GFile *
nautilus_bookmark_list_get_file (void)
{
	char *filename;
	GFile *file;

	filename = g_build_filename (g_get_user_config_dir (),
				     "gtk-3.0",
				     "bookmarks",
				     NULL);
	file = g_file_new_for_path (filename);

	g_free (filename);

	return file;
}

109 110 111
/* Initialization.  */

static void
112 113 114 115 116 117
bookmark_in_list_changed_callback (NautilusBookmark     *bookmark,
				   NautilusBookmarkList *bookmarks)
{
	g_assert (NAUTILUS_IS_BOOKMARK (bookmark));
	g_assert (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));

118
	/* save changes to the list */
119 120 121
	nautilus_bookmark_list_save_file (bookmarks);
}

122 123
static void
bookmark_in_list_notify (GObject *object,
124 125
			 GParamSpec *pspec,
			 NautilusBookmarkList *bookmarks)
126 127 128 129 130
{
	/* emit the changed signal without saving, as only appearance properties changed */
	g_signal_emit (bookmarks, signals[CHANGED], 0);
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
static void
stop_monitoring_bookmark (NautilusBookmarkList *bookmarks,
			  NautilusBookmark     *bookmark)
{
	g_signal_handlers_disconnect_by_func (bookmark,
					      bookmark_in_list_changed_callback,
					      bookmarks);
}

static void
stop_monitoring_one (gpointer data, gpointer user_data)
{
	g_assert (NAUTILUS_IS_BOOKMARK (data));
	g_assert (NAUTILUS_IS_BOOKMARK_LIST (user_data));

	stop_monitoring_bookmark (NAUTILUS_BOOKMARK_LIST (user_data), 
				  NAUTILUS_BOOKMARK (data));
}

static void
clear (NautilusBookmarkList *bookmarks)
{
	g_list_foreach (bookmarks->list, stop_monitoring_one, bookmarks);
154
	g_list_free_full (bookmarks->list, g_object_unref);
155 156 157 158 159
	bookmarks->list = NULL;
}

static void
do_finalize (GObject *object)
160
{
161 162 163 164
	if (NAUTILUS_BOOKMARK_LIST (object)->monitor != NULL) {
		g_file_monitor_cancel (NAUTILUS_BOOKMARK_LIST (object)->monitor);
		NAUTILUS_BOOKMARK_LIST (object)->monitor = NULL;
	}
165

166 167
	g_queue_free (NAUTILUS_BOOKMARK_LIST (object)->pending_ops);

168 169 170 171 172 173 174 175 176
	clear (NAUTILUS_BOOKMARK_LIST (object));

	G_OBJECT_CLASS (nautilus_bookmark_list_parent_class)->finalize (object);
}

static void
nautilus_bookmark_list_class_init (NautilusBookmarkListClass *class)
{
	GObjectClass *object_class = G_OBJECT_CLASS (class);
177

178
	object_class->finalize = do_finalize;
179

180 181
	signals[CHANGED] =
		g_signal_new ("changed",
182 183 184
		              G_TYPE_FROM_CLASS (object_class),
		              G_SIGNAL_RUN_LAST,
		              G_STRUCT_OFFSET (NautilusBookmarkListClass, 
185
					       changed),
186
		              NULL, NULL,
187
		              g_cclosure_marshal_VOID__VOID,
188
		              G_TYPE_NONE, 0);
189 190
}

Alexander Larsson's avatar
Alexander Larsson committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204
static void
bookmark_monitor_changed_cb (GFileMonitor      *monitor,
			     GFile             *child,
			     GFile             *other_file,
			     GFileMonitorEvent  eflags,
			     gpointer           user_data)
{
	if (eflags == G_FILE_MONITOR_EVENT_CHANGED ||
	    eflags == G_FILE_MONITOR_EVENT_CREATED) {
		g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (NAUTILUS_BOOKMARK_LIST (user_data)));
		nautilus_bookmark_list_load_file (NAUTILUS_BOOKMARK_LIST (user_data));
	}
}

205
static void
206
nautilus_bookmark_list_init (NautilusBookmarkList *bookmarks)
Alexander Larsson's avatar
Alexander Larsson committed
207 208
{
	GFile *file;
209

210 211
	bookmarks->pending_ops = g_queue_new ();

212
	nautilus_bookmark_list_load_file (bookmarks);
Alexander Larsson's avatar
Alexander Larsson committed
213 214

	file = nautilus_bookmark_list_get_file ();
Alexander Larsson's avatar
Alexander Larsson committed
215
	bookmarks->monitor = g_file_monitor_file (file, 0, NULL, NULL);
216 217
	g_file_monitor_set_rate_limit (bookmarks->monitor, 1000);

Alexander Larsson's avatar
Alexander Larsson committed
218 219 220 221
	g_signal_connect (bookmarks->monitor, "changed",
			  G_CALLBACK (bookmark_monitor_changed_cb), bookmarks);

	g_object_unref (file);
222 223
}

224 225
static void
insert_bookmark_internal (NautilusBookmarkList *bookmarks,
Alexander Larsson's avatar
Alexander Larsson committed
226 227
			  NautilusBookmark     *bookmark,
			  int                   index)
228
{
229 230
	bookmarks->list = g_list_insert (bookmarks->list, bookmark, index);

231
	g_signal_connect_object (bookmark, "contents-changed",
232
				 G_CALLBACK (bookmark_in_list_changed_callback), bookmarks, 0);
233 234 235 236
	g_signal_connect_object (bookmark, "notify::icon",
				 G_CALLBACK (bookmark_in_list_notify), bookmarks, 0);
	g_signal_connect_object (bookmark, "notify::name",
				 G_CALLBACK (bookmark_in_list_notify), bookmarks, 0);
237 238
}

239
/**
240
 * nautilus_bookmark_list_append:
241
 *
242 243
 * Append a bookmark to a bookmark list.
 * @bookmarks: NautilusBookmarkList to append to.
244 245 246
 * @bookmark: Bookmark to append a copy of.
 **/
void
247
nautilus_bookmark_list_append (NautilusBookmarkList *bookmarks, 
Alexander Larsson's avatar
Alexander Larsson committed
248
			       NautilusBookmark     *bookmark)
249
{
250
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
251 252
	g_return_if_fail (NAUTILUS_IS_BOOKMARK (bookmark));

253 254 255 256 257
	if (g_list_find_custom (bookmarks->list, bookmark,
				nautilus_bookmark_compare_with) != NULL) {
		return;
	}

Cosimo Cecchi's avatar
Cosimo Cecchi committed
258
	insert_bookmark_internal (bookmarks, g_object_ref (bookmark), -1);
259
	nautilus_bookmark_list_save_file (bookmarks);
260 261
}

262
/**
263
 * nautilus_bookmark_list_delete_item_at:
264
 * 
265
 * Delete the bookmark at the specified position.
266 267 268
 * @bookmarks: the list of bookmarks.
 * @index: index, must be less than length of list.
 **/
Alexander Larsson's avatar
Alexander Larsson committed
269
void
270
nautilus_bookmark_list_delete_item_at (NautilusBookmarkList *bookmarks, 
Alexander Larsson's avatar
Alexander Larsson committed
271
				       guint                 index)
272 273 274
{
	GList *doomed;

275
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
276
	g_return_if_fail (index < g_list_length (bookmarks->list));
277 278 279

	doomed = g_list_nth (bookmarks->list, index);
	bookmarks->list = g_list_remove_link (bookmarks->list, doomed);
280

281
	g_assert (NAUTILUS_IS_BOOKMARK (doomed->data));
282
	stop_monitoring_bookmark (bookmarks, NAUTILUS_BOOKMARK (doomed->data));
283
	g_object_unref (doomed->data);
Alexander Larsson's avatar
Alexander Larsson committed
284

285
	g_list_free_1 (doomed);
Alexander Larsson's avatar
Alexander Larsson committed
286

287
	nautilus_bookmark_list_save_file (bookmarks);
288 289
}

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
/**
 * nautilus_bookmark_list_move_item:
 *
 * Move the item from the given position to the destination.
 * @index: the index of the first bookmark.
 * @destination: the index of the second bookmark.
 **/
void
nautilus_bookmark_list_move_item (NautilusBookmarkList *bookmarks,
				  guint index,
				  guint destination)
{
	GList *bookmark_item;

	if (index == destination) {
		return;
	}

	bookmark_item = g_list_nth (bookmarks->list, index);
	bookmarks->list = g_list_remove_link (bookmarks->list,
					      bookmark_item);

312 313 314
	bookmarks->list = g_list_insert (bookmarks->list,
					 bookmark_item->data,
					 destination);
315

316
	nautilus_bookmark_list_save_file (bookmarks);
317 318
}

319
/**
320
 * nautilus_bookmark_list_insert_item:
321
 * 
322
 * Insert a bookmark at a specified position.
323 324 325 326
 * @bookmarks: the list of bookmarks.
 * @index: the position to insert the bookmark at.
 * @new_bookmark: the bookmark to insert a copy of.
 **/
Alexander Larsson's avatar
Alexander Larsson committed
327
void
328
nautilus_bookmark_list_insert_item (NautilusBookmarkList *bookmarks,
Alexander Larsson's avatar
Alexander Larsson committed
329 330
				    NautilusBookmark     *new_bookmark,
				    guint                 index)
331
{
332
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
333
	g_return_if_fail (index <= g_list_length (bookmarks->list));
334

Cosimo Cecchi's avatar
Cosimo Cecchi committed
335
	insert_bookmark_internal (bookmarks, g_object_ref (new_bookmark), index);
336
	nautilus_bookmark_list_save_file (bookmarks);
337 338
}

339
/**
340
 * nautilus_bookmark_list_item_at:
341 342 343 344 345 346 347
 * 
 * Get the bookmark at the specified position.
 * @bookmarks: the list of bookmarks.
 * @index: index, must be less than length of list.
 * 
 * Return value: the bookmark at position @index in @bookmarks.
 **/
348
NautilusBookmark *
349
nautilus_bookmark_list_item_at (NautilusBookmarkList *bookmarks, guint index)
350
{
351
	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks), NULL);
352
	g_return_val_if_fail (index < g_list_length (bookmarks->list), NULL);
353

354
	return NAUTILUS_BOOKMARK (g_list_nth_data (bookmarks->list, index));
355 356
}

357
/**
358
 * nautilus_bookmark_list_item_with_location:
359
 *
360
 * Get the bookmark with the specified location, if any
361
 * @bookmarks: the list of bookmarks.
362
 * @location: a #GFile
363
 * @index: location where to store bookmark index, or %NULL
364
 *
365
 * Return value: the bookmark with location @location, or %NULL.
366 367
 **/
NautilusBookmark *
368
nautilus_bookmark_list_item_with_location (NautilusBookmarkList *bookmarks,
369 370
					   GFile                *location,
					   guint                *index)
371 372
{
	GList *node;
373
	GFile *bookmark_location;
374 375
	NautilusBookmark *bookmark;
	gboolean found = FALSE;
376
	guint idx;
377 378

	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks), NULL);
379
	g_return_val_if_fail (G_IS_FILE (location), NULL);
380

381 382
	idx = 0;

383 384
	for (node = bookmarks->list; node != NULL; node = node->next) {
		bookmark = node->data;
385
		bookmark_location = nautilus_bookmark_get_location (bookmark);
386

387
		if (g_file_equal (location, bookmark_location)) {
388 389 390
			found = TRUE;
		}

391
		g_object_unref (bookmark_location);
392 393

		if (found) {
394 395 396
			if (index) {
				*index = idx;
			}
397 398
			return bookmark;
		}
399 400

		idx++;
401 402 403 404 405
	}

	return NULL;
}

406
/**
407
 * nautilus_bookmark_list_length:
408 409 410 411 412 413 414
 * 
 * Get the number of bookmarks in the list.
 * @bookmarks: the list of bookmarks.
 * 
 * Return value: the length of the bookmark list.
 **/
guint
415
nautilus_bookmark_list_length (NautilusBookmarkList *bookmarks)
416
{
417
	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK_LIST(bookmarks), 0);
418

419
	return g_list_length (bookmarks->list);
420 421
}

422
static void
423 424 425 426
process_next_op (NautilusBookmarkList *bookmarks);

static void
op_processed_cb (NautilusBookmarkList *self)
427
{
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
	g_queue_pop_tail (self->pending_ops);

	if (!g_queue_is_empty (self->pending_ops)) {
		process_next_op (self);
	}
}

static void
load_callback (GObject *source,
	       GAsyncResult *res,
	       gpointer user_data)
{
	NautilusBookmarkList *self = NAUTILUS_BOOKMARK_LIST (source);
	gchar *contents;
	char **lines;
	int i;

	contents = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));

	if (contents == NULL) {
448
		op_processed_cb (self);
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
		return;
	}

	lines = g_strsplit (contents, "\n", -1);
	for (i = 0; lines[i]; i++) {
		/* Ignore empty or invalid lines that cannot be parsed properly */
		if (lines[i][0] != '\0' && lines[i][0] != ' ') {
			/* gtk 2.7/2.8 might have labels appended to bookmarks which are separated by a space */
			/* we must seperate the bookmark uri and the potential label */
			char *space, *label;

			label = NULL;
			space = strchr (lines[i], ' ');
			if (space) {
				*space = '\0';
				label = g_strdup (space + 1);
465
			}
466

467 468 469
			insert_bookmark_internal (self, new_bookmark_from_uri (lines[i], label), -1);
			g_free (label);
		}
Alexander Larsson's avatar
Alexander Larsson committed
470
	}
471 472 473 474 475

	g_signal_emit (self, signals[CHANGED], 0);
	op_processed_cb (self);

	g_strfreev (lines);
476 477 478
}

static void
479 480 481
load_io_thread (GSimpleAsyncResult *result,
		GObject *object,
		GCancellable *cancellable)
482 483
{
	GFile *file;
484 485
	gchar *contents;
	GError *error = NULL;
486 487

	file = nautilus_bookmark_list_get_file ();
488
	if (!g_file_query_exists (file, NULL)) {
William Jon McCann's avatar
William Jon McCann committed
489
		g_object_unref (file);
490 491
		file = nautilus_bookmark_list_get_legacy_file ();
	}
492

493
	g_file_load_contents (file, NULL, &contents, NULL, NULL, &error);
William Jon McCann's avatar
William Jon McCann committed
494
	g_object_unref (file);
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

	if (error != NULL) {
		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
			g_warning ("Could not load bookmark file: %s\n", error->message);
		}
		g_error_free (error);
	} else {
		g_simple_async_result_set_op_res_gpointer (result, contents, g_free);
	}
}

static void
load_file_async (NautilusBookmarkList *self)
{
	GSimpleAsyncResult *result;

511 512 513
	/* Wipe out old list. */
	clear (self);

514 515 516 517 518 519
	result = g_simple_async_result_new (G_OBJECT (self), 
					    load_callback, NULL, NULL);
	g_simple_async_result_run_in_thread (result, load_io_thread,
					     G_PRIORITY_DEFAULT, NULL);
	g_object_unref (result);
}
Alexander Larsson's avatar
Alexander Larsson committed
520

521 522 523 524 525 526 527 528 529 530 531
static void
save_callback (GObject *source,
	       GAsyncResult *res,
	       gpointer user_data)
{
	NautilusBookmarkList *self = NAUTILUS_BOOKMARK_LIST (source);
	GFile *file;

	/* re-enable bookmark file monitoring */
	file = nautilus_bookmark_list_get_file ();
	self->monitor = g_file_monitor_file (file, 0, NULL, NULL);
Alexander Larsson's avatar
Alexander Larsson committed
532
	g_object_unref (file);
533 534 535 536 537 538

	g_file_monitor_set_rate_limit (self->monitor, 1000);
	g_signal_connect (self->monitor, "changed",
			  G_CALLBACK (bookmark_monitor_changed_cb), self);

	op_processed_cb (self);
539 540
}

541
static void
542 543 544
save_io_thread (GSimpleAsyncResult *result,
		GObject *object,
		GCancellable *cancellable)
545
{
546 547
	gchar *contents, *path;
	GFile *parent, *file;
548
	GError *error = NULL;
549

550 551 552 553 554 555 556 557 558 559 560 561
	file = nautilus_bookmark_list_get_file ();
	parent = g_file_get_parent (file);
	path = g_file_get_path (parent);
	g_mkdir_with_parents (path, 0700);
	g_free (path);
	g_object_unref (parent);

	contents = g_simple_async_result_get_op_res_gpointer (result);
	g_file_replace_contents (file, 
				 contents, strlen (contents),
				 NULL, FALSE, 0, NULL,
				 NULL, &error);
562

563 564 565 566 567 568 569
	if (error != NULL) {
		g_warning ("Unable to replace contents of the bookmarks file: %s",
			   error->message);
		g_error_free (error);
	}

	g_object_unref (file);
570
}
571 572

static void
573
save_file_async (NautilusBookmarkList *self)
574
{
575
	GSimpleAsyncResult *result;
576
	GString *bookmark_string;
577 578 579 580
	gchar *contents;
	GList *l;

	bookmark_string = g_string_new (NULL);
Alexander Larsson's avatar
Alexander Larsson committed
581 582

	/* temporarily disable bookmark file monitoring when writing file */
583 584 585
	if (self->monitor != NULL) {
		g_file_monitor_cancel (self->monitor);
		self->monitor = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
586
	}
587

588
	for (l = self->list; l; l = l->next) {
Alexander Larsson's avatar
Alexander Larsson committed
589 590 591 592 593 594
		NautilusBookmark *bookmark;

		bookmark = NAUTILUS_BOOKMARK (l->data);

		/* make sure we save label if it has one for compatibility with GTK 2.7 and 2.8 */
		if (nautilus_bookmark_get_has_custom_name (bookmark)) {
595 596
			const char *label;
			char *uri;
Alexander Larsson's avatar
Alexander Larsson committed
597 598
			label = nautilus_bookmark_get_name (bookmark);
			uri = nautilus_bookmark_get_uri (bookmark);
599 600
			g_string_append_printf (bookmark_string,
						"%s %s\n", uri, label);
Alexander Larsson's avatar
Alexander Larsson committed
601 602 603 604
			g_free (uri);
		} else {
			char *uri;
			uri = nautilus_bookmark_get_uri (bookmark);
605
			g_string_append_printf (bookmark_string, "%s\n", uri);
Alexander Larsson's avatar
Alexander Larsson committed
606
			g_free (uri);
607
		}
608
	}
609

610 611 612 613
	result = g_simple_async_result_new (G_OBJECT (self),
					    save_callback, NULL, NULL);
	contents = g_string_free (bookmark_string, FALSE);
	g_simple_async_result_set_op_res_gpointer (result, contents, g_free);
614

615 616 617
	g_simple_async_result_run_in_thread (result, save_io_thread,
					     G_PRIORITY_DEFAULT, NULL);
	g_object_unref (result);
618 619 620 621 622 623
}

static void
process_next_op (NautilusBookmarkList *bookmarks)
{
	gint op;
624

625
	op = GPOINTER_TO_INT (g_queue_peek_tail (bookmarks->pending_ops));
626

627
	if (op == LOAD_JOB) {
628
		load_file_async (bookmarks);
629
	} else {
630
		save_file_async (bookmarks);
631 632
	}
}
633

634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
/**
 * nautilus_bookmark_list_load_file:
 * 
 * Reads bookmarks from file, clobbering contents in memory.
 * @bookmarks: the list of bookmarks to fill with file contents.
 **/
static void
nautilus_bookmark_list_load_file (NautilusBookmarkList *bookmarks)
{
	g_queue_push_head (bookmarks->pending_ops, GINT_TO_POINTER (LOAD_JOB));

	if (g_queue_get_length (bookmarks->pending_ops) == 1) {
		process_next_op (bookmarks);
	}
}

/**
 * nautilus_bookmark_list_save_file:
 * 
 * Save bookmarks to disk.
 * @bookmarks: the list of bookmarks to save.
 **/
static void
nautilus_bookmark_list_save_file (NautilusBookmarkList *bookmarks)
{
659
	g_signal_emit (bookmarks, signals[CHANGED], 0);
660 661 662 663 664 665 666 667

	g_queue_push_head (bookmarks->pending_ops, GINT_TO_POINTER (SAVE_JOB));

	if (g_queue_get_length (bookmarks->pending_ops) == 1) {
		process_next_op (bookmarks);
	}
}

668 669 670 671
gboolean
nautilus_bookmark_list_can_bookmark_location (NautilusBookmarkList *list,
					      GFile                *location)
{
672 673 674
	NautilusBookmark *bookmark;
	gboolean is_builtin;

675
	if (nautilus_bookmark_list_item_with_location (list, location, NULL)) {
676 677 678
		return FALSE;
	}

679 680 681 682 683 684 685 686 687
	if (nautilus_is_home_directory (location)) {
		return FALSE;
	}

	bookmark = nautilus_bookmark_new (location, NULL);
	is_builtin = nautilus_bookmark_get_is_builtin (bookmark);
	g_object_unref (bookmark);

	return !is_builtin;
688 689
}

690 691 692 693 694 695 696 697 698 699 700 701 702 703
/**
 * nautilus_bookmark_list_new:
 * 
 * Create a new bookmark_list, with contents read from disk.
 * 
 * Return value: A pointer to the new widget.
 **/
NautilusBookmarkList *
nautilus_bookmark_list_new (void)
{
	NautilusBookmarkList *list;

	list = NAUTILUS_BOOKMARK_LIST (g_object_new (NAUTILUS_TYPE_BOOKMARK_LIST, NULL));

704
	return list;
705
}