/* * Copyright 2010-2014 Hong Jen Yee (PCMan) * Copyright 2012-2013 Andriy Grytsenko (LStranger) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "appchooserdialog.h" #include "ui_app-chooser-dialog.h" #include #include namespace Fm { AppChooserDialog::AppChooserDialog(FmMimeType* mimeType, QWidget* parent, Qt::WindowFlags f): QDialog(parent, f), mimeType_(NULL), selectedApp_(NULL), canSetDefault_(true), ui(new Ui::AppChooserDialog()) { ui->setupUi(this); connect(ui->appMenuView, &AppMenuView::selectionChanged, this, &AppChooserDialog::onSelectionChanged); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AppChooserDialog::onTabChanged); if(!ui->appMenuView->isAppSelected()) ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // disable OK button if(mimeType) setMimeType(mimeType); } AppChooserDialog::~AppChooserDialog() { delete ui; if(mimeType_) fm_mime_type_unref(mimeType_); if(selectedApp_) g_object_unref(selectedApp_); } bool AppChooserDialog::isSetDefault() { return ui->setDefault->isChecked(); } static void on_temp_appinfo_destroy(gpointer data, GObject* objptr) { char* filename = (char*)data; if(g_unlink(filename) < 0) g_critical("failed to remove %s", filename); /* else qDebug("temp file %s removed", filename); */ g_free(filename); } static GAppInfo* app_info_create_from_commandline(const char* commandline, const char* application_name, const char* bin_name, const char* mime_type, gboolean terminal, gboolean keep) { GAppInfo* app = NULL; char* dirname = g_build_filename(g_get_user_data_dir(), "applications", NULL); const char* app_basename = strrchr(bin_name, '/'); if(app_basename) app_basename++; else app_basename = bin_name; if(g_mkdir_with_parents(dirname, 0700) == 0) { char* filename = g_strdup_printf("%s/userapp-%s-XXXXXX.desktop", dirname, app_basename); int fd = g_mkstemp(filename); if(fd != -1) { GString* content = g_string_sized_new(256); g_string_printf(content, "[" G_KEY_FILE_DESKTOP_GROUP "]\n" G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n" G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n" G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n" G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=Other;\n" G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY "=true\n", application_name, commandline ); if(mime_type) g_string_append_printf(content, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE "=%s\n", mime_type); g_string_append_printf(content, G_KEY_FILE_DESKTOP_KEY_TERMINAL "=%s\n", terminal ? "true" : "false"); if(terminal) g_string_append_printf(content, "X-KeepTerminal=%s\n", keep ? "true" : "false"); close(fd); /* g_file_set_contents() may fail creating duplicate */ if(g_file_set_contents(filename, content->str, content->len, NULL)) { char* fbname = g_path_get_basename(filename); app = G_APP_INFO(g_desktop_app_info_new(fbname)); g_free(fbname); /* if there is mime_type set then created application will be saved for the mime type (see fm_choose_app_for_mime_type() below) but if not then we should remove this temp. file */ if(!mime_type || !application_name[0]) /* save the name so this file will be removed later */ g_object_weak_ref(G_OBJECT(app), on_temp_appinfo_destroy, g_strdup(filename)); } else g_unlink(filename); g_string_free(content, TRUE); } g_free(filename); } g_free(dirname); return app; } inline static char* get_binary(const char* cmdline, gboolean* arg_found) { /* see if command line contains %f, %F, %u, or %U. */ const char* p = strstr(cmdline, " %"); if(p) { if(!strchr("fFuU", *(p + 2))) p = NULL; } if(arg_found) *arg_found = (p != NULL); if(p) return g_strndup(cmdline, p - cmdline); else return g_strdup(cmdline); } GAppInfo* AppChooserDialog::customCommandToApp() { GAppInfo* app = NULL; QByteArray cmdline = ui->cmdLine->text().toLocal8Bit(); QByteArray app_name = ui->appName->text().toUtf8(); if(!cmdline.isEmpty()) { gboolean arg_found = FALSE; char* bin1 = get_binary(cmdline.constData(), &arg_found); qDebug("bin1 = %s", bin1); /* see if command line contains %f, %F, %u, or %U. */ if(!arg_found) { /* append %f if no %f, %F, %u, or %U was found. */ cmdline += " %f"; } /* FIXME: is there any better way to do this? */ /* We need to ensure that no duplicated items are added */ if(mimeType_) { MenuCache* menu_cache; /* see if the command is already in the list of known apps for this mime-type */ GList* apps = g_app_info_get_all_for_type(fm_mime_type_get_type(mimeType_)); GList* l; for(l = apps; l; l = l->next) { GAppInfo* app2 = G_APP_INFO(l->data); const char* cmd = g_app_info_get_commandline(app2); char* bin2 = get_binary(cmd, NULL); if(g_strcmp0(bin1, bin2) == 0) { app = G_APP_INFO(g_object_ref(app2)); qDebug("found in app list"); g_free(bin2); break; } g_free(bin2); } g_list_foreach(apps, (GFunc)g_object_unref, NULL); g_list_free(apps); if(app) goto _out; /* see if this command can be found in menu cache */ menu_cache = menu_cache_lookup("applications.menu"); if(menu_cache) { MenuCacheDir* root_dir = menu_cache_dup_root_dir(menu_cache); if(root_dir) { GSList* all_apps = menu_cache_list_all_apps(menu_cache); GSList* l; for(l = all_apps; l; l = l->next) { MenuCacheApp* ma = MENU_CACHE_APP(l->data); const char* exec = menu_cache_app_get_exec(ma); char* bin2; if(exec == NULL) { g_warning("application %s has no Exec statement", menu_cache_item_get_id(MENU_CACHE_ITEM(ma))); continue; } bin2 = get_binary(exec, NULL); if(g_strcmp0(bin1, bin2) == 0) { app = G_APP_INFO(g_desktop_app_info_new(menu_cache_item_get_id(MENU_CACHE_ITEM(ma)))); qDebug("found in menu cache"); menu_cache_item_unref(MENU_CACHE_ITEM(ma)); g_free(bin2); break; } menu_cache_item_unref(MENU_CACHE_ITEM(ma)); g_free(bin2); } g_slist_free(all_apps); menu_cache_item_unref(MENU_CACHE_ITEM(root_dir)); } menu_cache_unref(menu_cache); } if(app) goto _out; } /* FIXME: g_app_info_create_from_commandline force the use of %f or %u, so this is not we need */ app = app_info_create_from_commandline(cmdline.constData(), app_name.constData(), bin1, mimeType_ ? fm_mime_type_get_type(mimeType_) : NULL, ui->useTerminal->isChecked(), ui->keepTermOpen->isChecked()); _out: g_free(bin1); } return app; } void AppChooserDialog::accept() { QDialog::accept(); if(ui->tabWidget->currentIndex() == 0) { selectedApp_ = ui->appMenuView->selectedApp(); } else { // custom command line selectedApp_ = customCommandToApp(); } if(selectedApp_) { if(mimeType_ && fm_mime_type_get_type(mimeType_) && g_app_info_get_name(selectedApp_)[0]) { /* add this app to the mime-type */ #if GLIB_CHECK_VERSION(2, 27, 6) g_app_info_set_as_last_used_for_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL); #else g_app_info_add_supports_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL); #endif /* if need to set default */ if(ui->setDefault->isChecked()) g_app_info_set_as_default_for_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL); } } } void AppChooserDialog::onSelectionChanged() { bool isAppSelected = ui->appMenuView->isAppSelected(); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isAppSelected); } void AppChooserDialog::setMimeType(FmMimeType* mimeType) { if(mimeType_) fm_mime_type_unref(mimeType_); mimeType_ = mimeType ? fm_mime_type_ref(mimeType) : NULL; if(mimeType_) { QString text = tr("Select an application to open \"%1\" files") .arg(QString::fromUtf8(fm_mime_type_get_desc(mimeType_))); ui->fileTypeHeader->setText(text); } else { ui->fileTypeHeader->hide(); ui->setDefault->hide(); } } void AppChooserDialog::setCanSetDefault(bool value) { canSetDefault_ = value; ui->setDefault->setVisible(value); } void AppChooserDialog::onTabChanged(int index) { if(index == 0) { // app menu view onSelectionChanged(); } else if(index == 1) { // custom command ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } } } // namespace Fm