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

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

31
#include "nautilus-bookmark-parsing.h"
Darin Adler's avatar
Darin Adler committed
32
#include <eel/eel-glib-extensions.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
33 34 35
#include <eel/eel-gtk-macros.h>
#include <eel/eel-string.h>
#include <eel/eel-xml-extensions.h>
Darin Adler's avatar
Darin Adler committed
36 37 38
#include <gtk/gtksignal.h>
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-icon-factory.h>
39 40
#include <libxml/parser.h>
#include <libxml/tree.h>
Darin Adler's avatar
Darin Adler committed
41
#include <stdlib.h>
42

43 44 45 46 47
enum {
	CONTENTS_CHANGED,
	LAST_SIGNAL
};

48 49 50
static guint signals[LAST_SIGNAL];
static char *window_geometry;

51
/* forward declarations */
52 53
static void        append_bookmark_node                 (gpointer              list_element,
							 gpointer              user_data);
54
static void        destroy                              (GtkObject            *object);
55 56 57 58
static const char *nautilus_bookmark_list_get_file_path (NautilusBookmarkList *bookmarks);
static void        nautilus_bookmark_list_load_file     (NautilusBookmarkList *bookmarks);
static void        nautilus_bookmark_list_save_file     (NautilusBookmarkList *bookmarks);
static void        set_window_geometry_internal         (const char           *string);
59 60
static void        stop_monitoring_bookmark             (NautilusBookmarkList *bookmarks,
							 NautilusBookmark     *bookmark);
61 62 63 64

/* Initialization.  */

static void
65
nautilus_bookmark_list_class_init (NautilusBookmarkListClass *class)
66 67 68 69 70
{
	GtkObjectClass *object_class;

	object_class = GTK_OBJECT_CLASS (class);

71 72
	object_class->destroy = destroy;

73
	signals[CONTENTS_CHANGED] =
74 75 76 77
		g_signal_new ("contents_changed",
		              G_TYPE_FROM_CLASS (object_class),
		              G_SIGNAL_RUN_LAST,
		              G_STRUCT_OFFSET (NautilusBookmarkListClass, 
78
						   contents_changed),
79
		              NULL, NULL,
80
		              g_cclosure_marshal_VOID__VOID,
81
		              G_TYPE_NONE, 0);
82 83 84
}

static void
85
nautilus_bookmark_list_init (NautilusBookmarkList *bookmarks)
86
{
87
	nautilus_bookmark_list_load_file (bookmarks);
88 89
}

90
EEL_CLASS_BOILERPLATE (NautilusBookmarkList, nautilus_bookmark_list, GTK_TYPE_OBJECT)
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105
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);
Darin Adler's avatar
Darin Adler committed
106
	eel_g_object_list_free (bookmarks->list);
107 108 109 110 111 112 113 114 115
	bookmarks->list = NULL;
}

static void
destroy (GtkObject *object)
{
	clear (NAUTILUS_BOOKMARK_LIST (object));
}

116 117 118 119 120 121 122 123 124 125
/**
 * append_bookmark_node:
 * 
 * Foreach function; add a single bookmark xml node to a root node.
 * @data: a NautilusBookmark * that is the data of a GList node
 * @user_data: the xmlNodePtr to add a node to.
 **/
static void
append_bookmark_node (gpointer data, gpointer user_data)
{
126
	xmlNodePtr root_node, bookmark_node;
127
	NautilusBookmark *bookmark;
128
	char *icon;
129
	char *bookmark_uri, *bookmark_name;
130

131
	g_assert (NAUTILUS_IS_BOOKMARK (data));
132

133 134
	bookmark = NAUTILUS_BOOKMARK (data);
	root_node = (xmlNodePtr) user_data;	
135

136 137 138
	bookmark_name = nautilus_bookmark_get_name (bookmark);
	bookmark_uri = nautilus_bookmark_get_uri (bookmark);

139
	bookmark_node = xmlNewChild (root_node, NULL, "bookmark", NULL);
140 141 142 143 144
	xmlSetProp (bookmark_node, "name", bookmark_name);
	xmlSetProp (bookmark_node, "uri", bookmark_uri);

	g_free (bookmark_name);
	g_free (bookmark_uri);
145 146 147 148

	icon = nautilus_bookmark_get_icon (bookmark);
	if (icon != NULL) {
		/* Don't bother storing modifier or embedded text for bookmarks. */
149
		xmlSetProp (bookmark_node, "icon_name", icon);
150
		g_free (icon);
151
	}
152 153
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static void
bookmark_in_list_changed_callback (NautilusBookmark *bookmark,
				   NautilusBookmarkList *bookmarks)
{
	g_assert (NAUTILUS_IS_BOOKMARK (bookmark));
	g_assert (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));

	/* Save changes so we'll have the good icon next time. */
	nautilus_bookmark_list_contents_changed (bookmarks);
}				   
				   
