~ubuntu-branches/ubuntu/utopic/pcmanfm/utopic

« back to all changes in this revision

Viewing changes to src/ptk/ptk-file-list.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee (李健秋)
  • Date: 2010-05-23 23:04:11 UTC
  • mfrom: (8.1.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20100523230411-4ei3g4u14pf5rozb
Tags: 0.9.5-2
* Upload to sid. No new additional bug report since last upload into 
  experimental. (Closes:#506243, #509257, #532973, #502225, #535810, 
  #570114, #581033, #518683)
* debian/control:
  Adjusted depends/recommends for people who doesn't want to have gvfs
  on their system. Without gvfs installed, pcmanfm would still works 
  but lose volume management and trashcan support.
  - Drop depends on gamin, shared-mime-info, desktop-file-utils, dbus, 
    xdg-user-dirs
  - Recommends on lxde-icon-theme | gnome-icon-theme, gvfs-backends,
    gvfs-fuse

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
*  C Implementation: ptk-file-list
3
 
*
4
 
* Description:
5
 
*
6
 
*
7
 
* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006
8
 
*
9
 
* Copyright: See COPYING file that comes with this distribution
10
 
*
11
 
*/
12
 
 
13
 
#include "ptk-file-list.h"
14
 
#include <gdk/gdk.h>
15
 
 
16
 
#include "glib-mem.h"
17
 
#include "vfs-file-info.h"
18
 
#include "vfs-thumbnail-loader.h"
19
 
 
20
 
#include <string.h>
21
 
 
22
 
static void ptk_file_list_init ( PtkFileList *list );
23
 
 
24
 
static void ptk_file_list_class_init ( PtkFileListClass *klass );
25
 
 
26
 
static void ptk_file_list_tree_model_init ( GtkTreeModelIface *iface );
27
 
 
28
 
static void ptk_file_list_tree_sortable_init ( GtkTreeSortableIface *iface );
29
 
 
30
 
static void ptk_file_list_drag_source_init ( GtkTreeDragSourceIface *iface );
31
 
 
32
 
static void ptk_file_list_drag_dest_init ( GtkTreeDragDestIface *iface );
33
 
 
34
 
static void ptk_file_list_finalize ( GObject *object );
35
 
 
36
 
static GtkTreeModelFlags ptk_file_list_get_flags ( GtkTreeModel *tree_model );
37
 
 
38
 
static gint ptk_file_list_get_n_columns ( GtkTreeModel *tree_model );
39
 
 
40
 
static GType ptk_file_list_get_column_type ( GtkTreeModel *tree_model,
41
 
                                             gint index );
42
 
 
43
 
static gboolean ptk_file_list_get_iter ( GtkTreeModel *tree_model,
44
 
                                         GtkTreeIter *iter,
45
 
                                         GtkTreePath *path );
46
 
 
47
 
static GtkTreePath *ptk_file_list_get_path ( GtkTreeModel *tree_model,
48
 
                                             GtkTreeIter *iter );
49
 
 
50
 
static void ptk_file_list_get_value ( GtkTreeModel *tree_model,
51
 
                                      GtkTreeIter *iter,
52
 
                                      gint column,
53
 
                                      GValue *value );
54
 
 
55
 
static gboolean ptk_file_list_iter_next ( GtkTreeModel *tree_model,
56
 
                                          GtkTreeIter *iter );
57
 
 
58
 
static gboolean ptk_file_list_iter_children ( GtkTreeModel *tree_model,
59
 
                                              GtkTreeIter *iter,
60
 
                                              GtkTreeIter *parent );
61
 
 
62
 
static gboolean ptk_file_list_iter_has_child ( GtkTreeModel *tree_model,
63
 
                                               GtkTreeIter *iter );
64
 
 
65
 
static gint ptk_file_list_iter_n_children ( GtkTreeModel *tree_model,
66
 
                                            GtkTreeIter *iter );
67
 
 
68
 
static gboolean ptk_file_list_iter_nth_child ( GtkTreeModel *tree_model,
69
 
                                               GtkTreeIter *iter,
70
 
                                               GtkTreeIter *parent,
71
 
                                               gint n );
72
 
 
73
 
static gboolean ptk_file_list_iter_parent ( GtkTreeModel *tree_model,
74
 
                                            GtkTreeIter *iter,
75
 
                                            GtkTreeIter *child );
76
 
 
77
 
static gboolean ptk_file_list_get_sort_column_id( GtkTreeSortable* sortable,
78
 
                                                  gint* sort_column_id,
79
 
                                                  GtkSortType* order );
80
 
 
81
 
static void ptk_file_list_set_sort_column_id( GtkTreeSortable* sortable,
82
 
                                              gint sort_column_id,
83
 
                                              GtkSortType order );
84
 
 
85
 
static void ptk_file_list_set_sort_func( GtkTreeSortable *sortable,
86
 
                                         gint sort_column_id,
87
 
                                         GtkTreeIterCompareFunc sort_func,
88
 
                                         gpointer user_data,
89
 
                                         GtkDestroyNotify destroy );
90
 
 
91
 
static void ptk_file_list_set_default_sort_func( GtkTreeSortable *sortable,
92
 
                                                 GtkTreeIterCompareFunc sort_func,
93
 
                                                 gpointer user_data,
94
 
                                                 GtkDestroyNotify destroy );
95
 
 
96
 
static void ptk_file_list_sort ( PtkFileList* list );
97
 
 
98
 
/* signal handlers */
99
 
 
100
 
static void on_thumbnail_loaded( VFSDir* dir, VFSFileInfo* file, PtkFileList* list );
101
 
 
102
 
/*
103
 
 * already declared in ptk-file-list.h
104
 
void ptk_file_list_file_created( VFSDir* dir, VFSFileInfo* file,
105
 
                                        PtkFileList* list );
106
 
 
107
 
void ptk_file_list_file_deleted( VFSDir* dir, VFSFileInfo* file,
108
 
                                        PtkFileList* list );
109
 
 
110
 
void ptk_file_list_file_changed( VFSDir* dir, VFSFileInfo* file,
111
 
                                        PtkFileList* list );
112
 
*/
113
 
 
114
 
static GObjectClass* parent_class = NULL;
115
 
 
116
 
static GType column_types[ N_FILE_LIST_COLS ];
117
 
 
118
 
GType ptk_file_list_get_type ( void )
119
 
{
120
 
    static GType type = 0;
121
 
    if ( G_UNLIKELY( !type ) )
122
 
    {
123
 
        static const GTypeInfo type_info =
124
 
            {
125
 
                sizeof ( PtkFileListClass ),
126
 
                NULL,                                           /* base_init */
127
 
                NULL,                                           /* base_finalize */
128
 
                ( GClassInitFunc ) ptk_file_list_class_init,
129
 
                NULL,                                           /* class finalize */
130
 
                NULL,                                           /* class_data */
131
 
                sizeof ( PtkFileList ),
132
 
                0,                                             /* n_preallocs */
133
 
                ( GInstanceInitFunc ) ptk_file_list_init
134
 
            };
135
 
 
136
 
        static const GInterfaceInfo tree_model_info =
137
 
            {
138
 
                ( GInterfaceInitFunc ) ptk_file_list_tree_model_init,
139
 
                NULL,
140
 
                NULL
141
 
            };
142
 
 
143
 
        static const GInterfaceInfo tree_sortable_info =
144
 
            {
145
 
                ( GInterfaceInitFunc ) ptk_file_list_tree_sortable_init,
146
 
                NULL,
147
 
                NULL
148
 
            };
149
 
 
150
 
        static const GInterfaceInfo drag_src_info =
151
 
            {
152
 
                ( GInterfaceInitFunc ) ptk_file_list_drag_source_init,
153
 
                NULL,
154
 
                NULL
155
 
            };
156
 
 
157
 
        static const GInterfaceInfo drag_dest_info =
158
 
            {
159
 
                ( GInterfaceInitFunc ) ptk_file_list_drag_dest_init,
160
 
                NULL,
161
 
                NULL
162
 
            };
163
 
 
164
 
        type = g_type_register_static ( G_TYPE_OBJECT, "PtkFileList",
165
 
                                        &type_info, ( GTypeFlags ) 0 );
166
 
        g_type_add_interface_static ( type, GTK_TYPE_TREE_MODEL, &tree_model_info );
167
 
        g_type_add_interface_static ( type, GTK_TYPE_TREE_SORTABLE, &tree_sortable_info );
168
 
        g_type_add_interface_static ( type, GTK_TYPE_TREE_DRAG_SOURCE, &drag_src_info );
169
 
        g_type_add_interface_static ( type, GTK_TYPE_TREE_DRAG_DEST, &drag_dest_info );
170
 
    }
171
 
    return type;
172
 
}
173
 
 
174
 
void ptk_file_list_init ( PtkFileList *list )
175
 
{
176
 
    list->n_files = 0;
177
 
    list->files = NULL;
178
 
    list->sort_order = -1;
179
 
    list->sort_col = -1;
180
 
    /* Random int to check whether an iter belongs to our model */
181
 
    list->stamp = g_random_int();
182
 
}
183
 
 
184
 
void ptk_file_list_class_init ( PtkFileListClass *klass )
185
 
{
186
 
    GObjectClass * object_class;
187
 
 
188
 
    parent_class = ( GObjectClass* ) g_type_class_peek_parent ( klass );
189
 
    object_class = ( GObjectClass* ) klass;
190
 
 
191
 
    object_class->finalize = ptk_file_list_finalize;
192
 
}
193
 
 
194
 
void ptk_file_list_tree_model_init ( GtkTreeModelIface *iface )
195
 
{
196
 
    iface->get_flags = ptk_file_list_get_flags;
197
 
    iface->get_n_columns = ptk_file_list_get_n_columns;
198
 
    iface->get_column_type = ptk_file_list_get_column_type;
199
 
    iface->get_iter = ptk_file_list_get_iter;
200
 
    iface->get_path = ptk_file_list_get_path;
201
 
    iface->get_value = ptk_file_list_get_value;
202
 
    iface->iter_next = ptk_file_list_iter_next;
203
 
    iface->iter_children = ptk_file_list_iter_children;
204
 
    iface->iter_has_child = ptk_file_list_iter_has_child;
205
 
    iface->iter_n_children = ptk_file_list_iter_n_children;
206
 
    iface->iter_nth_child = ptk_file_list_iter_nth_child;
207
 
    iface->iter_parent = ptk_file_list_iter_parent;
208
 
 
209
 
    column_types [ COL_FILE_BIG_ICON ] = GDK_TYPE_PIXBUF;
210
 
    column_types [ COL_FILE_SMALL_ICON ] = GDK_TYPE_PIXBUF;
211
 
    column_types [ COL_FILE_NAME ] = G_TYPE_STRING;
212
 
    column_types [ COL_FILE_DESC ] = G_TYPE_STRING;
213
 
    column_types [ COL_FILE_SIZE ] = G_TYPE_STRING;
214
 
    column_types [ COL_FILE_DESC ] = G_TYPE_STRING;
215
 
    column_types [ COL_FILE_PERM ] = G_TYPE_STRING;
216
 
    column_types [ COL_FILE_OWNER ] = G_TYPE_STRING;
217
 
    column_types [ COL_FILE_MTIME ] = G_TYPE_STRING;
218
 
    column_types [ COL_FILE_INFO ] = G_TYPE_POINTER;
219
 
}
220
 
 
221
 
void ptk_file_list_tree_sortable_init ( GtkTreeSortableIface *iface )
222
 
{
223
 
    /* iface->sort_column_changed = ptk_file_list_sort_column_changed; */
224
 
    iface->get_sort_column_id = ptk_file_list_get_sort_column_id;
225
 
    iface->set_sort_column_id = ptk_file_list_set_sort_column_id;
226
 
    iface->set_sort_func = ptk_file_list_set_sort_func;
227
 
    iface->set_default_sort_func = ptk_file_list_set_default_sort_func;
228
 
    iface->has_default_sort_func = (gboolean(*)(GtkTreeSortable *))gtk_false;
229
 
}
230
 
 
231
 
void ptk_file_list_drag_source_init ( GtkTreeDragSourceIface *iface )
232
 
{
233
 
    /* FIXME: Unused. Will this cause any problem? */
234
 
}
235
 
 
236
 
void ptk_file_list_drag_dest_init ( GtkTreeDragDestIface *iface )
237
 
{
238
 
    /* FIXME: Unused. Will this cause any problem? */
239
 
}
240
 
 
241
 
void ptk_file_list_finalize ( GObject *object )
242
 
{
243
 
    PtkFileList *list = ( PtkFileList* ) object;
244
 
 
245
 
    ptk_file_list_set_dir( list, NULL );
246
 
    /* must chain up - finalize parent */
247
 
    ( * parent_class->finalize ) ( object );
248
 
}
249
 
 
250
 
PtkFileList *ptk_file_list_new ( VFSDir* dir, gboolean show_hidden )
251
 
{
252
 
    PtkFileList * list;
253
 
    list = ( PtkFileList* ) g_object_new ( PTK_TYPE_FILE_LIST, NULL );
254
 
    list->show_hidden = show_hidden;
255
 
    ptk_file_list_set_dir( list, dir );
256
 
    return list;
257
 
}
258
 
 
259
 
static void _ptk_file_list_file_changed( VFSDir* dir, VFSFileInfo* file,
260
 
                                        PtkFileList* list )
261
 
{
262
 
    ptk_file_list_file_changed( dir, file, list );
263
 
 
264
 
    /* check if reloading of thumbnail is needed. */
265
 
    if( vfs_file_info_is_image( file )
266
 
        && vfs_file_info_get_size( file ) < list->max_thumbnail )
267
 
    {
268
 
        if( ! vfs_file_info_is_thumbnail_loaded( file, list->big_thumbnail ) )
269
 
            vfs_thumbnail_loader_request( list->dir, file, list->big_thumbnail );
270
 
    }
271
 
}
272
 
 
273
 
static void _ptk_file_list_file_created( VFSDir* dir, VFSFileInfo* file,
274
 
                                        PtkFileList* list )
275
 
{
276
 
    ptk_file_list_file_created( dir, file, list );
277
 
 
278
 
    /* check if reloading of thumbnail is needed. */
279
 
    if( vfs_file_info_is_image( file )
280
 
        && vfs_file_info_get_size( file ) < list->max_thumbnail )
281
 
    {
282
 
        if( ! vfs_file_info_is_thumbnail_loaded( file, list->big_thumbnail ) )
283
 
            vfs_thumbnail_loader_request( list->dir, file, list->big_thumbnail );
284
 
    }
285
 
}
286
 
 
287
 
void ptk_file_list_set_dir( PtkFileList* list, VFSDir* dir )
288
 
{
289
 
    GList* l;
290
 
 
291
 
    if( list->dir == dir )
292
 
        return;
293
 
 
294
 
    if ( list->dir )
295
 
    {
296
 
        if( list->max_thumbnail > 0 )
297
 
        {
298
 
            /* cancel all possible pending requests */
299
 
            vfs_thumbnail_loader_cancel_all_requests( list->dir, list->big_thumbnail );
300
 
        }
301
 
        g_list_foreach( list->files, (GFunc)vfs_file_info_unref, NULL );
302
 
        g_list_free( list->files );
303
 
        g_signal_handlers_disconnect_by_func( list->dir,
304
 
                                              _ptk_file_list_file_created, list );
305
 
        g_signal_handlers_disconnect_by_func( list->dir,
306
 
                                              ptk_file_list_file_deleted, list );
307
 
        g_signal_handlers_disconnect_by_func( list->dir,
308
 
                                              _ptk_file_list_file_changed, list );
309
 
        g_signal_handlers_disconnect_by_func( list->dir,
310
 
                                              on_thumbnail_loaded, list );
311
 
        g_object_unref( list->dir );
312
 
    }
313
 
 
314
 
    list->dir = dir;
315
 
    list->files = NULL;
316
 
    list->n_files = 0;
317
 
    if( ! dir )
318
 
        return;
319
 
 
320
 
    g_object_ref( list->dir );
321
 
 
322
 
    g_signal_connect( list->dir, "file-created",
323
 
                      G_CALLBACK(_ptk_file_list_file_created),
324
 
                      list );
325
 
    g_signal_connect( list->dir, "file-deleted",
326
 
                      G_CALLBACK(ptk_file_list_file_deleted),
327
 
                      list );
328
 
    g_signal_connect( list->dir, "file-changed",
329
 
                      G_CALLBACK(_ptk_file_list_file_changed),
330
 
                      list );
331
 
 
332
 
    if( dir && dir->file_list )
333
 
    {
334
 
        for( l = dir->file_list; l; l = l->next )
335
 
        {
336
 
            if( list->show_hidden ||
337
 
                    ((VFSFileInfo*)l->data)->disp_name[0] != '.' )
338
 
            {
339
 
                list->files = g_list_prepend( list->files, vfs_file_info_ref( (VFSFileInfo*)l->data) );
340
 
                ++list->n_files;
341
 
            }
342
 
        }
343
 
    }
344
 
}
345
 
 
346
 
GtkTreeModelFlags ptk_file_list_get_flags ( GtkTreeModel *tree_model )
347
 
{
348
 
    g_return_val_if_fail ( PTK_IS_FILE_LIST( tree_model ), ( GtkTreeModelFlags ) 0 );
349
 
    return ( GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST );
350
 
}
351
 
 
352
 
gint ptk_file_list_get_n_columns ( GtkTreeModel *tree_model )
353
 
{
354
 
    return N_FILE_LIST_COLS;
355
 
}
356
 
 
357
 
GType ptk_file_list_get_column_type ( GtkTreeModel *tree_model,
358
 
                                      gint index )
359
 
{
360
 
    g_return_val_if_fail ( PTK_IS_FILE_LIST( tree_model ), G_TYPE_INVALID );
361
 
    g_return_val_if_fail ( index < G_N_ELEMENTS( column_types ) && index >= 0, G_TYPE_INVALID );
362
 
    return column_types[ index ];
363
 
}
364
 
 
365
 
gboolean ptk_file_list_get_iter ( GtkTreeModel *tree_model,
366
 
                                  GtkTreeIter *iter,
367
 
                                  GtkTreePath *path )
368
 
{
369
 
    PtkFileList *list;
370
 
    gint *indices, n, depth;
371
 
    GList* l;
372
 
 
373
 
    g_assert(PTK_IS_FILE_LIST(tree_model));
374
 
    g_assert(path!=NULL);
375
 
 
376
 
    list = PTK_FILE_LIST(tree_model);
377
 
 
378
 
    indices = gtk_tree_path_get_indices(path);
379
 
    depth   = gtk_tree_path_get_depth(path);
380
 
 
381
 
    /* we do not allow children */
382
 
    g_assert(depth == 1); /* depth 1 = top level; a list only has top level nodes and no children */
383
 
 
384
 
    n = indices[0]; /* the n-th top level row */
385
 
 
386
 
    if ( n >= list->n_files || n < 0 )
387
 
        return FALSE;
388
 
 
389
 
    l = g_list_nth( list->files, n );
390
 
 
391
 
    g_assert(l != NULL);
392
 
 
393
 
    /* We simply store a pointer in the iter */
394
 
    iter->stamp = list->stamp;
395
 
    iter->user_data  = l;
396
 
    iter->user_data2 = l->data;
397
 
    iter->user_data3 = NULL;   /* unused */
398
 
 
399
 
    return TRUE;
400
 
}
401
 
 
402
 
GtkTreePath *ptk_file_list_get_path ( GtkTreeModel *tree_model,
403
 
                                      GtkTreeIter *iter )
404
 
{
405
 
    GtkTreePath* path;
406
 
    GList* l;
407
 
    PtkFileList* list = PTK_FILE_LIST(tree_model);
408
 
 
409
 
    g_return_val_if_fail (list, NULL);
410
 
    g_return_val_if_fail (iter->stamp == list->stamp, NULL);
411
 
    g_return_val_if_fail (iter != NULL, NULL);
412
 
    g_return_val_if_fail (iter->user_data != NULL, NULL);
413
 
 
414
 
    l = (GList*) iter->user_data;
415
 
 
416
 
    path = gtk_tree_path_new();
417
 
    gtk_tree_path_append_index(path, g_list_index(list->files, l->data) );
418
 
    return path;
419
 
}
420
 
 
421
 
void ptk_file_list_get_value ( GtkTreeModel *tree_model,
422
 
                               GtkTreeIter *iter,
423
 
                               gint column,
424
 
                               GValue *value )
425
 
{
426
 
    GList* l;
427
 
    PtkFileList* list = PTK_FILE_LIST(tree_model);
428
 
    VFSFileInfo* info;
429
 
    GdkPixbuf* icon;
430
 
 
431
 
    g_return_if_fail (PTK_IS_FILE_LIST (tree_model));
432
 
    g_return_if_fail (iter != NULL);
433
 
    g_return_if_fail (column < G_N_ELEMENTS(column_types) );
434
 
 
435
 
    g_value_init (value, column_types[column] );
436
 
 
437
 
    l = (GList*) iter->user_data;
438
 
    g_return_if_fail ( l != NULL );
439
 
 
440
 
    info = (VFSFileInfo*)iter->user_data2;
441
 
 
442
 
    switch(column)
443
 
    {
444
 
    case COL_FILE_BIG_ICON:
445
 
        icon = NULL;
446
 
        /* special file can use special icons saved as thumbnails*/
447
 
        if( list->max_thumbnail > vfs_file_info_get_size( info )
448
 
            && info->flags == VFS_FILE_INFO_NONE )
449
 
            icon = vfs_file_info_get_big_thumbnail( info );
450
 
 
451
 
        if( ! icon )
452
 
            icon = vfs_file_info_get_big_icon( info );
453
 
        if( icon )
454
 
        {
455
 
            g_value_set_object( value, icon );
456
 
            gdk_pixbuf_unref( icon );
457
 
        }
458
 
        break;
459
 
    case COL_FILE_SMALL_ICON:
460
 
        icon = NULL;
461
 
        /* special file can use special icons saved as thumbnails*/
462
 
        if( list->max_thumbnail > vfs_file_info_get_size( info ) )
463
 
            icon = vfs_file_info_get_small_thumbnail( info );
464
 
        if( !icon )
465
 
            icon = vfs_file_info_get_small_icon( info );
466
 
        if( icon )
467
 
        {
468
 
            g_value_set_object( value, icon );
469
 
            gdk_pixbuf_unref( icon );
470
 
        }
471
 
        break;
472
 
    case COL_FILE_NAME:
473
 
        g_value_set_string( value, vfs_file_info_get_disp_name(info) );
474
 
        break;
475
 
    case COL_FILE_SIZE:
476
 
        g_value_set_string( value, vfs_file_info_get_disp_size(info) );
477
 
        break;
478
 
    case COL_FILE_DESC:
479
 
        g_value_set_string( value, vfs_file_info_get_mime_type_desc( info ) );
480
 
        break;
481
 
    case COL_FILE_PERM:
482
 
        g_value_set_string( value, vfs_file_info_get_disp_perm(info) );
483
 
        break;
484
 
    case COL_FILE_OWNER:
485
 
        g_value_set_string( value, vfs_file_info_get_disp_owner(info) );
486
 
        break;
487
 
    case COL_FILE_MTIME:
488
 
        g_value_set_string( value, vfs_file_info_get_disp_mtime(info) );
489
 
        break;
490
 
    case COL_FILE_INFO:
491
 
        g_value_set_pointer( value, vfs_file_info_ref( info ) );
492
 
        break;
493
 
    }
494
 
}
495
 
 
496
 
gboolean ptk_file_list_iter_next ( GtkTreeModel *tree_model,
497
 
                                   GtkTreeIter *iter )
498
 
{
499
 
    GList* l;
500
 
    PtkFileList* list;
501
 
 
502
 
    g_return_val_if_fail (PTK_IS_FILE_LIST (tree_model), FALSE);
503
 
 
504
 
    if (iter == NULL || iter->user_data == NULL)
505
 
        return FALSE;
506
 
 
507
 
    list = PTK_FILE_LIST(tree_model);
508
 
    l = (GList *) iter->user_data;
509
 
 
510
 
    /* Is this the last l in the list? */
511
 
    if ( ! l->next )
512
 
        return FALSE;
513
 
 
514
 
    iter->stamp = list->stamp;
515
 
    iter->user_data = l->next;
516
 
    iter->user_data2 = l->next->data;
517
 
 
518
 
    return TRUE;
519
 
}
520
 
 
521
 
gboolean ptk_file_list_iter_children ( GtkTreeModel *tree_model,
522
 
                                       GtkTreeIter *iter,
523
 
                                       GtkTreeIter *parent )
524
 
{
525
 
    PtkFileList* list;
526
 
    g_return_val_if_fail ( parent == NULL || parent->user_data != NULL, FALSE );
527
 
 
528
 
    /* this is a list, nodes have no children */
529
 
    if ( parent )
530
 
        return FALSE;
531
 
 
532
 
    /* parent == NULL is a special case; we need to return the first top-level row */
533
 
    g_return_val_if_fail ( PTK_IS_FILE_LIST ( tree_model ), FALSE );
534
 
    list = PTK_FILE_LIST( tree_model );
535
 
 
536
 
    /* No rows => no first row */
537
 
    if ( list->dir->n_files == 0 )
538
 
        return FALSE;
539
 
 
540
 
    /* Set iter to first item in list */
541
 
    iter->stamp = list->stamp;
542
 
    iter->user_data = list->files;
543
 
    iter->user_data2 = list->files->data;
544
 
    return TRUE;
545
 
}
546
 
 
547
 
gboolean ptk_file_list_iter_has_child ( GtkTreeModel *tree_model,
548
 
                                        GtkTreeIter *iter )
549
 
{
550
 
    return FALSE;
551
 
}
552
 
 
553
 
gint ptk_file_list_iter_n_children ( GtkTreeModel *tree_model,
554
 
                                     GtkTreeIter *iter )
555
 
{
556
 
    PtkFileList* list;
557
 
    g_return_val_if_fail ( PTK_IS_FILE_LIST ( tree_model ), -1 );
558
 
    g_return_val_if_fail ( iter == NULL || iter->user_data != NULL, FALSE );
559
 
    list = PTK_FILE_LIST( tree_model );
560
 
    /* special case: if iter == NULL, return number of top-level rows */
561
 
    if ( !iter )
562
 
        return list->n_files;
563
 
    return 0; /* otherwise, this is easy again for a list */
564
 
}
565
 
 
566
 
gboolean ptk_file_list_iter_nth_child ( GtkTreeModel *tree_model,
567
 
                                        GtkTreeIter *iter,
568
 
                                        GtkTreeIter *parent,
569
 
                                        gint n )
570
 
{
571
 
    GList* l;
572
 
    PtkFileList* list;
573
 
 
574
 
    g_return_val_if_fail (PTK_IS_FILE_LIST (tree_model), FALSE);
575
 
    list = PTK_FILE_LIST(tree_model);
576
 
 
577
 
    /* a list has only top-level rows */
578
 
    if(parent)
579
 
        return FALSE;
580
 
 
581
 
    /* special case: if parent == NULL, set iter to n-th top-level row */
582
 
    if( n >= list->n_files || n < 0 )
583
 
        return FALSE;
584
 
 
585
 
    l = g_list_nth( list->files, n );
586
 
    g_assert( l != NULL );
587
 
 
588
 
    iter->stamp = list->stamp;
589
 
    iter->user_data = l;
590
 
    iter->user_data2 = l->data;
591
 
 
592
 
    return TRUE;
593
 
}
594
 
 
595
 
gboolean ptk_file_list_iter_parent ( GtkTreeModel *tree_model,
596
 
                                     GtkTreeIter *iter,
597
 
                                     GtkTreeIter *child )
598
 
{
599
 
    return FALSE;
600
 
}
601
 
 
602
 
gboolean ptk_file_list_get_sort_column_id( GtkTreeSortable* sortable,
603
 
                                           gint* sort_column_id,
604
 
                                           GtkSortType* order )
605
 
{
606
 
    PtkFileList* list = (PtkFileList*)sortable;
607
 
    if( sort_column_id )
608
 
        *sort_column_id = list->sort_col;
609
 
    if( order )
610
 
        *order = list->sort_order;
611
 
    return TRUE;
612
 
}
613
 
 
614
 
void ptk_file_list_set_sort_column_id( GtkTreeSortable* sortable,
615
 
                                       gint sort_column_id,
616
 
                                       GtkSortType order )
617
 
{
618
 
    PtkFileList* list = (PtkFileList*)sortable;
619
 
    if( list->sort_col == sort_column_id && list->sort_order == order )
620
 
        return;
621
 
    list->sort_col = sort_column_id;
622
 
    list->sort_order = order;
623
 
    gtk_tree_sortable_sort_column_changed (sortable);
624
 
    ptk_file_list_sort (list);
625
 
}
626
 
 
627
 
void ptk_file_list_set_sort_func( GtkTreeSortable *sortable,
628
 
                                  gint sort_column_id,
629
 
                                  GtkTreeIterCompareFunc sort_func,
630
 
                                  gpointer user_data,
631
 
                                  GtkDestroyNotify destroy )
632
 
{
633
 
    g_warning( "ptk_file_list_set_sort_func: Not supported\n" );
634
 
}
635
 
 
636
 
void ptk_file_list_set_default_sort_func( GtkTreeSortable *sortable,
637
 
                                          GtkTreeIterCompareFunc sort_func,
638
 
                                          gpointer user_data,
639
 
                                          GtkDestroyNotify destroy )
640
 
{
641
 
    g_warning( "ptk_file_list_set_default_sort_func: Not supported\n" );
642
 
}
643
 
 
644
 
static gint ptk_file_list_compare( gconstpointer a,
645
 
                                   gconstpointer b,
646
 
                                   gpointer user_data)
647
 
{
648
 
    VFSFileInfo* file1 = (VFSFileInfo*)a;
649
 
    VFSFileInfo* file2 = (VFSFileInfo*)b;
650
 
    PtkFileList* list = (PtkFileList*)user_data;
651
 
    int ret;
652
 
    /* put folders before files */
653
 
    ret = vfs_file_info_is_dir(file1) - vfs_file_info_is_dir(file2);
654
 
    if( ret )
655
 
        return -ret;
656
 
 
657
 
    /* FIXME: strings should not be treated as ASCII when sorted  */
658
 
    switch( list->sort_col )
659
 
    {
660
 
    case COL_FILE_NAME:
661
 
        ret = g_ascii_strcasecmp( vfs_file_info_get_disp_name(file1),
662
 
                                  vfs_file_info_get_disp_name(file2) );
663
 
        break;
664
 
    case COL_FILE_SIZE:
665
 
        ret = file1->size - file2->size;
666
 
        break;
667
 
    case COL_FILE_DESC:
668
 
        ret = g_ascii_strcasecmp( vfs_file_info_get_mime_type_desc(file1),
669
 
                                  vfs_file_info_get_mime_type_desc(file2) );
670
 
        break;
671
 
    case COL_FILE_PERM:
672
 
        ret = g_ascii_strcasecmp( vfs_file_info_get_disp_perm(file1),
673
 
                                  vfs_file_info_get_disp_perm(file2) );
674
 
        break;
675
 
    case COL_FILE_OWNER:
676
 
        ret = g_ascii_strcasecmp( vfs_file_info_get_disp_owner(file1),
677
 
                                  vfs_file_info_get_disp_owner(file2) );
678
 
        break;
679
 
    case COL_FILE_MTIME:
680
 
        ret = file1->mtime - file2->mtime;
681
 
        break;
682
 
    }
683
 
    return list->sort_order == GTK_SORT_ASCENDING ? ret : -ret;
684
 
}
685
 
 
686
 
void ptk_file_list_sort ( PtkFileList* list )
687
 
{
688
 
    GHashTable* old_order;
689
 
    gint *new_order;
690
 
    GtkTreePath *path;
691
 
    GList* l;
692
 
    int i;
693
 
 
694
 
    if( list->n_files <=1 )
695
 
        return;
696
 
 
697
 
    old_order = g_hash_table_new( g_direct_hash, g_direct_equal );
698
 
    /* save old order */
699
 
    for( i = 0, l = list->files; l; l = l->next, ++i )
700
 
        g_hash_table_insert( old_order, l, GINT_TO_POINTER(i) );
701
 
 
702
 
    /* sort the list */
703
 
    list->files = g_list_sort_with_data( list->files,
704
 
                                         ptk_file_list_compare, list );
705
 
 
706
 
    /* save new order */
707
 
    new_order = g_new( int, list->n_files );
708
 
    for( i = 0, l = list->files; l; l = l->next, ++i )
709
 
        new_order[i] = (guint)g_hash_table_lookup( old_order, l );
710
 
    g_hash_table_destroy( old_order );
711
 
    path = gtk_tree_path_new ();
712
 
    gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list),
713
 
                                   path, NULL, new_order);
714
 
    gtk_tree_path_free (path);
715
 
    g_free( new_order );
716
 
}
717
 
 
718
 
