~ubuntu-branches/ubuntu/lucid/libfm/lucid

« back to all changes in this revision

Viewing changes to src/base/fm-mime-type.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2010-02-06 22:53:35 UTC
  • Revision ID: james.westby@ubuntu.com-20100206225335-wsjxzkub56ak4tp5
Tags: upstream-0.1~git20100207
ImportĀ upstreamĀ versionĀ 0.1~git20100207

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      fm-mime-type.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
 
 
23
#include "fm-mime-type.h"
 
24
 
 
25
#include <sys/types.h>
 
26
#include <sys/stat.h>
 
27
#include <unistd.h>
 
28
#include <fcntl.h>
 
29
#include <string.h>
 
30
 
 
31
/* FIXME: how can we handle reload of xdg mime? */
 
32
 
 
33
static GHashTable *mime_hash = NULL;
 
34
G_LOCK_DEFINE(mime_hash);
 
35
 
 
36
static guint reload_callback_id = 0;
 
37
static GList* reload_cb = NULL;
 
38
 
 
39
//static VFSFileMonitor** mime_caches_monitor = NULL;
 
40
 
 
41
typedef struct {
 
42
    GFreeFunc cb;
 
43
    gpointer user_data;
 
44
}FmMimeReloadCbEnt;
 
45
 
 
46
#if 0
 
47
static gboolean fm_mime_type_reload( gpointer user_data )
 
48
{
 
49
    GList* l;
 
50
    /* FIXME: process mime database reloading properly. */
 
51
    /* Remove all items in the hash table */
 
52
    GDK_THREADS_ENTER();
 
53
 
 
54
    g_static_rw_lock_writer_lock( &mime_hash_lock );
 
55
    g_hash_table_foreach_remove ( mime_hash, ( GHRFunc ) gtk_true, NULL );
 
56
    g_static_rw_lock_writer_unlock( &mime_hash_lock );
 
57
 
 
58
    g_source_remove( reload_callback_id );
 
59
    reload_callback_id = 0;
 
60
 
 
61
    /* g_debug( "reload mime-types" ); */
 
62
 
 
63
    /* call all registered callbacks */
 
64
    for( l = reload_cb; l; l = l->next )
 
65
    {
 
66
        FmMimeReloadCbEnt* ent = (FmMimeReloadCbEnt*)l->data;
 
67
        ent->cb( ent->user_data );
 
68
    }
 
69
    GDK_THREADS_LEAVE();
 
70
    return FALSE;
 
71
}
 
72
 
 
73
static void on_mime_cache_changed( VFSFileMonitor* fm,
 
74
                                        VFSFileMonitorEvent event,
 
75
                                        const char* file_name,
 
76
                                        gpointer user_data )
 
77
{
 
78
    MimeCache* cache = (MimeCache*)user_data;
 
79
    switch( event )
 
80
    {
 
81
    case FM_FILE_MONITOR_CREATE:
 
82
    case FM_FILE_MONITOR_DELETE:
 
83
        /* NOTE: FAM sometimes generate incorrect "delete" notification for non-existent files.
 
84
         *  So if the cache is not loaded originally (the cache file is non-existent), we skip it. */
 
85
        if( ! cache->buffer )
 
86
            return;
 
87
    case FM_FILE_MONITOR_CHANGE:
 
88
        mime_cache_reload( cache );
 
89
        /* g_debug( "reload cache: %s", file_name ); */
 
90
        if( 0 == reload_callback_id )
 
91
            reload_callback_id = g_idle_add( fm_mime_type_reload, NULL );
 
92
    }
 
93
}
 
94
#endif
 
95
 
 
96
void fm_mime_type_init()
 
97
{
 
98
    FmMimeType* tmp;
 
99
#if 0
 
100
    GtkIconTheme * theme;
 
101
    MimeCache** caches;
 
102
    int i, n_caches;
 
103
 
 
104
    mime_type_init();
 
105
 
 
106
    /* install file alteration monitor for mime-cache */
 
107
    caches = mime_type_get_caches( &n_caches );
 
108
    mime_caches_monitor = g_new0( VFSFileMonitor*, n_caches );
 
109
    for( i = 0; i < n_caches; ++i )
 
110
    {
 
111
        VFSFileMonitor* fm = vfs_file_monitor_add_file( caches[i]->file_path,
 
112
                                                                on_mime_cache_changed, caches[i] );
 
113
        mime_caches_monitor[i] = fm;
 
114
    }
 
115
#endif
 
116
 
 
117
    mime_hash = g_hash_table_new_full( g_str_hash, g_str_equal,
 
118
                                       NULL, fm_mime_type_unref );
 
119
}
 
