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

« back to all changes in this revision

Viewing changes to src/vfs/vfs-file-info.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee
  • Date: 2006-10-16 16:47:04 UTC
  • Revision ID: james.westby@ubuntu.com-20061016164704-w8zobnrvdxc7rvm5
Tags: upstream-0.3.2.1
ImportĀ upstreamĀ versionĀ 0.3.2.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
*  C Implementation: vfs-file-info
 
3
*
 
4
* Description: File information
 
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 "vfs-file-info.h"
 
14
#include "xdgmime.h"
 
15
#include <glib.h>
 
16
#include "glib-mem.h"
 
17
#include <glib/gi18n.h>
 
18
#include <grp.h> /* Query group name */
 
19
#include <pwd.h> /* Query user name */
 
20
#include <string.h>
 
21
 
 
22
#include "vfs-app-desktop.h"
 
23
#include "md5.h"    /* for thumbnails */
 
24
 
 
25
#define _MAX( x, y )     (x > y ? x : y)
 
26
 
 
27
static int big_thumb_size = 48, small_thumb_size = 20;
 
28
static gboolean utf8_file_name = FALSE;
 
29
 
 
30
void vfs_file_info_set_utf8_filename( gboolean is_utf8 )
 
31
{
 
32
    utf8_file_name = is_utf8;
 
33
}
 
34
 
 
35
VFSFileInfo* vfs_file_info_new ()
 
36
{
 
37
    VFSFileInfo * fi = g_slice_new0( VFSFileInfo );
 
38
    fi->n_ref = 1;
 
39
    return fi;
 
40
}
 
41
 
 
42
static void vfs_file_info_clear( VFSFileInfo* fi )
 
43
{
 
44
    if ( fi->disp_name && fi->disp_name != fi->name )
 
45
    {
 
46
        g_free( fi->disp_name );
 
47
        fi->disp_name = NULL;
 
48
    }
 
49
    if ( fi->name )
 
50
    {
 
51
        g_free( fi->name );
 
52
        fi->name = NULL;
 
53
    }
 
54
    if ( fi->disp_size )
 
55
    {
 
56
        g_free( fi->disp_size );
 
57
        fi->disp_size = NULL;
 
58
    }
 
59
    if ( fi->disp_owner )
 
60
    {
 
61
        g_free( fi->disp_owner );
 
62
        fi->disp_owner = NULL;
 
63
    }
 
64
    if ( fi->disp_mtime )
 
65
    {
 
66
        g_free( fi->disp_mtime );
 
67
        fi->disp_mtime = NULL;
 
68
    }
 
69
    if ( fi->big_thumbnail )
 
70
    {
 
71
        gdk_pixbuf_unref( fi->big_thumbnail );
 
72
        fi->big_thumbnail = NULL;
 
73
    }
 
74
    if ( fi->small_thumbnail )
 
75
    {
 
76
        gdk_pixbuf_unref( fi->small_thumbnail );
 
77
        fi->small_thumbnail = NULL;
 
78
    }
 
79
 
 
80
    fi->disp_perm[ 0 ] = '\0';
 
81
 
 
82
    if ( fi->mime_type )
 
83
    {
 
84
        vfs_mime_type_unref( fi->mime_type );
 
85
        fi->mime_type = NULL;
 
86
    }
 
87
    fi->flags = VFS_FILE_INFO_NONE;
 
88
}
 
89
 
 
90
void vfs_file_info_ref( VFSFileInfo* fi )
 
91
{
 
92
    ++fi->n_ref;
 
93
}
 
94
 
 
95
void vfs_file_info_unref( VFSFileInfo* fi )
 
96
{
 
97
    --fi->n_ref;
 
98
    if ( fi->n_ref <= 0 )
 
99
    {
 
100
        vfs_file_info_clear( fi );
 
101
        g_slice_free( VFSFileInfo, fi );
 
102
    }
 
103
}
 
104
 
 
105
gboolean vfs_file_info_get( VFSFileInfo* fi,
 
106
                            const char* file_path,
 
107
                            const char* base_name )
 
