~ubuntu-branches/ubuntu/oneiric/libfm/oneiric

« back to all changes in this revision

Viewing changes to .pc/02-libfm-0.1.14-API-changes.patch/src/gtk/fm-folder-model.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2010-12-02 00:20:44 UTC
  • mfrom: (1.1.5 upstream) (0.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20101202002044-qhja03c1hk95b0q7
Tags: 0.1.14-2ubuntu1
* Sync with Debian unstable.
 - Fix crash on right click (LP: #617655)
* Ubuntu remaining changes:
 - debian/patches/90_add_gobject_link.patch:
  + From upstream, add "gobject-2.0" in configure.ac explicitly.
 - debian/patches/03_disable_deprecated_gio_module.patch:
  + Disable -DG_DISABLE_DEPRECATED for the gio module to build with glib
    2.27+.
 - debian/control:
  + Add Replaces/Conflicts for libfm0 and libfm-gtk0 (<= 0.1.11-1) to ensure
    migration from lucid.
  + Build-depends on dh-autoreconf.
 - debian/rules:
  + Use dh --with autoreconf.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      fm-folder-model.c
 
3
 *
 
4
 *      Copyright 2009 PCMan <pcman.tw@gmail.com>
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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,
 
19
 *      MA 02110-1301, USA.
 
20
 */
 
21
 
 
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"
 
27
 
 
28
#include <gdk/gdk.h>
 
29
 
 
30
#include <string.h>
 
31
#include <gio/gio.h>
 
32
 
 
33
/* #define ENABLE_DEBUG */
 
34
#ifdef ENABLE_DEBUG
 
35
#define DEBUG(...)  g_debug(__VA_ARGS__)
 
36
#else
 
37
#define DEBUG(...)
 
38
#endif
 
39
 
 
40
enum {
 
41
    LOADED,
 
42
    N_SIGNALS
 
43
};
 
44
 
 
45
typedef struct _FmFolderItem FmFolderItem;
 
46
struct _FmFolderItem
 
47
{
 
48
    FmFileInfo* inf;
 
49
    GdkPixbuf* icon;
 
50
    gboolean is_thumbnail : 1;
 
51
    gboolean thumbnail_loading : 1;
 
52
    gboolean thumbnail_failed : 1;
 
53
};
 
54
 
 
55
enum ReloadFlags
 
56
{
 
57
    RELOAD_ICONS = 1 << 0,
 
58
    RELOAD_THUMBNAILS = 1 << 1,
 
59
    RELOAD_BOTH = (RELOAD_ICONS | RELOAD_THUMBNAILS)
 
60
};
 
61
 
 
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);
 
66
 
 
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) )
 
73
 
 
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,
 
77
                                             gint index);
 
78
static gboolean fm_folder_model_get_iter(GtkTreeModel *tree_model,
 
79
                                         GtkTreeIter *iter,
 
80
                                         GtkTreePath *path);
 
81
static GtkTreePath *fm_folder_model_get_path(GtkTreeModel *tree_model,
 
82
                                             GtkTreeIter *iter);
 
83
static void fm_folder_model_get_value(GtkTreeModel *tree_model,
 
84
                                      GtkTreeIter *iter,
 
85
                                      gint column,
 
86
                                      GValue *value);
 
87
static gboolean fm_folder_model_iter_next(GtkTreeModel *tree_model,
 
88
                                          GtkTreeIter *iter);
 
89
static gboolean fm_folder_model_iter_children(GtkTreeModel *tree_model,
 
90
                                              GtkTreeIter *iter,
 
91
                                              GtkTreeIter *parent);
 
92
static gboolean fm_folder_model_iter_has_child(GtkTreeModel *tree_model,
 
93
                                               GtkTreeIter *iter);
 
94
static gint fm_folder_model_iter_n_children(GtkTreeModel *tree_model,
 
95
                                            GtkTreeIter *iter);
 
96
static gboolean fm_folder_model_iter_nth_child(GtkTreeModel *tree_model,
 
97
                                               GtkTreeIter *iter,
 
98
                                               GtkTreeIter *parent,
 
99
                                               gint n);
 
100
static gboolean fm_folder_model_iter_parent(GtkTreeModel *tree_model,
 
101
                                            GtkTreeIter *iter,
 
102
                                            GtkTreeIter *child);
 
103
static gboolean fm_folder_model_get_sort_column_id(GtkTreeSortable* sortable,
 
104
                                                   gint* sort_column_id,
 
105
                                                   GtkSortType* order);
 
106
static void fm_folder_model_set_sort_column_id(GtkTreeSortable* sortable,
 
107
                                               gint sort_column_id,
 
108
                                               GtkSortType order);
 
109
static void fm_folder_model_set_sort_func(GtkTreeSortable *sortable,
 
110
                                          gint sort_column_id,
 
111
                                          GtkTreeIterCompareFunc sort_func,
 
112
                                          gpointer user_data,
 
113
                                          GtkDestroyNotify destroy);
 
114
static void fm_folder_model_set_default_sort_func(GtkTreeSortable *sortable,
 
115
                                                  GtkTreeIterCompareFunc sort_func,
 
116
                                                  gpointer user_data,
 
117
                                                  GtkDestroyNotify destroy);
 
118
static void fm_folder_model_sort(FmFolderModel* model);
 
119
 
 
120
/* signal handlers */
 
121
static void on_folder_loaded(FmFolder* folder, FmFolderModel* model);
 
122
 
 
123
static void on_icon_theme_changed(GtkIconTheme* theme, FmFolderModel* model);
 
124
 
 
125
static void on_thumbnail_loaded(FmThumbnailRequest* req, gpointer user_data);
 
