~ubuntu-branches/ubuntu/hardy/pcmanfm/hardy-backports

« back to all changes in this revision

Viewing changes to src/ptk/ptk-app-chooser.c

  • Committer: Bazaar Package Importer
  • Author(s): J?r?me Guelfucci
  • Date: 2008-07-01 00:40:37 UTC
  • mfrom: (5.1.3 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080701004037-q6pfacskp0xnk10k
Tags: 0.4.3-1~hardy1
Automated backport upload; no source changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
*  C Implementation: appchooserdlg
 
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
#ifdef HAVE_CONFIG_H
 
14
#  include <config.h>
 
15
#endif
 
16
 
 
17
#include "ptk-app-chooser.h"
 
18
#include "ptk-ui-xml.h"
 
19
 
 
20
#include <gtk/gtk.h>
 
21
#include <glib.h>
 
22
#include <string.h>
 
23
 
 
24
#include "private.h"
 
25
 
 
26
#include "vfs-mime-type.h"
 
27
#include "vfs-app-desktop.h"
 
28
 
 
29
#include "vfs-async-task.h"
 
30
 
 
31
enum{
 
32
    COL_APP_ICON = 0,
 
33
    COL_APP_NAME,
 
34
    COL_DESKTOP_FILE,
 
35
    N_COLS
 
36
};
 
37
extern gboolean is_my_lock;
 
38
 
 
39
static void load_all_apps_in_dir( const char* dir_path, GtkListStore* list, VFSAsyncTask* task );
 
40
static gpointer load_all_known_apps_thread( VFSAsyncTask* task );
 
41
 
 
42
static void init_list_view( GtkTreeView* view )
 
43
{
 
44
    GtkTreeViewColumn * col = gtk_tree_view_column_new();
 
45
    GtkCellRenderer* renderer;
 
46
 
 
47
    renderer = gtk_cell_renderer_pixbuf_new();
 
48
    gtk_tree_view_column_pack_start( col, renderer, FALSE );
 
49
    gtk_tree_view_column_set_attributes( col, renderer, "pixbuf",
 
50
                                         COL_APP_ICON, NULL );
 
51
 
 
52
    renderer = gtk_cell_renderer_text_new();
 
53
    gtk_tree_view_column_pack_start( col, renderer, TRUE );
 
54
    gtk_tree_view_column_set_attributes( col, renderer, "text",
 
55
                                         COL_APP_NAME, NULL );
 
56
 
 
57
    gtk_tree_view_append_column ( view, col );
 
58
}
 
59
 
 
60
static gint sort_by_name( GtkTreeModel *model,
 
61
                          GtkTreeIter *a,
 
62
                          GtkTreeIter *b,
 
63
                          gpointer user_data )
 
64
{
 
65
    char * name_a, *name_b;
 
66
    gint ret = 0;
 
67
    gtk_tree_model_get( model, a, COL_APP_NAME, &name_a, -1 );
 
68
    if ( name_a )
 
69
    {
 
70
        gtk_tree_model_get( model, b, COL_APP_NAME, &name_b, -1 );
 
71
        if ( name_b )
 
72
        {
 
73
            ret = strcmp( name_a, name_b );
 
74
            g_free( name_b );
 
75
        }
 
76
        g_free( name_a );
 
77
    }
 
78
    return ret;
 
79
}
 
80
 
 
81
static void add_list_item( GtkListStore* list, VFSAppDesktop* desktop )
 
82
{
 
83
    GtkTreeIter it;
 
84
    GdkPixbuf* icon = NULL;
 
85
 
 
86
    icon = vfs_app_desktop_get_icon( desktop, 20, TRUE );
 
87
    gtk_list_store_append( list, &it );
 
88
    gtk_list_store_set( list, &it, COL_APP_ICON, icon,
 
89
                        COL_APP_NAME, vfs_app_desktop_get_disp_name( desktop ),
 
90
                        COL_DESKTOP_FILE, vfs_app_desktop_get_name( desktop ), -1 );
 
91
    if ( icon )
 
92
        gdk_pixbuf_unref( icon );
 
93
}
 
94
 
 
95
static GtkTreeModel* create_model_from_mime_type( VFSMimeType* mime_type )
 
96
{
 
97
    char** apps, **app;
 
98
    const char *type;
 
99
    GtkListStore* list = gtk_list_store_new( N_COLS, GDK_TYPE_PIXBUF,
 
100
                                                    G_TYPE_STRING, G_TYPE_STRING );
 
101
    if ( mime_type )
 
102
    {
 
103
        apps = vfs_mime_type_get_actions( mime_type );
 
104
        type = vfs_mime_type_get_type( mime_type );
 
105
        if ( !apps && mime_type_is_text_file( NULL, type ) )
 
106
        {
 
107
            mime_type = vfs_mime_type_get_from_type( XDG_MIME_TYPE_PLAIN_TEXT );
 
108
            apps = vfs_mime_type_get_actions( mime_type );
 
109
            vfs_mime_type_unref( mime_type );
 
110
        }
 
111
        if( apps )
 
112
        {
 
113
            for( app = apps; *app; ++app )
 
114
            {
 
115
                VFSAppDesktop* desktop = vfs_app_desktop_new( *app );
 
116
                add_list_item( list, desktop );
 
117
                vfs_app_desktop_unref( desktop );
 
118
            }
 
119
            g_strfreev( apps );
 
120
        }
 
121
    }
 
122
    return (GtkTreeModel*) list;
 
123
}
 
124
 
 
125
GtkWidget* app_chooser_dialog_new( GtkWindow* parent, VFSMimeType* mime_type )
 
126
{
 
127
    GtkWidget * dlg = ptk_ui_xml_create_widget_from_file( PACKAGE_UI_DIR "/appchooserdlg.glade" );
 
128
    GtkWidget* file_type = ptk_ui_xml_get_widget( dlg, "file_type" );
 
129
    const char* mime_desc;
 
130
    GtkTreeView* view;
 
131
    GtkTreeModel* model;
 
132
 
 
133
    mime_desc = vfs_mime_type_get_description( mime_type );
 
134
    if ( mime_desc )
 
135
        gtk_label_set_text( GTK_LABEL( file_type ), mime_desc );
 
136
 
 
137
    /* Don't set default handler for directories and files with unknown type */
 
138
    if ( 0 == strcmp( vfs_mime_type_get_type( mime_type ), XDG_MIME_TYPE_UNKNOWN ) ||
 
139
         0 == strcmp( vfs_mime_type_get_type( mime_type ), XDG_MIME_TYPE_DIRECTORY ) )
 
140
    {
 
141
        gtk_widget_hide( ptk_ui_xml_get_widget( dlg, "set_default" ) );
 
142
    }
 
143
 
 
144
    view = GTK_TREE_VIEW( ptk_ui_xml_get_widget( dlg, "recommended_apps" ) );
 
145
 
 
146
    model = create_model_from_mime_type( mime_type );
 
147
    gtk_tree_view_set_model( view, model );
 
148
    g_object_unref( G_OBJECT( model ) );
 
149
    init_list_view( view );
 
150
    gtk_widget_grab_focus( GTK_WIDGET( view ) );
 
151
 
 
152
    g_signal_connect( ptk_ui_xml_get_widget( dlg, "notebook"),
 
153
                                    "switch_page",
 
154
                                    G_CALLBACK(on_notebook_switch_page), dlg );
 
155
    g_signal_connect( ptk_ui_xml_get_widget( dlg, "browse_btn"),
 
156
                                    "clicked",
 
157
                                    G_CALLBACK(on_browse_btn_clicked), dlg );
 
158
 
 
159
    gtk_window_set_transient_for( GTK_WINDOW( dlg ), parent );
 
160
    return dlg;
 
161
}
 
162
 
 
163
static void on_load_all_apps_finish( VFSAsyncTask* task, gboolean is_cancelled, GtkWidget* dlg )
 
164
{
 
165
    GtkTreeModel* model;
 
166
    GtkTreeView* view;
 
167
 
 
168
    model = (GtkTreeModel*)vfs_async_task_get_data( task );
 
169
    if( is_cancelled )
 
170
    {
 
171
        g_object_unref( model );
 
172
        return;
 
173
    }
 
174
 
 
175
    view = (GtkTreeView*)g_object_get_data( G_OBJECT(task), "view" );
 
176
 
 
177
    gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE( model ),
 
178
                                    COL_APP_NAME, sort_by_name, NULL, NULL );
 
179
    gtk_tree_sortable_set_sort_column_id ( GTK_TREE_SORTABLE( model ),
 
180
                                        COL_APP_NAME, GTK_SORT_ASCENDING );
 
181
 
 
182
    gtk_tree_view_set_model( view, model );
 
183
    g_object_unref( model );
 
184
 
 
185
    gdk_window_set_cursor( dlg->window, NULL );
 
186
}
 