gboolean ptk_file_list_find_iter(  PtkFileList* list, GtkTreeIter* it, VFSFileInfo* fi )
719
 
{
720
 
    GList* l;
721
 
    for( l = list->files; l; l = l->next )
722
 
    {
723
 
        VFSFileInfo* fi2 = (VFSFileInfo*)l->data;
724
 
        if( G_UNLIKELY( fi2 == fi
725
 
            || 0 == strcmp( vfs_file_info_get_name(fi), vfs_file_info_get_name(fi2) ) ) )
726
 
        {
727
 
            it->stamp = list->stamp;
728
 
            it->user_data = l;
729
 
            it->user_data2 = fi2;
730
 
            return TRUE;
731
 
        }
732
 
    }
733
 
    return FALSE;
734
 
}
735
 
 
736
 
void ptk_file_list_file_created( VFSDir* dir,
737
 
                                 VFSFileInfo* file,
738
 
                                 PtkFileList* list )
739
 
{
740
 
    GList* l;
741
 
    GtkTreeIter it;
742
 
    GtkTreePath* path;
743
 
    VFSFileInfo* file2;
744
 
 
745
 
    if( ! list->show_hidden && vfs_file_info_get_name(file)[0] == '.' )
746
 
        return;
747
 
 
748
 
    for( l = list->files; l; l = l->next )
749
 
    {
750
 
        file2 = (VFSFileInfo*)l->data;
751
 
        if( G_UNLIKELY( file == file2 || ptk_file_list_compare( file2, file, list ) == 0 ) )
752
 
        {
753
 
            /* The file is already in the list */
754
 
            return;
755
 
        }
756
 
        if( ptk_file_list_compare( file2, file, list ) > 0 )
757
 
        {
758
 
            break;
759
 
        }
760
 
    }
761
 
 
762
 
    list->files = g_list_insert_before( list->files, l, vfs_file_info_ref( file ) );
763
 
    ++list->n_files;
764
 
 
765
 
    if( l )
766
 
        l = l->prev;
767
 
    else
768
 
        l = g_list_last( list->files );
769
 
 
770
 
    it.stamp = list->stamp;
771
 
    it.user_data = l;
772
 
    it.user_data2 = file;
773
 
 
774
 
    path = gtk_tree_path_new_from_indices( g_list_index(list->files, l->data), -1 );
775
 
 
776
 
    gtk_tree_model_row_inserted( GTK_TREE_MODEL(list), path, &it );
777
 
 
778
 
    gtk_tree_path_free( path );
779
 
}
780
 
 
781
 