126
 
 
127
static void on_show_thumbnail_changed(FmConfig* cfg, gpointer user_data);
 
128
 
 
129
static void on_thumbnail_local_changed(FmConfig* cfg, gpointer user_data);
 
130
 
 
131
static void on_thumbnail_max_changed(FmConfig* cfg, gpointer user_data);
 
132
 
 
133
static void reload_icons(FmFolderModel* model, enum ReloadFlags flags);
 
134
 
 
135
#define IS_HIDDEN_FILE(fn)  \
 
136
    (fn[0] == '.' || g_str_has_suffix(fn, "~"))
 
137
 
 
138
 
 
139
static GType column_types[ N_FOLDER_MODEL_COLS ];
 
140
static guint signals[N_SIGNALS];
 
141
 
 
142
void fm_folder_model_init(FmFolderModel* model)
 
143
{
 
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();
 
148
 
 
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);
 
154
 
 
155
    model->thumbnail_max = fm_config->thumbnail_max << 10;
 
156
}
 
157
 
 
158
void fm_folder_model_class_init(FmFolderModelClass *klass)
 
159
{
 
160
    GObjectClass * object_class;
 
161
 
 
162
    fm_folder_model_parent_class = ( GObjectClass* )g_type_class_peek_parent(klass);
 
163
    object_class = ( GObjectClass* )klass;
 
164
 
 
165
    object_class->finalize = fm_folder_model_finalize;
 
166
 
 
167
    signals[LOADED]=
 
168
        g_signal_new("loaded",
 
169
                     G_TYPE_FROM_CLASS(klass),
 
170
                     G_SIGNAL_RUN_FIRST,
 
171
                     G_STRUCT_OFFSET(FmFolderModelClass, loaded),
 
172
                     NULL, NULL,
 
173
                     g_cclosure_marshal_VOID__VOID,
 
174
                     G_TYPE_NONE, 0);
 
175
}
 
176
 
 
177
void fm_folder_model_tree_model_init(GtkTreeModelIface *iface)
 
178
{
 
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;
 
191
 
 
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;
 
202
}
 
203
 
 
204
void fm_folder_model_tree_sortable_init(GtkTreeSortableIface *iface)
 
205
{
 
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;
 
212
}
 
213
 
 
214
void fm_folder_model_drag_source_init(GtkTreeDragSourceIface *iface)
 
215
{
 
216
    /* FIXME: Unused. Will this cause any problem? */
 
217
}
 
218
 
 
219
void fm_folder_model_drag_dest_init(GtkTreeDragDestIface *iface)
 
220
{
 
221
    /* FIXME: Unused. Will this cause any problem? */
 
222
}
 
223
 
 
224
void fm_folder_model_finalize(GObject *object)
 
225
{
 
226
    FmFolderModel* model = ( FmFolderModel* )object;
 
227
    int i;
 
228
    /*
 
229
    char* str = fm_path_to_str(model->dir->dir_path);
 
230
    g_debug("FINALIZE FOLDER MODEL(%p): %s", model, str);
 
231
    g_free(str);
 
232
    */
 
233
    fm_folder_model_set_folder(model, NULL);
 
234
    g_signal_handler_disconnect(gtk_icon_theme_get_default(),
 
235
                                model->theme_change_handler);
 
236
 
 
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);
 
240
 
 
241
    g_list_foreach(model->thumbnail_requests, (GFunc)fm_thumbnail_request_cancel, NULL);
 
242
    g_list_free(model->thumbnail_requests);
 
243
 
 
244
    /* must chain up - finalize parent */
 
245
    (*G_OBJECT_CLASS(fm_folder_model_parent_class)->finalize)(object);
 
246
}
 
247
 
 
248
FmFolderModel *fm_folder_model_new(FmFolder* dir, gboolean show_hidden)
 
249
{
 
250
    FmFolderModel* model;
 
251
    model = ( FmFolderModel* )g_object_new(FM_TYPE_FOLDER_MODEL, NULL);
 
252
    model->items = NULL;
 
253
    model->hidden = NULL;
 
254
    model->show_hidden = show_hidden;
 
255
    fm_folder_model_set_folder(model, dir);
 
256
    return model;
 
257
}
 
258
 
 
259
static inline FmFolderItem* fm_folder_item_new(FmFileInfo* inf)
 
260
{
 
261
    FmFolderItem* item = g_slice_new0(FmFolderItem);
 
262
    item->inf = fm_file_info_ref(inf);
 
263
    return item;
 
264
}
 
265
 
 
266
static inline void fm_folder_item_free(FmFolderItem* item)
 
267
{
 
268
    if( item->icon )
 
269
        g_object_unref(item->icon);
 
270
    fm_file_info_unref(item->inf);
 
271
    g_slice_free(FmFolderItem, item);
 
272
}
 
273
 
 
274
static void _fm_folder_model_insert_item(FmFolder* dir,
 
275
                                         FmFolderItem* new_item,
 
276
                                         FmFolderModel* model);
 
277
 
 
278
static void _fm_folder_model_files_changed(FmFolder* dir, GSList* files,
 
279
                                           FmFolderModel* model)
 
280
{
 
281
    GSList* l;
 
282
    for( l = files; l; l=l->next )
 
283
        fm_folder_model_file_changed(model, l->data);
 
284
}
 
285
 
 
286
static void _fm_folder_model_add_file(FmFolderModel* model, FmFileInfo* file)
 
287
{
 
288
    if( !model->show_hidden && IS_HIDDEN_FILE(file->path->name) )
 
289
        g_sequence_append( model->hidden, fm_folder_item_new(file) );
 
290
    else
 
291
        fm_folder_model_file_created(model, file);
 
292
}
 
