~noskcaj/ubuntu/utopic/libfm/merge

« back to all changes in this revision

Viewing changes to src/gtk/fm-app-chooser-dlg.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee (李健秋)
  • Date: 2010-04-29 03:52:06 UTC
  • Revision ID: james.westby@ubuntu.com-20100429035206-qlj1jwsfcgr5mdx3
Tags: upstream-0.1.11
Import upstream version 0.1.11

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      fm-app-chooser-dlg.c
 
3
 *
 
4
 *      Copyright 2010 Hong Jen Yee (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
#ifdef HAVE_CONFIG_H
 
22
#include <config.h>
 
23
#endif
 
24
 
 
25
#include <glib/gi18n-lib.h>
 
26
#include <string.h>
 
27
#include <unistd.h>
 
28
#include "fm.h"
 
29
#include "fm-app-chooser-dlg.h"
 
30
#include "fm-app-menu-view.h"
 
31
#include <menu-cache.h>
 
32
#include <gio/gdesktopappinfo.h>
 
33
 
 
34
typedef struct _AppChooserData AppChooserData;
 
35
struct _AppChooserData
 
36
{
 
37
    GtkWidget* dlg;
 
38
    GtkWidget* notebook;
 
39
    GtkWidget* apps_view;
 
40
    GtkWidget* cmdline;
 
41
    GtkWidget* set_default;
 
42
    GtkWidget* status;
 
43
    GtkWidget* use_terminal;
 
44
    FmMimeType* mime_type;
 
45
};
 
46
 
 
47
GAppInfo* fm_app_info_create_from_commandline(const char *commandline,
 
48
                                               const char *application_name,
 
49
                                               gboolean terminal)
 
50
{
 
51
    GAppInfo* app = NULL;
 
52
    char* dirname = g_build_filename (g_get_user_data_dir (), "applications", NULL);
 
53
 
 
54
    if(g_mkdir_with_parents(dirname, 0700) == 0)
 
55
    {
 
56
        char* filename = g_strdup_printf ("%s/userapp-%s-XXXXXX.desktop", dirname, application_name);
 
57
        int fd = g_mkstemp (filename);
 
58
        if(fd != -1)
 
59
        {
 
60
            GString* content = g_string_sized_new(256);
 
61
            g_string_printf(content,
 
62
                "[Desktop Entry]\n"
 
63
                "Type=Application\n"
 
64
                "Name=%s\n"
 
65
                "Exec=%s\n"
 
66
                "NoDisplay=true\n",
 
67
                application_name,
 
68
                commandline
 
69
            );
 
70
            if(terminal)
 
71
                g_string_append_printf(content,
 
72
                    "Terminal=%s\n", terminal ? "true" : "false");
 
73
            if(g_file_set_contents(filename, content->str, content->len, NULL))
 
74
            {
 
75
                char* desktop_id = g_path_get_basename(filename);
 
76
                app = g_desktop_app_info_new(desktop_id);
 
77
                g_free(desktop_id);
 
78
            }
 
79
            close(fd);
 
80
        }
 
81
        g_free(filename);
 
82
    }
 
83
    g_free(dirname);
 
84
    return app;
 
85
}
 
86
 
 
87
static void on_dlg_destroy(AppChooserData* data, GObject* dlg)
 
88
{
 
89
    g_slice_free(AppChooserData, data);
 
90
}
 
91
 
 
92
static void on_switch_page(GtkNotebook* nb, GtkWidget* page, gint num, AppChooserData* data)
 
93
{
 
94
    if(num == 0) /* list of installed apps */
 
95
    {
 
96
        gtk_label_set_text(GTK_LABEL(data->status), _("Use selected application to open files"));
 
97
        gtk_dialog_set_response_sensitive(data->dlg, GTK_RESPONSE_OK,
 
98
                        fm_app_menu_view_is_app_selected(GTK_TREE_VIEW(data->apps_view)));
 
99
    }
 
100
    else /* custom app */
 
101
    {
 
102
        const char* cmd = gtk_entry_get_text(GTK_ENTRY(data->cmdline));
 
103
        gtk_label_set_text(GTK_LABEL(data->status), _("Execute custom command line to open files"));
 
104
        gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dlg), GTK_RESPONSE_OK, (cmd && cmd[0]));
 
105
    }
 
106
}
 
107
 
 
108
static void on_apps_view_sel_changed(GtkTreeSelection* tree_sel, AppChooserData* data)
 
109
{
 
110
    if(gtk_notebook_get_current_page(GTK_NOTEBOOK(data->notebook)) == 0)
 
111
    {
 
112
        gtk_dialog_set_response_sensitive(data->dlg, GTK_RESPONSE_OK,
 
113
                        fm_app_menu_view_is_app_selected(GTK_TREE_VIEW(data->apps_view)));
 
114
    }
 
115
}
 
116
 
 
117
static void on_cmdline_changed(GtkEditable* cmdline, AppChooserData* data)
 