108
{
 
109
    struct stat file_stat;
 
110
    vfs_file_info_clear( fi );
 
111
 
 
112
    if ( base_name )
 
113
        fi->name = g_strdup( base_name );
 
114
    else
 
115
        fi->name = g_path_get_basename( file_path );
 
116
 
 
117
    if ( lstat( file_path, &file_stat ) == 0 )
 
118
    {
 
119
        /* This is time-consuming but can save much memory */
 
120
        fi->mode = file_stat.st_mode;
 
121
        fi->dev = file_stat.st_dev;
 
122
        fi->uid = file_stat.st_uid;
 
123
        fi->gid = file_stat.st_gid;
 
124
        fi->size = file_stat.st_size;
 
125
        fi->mtime = file_stat.st_mtime;
 
126
        fi->atime = file_stat.st_atime;
 
127
        fi->blksize = file_stat.st_blksize;
 
128
        fi->blocks = file_stat.st_blocks;
 
129
 
 
130
        if ( G_LIKELY( utf8_file_name && g_utf8_validate ( fi->name, -1, NULL ) ) )
 
131
        {
 
132
            fi->disp_name = fi->name;   /* Don't duplicate the name and save memory */
 
133
        }
 
134
        else
 
135
        {
 
136
            fi->disp_name = g_filename_display_name( fi->name );
 
137
        }
 
138
        fi->mime_type = vfs_mime_type_get_from_file( file_path,
 
139
                                                     fi->disp_name,
 
140
                                                     &file_stat );
 
141
        return TRUE;
 
142
    }
 
143
    else
 
144
        fi->mime_type = vfs_mime_type_get_from_type( XDG_MIME_TYPE_UNKNOWN );
 
145
    return FALSE;
 
146
}
 
147
 
 
148
const char* vfs_file_info_get_name( VFSFileInfo* fi )
 
149
{
 
150
    return fi->name;
 
151
}
 
152
 
 
153
/* Get displayed name encoded in UTF-8 */
 
154
const char* vfs_file_info_get_disp_name( VFSFileInfo* fi )
 
155
{
 
156
    return fi->disp_name;
 
157
}
 
158
 
 
159
void vfs_file_info_set_disp_name( VFSFileInfo* fi, const char* name )
 
160
{
 
161
    if ( fi->disp_name && fi->disp_name != fi->name )
 
162
        g_free( fi->disp_name );
 
163
    fi->disp_name = g_strdup( name );
 
164
}
 
165
 
 
166
void vfs_file_info_set_name( VFSFileInfo* fi, const char* name )
 
167
{
 
168
    g_free( fi->name );
 
169
    fi->name = g_strdup( name );
 
170
}
 
171
 
 
172
off_t vfs_file_info_get_size( VFSFileInfo* fi )
 
173
{
 
174
    return fi->size;
 
175
}
 
176
 
 
177
const char* vfs_file_info_get_disp_size( VFSFileInfo* fi )
 
178
{
 
179
    if ( G_UNLIKELY( !fi->disp_size ) )
 
180
    {
 
181
        char buf[ 64 ];
 
182
        file_size_to_string( buf, fi->size );
 
183
        fi->disp_size = g_strdup( buf );
 
184
    }
 
185
    return fi->disp_size;
 
186
}
 
187
 
 
188
off_t vfs_file_info_get_blocks( VFSFileInfo* fi )
 
189
{
 
190
    return fi->blocks;
 
191
}
 
192
 
 
193
VFSMimeType* vfs_file_info_get_mime_type( VFSFileInfo* fi )
 
194
{
 
195
    vfs_mime_type_ref( fi->mime_type );
 
196
    return fi->mime_type;
 
197
}
 
198
 
 
199
void vfs_file_info_reload_mime_type( VFSFileInfo* fi,
 
200
                                     const char* full_path )
 
201
{
 
202
    VFSMimeType * old_mime_type;
 
203
    struct stat file_stat;
 
204
 
 
205
    /* convert VFSFileInfo to struct stat */
 
206
    /* In current implementation, only st_mode is used in
 
207
       mime-type detection, so let's save some CPU cycles
 
208
       and don't copy unused fields.
 
209
    */
 
210
    file_stat.st_mode = fi->mode;
 
211
    /*
 
212
    file_stat.st_dev = fi->dev;
 
213
    file_stat.st_uid = fi->uid;
 
214
    file_stat.st_gid = fi->gid;
 
215
    file_stat.st_size = fi->size;
 
216
    file_stat.st_mtime = fi->mtime;
 
217
    file_stat.st_atime = fi->atime;
 
218
    file_stat.st_blksize = fi->blksize;
 
219
    file_stat.st_blocks = fi->blocks;
 
220
    */
 
221
    old_mime_type = fi->mime_type;
 
222
    fi->mime_type = vfs_mime_type_get_from_file( full_path,
 
223
                                                 fi->name, &file_stat );
 
224
    vfs_mime_type_unref( old_mime_type );  /* FIXME: is vfs_mime_type_unref needed ?*/
 
225
}
 