293
 
 
294
static void _fm_folder_model_files_added(FmFolder* dir, GSList* files,
 
295
                                         FmFolderModel* model)
 
296
{
 
297
    GSList* l;
 
298
    FmFileInfo* file;
 
299
    for( l = files; l; l=l->next )
 
300
        _fm_folder_model_add_file(model, (FmFileInfo*)l->data);
 
301
}
 
302
 
 
303
 
 
304
static void _fm_folder_model_files_removed(FmFolder* dir, GSList* files,
 
305
                                           FmFolderModel* model)
 
306
{
 
307
    GSList* l;
 
308
    for( l = files; l; l=l->next )
 
309
        fm_folder_model_file_deleted(model, (FmFileInfo*)l->data);
 
310
}
 
311
 
 
312
void fm_folder_model_set_folder(FmFolderModel* model, FmFolder* dir)
 
313
{
 
314
    GSequenceIter *it;
 
315
    if( model->dir == dir )
 
316
        return;
 
317
    if( model->dir )
 
318
    {
 
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);
 
327
 
 
328
        g_sequence_free(model->items);
 
329
        g_sequence_free(model->hidden);
 
330
        g_object_unref(model->dir);
 
331
    }
 
332
    model->dir = dir;
 
333
    model->items = g_sequence_new((GDestroyNotify)fm_folder_item_free);
 
334
    model->hidden = g_sequence_new((GDestroyNotify)fm_folder_item_free);
 
335
    if( !dir )
 
336
        return;
 
337
 
 
338
    model->dir = (FmFolder*)g_object_ref(model->dir);
 
339
 
 
340
    g_signal_connect(model->dir, "files-added",
 
341
                     G_CALLBACK(_fm_folder_model_files_added),
 
342
                     model);
 
343
    g_signal_connect(model->dir, "files-removed",
 
344
                     G_CALLBACK(_fm_folder_model_files_removed),
 
345
                     model);
 
346
    g_signal_connect(model->dir, "files-changed",
 
347
                     G_CALLBACK(_fm_folder_model_files_changed),
 
348
                     model);
 
349
    g_signal_connect(model->dir, "loaded",
 
350
                     G_CALLBACK(on_folder_loaded), model);
 
351
 
 
352
    if( !fm_list_is_empty(dir->files) )
 
353
    {
 
354
        GList *l;
 
355
        for( l = fm_list_peek_head_link(dir->files); l; l = l->next )
 
356
            _fm_folder_model_add_file(model, (FmFileInfo*)l->data);
 
357
    }
 
358
 
 
359
    if( !fm_folder_get_is_loading(model->dir) ) /* if it's already loaded */
 
360
        on_folder_loaded(model->dir, model);  /* emit 'loaded' signal */
 
361
}
 
362
 
 
363
gboolean fm_folder_model_get_is_loading(FmFolderModel* model)
 
364
{
 
365
    return fm_folder_get_is_loading(model->dir);
 
366
}
 
367
 
 
368
GtkTreeModelFlags fm_folder_model_get_flags(GtkTreeModel *tree_model)
 
369
{
 
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);
 
372
}
 
373
 
 
374
gint fm_folder_model_get_n_columns(GtkTreeModel *tree_model)
 
375
{
 
376
    return N_FOLDER_MODEL_COLS;
 
377
}
 
378
 
 
379
GType fm_folder_model_get_column_type(GtkTreeModel *tree_model,
 
380
                                      gint index)
 
381
{
 
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 ];
 
385
}
 
386
 
 
387
gboolean fm_folder_model_get_iter(GtkTreeModel *tree_model,
 
388
                                  GtkTreeIter *iter,
 
389
                                  GtkTreePath *path)
 
390
{
 
391
    FmFolderModel* model;
 
392
    gint *indices, n, depth;
 
393
    GSequenceIter* items_it;
 
394
 
 
395
    g_assert( FM_IS_FOLDER_MODEL(tree_model) );
 
396
    g_assert(path!=NULL);
 
397
 
 
398
    model = FM_FOLDER_MODEL(tree_model);
 
399
 
 
400
    indices = gtk_tree_path_get_indices(path);
 
401
    depth   = gtk_tree_path_get_depth(path);
 
402
 
 
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 */
 
405
 
 
406
    n = indices[0]; /* the n-th top level row */
 
407
 
 
408
    if( n >= g_sequence_get_length(model->items) || n < 0 )
 
409
        return FALSE;
 
410
 
 
411
    items_it = g_sequence_get_iter_at_pos(model->items, n);
 
412
 
 
413
    g_assert( items_it  != g_sequence_get_end_iter(model->items) );
 
414
 
 
415
    /* We simply store a pointer in the iter */
 
416
    iter->stamp = model->stamp;
 
417
    iter->user_data  = items_it;
 
418
 
 
419
    return TRUE;
 
420
}
 
421
 
 
422
GtkTreePath *fm_folder_model_get_path(GtkTreeModel *tree_model,
 
423
                                      GtkTreeIter *iter)
 
424
{
 
425
    GtkTreePath* path;
 
426
    GSequenceIter* items_it;
 
427
    FmFolderModel* model = FM_FOLDER_MODEL(tree_model);
 
428
 
 
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);
 
433
 
 
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) );
 
437
    return path;
 
438
}
 
439
 
 
440
void fm_folder_model_get_value(GtkTreeModel *tree_model,
 
441
                               GtkTreeIter *iter,
 
442
                               gint column,
 
443
                               GValue *value)
 
