evolution-3.6.4/widgets/menus/gal-view-collection.c

No issues found

  1 /*
  2  * This program is free software; you can redistribute it and/or
  3  * modify it under the terms of the GNU Lesser General Public
  4  * License as published by the Free Software Foundation; either
  5  * version 2 of the License, or (at your option) version 3.
  6  *
  7  * This program is distributed in the hope that it will be useful,
  8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 10  * Lesser General Public License for more details.
 11  *
 12  * You should have received a copy of the GNU Lesser General Public
 13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 14  *
 15  *
 16  * Authors:
 17  *		Chris Lahey <clahey@ximian.com>
 18  *
 19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 20  *
 21  */
 22 
 23 #ifdef HAVE_CONFIG_H
 24 #include <config.h>
 25 #endif
 26 
 27 #include <ctype.h>
 28 #include <string.h>
 29 #include <errno.h>
 30 
 31 #include <libxml/parser.h>
 32 #include <libedataserver/libedataserver.h>
 33 
 34 #include <glib/gi18n.h>
 35 #include "e-util/e-util.h"
 36 #include "libevolution-utils/e-xml-utils.h"
 37 #include "e-util/e-unicode.h"
 38 
 39 #include "gal-view-collection.h"
 40 
 41 G_DEFINE_TYPE (GalViewCollection, gal_view_collection, G_TYPE_OBJECT)
 42 
 43 #define d(x)
 44 
 45 enum {
 46 	DISPLAY_VIEW,
 47 	CHANGED,
 48 	LAST_SIGNAL
 49 };
 50 
 51 static guint gal_view_collection_signals[LAST_SIGNAL] = { 0, };
 52 
 53 /**
 54  * gal_view_collection_display_view:
 55  * @collection: The GalViewCollection to send the signal on.
 56  * @view: The view to display.
 57  *
 58  */
 59 void
 60 gal_view_collection_display_view (GalViewCollection *collection,
 61                                   GalView *view)
 62 {
 63 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
 64 	g_return_if_fail (GAL_IS_VIEW (view));
 65 
 66 	g_signal_emit (
 67 		collection,
 68 		gal_view_collection_signals[DISPLAY_VIEW], 0,
 69 		view);
 70 }
 71 
 72 static void
 73 gal_view_collection_changed (GalViewCollection *collection)
 74 {
 75 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
 76 
 77 	g_signal_emit (
 78 		collection,
 79 		gal_view_collection_signals[CHANGED], 0);
 80 }
 81 
 82 static void
 83 gal_view_collection_item_free (GalViewCollectionItem *item)
 84 {
 85 	g_free (item->id);
 86 	if (item->view) {
 87 		if (item->view_changed_id)
 88 			g_signal_handler_disconnect (
 89 				item->view,
 90 				item->view_changed_id);
 91 		g_object_unref (item->view);
 92 	}
 93 	g_free (item);
 94 }
 95 
 96 static gchar *
 97 gal_view_generate_string (GalViewCollection *collection,
 98                           GalView *view,
 99                           gint which)