226
 
 
227
const char* vfs_file_info_get_mime_type_desc( VFSFileInfo* fi )
 
228
{
 
229
    return vfs_mime_type_get_description( fi->mime_type );
 
230
}
 
231
 
 
232
GdkPixbuf* vfs_file_info_get_big_icon( VFSFileInfo* fi )
 
233
{
 
234
    /* get special icons for special files, especially for
 
235
       some desktop icons */
 
236
 
 
237
    if ( G_UNLIKELY( fi->flags != VFS_FILE_INFO_NONE ) )
 
238
    {
 
239
        int w, h;
 
240
        int icon_size;
 
241
        vfs_mime_type_get_icon_size( &icon_size, NULL );
 
242
        if ( fi->big_thumbnail )
 
243
        {
 
244
            w = gdk_pixbuf_get_width( fi->big_thumbnail );
 
245
            h = gdk_pixbuf_get_height( fi->big_thumbnail );
 
246
        }
 
247
        else
 
248
            w = h = 0;
 
249
 
 
250
        if ( _MAX( w, h ) != icon_size )
 
251
        {
 
252
            char * icon_name = NULL;
 
253
            if ( fi->big_thumbnail )
 
254
            {
 
255
                icon_name = ( char* ) g_object_steal_data(
 
256
                                G_OBJECT(fi->big_thumbnail), "name" );
 
257
                gdk_pixbuf_unref( fi->big_thumbnail );
 
258
                fi->big_thumbnail = NULL;
 
259
            }
 
260
            if ( G_LIKELY( icon_name ) )
 
261
            {
 
262
                if ( G_UNLIKELY( icon_name[ 0 ] == '/' ) )
 
263
                    fi->big_thumbnail = gdk_pixbuf_new_from_file( icon_name, NULL );
 
264
                else
 
265
                    fi->big_thumbnail = gtk_icon_theme_load_icon(
 
266
                                            gtk_icon_theme_get_default(),
 
267
                                            icon_name,
 
268
                                            icon_size, 0, NULL );
 
269
            }
 
270
            if ( fi->big_thumbnail )
 
271
                g_object_set_data_full( G_OBJECT(fi->big_thumbnail), "name", icon_name, g_free );
 
272
            else
 
273
                g_free( icon_name );
 
274
        }
 
275
        return fi->big_thumbnail ? gdk_pixbuf_ref( fi->big_thumbnail ) : NULL;
 
276
    }
 
277
    if( G_UNLIKELY(!fi->mime_type) )
 
278
        return NULL;
 
279
    return vfs_mime_type_get_icon( fi->mime_type, TRUE );
 
280
}
 
281
 
 
282
GdkPixbuf* vfs_file_info_get_small_icon( VFSFileInfo* fi )
 
283
{
 
284
    return vfs_mime_type_get_icon( fi->mime_type, FALSE );
 
285
}
 
286
 
 
287
GdkPixbuf* vfs_file_info_get_big_thumbnail( VFSFileInfo* fi )
 
288
{
 
289
    return fi->big_thumbnail ? gdk_pixbuf_ref( fi->big_thumbnail ) : NULL;
 
290
}
 
291
 
 
292
GdkPixbuf* vfs_file_info_get_small_thumbnail( VFSFileInfo* fi )
 
293
{
 
294
    return fi->small_thumbnail ? gdk_pixbuf_ref( fi->small_thumbnail ) : NULL;
 
295
}
 
296
 
 
297
const char* vfs_file_info_get_disp_owner( VFSFileInfo* fi )
 