444
{
 
445
    GSequenceIter* item_it;
 
446
    FmFolderModel* model = FM_FOLDER_MODEL(tree_model);
 
447
 
 
448
    g_return_if_fail(iter != NULL);
 
449
    g_return_if_fail( column < G_N_ELEMENTS(column_types) );
 
450
 
 
451
    g_value_init(value, column_types[column]);
 
452
 
 
453
    item_it = (GSequenceIter*)iter->user_data;
 
454
    g_return_if_fail(item_it != NULL);
 
455
 
 
456
    FmFolderItem* item = (FmFolderItem*)g_sequence_get(item_it);
 
457
    FmFileInfo* info = item->inf;
 
458
 
 
459
    switch( column )
 
460
    {
 
461
    case COL_FILE_GICON:
 
462
        g_value_set_object(value, info->icon->gicon);
 
463
        break;
 
464
    case COL_FILE_ICON:
 
465
    {
 
466
        if( G_UNLIKELY(!item->icon) )
 
467
        {
 
468
            if( !info->icon )
 
469
                return;
 
470
            item->icon = fm_icon_get_pixbuf(info->icon, model->icon_size);
 
471
        }
 
472
        g_value_set_object(value, item->icon);
 
473
 
 
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))
 
477
        {
 
478
            if(!item->is_thumbnail && !item->thumbnail_failed && !item->thumbnail_loading)
 
479
            {
 
480
                if(fm_file_info_can_thumbnail(item->inf))
 
481
                {
 
482
                    if(item->inf->size > 0 && item->inf->size <= (fm_config->thumbnail_max << 10))
 
483
                    {
 
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;
 
487
                    }
 
488
                }
 
489
                else
 
490
                {
 
491
                    item->thumbnail_failed = TRUE;
 
492
                }
 
493
            }
 
494
        }
 
495
        break;
 
496
    }
 
497
    case COL_FILE_NAME:
 
498
        g_value_set_string(value, info->disp_name);
 
499
        break;
 
500
    case COL_FILE_SIZE:
 
501
        g_value_set_string( value, fm_file_info_get_disp_size(info) );
 
502
        break;
 
503
    case COL_FILE_DESC:
 
504
        g_value_set_string( value, fm_file_info_get_desc(info) );
 
505
        break;
 
506
    case COL_FILE_PERM:
 
507
//        g_value_set_string( value, fm_file_info_get_disp_perm(info) );
 
508
        break;
 
509
    case COL_FILE_OWNER:
 
510
//        g_value_set_string( value, fm_file_info_get_disp_owner(info) );
 
511
        break;
 
512
    case COL_FILE_MTIME:
 
513
        g_value_set_string( value, fm_file_info_get_disp_mtime(info) );
 
514
        break;
 
515
    case COL_FILE_INFO:
 
516
        g_value_set_pointer(value, info);
 
517
        break;
 
518
    }
 
519
}
 
520
 
 
521
gboolean fm_folder_model_iter_next(GtkTreeModel *tree_model,
 
522
                                   GtkTreeIter *iter)
 
523
{
 
524
    GSequenceIter* item_it, *next_item_it;
 
525
    FmFolderModel* model;
 
526
 
 
527
    g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), FALSE);
 
528
 
 
529
    if( iter == NULL || iter->user_data == NULL )
 
530
        return FALSE;
 
531
 
 
532
    model = FM_FOLDER_MODEL(tree_model);
 
533
    item_it = (GSequenceIter *)iter->user_data;
 
534
 
 
535
    /* Is this the last iter in the list? */
 
536
    next_item_it = g_sequence_iter_next(item_it);
 
537
 
 
538
    if( g_sequence_iter_is_end(next_item_it) )
 
539
        return FALSE;
 
540
 
 
541
    iter->stamp = model->stamp;
 
542
    iter->user_data = next_item_it;
 
543
 
 
544
    return TRUE;
 
545
}
 
546
 
 
547
gboolean fm_folder_model_iter_children(GtkTreeModel *tree_model,
 
548
                                       GtkTreeIter *iter,
 
549
                                       GtkTreeIter *parent)
 
550
{
 
551
    FmFolderModel* model;
 
552
    GSequenceIter* items_it;
 
553
    g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
 
554
 
 
555
    /* this is a list, nodes have no children */
 
556
    if( parent )
 
557
        return FALSE;
 
558
 
 
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);
 
562
 
 
563
    /* No rows => no first row */
 
564
//    if ( model->dir->n_items == 0 )
 
565
//        return FALSE;
 
566
 
 
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;
 
571
    return TRUE;
 
572
}
 
573
 
 
574
gboolean fm_folder_model_iter_has_child(GtkTreeModel *tree_model,
 
575
                                        GtkTreeIter *iter)
 
576
{
 
577
    return FALSE;
 
578
}
 
579
 
 
580
gint fm_folder_model_iter_n_children(GtkTreeModel *tree_model,
 
581
                                     GtkTreeIter *iter)
 
582
{
 
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 */
 
588
    if( !iter )
 
589
        return g_sequence_get_length(model->items);
 
590
    return 0; /* otherwise, this is easy again for a list */
 
591
}
 
592
 
 
593
gboolean fm_folder_model_iter_nth_child(GtkTreeModel *tree_model,
 
594
                                        GtkTreeIter *iter,
 
595
                                        GtkTreeIter *parent,
 
596
                                        gint n)
 
597
{
 
598
    GSequenceIter* items_it;
 
599
    FmFolderModel* model;
 
600
 
 
601
    g_return_val_if_fail(FM_IS_FOLDER_MODEL(tree_model), FALSE);
 
602
    model = FM_FOLDER_MODEL(tree_model);
 
603
 
 
604
    /* a list has only top-level rows */
 
605
    if( parent )
 
606
        return FALSE;
 
607
 
 
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 )
 
610
        return FALSE;
 
611
 
 
612
    items_it = g_sequence_get_iter_at_pos(model->items, n);
 