static void
stop_monitoring_bookmark (NautilusBookmarkList *bookmarks,
			  NautilusBookmark *bookmark)
{
169 170 171
	g_signal_handlers_disconnect_by_func (bookmark,
					      bookmark_in_list_changed_callback,
					      bookmarks);
172 173 174 175 176 177 178
}

static void
insert_bookmark_internal (NautilusBookmarkList *bookmarks,
			  NautilusBookmark *bookmark,
			  int index)
{
179 180 181 182 183 184
	bookmarks->list = g_list_insert (bookmarks->list, bookmark, index);

	g_signal_connect_object (bookmark, "appearance_changed",
				 G_CALLBACK (bookmark_in_list_changed_callback), bookmarks, 0);
	g_signal_connect_object (bookmark, "contents_changed",
				 G_CALLBACK (bookmark_in_list_changed_callback), bookmarks, 0);
185 186
}

187
/**
188
 * nautilus_bookmark_list_append:
189
 *
190 191
 * Append a bookmark to a bookmark list.
 * @bookmarks: NautilusBookmarkList to append to.
192 193 194
 * @bookmark: Bookmark to append a copy of.
 **/
void
195
nautilus_bookmark_list_append (NautilusBookmarkList *bookmarks, 
196
			       NautilusBookmark *bookmark)
197
{
198
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
199 200
	g_return_if_fail (NAUTILUS_IS_BOOKMARK (bookmark));

201 202 203 204
	insert_bookmark_internal (bookmarks, 
				  nautilus_bookmark_copy (bookmark), 
				  -1);

205
	nautilus_bookmark_list_contents_changed (bookmarks);
206 207
}

208
/**
209
 * nautilus_bookmark_list_contains:
210 211
 *
 * Check whether a bookmark with matching name and url is already in the list.
212
 * @bookmarks: NautilusBookmarkList to check contents of.
213 214 215 216 217
 * @bookmark: NautilusBookmark to match against.
 * 
 * Return value: TRUE if matching bookmark is in list, FALSE otherwise
 **/
gboolean
218
nautilus_bookmark_list_contains (NautilusBookmarkList *bookmarks, 
219
				 NautilusBookmark *bookmark)
220
{
221
	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks), FALSE);
222
	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK (bookmark), FALSE);
223

224 225 226
	return g_list_find_custom (bookmarks->list, 
				   (gpointer)bookmark, 
				   nautilus_bookmark_compare_with) 
227 228 229
		!= NULL;
}

230
/**
231
 * nautilus_bookmark_list_contents_changed:
232
 * 
233 234
 * Save the bookmark list to disk, and emit the contents_changed signal.
 * @bookmarks: NautilusBookmarkList whose contents have been modified.
235 236
 **/ 
void
237
nautilus_bookmark_list_contents_changed (NautilusBookmarkList *bookmarks)
238
{
239
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
240

241
	nautilus_bookmark_list_save_file (bookmarks);
242
	g_signal_emit (bookmarks, 
243
			 signals[CONTENTS_CHANGED], 0);
244 245
}

246

247
/**
248
 * nautilus_bookmark_list_delete_item_at:
249
 * 
250
 * Delete the bookmark at the specified position.
251 252 253 254
 * @bookmarks: the list of bookmarks.
 * @index: index, must be less than length of list.
 **/
void			
255
nautilus_bookmark_list_delete_item_at (NautilusBookmarkList *bookmarks, 
256 257 258 259
				      guint index)
{
	GList *doomed;

260
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
261
	g_return_if_fail (index < g_list_length (bookmarks->list));
262 263 264

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

266
	g_assert (NAUTILUS_IS_BOOKMARK (doomed->data));
267
	stop_monitoring_bookmark (bookmarks, NAUTILUS_BOOKMARK (doomed->data));
268
	g_object_unref (doomed->data);
269
	
270
	g_list_free_1 (doomed);
271
	
272
	nautilus_bookmark_list_contents_changed (bookmarks);
273 274
}