298
{
 
299
    struct passwd * puser;
 
300
    struct group* pgroup;
 
301
    char uid_str_buf[ 32 ];
 
302
    char* user_name;
 
303
    char gid_str_buf[ 32 ];
 
304
    char* group_name;
 
305
 
 
306
    /* FIXME: user names should be cached */
 
307
    if ( ! fi->disp_owner )
 
308
    {
 
309
        puser = getpwuid( fi->uid );
 
310
        if ( puser && puser->pw_name && *puser->pw_name )
 
311
            user_name = puser->pw_name;
 
312
        else
 
313
        {
 
314
            sprintf( uid_str_buf, "%d", fi->uid );
 
315
            user_name = uid_str_buf;
 
316
        }
 
317
 
 
318
        pgroup = getgrgid( fi->gid );
 
319
        if ( pgroup && pgroup->gr_name && *pgroup->gr_name )
 
320
            group_name = pgroup->gr_name;
 
321
        else
 
322
        {
 
323
            sprintf( gid_str_buf, "%d", fi->gid );
 
324
            group_name = gid_str_buf;
 
325
        }
 
326
        fi->disp_owner = g_strdup_printf ( "%s:%s", user_name, group_name );
 
327
    }
 
328
    return fi->disp_owner;
 
329
}
 
330
 
 
331
const char* vfs_file_info_get_disp_mtime( VFSFileInfo* fi )
 
332
{
 
333
    if ( ! fi->disp_mtime )
 
334
    {
 
335
        char buf[ 64 ];
 
336
        strftime( buf, sizeof( buf ),
 
337
                  "%Y-%m-%d %H:%M",
 
338
                  localtime( &fi->mtime ) );
 
339
        fi->disp_mtime = g_strdup( buf );
 
340
    }
 
341
    return fi->disp_mtime;
 
342
}
 
343
 
 
344
time_t* vfs_file_info_get_mtime( VFSFileInfo* fi )
 
345
{
 
346
    return & fi->mtime;
 
347
}
 
348
 
 
349
time_t* vfs_file_info_get_atime( VFSFileInfo* fi )
 
350
{
 
351
    return & fi->atime;
 
352
}
 
353
 
 
354
static void get_file_perm_string( char* perm, mode_t mode )
 
355
{
 
356
    perm[ 0 ] = S_ISDIR( mode ) ? 'd' : ( S_ISLNK( mode ) ? 'l' : '-' );
 
357
    perm[ 1 ] = ( mode & S_IRUSR ) ? 'r' : '-';
 
358
    perm[ 2 ] = ( mode & S_IWUSR ) ? 'w' : '-';
 
359
    perm[ 3 ] = ( mode & S_IXUSR ) ? 'x' : '-';
 
360
    perm[ 4 ] = ( mode & S_IRGRP ) ? 'r' : '-';
 
361
    perm[ 5 ] = ( mode & S_IWGRP ) ? 'w' : '-';
 
362
    perm[ 6 ] = ( mode & S_IXGRP ) ? 'x' : '-';
 
363
    perm[ 7 ] = ( mode & S_IROTH ) ? 'r' : '-';
 
364
    perm[ 8 ] = ( mode & S_IWOTH ) ? 'w' : '-';
 
365
    perm[ 9 ] = ( mode & S_IXOTH ) ? 'x' : '-';
 
366
    perm[ 10 ] = '\0';
 
367
}
 
368
 
 
369
const char* vfs_file_info_get_disp_perm( VFSFileInfo* fi )
 
370
{
 
371
    if ( ! fi->disp_perm[ 0 ] )
 
372
        get_file_perm_string( fi->disp_perm,
 
373
                              fi->mode );
 
374
    return fi->disp_perm;
 
375
}
 
376
 
 
377
void file_size_to_string( char* buf, guint64 size )
 
