4
* Copyright 2009 PCMan <pcman.tw@gmail.com>
6
* This program 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
* This program 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. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22
#include "fm-config.h"
23
#include "fm-folder-model.h"
24
#include "fm-file-info.h"
25
#include "fm-icon-pixbuf.h"
26
#include "fm-thumbnail.h"
33
/* #define ENABLE_DEBUG */
35
#define DEBUG(...) g_debug(__VA_ARGS__)
45
typedef struct _FmFolderItem FmFolderItem;
50
gboolean is_thumbnail : 1;
51
gboolean thumbnail_loading : 1;
52
gboolean thumbnail_failed : 1;
57
RELOAD_ICONS = 1 << 0,
58
RELOAD_THUMBNAILS = 1 << 1,
59
RELOAD_BOTH = (RELOAD_ICONS | RELOAD_THUMBNAILS)
62
static void fm_folder_model_tree_model_init(GtkTreeModelIface *iface);
63
static void fm_folder_model_tree_sortable_init(GtkTreeSortableIface *iface);
64
static void fm_folder_model_drag_source_init(GtkTreeDragSourceIface *iface);
65
static void fm_folder_model_drag_dest_init(GtkTreeDragDestIface *iface);
67
static void fm_folder_model_finalize(GObject *object);
68
G_DEFINE_TYPE_WITH_CODE( FmFolderModel, fm_folder_model, G_TYPE_OBJECT,
69
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, fm_folder_model_tree_model_init)
70
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_SORTABLE, fm_folder_model_tree_sortable_init)
71
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_DRAG_SOURCE, fm_folder_model_drag_source_init)
72
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_DRAG_DEST, fm_folder_model_drag_dest_init) )
74
static GtkTreeModelFlags fm_folder_model_get_flags(GtkTreeModel *tree_model);
75
static gint fm_folder_model_get_n_columns(GtkTreeModel *tree_model);
76
static GType fm_folder_model_get_column_type(GtkTreeModel *tree_model,
78
static gboolean fm_folder_model_get_iter(GtkTreeModel *tree_model,
81
static GtkTreePath *fm_folder_model_get_path(GtkTreeModel *tree_model,
83
static void fm_folder_model_get_value(GtkTreeModel *tree_model,
87
static gboolean fm_folder_model_iter_next(GtkTreeModel *tree_model,
89
static gboolean fm_folder_model_iter_children(GtkTreeModel *tree_model,
92
static gboolean fm_folder_model_iter_has_child(GtkTreeModel *tree_model,
94
static gint fm_folder_model_iter_n_children(GtkTreeModel *tree_model,
96
static gboolean fm_folder_model_iter_nth_child(GtkTreeModel *tree_model,
100
static gboolean fm_folder_model_iter_parent(GtkTreeModel *tree_model,
103
static gboolean fm_folder_model_get_sort_column_id(GtkTreeSortable* sortable,
104
gint* sort_column_id,
106
static void fm_folder_model_set_sort_column_id(GtkTreeSortable* sortable,
109
static void fm_folder_model_set_sort_func(GtkTreeSortable *sortable,
111
GtkTreeIterCompareFunc sort_func,
113
GtkDestroyNotify destroy);
114
static void fm_folder_model_set_default_sort_func(GtkTreeSortable *sortable,
115
GtkTreeIterCompareFunc sort_func,
117
GtkDestroyNotify destroy);
118
static void fm_folder_model_sort(FmFolderModel* model);
120
/* signal handlers */
121
static void on_folder_loaded(FmFolder* folder, FmFolderModel* model);
123
static void on_icon_theme_changed(GtkIconTheme* theme, FmFolderModel* model);
125
static void on_thumbnail_loaded(FmThumbnailRequest* req, gpointer user_data);
127
static void on_show_thumbnail_changed(FmConfig* cfg, gpointer user_data);
129
static void on_thumbnail_local_changed(FmConfig* cfg, gpointer user_data);
131
static void on_thumbnail_max_changed(FmConfig* cfg, gpointer user_data);
133
static void reload_icons(FmFolderModel* model, enum ReloadFlags flags);
135
#define IS_HIDDEN_FILE(fn) \
136
(fn[0] == '.' || g_str_has_suffix(fn, "~"))
139
static GType column_types[ N_FOLDER_MODEL_COLS ];
140
static guint signals[N_SIGNALS];
142
void fm_folder_model_init(FmFolderModel* model)
144
model->sort_order = -1;
145
model->sort_col = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
146
/* Random int to check whether an iter belongs to our model */
147
model->stamp = g_random_int();
149
model->theme_change_handler = g_signal_connect(gtk_icon_theme_get_default(), "changed",
150
G_CALLBACK(on_icon_theme_changed), model);
151
g_signal_connect(fm_config, "changed::show_thumbnail", G_CALLBACK(on_show_thumbnail_changed), model);
152
g_signal_connect(fm_config, "changed::thumbnail_local", G_CALLBACK(on_thumbnail_local_changed), model);
153
g_signal_connect(fm_config, "changed::thumbnail_max", G_CALLBACK(on_thumbnail_max_changed), model);
155
model->thumbnail_max = fm_config->thumbnail_max << 10;
158
void fm_folder_model_class_init(FmFolderModelClass *klass)
160
GObjectClass * object_class;
162
fm_folder_model_parent_class = ( GObjectClass* )g_type_class_peek_parent(klass);
163
object_class = ( GObjectClass* )klass;
165
object_class->finalize = fm_folder_model_finalize;
168
g_signal_new("loaded",
169
G_TYPE_FROM_CLASS(klass),
171
G_STRUCT_OFFSET(FmFolderModelClass, loaded),
173
g_cclosure_marshal_VOID__VOID,
177
void fm_folder_model_tree_model_init(GtkTreeModelIface *iface)
179
iface->get_flags = fm_folder_model_get_flags;
180
iface->get_n_columns = fm_folder_model_get_n_columns;
181
iface->get_column_type = fm_folder_model_get_column_type;
182
iface->get_iter = fm_folder_model_get_iter;
183
iface->get_path = fm_folder_model_get_path;
184
iface->get_value = fm_folder_model_get_value;
185
iface->iter_next = fm_folder_model_iter_next;
186
iface->iter_children = fm_folder_model_iter_children;
187
iface->iter_has_child = fm_folder_model_iter_has_child;
188
iface->iter_n_children = fm_folder_model_iter_n_children;
189
iface->iter_nth_child = fm_folder_model_iter_nth_child;
190
iface->iter_parent = fm_folder_model_iter_parent;
192
column_types [ COL_FILE_ICON ] = GDK_TYPE_PIXBUF;
193
column_types [ COL_FILE_NAME ] = G_TYPE_STRING;
194
column_types [ COL_FILE_DESC ] = G_TYPE_STRING;
195
column_types [ COL_FILE_SIZE ] = G_TYPE_STRING;
196
column_types [ COL_FILE_DESC ] = G_TYPE_STRING;
197
column_types [ COL_FILE_PERM ] = G_TYPE_STRING;
198
column_types [ COL_FILE_OWNER ] = G_TYPE_STRING;
199
column_types [ COL_FILE_MTIME ] = G_TYPE_STRING;
200
column_types [ COL_FILE_INFO ] = G_TYPE_POINTER;
201
column_types [ COL_FILE_GICON ] = G_TYPE_ICON;
204
void fm_folder_model_tree_sortable_init(GtkTreeSortableIface *iface)
206
/* iface->sort_column_changed = fm_folder_model_sort_column_changed; */
207
iface->get_sort_column_id = fm_folder_model_get_sort_column_id;
208
iface->set_sort_column_id = fm_folder_model_set_sort_column_id;
209
iface->set_sort_func = fm_folder_model_set_sort_func;
210
iface->set_default_sort_func = fm_folder_model_set_default_sort_func;
211
iface->has_default_sort_func = ( gboolean (*)(GtkTreeSortable *) )gtk_false;
214
void fm_folder_model_drag_source_init(GtkTreeDragSourceIface *iface)
216
/* FIXME: Unused. Will this cause any problem? */
219
void fm_folder_model_drag_dest_init(GtkTreeDragDestIface *iface)
221
/* FIXME: Unused. Will this cause any problem? */
224
void fm_folder_model_finalize(GObject *object)
226
FmFolderModel* model = ( FmFolderModel* )object;
229
char* str = fm_path_to_str(model->dir->dir_path);
230
g_debug("FINALIZE FOLDER MODEL(%p): %s", model, str);
233
fm_folder_model_set_folder(model, NULL);
234
g_signal_handler_disconnect(gtk_icon_theme_get_default(),
235
model->theme_change_handler);
237
g_signal_handlers_disconnect_by_func(fm_config, on_show_thumbnail_changed, model);
238
g_signal_handlers_disconnect_by_func(fm_config, on_thumbnail_local_changed, model);
239
g_signal_handlers_disconnect_by_func(fm_config, on_thumbnail_max_changed, model);
241
g_list_foreach(model->thumbnail_requests, (GFunc)fm_thumbnail_request_cancel, NULL);
242
g_list_free(model->thumbnail_requests);
244
/* must chain up - finalize parent */
245
(*G_OBJECT_CLASS(fm_folder_model_parent_class)->finalize)(object);
248
FmFolderModel *fm_folder_model_new(FmFolder* dir, gboolean show_hidden)
250
FmFolderModel* model;
251
model = ( FmFolderModel* )g_object_new(FM_TYPE_FOLDER_MODEL, NULL);
253
model->hidden = NULL;
254
model->show_hidden = show_hidden;
255
fm_folder_model_set_folder(model, dir);
259
static inline FmFolderItem* fm_folder_item_new(FmFileInfo* inf)
261
FmFolderItem* item = g_slice_new0(FmFolderItem);
262
item->inf = fm_file_info_ref(inf);
266
static inline void fm_folder_item_free(FmFolderItem* item)
269
g_object_unref(item->icon);
270
fm_file_info_unref(item->inf);
271
g_slice_free(FmFolderItem, item);
274
static void _fm_folder_model_insert_item(FmFolder* dir,
275
FmFolderItem* new_item,
276
FmFolderModel* model);
278
static void _fm_folder_model_files_changed(FmFolder* dir, GSList* files,
279
FmFolderModel* model)
282
for( l = files; l; l=l->next )
283
fm_folder_model_file_changed(model, l->data);
286
static void _fm_folder_model_add_file(FmFolderModel* model, FmFileInfo* file)
288
if( !model->show_hidden && IS_HIDDEN_FILE(file->path->name) )
289
g_sequence_append( model->hidden, fm_folder_item_new(file) );
291
fm_folder_model_file_created(model, file);
294
static void _fm_folder_model_files_added(FmFolder* dir, GSList* files,
295
FmFolderModel* model)
299
for( l = files; l; l=l->next )
300
_fm_folder_model_add_file(model, (FmFileInfo*)l->data);
304
static void _fm_folder_model_files_removed(FmFolder* dir, GSList* files,
305
FmFolderModel* model)
308
for( l = files; l; l=l->next )
309
fm_folder_model_file_deleted(model, (FmFileInfo*)l->data);
312
void fm_folder_model_set_folder(FmFolderModel* model, FmFolder* dir)
315
if( model->dir == dir )
319
g_signal_handlers_disconnect_by_func(model->dir,
320
_fm_folder_model_files_added, model);
321
g_signal_handlers_disconnect_by_func(model->dir,
322
_fm_folder_model_files_removed, model);
323
g_signal_handlers_disconnect_by_func(model->dir,
324
_fm_folder_model_files_changed, model);
325
g_signal_handlers_disconnect_by_func(model->dir,
326
on_folder_loaded, model);
328
g_sequence_free(model->items);
329
g_sequence_free(model->hidden);
330
g_object_unref(model->dir);
333
model->items = g_sequence_new((GDestroyNotify)fm_folder_item_free);
334
model->hidden = g_sequence_new((GDestroyNotify)fm_folder_item_free);
338
model->dir = (FmFolder*)g_object_ref(model->dir);
340
g_signal_connect(model->dir, "files-added",
341
G_CALLBACK(_fm_folder_model_files_added),
343
g_signal_connect(model->dir, "files-removed",
344
G_CALLBACK(_fm_folder_model_files_removed),
346
g_signal_connect(model->dir, "files-changed",
347
G_CALLBACK(_fm_folder_model_files_changed),
349
g_signal_connect(model->dir, "loaded",
350
G_CALLBACK(on_folder_loaded), model);
352
if( !fm_list_is_empty(dir->files) )
355
for( l = fm_list_peek_head_link(dir->files); l; l = l->next )
356
_fm_folder_model_add_file(model, (FmFileInfo*)l->data);
359
if( !fm_folder_get_is_loading(model->dir) ) /* if it's already loaded */
360
on_folder_loaded(model->dir, model); /* emit 'loaded' signal */
363
gboolean fm_folder_model_get_is_loading(FmFolderModel* model)
365
return fm_folder_get_is_loading(model->dir);
368
GtkTreeModelFlags fm_folder_model_get_flags(GtkTreeModel *tree_model)
370
g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), ( GtkTreeModelFlags )0);
371
return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
374
gint fm_folder_model_get_n_columns(GtkTreeModel *tree_model)
376
return N_FOLDER_MODEL_COLS;
379
GType fm_folder_model_get_column_type(GtkTreeModel *tree_model,
382
g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), G_TYPE_INVALID);
383
g_return_val_if_fail(index < G_N_ELEMENTS(column_types) && index >= 0, G_TYPE_INVALID);
384
return column_types[ index ];
387
gboolean fm_folder_model_get_iter(GtkTreeModel *tree_model,
391
FmFolderModel* model;
392
gint *indices, n, depth;
393
GSequenceIter* items_it;
395
g_assert( FM_IS_FOLDER_MODEL(tree_model) );
396
g_assert(path!=NULL);
398
model = FM_FOLDER_MODEL(tree_model);
400
indices = gtk_tree_path_get_indices(path);
401
depth = gtk_tree_path_get_depth(path);
403
/* we do not allow children */
404
g_assert(depth == 1); /* depth 1 = top level; a list only has top level nodes and no children */
406
n = indices[0]; /* the n-th top level row */
408
if( n >= g_sequence_get_length(model->items) || n < 0 )
411
items_it = g_sequence_get_iter_at_pos(model->items, n);
413
g_assert( items_it != g_sequence_get_end_iter(model->items) );
415
/* We simply store a pointer in the iter */
416
iter->stamp = model->stamp;
417
iter->user_data = items_it;
422
GtkTreePath *fm_folder_model_get_path(GtkTreeModel *tree_model,
426
GSequenceIter* items_it;
427
FmFolderModel* model = FM_FOLDER_MODEL(tree_model);
429
g_return_val_if_fail(model, NULL);
430
g_return_val_if_fail(iter->stamp == model->stamp, NULL);
431
g_return_val_if_fail(iter != NULL, NULL);
432
g_return_val_if_fail(iter->user_data != NULL, NULL);
434
items_it = (GSequenceIter*)iter->user_data;
435
path = gtk_tree_path_new();
436
gtk_tree_path_append_index( path, g_sequence_iter_get_position(items_it) );
440
void fm_folder_model_get_value(GtkTreeModel *tree_model,
445
GSequenceIter* item_it;
446
FmFolderModel* model = FM_FOLDER_MODEL(tree_model);
448
g_return_if_fail(iter != NULL);
449
g_return_if_fail( column < G_N_ELEMENTS(column_types) );
451
g_value_init(value, column_types[column]);
453
item_it = (GSequenceIter*)iter->user_data;
454
g_return_if_fail(item_it != NULL);
456
FmFolderItem* item = (FmFolderItem*)g_sequence_get(item_it);
457
FmFileInfo* info = item->inf;
462
g_value_set_object(value, info->icon->gicon);
466
if( G_UNLIKELY(!item->icon) )
470
item->icon = fm_icon_get_pixbuf(info->icon, model->icon_size);
472
g_value_set_object(value, item->icon);
474
/* if we want to show a thumbnail */
475
/* if we're on local filesystem or thumbnailing for remote files is allowed */
476
if(fm_config->show_thumbnail && (fm_path_is_local(item->inf->path) || !fm_config->thumbnail_local))
478
if(!item->is_thumbnail && !item->thumbnail_failed && !item->thumbnail_loading)
480
if(fm_file_info_can_thumbnail(item->inf))
482
if(item->inf->size > 0 && item->inf->size <= (fm_config->thumbnail_max << 10))
484
FmThumbnailRequest* req = fm_thumbnail_request(item->inf, model->icon_size, on_thumbnail_loaded, model);
485
model->thumbnail_requests = g_list_prepend(model->thumbnail_requests, req);
486
item->thumbnail_loading = TRUE;
491
item->thumbnail_failed = TRUE;
498
g_value_set_string(value, info->disp_name);
501
g_value_set_string( value, fm_file_info_get_disp_size(info) );
504
g_value_set_string( value, fm_file_info_get_desc(info) );
507
// g_value_set_string( value, fm_file_info_get_disp_perm(info) );
510
// g_value_set_string( value, fm_file_info_get_disp_owner(info) );
513
g_value_set_string( value, fm_file_info_get_disp_mtime(info) );
516
g_value_set_pointer(value, info);
521
gboolean fm_folder_model_iter_next(GtkTreeModel *tree_model,
524
GSequenceIter* item_it, *next_item_it;
525
FmFolderModel* model;
527
g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), FALSE);
529
if( iter == NULL || iter->user_data == NULL )
532
model = FM_FOLDER_MODEL(tree_model);
533
item_it = (GSequenceIter *)iter->user_data;
535
/* Is this the last iter in the list? */
536
next_item_it = g_sequence_iter_next(item_it);
538
if( g_sequence_iter_is_end(next_item_it) )
541
iter->stamp = model->stamp;
542
iter->user_data = next_item_it;
547
gboolean fm_folder_model_iter_children(GtkTreeModel *tree_model,
551
FmFolderModel* model;
552
GSequenceIter* items_it;
553
g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
555
/* this is a list, nodes have no children */
559
/* parent == NULL is a special case; we need to return the first top-level row */
560
g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), FALSE);
561
model = FM_FOLDER_MODEL(tree_model);
563
/* No rows => no first row */
564
// if ( model->dir->n_items == 0 )
567
/* Set iter to first item in list */
568
g_sequence_get_begin_iter(model->items);
569
iter->stamp = model->stamp;
570
iter->user_data = items_it;
574
gboolean fm_folder_model_iter_has_child(GtkTreeModel *tree_model,
580
gint fm_folder_model_iter_n_children(GtkTreeModel *tree_model,
583
FmFolderModel* model;
584
g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), -1);
585
g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
586
model = FM_FOLDER_MODEL(tree_model);
587
/* special case: if iter == NULL, return number of top-level rows */
589
return g_sequence_get_length(model->items);
590
return 0; /* otherwise, this is easy again for a list */
593
gboolean fm_folder_model_iter_nth_child(GtkTreeModel *tree_model,
598
GSequenceIter* items_it;
599
FmFolderModel* model;
601
g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), FALSE);
602
model = FM_FOLDER_MODEL(tree_model);
604
/* a list has only top-level rows */
608
/* special case: if parent == NULL, set iter to n-th top-level row */
609
if( n >= g_sequence_get_length(model->items) || n < 0 )
612
items_it = g_sequence_get_iter_at_pos(model->items, n);
613
g_assert( items_it != g_sequence_get_end_iter(model->items) );
615
iter->stamp = model->stamp;
616
iter->user_data = items_it;
621
gboolean fm_folder_model_iter_parent(GtkTreeModel *tree_model,
628
gboolean fm_folder_model_get_sort_column_id(GtkTreeSortable* sortable,
629
gint* sort_column_id,
632
FmFolderModel* model = (FmFolderModel*)sortable;
634
*sort_column_id = model->sort_col;
636
*order = model->sort_order;
640
void fm_folder_model_set_sort_column_id(GtkTreeSortable* sortable,
644
FmFolderModel* model = (FmFolderModel*)sortable;
645
if( model->sort_col == sort_column_id && model->sort_order == order )
647
model->sort_col = sort_column_id;
648
model->sort_order = order;
649
gtk_tree_sortable_sort_column_changed(sortable);
650
fm_folder_model_sort(model);
653
void fm_folder_model_set_sort_func(GtkTreeSortable *sortable,
655
GtkTreeIterCompareFunc sort_func,
657
GtkDestroyNotify destroy)
659
g_warning("fm_folder_model_set_sort_func: Not supported\n");
662
void fm_folder_model_set_default_sort_func(GtkTreeSortable *sortable,
663
GtkTreeIterCompareFunc sort_func,
665
GtkDestroyNotify destroy)
667
g_warning("fm_folder_model_set_default_sort_func: Not supported\n");
670
static gint fm_folder_model_compare(FmFolderItem* item1,
672
FmFolderModel* model)
674
FmFileInfo* file1 = item1->inf;
675
FmFileInfo* file2 = item2->inf;
680
/* put folders before files */
681
ret = fm_file_info_is_dir(file2) - fm_file_info_is_dir(file1);
685
switch( model->sort_col )
690
key1 = fm_file_info_get_collate_key(file1);
691
key2 = fm_file_info_get_collate_key(file2);
693
collate keys are already passed to g_utf8_casefold, no need to
694
use strcasecmp here (and g_utf8_collate_key returns a string of
695
which case cannot be ignored)
697
ret = g_strcmp0(key1, key2);
702
/* to support files more than 2Gb */
703
goffset diff = file1->size - file2->size;
707
ret = diff > 0 ? 1 : -1;
711
ret = file1->mtime - file2->mtime;
716
/* FIXME: this is very slow */
717
ret = g_utf8_collate(fm_file_info_get_desc(file1), fm_file_info_get_desc(file2));
724
return model->sort_order == GTK_SORT_ASCENDING ? ret : -ret;
727
void fm_folder_model_sort(FmFolderModel* model)
729
GHashTable* old_order;
731
GSequenceIter *items_it;
734
/* if there is only one item */
735
if( model->items == NULL || g_sequence_get_length(model->items) <= 1 )
738
old_order = g_hash_table_new(g_direct_hash, g_direct_equal);
740
items_it = g_sequence_get_begin_iter(model->items);
741
while( !g_sequence_iter_is_end(items_it) )
743
int i = g_sequence_iter_get_position(items_it);
744
g_hash_table_insert( old_order, items_it, GINT_TO_POINTER(i) );
745
items_it = g_sequence_iter_next(items_it);
749
g_sequence_sort(model->items, fm_folder_model_compare, model);
752
new_order = g_new( int, g_sequence_get_length(model->items) );
753
items_it = g_sequence_get_begin_iter(model->items);
754
while( !g_sequence_iter_is_end(items_it) )
756
int i = g_sequence_iter_get_position(items_it);
757
new_order[i] = (guint)g_hash_table_lookup(old_order, items_it);
758
items_it = g_sequence_iter_next(items_it);
760
g_hash_table_destroy(old_order);
761
path = gtk_tree_path_new();
762
gtk_tree_model_rows_reordered(GTK_TREE_MODEL(model),
763
path, NULL, new_order);
764
gtk_tree_path_free(path);
768
void fm_folder_model_file_created(FmFolderModel* model, FmFileInfo* file)
770
FmFolderItem* new_item = fm_folder_item_new(file);
771
_fm_folder_model_insert_item(model->dir, new_item, model);
774
void _fm_folder_model_insert_item(FmFolder* dir,
775
FmFolderItem* new_item,
776
FmFolderModel* model)
782
FmFileInfo* file = new_item->inf;
784
GSequenceIter *item_it = g_sequence_insert_sorted(model->items, new_item, fm_folder_model_compare, model);
786
it.stamp = model->stamp;
787
it.user_data = item_it;
789
path = gtk_tree_path_new_from_indices(g_sequence_iter_get_position(item_it), -1);
790
gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, &it);
791
gtk_tree_path_free(path);
795
void fm_folder_model_file_deleted(FmFolderModel* model, FmFileInfo* file)
797
GSequenceIter *seq_it;
798
/* not required for hidden files */
799
gboolean update_view;
801
/* If there is no file info, that means the dir itself was deleted. */
802
if( G_UNLIKELY(!file) )
804
/* Clear the whole list */
805
GSequenceIter *items_it = g_sequence_get_begin_iter(model->items);
806
path = gtk_tree_path_new_from_indices(0, -1);
807
while( !g_sequence_iter_is_end(items_it) )
809
gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path);
810
file = (VFSFileInfo*)g_sequence_get(items_it);
811
items_it = g_sequence_iter_next(it);
812
vfs_file_info_unref(file);
814
for( l = model->items; l; l = model->items )
816
gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path);
817
file = (VFSFileInfo*)l->data;
818
model->items = g_list_delete_link(model->items, l);
819
vfs_file_info_unref(file);
821
g_sequence_remove_range( g_sequence_get_begin_iter(model->items), g_sequence_get_end_iter(model->items) );
822
gtk_tree_path_free(path);
827
if( !model->show_hidden && IS_HIDDEN_FILE(file->path->name) ) /* if this is a hidden file */
830
seq_it = g_sequence_get_begin_iter(model->hidden);
835
seq_it = g_sequence_get_begin_iter(model->items);
838
while( !g_sequence_iter_is_end(seq_it) )
840
FmFolderItem* item = (FmFolderItem*)g_sequence_get(seq_it);
841
if( item->inf == file )
843
seq_it = g_sequence_iter_next(seq_it);
848
GtkTreePath* path = gtk_tree_path_new_from_indices(g_sequence_iter_get_position(seq_it), -1);
849
gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path);
850
gtk_tree_path_free(path);
852
g_sequence_remove(seq_it);
855
void fm_folder_model_file_changed(FmFolderModel* model, FmFileInfo* file)
858
GSequenceIter* items_it;
862
if( !model->show_hidden && IS_HIDDEN_FILE(file->path->name) )
865
items_it = g_sequence_get_begin_iter(model->items);
866
/* FIXME: write a GCompareDataFunc for this */
867
while( !g_sequence_iter_is_end(items_it) )
869
item = (FmFolderItem*)g_sequence_get(items_it);
870
if( item->inf == file )
872
items_it = g_sequence_iter_next(items_it);
875
if( items_it == g_sequence_get_end_iter(model->items) )
878
/* update the icon */
881
g_object_unref(item->icon);
884
it.stamp = model->stamp;
885
it.user_data = items_it;
887
path = gtk_tree_path_new_from_indices(g_sequence_iter_get_position(items_it), -1);
888
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &it);
889
gtk_tree_path_free(path);
892
gboolean fm_folder_model_get_show_hidden(FmFolderModel* model)
894
return model->show_hidden;
897
void fm_folder_model_set_show_hidden(FmFolderModel* model, gboolean show_hidden)
901
GSequenceIter *items_it;
902
g_return_if_fail(model != NULL);
903
if( model->show_hidden == show_hidden )
906
model->show_hidden = show_hidden;
907
if( show_hidden ) /* add previously hidden items back to the list */
909
GSequenceIter *hidden_it = g_sequence_get_begin_iter(model->hidden);
910
while( !g_sequence_iter_is_end(hidden_it) )
913
GSequenceIter *next_hidden_it;
914
GSequenceIter *insert_item_it = g_sequence_search(model->items, g_sequence_get(hidden_it),
915
fm_folder_model_compare, model);
916
next_hidden_it = g_sequence_iter_next(hidden_it);
917
item = (FmFolderItem*)g_sequence_get(hidden_it);
918
it.stamp = model->stamp;
919
it.user_data = hidden_it;
920
g_sequence_move(hidden_it, insert_item_it);
921
GtkTreePath *path = gtk_tree_path_new_from_indices(g_sequence_iter_get_position(hidden_it), -1);
922
gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, &it);
923
gtk_tree_path_free(path);
924
hidden_it = next_hidden_it;
927
else /* move invisible items to hidden list */
929
GSequenceIter *items_it = g_sequence_get_begin_iter(model->items);
930
while( !g_sequence_iter_is_end(items_it) )
933
GSequenceIter *next_item_it = g_sequence_iter_next(items_it);
934
item = (FmFolderItem*)g_sequence_get(items_it);
935
if( IS_HIDDEN_FILE(item->inf->path->name) )
937
gint delete_pos = g_sequence_iter_get_position(items_it);
938
g_sequence_move( items_it, g_sequence_get_begin_iter(model->hidden) );
939
tp = gtk_tree_path_new_from_indices(delete_pos, -1);
940
/* tell everybody that we removed an item */
941
gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), tp);
942
gtk_tree_path_free(tp);
944
items_it = next_item_it;
949
void on_folder_loaded(FmFolder* folder, FmFolderModel* model)
951
g_signal_emit(model, signals[LOADED], 0);
954
void reload_icons(FmFolderModel* model, enum ReloadFlags flags)
957
GSequenceIter* it = g_sequence_get_begin_iter(model->items);
958
GtkTreePath* tp = gtk_tree_path_new_from_indices(0, -1);
960
if(model->thumbnail_requests)
962
g_list_foreach(model->thumbnail_requests, (GFunc)fm_thumbnail_request_cancel, NULL);
963
g_list_free(model->thumbnail_requests);
964
model->thumbnail_requests = NULL;
967
for( ; !g_sequence_iter_is_end(it); it = g_sequence_iter_next(it) )
969
FmFolderItem* item = (FmFolderItem*)g_sequence_get(it);
972
GtkTreeIter tree_it = {0};
973
if((flags & RELOAD_ICONS && !item->is_thumbnail) ||
974
(flags & RELOAD_THUMBNAILS && item->is_thumbnail))
976
g_object_unref(item->icon);
978
item->is_thumbnail = FALSE;
979
item->thumbnail_loading = FALSE;
980
tree_it.stamp = model->stamp;
981
tree_it.user_data = it;
982
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), tp, &tree_it);
985
gtk_tree_path_next(tp);
987
gtk_tree_path_free(tp);
989
it = g_sequence_get_begin_iter(model->hidden);
990
for( ; !g_sequence_iter_is_end(it); it = g_sequence_iter_next(it) )
992
FmFolderItem* item = (FmFolderItem*)g_sequence_get(it);
995
g_object_unref(item->icon);
997
item->is_thumbnail = FALSE;
998
item->thumbnail_loading = FALSE;
1003
void on_icon_theme_changed(GtkIconTheme* theme, FmFolderModel* model)
1005
reload_icons(model, RELOAD_ICONS);
1008
void fm_folder_model_get_common_suffix_for_prefix(FmFolderModel* model,
1009
const gchar* prefix,
1010
gboolean (*file_info_predicate)(FmFileInfo*),
1011
gchar* common_suffix)
1013
GSequenceIter *item_it;
1015
gboolean common_suffix_initialized = FALSE;
1017
g_return_if_fail(common_suffix != NULL);
1022
prefix_len = strlen(prefix);
1023
common_suffix[0] = 0;
1025
for( item_it = g_sequence_get_begin_iter(model->items);
1026
!g_sequence_iter_is_end(item_it);
1027
item_it = g_sequence_iter_next(item_it) )
1029
FmFolderItem* item = (FmFolderItem*)g_sequence_get(item_it);
1030
gboolean predicate_ok = (file_info_predicate == NULL) || file_info_predicate(item->inf);
1032
if( predicate_ok && g_str_has_prefix(item->inf->disp_name, prefix) )
1034
/* first match -> init */
1035
if( !common_suffix_initialized )
1037
strcpy(common_suffix, item->inf->disp_name + prefix_len);
1038
common_suffix_initialized = TRUE;
1042
while( common_suffix[i] == item->inf->disp_name[prefix_len + i] )
1044
common_suffix[i] = 0;
1051
gboolean fm_folder_model_find_iter_by_filename(FmFolderModel* model, GtkTreeIter* it, const char* name)
1053
GSequenceIter *item_it = g_sequence_get_begin_iter(model->items);
1054
for( ; !g_sequence_iter_is_end(item_it); item_it = g_sequence_iter_next(item_it) )
1056
FmFolderItem* item = (FmFolderItem*)g_sequence_get(item_it);
1057
if( g_strcmp0(item->inf->path->name, name) == 0 )
1059
it->stamp = model->stamp;
1060
it->user_data = item_it;
1067
void on_thumbnail_loaded(FmThumbnailRequest* req, gpointer user_data)
1069
FmFolderModel* model = (FmFolderModel*)user_data;
1070
FmFileInfo* fi = fm_thumbnail_request_get_file_info(req);
1071
GdkPixbuf* pix = fm_thumbnail_request_get_pixbuf(req);
1073
guint size = fm_thumbnail_request_get_size(req);
1074
GSequenceIter* seq_it;
1076
DEBUG("thumbnail loaded for %s, %p, size = %d", fi->path->name, pix, size);
1078
/* remove the request from list */
1079
model->thumbnail_requests = g_list_remove(model->thumbnail_requests, req);
1081
/* FIXME: it's better to find iter by file_info */
1082
if(fm_folder_model_find_iter_by_filename(model, &it, fi->path->name))
1085
seq_it = (GSequenceIter*)it.user_data;
1086
item = (FmFolderItem*)g_sequence_get(seq_it);
1089
GtkTreePath* tp = fm_folder_model_get_path(GTK_TREE_MODEL(model), &it);
1091
g_object_unref(item->icon);
1092
item->icon = g_object_ref(pix);
1093
item->is_thumbnail = TRUE;
1094
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), tp, &it);
1095
gtk_tree_path_free(tp);
1099
item->thumbnail_failed = TRUE;
1101
item->thumbnail_loading = FALSE;
1105
void fm_folder_model_set_icon_size(FmFolderModel* model, guint icon_size)
1107
if(model->icon_size == icon_size)
1109
model->icon_size = icon_size;
1110
reload_icons(model, RELOAD_BOTH);
1113
guint fm_folder_model_get_icon_size(FmFolderModel* model)
1115
return model->icon_size;
1118
void on_show_thumbnail_changed(FmConfig* cfg, gpointer user_data)
1120
FmFolderModel* model = (FmFolderModel*)user_data;
1121
reload_icons(model, RELOAD_THUMBNAILS);
1124
static GList* find_in_pending_thumbnail_requests(FmFolderModel* model, FmFileInfo* fi)
1126
GList* reqs = model->thumbnail_requests, *l;
1127
for(l=reqs;l;l=l->next)
1129
FmThumbnailRequest* req = (FmThumbnailRequest*)l->data;
1130
FmFileInfo* fi2 = fm_thumbnail_request_get_file_info(req);
1131
if(0 == g_strcmp0(fi->path->name, fi2->path->name))
1137
static void reload_thumbnail(FmFolderModel* model, GSequenceIter* seq_it, FmFolderItem* item)
1141
if(item->is_thumbnail)
1143
g_object_unref(item->icon);
1145
it.stamp = model->stamp;
1146
it.user_data = seq_it;
1147
tp = fm_folder_model_get_path(GTK_TREE_MODEL(model), &it);
1148
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), tp, &it);
1149
gtk_tree_path_free(tp);
1153
/* FIXME: how about hidden files? */
1154
void on_thumbnail_local_changed(FmConfig* cfg, gpointer user_data)
1156
FmFolderModel* model = (FmFolderModel*)user_data;
1157
FmThumbnailRequest* req;
1158
GList* new_reqs = NULL;
1159
GSequenceIter* seq_it;
1162
if(cfg->thumbnail_local)
1164
GList* l; /* remove non-local files from thumbnail requests */
1165
for(l = model->thumbnail_requests; l; )
1167
GList* next = l->next;
1168
req = (FmThumbnailRequest*)l->data;
1169
fi = fm_thumbnail_request_get_file_info(req);
1170
if(!fm_path_is_local(fi->path))
1172
fm_thumbnail_request_cancel(req);
1173
model->thumbnail_requests = g_list_delete_link(model->thumbnail_requests, l);
1174
/* FIXME: item->thumbnail_loading should be set to FALSE. */
1179
seq_it = g_sequence_get_begin_iter(model->items);
1180
while( !g_sequence_iter_is_end(seq_it) )
1182
FmFolderItem* item = (FmFolderItem*)g_sequence_get(seq_it);
1184
if(cfg->thumbnail_local)
1186
/* add all non-local files to thumbnail requests */
1187
if(!fm_path_is_local(fi->path))
1188
reload_thumbnail(model, seq_it, item);
1192
/* add all non-local files to thumbnail requests */
1193
if(!fm_path_is_local(fi->path))
1195
req = fm_thumbnail_request(fi, model->icon_size, on_thumbnail_loaded, model);
1196
new_reqs = g_list_append(new_reqs, req);
1199
seq_it = g_sequence_iter_next(seq_it);
1202
model->thumbnail_requests = g_list_concat(model->thumbnail_requests, new_reqs);
1205
/* FIXME: how about hidden files? */
1206
void on_thumbnail_max_changed(FmConfig* cfg, gpointer user_data)
1208
FmFolderModel* model = (FmFolderModel*)user_data;
1209
FmThumbnailRequest* req;
1210
GList* new_reqs = NULL, *l;
1211
GSequenceIter* seq_it;
1213
guint thumbnail_max_bytes = fm_config->thumbnail_max << 10;
1215
if(cfg->thumbnail_max)
1217
/* remove files which are too big from thumbnail requests */
1218
for(l = model->thumbnail_requests; l; )
1220
GList* next = l->next;
1221
req = (FmThumbnailRequest*)l->data;
1222
fi = fm_thumbnail_request_get_file_info(req);
1223
if(fi->size > (cfg->thumbnail_max << 10))
1225
fm_thumbnail_request_cancel(req);
1226
model->thumbnail_requests = g_list_delete_link(model->thumbnail_requests, l);
1231
seq_it = g_sequence_get_begin_iter(model->items);
1232
while( !g_sequence_iter_is_end(seq_it) )
1234
FmFolderItem* item = (FmFolderItem*)g_sequence_get(seq_it);
1236
if(cfg->thumbnail_max)
1238
if(thumbnail_max_bytes > model->thumbnail_max)
1240
if(fi->size < thumbnail_max_bytes && fi->size > model->thumbnail_max )
1242
if(!item->thumbnail_failed && fm_file_info_can_thumbnail(fi))
1244
req = fm_thumbnail_request(fi, model->icon_size, on_thumbnail_loaded, model);
1245
new_reqs = g_list_append(new_reqs, req);
1251
if(fi->size > thumbnail_max_bytes)
1252
reload_thumbnail(model, seq_it, item);
1255
else /* no limit, all files can be added */
1257
/* add all files to thumbnail requests */
1258
if(!item->is_thumbnail && !item->thumbnail_loading && !item->thumbnail_failed && fm_file_info_can_thumbnail(fi))
1260
GList* l = find_in_pending_thumbnail_requests(model, fi);
1263
req = fm_thumbnail_request(fi, model->icon_size, on_thumbnail_loaded, model);
1264
new_reqs = g_list_append(new_reqs, req);
1268
seq_it = g_sequence_iter_next(seq_it);
1271
model->thumbnail_requests = g_list_concat(model->thumbnail_requests, new_reqs);
1272
model->thumbnail_max = thumbnail_max_bytes;