120
 
 
121
void fm_mime_type_finalize()
 
122
{
 
123
#if 0
 
124
    GtkIconTheme * theme;
 
125
    MimeCache** caches;
 
126
    int i, n_caches;
 
127
 
 
128
    theme = gtk_icon_theme_get_default();
 
129
    g_signal_handler_disconnect( theme, theme_change_notify );
 
130
 
 
131
    /* remove file alteration monitor for mime-cache */
 
132
    caches = mime_type_get_caches( &n_caches );
 
133
    for( i = 0; i < n_caches; ++i )
 
134
    {
 
135
        vfs_file_monitor_remove( mime_caches_monitor[i],
 
136
                                        on_mime_cache_changed, caches[i] );
 
137
    }
 
138
    g_free( mime_caches_monitor );
 
139
 
 
140
    mime_type_finalize();
 
141
#endif
 
142
    g_hash_table_destroy( mime_hash );
 
143
}
 
144
 
 
145
FmMimeType* fm_mime_type_get_for_file_name( const char* ufile_name )
 
146
{
 
147
        FmMimeType* mime_type;
 
148
    char * type;
 
149
        gboolean uncertain;
 
150
    type = g_content_type_guess( ufile_name, NULL, 0, &uncertain );
 
151
        mime_type = fm_mime_type_get_for_type( type );
 
152
        g_free(type);
 
153
    return mime_type;
 
154
}
 
155
 
 
156
/*
 
157
static FmMimeType* fm_mime_type_get_internal(char* type)
 
158
{
 
159
        FmMimeType* 
 
160
}
 
161
*/
 
162
 
 
163
FmMimeType* fm_mime_type_get_for_native_file( const char* file_path,
 
164
                                        const char* base_name,
 
165
                                        struct stat* pstat )
 
166
{
 
167
        FmMimeType* mime_type;
 
168
        struct stat st;
 
169
 
 
170
        if( !pstat )
 
171
        {
 
172
                pstat = &st;
 
173
                if( stat( file_path, &st ) == -1 )
 
174
                    return NULL;
 
175
        }
 
176
 
 
177
        if( S_ISREG(pstat->st_mode) )
 
178
        {
 
179
            if( pstat->st_size == 0 ) /* empty file = text file with 0 characters in it. */
 
180
            return fm_mime_type_get_for_type( "text/plain" );
 
181
            else
 
182
            {
 
183
            gboolean uncertain;
 
184
            char* type = g_content_type_guess( base_name, NULL, 0, &uncertain );
 
185
            if( uncertain )
 
186
            {
 
187
                char buf[4096];
 
188
                int fd, len;
 
189
                fd = open(file_path, O_RDONLY);
 
190
                if( fd >= 0 )
 
191
                {
 
192
                    g_free(type);
 
193
                    len = read(fd, buf, 4096);
 
194
                    close(fd);
 
195
                    type = g_content_type_guess( NULL, buf, len, &uncertain );
 
196
                }
 
197
            }
 
198
            mime_type = fm_mime_type_get_for_type( type );
 
199
            g_free(type);
 
200
            return mime_type;
 
201
            }
 
202
        }
 
203
 
 
204
        if( S_ISDIR(pstat->st_mode) )
 
205
            return fm_mime_type_get_for_type( "inode/directory" );
 
206
        if (S_ISCHR(pstat->st_mode))
 
207
            return fm_mime_type_get_for_type( "inode/chardevice" );
 
208
        if (S_ISBLK(pstat->st_mode))
 
209
            return fm_mime_type_get_for_type( "inode/blockdevice" );
 
210
        if (S_ISFIFO(pstat->st_mode))
 
211
            return fm_mime_type_get_for_type( "inode/fifo" );
 
212
        if (S_ISLNK(pstat->st_mode)) 
 
213
            return fm_mime_type_get_for_type( "inode/symlink" );
 
214
#ifdef S_ISSOCK
 
215
        if (S_ISSOCK(pstat->st_mode))
 
216
            return fm_mime_type_get_for_type( "inode/socket" );
 
217
#endif
 
218
        /* impossible */
 
219
        g_error( "Invalid stat mode: %s", base_name );
 
220
        return NULL;
 
221
}
 