378
{
 
379
    char * unit;
 
380
    /* guint point; */
 
381
    gfloat val;
 
382
 
 
383
    /*
 
384
       FIXME: Is floating point calculation slower than integer division?
 
385
              Some profiling is needed here.
 
386
    */
 
387
    if ( size > ( ( guint64 ) 1 ) << 30 )
 
388
    {
 
389
        if ( size > ( ( guint64 ) 1 ) << 40 )
 
390
        {
 
391
            /*
 
392
            size /= ( ( ( guint64 ) 1 << 40 ) / 10 );
 
393
            point = ( guint ) ( size % 10 );
 
394
            size /= 10;
 
395
            */
 
396
            val = ((gfloat)size) / ( ( guint64 ) 1 << 40 );
 
397
            unit = "TB";
 
398
        }
 
399
        else
 
400
        {
 
401
            /*
 
402
            size /= ( ( 1 << 30 ) / 10 );
 
403
            point = ( guint ) ( size % 10 );
 
404
            size /= 10;
 
405
            */
 
406
            val = ((gfloat)size) / ( ( guint64 ) 1 << 30 );
 
407
            unit = "GB";
 
408
        }
 
409
    }
 
410
    else if ( size > ( 1 << 20 ) )
 
411
    {
 
412
        /*
 
413
        size /= ( ( 1 << 20 ) / 10 );
 
414
        point = ( guint ) ( size % 10 );
 
415
        size /= 10;
 
416
        */
 
417
        val = ((gfloat)size) / ( ( guint64 ) 1 << 20 );
 
418
        unit = "MB";
 
419
    }
 
420
    else if ( size > ( 1 << 10 ) )
 
421
    {
 
422
        /*
 
423
        size /= ( ( 1 << 10 ) / 10 );
 
424
        point = size % 10;
 
425
        size /= 10;
 
426
        */
 
427
        val = ((gfloat)size) / ( ( guint64 ) 1 << 10 );
 
428
        unit = "KB";
 
429
    }
 
430
    else
 
431
    {
 
432
        unit = size > 1 ? "Bytes" : "Byte";
 
433
        sprintf( buf, "%u %s", ( guint ) size, unit );
 
434
        return ;
 
435
    }
 
436
    /* sprintf( buf, "%llu.%u %s", size, point, unit ); */
 
437
    sprintf( buf, "%.1f %s", val, unit );
 
438
}
 
439
 
 
440
gboolean vfs_file_info_is_dir( VFSFileInfo* fi )
 
441
{
 
442
    if ( S_ISDIR( fi->mode ) )
 
443
        return TRUE;
 
444
    if ( S_ISLNK( fi->mode ) &&
 
445
            0 == strcmp( vfs_mime_type_get_type( fi->mime_type ), XDG_MIME_TYPE_DIRECTORY ) )
 
446
    {
 
447
        return TRUE;
 
448
    }
 
449
    return FALSE;
 
450
}
 
451
 
 
452
gboolean vfs_file_info_is_symlink( VFSFileInfo* fi )
 
453
{
 
454
    return S_ISLNK( fi->mode ) ? TRUE : FALSE;
 
455
}
 
456
 
 
457
gboolean vfs_file_info_is_image( VFSFileInfo* fi )
 
458
{
 
459
    /* FIXME: We had better use functions of xdg_mime to check this */
 
460
    if ( ! strncmp( "image/", vfs_mime_type_get_type( fi->mime_type ), 6 ) )
 
461
        return TRUE;
 
462
    return FALSE;
 
463
}
 
464
 
 
465
gboolean vfs_file_info_is_unknown_type( VFSFileInfo* fi )
 
466
{
 
467
    if ( ! strcmp( XDG_MIME_TYPE_UNKNOWN,
 
468
           vfs_mime_type_get_type( fi->mime_type ) ) )
 
469
        return TRUE;
 
470
    return FALSE;
 
471
}
 
472
 
 
473
/* full path of the file is required by this function */
 
474
gboolean vfs_file_info_is_executable( VFSFileInfo* fi, const char* file_path )
 
475
{
 
476
    return xdg_mime_is_executable_file( file_path, fi->mime_type->type );
 
477
}
 
478
 
 
479
/* full path of the file is required by this function */
 
480
gboolean vfs_file_info_is_text( VFSFileInfo* fi, const char* file_path )
 
481
{
 
482
    return xdg_mime_is_text_file( file_path, fi->mime_type->type );
 
483
}
 
484
 
 
485
/*
 
486
* Run default action of specified file.
 
487
* Full path of the file is required by this function.
 
488
*/
 
489
gboolean vfs_file_info_open_file( VFSFileInfo* fi,
 
490
                                  const char* file_path,
 
491
                                  GError** err )
 