275 276 277 278 279 280 281 282 283 284 285 286 287
/**
 * nautilus_bookmark_list_delete_items_with_uri:
 * 
 * Delete all bookmarks with the given uri.
 * @bookmarks: the list of bookmarks.
 * @uri: The uri to match.
 **/
void			
nautilus_bookmark_list_delete_items_with_uri (NautilusBookmarkList *bookmarks, 
				      	      const char *uri)
{
	GList *node, *next;
	gboolean list_changed;
288
	char *bookmark_uri;
289 290 291 292 293 294 295 296

	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
	g_return_if_fail (uri != NULL);

	list_changed = FALSE;
	for (node = bookmarks->list; node != NULL;  node = next) {
		next = node->next;

297
		bookmark_uri = nautilus_bookmark_get_uri (NAUTILUS_BOOKMARK (node->data));
Ramiro Estrugo's avatar
Ramiro Estrugo committed
298
		if (eel_strcmp (bookmark_uri, uri) == 0) {
299
			bookmarks->list = g_list_remove_link (bookmarks->list, node);
300
			stop_monitoring_bookmark (bookmarks, NAUTILUS_BOOKMARK (node->data));
301
			g_object_unref (node->data);
302
			g_list_free_1 (node);
303 304
			list_changed = TRUE;
		}
305
		g_free (bookmark_uri);
306 307 308 309 310 311 312
	}

	if (list_changed) {
		nautilus_bookmark_list_contents_changed (bookmarks);
	}
}

313
static const char *
314
nautilus_bookmark_list_get_file_path (NautilusBookmarkList *bookmarks)
315 316
{
	/* currently hardwired */
317 318

	static char *file_path = NULL;
319
	char *user_directory;
320

321
	if (file_path == NULL) {
322
		user_directory = nautilus_get_user_directory ();
323
		file_path = g_build_filename (user_directory, "bookmarks.xml", NULL);
324
		g_free (user_directory);
325 326 327 328 329
	}

	return file_path;
}

330
/**
331
 * nautilus_bookmark_list_get_window_geometry:
332
 * 
333 334
 * Get a string representing the bookmark_list's window's geometry.
 * This is the value set earlier by nautilus_bookmark_list_set_window_geometry.
335 336 337
 * @bookmarks: the list of bookmarks associated with the window.
 * Return value: string representation of window's geometry, suitable for
 * passing to gnome_parse_geometry(), or NULL if
338
 * no window geometry has yet been saved for this bookmark list.
339
 **/
340
const char *
341
nautilus_bookmark_list_get_window_geometry (NautilusBookmarkList *bookmarks)
342 343 344 345
{
	return window_geometry;
}

346
/**
347
 * nautilus_bookmark_list_insert_item:
348
 * 
349
 * Insert a bookmark at a specified position.
350 351 352 353 354
 * @bookmarks: the list of bookmarks.
 * @index: the position to insert the bookmark at.
 * @new_bookmark: the bookmark to insert a copy of.
 **/
void			
355
nautilus_bookmark_list_insert_item (NautilusBookmarkList *bookmarks,
356
				    NautilusBookmark* new_bookmark,
357
				    guint index)
358
{
359
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
360
	g_return_if_fail (index <= g_list_length (bookmarks->list));
361

362 363 364
	insert_bookmark_internal (bookmarks, 
				  nautilus_bookmark_copy (new_bookmark), 
				  index);
365

366
	nautilus_bookmark_list_contents_changed (bookmarks);
367 368
}

369
/**
370
 * nautilus_bookmark_list_item_at:
371 372 373 374 375 376 377
 * 
 * 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.
 **/
378
NautilusBookmark *
379
nautilus_bookmark_list_item_at (NautilusBookmarkList *bookmarks, guint index)
380
{
381
	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks), NULL);
382
	g_return_val_if_fail (index < g_list_length (bookmarks->list), NULL);
383

384
	return NAUTILUS_BOOKMARK (g_list_nth_data (bookmarks->list, index));
385 386 387
}

/**
388
 * nautilus_bookmark_list_length:
389 390 391 392 393 394 395
 * 
 * Get the number of bookmarks in the list.
 * @bookmarks: the list of bookmarks.
 * 
 * Return value: the length of the bookmark list.
 **/
guint
396
nautilus_bookmark_list_length (NautilusBookmarkList *bookmarks)
397
{
398
	g_return_val_if_fail (NAUTILUS_IS_BOOKMARK_LIST(bookmarks), 0);
399

400
	return g_list_length (bookmarks->list);
401 402
}