187
 
 
188
void
 
189
on_notebook_switch_page ( GtkNotebook *notebook,
 
190
                          GtkNotebookPage *page,
 
191
                          guint page_num,
 
192
                          gpointer user_data )
 
193
{
 
194
    GtkWidget * dlg = ( GtkWidget* ) user_data;
 
195
    GtkTreeView* view;
 
196
 
 
197
    /* Load all known apps installed on the system */
 
198
    if ( page_num == 1 )
 
199
    {
 
200
        view = GTK_TREE_VIEW( ptk_ui_xml_get_widget( dlg, "all_apps" ) );
 
201
        if ( ! gtk_tree_view_get_model( view ) )
 
202
        {
 
203
            GdkCursor* busy;
 
204
            VFSAsyncTask* task;
 
205
            GtkListStore* list;
 
206
            init_list_view( view );
 
207
            gtk_widget_grab_focus( GTK_WIDGET( view ) );
 
208
            busy = gdk_cursor_new_for_display( gtk_widget_get_display(GTK_WIDGET( view )), GDK_WATCH );
 
209
            gdk_window_set_cursor( GTK_WIDGET( gtk_widget_get_toplevel(GTK_WIDGET(view)) )->window, busy );
 
210
            gdk_cursor_unref( busy );
 
211
 
 
212
            list = gtk_list_store_new( N_COLS, GDK_TYPE_PIXBUF,
 
213
                                       G_TYPE_STRING, G_TYPE_STRING );
 
214
            task = vfs_async_task_new( (VFSAsyncFunc) load_all_known_apps_thread, list );
 
215
            g_object_set_data( G_OBJECT(task), "view", view );
 
216
            g_object_set_data( G_OBJECT(dlg), "task", task );
 
217
            g_signal_connect( task, "finish", G_CALLBACK(on_load_all_apps_finish), dlg );
 
218
            vfs_async_task_execute( task );
 
219
        }
 
220
    }
 
221
}
 