222
 
 
223
FmMimeType* fm_mime_type_get_for_type( const char* type )
 
224
{
 
225
    FmMimeType * mime_type;
 
226
 
 
227
    G_LOCK( mime_hash );
 
228
    mime_type = g_hash_table_lookup( mime_hash, type );
 
229
    if ( !mime_type )
 
230
    {
 
231
        mime_type = fm_mime_type_new( type );
 
232
        g_hash_table_insert( mime_hash, mime_type->type, mime_type );
 
233
    }
 
234
        G_UNLOCK( mime_hash );
 
235
    fm_mime_type_ref( mime_type );
 
236
    return mime_type;
 
237
}
 
238
 
 
239
FmMimeType* fm_mime_type_new( const char* type_name )
 
240
{
 
241
    FmMimeType * mime_type = g_slice_new0( FmMimeType );
 
242
    GIcon* gicon;
 
243
    mime_type->type = g_strdup( type_name );
 
244
    mime_type->n_ref = 1;
 
245
 
 
246
        gicon = g_content_type_get_icon(mime_type->type);
 
247
        if( strcmp(mime_type->type, "inode/directory") == 0 )
 
248
                g_themed_icon_prepend_name(gicon, "folder");
 
249
        else if( g_content_type_can_be_executable(mime_type->type) )
 
250
                g_themed_icon_append_name(gicon, "application-x-executable");
 
251
 
 
252
    mime_type->icon = fm_icon_from_gicon(gicon);
 
253
    g_object_unref(gicon);
 
254
 
 
255
#if 0
 
256
  /* TODO: Special case desktop dir? That could be expensive with xdg dirs... */
 
257
  if (strcmp (path, g_get_home_dir ()) == 0)
 
258
        type_icon = "user-home";
 
259
  else
 
260
        type_icon = "text-x-generic";
 
261
#endif
 
262
 
 
263
    return mime_type;
 
264
}
 
265
 
 
266
FmMimeType* fm_mime_type_ref( FmMimeType* mime_type )
 
267
{
 
268
    g_atomic_int_inc(&mime_type->n_ref);
 
269
        return mime_type;
 
270
}
 
271
 
 
272
void fm_mime_type_unref( gpointer mime_type_ )
 
273
{
 
274
    FmMimeType* mime_type = (FmMimeType*)mime_type_;
 
275
    if ( g_atomic_int_dec_and_test(&mime_type->n_ref) )
 
276
    {
 
277
        g_free( mime_type->type );
 
278
        if ( mime_type->icon )
 
279
            fm_icon_unref( mime_type->icon );
 
280
        g_slice_free( FmMimeType, mime_type );
 
281
    }
 
282
}
 
283
 
 
284
FmIcon* fm_mime_type_get_icon( FmMimeType* mime_type )
 
