You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pcmanfm-qt-packaging/libfm-qt/appchooserdialog.cpp

287 lines
9.8 KiB

/*
* Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
* Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
*
* 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 <QPushButton>
#include <gio/gdesktopappinfo.h>
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