222
 
 
223
/*
 
224
* Return selected application in a ``newly allocated'' string.
 
225
* Returned string is the file name of the *.desktop file or a command line.
 
226
* These two can be separated by check if the returned string is ended
 
227
* with ".desktop" postfix.
 
228
*/
 
229
const gchar* app_chooser_dialog_get_selected_app( GtkWidget* dlg )
 
230
{
 
231
    const gchar * app = NULL;
 
232
    GtkEntry* entry = GTK_ENTRY( ptk_ui_xml_get_widget( dlg, "cmdline" ) );
 
233
    GtkNotebook* notebook;
 
234
    int idx;
 
235
    GtkBin* scroll;
 
236
    GtkTreeView* view;
 
237
    GtkTreeSelection* tree_sel;
 
238
    GtkTreeModel* model;
 
239
    GtkTreeIter it;
 
240
 
 
241
    app = gtk_entry_get_text( entry );
 
242
    if ( app && *app )
 
243
    {
 
244
        return g_strdup( app );
 
245
    }
 
246
 
 
247
    notebook = GTK_NOTEBOOK( ptk_ui_xml_get_widget( dlg, "notebook" ) );
 
248
    idx = gtk_notebook_get_current_page ( notebook );
 
249
    scroll = GTK_BIN( gtk_notebook_get_nth_page( notebook, idx ) );
 
250
    view = GTK_TREE_VIEW(gtk_bin_get_child( scroll ));
 
251
    tree_sel = gtk_tree_view_get_selection( view );
 
252
 
 
253
    if ( gtk_tree_selection_get_selected ( tree_sel, &model, &it ) )
 
254
    {
 
255
        gtk_tree_model_get( model, &it, COL_DESKTOP_FILE, &app, -1 );
 
256
    }
 
257
    else
 
258
        app = NULL;
 
259
    return app;
 
260
}
 
