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