1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Libbrasero-burn
4  * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5  *
6  * Libbrasero-burn is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * The Libbrasero-burn authors hereby grant permission for non-GPL compatible
12  * GStreamer plugins to be used and distributed together with GStreamer
13  * and Libbrasero-burn. This permission is above and beyond the permissions granted
14  * by the GPL license by which Libbrasero-burn is covered. If you modify this code
15  * you may extend this exception to your version of the code, but you are not
16  * obligated to do so. If you do not wish to do so, delete this exception
17  * statement from your version.
18  *
19  * Libbrasero-burn is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to:
26  * 	The Free Software Foundation, Inc.,
27  * 	51 Franklin Street, Fifth Floor
28  * 	Boston, MA  02110-1301, USA.
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #  include <config.h>
33 #endif
34 
35 #include <string.h>
36 
37 #include <gio/gio.h>
38 
39 #include "brasero-misc.h"
40 
41 #include "burn-basics.h"
42 
43 #include "brasero-file-node.h"
44 #include "brasero-io.h"
45 
46 
47 BraseroFileNode *
brasero_file_node_root_new(void)48 brasero_file_node_root_new (void)
49 {
50 	BraseroFileNode *root;
51 
52 	root = g_new0 (BraseroFileNode, 1);
53 	root->is_root = TRUE;
54 	root->is_imported = TRUE;
55 
56 	root->union3.stats = g_new0 (BraseroFileTreeStats, 1);
57 	return root;
58 }
59 
60 BraseroFileNode *
brasero_file_node_get_root(BraseroFileNode * node,guint * depth_retval)61 brasero_file_node_get_root (BraseroFileNode *node,
62 			    guint *depth_retval)
63 {
64 	BraseroFileNode *parent;
65 	guint depth = 0;
66 
67 	parent = node;
68 	while (parent) {
69 		if (parent->is_root) {
70 			if (depth_retval)
71 				*depth_retval = depth;
72 
73 			return parent;
74 		}
75 
76 		depth ++;
77 		parent = parent->parent;
78 	}
79 
80 	return NULL;
81 }
82 
83 
84 guint
brasero_file_node_get_depth(BraseroFileNode * node)85 brasero_file_node_get_depth (BraseroFileNode *node)
86 {
87 	guint depth = 0;
88 
89 	while (node) {
90 		if (node->is_root)
91 			return depth;
92 
93 		depth ++;
94 		node = node->parent;
95 	}
96 
97 	return 0;
98 }
99 
100 BraseroFileTreeStats *
brasero_file_node_get_tree_stats(BraseroFileNode * node,guint * depth)101 brasero_file_node_get_tree_stats (BraseroFileNode *node,
102 				  guint *depth)
103 {
104 	BraseroFileTreeStats *stats;
105 	BraseroFileNode *root;
106 
107 	stats = BRASERO_FILE_NODE_STATS (node);
108 	if (stats)
109 		return stats;
110 
111 	root = brasero_file_node_get_root (node, depth);
112 	stats = BRASERO_FILE_NODE_STATS (root);
113 
114 	return stats;
115 }
116 
117 gint
brasero_file_node_sort_default_cb(gconstpointer obj_a,gconstpointer obj_b)118 brasero_file_node_sort_default_cb (gconstpointer obj_a, gconstpointer obj_b)
119 {
120 	const BraseroFileNode *a = obj_a;
121 	const BraseroFileNode *b = obj_b;
122 
123 	if (a->is_file == b->is_file)
124 		return 0;
125 
126 	if (b->is_file)
127 		return -1;
128 
129 	return 1;
130 }
131 
132 gint
brasero_file_node_sort_name_cb(gconstpointer obj_a,gconstpointer obj_b)133 brasero_file_node_sort_name_cb (gconstpointer obj_a, gconstpointer obj_b)
134 {
135 	gint res;
136 	const BraseroFileNode *a = obj_a;
137 	const BraseroFileNode *b = obj_b;
138 
139 
140 	res = brasero_file_node_sort_default_cb (a, b);
141 	if (res)
142 		return res;
143 
144 	return strcmp (BRASERO_FILE_NODE_NAME (a), BRASERO_FILE_NODE_NAME (b));
145 }
146 
147 gint
brasero_file_node_sort_size_cb(gconstpointer obj_a,gconstpointer obj_b)148 brasero_file_node_sort_size_cb (gconstpointer obj_a, gconstpointer obj_b)
149 {
150 	gint res;
151 	gint num_a, num_b;
152 	const BraseroFileNode *a = obj_a;
153 	const BraseroFileNode *b = obj_b;
154 
155 
156 	res = brasero_file_node_sort_default_cb (a, b);
157 	if (res)
158 		return res;
159 
160 	if (a->is_file)
161 		return BRASERO_FILE_NODE_SECTORS (a) - BRASERO_FILE_NODE_SECTORS (b);
162 
163 	/* directories */
164 	num_a = brasero_file_node_get_n_children (a);
165 	num_b = brasero_file_node_get_n_children (b);
166 	return num_a - num_b;
167 }
168 
169 gint
brasero_file_node_sort_mime_cb(gconstpointer obj_a,gconstpointer obj_b)170 brasero_file_node_sort_mime_cb (gconstpointer obj_a, gconstpointer obj_b)
171 {
172 	gint res;
173 	const BraseroFileNode *a = obj_a;
174 	const BraseroFileNode *b = obj_b;
175 
176 
177 	res = brasero_file_node_sort_default_cb (a, b);
178 	if (res)
179 		return res;
180 
181 	return strcmp (BRASERO_FILE_NODE_NAME (a), BRASERO_FILE_NODE_NAME (b));
182 }
183 
184 static BraseroFileNode *
brasero_file_node_insert(BraseroFileNode * head,BraseroFileNode * node,GCompareFunc sort_func,guint * newpos)185 brasero_file_node_insert (BraseroFileNode *head,
186 			  BraseroFileNode *node,
187 			  GCompareFunc sort_func,
188 			  guint *newpos)
189 {
190 	BraseroFileNode *iter;
191 	guint n = 0;
192 
193 	/* check for some special cases */
194 	if (!head) {
195 		node->next = NULL;
196 		return node;
197 	}
198 
199 	/* Set hidden nodes (whether virtual or not) always last */
200 	if (head->is_hidden) {
201 		node->next = head;
202 		if (newpos)
203 			*newpos = 0;
204 
205 		return node;
206 	}
207 
208 	if (node->is_hidden) {
209 		iter = head;
210 		n = 1;
211 		while (iter->next)  {
212 			iter = iter->next;
213 			n ++;
214 		}
215 
216 		iter->next = node;
217 
218 		if (newpos)
219 			*newpos = n;
220 
221 		return head;
222 	}
223 
224 	/* regular node, regular head node */
225 	if (sort_func (head, node) > 0) {
226 		/* head is after node */
227 		node->next = head;
228 		if (newpos)
229 			*newpos = 0;
230 
231 		return node;
232 	}
233 
234 	n = 1;
235 	for (iter = head; iter->next; iter = iter->next) {
236 		if (sort_func (iter->next, node) > 0) {
237 			/* iter->next should be located after node */
238 			node->next = iter->next;
239 			iter->next = node;
240 
241 			if (newpos)
242 				*newpos = n;
243 
244 			return head;
245 		}
246 		n ++;
247 	}
248 
249 	/* append it */
250 	iter->next = node;
251 	node->next = NULL;
252 
253 	if (newpos)
254 		*newpos = n;
255 
256 	return head;
257 }
258 
259 gint *
brasero_file_node_need_resort(BraseroFileNode * node,GCompareFunc sort_func)260 brasero_file_node_need_resort (BraseroFileNode *node,
261 			       GCompareFunc sort_func)
262 {
263 	BraseroFileNode *previous;
264 	BraseroFileNode *parent;
265 	BraseroFileNode *head;
266 	gint *array = NULL;
267 	guint newpos = 0;
268 	guint oldpos;
269 	guint size;
270 
271 	if (node->is_hidden)
272 		return NULL;
273 
274 	parent = node->parent;
275 	head = BRASERO_FILE_NODE_CHILDREN (parent);
276 
277 	/* find previous node and get old position */
278 	if (head != node) {
279 		previous = head;
280 		oldpos = 0;
281 		while (previous->next != node) {
282 			previous = previous->next;
283 			oldpos ++;
284 		}
285 		oldpos ++;
286 	}
287 	else {
288 		previous = NULL;
289 		oldpos = 0;
290 	}
291 
292 	/* see where we should start from head or from node->next */
293 	if (previous && sort_func (previous, node) > 0) {
294 		gint i;
295 
296 		/* move on the left */
297 
298 		previous->next = node->next;
299 
300 		head = brasero_file_node_insert (head, node, sort_func, &newpos);
301 		parent->union2.children = head;
302 
303 		/* create an array to reflect the changes */
304 		/* NOTE: hidden nodes are not taken into account. */
305 		size = brasero_file_node_get_n_children (parent);
306 		array = g_new0 (gint, size);
307 
308 		for (i = 0; i < size; i ++) {
309 			if (i == newpos)
310 				array [i] = oldpos;
311 			else if (i > newpos && i <= oldpos)
312 				array [i] = i - 1;
313 			else
314 				array [i] = i;
315 		}
316 	}
317 	/* Hidden nodes stay at the end, hence the !node->next->is_hidden */
318 	else if (node->next && !node->next->is_hidden && sort_func (node, node->next) > 0) {
319 		gint i;
320 
321 		/* move on the right */
322 
323 		if (previous)
324 			previous->next = node->next;
325 		else
326 			parent->union2.children = node->next;
327 
328 		/* NOTE: here we're sure head hasn't changed since we checked
329 		 * that node should go after node->next (given as head for the
330 		 * insertion here) */
331 		brasero_file_node_insert (node->next, node, sort_func, &newpos);
332 
333 		/* we started from oldpos so newpos needs updating */
334 		newpos += oldpos;
335 
336 		/* create an array to reflect the changes. */
337 		/* NOTE: hidden nodes are not taken into account. */
338 		size = brasero_file_node_get_n_children (parent);
339 		array = g_new0 (gint, size);
340 
341 		for (i = 0; i < size; i ++) {
342 			if (i == newpos)
343 				array [i] = oldpos;
344 			else if (i >= oldpos && i < newpos)
345 				array [i] = i + 1;
346 			else
347 				array [i] = i;
348 		}
349 	}
350 
351 	return array;
352 }
353 
354 gint *
brasero_file_node_sort_children(BraseroFileNode * parent,GCompareFunc sort_func)355 brasero_file_node_sort_children (BraseroFileNode *parent,
356 				 GCompareFunc sort_func)
357 {
358 	BraseroFileNode *new_order = NULL;
359 	BraseroFileNode *iter;
360 	BraseroFileNode *next;
361 	gint *array = NULL;
362 	gint num_children;
363 	guint oldpos = 1;
364 	guint newpos;
365 
366 	/* check for some special cases */
367 	if (parent->is_hidden)
368 		return NULL;
369 
370 	new_order = BRASERO_FILE_NODE_CHILDREN (parent);
371 	if (!new_order)
372 		return NULL;
373 
374 	if (!new_order->next)
375 		return NULL;
376 
377 	/* make the array */
378 	num_children = brasero_file_node_get_n_children (parent);
379 	array = g_new (gint, num_children);
380 
381 	next = new_order->next;
382 	new_order->next = NULL;
383 	array [0] = 0;
384 
385 	for (iter = next; iter; iter = next, oldpos ++) {
386 		/* unlink iter */
387 		next = iter->next;
388 		iter->next = NULL;
389 
390 		newpos = 0;
391 		new_order = brasero_file_node_insert (new_order,
392 						      iter,
393 						      sort_func,
394 						      &newpos);
395 
396 		if (newpos < oldpos)
397 			memmove (array + newpos + 1, array + newpos, (oldpos - newpos) * sizeof (guint));
398 
399 		array [newpos] = oldpos;
400 	}
401 
402 	/* set the new order */
403 	parent->union2.children = new_order;
404 
405 	return array;
406 }
407 
408 gint *
brasero_file_node_reverse_children(BraseroFileNode * parent)409 brasero_file_node_reverse_children (BraseroFileNode *parent)
410 {
411 	BraseroFileNode *previous;
412 	BraseroFileNode *last;
413 	BraseroFileNode *iter;
414 	gint firstfile = 0;
415 	gint *array;
416 	gint size;
417 	gint i;
418 
419 	/* when reversing the list of children the only thing we must pay
420 	 * attention to is to keep directories first; so we do it in two passes
421 	 * first order the directories and then the files */
422 	last = BRASERO_FILE_NODE_CHILDREN (parent);
423 
424 	/* special case */
425 	if (!last || !last->next)
426 		return NULL;
427 
428 	previous = last;
429 	iter = last->next;
430 	size = 1;
431 
432 	if (!last->is_file) {
433 		while (!iter->is_file) {
434 			BraseroFileNode *next;
435 
436 			next = iter->next;
437 			iter->next = previous;
438 
439 			size ++;
440 			if (!next) {
441 				/* No file afterwards */
442 				parent->union2.children = iter;
443 				last->next = NULL;
444 				firstfile = size;
445 				goto end;
446 			}
447 
448 			previous = iter;
449 			iter = next;
450 		}
451 
452 		/* the new head is the last processed node */
453 		parent->union2.children = previous;
454 		firstfile = size;
455 
456 		previous = iter;
457 		iter = iter->next;
458 		previous->next = NULL;
459 	}
460 
461 	while (iter) {
462 		BraseroFileNode *next;
463 
464 		next = iter->next;
465 		iter->next = previous;
466 
467 		size ++;
468 
469 		previous = iter;
470 		iter = next;
471 	}
472 
473 	/* NOTE: iter is NULL here */
474 	if (last->is_file) {
475 		last->next = NULL;
476 		parent->union2.children = previous;
477 	}
478 	else
479 		last->next = previous;
480 
481 end:
482 
483 	array = g_new (gint, size);
484 
485 	for (i = 0; i < firstfile; i ++)
486 		array [i] = firstfile - i - 1;
487 
488 	for (i = firstfile; i < size; i ++)
489 		array [i] = size - i + firstfile - 1;
490 
491 	return array;
492 }
493 
494 BraseroFileNode *
brasero_file_node_nth_child(BraseroFileNode * parent,guint nth)495 brasero_file_node_nth_child (BraseroFileNode *parent,
496 			     guint nth)
497 {
498 	BraseroFileNode *peers;
499 	guint pos;
500 
501 	if (!parent)
502 		return NULL;
503 
504 	peers = BRASERO_FILE_NODE_CHILDREN (parent);
505 	for (pos = 0; pos < nth && peers; pos ++)
506 		peers = peers->next;
507 
508 	return peers;
509 }
510 
511 guint
brasero_file_node_get_n_children(const BraseroFileNode * node)512 brasero_file_node_get_n_children (const BraseroFileNode *node)
513 {
514 	BraseroFileNode *children;
515 	guint num = 0;
516 
517 	if (!node)
518 		return 0;
519 
520 	for (children = BRASERO_FILE_NODE_CHILDREN (node); children; children = children->next) {
521 		if (children->is_hidden)
522 			continue;
523 		num ++;
524 	}
525 
526 	return num;
527 }
528 
529 guint
brasero_file_node_get_pos_as_child(BraseroFileNode * node)530 brasero_file_node_get_pos_as_child (BraseroFileNode *node)
531 {
532 	BraseroFileNode *parent;
533 	BraseroFileNode *peers;
534 	guint pos = 0;
535 
536 	if (!node)
537 		return 0;
538 
539 	parent = node->parent;
540 	for (peers = BRASERO_FILE_NODE_CHILDREN (parent); peers; peers = peers->next) {
541 		if (peers == node)
542 			break;
543 		pos ++;
544 	}
545 
546 	return pos;
547 }
548 
549 gboolean
brasero_file_node_is_ancestor(BraseroFileNode * parent,BraseroFileNode * node)550 brasero_file_node_is_ancestor (BraseroFileNode *parent,
551 			       BraseroFileNode *node)
552 {
553 	while (node && node != parent)
554 		node = node->parent;
555 
556 	if (!node)
557 		return FALSE;
558 
559 	return TRUE;
560 }
561 
562 BraseroFileNode *
brasero_file_node_check_name_existence_case(BraseroFileNode * parent,const gchar * name)563 brasero_file_node_check_name_existence_case (BraseroFileNode *parent,
564 					     const gchar *name)
565 {
566 	BraseroFileNode *iter;
567 
568 	if (name && name [0] == '\0')
569 		return NULL;
570 
571 	iter = BRASERO_FILE_NODE_CHILDREN (parent);
572 	for (; iter; iter = iter->next) {
573 		if (!strcasecmp (name, BRASERO_FILE_NODE_NAME (iter)))
574 			return iter;
575 	}
576 
577 	return NULL;
578 }
579 
580 BraseroFileNode *
brasero_file_node_check_name_existence(BraseroFileNode * parent,const gchar * name)581 brasero_file_node_check_name_existence (BraseroFileNode *parent,
582 				        const gchar *name)
583 {
584 	BraseroFileNode *iter;
585 
586 	if (name && name [0] == '\0')
587 		return NULL;
588 
589 	iter = BRASERO_FILE_NODE_CHILDREN (parent);
590 	for (; iter; iter = iter->next) {
591 		if (!strcmp (name, BRASERO_FILE_NODE_NAME (iter)))
592 			return iter;
593 	}
594 
595 	return NULL;
596 }
597 
598 BraseroFileNode *
brasero_file_node_get_from_path(BraseroFileNode * root,const gchar * path)599 brasero_file_node_get_from_path (BraseroFileNode *root,
600 				 const gchar *path)
601 {
602 	gchar **array;
603 	gchar **iter;
604 
605 	if (!path)
606 		return NULL;
607 
608 	/* If we don't do that array[0] == '\0' */
609 	if (g_str_has_prefix (path, G_DIR_SEPARATOR_S))
610 		array = g_strsplit (path + 1, G_DIR_SEPARATOR_S, 0);
611 	else
612 		array = g_strsplit (path, G_DIR_SEPARATOR_S, 0);
613 
614 	if (!array)
615 		return NULL;
616 
617 	for (iter = array; iter && *iter; iter++) {
618 		root = brasero_file_node_check_name_existence (root, *iter);
619 		if (!root)
620 			break;
621 	}
622 	g_strfreev (array);
623 
624 	return root;
625 }
626 
627 BraseroFileNode *
brasero_file_node_check_imported_sibling(BraseroFileNode * node)628 brasero_file_node_check_imported_sibling (BraseroFileNode *node)
629 {
630 	BraseroFileNode *parent;
631 	BraseroFileNode *iter;
632 	BraseroImport *import;
633 
634 	parent = node->parent;
635 
636 	/* That could happen if a node is moved to a location where another node
637 	 * (to be removed) has the same name and is a parent of this node */
638 	if (!parent)
639 		return NULL;
640 
641 	/* See if among the imported children of the parent one of them
642 	 * has the same name as the node being removed. If so, restore
643 	 * it with all its imported children (provided that's a
644 	 * directory). */
645 	import = BRASERO_FILE_NODE_IMPORT (parent);
646 	if (!import)
647 		return NULL;
648 
649 	iter = import->replaced;
650 	if (!strcmp (BRASERO_FILE_NODE_NAME (iter), BRASERO_FILE_NODE_NAME (node))) {
651 		/* A match, remove it from the list and return it */
652 		import->replaced = iter->next;
653 		if (!import->replaced) {
654 			/* no more imported saved import structure */
655 			parent->union1.name = import->name;
656 			parent->has_import = FALSE;
657 			g_free (import);
658 		}
659 
660 		iter->next = NULL;
661 		return iter;
662 	}
663 
664 	for (; iter->next; iter = iter->next) {
665 		if (!strcmp (BRASERO_FILE_NODE_NAME (iter->next), BRASERO_FILE_NODE_NAME (node))) {
666 			BraseroFileNode *removed;
667 			/* There is one match, remove it from the list */
668 			removed = iter->next;
669 			iter->next = removed->next;
670 			removed->next = NULL;
671 			return removed;
672 		}
673 	}
674 
675 	return NULL;
676 }
677 
678 void
brasero_file_node_graft(BraseroFileNode * file_node,BraseroURINode * uri_node)679 brasero_file_node_graft (BraseroFileNode *file_node,
680 			 BraseroURINode *uri_node)
681 {
682 	BraseroGraft *graft;
683 
684 	if (!file_node->is_grafted) {
685 		BraseroFileNode *parent;
686 
687 		graft = g_new (BraseroGraft, 1);
688 		graft->name = file_node->union1.name;
689 		file_node->union1.graft = graft;
690 		file_node->is_grafted = TRUE;
691 
692 		/* since it wasn't grafted propagate the size change; that is
693 		 * substract the current node size from the parent nodes until
694 		 * the parent graft point. */
695 		for (parent = file_node->parent; parent && !parent->is_root; parent = parent->parent) {
696 			parent->union3.sectors -= BRASERO_FILE_NODE_SECTORS (file_node);
697 			if (parent->is_grafted)
698 				break;
699 		}
700 	}
701 	else {
702 		BraseroURINode *old_uri_node;
703 
704 		graft = BRASERO_FILE_NODE_GRAFT (file_node);
705 		old_uri_node = graft->node;
706 		if (old_uri_node == uri_node)
707 			return;
708 
709 		old_uri_node->nodes = g_slist_remove (old_uri_node->nodes, file_node);
710 	}
711 
712 	graft->node = uri_node;
713 	uri_node->nodes = g_slist_prepend (uri_node->nodes, file_node);
714 }
715 
716 void
brasero_file_node_ungraft(BraseroFileNode * node)717 brasero_file_node_ungraft (BraseroFileNode *node)
718 {
719 	BraseroGraft *graft;
720 	BraseroFileNode *parent;
721 
722 	if (!node->is_grafted)
723 		return;
724 
725 	graft = node->union1.graft;
726 
727 	/* Remove it from the URINode list of grafts */
728 	graft->node->nodes = g_slist_remove (graft->node->nodes, node);
729 
730 	/* The name must be exactly the one of the URI */
731 	node->is_grafted = FALSE;
732 	node->union1.name = graft->name;
733 
734 	/* Removes the graft */
735 	g_free (graft);
736 
737 	/* Propagate the size change up the parents to the next
738 	 * grafted parent in the tree (if any). */
739 	for (parent = node->parent; parent && !parent->is_root; parent = parent->parent) {
740 		parent->union3.sectors += BRASERO_FILE_NODE_SECTORS (node);
741 		if (parent->is_grafted)
742 			break;
743 	}
744 }
745 
746 void
brasero_file_node_rename(BraseroFileNode * node,const gchar * name)747 brasero_file_node_rename (BraseroFileNode *node,
748 			  const gchar *name)
749 {
750 	g_free (BRASERO_FILE_NODE_NAME (node));
751 	if (node->is_grafted)
752 		node->union1.graft->name = g_strdup (name);
753 	else
754 		node->union1.name = g_strdup (name);
755 }
756 
757 void
brasero_file_node_add(BraseroFileNode * parent,BraseroFileNode * node,GCompareFunc sort_func)758 brasero_file_node_add (BraseroFileNode *parent,
759 		       BraseroFileNode *node,
760 		       GCompareFunc sort_func)
761 {
762 	BraseroFileTreeStats *stats;
763 	guint depth = 0;
764 
765 	parent->union2.children = brasero_file_node_insert (BRASERO_FILE_NODE_CHILDREN (parent),
766 							    node,
767 							    sort_func,
768 							    NULL);
769 	node->parent = parent;
770 
771 	if (BRASERO_FILE_NODE_VIRTUAL (node))
772 		return;
773 
774 	stats = brasero_file_node_get_tree_stats (node->parent, &depth);
775 	if (!node->is_imported) {
776 		/* book keeping */
777 		if (!node->is_file)
778 			stats->num_dir ++;
779 		else
780 			stats->children ++;
781 
782 		/* NOTE: parent will be changed afterwards !!! */
783 		if (!node->is_grafted) {
784 			/* propagate the size change*/
785 			for (; parent && !parent->is_root; parent = parent->parent) {
786 				parent->union3.sectors += BRASERO_FILE_NODE_SECTORS (node);
787 				if (parent->is_grafted)
788 					break;
789 			}
790 		}
791 	}
792 
793 	/* Even imported should be included. The only type of nodes that are not
794 	 * heeded are the virtual nodes. */
795 	if (node->is_file) {
796 		if (depth < 6)
797 			return;
798 	}
799 	else if (depth < 5)
800 		return;
801 
802 	stats->num_deep ++;
803 	node->is_deep = TRUE;
804 }
805 
806 void
brasero_file_node_set_from_info(BraseroFileNode * node,BraseroFileTreeStats * stats,GFileInfo * info)807 brasero_file_node_set_from_info (BraseroFileNode *node,
808 				 BraseroFileTreeStats *stats,
809 				 GFileInfo *info)
810 {
811 	/* NOTE: the name will never be replaced here since that means
812 	 * we could replace a previously set name (that triggered the
813 	 * creation of a graft). If someone wants to set a new name,
814 	 * then rename_node is the function. */
815 
816 	if (node->parent) {
817 		/* update the stats since a file could have been added to the tree but
818 		 * at this point we didn't know what it was (a file or a directory).
819 		 * Only do this if it wasn't a file before.
820 		 * Do this only if it's in the tree. */
821 		if (!node->is_file && (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)) {
822 			stats->children ++;
823 			stats->num_dir --;
824 		}
825 		else if (node->is_file && (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)) {
826 			stats->children --;
827 			stats->num_dir ++;
828 		}
829 	}
830 
831 	if (!node->is_symlink
832 	&& (g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK)) {
833 		/* only count files */
834 		stats->num_sym ++;
835 	}
836 
837 	/* update :
838 	 * - the mime type
839 	 * - the size (and possibly the one of his parent)
840 	 * - the type */
841 	node->is_file = (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY);
842 	node->is_fake = FALSE;
843 	node->is_loading = FALSE;
844 	node->is_imported = FALSE;
845 	node->is_reloading = FALSE;
846 	node->is_symlink = (g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK);
847 
848 	if (node->is_file) {
849 		guint sectors;
850 		gint sectors_diff;
851 
852 		/* register mime type string */
853 		if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)) {
854 			const gchar *mime;
855 
856 			if (BRASERO_FILE_NODE_MIME (node))
857 				brasero_utils_unregister_string (BRASERO_FILE_NODE_MIME (node));
858 
859 			mime = g_file_info_get_content_type (info);
860 			node->union2.mime = brasero_utils_register_string (mime);
861 		}
862 
863 		sectors = BRASERO_BYTES_TO_SECTORS (g_file_info_get_size (info), 2048);
864 
865 		if (sectors > BRASERO_FILE_2G_LIMIT && BRASERO_FILE_NODE_SECTORS (node) <= BRASERO_FILE_2G_LIMIT) {
866 			node->is_2GiB = 1;
867 			stats->num_2GiB ++;
868 		}
869 		else if (sectors <= BRASERO_FILE_2G_LIMIT && BRASERO_FILE_NODE_SECTORS (node) > BRASERO_FILE_2G_LIMIT) {
870 			node->is_2GiB = 0;
871 			stats->num_2GiB --;
872 		}
873 
874 		/* The node isn't grafted and it's a file. So we must propagate
875 		 * its size up to the parent graft node. */
876 		/* NOTE: we used to accumulate all the directory contents till
877 		 * the end and process all of entries at once, when it was
878 		 * finished. We had to do that to calculate the whole size. */
879 		sectors_diff = sectors - BRASERO_FILE_NODE_SECTORS (node);
880 		for (; node; node = node->parent) {
881 			node->union3.sectors += sectors_diff;
882 			if (node->is_grafted)
883 				break;
884 		}
885 	}
886 	else	/* since that's directory then it must be explored now */
887 		node->is_exploring = TRUE;
888 }
889 
890 BraseroFileNode *
brasero_file_node_new_loading(const gchar * name)891 brasero_file_node_new_loading (const gchar *name)
892 {
893 	BraseroFileNode *node;
894 
895 	node = g_new0 (BraseroFileNode, 1);
896 	node->union1.name = g_strdup (name);
897 	node->is_loading = TRUE;
898 
899 	return node;
900 }
901 
902 BraseroFileNode *
brasero_file_node_new_virtual(const gchar * name)903 brasero_file_node_new_virtual (const gchar *name)
904 {
905 	BraseroFileNode *node;
906 
907 	/* virtual nodes are nodes that "don't exist". They appear as temporary
908 	 * parents (and therefore replacable) and hidden (not displayed in the
909 	 * GtkTreeModel). They are used as 'placeholders' to trigger
910 	 * name-collision signal. */
911 	node = g_new0 (BraseroFileNode, 1);
912 	node->union1.name = g_strdup (name);
913 	node->is_fake = TRUE;
914 	node->is_hidden = TRUE;
915 
916 	return node;
917 }
918 
919 BraseroFileNode *
brasero_file_node_new(const gchar * name)920 brasero_file_node_new (const gchar *name)
921 {
922 	BraseroFileNode *node;
923 
924 	node = g_new0 (BraseroFileNode, 1);
925 	node->union1.name = g_strdup (name);
926 
927 	return node;
928 }
929 
930 BraseroFileNode *
brasero_file_node_new_imported_session_file(GFileInfo * info)931 brasero_file_node_new_imported_session_file (GFileInfo *info)
932 {
933 	BraseroFileNode *node;
934 
935 	/* Create the node information */
936 	node = g_new0 (BraseroFileNode, 1);
937 	node->union1.name = g_strdup (g_file_info_get_name (info));
938 	node->is_file = (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY);
939 	node->is_imported = TRUE;
940 
941 	if (!node->is_file) {
942 		node->is_fake = TRUE;
943 		node->union3.imported_address = g_file_info_get_attribute_int64 (info, BRASERO_IO_DIR_CONTENTS_ADDR);
944 	}
945 	else
946 		node->union3.sectors = BRASERO_BYTES_TO_SECTORS (g_file_info_get_size (info), 2048);
947 
948 	return node;
949 }
950 
951 BraseroFileNode *
brasero_file_node_new_empty_folder(const gchar * name)952 brasero_file_node_new_empty_folder (const gchar *name)
953 {
954 	BraseroFileNode *node;
955 
956 	/* Create the node information */
957 	node = g_new0 (BraseroFileNode, 1);
958 	node->union1.name = g_strdup (name);
959 	node->is_fake = TRUE;
960 
961 	return node;
962 }
963 
964 void
brasero_file_node_unlink(BraseroFileNode * node)965 brasero_file_node_unlink (BraseroFileNode *node)
966 {
967 	BraseroFileNode *iter;
968 	BraseroImport *import;
969 
970 	if (!node->parent)
971 		return;
972 
973 	iter = BRASERO_FILE_NODE_CHILDREN (node->parent);
974 
975 	/* handle the size change for previous parent */
976 	if (!node->is_grafted
977 	&&  !node->is_imported
978 	&&  !BRASERO_FILE_NODE_VIRTUAL (node)) {
979 		BraseroFileNode *parent;
980 
981 		/* handle the size change if it wasn't grafted */
982 		for (parent = node->parent; parent && !parent->is_root; parent = parent->parent) {
983 			parent->union3.sectors -= BRASERO_FILE_NODE_SECTORS (node);
984 			if (parent->is_grafted)
985 				break;
986 		}
987 	}
988 
989 	node->is_deep = FALSE;
990 
991 	if (iter == node) {
992 		node->parent->union2.children = node->next;
993 		node->parent = NULL;
994 		node->next = NULL;
995 		return;
996 	}
997 
998 	for (; iter->next; iter = iter->next) {
999 		if (iter->next == node) {
1000 			iter->next = node->next;
1001 			node->parent = NULL;
1002 			node->next = NULL;
1003 			return;
1004 		}
1005 	}
1006 
1007 	if (!node->is_imported || !node->parent->has_import)
1008 		return;
1009 
1010 	/* It wasn't found among the parent children. If parent is imported and
1011 	 * the node is imported as well then check if it isn't among the import
1012 	 * children */
1013 	import = BRASERO_FILE_NODE_IMPORT (node->parent);
1014 	iter = import->replaced;
1015 
1016 	if (iter == node) {
1017 		import->replaced = iter->next;
1018 		node->parent = NULL;
1019 		node->next = NULL;
1020 		return;
1021 	}
1022 
1023 	for (; iter->next; iter = iter->next) {
1024 		if (iter->next == node) {
1025 			iter->next = node->next;
1026 			node->parent = NULL;
1027 			node->next = NULL;
1028 			return;
1029 		}
1030 	}
1031 }
1032 
1033 void
brasero_file_node_move_from(BraseroFileNode * node,BraseroFileTreeStats * stats)1034 brasero_file_node_move_from (BraseroFileNode *node,
1035 			     BraseroFileTreeStats *stats)
1036 {
1037 	/* NOTE: for the time being no backend supports moving imported files */
1038 	if (node->is_imported)
1039 		return;
1040 
1041 	if (node->is_deep)
1042 		stats->num_deep --;
1043 
1044 	brasero_file_node_unlink (node);
1045 }
1046 
1047 void
brasero_file_node_move_to(BraseroFileNode * node,BraseroFileNode * parent,GCompareFunc sort_func)1048 brasero_file_node_move_to (BraseroFileNode *node,
1049 			   BraseroFileNode *parent,
1050 			   GCompareFunc sort_func)
1051 {
1052 	BraseroFileTreeStats *stats;
1053 	guint depth = 0;
1054 
1055 	/* NOTE: for the time being no backend supports moving imported files */
1056 	if (node->is_imported)
1057 		return;
1058 
1059 	/* reinsert it now at the new location */
1060 	parent->union2.children = brasero_file_node_insert (BRASERO_FILE_NODE_CHILDREN (parent),
1061 							    node,
1062 							    sort_func,
1063 							    NULL);
1064 	node->parent = parent;
1065 
1066 	if (!node->is_grafted) {
1067 		BraseroFileNode *parent;
1068 
1069 		/* propagate the size change for new parent */
1070 		for (parent = node->parent; parent && !parent->is_root; parent = parent->parent) {
1071 			parent->union3.sectors += BRASERO_FILE_NODE_SECTORS (node);
1072 			if (parent->is_grafted)
1073 				break;
1074 		}
1075 	}
1076 
1077 	/* NOTE: here stats about the tree can change if the parent has a depth
1078 	 * > 6 and if previous didn't. Other stats remains unmodified. */
1079 	stats = brasero_file_node_get_tree_stats (node->parent, &depth);
1080 	if (node->is_file) {
1081 		if (depth < 6)
1082 			return;
1083 	}
1084 	else if (depth < 5)
1085 		return;
1086 
1087 	stats->num_deep ++;
1088 	node->is_deep = TRUE;
1089 }
1090 
1091 static void
brasero_file_node_destroy_with_children(BraseroFileNode * node,BraseroFileTreeStats * stats)1092 brasero_file_node_destroy_with_children (BraseroFileNode *node,
1093 					 BraseroFileTreeStats *stats)
1094 {
1095 	BraseroFileNode *child;
1096 	BraseroFileNode *next;
1097 	BraseroImport *import;
1098 	BraseroGraft *graft;
1099 
1100 	/* destroy all children recursively */
1101 	for (child = BRASERO_FILE_NODE_CHILDREN (node); child; child = next) {
1102 		next = child->next;
1103 		brasero_file_node_destroy_with_children (child, stats);
1104 	}
1105 
1106 	/* update all statistics on tree if any */
1107 	if (!BRASERO_FILE_NODE_VIRTUAL (node) && stats) {
1108 		/* check if that's a 2 GiB file */
1109 		if (node->is_2GiB)
1110 			stats->num_2GiB --;
1111 
1112 		/* check if that's a deep directory file */
1113 		if (node->is_deep)
1114 			stats->num_deep --;
1115 
1116 		/* check if that's a symlink */
1117 		if (node->is_symlink)
1118 			stats->num_sym --;
1119 
1120 		/* update file/directory number statistics */
1121 		if (!node->is_imported) {
1122 			if (node->is_file)
1123 				stats->children --;
1124 			else
1125 				stats->num_dir --;
1126 		}
1127 	}
1128 
1129 	/* destruction */
1130 	import = BRASERO_FILE_NODE_IMPORT (node);
1131 	graft = BRASERO_FILE_NODE_GRAFT (node);
1132 	if (graft) {
1133 		BraseroURINode *uri_node;
1134 
1135 		uri_node = graft->node;
1136 
1137 		/* Handle removal from BraseroURINode struct */
1138 		if (uri_node)
1139 			uri_node->nodes = g_slist_remove (uri_node->nodes, node);
1140 
1141 		g_free (graft->name);
1142 		g_free (graft);
1143 	}
1144 	else if (import) {
1145 		/* if imported then destroy the saved children */
1146 		for (child = import->replaced; child; child = next) {
1147 			next = child->next;
1148 			brasero_file_node_destroy_with_children (child, stats);
1149 		}
1150 
1151 		g_free (import->name);
1152 		g_free (import);
1153 	}
1154 	else
1155 		g_free (BRASERO_FILE_NODE_NAME (node));
1156 
1157 	/* destroy the node */
1158 	if (node->is_file && !node->is_imported && BRASERO_FILE_NODE_MIME (node))
1159 		brasero_utils_unregister_string (BRASERO_FILE_NODE_MIME (node));
1160 
1161 	if (node->is_root)
1162 		g_free (BRASERO_FILE_NODE_STATS (node));
1163 
1164 	g_free (node);
1165 }
1166 
1167 /**
1168  * Destroy a node and its children updating the tree stats.
1169  * If it isn't unlinked yet, it does it.
1170  */
1171 void
brasero_file_node_destroy(BraseroFileNode * node,BraseroFileTreeStats * stats)1172 brasero_file_node_destroy (BraseroFileNode *node,
1173 			   BraseroFileTreeStats *stats)
1174 {
1175 	/* remove from the parent children list or more probably from the
1176 	 * import list. */
1177 	if (node->parent)
1178 		brasero_file_node_unlink (node);
1179 
1180 	/* traverse the whole tree and free children updating tree stats */
1181 	brasero_file_node_destroy_with_children (node, stats);
1182 }
1183 
1184 /**
1185  * Pre-remove function that unparent a node (before a possible destruction).
1186  * If node is imported, it saves it in its parent, destroys all child nodes
1187  * that are not imported and restore children that were imported.
1188  * NOTE: tree stats are only updated if the node is imported.
1189  */
1190 
1191 static void
brasero_file_node_save_imported_children(BraseroFileNode * node,BraseroFileTreeStats * stats,GCompareFunc sort_func)1192 brasero_file_node_save_imported_children (BraseroFileNode *node,
1193 					  BraseroFileTreeStats *stats,
1194 					  GCompareFunc sort_func)
1195 {
1196 	BraseroFileNode *iter;
1197 	BraseroImport *import;
1198 
1199 	/* clean children */
1200 	for (iter = BRASERO_FILE_NODE_CHILDREN (node); iter; iter = iter->next) {
1201 		if (!iter->is_imported)
1202 			brasero_file_node_destroy_with_children (iter, stats);
1203 
1204 		if (!iter->is_file)
1205 			brasero_file_node_save_imported_children (iter, stats, sort_func);
1206 	}
1207 
1208 	/* restore all replaced children */
1209 	import = BRASERO_FILE_NODE_IMPORT (node);
1210 	if (!import)
1211 		return;
1212 
1213 	for (iter = import->replaced; iter; iter = iter->next)
1214 		brasero_file_node_insert (iter, node, sort_func, NULL);
1215 
1216 	/* remove import */
1217 	node->union1.name = import->name;
1218 	node->has_import = FALSE;
1219 	g_free (import);
1220 }
1221 
1222 void
brasero_file_node_save_imported(BraseroFileNode * node,BraseroFileTreeStats * stats,BraseroFileNode * parent,GCompareFunc sort_func)1223 brasero_file_node_save_imported (BraseroFileNode *node,
1224 				 BraseroFileTreeStats *stats,
1225 				 BraseroFileNode *parent,
1226 				 GCompareFunc sort_func)
1227 {
1228 	BraseroImport *import;
1229 
1230 	/* if it isn't imported return */
1231 	if (!node->is_imported)
1232 		return;
1233 
1234 	/* Remove all the children that are not imported. Also restore
1235 	 * all children that were replaced so as to restore the original
1236 	 * order of files. */
1237 
1238 	/* that shouldn't happen since root itself is considered imported */
1239 	if (!parent || !parent->is_imported)
1240 		return;
1241 
1242 	/* save the node in its parent import structure */
1243 	import = BRASERO_FILE_NODE_IMPORT (parent);
1244 	if (!import) {
1245 		import = g_new0 (BraseroImport, 1);
1246 		import->name = BRASERO_FILE_NODE_NAME (parent);
1247 		parent->union1.import = import;
1248 		parent->has_import = TRUE;
1249 	}
1250 
1251 	/* unlink it and add it to the list */
1252 	brasero_file_node_unlink (node);
1253 	node->next = import->replaced;
1254 	import->replaced = node;
1255 	node->parent = parent;
1256 
1257 	/* Explore children and remove not imported ones and restore.
1258 	 * Update the tree stats at the same time.
1259 	 * NOTE: here the tree stats are only used for the grafted children that
1260 	 * are not imported in the tree. */
1261 	brasero_file_node_save_imported_children (node, stats, sort_func);
1262 }
1263