void ptk_file_list_file_deleted( VFSDir* dir,
782
 
                                 VFSFileInfo* file,
783
 
                                 PtkFileList* list )
784
 
{
785
 
    GList* l;
786
 
    GtkTreePath* path;
787
 
 
788
 
    /* If there is no file info, that means the dir itself was deleted. */
789
 
    if( G_UNLIKELY( ! file ) )
790
 
    {
791
 
        /* Clear the whole list */
792
 
        path = gtk_tree_path_new_from_indices(0, -1);
793
 
        for( l = list->files; l; l = list->files )
794
 
        {
795
 
            gtk_tree_model_row_deleted( GTK_TREE_MODEL(list), path );
796
 
            file = (VFSFileInfo*)l->data;
797
 
            list->files = g_list_delete_link( list->files, l );
798
 
            vfs_file_info_unref( file );
799
 
            --list->n_files;
800
 
        }
801
 
        gtk_tree_path_free( path );
802
 
        return;
803
 
    }
804
 
 
805
 
    if( ! list->show_hidden && vfs_file_info_get_name(file)[0] == '.' )
806
 
        return;
807
 
 
808
 
    l = g_list_find( list->files, file );
809
 
    if( ! l )
810
 
        return;
811
 
 
812
 
    path = gtk_tree_path_new_from_indices( g_list_index(list->files, l->data), -1 );
813
 
 
814
 
    gtk_tree_model_row_deleted( GTK_TREE_MODEL(list), path );
815
 
 
816
 
    gtk_tree_path_free( path );
817
 
 
818
 
    list->files = g_list_delete_link( list->files, l );
819
 
    vfs_file_info_unref( file );
820
 
    --list->n_files;
821
 
}
822
 
 
823
 