613
    g_assert( items_it  != g_sequence_get_end_iter(model->items) );
 
614
 
 
615
    iter->stamp = model->stamp;
 
616
    iter->user_data  = items_it;
 
617
 
 
618
    return TRUE;
 
619
}
 
620
 
 
621
gboolean fm_folder_model_iter_parent(GtkTreeModel *tree_model,
 
622
                                     GtkTreeIter *iter,
 
623
                                     GtkTreeIter *child)
 
624
{
 
625
    return FALSE;
 
626
}
 
627
 
 
628
gboolean fm_folder_model_get_sort_column_id(GtkTreeSortable* sortable,
 
629
                                            gint* sort_column_id,
 
630
                                            GtkSortType* order)
 
631
{
 
632
    FmFolderModel* model = (FmFolderModel*)sortable;
 
633
    if( sort_column_id )
 
634
        *sort_column_id = model->sort_col;
 
635
    if( order )
 
636
        *order = model->sort_order;
 
637
    return TRUE;
 
638
}
 
639
 
 
640
void fm_folder_model_set_sort_column_id(GtkTreeSortable* sortable,
 
641
                                        gint sort_column_id,
 
642
                                        GtkSortType order)
 
643
{
 
644
    FmFolderModel* model = (FmFolderModel*)sortable;
 
645
    if( model->sort_col == sort_column_id && model->sort_order == order )
 
646
        return;
 
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);
 
651
}
 
652
 
 
653
void fm_folder_model_set_sort_func(GtkTreeSortable *sortable,
 
654
                                   gint sort_column_id,
 
655
                                   GtkTreeIterCompareFunc sort_func,
 
656
                                   gpointer user_data,
 
657
                                   GtkDestroyNotify destroy)
 
658
{
 
659
    g_warning("fm_folder_model_set_sort_func: Not supported\n");
 
660
}
 
661
 
 
662
void fm_folder_model_set_default_sort_func(GtkTreeSortable *sortable,
 
663
                                           GtkTreeIterCompareFunc sort_func,
 
664
                                           gpointer user_data,
 
665
                                           GtkDestroyNotify destroy)
 
666
{
 
667
    g_warning("fm_folder_model_set_default_sort_func: Not supported\n");
 
668
}
 
669
 
 
670
static gint fm_folder_model_compare(FmFolderItem* item1,
 
671
                                    FmFolderItem* item2,
 
672
                                    FmFolderModel* model)
 
673
{
 
674
    FmFileInfo* file1 = item1->inf;
 
675
    FmFileInfo* file2 = item2->inf;
 
676
    const char* key1;
 
677
    const char* key2;
 
678
    int ret = 0;
 
679
 
 
680
    /* put folders before files */
 
681
    ret = fm_file_info_is_dir(file2) - fm_file_info_is_dir(file1);
 
682
    if( ret )
 
683
        return ret;
 
684
 
 
685
    switch( model->sort_col )
 
686
    {
 
687
    case COL_FILE_NAME:
 
688
    {
 
689
_sort_by_name:
 
690
        key1 = fm_file_info_get_collate_key(file1);
 
691
        key2 = fm_file_info_get_collate_key(file2);
 
692
        /*
 
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)
 
696
        */
 
697
        ret = g_strcmp0(key1, key2);
 
698
        break;
 
699
    }
 
700
    case COL_FILE_SIZE:
 
701
    {
 
702
        /* to support files more than 2Gb */
 
703
        goffset diff = file1->size - file2->size;
 
704
        if(0 == diff)
 
705
            goto _sort_by_name;
 
706
        else
 
707
            ret = diff > 0 ? 1 : -1;
 
708
        break;
 
709
    }
 
710
    case COL_FILE_MTIME:
 
711
        ret = file1->mtime - file2->mtime;
 
712
        if(0 == ret)
 
713
            goto _sort_by_name;
 
714
        break;
 
715
    case COL_FILE_DESC:
 
716
        /* FIXME: this is very slow */
 
717
        ret = g_utf8_collate(fm_file_info_get_desc(file1), fm_file_info_get_desc(file2));
 
718
        if(0 == ret)
 
719
            goto _sort_by_name;
 
720
        break;
 
721
    default:
 
722
        return 0;
 
723
    }
 
724
    return model->sort_order == GTK_SORT_ASCENDING ? ret : -ret;
 
725
}
 
726
 
 
727
void fm_folder_model_sort(FmFolderModel* model)
 
728
{
 
729
    GHashTable* old_order;
 
730
    gint *new_order;
 
731
    GSequenceIter *items_it;
 
732
    GtkTreePath *path;
 
733
 
 
734
    /* if there is only one item */
 
735
    if( model->items == NULL || g_sequence_get_length(model->items) <= 1 )
 
736
        return;
 
737
 
 
738
    old_order = g_hash_table_new(g_direct_hash, g_direct_equal);
 
739
    /* save old order */
 
740
    items_it = g_sequence_get_begin_iter(model->items);
 
741
    while( !g_sequence_iter_is_end(items_it) )
 
742
    {
 
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);
 
746
    }
 
747
 
 
748
    /* sort the list */
 
749
    g_sequence_sort(model->items, fm_folder_model_compare, model);
 
750
 
 
751
    /* save new order */
 
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) )
 
755
    {
 
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);
 
759
    }
 
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);
 
765
    g_free(new_order);
 
766
}
 
767
 
 
768
void fm_folder_model_file_created(FmFolderModel* model, FmFileInfo* file)
 
769
{
 
770
    FmFolderItem* new_item = fm_folder_item_new(file);
 
771
    _fm_folder_model_insert_item(model->dir, new_item, model);
 
772
}
 