285
{
 
286
        return mime_type->icon;
 
287
#if 0
 
288
    GdkPixbuf * icon = NULL;
 
289
    const char* sep;
 
290
    char icon_name[ 100 ];
 
291
    GtkIconTheme *icon_theme;
 
292
    int size;
 
293
 
 
294
    if ( big )
 
295
    {
 
296
        if ( G_LIKELY( mime_type->big_icon ) )     /* big icon */
 
297
            return gdk_pixbuf_ref( mime_type->big_icon );
 
298
        size = big_icon_size;
 
299
    }
 
300
    else    /* small icon */
 
301
    {
 
302
        if ( G_LIKELY( mime_type->small_icon ) )
 
303
            return gdk_pixbuf_ref( mime_type->small_icon );
 
304
        size = small_icon_size;
 
305
    }
 
306
 
 
307
    icon_theme = gtk_icon_theme_get_default ();
 
308
 
 
309
    if ( G_UNLIKELY( 0 == strcmp( mime_type->type, XDG_MIME_TYPE_DIRECTORY ) ) )
 
310
    {
 
311
        icon = vfs_load_icon ( icon_theme, "folder", size );
 
312
        if( G_UNLIKELY(! icon) )
 
313
            icon = vfs_load_icon ( icon_theme, "gnome-fs-directory", size );
 
314
        if ( big )
 
315
            mime_type->big_icon = icon;
 
316
        else
 
317
            mime_type->small_icon = icon;
 
318
        return icon ? gdk_pixbuf_ref( icon ) : NULL;
 
319
    }
 
320
 
 
321
    sep = strchr( mime_type->type, '/' );
 
322
    if ( sep )
 
323
    {
 
324
        /* convert mime-type foo/bar to foo-bar */
 
325
        strcpy( icon_name, mime_type->type );
 
326
        icon_name[ (sep - mime_type->type) ] = '-';
 
327
        /* is there an icon named foo-bar? */
 
328
        icon = vfs_load_icon ( icon_theme, icon_name, size );
 
329
        if ( ! icon )
 
330
        {
 
331
            /* maybe we can find a legacy icon named gnome-mime-foo-bar */
 
332
            strcpy( icon_name, "gnome-mime-" );
 
333
            strncat( icon_name, mime_type->type, ( sep - mime_type->type ) );
 
334
            strcat( icon_name, "-" );
 
335
            strcat( icon_name, sep + 1 );
 
336
            icon = vfs_load_icon ( icon_theme, icon_name, size );
 
337
        }
 
338
        /* try gnome-mime-foo */
 
339
        if ( G_UNLIKELY( ! icon ) )
 
340
        {
 
341
            icon_name[ 11 ] = '\0'; /* strlen("gnome-mime-") = 11 */
 
342
            strncat( icon_name, mime_type->type, ( sep - mime_type->type ) );
 
343
            icon = vfs_load_icon ( icon_theme, icon_name, size );
 
344
        }
 
345
        /* try foo-x-generic */
 
346
        if ( G_UNLIKELY( ! icon ) )
 
347
        {
 
348
            strncpy( icon_name, mime_type->type, ( sep - mime_type->type ) );
 
349
            icon_name[ (sep - mime_type->type) ] = '\0';
 
350
            strcat( icon_name, "-x-generic" );
 
351
            icon = vfs_load_icon ( icon_theme, icon_name, size );
 
352
        }
 
353
    }
 
354
 
 
355
    if( G_UNLIKELY( !icon ) )
 
356
    {
 
357
        /* prevent endless recursion of XDG_MIME_TYPE_UNKNOWN */
 
358
        if( G_LIKELY( strcmp(mime_type->type, XDG_MIME_TYPE_UNKNOWN) ) )
 
359
        {
 
360
            /* FIXME: fallback to icon of parent mime-type */
 
361
            FmMimeType* unknown;
 
362
            unknown = fm_mime_type_get_from_type( XDG_MIME_TYPE_UNKNOWN );
 
363
            icon = fm_mime_type_get_icon( unknown, big );
 
364
            fm_mime_type_unref( unknown );
 
365
        }
 
366
        else /* unknown */
 
367
        {
 
368
            icon = vfs_load_icon ( icon_theme, "unknown", size );
 
369
        }
 
370
    }
 
371
 
 
372
    if ( big )
 
373
        mime_type->big_icon = icon;
 
374
    else
 
375
        mime_type->small_icon = icon;
 
376
    return icon ? gdk_pixbuf_ref( icon ) : NULL;
 
377
#endif
 
378
}
 
379
 
 
380
const char* fm_mime_type_get_type( FmMimeType* mime_type )
 
381
{
 
382
    return mime_type->type;
 
383
}
 
384
 
 
385
/* Get human-readable description of mime type */
 
386
const char* fm_mime_type_get_desc( FmMimeType* mime_type )
 
387
{
 
388
        /* FIXME: is locking needed here or not? */
 
389
    if ( G_UNLIKELY( ! mime_type->description ) )
 
390
    {
 
391
        mime_type->description = g_content_type_get_description( mime_type->type );
 
392
        /* FIXME: should handle this better */
 
393
        if ( G_UNLIKELY( ! mime_type->description || ! *mime_type->description ) )
 
394
            mime_type->description = g_content_type_get_description( mime_type->type );
 
395
    }
 
396
    return mime_type->description;
 
397
}
 