100 {
101 	gchar *ret_val;
102 	gchar *pointer;
103 
104 	if (which == 1)
105 		ret_val = g_strdup (gal_view_get_title (view));
106 	else
107 		ret_val = g_strdup_printf ("%s_%d", gal_view_get_title (view), which);
108 	for (pointer = ret_val; *pointer; pointer = g_utf8_next_char (pointer)) {
109 		if (!g_unichar_isalnum (g_utf8_get_char (pointer))) {
110 			gchar *ptr = pointer;
111 			for (; ptr < g_utf8_next_char (pointer); *ptr = '_', ptr++)
112 				;
113 		}
114 	}
115 	return ret_val;
116 }
117 
118 static gint
119 gal_view_check_string (GalViewCollection *collection,
120                        gchar *string)
121 {
122 	gint i;
123 
124 	if (!strcmp (string, "current_view"))
125 		return FALSE;
126 
127 	for (i = 0; i < collection->view_count; i++) {
128 		if (!strcmp (string, collection->view_data[i]->id))
129 			return FALSE;
130 	}
131 	for (i = 0; i < collection->removed_view_count; i++) {
132 		if (!strcmp (string, collection->removed_view_data[i]->id))
133 			return FALSE;
134 	}
135 	return TRUE;
136 }
137 
138 static gchar *
139 gal_view_generate_id (GalViewCollection *collection,
140                       GalView *view)
141 {
142 	gint i;
143 	for (i = 1; TRUE; i++) {
144 		gchar *try;
145 
146 		try = gal_view_generate_string (collection, view, i);
147 		if (gal_view_check_string (collection, try))
148 			return try;
149 		g_free (try);
150 	}
151 }
152 
153 static void
154 gal_view_collection_dispose (GObject *object)
155 {
156 	GalViewCollection *collection = GAL_VIEW_COLLECTION (object);
157 	gint i;
158 
159 	for (i = 0; i < collection->view_count; i++) {
160 		gal_view_collection_item_free (collection->view_data[i]);
161 	}
162 	g_free (collection->view_data);
163 	collection->view_data = NULL;
164 	collection->view_count = 0;
165 
166 	g_list_foreach (
167 		collection->factory_list,
168 		(GFunc) g_object_unref, NULL);
169 	g_list_free (collection->factory_list);
170 	collection->factory_list = NULL;
171 
172 	for (i = 0; i < collection->removed_view_count; i++) {
173 		gal_view_collection_item_free (collection->removed_view_data[i]);
174 	}
175 	g_free (collection->removed_view_data);
176 	collection->removed_view_data  = NULL;
177 	collection->removed_view_count = 0;
178 
179 	g_free (collection->system_dir);
180 	collection->system_dir = NULL;
181 
182 	g_free (collection->local_dir);
183 	collection->local_dir = NULL;
184 
185 	g_free (collection->default_view);
186 	collection->default_view = NULL;
187 
188 	g_free (collection->title);
189 	collection->title = NULL;
190 
191 	/* Chain up to parent's dispose() method. */
192 	G_OBJECT_CLASS (gal_view_collection_parent_class)->dispose (object);
193 }
194 
195 static void
196 gal_view_collection_class_init (GalViewCollectionClass *class)
197 {
198 	GObjectClass *object_class = G_OBJECT_CLASS (class);
199 
200 	object_class->dispose = gal_view_collection_dispose;
201 
202 	gal_view_collection_signals[DISPLAY_VIEW] = g_signal_new (
203 		"display_view",
204 		G_OBJECT_CLASS_TYPE (object_class),
205 		G_SIGNAL_RUN_LAST,
206 		G_STRUCT_OFFSET (GalViewCollectionClass, display_view),
207 		NULL, NULL,
208 		g_cclosure_marshal_VOID__OBJECT,
209 		G_TYPE_NONE, 1,
210 		GAL_TYPE_VIEW);
211 
212 	gal_view_collection_signals[CHANGED] = g_signal_new (
213 		"changed",
214 		G_OBJECT_CLASS_TYPE (object_class),
215 		G_SIGNAL_RUN_LAST,
216 		G_STRUCT_OFFSET (GalViewCollectionClass, changed),
217 		NULL, NULL,
218 		g_cclosure_marshal_VOID__VOID,
219 		G_TYPE_NONE, 0);
220 
221 	class->display_view = NULL;
222 	class->changed      = NULL;
223 }
224 
225 static void
226 gal_view_collection_init (GalViewCollection *collection)
227 {
228 	collection->view_data             = NULL;
229 	collection->view_count            = 0;
230 	collection->factory_list          = NULL;
231 
232 	collection->removed_view_data     = NULL;
233 	collection->removed_view_count    = 0;
234 
235 	collection->system_dir            = NULL;
236 	collection->local_dir             = NULL;
237 
238 	collection->loaded                = FALSE;
239 	collection->default_view          = NULL;
240 	collection->default_view_built_in = TRUE;
241 
242 	collection->title                 = NULL;
243 }
244 
245 /**
246  * gal_view_collection_new:
247  *
248  * A collection of views and view factories.
249  */
250 GalViewCollection *
251 gal_view_collection_new (void)
252 {
253 	return g_object_new (GAL_VIEW_COLLECTION_TYPE, NULL);
254 }
255 
256 void
257 gal_view_collection_set_title (GalViewCollection *collection,
258                                const gchar *title)
259 {
260 	g_free (collection->title);
261 	collection->title = g_strdup (title);
262 }
263 
264 /**
265  * gal_view_collection_set_storage_directories
266  * @collection: The view collection to initialize
267  * @system_dir: The location of the system built in views
268  * @local_dir: The location to store the users set up views
269  *
270  * Sets up the GalViewCollection.
271  */
272 void
273 gal_view_collection_set_storage_directories (GalViewCollection *collection,
274                                              const gchar *system_dir,
275                                              const gchar *local_dir)
276 {
277 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
278 	g_return_if_fail (system_dir != NULL);
279 	g_return_if_fail (local_dir != NULL);
280 
281 	g_free (collection->system_dir);
282 	g_free (collection->local_dir);
283 
284 	collection->system_dir = g_strdup (system_dir);
285 	collection->local_dir = g_strdup (local_dir);
286 }
287 
288 /**
289  * gal_view_collection_add_factory
290  * @collection: The view collection to add a factory to
291  * @factory: The factory to add.  The @collection will add a reference
292  * to the factory object, so you should unref it after calling this
293  * function if you no longer need it.
294  *
295  * Adds the given factory to this collection.  This list is used both
296  * when loading views from their xml description as well as when the
297  * user tries to create a new view.
298  */
299 void
300 gal_view_collection_add_factory (GalViewCollection *collection,
301                                  GalViewFactory *factory)
302 {
303 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
304 	g_return_if_fail (GAL_IS_VIEW_FACTORY (factory));
305 
306 	g_object_ref (factory);
307 	collection->factory_list = g_list_prepend (collection->factory_list, factory);
308 }
309 
310 static void
311 view_changed (GalView *view,
312               GalViewCollectionItem *item)
313 {
314 	item->changed = TRUE;
315 	item->ever_changed = TRUE;
316 
317 	g_signal_handler_block (item->view, item->view_changed_id);
318 	gal_view_collection_changed (item->collection);
319 	g_signal_handler_unblock (item->view, item->view_changed_id);
320 }
321 
322 /* Use factory list to load a GalView file. */
323 static GalView *
324 gal_view_collection_real_load_view_from_file (GalViewCollection *collection,
325                                               const gchar *type,
326                                               const gchar *title,
327                                               const gchar *dir,
328                                               const gchar *filename)
329 {
330 	GalViewFactory *factory;
331 	GList *factories;
332 
333 	factory = NULL;
334 	for (factories = collection->factory_list; factories; factories = factories->next) {
335 		if (type && !strcmp (gal_view_factory_get_type_code (factories->data), type)) {
336 			factory = factories->data;
337 			break;
338 		}
339 	}
340 	if (factory) {
341 		GalView *view;
342 
343 		view = gal_view_factory_new_view (factory, title);
344 		gal_view_set_title (view, title);
345 		gal_view_load (view, filename);
346 		return view;
347 	}
348 	return NULL;
349 }
350 
351 GalView *
352 gal_view_collection_load_view_from_file (GalViewCollection *collection,
353                                          const gchar *type,
354                                          const gchar *filename)
355 {
356 	return gal_view_collection_real_load_view_from_file (collection, type, "", collection->local_dir, filename);
357 }
358 
359 static GalViewCollectionItem *
360 load_single_file (GalViewCollection *collection,
361                   gchar *dir,
362                   gboolean local,
363                   xmlNode *node)
364 {
365 	GalViewCollectionItem *item;
366 	item = g_new (GalViewCollectionItem, 1);
367 	item->ever_changed = local;
368 	item->changed = FALSE;
369 	item->built_in = !local;
370 	item->id = e_xml_get_string_prop_by_name (node, (const guchar *)"id");
371 	item->filename = e_xml_get_string_prop_by_name (node, (const guchar *)"filename");
372 	item->title = e_xml_get_translated_utf8_string_prop_by_name (node, (const guchar *)"title");
373 	item->type = e_xml_get_string_prop_by_name (node, (const guchar *)"type");
374 	item->collection = collection;
375 	item->view_changed_id = 0;
376 
377 	if (item->filename) {
378 		gchar *fullpath;
379 		fullpath = g_build_filename (dir, item->filename, NULL);
380 		item->view = gal_view_collection_real_load_view_from_file (collection, item->type, item->title, dir, fullpath);
381 		g_free (fullpath);
382 		if (item->view) {
383 			item->view_changed_id = g_signal_connect (
384 				item->view, "changed",
385 				G_CALLBACK (view_changed), item);
386 		}
387 	}
388 	return item;
389 }
390 
391 static void
392 load_single_dir (GalViewCollection *collection,
393                  gchar *dir,
394                  gboolean local)
395 {
396 	xmlDoc *doc = NULL;
397 	xmlNode *root;
398 	xmlNode *child;
399 	gchar *filename = g_build_filename (dir, "galview.xml", NULL);
400 	gchar *default_view;
401 
402 	if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
403 #ifdef G_OS_WIN32
404 		gchar *locale_filename = g_win32_locale_filename_from_utf8 (filename);
405 		if (locale_filename != NULL)
406 			doc = xmlParseFile (locale_filename);
407 		g_free (locale_filename);
408 #else
409 		doc = xmlParseFile (filename);
410 #endif
411 	}
412 
413 	if (!doc) {
414 		g_free (filename);
415 		return;
416 	}
417 	root = xmlDocGetRootElement (doc);
418 	for (child = root->xmlChildrenNode; child; child = child->next) {
419 		gchar *id;
420 		gboolean found = FALSE;
421 		gint i;
422 
423 		if (!strcmp ((gchar *) child->name, "text"))
424 			continue;
425 
426 		id = e_xml_get_string_prop_by_name (child, (const guchar *)"id");
427 		for (i = 0; i < collection->view_count; i++) {
428 			if (!strcmp (id, collection->view_data[i]->id)) {
429 				if (!local)
430 					collection->view_data[i]->built_in = TRUE;
431 				found = TRUE;
432 				break;
433 			}
434 		}
435 		if (!found) {
436 			for (i = 0; i < collection->removed_view_count; i++) {
437 				if (!strcmp (id, collection->removed_view_data[i]->id)) {
438 					if (!local)
439 						collection->removed_view_data[i]->built_in = TRUE;
440 					found = TRUE;
441 					break;
442 				}
443 			}
444 		}
445 
446 		if (!found) {
447 			GalViewCollectionItem *item = load_single_file (collection, dir, local, child);
448 			if (item->filename && *item->filename) {
449 				collection->view_data = g_renew (GalViewCollectionItem *, collection->view_data, collection->view_count + 1);
450 				collection->view_data[collection->view_count] = item;
451 				collection->view_count++;
452 			} else {
453 				collection->removed_view_data = g_renew (GalViewCollectionItem *, collection->removed_view_data, collection->removed_view_count + 1);
454 				collection->removed_view_data[collection->removed_view_count] = item;
455 				collection->removed_view_count++;
456 			}
457 		}
458 		g_free (id);
459 	}
460 
461 	default_view = e_xml_get_string_prop_by_name (root, (const guchar *)"default-view");
462 	if (default_view) {
463 		if (local)
464 			collection->default_view_built_in = FALSE;
465 		else
466 			collection->default_view_built_in = TRUE;
467 		g_free (collection->default_view);
468 		collection->default_view = default_view;
469 	}
470 
471 	g_free (filename);
472 	xmlFreeDoc (doc);
473 }
474 
475 /**
476  * gal_view_collection_load
477  * @collection: The view collection to load information for
478  *
479  * Loads the data from the system and user directories specified in
480  * set storage directories.  This is primarily for internal use by
481  * other parts of gal_view.
482  */
483 void
484 gal_view_collection_load (GalViewCollection *collection)
485 {
486 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
487 	g_return_if_fail (collection->local_dir != NULL);
488 	g_return_if_fail (collection->system_dir != NULL);
489 	g_return_if_fail (!collection->loaded);
490 
491 	if ((g_mkdir_with_parents (collection->local_dir, 0777) == -1) && (errno != EEXIST))
492 		g_warning ("Unable to create dir %s: %s", collection->local_dir, g_strerror (errno));
493 
494 	load_single_dir (collection, collection->local_dir, TRUE);
495 	load_single_dir (collection, collection->system_dir, FALSE);
496 	gal_view_collection_changed (collection);
497 
498 	collection->loaded = TRUE;
499 }
500 
501 /**
502  * gal_view_collection_save
503  * @collection: The view collection to save information for
504  *
505  * Saves the data to the user directory specified in set storage
506  * directories.  This is primarily for internal use by other parts of
507  * gal_view.
508  */
509 void
510 gal_view_collection_save (GalViewCollection *collection)
511 {
512 	gint i;
513 	xmlDoc *doc;
514 	xmlNode *root;
515 	gchar *filename;
516 
517 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
518 	g_return_if_fail (collection->local_dir != NULL);
519 
520 	doc = xmlNewDoc ((const guchar *)"1.0");
521 	root = xmlNewNode (NULL, (const guchar *)"GalViewCollection");
522 	xmlDocSetRootElement (doc, root);
523 
524 	if (collection->default_view && !collection->default_view_built_in) {
525 		e_xml_set_string_prop_by_name (root, (const guchar *)"default-view", collection->default_view);
526 	}
527 
528 	for (i = 0; i < collection->view_count; i++) {
529 		xmlNode *child;
530 		GalViewCollectionItem *item;
531 
532 		item = collection->view_data[i];
533 		if (item->ever_changed) {
534 			child = xmlNewChild (root, NULL, (const guchar *)"GalView", NULL);
535 			e_xml_set_string_prop_by_name (child, (const guchar *)"id", item->id);
536 			e_xml_set_string_prop_by_name (child, (const guchar *)"title", item->title);
537 			e_xml_set_string_prop_by_name (child, (const guchar *)"filename", item->filename);
538 			e_xml_set_string_prop_by_name (child, (const guchar *)"type", item->type);
539 
540 			if (item->changed) {
541 				filename = g_build_filename (collection->local_dir, item->filename, NULL);
542 				gal_view_save (item->view, filename);
543 				g_free (filename);
544 			}
545 		}
546 	}
547 	for (i = 0; i < collection->removed_view_count; i++) {
548 		xmlNode *child;
549 		GalViewCollectionItem *item;
550 
551 		item = collection->removed_view_data[i];
552 
553 		child = xmlNewChild (root, NULL, (const guchar *)"GalView", NULL);
554 		e_xml_set_string_prop_by_name (child, (const guchar *)"id", item->id);
555 		e_xml_set_string_prop_by_name (child, (const guchar *)"title", item->title);
556 		e_xml_set_string_prop_by_name (child, (const guchar *)"type", item->type);
557 	}
558 	filename = g_build_filename (collection->local_dir, "galview.xml", NULL);
559 	if (e_xml_save_file (filename, doc) == -1)
560 		g_warning ("Unable to save view to %s - %s", filename, g_strerror (errno));
561 	xmlFreeDoc (doc);
562 	g_free (filename);
563 }
564 
565 /**
566  * gal_view_collection_get_count
567  * @collection: The view collection to count
568  *
569  * Calculates the number of views in the given collection.
570  *
571  * Returns: The number of views in the collection.
572  */
573 gint
574 gal_view_collection_get_count (GalViewCollection *collection)
575 {
576 	g_return_val_if_fail (GAL_IS_VIEW_COLLECTION (collection), -1);
577 
578 	return collection->view_count;
579 }
580 
581 /**
582  * gal_view_collection_get_view
583  * @collection: The view collection to query
584  * @n: The view to get.
585  *
586  * Returns: The nth view in the collection
587  */
588 GalView *
589 gal_view_collection_get_view (GalViewCollection *collection,
590                               gint n)
591 {
592 	g_return_val_if_fail (GAL_IS_VIEW_COLLECTION (collection), NULL);
593 	g_return_val_if_fail (n < collection->view_count, NULL);
594 	g_return_val_if_fail (n >= 0, NULL);
595 
596 	return collection->view_data[n]->view;
597 }
598 
599 /**
600  * gal_view_collection_get_view_item
601  * @collection: The view collection to query
602  * @n: The view item to get.
603  *
604  * Returns: The nth view item in the collection
605  */
606 GalViewCollectionItem *
607 gal_view_collection_get_view_item (GalViewCollection *collection,
608                                    gint n)
609 {
610 	g_return_val_if_fail (GAL_IS_VIEW_COLLECTION (collection), NULL);
611 	g_return_val_if_fail (n < collection->view_count, NULL);
612 	g_return_val_if_fail (n >= 0, NULL);
613 
614 	return collection->view_data[n];
615 }
616 
617 gint
618 gal_view_collection_get_view_index_by_id (GalViewCollection *collection,
619                                           const gchar *view_id)
620 {
621 	gint i;
622 	for (i = 0; i < collection->view_count; i++) {
623 		if (!strcmp (collection->view_data[i]->id, view_id))
624 			return i;
625 	}
626 	return -1;
627 }
628 
629 gchar *
630 gal_view_collection_get_view_id_by_index (GalViewCollection *collection,
631                                           gint n)
632 {
633 	g_return_val_if_fail (GAL_IS_VIEW_COLLECTION (collection), NULL);
634 	g_return_val_if_fail (n < collection->view_count, NULL);
635 	g_return_val_if_fail (n >= 0, NULL);
636 
637 	return g_strdup (collection->view_data[n]->id);
638 }
639 
640 void
641 gal_view_collection_append (GalViewCollection *collection,
642                             GalView *view)
643 {
644 	GalViewCollectionItem *item;
645 
646 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
647 	g_return_if_fail (GAL_IS_VIEW (view));
648 
649 	item = g_new (GalViewCollectionItem, 1);
650 	item->ever_changed = TRUE;
651 	item->changed = TRUE;
652 	item->built_in = FALSE;
653 	item->title = g_strdup (gal_view_get_title (view));
654 	item->type = g_strdup (gal_view_get_type_code (view));
655 	item->id = gal_view_generate_id (collection, view);
656 	item->filename = g_strdup_printf ("%s.galview", item->id);
657 	item->view = view;
658 	item->collection = collection;
659 	g_object_ref (view);
660 
661 	item->view_changed_id = g_signal_connect (
662 		item->view, "changed",
663 		G_CALLBACK (view_changed), item);
664 
665 	collection->view_data = g_renew (GalViewCollectionItem *, collection->view_data, collection->view_count + 1);
666 	collection->view_data[collection->view_count] = item;
667 	collection->view_count++;
668 
669 	gal_view_collection_changed (collection);
670 }
671 
672 void
673 gal_view_collection_delete_view (GalViewCollection *collection,
674                                  gint i)
675 {
676 	GalViewCollectionItem *item;
677 
678 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
679 	g_return_if_fail (i >= 0 && i < collection->view_count);
680 
681 	item = collection->view_data[i];
682 	memmove (collection->view_data + i, collection->view_data + i + 1, (collection->view_count - i - 1) * sizeof (GalViewCollectionItem *));
683 	collection->view_count--;
684 	if (item->built_in) {
685 		g_free (item->filename);
686 		item->filename = NULL;
687 
688 		collection->removed_view_data = g_renew (GalViewCollectionItem *, collection->removed_view_data, collection->removed_view_count + 1);
689 		collection->removed_view_data[collection->removed_view_count] = item;
690 		collection->removed_view_count++;
691 	} else {
692 		gal_view_collection_item_free (item);
693 	}
694 
695 	gal_view_collection_changed (collection);
696 }
697 
698 void
699 gal_view_collection_copy_view (GalViewCollection *collection,
700                                gint i)
701 {
702 	GalViewCollectionItem *item;
703 	GalView *view;
704 
705 	g_return_if_fail (GAL_IS_VIEW_COLLECTION (collection));
706 	g_return_if_fail (i >= 0 && i < collection->view_count);
707 
708 	view = collection->view_data[i]->view;
709 
710 	item = g_new (GalViewCollectionItem, 1);
711 	item->ever_changed = TRUE;
712 	item->changed = FALSE;
713 	item->built_in = FALSE;
714 	item->title = g_strdup (gal_view_get_title (view));
715 	item->type = g_strdup (gal_view_get_type_code (view));
716 	item->id = gal_view_generate_id (collection, view);
717 	item->filename = g_strdup_printf ("%s.galview", item->id);
718 	item->view = gal_view_clone (view);
719 	item->collection = collection;
720 
721 	item->view_changed_id = g_signal_connect (
722 		item->view, "changed",
723 		G_CALLBACK (view_changed), item);
724 
725 	collection->view_data = g_renew (GalViewCollectionItem *, collection->view_data, collection->view_count + 1);
726 	collection->view_data[collection->view_count] = item;
727 	collection->view_count++;
728 
729 	gal_view_collection_changed (collection);
730 }
731 
732 gboolean
733 gal_view_collection_loaded (GalViewCollection *collection)
734 {
735 	return collection->loaded;
736 }
737 
738 const gchar *
739 gal_view_collection_append_with_title (GalViewCollection *collection,
740                                        const gchar *title,
741                                        GalView *view)
742 {
743 	GalViewCollectionItem *item;
744 
745 	g_return_val_if_fail (GAL_IS_VIEW_COLLECTION (collection), NULL);
746 	g_return_val_if_fail (GAL_IS_VIEW (view), NULL);
747 
748 	gal_view_set_title (view, title);
749 
750 	d (g_print ("%s: %p\n", G_STRFUNC, view));
751 
752 	item = g_new (GalViewCollectionItem, 1);
753 	item->ever_changed = TRUE;
754 	item->changed = TRUE;
755 	item->built_in = FALSE;
756 	item->title = g_strdup (gal_view_get_title (view));
757 	item->type = g_strdup (gal_view_get_type_code (view));
758 	item->id = gal_view_generate_id (collection, view);
759 	item->filename = g_strdup_printf ("%s.galview", item->id);
760 	item->view = view;
761 	item->collection = collection;
762 	g_object_ref (view);
763 
764 	item->view_changed_id = g_signal_connect (
765 		item->view, "changed",
766 		G_CALLBACK (view_changed), item);
767 
768 	collection->view_data = g_renew (GalViewCollectionItem *, collection->view_data, collection->view_count + 1);
769 	collection->view_data[collection->view_count] = item;
770 	collection->view_count++;
771 
772 	gal_view_collection_changed (collection);
773 	return item->id;
774 }
775 
776 const gchar *
777 gal_view_collection_set_nth_view (GalViewCollection *collection,
778                                   gint i,
779                                   GalView *view)
780 {
781 	GalViewCollectionItem *item;
782 
783 	g_return_val_if_fail (GAL_IS_VIEW_COLLECTION (collection), NULL);
784 	g_return_val_if_fail (GAL_IS_VIEW (view), NULL);
785 	g_return_val_if_fail (i >= 0, NULL);
786 	g_return_val_if_fail (i < collection->view_count, NULL);
787 
788 	d (g_print ("%s: %p\n", G_STRFUNC, view));
789 
790 	item = collection->view_data[i];
791 
792 	gal_view_set_title (view, item->title);
793 	g_object_ref (view);
794 	if (item->view) {
795 		g_signal_handler_disconnect (
796 			item->view,
797 			item->view_changed_id);
798 		g_object_unref (item->view);
799 	}
800 	item->view = view;
801 
802 	item->ever_changed = TRUE;
803 	item->changed = TRUE;
804 	item->type = g_strdup (gal_view_get_type_code (view));
805 
806 	item->view_changed_id = g_signal_connect (
807 		item->view, "changed",
808 		G_CALLBACK (view_changed), item);
809 
810 	gal_view_collection_changed (collection);
811 	return item->id;
812 }
813 
814 const gchar *
815 gal_view_collection_get_default_view (GalViewCollection *collection)
816 {
817 	return collection->default_view;
818 }
819 
820 void
821 gal_view_collection_set_default_view (GalViewCollection *collection,
822                                       const gchar *id)
823 {
824 	g_free (collection->default_view);
825 	collection->default_view = g_strdup (id);
826 	gal_view_collection_changed (collection);
827 	collection->default_view_built_in = FALSE;
828 }