773
 
 
774
void _fm_folder_model_insert_item(FmFolder* dir,
 
775
                                  FmFolderItem* new_item,
 
776
                                  FmFolderModel* model)
 
777
{
 
778
    GList* l;
 
779
    GtkTreeIter it;
 
780
    GtkTreePath* path;
 
781
    FmFolderItem* item;
 
782
    FmFileInfo* file = new_item->inf;
 
783
 
 
784
    GSequenceIter *item_it = g_sequence_insert_sorted(model->items, new_item, fm_folder_model_compare, model);
 
785
 
 
786
    it.stamp = model->stamp;
 
787
    it.user_data  = item_it;
 
788
 
 
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);
 
792
}
 
793
 
 
794
 
 
795
void fm_folder_model_file_deleted(FmFolderModel* model, FmFileInfo* file)
 
796
{
 
797
    GSequenceIter *seq_it;
 
798
    /* not required for hidden files */
 
799
    gboolean update_view;
 
800
#if 0
 
801
    /* If there is no file info, that means the dir itself was deleted. */
 
802
    if( G_UNLIKELY(!file) )
 
803
    {
 
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) )
 
808
        {
 
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);
 
813
        }
 
814
        for( l = model->items; l; l = model->items )
 
815
        {
 
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);
 
820
        }
 
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);
 
823
        return;
 
824
    }
 
825
#endif
 
826
 
 
827
    if( !model->show_hidden && IS_HIDDEN_FILE(file->path->name) ) /* if this is a hidden file */
 
828
    {
 
829
        update_view = FALSE;
 
830
        seq_it = g_sequence_get_begin_iter(model->hidden);
 
831
    }
 
832
    else
 
833
    {
 
834
        update_view = TRUE;
 
835
        seq_it = g_sequence_get_begin_iter(model->items);
 
836
    }
 
837
 
 
838
    while( !g_sequence_iter_is_end(seq_it) )
 
839
    {
 
840
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(seq_it);
 
841
        if( item->inf == file )
 
842
            break;
 
843
        seq_it = g_sequence_iter_next(seq_it);
 
844
    }
 
845
 
 
846
    if( update_view )
 
847
    {
 
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);
 
851
    }
 
852
    g_sequence_remove(seq_it);
 
853
}
 
854
 
 
855
void fm_folder_model_file_changed(FmFolderModel* model, FmFileInfo* file)
 
856
{
 
857
    FmFolderItem* item;
 
858
    GSequenceIter* items_it;
 
859
    GtkTreeIter it;
 
860
    GtkTreePath* path;
 
861
 
 
862
    if( !model->show_hidden && IS_HIDDEN_FILE(file->path->name) )
 
863
        return;
 
864
 
 
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) )
 
868
    {
 
869
        item = (FmFolderItem*)g_sequence_get(items_it);
 
870
        if( item->inf == file )
 
871
            break;
 
872
        items_it = g_sequence_iter_next(items_it);
 
873
    }
 
874
 
 
875
    if( items_it == g_sequence_get_end_iter(model->items) )
 
876
        return;
 
877
 
 
878
    /* update the icon */
 
879
    if( item->icon )
 
880
    {
 
881
        g_object_unref(item->icon);
 
882
        item->icon = NULL;
 
883
    }
 
884
    it.stamp = model->stamp;
 
885
    it.user_data  = items_it;
 
886
 
 
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);
 
890
}
 
891
 
 
892
gboolean fm_folder_model_get_show_hidden(FmFolderModel* model)
 
893
{
 
894
    return model->show_hidden;
 
895
}
 
896
 
 
897
void fm_folder_model_set_show_hidden(FmFolderModel* model, gboolean show_hidden)
 
898
{
 
899
    FmFolderItem* item;
 
900
    GList *l, *next;
 
901
    GSequenceIter *items_it;
 
902
    g_return_if_fail(model != NULL);
 
903
    if( model->show_hidden == show_hidden )
 
904
        return;
 
905
 
 
906
    model->show_hidden = show_hidden;
 
907
    if( show_hidden ) /* add previously hidden items back to the list */
 
908
    {
 
909
        GSequenceIter *hidden_it = g_sequence_get_begin_iter(model->hidden);
 
910
        while( !g_sequence_iter_is_end(hidden_it) )
 
911
        {
 
912
            GtkTreeIter 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;
 
925
        }
 
926
    }
 
927
    else /* move invisible items to hidden list */
 
928
    {
 
929
        GSequenceIter *items_it = g_sequence_get_begin_iter(model->items);
 
930
        while( !g_sequence_iter_is_end(items_it) )
 
931
        {
 
932
            GtkTreePath* tp;
 
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) )
 
936
            {
 
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);
 
943
            }
 
944
            items_it = next_item_it;
 
945
        }
 
946
    }
 
947
}
 
948
 
 
949
void on_folder_loaded(FmFolder* folder, FmFolderModel* model)
 
950
{
 
951
    g_signal_emit(model, signals[LOADED], 0);
 
952
}
 
953
 
 
954
void reload_icons(FmFolderModel* model, enum ReloadFlags flags)
 
955
{
 
956
    /* reload icons */
 
957
    GSequenceIter* it = g_sequence_get_begin_iter(model->items);
 
958
    GtkTreePath* tp = gtk_tree_path_new_from_indices(0, -1);
 
959
 
 
960
    if(model->thumbnail_requests)
 
961
    {
 
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;
 
965
    }
 
966
 
 
967
    for( ; !g_sequence_iter_is_end(it); it = g_sequence_iter_next(it) )
 
968
    {
 
969
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(it);
 
970
        if(item->icon)
 
971
        {
 
972
            GtkTreeIter tree_it = {0};
 
973
            if((flags & RELOAD_ICONS && !item->is_thumbnail) ||
 
974
               (flags & RELOAD_THUMBNAILS && item->is_thumbnail))
 
975
            {
 
976
                g_object_unref(item->icon);
 
977
                item->icon = NULL;
 
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);
 
983
            }
 
984
        }
 
985
        gtk_tree_path_next(tp);
 
986
    }
 