398
 
 
399
#if 0
 
400
 
 
401
/*
 
402
* Join two string vector containing app lists to generate a new one.
 
403
* Duplicated app will be removed.
 
404
*/
 
405
char** fm_mime_type_join_actions( char** list1, gsize len1,
 
406
                                   char** list2, gsize len2 )
 
407
{
 
408
    gchar **ret = NULL;
 
409
    int i, j, k;
 
410
 
 
411
    if ( len1 > 0 || len2 > 0 )
 
412
        ret = g_new0( char*, len1 + len2 + 1 );
 
413
    for ( i = 0; i < len1; ++i )
 
414
    {
 
415
        ret[ i ] = g_strdup( list1[ i ] );
 
416
    }
 
417
    for ( j = 0, k = 0; j < len2; ++j )
 
418
    {
 
419
        for ( i = 0; i < len1; ++i )
 
420
        {
 
421
            if ( 0 == strcmp( ret[ i ], list2[ j ] ) )
 
422
                break;
 
423
        }
 
424
        if ( i >= len1 )
 
425
        {
 
426
            ret[ len1 + k ] = g_strdup( list2[ j ] );
 
427
            ++k;
 
428
        }
 
429
    }
 
430
    return ret;
 
431
}
 
432
 
 
433
char** fm_mime_type_get_actions( FmMimeType* mime_type )
 
434
{
 
435
    return (char**)mime_type_get_actions( mime_type->type );
 
436
}
 
437
 
 
438
char* fm_mime_type_get_default_action( FmMimeType* mime_type )
 
439
{
 
440
    char* def = (char*)mime_type_get_default_action( mime_type->type );
 
441
    /* FIXME:
 
442
     * If default app is not set, choose one from all availble actions.
 
443
     * Is there any better way to do this?
 
444
     * Should we put this fallback handling here, or at API of higher level?
 
445
     */
 
446
    if( ! def )
 
447
    {
 
448
        char** actions = mime_type_get_actions( mime_type->type );
 
449
        if( actions )
 
450
        {
 
451
            def = g_strdup( actions[0] );
 
452
            g_strfreev( actions );
 
453
        }
 
454
    }
 
455
    return def;
 
456
}
 
457
 
 
458
/*
 
459
* Set default app.desktop for specified file.
 
460
* app can be the name of the desktop file or a command line.
 
461
*/
 
462
void fm_mime_type_set_default_action( FmMimeType* mime_type,
 
463
                                       const char* desktop_id )
 
464
{
 
465
    char* cust_desktop = NULL;
 
466
/*
 
467
    if( ! g_str_has_suffix( desktop_id, ".desktop" ) )
 
468
        return;
 
469
*/
 
470
    fm_mime_type_add_action( mime_type, desktop_id, &cust_desktop );
 
471
    if( cust_desktop )
 
472
        desktop_id = cust_desktop;
 
473
    mime_type_set_default_action( mime_type->type, desktop_id );
 
474
 
 
475
    g_free( cust_desktop );
 
476
}
 
477
 
 
478
/* If user-custom desktop file is created, it's returned in custom_desktop. */
 
479
void fm_mime_type_add_action( FmMimeType* mime_type,
 
480
                               const char* desktop_id,
 
481
                               char** custom_desktop )
 
482
{
 
483
    mime_type_add_action( mime_type->type, desktop_id, custom_desktop );
 
484
}
 
485
 
 
486
GList* fm_mime_type_add_reload_cb( GFreeFunc cb, gpointer user_data )
 
487
{
 
488
    FmMimeReloadCbEnt* ent = g_slice_new( FmMimeReloadCbEnt );
 
489
    ent->cb = cb;
 
490
    ent->user_data = user_data;
 
491
    reload_cb = g_list_append( reload_cb, ent );
 
492
    return g_list_last( reload_cb );
 
493
}
 
494
 
 
495
void fm_mime_type_remove_reload_cb( GList* cb )
 
496
{
 
497
    g_slice_free( FmMimeReloadCbEnt, cb->data );
 
498
    reload_cb = g_list_delete_link( reload_cb, cb );
 
499
}
 
500
 
 
501
#endif