492
{
 
493
    VFSMimeType * mime_type;
 
494
    char* app_name;
 
495
    VFSAppDesktop* app;
 
496
    GList* files = NULL;
 
497
    gboolean ret = FALSE;
 
498
    char* argv[ 2 ];
 
499
 
 
500
    if ( vfs_file_info_is_executable( fi, file_path ) )
 
501
    {
 
502
        argv[ 0 ] = file_path;
 
503
        argv[ 1 ] = '\0';
 
504
        ret = g_spawn_async( NULL, argv, NULL, G_SPAWN_STDOUT_TO_DEV_NULL|
 
505
                             G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, err );
 
506
    }
 
507
    else
 
508
    {
 
509
        mime_type = vfs_file_info_get_mime_type( fi );
 
510
        app_name = vfs_mime_type_get_default_action( mime_type );
 
511
        if ( app_name )
 
512
        {
 
513
            app = vfs_app_desktop_new( app_name );
 
514
            if ( ! vfs_app_desktop_get_exec( app ) )
 
515
                app->exec = g_strdup( app_name );   /* FIXME: app->exec */
 
516
            files = g_list_prepend( files, file_path );
 
517
            /* FIXME: working dir is needed */
 
518
            ret = vfs_app_desktop_open_files( gdk_screen_get_default(),
 
519
                                              NULL, app, files, err );
 
520
            g_list_free( files );
 
521
            vfs_app_desktop_unref( app );
 
522
            g_free( app_name );
 
523
        }
 
524
        vfs_mime_type_unref( mime_type );
 
525
    }
 
526
    return ret;
 
527
}
 
528
 
 
529
mode_t vfs_file_info_get_mode( VFSFileInfo* fi )
 
530
{
 
531
    return fi->mode;
 
532
}
 
533
 
 
534
static void unload_thumnails_if_needed( VFSFileInfo* fi )
 
535
{
 
536
    int w, h;
 
537
    if ( fi->big_thumbnail && fi->flags == VFS_FILE_INFO_NONE )
 
538
    {
 
539
        w = gdk_pixbuf_get_width( fi->big_thumbnail );
 
540
        h = gdk_pixbuf_get_height( fi->big_thumbnail );
 
541
        if ( _MAX( w, h ) != big_thumb_size )
 
542
        {
 
543
            gdk_pixbuf_unref( fi->big_thumbnail );
 
544
            fi->big_thumbnail = NULL;
 
545
        }
 
546
    }
 
547
    if ( fi->small_thumbnail && fi->flags == VFS_FILE_INFO_NONE )
 
548
    {
 
549
        w = gdk_pixbuf_get_width( fi->small_thumbnail );
 
550
        h = gdk_pixbuf_get_height( fi->small_thumbnail );
 
551
        if ( MAX( w, h ) != small_thumb_size )
 
552
        {
 
553
            gdk_pixbuf_unref( fi->small_thumbnail );
 
554
            fi->small_thumbnail = NULL;
 
555
        }
 
556
    }
 
557
}
 
558
 
 
559
gboolean vfs_file_info_is_thumbnail_loaded( VFSFileInfo* fi, gboolean big )
 
560
{
 
561
    /* FIXME: I cannot find a better place to unload thumbnails */
 
562
    unload_thumnails_if_needed( fi );
 
563
    if ( big )
 
564
        return ( fi->big_thumbnail != NULL );
 
565
    return ( fi->small_thumbnail != NULL );
 
566
}
 
567
 
 
568
gboolean vfs_file_info_load_thumbnail( VFSFileInfo* fi,
 
569
                                       const char* full_path,
 
570
                                       gboolean big )
 
