2
* C Implementation: appchooserdlg
7
* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006
9
* Copyright: See COPYING file that comes with this distribution
17
#include "ptk-app-chooser.h"
18
#include "ptk-ui-xml.h"
26
#include "vfs-mime-type.h"
27
#include "vfs-app-desktop.h"
29
#include "vfs-async-task.h"
37
extern gboolean is_my_lock;
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 );
42
static void init_list_view( GtkTreeView* view )
44
GtkTreeViewColumn * col = gtk_tree_view_column_new();
45
GtkCellRenderer* renderer;
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",
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",
57
gtk_tree_view_append_column ( view, col );
60
static gint sort_by_name( GtkTreeModel *model,
65
char * name_a, *name_b;
67
gtk_tree_model_get( model, a, COL_APP_NAME, &name_a, -1 );
70
gtk_tree_model_get( model, b, COL_APP_NAME, &name_b, -1 );
73
ret = strcmp( name_a, name_b );
81
static void add_list_item( GtkListStore* list, VFSAppDesktop* desktop )
84
GdkPixbuf* icon = NULL;
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 );
92
gdk_pixbuf_unref( icon );
95
static GtkTreeModel* create_model_from_mime_type( VFSMimeType* mime_type )
99
GtkListStore* list = gtk_list_store_new( N_COLS, GDK_TYPE_PIXBUF,
100
G_TYPE_STRING, G_TYPE_STRING );
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 ) )
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 );
113
for( app = apps; *app; ++app )
115
VFSAppDesktop* desktop = vfs_app_desktop_new( *app );
116
add_list_item( list, desktop );
117
vfs_app_desktop_unref( desktop );
122
return (GtkTreeModel*) list;
125
GtkWidget* app_chooser_dialog_new( GtkWindow* parent, VFSMimeType* mime_type )
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;
133
mime_desc = vfs_mime_type_get_description( mime_type );
135
gtk_label_set_text( GTK_LABEL( file_type ), mime_desc );
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 ) )
141
gtk_widget_hide( ptk_ui_xml_get_widget( dlg, "set_default" ) );
144
view = GTK_TREE_VIEW( ptk_ui_xml_get_widget( dlg, "recommended_apps" ) );
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 ) );
152
g_signal_connect( ptk_ui_xml_get_widget( dlg, "notebook"),
154
G_CALLBACK(on_notebook_switch_page), dlg );
155
g_signal_connect( ptk_ui_xml_get_widget( dlg, "browse_btn"),
157
G_CALLBACK(on_browse_btn_clicked), dlg );
159
gtk_window_set_transient_for( GTK_WINDOW( dlg ), parent );
163
static void on_load_all_apps_finish( VFSAsyncTask* task, gboolean is_cancelled, GtkWidget* dlg )
168
model = (GtkTreeModel*)vfs_async_task_get_data( task );
171
g_object_unref( model );
175
view = (GtkTreeView*)g_object_get_data( G_OBJECT(task), "view" );
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 );
182
gtk_tree_view_set_model( view, model );
183
g_object_unref( model );
185
gdk_window_set_cursor( dlg->window, NULL );
189
on_notebook_switch_page ( GtkNotebook *notebook,
190
GtkNotebookPage *page,
194
GtkWidget * dlg = ( GtkWidget* ) user_data;
197
/* Load all known apps installed on the system */
200
view = GTK_TREE_VIEW( ptk_ui_xml_get_widget( dlg, "all_apps" ) );
201
if ( ! gtk_tree_view_get_model( view ) )
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 );
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 );
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.
229
const gchar* app_chooser_dialog_get_selected_app( GtkWidget* dlg )
231
const gchar * app = NULL;
232
GtkEntry* entry = GTK_ENTRY( ptk_ui_xml_get_widget( dlg, "cmdline" ) );
233
GtkNotebook* notebook;
237
GtkTreeSelection* tree_sel;
241
app = gtk_entry_get_text( entry );
244
return g_strdup( app );
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 );
253
if ( gtk_tree_selection_get_selected ( tree_sel, &model, &it ) )
255
gtk_tree_model_get( model, &it, COL_DESKTOP_FILE, &app, -1 );
263
* Check if the user set the selected app default handler.
265
gboolean app_chooser_dialog_get_set_default( GtkWidget* dlg )
267
GtkWidget * check = ptk_ui_xml_get_widget( dlg, "set_default" );
268
return gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON( check ) );
272
on_browse_btn_clicked ( GtkButton *button,
278
const char* app_path = "/usr/share/applications";
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,
288
gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER ( dlg ),
290
if ( gtk_dialog_run( GTK_DIALOG( dlg ) ) == GTK_RESPONSE_OK )
292
filename = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER ( dlg ) );
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" ) )
300
app_name = g_path_get_basename( filename );
301
gtk_entry_set_text( entry, app_name );
305
gtk_entry_set_text( entry, filename );
309
gtk_widget_destroy( dlg );
312
static void on_dlg_response( GtkDialog* dlg, int id, gpointer user_data )
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" );
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 );
334
const gchar* ptk_choose_app_for_mime_type( GtkWindow* parent,
335
VFSMimeType* mime_type )
338
const gchar* app = NULL;
340
dlg = app_chooser_dialog_new( parent, mime_type );
342
g_signal_connect( dlg, "response", G_CALLBACK(on_dlg_response), NULL );
344
if ( gtk_dialog_run( GTK_DIALOG( dlg ) ) == GTK_RESPONSE_OK )
346
app = app_chooser_dialog_get_selected_app( dlg );
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 ))
356
vfs_mime_type_add_action( mime_type, app, NULL );
361
gtk_widget_destroy( dlg );
365
void load_all_apps_in_dir( const char* dir_path, GtkListStore* list, VFSAsyncTask* task )
368
GDir* dir = g_dir_open( dir_path, 0, NULL );
374
while( (name = g_dir_read_name( dir )) )
376
vfs_async_task_lock( task );
379
vfs_async_task_unlock( task );
382
vfs_async_task_unlock( task );
384
path = g_build_filename( dir_path, name, NULL );
385
if( G_UNLIKELY( g_file_test( path, G_FILE_TEST_IS_DIR ) ) )
387
/* recursively load sub dirs */
388
load_all_apps_in_dir( path, list, task );
392
if( ! g_str_has_suffix(name, ".desktop") )
395
vfs_async_task_lock( task );
398
vfs_async_task_unlock( task );
401
vfs_async_task_unlock( task );
403
app = vfs_app_desktop_new( path );
406
add_list_item( list, app ); /* There are some operations using GTK+, so lock may be needed. */
409
vfs_app_desktop_unref( app );
416
gpointer load_all_known_apps_thread( VFSAsyncTask* task )
420
gboolean cancel = FALSE;
423
list = GTK_LIST_STORE( vfs_async_task_get_data(task) );
426
dir = g_build_filename( g_get_user_data_dir(), "applications", NULL );
427
load_all_apps_in_dir( dir, list, task );
430
for( dirs = (gchar **) g_get_system_data_dirs(); ! task->cancel && *dirs; ++dirs )
432
dir = g_build_filename( *dirs, "applications", NULL );
433
load_all_apps_in_dir( dir, list, task );
437
vfs_async_task_lock( task );
438
cancel = task->cancel;
439
vfs_async_task_unlock( task );