261
 
 
262
/*
 
263
* Check if the user set the selected app default handler.
 
264
*/
 
265
gboolean app_chooser_dialog_get_set_default( GtkWidget* dlg )
 
266
{
 
267
    GtkWidget * check = ptk_ui_xml_get_widget( dlg, "set_default" );
 
268
    return gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON( check ) );
 
269
}
 
270
 
 
271
void
 
272
on_browse_btn_clicked ( GtkButton *button,
 
273
                        gpointer user_data )
 
274
{
 
275
    char * filename;
 
276
    char* app_name;
 
277
    GtkEntry* entry;
 
278
    const char* app_path = "/usr/share/applications";
 
279
 
 
280
    GtkWidget* parent = GTK_WIDGET(user_data );
 
281
    GtkWidget* dlg = gtk_file_chooser_dialog_new( NULL, GTK_WINDOW( parent ),
 
282
                                                  GTK_FILE_CHOOSER_ACTION_OPEN,
 
283
                                                  GTK_STOCK_OPEN,
 
284
                                                  GTK_RESPONSE_OK,
 
285
                                                  GTK_STOCK_CANCEL,
 
286
                                                  GTK_RESPONSE_CANCEL,
 
287
                                                  NULL );
 
288
    gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER ( dlg ),
 
289
                                          "/usr/bin" );
 
290
    if ( gtk_dialog_run( GTK_DIALOG( dlg ) ) == GTK_RESPONSE_OK )
 
291
    {
 
292
        filename = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER ( dlg ) );
 
293
        if( filename )
 
294
        {
 
295
            entry = GTK_ENTRY( ptk_ui_xml_get_widget( dlg, "cmdline" ) );
 
296
            /* FIXME: path shouldn't be hard-coded */
 
297
            if( g_str_has_prefix( filename, app_path )
 
298
                && g_str_has_suffix( filename, ".desktop" ) )
 
299
            {
 
300
                app_name = g_path_get_basename( filename );
 
301
                gtk_entry_set_text( entry, app_name );
 
302
                g_free( app_name );
 
303
            }
 
304
            else
 
305
                gtk_entry_set_text( entry, filename );
 
306
            g_free ( filename );
 
307
        }
 
308
    }
 
309
    gtk_widget_destroy( dlg );
 
310
}
 
311
 
 
312
static void on_dlg_response( GtkDialog* dlg, int id, gpointer user_data )
 
313
{
 
314
    VFSAsyncTask* task;
 
315
    switch( id )
 
316
    {
 
317
    /* The dialog is going to be closed */
 
318
    case GTK_RESPONSE_OK:
 
319
    case GTK_RESPONSE_CANCEL:
 
320
    case GTK_RESPONSE_NONE:
 
321
    case GTK_RESPONSE_DELETE_EVENT:
 
322
        /* cancel app loading on dialog closing... */
 
323
        task = (VFSAsyncTask*)g_object_get_data( G_OBJECT(dlg), "task" );
 
324
        if( task )
 
325
        {
 
326
            vfs_async_task_cancel( task );
 
327
            /* The GtkListStore will be freed in "finish" handler of task - on_load_all_app_finish(). */
 
328
            g_object_unref( task );
 
329
        }
 
330
        break;
 
331
    }
 
332
}
 
333
 
 
334
const gchar* ptk_choose_app_for_mime_type( GtkWindow* parent,
 
335
                                           VFSMimeType* mime_type )
 