987
    gtk_tree_path_free(tp);
 
988
 
 
989
    it = g_sequence_get_begin_iter(model->hidden);
 
990
    for( ; !g_sequence_iter_is_end(it); it = g_sequence_iter_next(it) )
 
991
    {
 
992
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(it);
 
993
        if(item->icon)
 
994
        {
 
995
            g_object_unref(item->icon);
 
996
            item->icon = NULL;
 
997
            item->is_thumbnail = FALSE;
 
998
            item->thumbnail_loading = FALSE;
 
999
        }
 
1000
    }
 
1001
}
 
1002
 
 
1003
void on_icon_theme_changed(GtkIconTheme* theme, FmFolderModel* model)
 
1004
{
 
1005
    reload_icons(model, RELOAD_ICONS);
 
1006
}
 
1007
 
 
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)
 
1012
{
 
1013
    GSequenceIter *item_it;
 
1014
    gint prefix_len;
 
1015
    gboolean common_suffix_initialized = FALSE;
 
1016
 
 
1017
    g_return_if_fail(common_suffix != NULL);
 
1018
 
 
1019
    if( !model )
 
1020
        return;
 
1021
 
 
1022
    prefix_len = strlen(prefix);
 
1023
    common_suffix[0] = 0;
 
1024
 
 
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) )
 
1028
    {
 
1029
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(item_it);
 
1030
        gboolean predicate_ok = (file_info_predicate == NULL) || file_info_predicate(item->inf);
 
1031
        gint i = 0;
 
1032
        if( predicate_ok && g_str_has_prefix(item->inf->disp_name, prefix) )
 
1033
        {
 
1034
            /* first match -> init */
 
1035
            if( !common_suffix_initialized )
 
1036
            {
 
1037
                strcpy(common_suffix,  item->inf->disp_name + prefix_len);
 
1038
                common_suffix_initialized = TRUE;
 
1039
            }
 
1040
            else
 
1041
            {
 
1042
                while( common_suffix[i] == item->inf->disp_name[prefix_len + i] )
 
1043
                    i++;
 
1044
                common_suffix[i] = 0;
 
1045
            }
 
1046
 
 
1047
        }
 
1048
    }
 
1049
}
 
1050
 
 
1051
gboolean fm_folder_model_find_iter_by_filename(FmFolderModel* model, GtkTreeIter* it, const char* name)
 
1052
{
 
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) )
 
1055
    {
 
1056
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(item_it);
 
1057
        if( g_strcmp0(item->inf->path->name, name) == 0 )
 
1058
        {
 
1059
            it->stamp = model->stamp;
 
1060
            it->user_data  = item_it;
 
1061
            return TRUE;
 
1062
        }
 
1063
    }
 
1064
    return FALSE;
 
1065
}
 
1066
 
 
1067
void on_thumbnail_loaded(FmThumbnailRequest* req, gpointer user_data)
 
1068
{
 
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);
 
1072
    GtkTreeIter it;
 
1073
    guint size = fm_thumbnail_request_get_size(req);
 
1074
    GSequenceIter* seq_it;
 
1075
 
 
1076
    DEBUG("thumbnail loaded for %s, %p, size = %d", fi->path->name, pix, size);
 
1077
 
 
1078
    /* remove the request from list */
 
1079
    model->thumbnail_requests = g_list_remove(model->thumbnail_requests, req);
 
1080
 
 
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))
 
1083
    {
 
1084
        FmFolderItem* item;
 
1085
        seq_it = (GSequenceIter*)it.user_data;
 
1086
        item = (FmFolderItem*)g_sequence_get(seq_it);
 
1087
        if(pix)
 
1088
        {
 
1089
            GtkTreePath* tp = fm_folder_model_get_path(GTK_TREE_MODEL(model), &it);
 
1090
            if(item->icon)
 
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);
 
1096
        }
 
1097
        else
 
1098
        {
 
1099
            item->thumbnail_failed = TRUE;
 
1100
        }
 
1101
        item->thumbnail_loading = FALSE;
 
1102
    }
 
1103
}
 
1104
 
 
1105
void fm_folder_model_set_icon_size(FmFolderModel* model, guint icon_size)
 
1106
{
 
1107
    if(model->icon_size == icon_size)
 
1108
        return;
 
1109
    model->icon_size = icon_size;
 
1110
    reload_icons(model, RELOAD_BOTH);
 
1111
}
 
1112
 
 
1113
guint fm_folder_model_get_icon_size(FmFolderModel* model)
 
1114
{
 
1115
    return model->icon_size;
 
1116
}
 
1117
 
 
1118
void on_show_thumbnail_changed(FmConfig* cfg, gpointer user_data)
 
1119
{
 
1120
    FmFolderModel* model = (FmFolderModel*)user_data;
 
1121
    reload_icons(model, RELOAD_THUMBNAILS);
 
1122
}
 
1123
 
 
1124
static GList* find_in_pending_thumbnail_requests(FmFolderModel* model, FmFileInfo* fi)
 
1125
{
 
1126
    GList* reqs = model->thumbnail_requests, *l;
 
1127
    for(l=reqs;l;l=l->next)
 
1128
    {
 
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))
 
1132
            return l;
 
1133
    }
 
1134
    return NULL;
 
1135
}
 
