1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3
/* fm-list-model.h - a GtkTreeModel for file lists.
5
Copyright (C) 2001, 2002 Anders Carlsson
6
Copyright (C) 2003, Soeren Sandmann
7
Copyright (C) 2004, Novell, Inc.
9
The Gnome Library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Library General Public License as
11
published by the Free Software Foundation; either version 2 of the
12
License, or (at your option) any later version.
14
The Gnome Library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Library General Public License for more details.
19
You should have received a copy of the GNU Library General Public
20
License along with the Gnome Library; see the file COPYING.LIB. If not,
21
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22
Boston, MA 02111-1307, USA.
24
Authors: Anders Carlsson <andersca@gnu.org>, Soeren Sandmann (sandmann@daimi.au.dk), Dave Camp <dave@ximian.com>
28
//#include <libegg/eggtreemultidnd.h>
31
/*#include <eel/eel-gtk-macros.h>
32
#include <eel/eel-glib-extensions.h>
33
#include <eel/eel-gdk-pixbuf-extensions.h>*/
35
#include <glib/gi18n.h>
36
//#include <libnautilus-private/nautilus-dnd.h>
38
#include "fm-list-model.h"
41
SUBDIRECTORY_UNLOADED,
50
static GQuark attribute_name_q,
51
attribute_modification_date_q,
52
attribute_date_modified_q;
54
static guint list_model_signals[LAST_SIGNAL] = { 0 };
56
static void fm_list_model_get_property (GObject *object,
60
static void fm_list_model_set_property (GObject *object,
64
static int fm_list_model_file_entry_compare_func (gconstpointer a,
67
static void fm_list_model_tree_model_init (GtkTreeModelIface *iface);
68
static void fm_list_model_sortable_init (GtkTreeSortableIface *iface);
69
//static void fm_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface);
71
struct FMListModelDetails {
73
GHashTable *directory_reverse_map; /* map from directory to GSequenceIter's */
74
GHashTable *top_reverse_map; /* map from files in top dir to GSequenceIter's */
79
//GQuark sort_attribute;
83
gboolean sort_directories_first;
85
/*GtkTreeView *drag_view;
98
typedef struct FileEntry FileEntry;
102
GHashTable *reverse_map; /* map from files to GSequenceIter's */
103
GOFDirectoryAsync *subdirectory;
110
void fm_list_model_remove_file (FMListModel *model, GOFFile *file,
111
GOFDirectoryAsync *directory);
113
G_DEFINE_TYPE_WITH_CODE (FMListModel, fm_list_model, G_TYPE_OBJECT,
114
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
115
fm_list_model_tree_model_init)
116
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
117
fm_list_model_sortable_init));
118
/* G_IMPLEMENT_INTERFACE (EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
119
fm_list_model_multi_drag_source_init));*/
121
/*static const GtkTargetEntry drag_types [] = {
122
{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
123
{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
126
//static GtkTargetList *drag_target_list = NULL;
129
file_entry_free (FileEntry *file_entry)
131
gof_file_unref (file_entry->file);
132
//gof_file_unref (file_entry->file);
133
if (file_entry->reverse_map) {
134
g_hash_table_destroy (file_entry->reverse_map);
135
file_entry->reverse_map = NULL;
137
if (file_entry->subdirectory != NULL) {
138
g_object_unref (file_entry->subdirectory);
139
file_entry->subdirectory = NULL;
141
if (file_entry->files != NULL) {
142
g_sequence_free (file_entry->files);
148
static GtkTreeModelFlags
149
fm_list_model_get_flags (GtkTreeModel *tree_model)
151
//return GTK_TREE_MODEL_ITERS_PERSIST;
152
return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
156
fm_list_model_get_n_columns (GtkTreeModel *tree_model)
158
//return FM_LIST_MODEL_NUM_COLUMNS + FM_LIST_MODEL (tree_model)->details->columns->len;
159
return FM_LIST_MODEL_NUM_COLUMNS;
163
fm_list_model_get_column_type (GtkTreeModel *tree_model, int index)
166
case FM_LIST_MODEL_FILE_COLUMN:
167
return GOF_TYPE_FILE;
168
/*case FM_LIST_MODEL_SUBDIRECTORY_COLUMN:
169
return NAUTILUS_TYPE_DIRECTORY;*/
170
/*case FM_LIST_MODEL_SMALLEST_ICON_COLUMN:
171
case FM_LIST_MODEL_SMALLER_ICON_COLUMN:
172
case FM_LIST_MODEL_SMALL_ICON_COLUMN:
173
case FM_LIST_MODEL_STANDARD_ICON_COLUMN:
174
case FM_LIST_MODEL_LARGE_ICON_COLUMN:
175
case FM_LIST_MODEL_LARGER_ICON_COLUMN:
176
case FM_LIST_MODEL_LARGEST_ICON_COLUMN:
177
case FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN:
178
case FM_LIST_MODEL_SMALLER_EMBLEM_COLUMN:
179
case FM_LIST_MODEL_SMALL_EMBLEM_COLUMN:
180
case FM_LIST_MODEL_STANDARD_EMBLEM_COLUMN:
181
case FM_LIST_MODEL_LARGE_EMBLEM_COLUMN:
182
case FM_LIST_MODEL_LARGER_EMBLEM_COLUMN:
183
case FM_LIST_MODEL_LARGEST_EMBLEM_COLUMN:
184
return GDK_TYPE_PIXBUF;
185
case FM_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN:
186
return G_TYPE_BOOLEAN;*/
187
case FM_LIST_MODEL_ICON:
188
return GDK_TYPE_PIXBUF;
190
if (index < FM_LIST_MODEL_NUM_COLUMNS) {
191
return G_TYPE_STRING;
193
return G_TYPE_INVALID;
197
if (index < FM_LIST_MODEL_NUM_COLUMNS + FM_LIST_MODEL (tree_model)->details->columns->len) {
198
return G_TYPE_STRING;
200
return G_TYPE_INVALID;
206
fm_list_model_ptr_to_iter (FMListModel *model, GSequenceIter *ptr, GtkTreeIter *iter)
208
g_assert (!g_sequence_iter_is_end (ptr));
210
iter->stamp = model->details->stamp;
211
iter->user_data = ptr;
216
fm_list_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
221
FileEntry *file_entry;
224
model = (FMListModel *)tree_model;
227
files = model->details->files;
228
for (d = 0; d < gtk_tree_path_get_depth (path); d++) {
229
i = gtk_tree_path_get_indices (path)[d];
231
if (files == NULL || i >= g_sequence_get_length (files)) {
235
ptr = g_sequence_get_iter_at_pos (files, i);
236
file_entry = g_sequence_get (ptr);
237
files = file_entry->files;
240
fm_list_model_ptr_to_iter (model, ptr, iter);
246
fm_list_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter)
251
FileEntry *file_entry;
254
model = (FMListModel *)tree_model;
256
g_return_val_if_fail (iter->stamp == model->details->stamp, NULL);
258
if (g_sequence_iter_is_end (iter->user_data)) {
259
/* FIXME is this right? */
263
path = gtk_tree_path_new ();
264
ptr = iter->user_data;
265
while (ptr != NULL) {
266
gtk_tree_path_prepend_index (path, g_sequence_iter_get_position (ptr));
267
file_entry = g_sequence_get (ptr);
268
if (file_entry->parent != NULL) {
269
ptr = file_entry->parent->ptr;
279
fm_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int column, GValue *value)
282
FileEntry *file_entry;
289
//NautilusZoomLevel zoom_level;
290
//GList *emblem_pixbufs;
291
//GOFFile *parent_file;
292
//char *emblems_to_ignore[3];
294
//GOFFileIconFlags flags;
296
model = (FMListModel *)tree_model;
298
g_return_if_fail (model->details->stamp == iter->stamp);
299
g_return_if_fail (!g_sequence_iter_is_end (iter->user_data));
301
file_entry = g_sequence_get (iter->user_data);
302
file = file_entry->file;
305
case FM_LIST_MODEL_FILE_COLUMN:
306
g_value_init (value, GOF_TYPE_FILE);
308
g_value_set_object (value, file);
311
case FM_LIST_MODEL_ICON:
312
g_value_init (value, GDK_TYPE_PIXBUF);
314
g_value_set_object (value, file->pix);
317
case FM_LIST_MODEL_FILENAME:
318
g_value_init (value, G_TYPE_STRING);
320
g_value_set_string(value, file->name);
323
case FM_LIST_MODEL_SIZE:
324
g_value_init (value, G_TYPE_STRING);
326
g_value_set_string(value, file->format_size);
329
case FM_LIST_MODEL_TYPE:
330
g_value_init (value, G_TYPE_STRING);
332
g_value_set_string(value, file->ftype);
335
case FM_LIST_MODEL_MODIFIED:
336
g_value_init (value, G_TYPE_STRING);
338
g_value_set_string(value, gof_file_get_date_as_string (file->modified));
344
/* case FM_LIST_MODEL_SUBDIRECTORY_COLUMN:
345
g_value_init (value, NAUTILUS_TYPE_DIRECTORY);
347
g_value_set_object (value, file_entry->subdirectory);
349
case FM_LIST_MODEL_SMALLEST_ICON_COLUMN:
350
case FM_LIST_MODEL_SMALLER_ICON_COLUMN:
351
case FM_LIST_MODEL_SMALL_ICON_COLUMN:
352
case FM_LIST_MODEL_STANDARD_ICON_COLUMN:
353
case FM_LIST_MODEL_LARGE_ICON_COLUMN:
354
case FM_LIST_MODEL_LARGER_ICON_COLUMN:
355
case FM_LIST_MODEL_LARGEST_ICON_COLUMN:
356
g_value_init (value, GDK_TYPE_PIXBUF);
359
//zoom_level = fm_list_model_get_zoom_level_from_column_id (column);
360
//icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level);
363
/*flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS |
364
NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE |
365
NAUTILUS_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM;*/
366
/*if (model->details->drag_view != NULL) {
367
GtkTreePath *path_a, *path_b;
369
gtk_tree_view_get_drag_dest_row (model->details->drag_view,
372
if (path_a != NULL) {
373
path_b = gtk_tree_model_get_path (tree_model, iter);
375
if (gtk_tree_path_compare (path_a, path_b) == 0) {
376
flags |= NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT;
379
gtk_tree_path_free (path_a);
380
gtk_tree_path_free (path_b);
384
//icon = nautilus_file_get_icon_pixbuf (file, icon_size, TRUE, flags);
387
g_value_set_object (value, icon);
388
g_object_unref (icon);
391
case FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN:
392
case FM_LIST_MODEL_SMALLER_EMBLEM_COLUMN:
393
case FM_LIST_MODEL_SMALL_EMBLEM_COLUMN:
394
case FM_LIST_MODEL_STANDARD_EMBLEM_COLUMN:
395
case FM_LIST_MODEL_LARGE_EMBLEM_COLUMN:
396
case FM_LIST_MODEL_LARGER_EMBLEM_COLUMN:
397
case FM_LIST_MODEL_LARGEST_EMBLEM_COLUMN:
398
g_value_init (value, GDK_TYPE_PIXBUF);
401
parent_file = nautilus_file_get_parent (file);
403
emblems_to_ignore[i++] = NAUTILUS_FILE_EMBLEM_NAME_TRASH;
405
if (!nautilus_file_can_write (parent_file)) {
406
emblems_to_ignore[i++] = NAUTILUS_FILE_EMBLEM_NAME_CANT_WRITE;
408
nautilus_file_unref (parent_file);
410
emblems_to_ignore[i++] = NULL;
412
zoom_level = fm_list_model_get_zoom_level_from_emblem_column_id (column);
413
icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level);
414
emblem_size = nautilus_icon_get_emblem_size_for_icon_size (icon_size);
415
if (emblem_size != 0) {
416
emblem_pixbufs = nautilus_file_get_emblem_pixbufs (file,
420
if (emblem_pixbufs != NULL) {
421
icon = emblem_pixbufs->data;
422
g_value_set_object (value, icon);
424
eel_gdk_pixbuf_list_free (emblem_pixbufs);
427
g_value_set_object (value, NULL);
429
case FM_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN:
430
g_value_init (value, G_TYPE_BOOLEAN);
432
g_value_set_boolean (value, file != NULL && nautilus_file_can_rename (file));
435
if (column >= FM_LIST_MODEL_NUM_COLUMNS || column < FM_LIST_MODEL_NUM_COLUMNS + model->details->columns->len) {
436
NautilusColumn *nautilus_column;
438
nautilus_column = model->details->columns->pdata[column - FM_LIST_MODEL_NUM_COLUMNS];
440
g_value_init (value, G_TYPE_STRING);
441
g_object_get (nautilus_column,
442
"attribute_q", &attribute,
445
str = nautilus_file_get_string_attribute_with_default_q (file,
447
g_value_take_string (value, str);
448
} else if (attribute == attribute_name_q) {
449
if (file_entry->parent->loaded) {
450
g_value_set_string (value, _("(Empty)"));
452
g_value_set_string (value, _("Loading..."));
456
g_assert_not_reached ();
463
fm_list_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter)
467
model = (FMListModel *)tree_model;
469
g_return_val_if_fail (model->details->stamp == iter->stamp, FALSE);
471
iter->user_data = g_sequence_iter_next (iter->user_data);
473
return !g_sequence_iter_is_end (iter->user_data);
477
fm_list_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent)
481
FileEntry *file_entry;
483
model = (FMListModel *)tree_model;
485
if (parent == NULL) {
486
files = model->details->files;
488
file_entry = g_sequence_get (parent->user_data);
489
files = file_entry->files;
492
if (files == NULL || g_sequence_get_length (files) == 0) {
496
iter->stamp = model->details->stamp;
497
iter->user_data = g_sequence_get_begin_iter (files);
503
fm_list_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
505
FMListModel *model = (FMListModel *)tree_model;
507
if (!model->details->has_child)
509
FileEntry *file_entry;
512
return !fm_list_model_is_empty (FM_LIST_MODEL (tree_model));
515
file_entry = g_sequence_get (iter->user_data);
516
return (file_entry->files != NULL && g_sequence_get_length (file_entry->files) > 0);
520
fm_list_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter)
524
FileEntry *file_entry;
526
model = (FMListModel *)tree_model;
529
files = model->details->files;
531
file_entry = g_sequence_get (iter->user_data);
532
files = file_entry->files;
535
return g_sequence_get_length (files);
539
fm_list_model_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, int n)
542
GSequenceIter *child;
544
FileEntry *file_entry;
546
model = (FMListModel *)tree_model;
548
if (parent != NULL) {
549
file_entry = g_sequence_get (parent->user_data);
550
files = file_entry->files;
552
files = model->details->files;
555
child = g_sequence_get_iter_at_pos (files, n);
557
if (g_sequence_iter_is_end (child)) {
561
iter->stamp = model->details->stamp;
562
iter->user_data = child;
568
fm_list_model_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child)
571
FileEntry *file_entry;
573
model = (FMListModel *)tree_model;
575
file_entry = g_sequence_get (child->user_data);
577
if (file_entry->parent == NULL) {
581
iter->stamp = model->details->stamp;
582
iter->user_data = file_entry->parent->ptr;
587
static GSequenceIter *
588
lookup_file (FMListModel *model, GOFFile *file, GOFDirectoryAsync *directory)
590
FileEntry *file_entry;
591
GSequenceIter *ptr, *parent_ptr;
595
parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
600
/* we re looking for a folder */
601
if (file->is_directory)
603
file_entry = g_sequence_get (parent_ptr);
604
ptr = g_hash_table_lookup (file_entry->reverse_map, file);
606
ptr = g_hash_table_lookup (model->details->top_reverse_map, file);
610
g_assert (((FileEntry *)g_sequence_get (ptr))->file == file);
624
dir_to_iters (struct GetIters *data,
625
GHashTable *reverse_map)
629
ptr = g_hash_table_lookup (reverse_map, data->file);
632
iter = g_new0 (GtkTreeIter, 1);
633
fm_list_model_ptr_to_iter (data->model, ptr, iter);
634
data->iters = g_list_prepend (data->iters, iter);
639
file_to_iter_cb (gpointer key,
643
struct GetIters *data;
644
FileEntry *dir_file_entry;
647
dir_file_entry = g_sequence_get ((GSequenceIter *)value);
648
dir_to_iters (data, dir_file_entry->reverse_map);
652
fm_list_model_get_all_iters_for_file (FMListModel *model, GOFFile *file)
654
struct GetIters data;
660
dir_to_iters (&data, model->details->top_reverse_map);
661
g_hash_table_foreach (model->details->directory_reverse_map,
662
file_to_iter_cb, &data);
664
return g_list_reverse (data.iters);
668
fm_list_model_get_first_iter_for_file (FMListModel *model,
677
list = fm_list_model_get_all_iters_for_file (model, file);
680
*iter = *(GtkTreeIter *)list->data;
682
//eel_g_list_free_deep (list);
689
fm_list_model_get_tree_iter_from_file (FMListModel *model, GOFFile *file,
690
GOFDirectoryAsync *directory,
695
ptr = lookup_file (model, file, directory);
700
fm_list_model_ptr_to_iter (model, ptr, iter);
706
fm_list_model_file_entry_compare_func (gconstpointer a,
710
FileEntry *file_entry1;
711
FileEntry *file_entry2;
715
model = (FMListModel *)user_data;
717
file_entry1 = (FileEntry *)a;
718
file_entry2 = (FileEntry *)b;
720
if (file_entry1->file != NULL && file_entry2->file != NULL) {
721
/*result = nautilus_file_compare_for_sort_by_attribute_q (file_entry1->file, file_entry2->file,
722
model->details->sort_id,
723
model->details->sort_directories_first,
724
(model->details->order == GTK_SORT_DESCENDING));*/
725
result = gof_file_compare_for_sort (file_entry1->file, file_entry2->file,
726
model->details->sort_id,
727
//model->details->sort_directories_first,
729
(model->details->order == GTK_SORT_DESCENDING));
731
} else if (file_entry1->file == NULL) {
741
fm_list_model_compare_func (FMListModel *model,
747
/*result = nautilus_file_compare_for_sort_by_attribute_q (file1, file2,
748
model->details->sort_id,
749
model->details->sort_directories_first,
750
(model->details->order == GTK_SORT_DESCENDING));*/
757
fm_list_model_sort_file_entries (FMListModel *model, GSequence *files, GtkTreePath *path)
759
GSequenceIter **old_order;
764
FileEntry *file_entry;
767
length = g_sequence_get_length (files);
773
/* generate old order of GSequenceIter's */
774
old_order = g_new (GSequenceIter *, length);
775
for (i = 0; i < length; ++i) {
776
GSequenceIter *ptr = g_sequence_get_iter_at_pos (files, i);
778
file_entry = g_sequence_get (ptr);
779
if (file_entry->files != NULL) {
780
gtk_tree_path_append_index (path, i);
781
fm_list_model_sort_file_entries (model, file_entry->files, path);
782
gtk_tree_path_up (path);
789
g_sequence_sort (files, fm_list_model_file_entry_compare_func, model);
791
/* generate new order */
792
new_order = g_new (int, length);
793
/* Note: new_order[newpos] = oldpos */
794
for (i = 0; i < length; ++i) {
795
new_order[g_sequence_iter_get_position (old_order[i])] = i;
798
/* Let the world know about our new order */
800
g_assert (new_order != NULL);
803
if (gtk_tree_path_get_depth (path) != 0) {
804
gboolean get_iter_result;
806
get_iter_result = gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
807
g_assert (get_iter_result);
810
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
811
path, has_iter ? &iter : NULL, new_order);
818
fm_list_model_sort (FMListModel *model)
822
path = gtk_tree_path_new ();
824
fm_list_model_sort_file_entries (model, model->details->files, path);
826
gtk_tree_path_free (path);
830
fm_list_model_get_sort_column_id (GtkTreeSortable *sortable,
831
gint *sort_column_id,
837
model = (FMListModel *)sortable;
839
id = model->details->sort_id;
845
if (sort_column_id != NULL) {
846
*sort_column_id = id;
850
*order = model->details->order;
857
fm_list_model_set_sort_column_id (GtkTreeSortable *sortable, gint sort_column_id, GtkSortType order)
861
model = (FMListModel *)sortable;
863
model->details->sort_id = sort_column_id;
865
model->details->order = order;
867
fm_list_model_sort (model);
868
gtk_tree_sortable_sort_column_changed (sortable);
872
fm_list_model_has_default_sort_func (GtkTreeSortable *sortable)
879
fm_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source, GList *path_list)
886
each_path_get_data_binder (NautilusDragEachSelectedItemDataGet data_get,
890
DragDataGetInfo *info;
893
GtkTreeRowReference *row;
896
GdkRectangle cell_area;
897
GtkTreeViewColumn *column;
901
g_return_if_fail (info->model->details->drag_view);
903
column = gtk_tree_view_get_column (info->model->details->drag_view, 0);
905
for (l = info->path_list; l != NULL; l = l->next) {
908
path = gtk_tree_row_reference_get_path (row);
909
file = fm_list_model_file_for_path (info->model, path);
911
gtk_tree_view_get_cell_area
912
(info->model->details->drag_view,
917
uri = nautilus_file_get_uri (file);
921
cell_area.y - info->model->details->drag_begin_y,
922
cell_area.width, cell_area.height,
927
nautilus_file_unref (file);
930
gtk_tree_path_free (path);
935
fm_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source,
937
GtkSelectionData *selection_data)
940
DragDataGetInfo context;
943
model = FM_LIST_MODEL (drag_source);
945
context.model = model;
946
context.path_list = path_list;
948
if (!drag_target_list) {
949
drag_target_list = fm_list_model_get_drag_target_list ();
952
if (gtk_target_list_find (drag_target_list,
953
selection_data->target,
955
nautilus_drag_drag_data_get (NULL,
961
each_path_get_data_binder);
969
fm_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source, GList *path_list)
976
add_dummy_row (FMListModel *model, FileEntry *parent_entry)
978
FileEntry *dummy_file_entry;
982
dummy_file_entry = g_new0 (FileEntry, 1);
984
//dummy_file_entry->file = parent_entry->file;
985
dummy_file_entry->parent = parent_entry;
986
dummy_file_entry->ptr = g_sequence_insert_sorted (parent_entry->files, dummy_file_entry,
987
fm_list_model_file_entry_compare_func, model);
988
iter.stamp = model->details->stamp;
989
iter.user_data = dummy_file_entry->ptr;
991
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
992
gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
993
gtk_tree_path_free (path);
997
fm_list_model_add_file (FMListModel *model, GOFFile *file,
998
GOFDirectoryAsync *directory)
1002
FileEntry *file_entry;
1003
GSequenceIter *ptr, *parent_ptr;
1005
gboolean replace_dummy;
1006
GHashTable *parent_hash;
1008
parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
1011
file_entry = g_sequence_get (parent_ptr);
1012
ptr = g_hash_table_lookup (file_entry->reverse_map, file);
1015
ptr = g_hash_table_lookup (model->details->top_reverse_map, file);
1019
g_warning ("file already in tree (parent_ptr: %p)!!!\n", parent_ptr);
1023
file_entry = g_new0 (FileEntry, 1);
1024
//file_entry->file = gof_file_ref (file);
1025
file_entry->file = file;
1026
file_entry->parent = NULL;
1027
file_entry->subdirectory = NULL;
1028
file_entry->files = NULL;
1030
files = model->details->files;
1031
parent_hash = model->details->top_reverse_map;
1033
replace_dummy = FALSE;
1035
if (parent_ptr != NULL) {
1036
//printf ("parent_ptr != NULL %s\n", file->name);
1037
file_entry->parent = g_sequence_get (parent_ptr);
1038
/* At this point we set loaded. Either we saw
1039
* "done" and ignored it waiting for this, or we do this
1040
* earlier, but then we replace the dummy row anyway,
1041
* so it doesn't matter */
1042
file_entry->parent->loaded = 1;
1043
parent_hash = file_entry->parent->reverse_map;
1044
files = file_entry->parent->files;
1045
if (g_sequence_get_length (files) == 1) {
1046
GSequenceIter *dummy_ptr = g_sequence_get_iter_at_pos (files, 0);
1047
FileEntry *dummy_entry = g_sequence_get (dummy_ptr);
1048
if (dummy_entry->file == NULL) {
1049
/* replace the dummy loading entry */
1050
model->details->stamp++;
1051
g_sequence_remove (dummy_ptr);
1053
replace_dummy = TRUE;
1059
file_entry->ptr = g_sequence_insert_sorted (files, file_entry,
1060
fm_list_model_file_entry_compare_func, model);
1062
g_hash_table_insert (parent_hash, file, file_entry->ptr);
1064
iter.stamp = model->details->stamp;
1065
iter.user_data = file_entry->ptr;
1067
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1068
if (replace_dummy) {
1069
gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1071
gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
1074
if (file->is_directory) {
1075
file_entry->files = g_sequence_new ((GDestroyNotify)file_entry_free);
1077
add_dummy_row (model, file_entry);
1079
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
1082
gtk_tree_path_free (path);
1089
fm_list_model_file_changed (FMListModel *model, GOFFile *file,
1090
NautilusDirectory *directory)
1092
FileEntry *parent_file_entry;
1094
GtkTreePath *path, *parent_path;
1096
int pos_before, pos_after, length, i, old;
1101
ptr = lookup_file (model, file, directory);
1107
pos_before = g_sequence_iter_get_position (ptr);
1109
g_sequence_sort_changed (ptr, fm_list_model_file_entry_compare_func, model);
1111
pos_after = g_sequence_iter_get_position (ptr);
1113
if (pos_before != pos_after) {
1114
/* The file moved, we need to send rows_reordered */
1116
parent_file_entry = ((FileEntry *)g_sequence_get (ptr))->parent;
1118
if (parent_file_entry == NULL) {
1120
parent_path = gtk_tree_path_new ();
1121
files = model->details->files;
1124
fm_list_model_ptr_to_iter (model, parent_file_entry->ptr, &iter);
1125
parent_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1126
files = parent_file_entry->files;
1129
length = g_sequence_get_length (files);
1130
new_order = g_new (int, length);
1131
/* Note: new_order[newpos] = oldpos */
1132
for (i = 0, old = 0; i < length; ++i) {
1133
if (i == pos_after) {
1134
new_order[i] = pos_before;
1136
if (old == pos_before)
1138
new_order[i] = old++;
1142
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
1143
parent_path, has_iter ? &iter : NULL, new_order);
1145
gtk_tree_path_free (parent_path);
1149
fm_list_model_ptr_to_iter (model, ptr, &iter);
1150
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1151
gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1152
gtk_tree_path_free (path);
1157
fm_list_model_is_empty (FMListModel *model)
1159
return (g_sequence_get_length (model->details->files) == 0);
1163
fm_list_model_get_length (FMListModel *model)
1165
return g_sequence_get_length (model->details->files);
1169
fm_list_model_remove (FMListModel *model, GtkTreeIter *iter)
1171
GSequenceIter *ptr, *child_ptr;
1172
FileEntry *file_entry, *child_file_entry, *parent_file_entry;
1174
GtkTreeIter parent_iter;
1176
ptr = iter->user_data;
1177
file_entry = g_sequence_get (ptr);
1178
if (file_entry->files != NULL) {
1179
while (g_sequence_get_length (file_entry->files) > 0) {
1180
child_ptr = g_sequence_get_begin_iter (file_entry->files);
1181
child_file_entry = g_sequence_get (child_ptr);
1182
if (child_file_entry->file != NULL) {
1183
fm_list_model_remove_file (model,
1184
child_file_entry->file,
1185
file_entry->subdirectory);
1187
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
1188
gtk_tree_path_append_index (path, 0);
1189
model->details->stamp++;
1190
g_sequence_remove (child_ptr);
1191
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1192
gtk_tree_path_free (path);
1195
/* the parent iter didn't actually change */
1196
iter->stamp = model->details->stamp;
1201
if (file_entry->file != NULL) { /* Don't try to remove dummy row */
1202
if (file_entry->parent != NULL) {
1203
g_hash_table_remove (file_entry->parent->reverse_map, file_entry->file);
1205
g_hash_table_remove (model->details->top_reverse_map, file_entry->file);
1209
parent_file_entry = file_entry->parent;
1210
if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 1 &&
1211
file_entry->file != NULL) {
1212
/* this is the last non-dummy child, add a dummy node */
1213
/* We need to do this before removing the last file to avoid
1214
* collapsing the row.
1216
add_dummy_row (model, parent_file_entry);
1218
/* FIXME we don't need to unref file here - clean up this part */
1219
/*if (file_entry->file != NULL) {
1220
//printf ("remove file %s\n", file_entry->file->name);
1221
g_object_unref (file_entry->file);
1224
if (file_entry->subdirectory != NULL) {
1225
g_signal_emit (model,
1226
list_model_signals[SUBDIRECTORY_UNLOADED], 0,
1227
file_entry->subdirectory);
1230
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
1232
g_sequence_remove (ptr);
1233
model->details->stamp++;
1234
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1236
gtk_tree_path_free (path);
1238
if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 0) {
1239
parent_iter.stamp = model->details->stamp;
1240
parent_iter.user_data = parent_file_entry->ptr;
1241
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &parent_iter);
1242
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
1243
path, &parent_iter);
1244
gtk_tree_path_free (path);
1249
fm_list_model_remove_file (FMListModel *model, GOFFile *file,
1250
GOFDirectoryAsync *directory)
1254
if (fm_list_model_get_tree_iter_from_file (model, file, directory, &iter)) {
1255
//printf ("remove file %s\n", file->name);
1256
fm_list_model_remove (model, &iter);
1261
fm_list_model_clear_directory (FMListModel *model, GSequence *files)
1264
FileEntry *file_entry;
1266
while (g_sequence_get_length (files) > 0) {
1267
iter.user_data = g_sequence_get_begin_iter (files);
1269
file_entry = g_sequence_get (iter.user_data);
1270
if (file_entry->files != NULL) {
1271
fm_list_model_clear_directory (model, file_entry->files);
1274
iter.stamp = model->details->stamp;
1275
fm_list_model_remove (model, &iter);
1280
fm_list_model_clear (FMListModel *model)
1282
g_return_if_fail (model != NULL);
1284
fm_list_model_clear_directory (model, model->details->files);
1288
fm_list_model_file_for_path (FMListModel *model, GtkTreePath *path)
1294
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model),
1296
gtk_tree_model_get (GTK_TREE_MODEL (model),
1298
FM_LIST_MODEL_FILE_COLUMN, &file,
1304
void fm_list_model_get_directory_file (FMListModel *model, GtkTreePath *path, GOFDirectoryAsync **directory, GOFFile **file)
1307
FileEntry *file_entry;
1311
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) {
1314
file_entry = g_sequence_get (iter.user_data);
1315
*directory = file_entry->subdirectory;
1316
*file = file_entry->file;
1320
fm_list_model_load_subdirectory (FMListModel *model, GtkTreePath *path, GOFDirectoryAsync **directory)
1323
FileEntry *file_entry;
1325
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) {
1329
file_entry = g_sequence_get (iter.user_data);
1330
if (file_entry->file == NULL ||
1331
file_entry->subdirectory != NULL) {
1335
file_entry->subdirectory = gof_directory_async_get_for_file (file_entry->file);
1337
/* FIXME not sure the hash lookup is really needed gof_driectory_async_get_for_file is a always a new object */
1338
if (g_hash_table_lookup (model->details->directory_reverse_map,
1339
file_entry->subdirectory) != NULL) {
1340
g_object_unref (file_entry->subdirectory);
1341
g_warning ("Already in directory_reverse_map, failing\n");
1345
g_hash_table_insert (model->details->directory_reverse_map,
1346
file_entry->subdirectory, file_entry->ptr);
1347
file_entry->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1349
/* Return a ref too */
1350
//gof_directory_ref (file_entry->subdirectory);
1351
*directory = file_entry->subdirectory;
1353
load_dir_async (file_entry->subdirectory);
1358
/* removes all children of the subfolder and unloads the subdirectory */
1360
fm_list_model_unload_subdirectory (FMListModel *model, GtkTreeIter *iter)
1362
GSequenceIter *child_ptr;
1363
FileEntry *file_entry, *child_file_entry;
1364
GtkTreeIter child_iter;
1366
file_entry = g_sequence_get (iter->user_data);
1367
if (file_entry->file == NULL ||
1368
file_entry->subdirectory == NULL) {
1372
load_dir_async_cancel (file_entry->subdirectory);
1373
g_hash_table_remove (model->details->directory_reverse_map,
1374
file_entry->subdirectory);
1375
file_entry->loaded = 0;
1377
printf("remove all children\n");
1378
//printf("remove all children %d\n", g_sequence_get_length (file_entry->files));
1379
/* Remove all children */
1380
while (g_sequence_get_length (file_entry->files) > 0) {
1381
child_ptr = g_sequence_get_begin_iter (file_entry->files);
1382
child_file_entry = g_sequence_get (child_ptr);
1383
if (child_file_entry->file == NULL) {
1384
/* Don't delete the dummy node */
1387
fm_list_model_ptr_to_iter (model, child_ptr, &child_iter);
1388
fm_list_model_remove (model, &child_iter);
1392
/* Emit unload signal */
1393
g_signal_emit (model,
1394
list_model_signals[SUBDIRECTORY_UNLOADED], 0,
1395
file_entry->subdirectory);
1397
g_object_unref (file_entry->subdirectory);
1398
file_entry->subdirectory = NULL;
1399
g_assert (g_hash_table_size (file_entry->reverse_map) == 0);
1400
g_hash_table_destroy (file_entry->reverse_map);
1401
file_entry->reverse_map = NULL;
1405
fm_list_model_set_should_sort_directories_first (FMListModel *model, gboolean sort_directories_first)
1407
if (model->details->sort_directories_first == sort_directories_first) {
1411
model->details->sort_directories_first = sort_directories_first;
1412
fm_list_model_sort (model);
1417
fm_list_model_get_sort_column_id_from_attribute (FMListModel *model,
1422
if (attribute == 0) {
1426
/* Hack - the preferences dialog sets modification_date for some
1427
* rather than date_modified for some reason. Make sure that
1429
if (attribute == attribute_modification_date_q) {
1430
attribute = attribute_date_modified_q;
1433
for (i = 0; i < model->details->columns->len; i++) {
1434
NautilusColumn *column;
1435
GQuark column_attribute;
1438
NAUTILUS_COLUMN (model->details->columns->pdata[i]);
1439
g_object_get (G_OBJECT (column),
1440
"attribute_q", &column_attribute,
1442
if (column_attribute == attribute) {
1443
return FM_LIST_MODEL_NUM_COLUMNS + i;
1451
fm_list_model_get_attribute_from_sort_column_id (FMListModel *model,
1454
NautilusColumn *column;
1458
index = sort_column_id - FM_LIST_MODEL_NUM_COLUMNS;
1460
if (index < 0 || index >= model->details->columns->len) {
1461
g_warning ("unknown sort column id: %d", sort_column_id);
1465
column = NAUTILUS_COLUMN (model->details->columns->pdata[index]);
1466
g_object_get (G_OBJECT (column), "attribute_q", &attribute, NULL);
1472
fm_list_model_get_zoom_level_from_column_id (int column)
1475
case FM_LIST_MODEL_SMALLEST_ICON_COLUMN:
1476
return NAUTILUS_ZOOM_LEVEL_SMALLEST;
1477
case FM_LIST_MODEL_SMALLER_ICON_COLUMN:
1478
return NAUTILUS_ZOOM_LEVEL_SMALLER;
1479
case FM_LIST_MODEL_SMALL_ICON_COLUMN:
1480
return NAUTILUS_ZOOM_LEVEL_SMALL;
1481
case FM_LIST_MODEL_STANDARD_ICON_COLUMN:
1482
return NAUTILUS_ZOOM_LEVEL_STANDARD;
1483
case FM_LIST_MODEL_LARGE_ICON_COLUMN:
1484
return NAUTILUS_ZOOM_LEVEL_LARGE;
1485
case FM_LIST_MODEL_LARGER_ICON_COLUMN:
1486
return NAUTILUS_ZOOM_LEVEL_LARGER;
1487
case FM_LIST_MODEL_LARGEST_ICON_COLUMN:
1488
return NAUTILUS_ZOOM_LEVEL_LARGEST;
1491
g_return_val_if_reached (NAUTILUS_ZOOM_LEVEL_STANDARD);
1495
fm_list_model_get_column_id_from_zoom_level (NautilusZoomLevel zoom_level)
1497
switch (zoom_level) {
1498
case NAUTILUS_ZOOM_LEVEL_SMALLEST:
1499
return FM_LIST_MODEL_SMALLEST_ICON_COLUMN;
1500
case NAUTILUS_ZOOM_LEVEL_SMALLER:
1501
return FM_LIST_MODEL_SMALLER_ICON_COLUMN;
1502
case NAUTILUS_ZOOM_LEVEL_SMALL:
1503
return FM_LIST_MODEL_SMALL_ICON_COLUMN;
1504
case NAUTILUS_ZOOM_LEVEL_STANDARD:
1505
return FM_LIST_MODEL_STANDARD_ICON_COLUMN;
1506
case NAUTILUS_ZOOM_LEVEL_LARGE:
1507
return FM_LIST_MODEL_LARGE_ICON_COLUMN;
1508
case NAUTILUS_ZOOM_LEVEL_LARGER:
1509
return FM_LIST_MODEL_LARGER_ICON_COLUMN;
1510
case NAUTILUS_ZOOM_LEVEL_LARGEST:
1511
return FM_LIST_MODEL_LARGEST_ICON_COLUMN;
1514
g_return_val_if_reached (FM_LIST_MODEL_STANDARD_ICON_COLUMN);
1518
fm_list_model_get_zoom_level_from_emblem_column_id (int column)
1521
case FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN:
1522
return NAUTILUS_ZOOM_LEVEL_SMALLEST;
1523
case FM_LIST_MODEL_SMALLER_EMBLEM_COLUMN:
1524
return NAUTILUS_ZOOM_LEVEL_SMALLER;
1525
case FM_LIST_MODEL_SMALL_EMBLEM_COLUMN:
1526
return NAUTILUS_ZOOM_LEVEL_SMALL;
1527
case FM_LIST_MODEL_STANDARD_EMBLEM_COLUMN:
1528
return NAUTILUS_ZOOM_LEVEL_STANDARD;
1529
case FM_LIST_MODEL_LARGE_EMBLEM_COLUMN:
1530
return NAUTILUS_ZOOM_LEVEL_LARGE;
1531
case FM_LIST_MODEL_LARGER_EMBLEM_COLUMN:
1532
return NAUTILUS_ZOOM_LEVEL_LARGER;
1533
case FM_LIST_MODEL_LARGEST_EMBLEM_COLUMN:
1534
return NAUTILUS_ZOOM_LEVEL_LARGEST;
1537
g_return_val_if_reached (NAUTILUS_ZOOM_LEVEL_STANDARD);
1541
fm_list_model_get_emblem_column_id_from_zoom_level (NautilusZoomLevel zoom_level)
1543
switch (zoom_level) {
1544
case NAUTILUS_ZOOM_LEVEL_SMALLEST:
1545
return FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN;
1546
case NAUTILUS_ZOOM_LEVEL_SMALLER:
1547
return FM_LIST_MODEL_SMALLER_EMBLEM_COLUMN;
1548
case NAUTILUS_ZOOM_LEVEL_SMALL:
1549
return FM_LIST_MODEL_SMALL_EMBLEM_COLUMN;
1550
case NAUTILUS_ZOOM_LEVEL_STANDARD:
1551
return FM_LIST_MODEL_STANDARD_EMBLEM_COLUMN;
1552
case NAUTILUS_ZOOM_LEVEL_LARGE:
1553
return FM_LIST_MODEL_LARGE_EMBLEM_COLUMN;
1554
case NAUTILUS_ZOOM_LEVEL_LARGER:
1555
return FM_LIST_MODEL_LARGER_EMBLEM_COLUMN;
1556
case NAUTILUS_ZOOM_LEVEL_LARGEST:
1557
return FM_LIST_MODEL_LARGEST_EMBLEM_COLUMN;
1560
g_return_val_if_reached (FM_LIST_MODEL_STANDARD_EMBLEM_COLUMN);
1566
fm_list_model_set_drag_view (FMListModel *model,
1571
g_return_if_fail (model != NULL);
1572
g_return_if_fail (FM_IS_LIST_MODEL (model));
1573
g_return_if_fail (!view || GTK_IS_TREE_VIEW (view));
1575
model->details->drag_view = view;
1576
model->details->drag_begin_x = drag_begin_x;
1577
model->details->drag_begin_y = drag_begin_y;
1581
fm_list_model_get_drag_target_list ()
1583
GtkTargetList *target_list;
1585
target_list = gtk_target_list_new (drag_types, G_N_ELEMENTS (drag_types));
1586
gtk_target_list_add_text_targets (target_list, NAUTILUS_ICON_DND_TEXT);
1592
fm_list_model_add_column (FMListModel *model,
1593
NautilusColumn *column)
1595
g_ptr_array_add (model->details->columns, column);
1596
g_object_ref (column);
1598
return FM_LIST_MODEL_NUM_COLUMNS + (model->details->columns->len - 1);
1602
fm_list_model_get_column_number (FMListModel *model,
1603
const char *column_name)
1607
for (i = 0; i < model->details->columns->len; i++) {
1608
NautilusColumn *column;
1611
column = model->details->columns->pdata[i];
1613
g_object_get (G_OBJECT (column), "name", &name, NULL);
1615
if (!strcmp (name, column_name)) {
1617
return FM_LIST_MODEL_NUM_COLUMNS + i;
1626
fm_list_model_dispose (GObject *object)
1631
model = FM_LIST_MODEL (object);
1633
/*if (model->details->columns) {
1634
for (i = 0; i < model->details->columns->len; i++) {
1635
g_object_unref (model->details->columns->pdata[i]);
1637
g_ptr_array_free (model->details->columns, TRUE);
1638
model->details->columns = NULL;
1641
if (model->details->files) {
1642
g_sequence_free (model->details->files);
1643
model->details->files = NULL;
1646
if (model->details->top_reverse_map) {
1647
g_hash_table_destroy (model->details->top_reverse_map);
1648
model->details->top_reverse_map = NULL;
1650
if (model->details->directory_reverse_map) {
1651
g_hash_table_destroy (model->details->directory_reverse_map);
1652
model->details->directory_reverse_map = NULL;
1655
G_OBJECT_CLASS (fm_list_model_parent_class)->dispose (object);
1659
fm_list_model_finalize (GObject *object)
1661
FMListModel *model = FM_LIST_MODEL (object);
1663
printf ("$$ %s\n", G_STRFUNC);
1664
g_free (model->details);
1666
G_OBJECT_CLASS (fm_list_model_parent_class)->finalize (object);
1670
fm_list_model_init (FMListModel *model)
1672
model->details = g_new0 (FMListModelDetails, 1);
1673
model->details->files = g_sequence_new ((GDestroyNotify)file_entry_free);
1674
model->details->top_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1675
model->details->directory_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1676
model->details->stamp = g_random_int ();
1677
model->details->sort_id = FM_LIST_MODEL_FILENAME;
1678
//model->details->columns = g_ptr_array_new ();
1682
fm_list_model_class_init (FMListModelClass *klass)
1684
GObjectClass *object_class;
1686
attribute_name_q = g_quark_from_static_string ("name");
1687
attribute_modification_date_q = g_quark_from_static_string ("modification_date");
1688
attribute_date_modified_q = g_quark_from_static_string ("date_modified");
1690
object_class = (GObjectClass *)klass;
1691
object_class->finalize = fm_list_model_finalize;
1692
object_class->dispose = fm_list_model_dispose;
1693
object_class->get_property = fm_list_model_get_property;
1694
object_class->set_property = fm_list_model_set_property;
1696
g_object_class_install_property (object_class,
1698
g_param_spec_boolean ("has-child",
1700
"Whether the model list has child(s) and the treeview can expand subfolders",
1702
G_PARAM_READWRITE));
1705
list_model_signals[SUBDIRECTORY_UNLOADED] =
1706
g_signal_new ("subdirectory_unloaded",
1709
G_STRUCT_OFFSET (FMListModelClass, subdirectory_unloaded),
1711
g_cclosure_marshal_VOID__OBJECT,
1713
GOF_TYPE_DIRECTORY_ASYNC);
1717
fm_list_model_tree_model_init (GtkTreeModelIface *iface)
1719
iface->get_flags = fm_list_model_get_flags;
1720
iface->get_n_columns = fm_list_model_get_n_columns;
1721
iface->get_column_type = fm_list_model_get_column_type;
1722
iface->get_iter = fm_list_model_get_iter;
1723
iface->get_path = fm_list_model_get_path;
1724
iface->get_value = fm_list_model_get_value;
1725
iface->iter_next = fm_list_model_iter_next;
1726
iface->iter_children = fm_list_model_iter_children;
1727
iface->iter_has_child = fm_list_model_iter_has_child;
1728
iface->iter_n_children = fm_list_model_iter_n_children;
1729
iface->iter_nth_child = fm_list_model_iter_nth_child;
1730
iface->iter_parent = fm_list_model_iter_parent;
1734
fm_list_model_sortable_init (GtkTreeSortableIface *iface)
1736
iface->get_sort_column_id = fm_list_model_get_sort_column_id;
1737
iface->set_sort_column_id = fm_list_model_set_sort_column_id;
1738
iface->has_default_sort_func = fm_list_model_has_default_sort_func;
1742
fm_list_model_set_has_child (FMListModel *model, gboolean has_child)
1744
g_return_if_fail (FM_IS_LIST_MODEL (model));
1746
model->details->has_child = has_child;
1747
/*if (model->details->has_child != has_child)
1749
model->details->has_child = has_child;
1750
//g_object_notify (G_OBJECT (tree_view), "single-click");
1755
fm_list_model_get_property (GObject *object,
1760
FMListModel *model = FM_LIST_MODEL (object);
1764
case PROP_HAS_CHILD:
1765
g_value_set_boolean (value, model->details->has_child);
1769
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1775
fm_list_model_set_property (GObject *object,
1777
const GValue *value,
1780
FMListModel *model = FM_LIST_MODEL (object);
1784
case PROP_HAS_CHILD:
1785
fm_list_model_set_has_child (model, g_value_get_boolean (value));
1789
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1797
fm_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface)
1799
iface->row_draggable = fm_list_model_multi_row_draggable;
1800
iface->drag_data_get = fm_list_model_multi_drag_data_get;
1801
iface->drag_data_delete = fm_list_model_multi_drag_data_delete;
1806
fm_list_model_subdirectory_done_loading (FMListModel *model, NautilusDirectory *directory)
1810
FileEntry *file_entry, *dummy_entry;
1811
GSequenceIter *parent_ptr, *dummy_ptr;
1814
if (model == NULL || model->details->directory_reverse_map == NULL) {
1817
parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
1819
if (parent_ptr == NULL) {
1823
file_entry = g_sequence_get (parent_ptr);
1824
files = file_entry->files;
1826
/* Only swap loading -> empty if we saw no files yet at "done",
1827
* otherwise, toggle loading at first added file to the model.
1829
if (!nautilus_directory_is_not_empty (directory) &&
1830
g_sequence_get_length (files) == 1) {
1831
dummy_ptr = g_sequence_get_iter_at_pos (file_entry->files, 0);
1832
dummy_entry = g_sequence_get (dummy_ptr);
1833
if (dummy_entry->file == NULL) {
1834
/* was the dummy file */
1835
file_entry->loaded = 1;
1837
iter.stamp = model->details->stamp;
1838
iter.user_data = dummy_ptr;
1840
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1841
gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1842
gtk_tree_path_free (path);