1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) Philippe Rouquier 2007-2008 <bonfire-app@wanadoo.fr>
6
* Brasero 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.
11
* brasero is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
* See the GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with brasero. If not, write to:
18
* The Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor
20
* Boston, MA 02110-1301, USA.
28
#include <glib/gi18n-lib.h>
32
#include "burn-basics.h"
34
#include "brasero-data-tree-model.h"
35
#include "brasero-data-project.h"
36
#include "brasero-data-vfs.h"
37
#include "brasero-file-node.h"
38
#include "brasero-utils.h"
40
#include "eggtreemultidnd.h"
42
typedef struct _BraseroDataTreeModelPrivate BraseroDataTreeModelPrivate;
43
struct _BraseroDataTreeModelPrivate
52
GtkSortType sort_type;
55
#define BRASERO_DATA_TREE_MODEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_DATA_TREE_MODEL, BraseroDataTreeModelPrivate))
58
BRASERO_ROW_REGULAR = 0,
63
brasero_data_tree_model_multi_drag_source_iface_init (gpointer g_iface, gpointer data);
65
brasero_data_tree_model_drag_source_iface_init (gpointer g_iface, gpointer data);
67
brasero_data_tree_model_drag_dest_iface_init (gpointer g_iface, gpointer data);
69
brasero_data_tree_model_sortable_iface_init (gpointer g_iface, gpointer data);
71
brasero_data_tree_model_iface_init (gpointer g_iface, gpointer data);
73
G_DEFINE_TYPE_WITH_CODE (BraseroDataTreeModel,
74
brasero_data_tree_model,
75
BRASERO_TYPE_DATA_VFS,
76
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
77
brasero_data_tree_model_iface_init)
78
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
79
brasero_data_tree_model_drag_dest_iface_init)
80
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
81
brasero_data_tree_model_drag_source_iface_init)
82
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
83
brasero_data_tree_model_sortable_iface_init)
84
G_IMPLEMENT_INTERFACE (EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
85
brasero_data_tree_model_multi_drag_source_iface_init));
89
brasero_data_tree_model_iter_parent (GtkTreeModel *model,
93
BraseroDataTreeModelPrivate *priv;
94
BraseroFileNode *node;
96
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
98
/* make sure that iter comes from us */
99
g_return_val_if_fail (priv->stamp == child->stamp, FALSE);
100
g_return_val_if_fail (child->user_data != NULL, FALSE);
102
node = child->user_data;
103
if (child->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
104
/* This is a bogus row intended for empty directories
105
* user_data has the parent empty directory. */
106
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
107
iter->user_data = child->user_data;
108
iter->stamp = priv->stamp;
113
iter->user_data = NULL;
117
iter->stamp = priv->stamp;
118
iter->user_data = node->parent;
119
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
124
brasero_data_tree_model_iter_nth_child (GtkTreeModel *model,
129
BraseroDataTreeModelPrivate *priv;
130
BraseroFileNode *node;
132
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
135
/* make sure that iter comes from us */
136
g_return_val_if_fail (priv->stamp == parent->stamp, FALSE);
137
g_return_val_if_fail (parent->user_data != NULL, FALSE);
139
if (parent->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
140
/* This is a bogus row intended for empty directories,
141
* it hasn't got children. */
145
node = parent->user_data;
148
node = brasero_data_project_get_root (BRASERO_DATA_PROJECT (model));
150
iter->user_data = brasero_file_node_nth_child (node, n);
151
if (!iter->user_data)
154
iter->stamp = priv->stamp;
155
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
160
brasero_data_tree_model_iter_n_children (GtkTreeModel *model,
163
BraseroDataTreeModelPrivate *priv;
164
BraseroFileNode *node;
166
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
170
node = brasero_data_project_get_root (BRASERO_DATA_PROJECT (model));
171
return brasero_file_node_get_n_children (node);
174
/* make sure that iter comes from us */
175
g_return_val_if_fail (priv->stamp == iter->stamp, 0);
176
g_return_val_if_fail (iter->user_data != NULL, 0);
178
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS))
181
node = iter->user_data;
185
/* return at least one for the bogus row labelled "empty". */
186
if (!BRASERO_FILE_NODE_CHILDREN (node))
189
return brasero_file_node_get_n_children (node);
193
brasero_data_tree_model_iter_has_child (GtkTreeModel *model,
196
BraseroDataTreeModelPrivate *priv;
197
BraseroFileNode *node;
199
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
201
/* make sure that iter comes from us */
202
g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
203
g_return_val_if_fail (iter->user_data != NULL, FALSE);
205
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
206
/* This is a bogus row intended for empty directories
207
* it hasn't got children */
211
node = iter->user_data;
213
/* This is a workaround for a warning in gailtreeview.c line 2946 where
214
* gail uses the GtkTreePath and not a copy which if the node inserted
215
* declares to have children and is not expanded leads to the path being
216
* upped and therefore wrong. */
217
if (node->is_inserting)
223
/* always return TRUE here when it's a directory since even if
224
* it's empty we'll add a row written empty underneath it
230
brasero_data_tree_model_iter_children (GtkTreeModel *model,
234
BraseroDataTreeModelPrivate *priv;
235
BraseroFileNode *node;
237
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
240
BraseroFileNode *root;
242
/* This is for the top directory */
243
root = brasero_data_project_get_root (BRASERO_DATA_PROJECT (model));
244
if (!root || !BRASERO_FILE_NODE_CHILDREN (root))
247
iter->stamp = priv->stamp;
248
iter->user_data = BRASERO_FILE_NODE_CHILDREN (root);
249
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
253
/* make sure that iter comes from us */
254
g_return_val_if_fail (priv->stamp == parent->stamp, FALSE);
255
g_return_val_if_fail (parent->user_data != NULL, FALSE);
257
if (parent->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
258
iter->user_data = NULL;
262
node = parent->user_data;
264
iter->user_data = NULL;
268
iter->stamp = priv->stamp;
269
if (!BRASERO_FILE_NODE_CHILDREN (node)) {
270
/* This is a directory but it hasn't got any child; yet
271
* we show a row written empty for that. Set bogus in
272
* user_data and put parent in user_data. */
273
iter->user_data = parent->user_data;
274
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_BOGUS);
278
iter->user_data = BRASERO_FILE_NODE_CHILDREN (node);
279
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
284
brasero_data_tree_model_iter_next (GtkTreeModel *model,
287
BraseroDataTreeModelPrivate *priv;
288
BraseroFileNode *node;
290
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
292
/* make sure that iter comes from us */
293
g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
294
g_return_val_if_fail (iter->user_data != NULL, FALSE);
296
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
297
/* This is a bogus row intended for empty directories
298
* user_data has the parent empty directory. It hasn't
300
iter->user_data = NULL;
304
node = iter->user_data;
305
iter->user_data = node->next;
313
brasero_data_tree_model_node_shown (GtkTreeModel *model,
316
BraseroFileNode *node;
317
BraseroDataTreeModelPrivate *priv;
319
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
320
node = iter->user_data;
322
/* Check if that's a BOGUS row. In this case that means the parent was
323
* expanded. Therefore ask vfs to increase its priority if it's loading
325
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
326
/* NOTE: this has to be a directory */
327
/* NOTE: there is no need to check for is_loading case here
328
* since before showing its BOGUS row the tree will have shown
329
* its parent itself and therefore that's the cases that follow
331
if (node->is_exploring) {
332
/* the directory is being explored increase priority */
333
brasero_data_vfs_require_directory_contents (BRASERO_DATA_VFS (model), node);
336
/* Otherwise, that's simply a BOGUS row and its parent was
337
* loaded but it is empty. Nothing to do. */
347
if (node->is_imported) {
348
if (node->is_fake && !node->is_file) {
349
/* we don't load all nodes when importing a session do it now */
350
brasero_data_session_load_directory_contents (BRASERO_DATA_SESSION (model), node, NULL);
356
if (node->is_visible > 1)
359
/* NOTE: no need to see if that's a directory being explored here. If it
360
* is being explored then it has a BOGUS row and that's the above case
361
* that is reached. */
362
if (node->is_loading) {
363
/* in this case have vfs to increase priority for this node */
364
brasero_data_vfs_require_node_load (BRASERO_DATA_VFS (model), node);
366
else if (!BRASERO_FILE_NODE_MIME (node)) {
367
/* that means that file wasn't completly loaded. To save
368
* some time we delayed the detection of the mime type
369
* since that takes a lot of time. */
370
brasero_data_vfs_load_mime (BRASERO_DATA_VFS (model), node);
373
/* add the node to the visible list that is used to update the disc
374
* share for the node (we don't want to update the whole tree).
375
* Moreover, we only want files since directories don't have space. */
376
priv->shown = g_slist_prepend (priv->shown, node);
380
brasero_data_tree_model_node_hidden (GtkTreeModel *model,
383
BraseroFileNode *node;
384
BraseroDataTreeModelPrivate *priv;
386
/* if it's a BOGUS row stop here since they are not added to shown list.
387
* In the same way returns if it is a file. */
388
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS))
391
node = iter->user_data;
398
if (node->is_imported)
401
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
403
/* update shown list */
404
if (!node->is_visible)
405
priv->shown = g_slist_remove (priv->shown, node);
409
brasero_data_tree_model_get_value (GtkTreeModel *model,
414
BraseroDataTreeModelPrivate *priv;
415
BraseroDataTreeModel *self;
416
BraseroFileNode *node;
418
self = BRASERO_DATA_TREE_MODEL (model);
419
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
421
/* make sure that iter comes from us */
422
g_return_if_fail (priv->stamp == iter->stamp);
423
g_return_if_fail (iter->user_data != NULL);
425
node = iter->user_data;
427
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS)) {
429
case BRASERO_DATA_TREE_MODEL_NAME:
430
g_value_init (value, G_TYPE_STRING);
431
if (node->is_exploring)
432
g_value_set_string (value, _("(loading ...)"));
434
g_value_set_string (value, _("Empty"));
438
case BRASERO_DATA_TREE_MODEL_MIME_DESC:
439
case BRASERO_DATA_TREE_MODEL_MIME_ICON:
440
case BRASERO_DATA_TREE_MODEL_SIZE:
441
g_value_init (value, G_TYPE_STRING);
442
g_value_set_string (value, NULL);
445
case BRASERO_DATA_TREE_MODEL_SHOW_PERCENT:
446
g_value_init (value, G_TYPE_BOOLEAN);
447
g_value_set_boolean (value, FALSE);
450
case BRASERO_DATA_TREE_MODEL_PERCENT:
451
g_value_init (value, G_TYPE_INT);
452
g_value_set_int (value, 0);
455
case BRASERO_DATA_TREE_MODEL_STYLE:
456
g_value_init (value, PANGO_TYPE_STYLE);
457
g_value_set_enum (value, PANGO_STYLE_ITALIC);
460
case BRASERO_DATA_TREE_MODEL_EDITABLE:
461
g_value_init (value, G_TYPE_BOOLEAN);
462
g_value_set_boolean (value, FALSE);
465
case BRASERO_DATA_TREE_MODEL_COLOR:
466
g_value_init (value, G_TYPE_STRING);
467
g_value_set_string (value, NULL);
478
case BRASERO_DATA_TREE_MODEL_EDITABLE:
479
g_value_init (value, G_TYPE_BOOLEAN);
480
g_value_set_boolean (value, (node->is_imported == FALSE) && node->is_selected);
483
case BRASERO_DATA_TREE_MODEL_NAME: {
486
g_value_init (value, G_TYPE_STRING);
487
filename = g_filename_to_utf8 (BRASERO_FILE_NODE_NAME (node),
493
filename = brasero_utils_validate_utf8 (BRASERO_FILE_NODE_NAME (node));
496
g_value_set_string (value, filename);
497
else /* Glib section on g_convert advise to use a string like
498
* "Invalid Filename". */
499
g_value_set_string (value, BRASERO_FILE_NODE_NAME (node));
505
case BRASERO_DATA_TREE_MODEL_MIME_DESC:
506
g_value_init (value, G_TYPE_STRING);
507
if (node->is_loading)
508
g_value_set_string (value, _("(loading ...)"));
509
else if (!node->is_file) {
512
description = g_content_type_get_description ("inode/directory");
513
g_value_set_string (value, description);
514
g_free (description);
516
else if (node->is_imported)
517
g_value_set_string (value, _("Disc file"));
518
else if (!BRASERO_FILE_NODE_MIME (node))
519
g_value_set_string (value, _("(loading ...)"));
523
description = g_content_type_get_description (BRASERO_FILE_NODE_MIME (node));
524
g_value_set_string (value, description);
525
g_free (description);
530
case BRASERO_DATA_TREE_MODEL_MIME_ICON:
531
g_value_init (value, G_TYPE_STRING);
532
if (node->is_loading)
533
g_value_set_string (value, "image-loading");
534
else if (!node->is_file) {
535
/* Here we have two states collapsed and expanded */
536
if (node->is_expanded)
537
g_value_set_string (value, "folder-open");
538
else if (node->is_imported)
539
/* that's for all the imported folders */
540
g_value_set_string (value, "folder-visiting");
542
g_value_set_string (value, "folder");
544
else if (node->is_imported) {
545
g_value_set_string (value, "media-cdrom");
547
else if (BRASERO_FILE_NODE_MIME (node)) {
548
const gchar *icon_string = BRASERO_DEFAULT_ICON;
551
/* NOTE: implemented in glib 2.15.6 (not for windows though) */
552
icon = g_content_type_get_icon (BRASERO_FILE_NODE_MIME (node));
553
if (G_IS_THEMED_ICON (icon)) {
554
const gchar * const *names = NULL;
556
names = g_themed_icon_get_names (G_THEMED_ICON (icon));
560
for (i = 0; names [i]; i++) {
561
if (gtk_icon_theme_has_icon (priv->theme, names [i])) {
562
icon_string = names [i];
569
g_value_set_string (value, icon_string);
570
g_object_unref (icon);
573
g_value_set_string (value, "image-loading");
577
case BRASERO_DATA_TREE_MODEL_SIZE:
578
g_value_init (value, G_TYPE_STRING);
579
if (node->is_loading)
580
g_value_set_string (value, _("(loading ...)"));
581
else if (!node->is_file) {
584
if (node->is_exploring) {
585
g_value_set_string (value, _("(loading ...)"));
589
nb_items = brasero_file_node_get_n_children (node);
591
g_value_set_string (value, _("Empty"));
595
text = g_strdup_printf (ngettext ("%d item", "%d items", nb_items), nb_items);
596
g_value_set_string (value, text);
603
text = g_format_size_for_display (BRASERO_FILE_NODE_SECTORS (node) * 2048);
604
g_value_set_string (value, text);
610
case BRASERO_DATA_TREE_MODEL_SHOW_PERCENT:
611
g_value_init (value, G_TYPE_BOOLEAN);
612
if (node->is_imported || node->is_loading)
613
g_value_set_boolean (value, FALSE);
615
g_value_set_boolean (value, TRUE);
619
case BRASERO_DATA_TREE_MODEL_PERCENT:
620
g_value_init (value, G_TYPE_INT);
621
if (!node->is_imported && !brasero_data_vfs_is_active (BRASERO_DATA_VFS (self))) {
625
size = brasero_data_project_get_size (BRASERO_DATA_PROJECT (self));
628
node_size = brasero_data_project_get_folder_size (BRASERO_DATA_PROJECT (self), node);
630
node_size = BRASERO_FILE_NODE_SECTORS (node);
632
g_value_set_int (value, MAX (0, MIN (node_size * 100 / size, 100)));
634
g_value_set_int (value, 0);
637
g_value_set_int (value, 0);
641
case BRASERO_DATA_TREE_MODEL_STYLE:
642
g_value_init (value, PANGO_TYPE_STYLE);
643
if (node->is_imported)
644
g_value_set_enum (value, PANGO_STYLE_ITALIC);
648
case BRASERO_DATA_TREE_MODEL_COLOR:
649
g_value_init (value, G_TYPE_STRING);
650
if (node->is_imported)
651
g_value_set_string (value, "grey50");
663
brasero_data_tree_model_node_to_path (BraseroDataTreeModel *self,
664
BraseroFileNode *node)
666
BraseroDataTreeModelPrivate *priv;
669
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (self);
671
path = gtk_tree_path_new ();
672
for (; node->parent && !node->is_root; node = node->parent) {
675
nth = brasero_file_node_get_pos_as_child (node);
676
gtk_tree_path_prepend_index (path, nth);
683
brasero_data_tree_model_get_path (GtkTreeModel *model,
686
BraseroDataTreeModelPrivate *priv;
687
BraseroFileNode *node;
690
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
692
/* make sure that iter comes from us */
693
g_return_val_if_fail (priv->stamp == iter->stamp, NULL);
694
g_return_val_if_fail (iter->user_data != NULL, NULL);
696
node = iter->user_data;
698
/* NOTE: there is only one single node without a name: root */
699
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (model), node);
701
/* Add index 0 for empty bogus row */
702
if (iter->user_data2 == GINT_TO_POINTER (BRASERO_ROW_BOGUS))
703
gtk_tree_path_append_index (path, 0);
709
brasero_data_tree_model_path_to_node (BraseroDataTreeModel *self,
712
BraseroDataTreeModelPrivate *priv;
713
BraseroFileNode *node;
718
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (self);
720
indices = gtk_tree_path_get_indices (path);
721
depth = gtk_tree_path_get_depth (path);
723
node = brasero_data_project_get_root (BRASERO_DATA_PROJECT (self));
724
for (i = 0; i < depth; i ++) {
725
BraseroFileNode *parent;
728
node = brasero_file_node_nth_child (parent, indices [i]);
736
brasero_data_tree_model_get_iter (GtkTreeModel *model,
740
BraseroDataTreeModelPrivate *priv;
741
BraseroFileNode *root;
742
BraseroFileNode *node;
747
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (model);
749
indices = gtk_tree_path_get_indices (path);
750
depth = gtk_tree_path_get_depth (path);
752
root = brasero_data_project_get_root (BRASERO_DATA_PROJECT (model));
753
/* NOTE: if we're in reset, then root won't exist anymore */
757
node = brasero_file_node_nth_child (root, indices [0]);
761
for (i = 1; i < depth; i ++) {
762
BraseroFileNode *parent;
765
node = brasero_file_node_nth_child (parent, indices [i]);
767
/* There is one case where this can happen and
768
* is allowed: that's when the parent is an
769
* empty directory. Then index must be 0. */
771
&& !BRASERO_FILE_NODE_CHILDREN (parent)
772
&& indices [i] == 0) {
773
iter->stamp = priv->stamp;
774
iter->user_data = parent;
775
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_BOGUS);
779
iter->user_data = NULL;
784
iter->user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
785
iter->stamp = priv->stamp;
786
iter->user_data = node;
792
brasero_data_tree_model_get_column_type (GtkTreeModel *model,
796
case BRASERO_DATA_TREE_MODEL_NAME:
797
return G_TYPE_STRING;
799
case BRASERO_DATA_TREE_MODEL_MIME_DESC:
800
return G_TYPE_STRING;
802
case BRASERO_DATA_TREE_MODEL_MIME_ICON:
803
return G_TYPE_STRING;
805
case BRASERO_DATA_TREE_MODEL_SIZE:
806
return G_TYPE_STRING;
808
case BRASERO_DATA_TREE_MODEL_SHOW_PERCENT:
809
return G_TYPE_BOOLEAN;
811
case BRASERO_DATA_TREE_MODEL_PERCENT:
814
case BRASERO_DATA_TREE_MODEL_STYLE:
815
return PANGO_TYPE_STYLE;
817
case BRASERO_DATA_TREE_MODEL_COLOR:
818
return G_TYPE_STRING;
820
case BRASERO_DATA_TREE_MODEL_EDITABLE:
821
return G_TYPE_BOOLEAN;
827
return G_TYPE_INVALID;
831
brasero_data_tree_model_get_n_columns (GtkTreeModel *model)
833
return BRASERO_DATA_TREE_MODEL_COL_NUM;
836
static GtkTreeModelFlags
837
brasero_data_tree_model_get_flags (GtkTreeModel *model)
843
brasero_data_tree_model_multi_row_draggable (EggTreeMultiDragSource *drag_source,
848
for (iter = path_list; iter && iter->data; iter = iter->next) {
849
GtkTreeRowReference *reference;
850
BraseroFileNode *node;
851
GtkTreePath *treepath;
853
reference = iter->data;
854
treepath = gtk_tree_row_reference_get_path (reference);
855
node = brasero_data_tree_model_path_to_node (BRASERO_DATA_TREE_MODEL (drag_source), treepath);
856
gtk_tree_path_free (treepath);
858
/* at least one row must not be an imported row. */
859
if (node && !node->is_imported)
867
brasero_data_tree_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source,
869
GtkSelectionData *selection_data)
871
if (selection_data->target == gdk_atom_intern (BRASERO_DND_TARGET_SELF_FILE_NODES, TRUE)) {
872
BraseroDNDDataContext context;
874
context.model = GTK_TREE_MODEL (drag_source);
875
context.references = path_list;
877
gtk_selection_data_set (selection_data,
878
gdk_atom_intern_static_string (BRASERO_DND_TARGET_SELF_FILE_NODES),
890
brasero_data_tree_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source,
893
/* NOTE: it's not the data in the selection_data here that should be
894
* deleted but rather the rows selected when there is a move. FALSE
895
* here means that we didn't delete anything. */
896
/* return TRUE to stop other handlers */
901
brasero_data_tree_model_drag_data_received (GtkTreeDragDest *drag_dest,
902
GtkTreePath *dest_path,
903
GtkSelectionData *selection_data)
905
BraseroFileNode *node;
906
BraseroFileNode *parent;
907
GtkTreePath *dest_parent;
908
BraseroDataTreeModel *self;
910
self = BRASERO_DATA_TREE_MODEL (drag_dest);
912
/* NOTE: dest_path is the path to insert before; so we may not have a
913
* valid path if it's in an empty directory */
915
dest_parent = gtk_tree_path_copy (dest_path);
916
gtk_tree_path_up (dest_parent);
917
parent = brasero_data_tree_model_path_to_node (self, dest_parent);
919
gtk_tree_path_up (dest_parent);
920
parent = brasero_data_tree_model_path_to_node (self, dest_parent);
922
else if (parent->is_file)
923
parent = parent->parent;
925
gtk_tree_path_free (dest_parent);
927
/* Received data: see where it comes from:
928
* - from us, then that's a simple move
929
* - from another widget then it's going to be URIS and we add
930
* them to the DataProject */
931
if (selection_data->target == gdk_atom_intern (BRASERO_DND_TARGET_SELF_FILE_NODES, TRUE)) {
932
BraseroDNDDataContext *context;
935
context = (BraseroDNDDataContext *) selection_data->data;
936
if (context->model != GTK_TREE_MODEL (drag_dest))
939
/* That's us: move the row and its children. */
940
for (iter = context->references; iter; iter = iter->next) {
941
GtkTreeRowReference *reference;
942
GtkTreePath *treepath;
944
reference = iter->data;
945
treepath = gtk_tree_row_reference_get_path (reference);
947
node = brasero_data_tree_model_path_to_node (BRASERO_DATA_TREE_MODEL (drag_dest), treepath);
948
gtk_tree_path_free (treepath);
950
brasero_data_project_move_node (BRASERO_DATA_PROJECT (self), node, parent);
953
else if (selection_data->target == gdk_atom_intern ("text/uri-list", TRUE)) {
956
gboolean success = FALSE;
958
/* NOTE: there can be many URIs at the same time. One
959
* success is enough to return TRUE. */
961
uris = gtk_selection_data_get_uris (selection_data);
965
for (i = 0; uris [i]; i ++) {
966
BraseroFileNode *node;
968
/* Add the URIs to the project */
969
node = brasero_data_project_add_loading_node (BRASERO_DATA_PROJECT (self),
984
brasero_data_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest,
985
GtkTreePath *dest_path,
986
GtkSelectionData *selection_data)
988
/* See if we are dropping to ourselves */
989
if (selection_data->target == gdk_atom_intern_static_string (BRASERO_DND_TARGET_SELF_FILE_NODES)) {
990
BraseroDNDDataContext *context;
991
GtkTreePath *dest_parent;
992
BraseroFileNode *parent;
995
context = (BraseroDNDDataContext *) selection_data->data;
996
if (context->model != GTK_TREE_MODEL (drag_dest))
999
/* make sure the parent is a directory.
1000
* NOTE: in this case dest_path is the exact path where it
1001
* should be inserted. */
1002
dest_parent = gtk_tree_path_copy (dest_path);
1003
gtk_tree_path_up (dest_parent);
1005
parent = brasero_data_tree_model_path_to_node (BRASERO_DATA_TREE_MODEL (drag_dest), dest_parent);
1008
/* See if that isn't a BOGUS row; if so, try with parent */
1009
gtk_tree_path_up (dest_parent);
1010
parent = brasero_data_tree_model_path_to_node (BRASERO_DATA_TREE_MODEL (drag_dest), dest_parent);
1013
gtk_tree_path_free (dest_parent);
1017
else if (parent->is_file) {
1018
/* if that's a file try with parent */
1019
gtk_tree_path_up (dest_parent);
1020
parent = parent->parent;
1023
if (parent->is_loading) {
1024
gtk_tree_path_free (dest_parent);
1028
for (iter = context->references; iter; iter = iter->next) {
1029
GtkTreePath *src_path;
1030
GtkTreeRowReference *reference;
1032
reference = iter->data;
1033
src_path = gtk_tree_row_reference_get_path (reference);
1035
/* see if we are not moving a parent to one of its children */
1036
if (gtk_tree_path_is_ancestor (src_path, dest_path)) {
1037
gtk_tree_path_free (src_path);
1041
if (gtk_tree_path_up (src_path)) {
1042
/* check that node was moved to another directory */
1043
if (!parent->parent) {
1044
if (gtk_tree_path_get_depth (src_path)) {
1045
gtk_tree_path_free (src_path);
1046
gtk_tree_path_free (dest_parent);
1050
else if (!gtk_tree_path_get_depth (src_path)
1051
|| gtk_tree_path_compare (src_path, dest_parent)) {
1052
gtk_tree_path_free (src_path);
1053
gtk_tree_path_free (dest_parent);
1058
gtk_tree_path_free (src_path);
1061
gtk_tree_path_free (dest_parent);
1064
else if (selection_data->target == gdk_atom_intern_static_string ("text/uri-list"))
1071
brasero_data_tree_model_drag_data_delete (GtkTreeDragSource *source,
1072
GtkTreePath *treepath)
1081
brasero_data_tree_model_get_sort_column_id (GtkTreeSortable *sortable,
1085
BraseroDataTreeModelPrivate *priv;
1087
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (sortable);
1090
*column = priv->sort_column;
1093
*type = priv->sort_type;
1099
brasero_data_tree_model_set_sort_column_id (GtkTreeSortable *sortable,
1103
BraseroDataTreeModelPrivate *priv;
1105
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (sortable);
1106
priv->sort_column = column;
1107
priv->sort_type = type;
1110
case BRASERO_DATA_TREE_MODEL_NAME:
1111
brasero_data_project_set_sort_function (BRASERO_DATA_PROJECT (sortable),
1113
brasero_file_node_sort_name_cb);
1115
case BRASERO_DATA_TREE_MODEL_SIZE:
1116
brasero_data_project_set_sort_function (BRASERO_DATA_PROJECT (sortable),
1118
brasero_file_node_sort_size_cb);
1120
case BRASERO_DATA_TREE_MODEL_MIME_DESC:
1121
brasero_data_project_set_sort_function (BRASERO_DATA_PROJECT (sortable),
1123
brasero_file_node_sort_mime_cb);
1126
brasero_data_project_set_sort_function (BRASERO_DATA_PROJECT (sortable),
1128
brasero_file_node_sort_default_cb);
1132
gtk_tree_sortable_sort_column_changed (sortable);
1136
brasero_data_tree_model_has_default_sort_func (GtkTreeSortable *sortable)
1138
/* That's always true since we sort files and directories */
1143
brasero_data_tree_model_clear (BraseroDataTreeModel *self,
1147
GtkTreePath *treepath;
1148
BraseroDataTreeModelPrivate *priv;
1150
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (self);
1152
g_slist_free (priv->shown);
1156
/* NOTE: no need to move to the next row since previous one was deleted */
1157
treepath = gtk_tree_path_new_first ();
1158
for (i = 0; i < num_nodes; i ++)
1159
gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), treepath);
1161
gtk_tree_path_free (treepath);
1165
brasero_data_tree_model_reset (BraseroDataProject *project,
1168
brasero_data_tree_model_clear (BRASERO_DATA_TREE_MODEL (project), num_nodes);
1170
/* chain up this function except if we invalidated the node */
1171
if (BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->reset)
1172
BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->reset (project, num_nodes);
1176
brasero_data_tree_model_node_added (BraseroDataProject *project,
1177
BraseroFileNode *node,
1180
BraseroDataTreeModelPrivate *priv;
1181
BraseroFileNode *parent;
1185
/* see if we really need to tell the treeview we changed */
1187
&& !node->parent->is_root
1188
&& !node->parent->is_visible)
1191
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (project);
1193
iter.stamp = priv->stamp;
1194
iter.user_data = node;
1195
iter.user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
1197
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), node);
1199
/* if the node is reloading (because of a file system change or because
1200
* it was a node that was a tmp folder) then no need to signal an added
1201
* signal but a changed one */
1202
if (node->is_reloading) {
1203
gtk_tree_model_row_changed (GTK_TREE_MODEL (project), path, &iter);
1204
gtk_tree_path_free (path);
1208
/* Add the row itself */
1209
/* This is a workaround for a warning in gailtreeview.c line 2946 where
1210
* gail uses the GtkTreePath and not a copy which if the node inserted
1211
* declares to have children and is not expanded leads to the path being
1212
* upped and therefore wrong. */
1213
node->is_inserting = 1;
1214
gtk_tree_model_row_inserted (GTK_TREE_MODEL (project),
1217
node->is_inserting = 0;
1218
gtk_tree_path_free (path);
1220
parent = node->parent;
1221
if (!parent->is_root) {
1222
/* Tell the tree that the parent changed (since the number of children
1223
* changed as well). */
1224
iter.user_data = parent;
1225
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), parent);
1227
gtk_tree_model_row_changed (GTK_TREE_MODEL (project), path, &iter);
1229
/* Check if the parent of this node is empty if so remove the BOGUS row.
1230
* Do it afterwards to prevent the parent row to be collapsed if it was
1231
* previously expanded. */
1232
if (parent && brasero_file_node_get_n_children (parent) == 1) {
1233
gtk_tree_path_append_index (path, 1);
1234
gtk_tree_model_row_deleted (GTK_TREE_MODEL (project), path);
1237
gtk_tree_path_free (path);
1240
/* Now see if this is a directory which is empty and needs a BOGUS */
1241
if (!node->is_file && !node->is_loading) {
1242
/* emit child-toggled. Thanks to bogus rows we only need to emit
1243
* this signal once since a directory will always have a child
1245
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), node);
1246
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (project), path, &iter);
1247
gtk_tree_path_free (path);
1250
/* we also have to set the is_visible property as all nodes added to
1251
* root are always visible but ref_node is not necessarily called on
1253
// if (parent->is_root)
1254
// node->is_visible = TRUE;
1257
/* chain up this function */
1258
if (BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_added)
1259
return BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_added (project, node, uri);
1265
brasero_data_tree_model_node_removed (BraseroDataProject *project,
1266
BraseroFileNode *former_parent,
1267
guint former_position,
1268
BraseroFileNode *node)
1270
BraseroDataTreeModelPrivate *priv;
1271
GSList *iter, *next;
1274
/* see if we really need to tell the treeview we changed */
1275
if (!node->is_visible
1277
&& !former_parent->is_root
1278
&& !former_parent->is_visible)
1281
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (project);
1283
/* remove it from the shown list and all its children as well */
1284
priv->shown = g_slist_remove (priv->shown, node);
1285
for (iter = priv->shown; iter; iter = next) {
1286
BraseroFileNode *tmp;
1290
if (brasero_file_node_is_ancestor (node, tmp))
1291
priv->shown = g_slist_remove (priv->shown, tmp);
1294
/* See if the parent of this node still has children. If not we need to
1295
* add a bogus row. If it hasn't got children then it only remains our
1297
* NOTE: parent has to be a directory. */
1298
if (!former_parent->is_root && !BRASERO_FILE_NODE_CHILDREN (former_parent)) {
1301
iter.stamp = priv->stamp;
1302
iter.user_data = former_parent;
1303
iter.user_data2 = GINT_TO_POINTER (BRASERO_ROW_BOGUS);
1305
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), former_parent);
1306
gtk_tree_path_append_index (path, 1);
1308
gtk_tree_model_row_inserted (GTK_TREE_MODEL (project), path, &iter);
1309
gtk_tree_path_free (path);
1312
/* remove the node. Do it after adding a possible BOGUS row.
1313
* NOTE since BOGUS row has been added move row. */
1314
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), former_parent);
1315
gtk_tree_path_append_index (path, former_position);
1317
gtk_tree_model_row_deleted (GTK_TREE_MODEL (project), path);
1318
gtk_tree_path_free (path);
1321
/* chain up this function */
1322
if (BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_removed)
1323
BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_removed (project,
1330
brasero_data_tree_model_node_changed (BraseroDataProject *project,
1331
BraseroFileNode *node)
1333
BraseroDataTreeModelPrivate *priv;
1337
/* see if we really need to tell the treeview we changed */
1339
&& !node->parent->is_root
1340
&& !node->parent->is_visible)
1343
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (project);
1345
/* Get the iter for the node */
1346
iter.stamp = priv->stamp;
1347
iter.user_data = node;
1348
iter.user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
1350
path = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), node);
1351
gtk_tree_model_row_changed (GTK_TREE_MODEL (project),
1355
/* Now see if this is a directory which is empty and needs a BOGUS */
1356
if (!node->is_file) {
1357
/* NOTE: No need to check for the number of children ... */
1359
/* emit child-toggled. Thanks to bogus rows we only need to emit
1360
* this signal once since a directory will always have a child
1362
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (project),
1366
/* The problem is that without that, the folder contents on disc
1367
* won't be added to the tree if the node it replaced was
1368
* already visible. */
1369
if (node->is_imported
1372
brasero_data_session_load_directory_contents (BRASERO_DATA_SESSION (project),
1377
if (!BRASERO_FILE_NODE_CHILDREN (node)) {
1378
iter.user_data2 = GINT_TO_POINTER (BRASERO_ROW_BOGUS);
1379
gtk_tree_path_append_index (path, 0);
1381
gtk_tree_model_row_inserted (GTK_TREE_MODEL (project),
1386
gtk_tree_path_free (path);
1389
/* chain up this function */
1390
if (BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_changed)
1391
BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_changed (project, node);
1395
brasero_data_tree_model_node_reordered (BraseroDataProject *project,
1396
BraseroFileNode *parent,
1399
GtkTreePath *treepath;
1400
BraseroDataTreeModelPrivate *priv;
1402
/* see if we really need to tell the treeview we changed */
1403
if (!parent->is_root
1404
&& !parent->is_visible)
1407
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (project);
1409
treepath = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (project), parent);
1410
if (parent != brasero_data_project_get_root (project)) {
1413
iter.stamp = priv->stamp;
1414
iter.user_data = parent;
1415
iter.user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
1417
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (project),
1423
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (project),
1428
gtk_tree_path_free (treepath);
1431
/* chain up this function */
1432
if (BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_reordered)
1433
BRASERO_DATA_PROJECT_CLASS (brasero_data_tree_model_parent_class)->node_reordered (project, parent, new_order);
1437
brasero_data_tree_model_activity_changed (BraseroDataVFS *vfs,
1442
BraseroDataTreeModelPrivate *priv;
1444
if (brasero_data_vfs_is_active (vfs))
1447
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (vfs);
1449
iter.stamp = priv->stamp;
1450
iter.user_data2 = GINT_TO_POINTER (BRASERO_ROW_REGULAR);
1452
/* NOTE: we shouldn't need to use reference here as unref_node is used */
1453
for (nodes = priv->shown; nodes; nodes = nodes->next) {
1454
GtkTreePath *treepath;
1456
iter.user_data = nodes->data;
1457
treepath = brasero_data_tree_model_node_to_path (BRASERO_DATA_TREE_MODEL (vfs), nodes->data);
1459
gtk_tree_model_row_changed (GTK_TREE_MODEL (vfs), treepath, &iter);
1460
gtk_tree_path_free (treepath);
1463
/* chain up this function */
1464
if (BRASERO_DATA_VFS_CLASS (brasero_data_tree_model_parent_class)->activity_changed)
1465
BRASERO_DATA_VFS_CLASS (brasero_data_tree_model_parent_class)->activity_changed (vfs, active);
1469
brasero_data_tree_model_init (BraseroDataTreeModel *object)
1471
BraseroDataTreeModelPrivate *priv;
1473
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (object);
1475
priv->sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
1477
priv->stamp = g_random_int ();
1478
} while (!priv->stamp);
1480
priv->theme = gtk_icon_theme_get_default ();
1484
brasero_data_tree_model_finalize (GObject *object)
1486
BraseroDataTreeModelPrivate *priv;
1488
priv = BRASERO_DATA_TREE_MODEL_PRIVATE (object);
1491
g_slist_free (priv->shown);
1495
G_OBJECT_CLASS (brasero_data_tree_model_parent_class)->finalize (object);
1499
brasero_data_tree_model_iface_init (gpointer g_iface, gpointer data)
1501
GtkTreeModelIface *iface = g_iface;
1502
static gboolean initialized = FALSE;
1509
iface->ref_node = brasero_data_tree_model_node_shown;
1510
iface->unref_node = brasero_data_tree_model_node_hidden;
1512
iface->get_flags = brasero_data_tree_model_get_flags;
1513
iface->get_n_columns = brasero_data_tree_model_get_n_columns;
1514
iface->get_column_type = brasero_data_tree_model_get_column_type;
1515
iface->get_iter = brasero_data_tree_model_get_iter;
1516
iface->get_path = brasero_data_tree_model_get_path;
1517
iface->get_value = brasero_data_tree_model_get_value;
1518
iface->iter_next = brasero_data_tree_model_iter_next;
1519
iface->iter_children = brasero_data_tree_model_iter_children;
1520
iface->iter_has_child = brasero_data_tree_model_iter_has_child;
1521
iface->iter_n_children = brasero_data_tree_model_iter_n_children;
1522
iface->iter_nth_child = brasero_data_tree_model_iter_nth_child;
1523
iface->iter_parent = brasero_data_tree_model_iter_parent;
1527
brasero_data_tree_model_multi_drag_source_iface_init (gpointer g_iface, gpointer data)
1529
EggTreeMultiDragSourceIface *iface = g_iface;
1530
static gboolean initialized = FALSE;
1537
iface->row_draggable = brasero_data_tree_model_multi_row_draggable;
1538
iface->drag_data_get = brasero_data_tree_model_multi_drag_data_get;
1539
iface->drag_data_delete = brasero_data_tree_model_multi_drag_data_delete;
1543
brasero_data_tree_model_drag_source_iface_init (gpointer g_iface, gpointer data)
1545
GtkTreeDragSourceIface *iface = g_iface;
1546
static gboolean initialized = FALSE;
1553
iface->drag_data_delete = brasero_data_tree_model_drag_data_delete;
1557
brasero_data_tree_model_drag_dest_iface_init (gpointer g_iface, gpointer data)
1559
GtkTreeDragDestIface *iface = g_iface;
1560
static gboolean initialized = FALSE;
1567
iface->drag_data_received = brasero_data_tree_model_drag_data_received;
1568
iface->row_drop_possible = brasero_data_tree_model_row_drop_possible;
1572
brasero_data_tree_model_sortable_iface_init (gpointer g_iface, gpointer data)
1574
GtkTreeSortableIface *iface = g_iface;
1575
static gboolean initialized = FALSE;
1582
iface->get_sort_column_id = brasero_data_tree_model_get_sort_column_id;
1583
iface->set_sort_column_id = brasero_data_tree_model_set_sort_column_id;
1584
iface->has_default_sort_func = brasero_data_tree_model_has_default_sort_func;
1588
brasero_data_tree_model_class_init (BraseroDataTreeModelClass *klass)
1590
GObjectClass* object_class = G_OBJECT_CLASS (klass);
1591
BraseroDataVFSClass *vfs_class = BRASERO_DATA_VFS_CLASS (klass);
1592
BraseroDataProjectClass *data_project_class = BRASERO_DATA_PROJECT_CLASS (klass);
1594
g_type_class_add_private (klass, sizeof (BraseroDataTreeModelPrivate));
1596
object_class->finalize = brasero_data_tree_model_finalize;
1598
vfs_class->activity_changed = brasero_data_tree_model_activity_changed;
1600
data_project_class->reset = brasero_data_tree_model_reset;
1601
data_project_class->node_added = brasero_data_tree_model_node_added;
1602
data_project_class->node_removed = brasero_data_tree_model_node_removed;
1603
data_project_class->node_changed = brasero_data_tree_model_node_changed;
1604
data_project_class->node_reordered = brasero_data_tree_model_node_reordered;
1607
BraseroDataTreeModel *
1608
brasero_data_tree_model_new (void)
1610
return g_object_new (BRASERO_TYPE_DATA_TREE_MODEL, NULL);