571
{
 
572
    char * uri;
 
573
    md5_byte_t md5[ 16 ];
 
574
    char* thumbnail_file;
 
575
    char file_name[ 36 ];
 
576
    char mtime_str[ 32 ];
 
577
    const char* thumb_mtime;
 
578
    int i, w, h, size;
 
579
    struct stat statbuf;
 
580
    GdkPixbuf* thumbnail, *result = NULL;
 
581
 
 
582
    /* FIXME: I cannot find a better place to unload thumbnails */
 
583
    unload_thumnails_if_needed( fi );
 
584
 
 
585
    if ( big )
 
586
    {
 
587
        if ( fi->big_thumbnail && big )
 
588
            return TRUE;
 
589
    }
 
590
    else
 
591
    {
 
592
        if ( fi->small_thumbnail )
 
593
            return TRUE;
 
594
    }
 
595
 
 
596
    if ( !gdk_pixbuf_get_file_info( full_path, &w, &h ) )
 
597
        return FALSE;   /* image format cannot be recognized */
 
598
 
 
599
    /* If the image itself is very small, we should load it directly */
 
600
    if ( w <= 128 && h <= 128 )
 
601
    {
 
602
        size = big ? big_thumb_size : small_thumb_size;
 
603
        thumbnail = gdk_pixbuf_new_from_file_at_scale( full_path, size, size,
 
604
                                                       TRUE, NULL );
 
605
        if ( big )
 
606
            fi->big_thumbnail = thumbnail;
 
607
        else
 
608
            fi->small_thumbnail = thumbnail;
 
609
        return ( thumbnail != NULL );
 
610
    }
 
611
 
 
612
    uri = g_filename_to_uri( full_path, NULL, NULL );
 
613
 
 
614
    md5_state_t md5_state;
 
615
    md5_init( &md5_state );
 
616
    md5_append( &md5_state, ( md5_byte_t * ) uri, strlen( uri ) );
 
617
    md5_finish( &md5_state, md5 );
 
618
 
 
619
    for ( i = 0; i < 16; ++i )
 
620
    {
 
621
        sprintf( ( file_name + i * 2 ), "%02x", md5[ i ] );
 
622
    }
 
623
    strcpy( ( file_name + i * 2 ), ".png" );
 
624
 
 
625
    thumbnail_file = g_build_filename( g_get_home_dir(),
 
626
                                       ".thumbnails/normal",
 
627
                                       file_name, NULL );
 
628
 
 
629
    /* load existing thumbnail */
 
630
    thumbnail = gdk_pixbuf_new_from_file( thumbnail_file, NULL );
 
631
    if ( !thumbnail ||
 
632
            !( thumb_mtime = gdk_pixbuf_get_option( thumbnail, "tEXt::Thumb::MTime" ) ) ||
 
633
            atol( thumb_mtime ) != fi->mtime )
 
634
    {
 
635
        /* create new thumbnail */
 
636
        thumbnail = gdk_pixbuf_new_from_file_at_size( full_path, 128, 128, NULL );
 
637
        if ( thumbnail )
 
638
        {
 
639
            sprintf( mtime_str, "%lu", fi->mtime );
 
640
            gdk_pixbuf_save( thumbnail, thumbnail_file, "png", NULL,
 
641
                             "tEXt::Thumb::URI", uri, "tEXt::Thumb::MTime", mtime_str, NULL );
 
642
        }
 
643
    }
 
644
 
 
645
    if ( thumbnail )
 
646
    {
 
647
        w = gdk_pixbuf_get_width( thumbnail );
 
648
        h = gdk_pixbuf_get_height( thumbnail );
 
649
        size = big ? big_thumb_size : small_thumb_size;
 
650
        if ( w > h )
 
651
        {
 
652
            h = h * size / w;
 
653
            w = size;
 
654
        }
 
655
        else if ( h > w )
 
656
        {
 
657
            w = w * size / h;
 
658
            h = size;
 
659
        }
 
660
        else
 
661
        {
 
662
            w = h = size;
 
663
        }
 
664
        result = gdk_pixbuf_scale_simple(
 
665
                     thumbnail,
 
666
                     w, h, GDK_INTERP_BILINEAR );
 
667
        gdk_pixbuf_unref( thumbnail );
 
668
        if ( big )
 
669
            fi->big_thumbnail = result;
 
670
        else
 
671
            fi->small_thumbnail = result;
 
672
    }
 
673
    g_free( uri );
 
674
    g_free( thumbnail_file );
 
675
    return ( result != NULL );
 
676
}
 
677
 
 
678
void vfs_file_info_set_thumbnail_size( int big, int small )
 
679
{
 
680
    big_thumb_size = big;
 
681
    small_thumb_size = small;
 
682
}
 
683
 
 
684
void vfs_file_info_load_special_info( VFSFileInfo* fi,
 
685
                                      const char* file_path )
 