118
{
 
119
    if(gtk_notebook_get_current_page(GTK_NOTEBOOK(data->notebook)) == 1)
 
120
    {
 
121
        const char* cmd = gtk_entry_get_text(GTK_ENTRY(data->cmdline));
 
122
        gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dlg), GTK_RESPONSE_OK, (cmd && cmd[0]));
 
123
    }
 
124
}
 
125
 
 
126
GtkWidget *fm_app_chooser_dlg_new(FmMimeType* mime_type, gboolean can_set_default)
 
127
{
 
128
    GtkWidget* scroll;
 
129
    GtkWidget* file_type;
 
130
    GtkTreeSelection* tree_sel;
 
131
    GtkBuilder* builder = gtk_builder_new();
 
132
    AppChooserData* data = g_slice_new0(AppChooserData);
 
133
 
 
134
    gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
 
135
    gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/app-chooser.ui", NULL);
 
136
    data->dlg = (GtkWidget*)gtk_builder_get_object(builder, "dlg");
 
137
    data->notebook = (GtkWidget*)gtk_builder_get_object(builder, "notebook");
 
138
    scroll = (GtkWidget*)gtk_builder_get_object(builder, "apps_scroll");
 
139
    file_type = (GtkWidget*)gtk_builder_get_object(builder, "file_type");
 
140
    data->cmdline = (GtkWidget*)gtk_builder_get_object(builder, "cmdline");
 
141
    data->set_default = (GtkWidget*)gtk_builder_get_object(builder, "set_default");
 
142
    data->use_terminal = (GtkWidget*)gtk_builder_get_object(builder, "use_terminal");
 
143
    data->status = (GtkWidget*)gtk_builder_get_object(builder, "status");
 
144
    data->mime_type = mime_type;
 
145
 
 
146
    gtk_dialog_set_alternative_button_order(GTK_DIALOG(data->dlg), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
 
147
 
 
148
    if(!can_set_default)
 
149
        gtk_widget_hide(data->set_default);
 
150
 
 
151
    if(mime_type && mime_type->type && mime_type->description)
 
152
        gtk_label_set_text(GTK_LABEL(file_type), mime_type->description);
 
153
    else
 
154
    {
 
155
        GtkWidget* hbox = (GtkWidget*)gtk_builder_get_object(builder, "file_type_hbox");
 
156
        gtk_widget_destroy(hbox);
 
157
        gtk_widget_hide(data->set_default);
 
158
    }
 
159
 
 
160
    data->apps_view = fm_app_menu_view_new();
 
161
    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(data->apps_view), FALSE);
 
162
    gtk_widget_show(data->apps_view);
 
163
    gtk_container_add(GTK_CONTAINER(scroll), data->apps_view);
 
164
    gtk_widget_grab_focus(data->apps_view);
 
165
 
 
166
    g_object_unref(builder);
 
167
 
 
168
    g_object_set_qdata_full(G_OBJECT(data->dlg), fm_qdata_id, data, (GDestroyNotify)on_dlg_destroy);
 
169
    g_signal_connect(data->notebook, "switch-page", G_CALLBACK(on_switch_page), data);
 
170
    on_switch_page(GTK_NOTEBOOK(data->notebook), NULL, 0, data);
 
171
    tree_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->apps_view));
 
172
    g_signal_connect(tree_sel, "changed", G_CALLBACK(on_apps_view_sel_changed), data);
 
173
    g_signal_connect(data->cmdline, "changed", G_CALLBACK(on_cmdline_changed), data);
 
174
    gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dlg), GTK_RESPONSE_OK, FALSE);
 
175
 
 
176
    return data->dlg;
 
177
}
 
178
 
 
179
inline static char* get_binary(const char* cmdline, gboolean* arg_found)
 
180
{
 
181
    /* see if command line contains %f, %F, %u, or %U. */
 
182
    const char* p = strstr(cmdline, " %");
 
183
    if(p)
 
184
    {
 
185
        if( !strchr("fFuU", *(p + 2)) )
 
186
            p = NULL;
 
187
    }
 
188
    if(arg_found)
 
189
        *arg_found = (p != NULL);
 
190
    if(p)
 
191
        return g_strndup(cmdline, p - cmdline);
 
192
    else
 
193
        return g_strdup(cmdline);
 
194
}
 
195
 
 
196
GAppInfo* fm_app_chooser_dlg_get_selected_app(GtkDialog* dlg, gboolean* set_default)
 