1136
 
 
1137
static void reload_thumbnail(FmFolderModel* model, GSequenceIter* seq_it, FmFolderItem* item)
 
1138
{
 
1139
    GtkTreeIter it;
 
1140
    GtkTreePath* tp;
 
1141
    if(item->is_thumbnail)
 
1142
    {
 
1143
        g_object_unref(item->icon);
 
1144
        item->icon = NULL;
 
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);
 
1150
    }
 
1151
}
 
1152
 
 
1153
/* FIXME: how about hidden files? */
 
1154
void on_thumbnail_local_changed(FmConfig* cfg, gpointer user_data)
 
1155
{
 
1156
    FmFolderModel* model = (FmFolderModel*)user_data;
 
1157
    FmThumbnailRequest* req;
 
1158
    GList* new_reqs = NULL;
 
1159
    GSequenceIter* seq_it;
 
1160
    FmFileInfo* fi;
 
1161
 
 
1162
    if(cfg->thumbnail_local)
 
1163
    {
 
1164
        GList* l; /* remove non-local files from thumbnail requests */
 
1165
        for(l = model->thumbnail_requests; l; )
 
1166
        {
 
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))
 
1171
            {
 
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. */
 
1175
            }
 
1176
            l = next;
 
1177
        }
 
1178
    }
 
1179
    seq_it = g_sequence_get_begin_iter(model->items);
 
1180
    while( !g_sequence_iter_is_end(seq_it) )
 
1181
    {
 
1182
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(seq_it);
 
1183
        fi = item->inf;
 
1184
        if(cfg->thumbnail_local)
 
1185
        {
 
1186
            /* add all non-local files to thumbnail requests */
 
1187
            if(!fm_path_is_local(fi->path))
 
1188
                reload_thumbnail(model, seq_it, item);
 
1189
        }
 
1190
        else
 
1191
        {
 
1192
            /* add all non-local files to thumbnail requests */
 
1193
            if(!fm_path_is_local(fi->path))
 
1194
            {
 
1195
                req = fm_thumbnail_request(fi, model->icon_size, on_thumbnail_loaded, model);
 
1196
                new_reqs = g_list_append(new_reqs, req);
 
1197
            }
 
1198
        }
 
1199
        seq_it = g_sequence_iter_next(seq_it);
 
1200
    }
 
1201
    if(new_reqs)
 
1202
        model->thumbnail_requests = g_list_concat(model->thumbnail_requests, new_reqs);
 
1203
}
 
1204
 
 
1205
/* FIXME: how about hidden files? */
 
1206
void on_thumbnail_max_changed(FmConfig* cfg, gpointer user_data)
 
1207
{
 
1208
    FmFolderModel* model = (FmFolderModel*)user_data;
 
1209
    FmThumbnailRequest* req;
 
1210
    GList* new_reqs = NULL, *l;
 
1211
    GSequenceIter* seq_it;
 
1212
    FmFileInfo* fi;
 
1213
    guint thumbnail_max_bytes = fm_config->thumbnail_max << 10;
 
1214
 
 
1215
    if(cfg->thumbnail_max)
 
1216
    {
 
1217
         /* remove files which are too big from thumbnail requests */
 
1218
        for(l = model->thumbnail_requests; l; )
 
1219
        {
 
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))
 
1224
            {
 
1225
                fm_thumbnail_request_cancel(req);
 
1226
                model->thumbnail_requests = g_list_delete_link(model->thumbnail_requests, l);
 
1227
            }
 
1228
            l = next;
 
1229
        }
 
1230
    }
 
1231
    seq_it = g_sequence_get_begin_iter(model->items);
 
1232
    while( !g_sequence_iter_is_end(seq_it) )
 
1233
    {
 
1234
        FmFolderItem* item = (FmFolderItem*)g_sequence_get(seq_it);
 
1235
        fi = item->inf;
 
1236
        if(cfg->thumbnail_max)
 
1237
        {
 
1238
            if(thumbnail_max_bytes > model->thumbnail_max)
 
1239
            {
 
1240
                if(fi->size < thumbnail_max_bytes && fi->size > model->thumbnail_max )
 
1241
                {
 
1242
                    if(!item->thumbnail_failed && fm_file_info_can_thumbnail(fi))
 
1243
                    {
 
1244
                        req = fm_thumbnail_request(fi, model->icon_size, on_thumbnail_loaded, model);
 
1245
                        new_reqs = g_list_append(new_reqs, req);
 
1246
                    }
 
1247
                }
 
1248
            }
 
1249
            else
 
1250
            {
 
1251
                if(fi->size > thumbnail_max_bytes)
 
1252
                    reload_thumbnail(model, seq_it, item);
 
1253
            }
 
1254
        }
 
1255
        else /* no limit, all files can be added */
 
1256
        {
 
1257
            /* add all files to thumbnail requests */
 
1258
            if(!item->is_thumbnail && !item->thumbnail_loading && !item->thumbnail_failed && fm_file_info_can_thumbnail(fi))
 
1259
            {
 
1260
                GList* l = find_in_pending_thumbnail_requests(model, fi);
 
1261
                if(!l)
 
1262
                {
 
1263
                    req = fm_thumbnail_request(fi, model->icon_size, on_thumbnail_loaded, model);
 
1264
                    new_reqs = g_list_append(new_reqs, req);
 
1265
                }
 
1266
            }
 
1267
        }
 
1268
        seq_it = g_sequence_iter_next(seq_it);
 
1269
    }
 
1270
    if(new_reqs)
 
1271
        model->thumbnail_requests = g_list_concat(model->thumbnail_requests, new_reqs);
 
1272
    model->thumbnail_max = thumbnail_max_bytes;
 
1273
}