void ptk_file_list_file_changed( VFSDir* dir,
824
 
                                 VFSFileInfo* file,
825
 
                                 PtkFileList* list )
826
 
{
827
 
    GList* l;
828
 
    GtkTreeIter it;
829
 
    GtkTreePath* path;
830
 
 
831
 
    if( ! list->show_hidden && vfs_file_info_get_name(file)[0] == '.' )
832
 
        return;
833
 
    l = g_list_find( list->files, file );
834
 
 
835
 
    if( ! l )
836
 
        return;
837
 
 
838
 
    it.stamp = list->stamp;
839
 
    it.user_data = l;
840
 
    it.user_data2 = l->data;
841
 
 
842
 
    path = gtk_tree_path_new_from_indices( g_list_index(list->files, l->data), -1 );
843
 
 
844
 
    gtk_tree_model_row_changed( GTK_TREE_MODEL(list), path, &it );
845
 
 
846
 
    gtk_tree_path_free( path );
847
 
}
848
 
 
849
 
void on_thumbnail_loaded( VFSDir* dir, VFSFileInfo* file, PtkFileList* list )
850
 
{
851
 
    /* g_debug( "LOADED: %s", file->name ); */
852
 
    ptk_file_list_file_changed( dir, file, list );
853
 
}
854
 
 
855
 