197
{
 
198
    GAppInfo* app = NULL;
 
199
    AppChooserData* data = (AppChooserData*)g_object_get_qdata(G_OBJECT(dlg), fm_qdata_id);
 
200
    switch( gtk_notebook_get_current_page(GTK_NOTEBOOK(data->notebook)) )
 
201
    {
 
202
    case 0: /* all applications */
 
203
        app = fm_app_menu_view_get_selected_app(GTK_TREE_VIEW(data->apps_view));
 
204
        break;
 
205
    case 1: /* custom cmd line */
 
206
        {
 
207
            const char* cmdline = gtk_entry_get_text(GTK_ENTRY(data->cmdline));
 
208
            if(cmdline && cmdline[0])
 
209
            {
 
210
                char* _cmdline = NULL;
 
211
                gboolean arg_found = FALSE;
 
212
                char* bin1 = get_binary(cmdline, &arg_found);
 
213
                g_debug("bin1 = %s", bin1);
 
214
                /* see if command line contains %f, %F, %u, or %U. */
 
215
                if(!arg_found)  /* append %f if no %f, %F, %u, or %U was found. */
 
216
                    cmdline = _cmdline = g_strconcat(cmdline, " %f", NULL);
 
217
 
 
218
                /* FIXME: is there any better way to do this? */
 
219
                /* We need to ensure that no duplicated items are added */
 
220
                if(data->mime_type)
 
221
                {
 
222
                    MenuCache* menu_cache;
 
223
                    #if GLIB_CHECK_VERSION(2, 10, 0)
 
224
                    /* see if the command is already in the list of known apps for this mime-type */
 
225
                    GList* apps = g_app_info_get_all_for_type(data->mime_type->type);
 
226
                    GList* l;
 
227
                    for(l=apps;l;l=l->next)
 
228
                    {
 
229
                        GAppInfo* app2 = (GAppInfo*)l->data;
 
230
                        const char* cmd = g_app_info_get_commandline(app2);
 
231
                        char* bin2 = get_binary(cmd, NULL);
 
232
                        if(g_strcmp0(bin1, bin2) == 0)
 
233
                        {
 
234
                            app = (GAppInfo*)g_object_ref(app2);
 
235
                            g_debug("found in app list");
 
236
                            g_free(bin2);
 
237
                            break;
 
238
                        }
 
239
                        g_free(bin2);
 
240
                    }
 
241
                    g_list_foreach(apps, (GFunc)g_object_unref, NULL);
 
242
                    g_list_free(apps);
 
243
                    if(app)
 
244
                        goto _out;
 
245
                    #endif
 
246
 
 
247
                    /* see if this command can be found in menu cache */
 
248
                    menu_cache = menu_cache_lookup("applications.menu");
 
249
                    if(menu_cache)
 
250
                    {
 
251
                        if(menu_cache_get_root_dir(menu_cache))
 
252
                        {
 
253
                            GSList* all_apps = menu_cache_list_all_apps(menu_cache);
 
254
                            GSList* l;
 
255
                            for(l=all_apps;l;l=l->next)
 
256
                            {
 
257
                                MenuCacheApp* ma = MENU_CACHE_APP(l->data);
 
258
                                char* bin2 = get_binary(menu_cache_app_get_exec(ma), NULL);
 
259
                                if(g_strcmp0(bin1, bin2) == 0)
 
260
                                {
 
261
                                    app = g_desktop_app_info_new(menu_cache_item_get_id(MENU_CACHE_ITEM(ma)));
 
262
                                    g_debug("found in menu cache");
 
263
                                    menu_cache_item_unref(MENU_CACHE_ITEM(ma));
 
264
                                    g_free(bin2);
 
265
                                    break;
 
266
                                }
 
267
                                menu_cache_item_unref(MENU_CACHE_ITEM(ma));
 
268
                                g_free(bin2);
 
269
                            }
 
270
                            g_slist_free(all_apps);
 
271
                        }
 
272
                        menu_cache_unref(menu_cache);
 
273
                        if(app)
 
274
                            goto _out;
 
275
                    }
 
276
                }
 
277
 
 
278
                /* FIXME: g_app_info_create_from_commandline force the use of %f or %u, so this is not we need */
 
279
                app = fm_app_info_create_from_commandline(cmdline, bin1, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->use_terminal)));
 
280
            _out:
 
281
                g_free(bin1);
 
282
                g_free(_cmdline);
 
283
            }
 
284
        }
 
285
        break;
 
286
    }
 
287
 
 
288
    if(set_default)
 
289
        *set_default = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->set_default));
 
290
    return app;
 
291
}
 
292
 
 
293
GAppInfo* fm_choose_app_for_mime_type(GtkWindow* parent, FmMimeType* mime_type, gboolean can_set_default)
 
294
{
 
295
    GAppInfo* app = NULL;
 
296
    GtkWidget* dlg = fm_app_chooser_dlg_new(mime_type, can_set_default);
 
297
    if(parent)
 
298
        gtk_window_set_transient_for(GTK_WINDOW(dlg), parent);
 
299
    if(gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK)
 
300
    {
 
301
        gboolean set_default;
 
302
        app = fm_app_chooser_dlg_get_selected_app(GTK_DIALOG(dlg), &set_default);
 
303
 
 
304
        if(app && mime_type && mime_type->type)
 
305
        {
 
306
            GError* err = NULL;
 
307
            /* add this app to the mime-type */
 
308
            if(!g_app_info_add_supports_type(app, mime_type->type, &err))
 
309
            {
 
310
                g_debug("error: %s", err->message);
 
311
                g_error_free(err);
 
312
            }
 
313
            /* if need to set default */
 
314
            if(set_default)
 
315
                g_app_info_set_as_default_for_type(app, mime_type->type, NULL);
 
316
        }
 
317
    }
 
318
    gtk_widget_destroy(dlg);
 
319
    return app;
 
320
}