686
{
 
687
    if ( g_str_has_suffix( fi->name, ".desktop" ) )
 
688
    {
 
689
        VFSAppDesktop * desktop;
 
690
        const char* icon_name;
 
691
        fi->flags |= VFS_FILE_INFO_DESKTOP_ENTRY;
 
692
        desktop = vfs_app_desktop_new( file_path );
 
693
        if ( vfs_app_desktop_get_disp_name( desktop ) )
 
694
        {
 
695
            vfs_file_info_set_disp_name(
 
696
                fi, vfs_app_desktop_get_disp_name( desktop ) );
 
697
        }
 
698
        if( fi->big_thumbnail )
 
699
        {
 
700
            gdk_pixbuf_unref( fi->big_thumbnail );
 
701
            fi->big_thumbnail = NULL;
 
702
        }
 
703
        if ( (icon_name = vfs_app_desktop_get_icon_name( desktop )) )
 
704
        {
 
705
            char * _icon_name = strdup( icon_name );
 
706
            GdkPixbuf* icon;
 
707
            int size;
 
708
            vfs_mime_type_get_icon_size( &size, NULL );
 
709
            /* icon name is a full path */
 
710
            if ( icon_name[ 0 ] == '/' )
 
711
            {
 
712
                icon = gdk_pixbuf_new_from_file_at_size(
 
713
                           icon_name, size, size, NULL );
 
714
            }
 
715
            else
 
716
            {
 
717
                char* dot = strchr( _icon_name, '.' );
 
718
                if ( dot )
 
719
                    * dot = '\0';
 
720
                icon = gtk_icon_theme_load_icon(
 
721
                           gtk_icon_theme_get_default(),
 
722
                           _icon_name, size, 0, NULL );
 
723
            }
 
724
            /* save app icon in thumbnail */
 
725
            fi->big_thumbnail = icon;
 
726
            if ( G_LIKELY( icon ) )
 
727
                g_object_set_data_full( G_OBJECT(icon), "name", _icon_name, g_free );
 
728
            else
 
729
                g_free( _icon_name );
 
730
        }
 
731
        vfs_app_desktop_unref( desktop );
 
732
    }
 
733
}
 
734
 
 
735
void vfs_file_info_list_free( GList* list )
 
736
{
 
737
    g_list_foreach( list, (GFunc)vfs_file_info_unref, NULL );
 
738
    g_list_free( list );
 
739
}
 
740
 
 
741
 
 
742
char* vfs_file_resolve_path( const char* cwd, const char* relative_path )
 
743
{
 
744
    GString* ret = g_string_sized_new( 4096 );
 
745
    const char* sep;
 
746
    if( G_UNLIKELY(*relative_path != '/') ) /* relative path */
 
747
    {
 
748
        if( G_UNLIKELY(relative_path[0] == '~') ) /* home dir */
 
749
        {
 
750
            g_string_append( ret, g_get_home_dir());
 
751
            ++relative_path;
 
752
        }
 
753
        else
 
754
        {
 
755
            if( ! cwd )
 
756
            {
 
757
                cwd = g_get_current_dir();
 
758
                g_string_append( ret, cwd );
 
759
                g_free( cwd );
 
760
            }
 
761
            else
 
762
                g_string_append( ret, cwd );
 
763
        }
 
764
    }
 
765
 
 
766
    if( relative_path[0] != '/' )
 
767
        g_string_append_c( ret, '/' );
 
768
 
 
769
    while( G_LIKELY( *relative_path ) )
 
770
    {
 
771
        if( G_UNLIKELY(*relative_path == '.') )
 
772
        {
 
773
            if( relative_path[1] == '/' || relative_path[1] == '\0' ) /* current dir */
 
774
            {
 
775
                relative_path += relative_path[1] ? 2 : 1;
 
776
                continue;
 
777
            }
 
778
            if( relative_path[1] == '.' &&
 
779
                ( relative_path[2] == '/' || relative_path[2] == '\0') ) /* parent dir */
 
780
            {
 
781
                gsize len = ret->len - 2;
 
782
                while( ret->str[ len ] != '/' )
 
783
                    --len;
 
784
                g_string_truncate( ret, len + 1 );
 
785
                relative_path += relative_path[2] ? 3 : 2;
 
786
                continue;
 
787
            }
 
788
        }
 
789
 
 
790
        do
 
791
        {
 
792
            g_string_append_c( ret, *relative_path );
 
793
        }while( G_LIKELY( *(relative_path++) != '/' && *relative_path ) );
 
794
    }
 
795
 
 
796
    /* remove tailing '/' */
 
797
    if( G_LIKELY( ret->len > 1 ) && G_UNLIKELY( ret->str[ ret->len - 1 ] == '/' ) )
 
798
        g_string_truncate( ret, ret->len - 1 );
 
799
    return g_string_free( ret, FALSE );
 
800
}
 
801