void ptk_file_list_show_thumbnails( PtkFileList* list, gboolean is_big,
856
 
                                    int max_file_size )
857
 
{
858
 
    GList* l;
859
 
    VFSFileInfo* file;
860
 
    int old_max_thumbnail;
861
 
 
862
 
    old_max_thumbnail = list->max_thumbnail;
863
 
    list->max_thumbnail = max_file_size;
864
 
    list->big_thumbnail = is_big;
865
 
    /* FIXME: This is buggy!!! Further testing might be needed.
866
 
    */
867
 
    if( 0 == max_file_size )
868
 
    {
869
 
        if( old_max_thumbnail > 0 ) /* cancel thumbnails */
870
 
        {
871
 
            vfs_thumbnail_loader_cancel_all_requests( list->dir, list->big_thumbnail );
872
 
            g_signal_handlers_disconnect_by_func( list->dir, on_thumbnail_loaded, list );
873
 
 
874
 
            for( l = list->files; l; l = l->next )
875
 
            {
876
 
                file = (VFSFileInfo*)l->data;
877
 
                if( vfs_file_info_is_image( file )
878
 
                    && vfs_file_info_is_thumbnail_loaded( file, is_big ) )
879
 
                {
880
 
                    /* update the model */
881
 
                    ptk_file_list_file_changed( list->dir, file, list );
882
 
                }
883
 
            }
884
 
        }
885
 
        return;
886
 
    }
887
 
 
888
 
    g_signal_connect( list->dir, "thumbnail-loaded",
889
 
                                    G_CALLBACK(on_thumbnail_loaded), list );
890
 
 
891
 
    for( l = list->files; l; l = l->next )
892
 
    {
893
 
        file = (VFSFileInfo*)l->data;
894
 
        if( vfs_file_info_is_image( file )
895
 
            && vfs_file_info_get_size( file ) < list->max_thumbnail )
896
 
        {
897
 
            if( vfs_file_info_is_thumbnail_loaded( file, is_big ) )
898
 
                ptk_file_list_file_changed( list->dir, file, list );
899
 
            else
900
 
            {
901
 
                vfs_thumbnail_loader_request( list->dir, file, is_big );
902
 
                /* g_debug( "REQUEST: %s", file->name ); */
903
 
            }
904
 
        }
905
 
    }
906
 
}