336
{
 
337
    GtkWidget * dlg;
 
338
    const gchar* app = NULL;
 
339
 
 
340
    dlg = app_chooser_dialog_new( parent, mime_type );
 
341
 
 
342
    g_signal_connect( dlg, "response",  G_CALLBACK(on_dlg_response), NULL );
 
343
 
 
344
    if ( gtk_dialog_run( GTK_DIALOG( dlg ) ) == GTK_RESPONSE_OK )
 
345
    {
 
346
        app = app_chooser_dialog_get_selected_app( dlg );
 
347
        if ( app )
 
348
        {
 
349
            /* The selected app is set to default action */
 
350
            /* TODO: full-featured mime editor??? */
 
351
            if ( app_chooser_dialog_get_set_default( dlg ) )
 
352
                vfs_mime_type_set_default_action( mime_type, app );
 
353
            else if ( strcmp( vfs_mime_type_get_type( mime_type ), XDG_MIME_TYPE_UNKNOWN )
 
354
                      && strcmp( vfs_mime_type_get_type( mime_type ), XDG_MIME_TYPE_DIRECTORY ))
 
355
            {
 
356
                vfs_mime_type_add_action( mime_type, app, NULL );
 
357
            }
 
358
        }
 
359
    }
 
360
 
 
361
    gtk_widget_destroy( dlg );
 
362
    return app;
 
363
}
 
364
 
 
365
void load_all_apps_in_dir( const char* dir_path, GtkListStore* list, VFSAsyncTask* task )
 
366
{
 
367
 
 
368
    GDir* dir = g_dir_open( dir_path, 0, NULL );
 
369
    if( dir )
 
370
    {
 
371
        const char* name;
 
372
        char* path;
 
373
        VFSAppDesktop* app;
 
374
        while( (name = g_dir_read_name( dir )) )
 
375
        {
 
376
            vfs_async_task_lock( task );
 
377
            if( task->cancel )
 
378
            {
 
379
                vfs_async_task_unlock( task );
 
380
                break;
 
381
            }
 
382
            vfs_async_task_unlock( task );
 
383
 
 
384
            path = g_build_filename( dir_path, name, NULL );
 
385
            if( G_UNLIKELY( g_file_test( path, G_FILE_TEST_IS_DIR ) ) )
 
386
            {
 
387
                /* recursively load sub dirs */
 
388
                load_all_apps_in_dir( path, list, task );
 
389
                g_free( path );
 
390
                continue;
 
391
            }
 
392
            if( ! g_str_has_suffix(name, ".desktop") )
 
393
                continue;
 
394
 
 
395
            vfs_async_task_lock( task );
 
396
            if( task->cancel )
 
397
            {
 
398
                vfs_async_task_unlock( task );
 
399
                break;
 
400
            }
 
401
            vfs_async_task_unlock( task );
 
402
 
 
403
            app = vfs_app_desktop_new( path );
 
404
 
 
405
            GDK_THREADS_ENTER();
 
406
            add_list_item( list, app ); /* There are some operations using GTK+, so lock may be needed. */
 
407
            GDK_THREADS_LEAVE();
 
408
 
 
409
            vfs_app_desktop_unref( app );
 
410
            g_free( path );
 
411
        }
 
412
        g_dir_close( dir );
 
413
    }
 
414
}
 
415
 
 
416
gpointer load_all_known_apps_thread( VFSAsyncTask* task )
 
417
{
 
418
    gchar* dir, **dirs;
 
419
    GtkListStore* list;
 
420
    gboolean cancel = FALSE;
 
421
 
 
422
    GDK_THREADS_ENTER();
 
423
    list = GTK_LIST_STORE( vfs_async_task_get_data(task) );
 
424
    GDK_THREADS_LEAVE();
 
425
 
 
426
    dir = g_build_filename( g_get_user_data_dir(), "applications", NULL );
 
427
    load_all_apps_in_dir( dir, list, task );
 
428
    g_free( dir );
 
429
 
 
430
    for( dirs = (gchar **) g_get_system_data_dirs(); ! task->cancel && *dirs; ++dirs )
 
431
    {
 
432
        dir = g_build_filename( *dirs, "applications", NULL );
 
433
        load_all_apps_in_dir( dir, list, task );
 
434
        g_free( dir );
 
435
    }
 
436
 
 
437
    vfs_async_task_lock( task );
 
438
    cancel = task->cancel;
 
439
    vfs_async_task_unlock( task );
 
440
    return NULL;
 
441
}
 
442