403
/**
404
 * nautilus_bookmark_list_load_file:
405 406 407 408 409
 * 
 * Reads bookmarks from file, clobbering contents in memory.
 * @bookmarks: the list of bookmarks to fill with file contents.
 **/
static void
410
nautilus_bookmark_list_load_file (NautilusBookmarkList *bookmarks)
411
{
412 413
	xmlDocPtr doc;
	xmlNodePtr node;
414 415

	/* Wipe out old list. */
416
	clear (bookmarks);
417

418 419 420 421 422
	if (!g_file_test (nautilus_bookmark_list_get_file_path (bookmarks),
			  G_FILE_TEST_EXISTS)) {
		return;
	}

423
	/* Read new list from file */
424
	doc = xmlParseFile (nautilus_bookmark_list_get_file_path (bookmarks));
Ramiro Estrugo's avatar
Ramiro Estrugo committed
425
	for (node = eel_xml_get_root_children (doc);
426 427
	     node != NULL;
	     node = node->next) {
428

429 430 431 432
		if (node->type != XML_ELEMENT_NODE) {
			continue;
		}

433
		if (strcmp (node->name, "bookmark") == 0) {
434
			insert_bookmark_internal (bookmarks, 
435
						  nautilus_bookmark_new_from_node (node), 
436
						  -1);
437
		} else if (strcmp (node->name, "window") == 0) {
438
			xmlChar *geometry_string;
439
			
440 441 442
			geometry_string = xmlGetProp (node, "geometry");
			set_window_geometry_internal (geometry_string);
			xmlFree (geometry_string);
443 444
		}
	}
445
	
446 447 448
	xmlFreeDoc(doc);
}

449
/**
450
 * nautilus_bookmark_list_new:
451
 * 
452
 * Create a new bookmark_list, with contents read from disk.
453 454 455
 * 
 * Return value: A pointer to the new widget.
 **/
456 457
NautilusBookmarkList *
nautilus_bookmark_list_new (void)
458
{
459 460
	NautilusBookmarkList *list;

461
	list = NAUTILUS_BOOKMARK_LIST (g_object_new (NAUTILUS_TYPE_BOOKMARK_LIST, NULL));
462
	g_object_ref (list);
463 464 465
	gtk_object_sink (GTK_OBJECT (list));

	return list;
466
}
467 468

/**
469
 * nautilus_bookmark_list_save_file:
470 471 472 473 474
 * 
 * Save bookmarks to disk.
 * @bookmarks: the list of bookmarks to save.
 **/
static void
475
nautilus_bookmark_list_save_file (NautilusBookmarkList *bookmarks)
476
{
477 478
	xmlDocPtr doc;
	xmlNodePtr root, node;
479

480 481 482
	doc = xmlNewDoc ("1.0");
	root = xmlNewDocNode (doc, NULL, "bookmarks", NULL);
	xmlDocSetRootElement (doc, root);
483

484
	/* save window position */
485 486
	if (window_geometry != NULL) {
		node = xmlNewChild (root, NULL, "window", NULL);
487 488 489 490
		xmlSetProp (node, "geometry", window_geometry);
	}

	/* save bookmarks */
491
	g_list_foreach (bookmarks->list, append_bookmark_node, root);
492

493
	xmlSaveFile (nautilus_bookmark_list_get_file_path (bookmarks), doc);
494
	xmlFreeDoc (doc);
495
}
496 497

/**
498
 * nautilus_bookmark_list_set_window_geometry:
499 500 501 502 503 504 505 506
 * 
 * Set a bookmarks window's geometry (position & size), in string form. This is
 * stored to disk by this class, and can be retrieved later in
 * the same session or in a future session.
 * @bookmarks: the list of bookmarks associated with the window.
 * @geometry: the new window geometry string.
 **/
void
507 508
nautilus_bookmark_list_set_window_geometry (NautilusBookmarkList *bookmarks,
					    const char *geometry)
509
{
510
	g_return_if_fail (NAUTILUS_IS_BOOKMARK_LIST (bookmarks));
511 512 513 514
	g_return_if_fail (geometry != NULL);

	set_window_geometry_internal (geometry);

515
	nautilus_bookmark_list_save_file(bookmarks);
516 517 518
}

static void
519
set_window_geometry_internal (const char *string)
520 521 522
{
	g_free (window_geometry);
	window_geometry = g_strdup (string);
523
}